Skip to content

Commit

Permalink
fix(core): ensure fuse directory exists (#139)
Browse files Browse the repository at this point in the history
* ensure fuse directory exists

* add test

* lock
  • Loading branch information
JoviDeCroock authored Feb 5, 2024
1 parent 1f225c5 commit fc8689b
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 179 deletions.
5 changes: 5 additions & 0 deletions .changeset/hungry-dodos-knock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'fuse': patch
---

Ensure fuse directory exists
14 changes: 12 additions & 2 deletions packages/core/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import sade from 'sade'
import path from 'path'
import { existsSync } from 'fs'
import fs, { writeFile } from 'fs/promises'
import fs from 'fs/promises'
import { createServer, build } from 'vite'
import { VitePluginNode } from 'vite-plugin-node'
import { generate, CodegenContext } from '@graphql-codegen/cli'
Expand Down Expand Up @@ -101,6 +101,11 @@ prog
const base = hasSrcDir
? path.resolve(baseDirectory, 'src')
: baseDirectory

if (!(await fs.exists(path.resolve(base, 'fuse')))) {
await fs.mkdir(path.resolve(base, 'fuse'))
}

await Promise.allSettled([
fs.writeFile(
path.resolve(base, 'fuse/index.ts'),
Expand Down Expand Up @@ -204,6 +209,11 @@ prog
const base = hasSrcDir
? path.resolve(baseDirectory, 'src')
: baseDirectory

if (!existsSync(path.resolve(base, 'fuse'))) {
await fs.mkdir(path.resolve(base, 'fuse'))
}

await Promise.allSettled([
fs.writeFile(
path.resolve(base, 'fuse/index.ts'),
Expand Down Expand Up @@ -232,7 +242,7 @@ export * from "fuse/client";\n`
noSilentErrors: true,
hooks: {
afterOneFileWrite: async () => {
await writeFile(
await fs.writeFile(
hasSrcDir
? baseDirectory + '/src/fuse/index.ts'
: baseDirectory + '/fuse/index.ts',
Expand Down
14 changes: 6 additions & 8 deletions packages/core/src/utils/gql-tada.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@ export async function isUsingGraphQLTada(cwd: string): Promise<boolean> {

try {
const parsed = JSON.parse(pkgJson.value as string)
if (
!parsed.dependencies['gql.tada'] &&
!parsed.devDependencies['gql.tada']
) {
const merged = Object.keys({
...parsed.dependencies,
...parsed.devDependencies,
})
if (!merged.find((x) => x.includes('gql.tada'))) {
return false
}

if (
!parsed.dependencies['@0no-co/graphqlsp'] &&
!parsed.devDependencies['@0no-co/graphqlsp']
) {
if (!merged.find((x) => x.includes('@0no-co/graphqlsp'))) {
return false
}
} catch (e) {
Expand Down
43 changes: 40 additions & 3 deletions packages/core/test/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import { afterEach } from 'node:test'
const fixturesDir = path.join(__dirname, 'fixtures')
const allFixtures = fs.readdirSync(fixturesDir)

const wait = () => {
const wait = (timeout = 1000) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(null)
}, 500)
}, timeout)
})
}

Expand All @@ -30,7 +30,14 @@ describe.each(allFixtures)('%s', (fixtureName) => {
await fs.promises.rm(path.join(fixtureDir, 'build'), {
recursive: true,
})
await fs.promises.rm(path.join(fixtureDir, 'schema.graphql'))
try {
await fs.promises.rm(path.join(fixtureDir, 'fuse'), {
recursive: true,
})
} catch (e) {}
try {
await fs.promises.rm(path.join(fixtureDir, 'schema.graphql'))
} catch (e) {}
}, 25_000)

afterEach(async () => {
Expand Down Expand Up @@ -67,6 +74,36 @@ describe.each(allFixtures)('%s', (fixtureName) => {
expect(result.data._version).toBeDefined()
}, 10_000)

if (fixtureName === 'tada') {
test('Should run the client dev command', async () => {
process = execa('pnpm', ['fuse', 'dev'], {
cwd: fixtureDir,
})

await new Promise((resolve) => {
process!.stdout?.on('data', (data) => {
const msg = data.toString()
console.log(msg)
if (msg.includes('Server listening on')) {
resolve(null)
}
})
})

await wait()

expect(existsSync(path.join(fixtureDir, 'fuse'))).toBe(true)
// TODO: the TS-LSP does not work in this process, we might want to
// add a safeguard to the client dev command to ensure that the
// types can work even if the LSP is not running.
//expect(
// existsSync(path.join(fixtureDir, 'fuse', 'introspection.ts')),
//).toBe(true)
//expect(existsSync(path.join(fixtureDir, 'fuse', 'tada.ts'))).toBe(true)
expect(existsSync(path.join(fixtureDir, 'fuse', 'index.ts'))).toBe(true)
}, 10_000)
}

test('Should run the build command', async () => {
process = execa('pnpm', ['fuse', 'build', '--server'], {
cwd: fixtureDir,
Expand Down
12 changes: 12 additions & 0 deletions packages/core/test/fixtures/tada/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "@fuse-fixtures/tada",
"private": true,
"version": "0.0.0",
"type": "module",
"devDependencies": {
"@0no-co/graphqlsp": "1.3.3",
"fuse": "workspace:*",
"typescript": "^5.2.2",
"gql.tada": "1.2.1"
}
}
21 changes: 21 additions & 0 deletions packages/core/test/fixtures/tada/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020"],
"module": "ESNext",
"skipLibCheck": true,
"plugins": [
{
"name": "@0no-co/graphqlsp",
"schema": "./schema.graphql",
"tadaOutputLocation": "./fuse/introspection.ts"
}
],
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
}
}
35 changes: 35 additions & 0 deletions packages/core/test/fixtures/tada/types/Test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { node } from 'fuse'

type UserSource = {
id: string
name: string
avatar_url: string
}

// "Nodes" are the core abstraction of Fuse. Each node represents
// a resource/entity with multiple fields and has to define two things:
// 1. load(): How to fetch from the underlying data source
// 2. fields: What fields should be exposed and added for clients
export const UserNode = node<UserSource>({
name: 'User',
load: async (ids) => getUsers(ids),
fields: (t) => ({
name: t.exposeString('name'),
// rename to camel-case
avatarUrl: t.exposeString('avatar_url'),
// Add an additional firstName field
firstName: t.string({
resolve: (user) => user.name.split(' ')[0],
}),
}),
})

// Fake function to fetch users. In real applications, this would
// talk to an underlying REST API/gRPC service/third-party API/…
async function getUsers(ids: string[]): Promise<UserSource[]> {
return ids.map((id) => ({
id,
name: `Peter #${id}`,
avatar_url: `https://i.pravatar.cc/300?u=${id}`,
}))
}
Loading

0 comments on commit fc8689b

Please sign in to comment.