Skip to content

Commit

Permalink
feat: support glob paths in schema (#33)
Browse files Browse the repository at this point in the history
closes #34
  • Loading branch information
Nnigmat authored Nov 23, 2024
1 parent 863a61a commit 2efef2e
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 8 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ codegen({
*/
matchOnDocuments?: boolean;
/**
* Run codegen when a schema matches. Only supports file path based schemas.
* Run codegen when a schema matches.
*
* @defaultValue `false`
*/
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export interface Options {
*/
matchOnDocuments?: boolean;
/**
* Run codegen when a schema matches. Only supports file path based schemas.
* Run codegen when a schema matches.
*
* @defaultValue `false`
*/
Expand Down
16 changes: 10 additions & 6 deletions src/utils/configPaths.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { normalizePath } from 'vite';
import { resolve } from 'node:path';
import type { CodegenContext } from '@graphql-codegen/cli';
import type { Types } from '@graphql-codegen/plugin-helpers/typings/types';

export async function getDocumentPaths(
context: CodegenContext,
Expand Down Expand Up @@ -44,15 +44,19 @@ export async function getSchemaPaths(
sourceSchemas.unshift(config.schema);
}

const schemas = sourceSchemas
const normalized = sourceSchemas
.filter((item): item is NonNullable<typeof item> => !!item)
.flat();

if (!schemas.length) return [];
if (!normalized.length) return [];

const schemas = await context.loadSchema(
// loadSchema supports array of string, but typings are wrong
normalized as unknown as Types.Schema,
);

return schemas
.filter((schema): schema is string => typeof schema === 'string')
return (schemas.extensions.sources as { name: string }[])
.map(({ name = '' }) => name)
.filter(Boolean)
.map((schema) => resolve(schema))
.map(normalizePath);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`match-on-glob-schema > generates on schema change 1`] = `
"export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };
export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: { input: string; output: string; }
String: { input: string; output: string; }
Boolean: { input: boolean; output: boolean; }
Int: { input: number; output: number; }
Float: { input: number; output: number; }
};
export type Query = {
__typename?: 'Query';
bar?: Maybe<Scalars['Int']['output']>;
baz?: Maybe<Scalars['Int']['output']>;
foo?: Maybe<Scalars['Int']['output']>;
qux?: Maybe<Scalars['Int']['output']>;
};
export type FooQueryVariables = Exact<{ [key: string]: never; }>;
export type FooQuery = { __typename?: 'Query', foo?: number | null };
"
`;
9 changes: 9 additions & 0 deletions test/match-on-glob-schema/codegen.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
schema:
- ./test/match-on-glob-schema/dir-1/**/*.graphql
- ./test/match-on-glob-schema/dir-2/**/*.graphql
documents: ./test/match-on-glob-schema/graphql/**/*.graphql
generates:
./test/match-on-glob-schema/generated/graphql.ts:
plugins:
- typescript
- typescript-operations
95 changes: 95 additions & 0 deletions test/match-on-glob-schema/match-on-glob-schema.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest';
import { createServer, UserConfig, ViteDevServer } from 'vite';
import { promises as fs } from 'node:fs';
import codegen from '../../src/index';

const TEST_PATH = './test/match-on-glob-schema' as const;
const DIR_PATH_1 = `${TEST_PATH}/dir-1` as const;
const DIR_PATH_2 = `${TEST_PATH}/dir-2` as const;
const SCHEMA_PATH_1 = `${DIR_PATH_1}/schema-1.graphql` as const;
const SCHEMA_PATH_2 = `${DIR_PATH_1}/schema-2.graphql` as const;
const SCHEMA_PATH_3 = `${DIR_PATH_2}/schema-1.graphql` as const;
const SCHEMA_PATH_4 = `${DIR_PATH_2}/schema-2.graphql` as const;
const DOCUMENT_PATH = `${TEST_PATH}/graphql` as const;
const OUTPUT_PATH = `${TEST_PATH}/generated` as const;
const OUTPUT_FILE_NAME = 'graphql.ts' as const;
const OUTPUT_FILE = `${OUTPUT_PATH}/${OUTPUT_FILE_NAME}` as const;

const viteConfig = {
plugins: [
codegen({
runOnStart: false,
matchOnDocuments: false,
matchOnSchemas: true,
configFilePathOverride: `${TEST_PATH}/codegen.yml`,
}),
],
} satisfies UserConfig;

describe('match-on-glob-schema', () => {
let viteServer: ViteDevServer | null = null;

const isFileGenerated = async (): Promise<boolean> => {
try {
await fs.access(OUTPUT_FILE);
return true;
} catch (error) {
// ignore
}

return new Promise((resolve, reject) => {
if (!viteServer) reject('Vite server not started');

viteServer?.watcher.on('add', (path: string) => {
if (path.includes(OUTPUT_FILE_NAME)) resolve(true);
});

setTimeout(() => reject('Generated file not found'), 5000);
});
};

beforeAll(async () => {
// Files in dir1
await fs.mkdir(DIR_PATH_1, { recursive: true });
await fs.writeFile(SCHEMA_PATH_1, 'type Query { foo: String }');
await fs.writeFile(SCHEMA_PATH_2, 'type Query { bar: String }');

// Files in dir2
await fs.mkdir(DIR_PATH_2, { recursive: true });
await fs.writeFile(SCHEMA_PATH_3, 'type Query { baz: String }');
await fs.writeFile(SCHEMA_PATH_4, 'type Query { qux: String }');

await fs.mkdir(DOCUMENT_PATH, { recursive: true });
await fs.writeFile(`${DOCUMENT_PATH}/Foo.graphql`, 'query Foo { foo }');
viteServer = await createServer(viteConfig).then((s) => s.listen());
});

afterAll(async () => {
await viteServer?.close();
viteServer = null;
await fs.rm(DIR_PATH_1, { recursive: true });
await fs.rm(DIR_PATH_2, { recursive: true });

await fs.rm(DOCUMENT_PATH, { recursive: true });
});

afterEach(async () => {
await fs.rm(OUTPUT_PATH, { recursive: true });
});

it('generates on schema change', async () => {
// Files in dir1
await fs.writeFile(SCHEMA_PATH_1, 'type Query { foo: Int }');
await fs.writeFile(SCHEMA_PATH_2, 'type Query { bar: Int }');

// Files in dir2
await fs.writeFile(SCHEMA_PATH_3, 'type Query { baz: Int }');
await fs.writeFile(SCHEMA_PATH_4, 'type Query { qux: Int }');

await isFileGenerated();

const file = await fs.readFile(OUTPUT_FILE, 'utf-8');

expect(file).toMatchSnapshot();
});
});

0 comments on commit 2efef2e

Please sign in to comment.