diff --git a/.changeset/metal-apes-sell.md b/.changeset/metal-apes-sell.md new file mode 100644 index 00000000000..ea3002604fc --- /dev/null +++ b/.changeset/metal-apes-sell.md @@ -0,0 +1,5 @@ +--- +'@graphql-tools/code-file-loader': minor +--- + +allow supplying config via constructor diff --git a/packages/load/src/load-typedefs/load-file.ts b/packages/load/src/load-typedefs/load-file.ts index ee7944a7426..237adce9ac0 100644 --- a/packages/load/src/load-typedefs/load-file.ts +++ b/packages/load/src/load-typedefs/load-file.ts @@ -1,4 +1,4 @@ -import { Source, Maybe } from '@graphql-tools/utils'; +import { Source, Maybe, isSome } from '@graphql-tools/utils'; import { env } from 'process'; import { LoadTypedefsOptions } from '../load-typedefs'; @@ -15,6 +15,9 @@ export async function loadFile(pointer: string, options: LoadTypedefsOptions): P if (canLoad) { const loadedValue = await loader.load(pointer, options); + if (!isSome(loadedValue) || loadedValue.length === 0) { + continue; + } return loadedValue; } } catch (error) { @@ -41,7 +44,11 @@ export function loadFileSync(pointer: string, options: LoadTypedefsOptions): May if (canLoad) { // We check for the existence so it is okay to force non null - return loader.loadSync!(pointer, options); + const loadedValue = loader.loadSync!(pointer, options); + if (!isSome(loadedValue) || loadedValue.length === 0) { + continue; + } + return loadedValue; } } catch (error) { if (env['DEBUG']) { diff --git a/packages/load/tests/loaders/documents/documents-from-glob.spec.ts b/packages/load/tests/loaders/documents/documents-from-glob.spec.ts index d8ebedf3283..27ddbe34fab 100644 --- a/packages/load/tests/loaders/documents/documents-from-glob.spec.ts +++ b/packages/load/tests/loaders/documents/documents-from-glob.spec.ts @@ -133,5 +133,12 @@ describe('documentsFromGlob', () => { }); expect(result.length).toBe(1); }); + test(`should try next loader if first one fails`, async () => { + const glob = join(__dirname, './test-with-brackets/', '**/*.ts'); + const result = await load(glob, { + loaders: [new GraphQLFileLoader(), new CodeFileLoader()], + }); + expect(result.length).toBe(1); + }) }) }); diff --git a/packages/loaders/code-file/src/index.ts b/packages/loaders/code-file/src/index.ts index 68274a89587..1ae63b12828 100644 --- a/packages/loaders/code-file/src/index.ts +++ b/packages/loaders/code-file/src/index.ts @@ -26,15 +26,19 @@ import { createRequire } from 'module'; const { readFile, access } = fsPromises; +export type CodeFileLoaderConfig = { + pluckConfig?: GraphQLTagPluckOptions; + noPluck?: boolean; + noRequire?: boolean; +}; + /** * Additional options for loading from a code file */ export type CodeFileLoaderOptions = { require?: string | string[]; - pluckConfig?: GraphQLTagPluckOptions; - noPluck?: boolean; - noRequire?: boolean; -} & BaseLoaderOptions; +} & CodeFileLoaderConfig & + BaseLoaderOptions; const FILE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.vue']; @@ -57,11 +61,21 @@ function createGlobbyOptions(options: CodeFileLoaderOptions): GlobbyOptions { * Supported extensions include: `.ts`, `.tsx`, `.js`, `.jsx`, `.vue` */ export class CodeFileLoader implements Loader { + private config: CodeFileLoaderConfig; + constructor(config?: CodeFileLoaderConfig) { + this.config = config ?? {}; + } + + private getMergedOptions(options: CodeFileLoaderOptions): CodeFileLoaderOptions { + return { ...this.config, ...options }; + } + loaderId(): string { return 'code-file'; } async canLoad(pointer: string, options: CodeFileLoaderOptions): Promise { + options = this.getMergedOptions(options); if (isGlob(pointer)) { // FIXME: parse to find and check the file extensions? return true; @@ -83,6 +97,7 @@ export class CodeFileLoader implements Loader { } canLoadSync(pointer: string, options: CodeFileLoaderOptions): boolean { + options = this.getMergedOptions(options); if (isGlob(pointer)) { // FIXME: parse to find and check the file extensions? return true; @@ -99,16 +114,19 @@ export class CodeFileLoader implements Loader { } async resolveGlobs(glob: string, options: CodeFileLoaderOptions) { + options = this.getMergedOptions(options); const ignores = asArray(options.ignore || []); return globby([glob, ...ignores.map(v => `!(${v})`).map(v => unixify(v))], createGlobbyOptions(options)); } resolveGlobsSync(glob: string, options: CodeFileLoaderOptions) { + options = this.getMergedOptions(options); const ignores = asArray(options.ignore || []); return globby.sync([glob, ...ignores.map(v => `!(${v})`).map(v => unixify(v))], createGlobbyOptions(options)); } async load(pointer: string, options: CodeFileLoaderOptions): Promise { + options = this.getMergedOptions(options); if (isGlob(pointer)) { const resolvedPaths = await this.resolveGlobs(pointer, options); const finalResult: Source[] = []; @@ -127,6 +145,7 @@ export class CodeFileLoader implements Loader { } loadSync(pointer: string, options: CodeFileLoaderOptions): Source[] | null { + options = this.getMergedOptions(options); if (isGlob(pointer)) { const resolvedPaths = this.resolveGlobsSync(pointer, options); const finalResult: Source[] = []; @@ -143,6 +162,7 @@ export class CodeFileLoader implements Loader { } async handleSinglePath(location: string, options: CodeFileLoaderOptions): Promise { + options = this.getMergedOptions(options); const normalizedFilePath = ensureAbsolutePath(location, options); const errors: Error[] = []; @@ -192,6 +212,7 @@ export class CodeFileLoader implements Loader { } handleSinglePathSync(location: string, options: CodeFileLoaderOptions): Source[] | null { + options = this.getMergedOptions(options); const normalizedFilePath = ensureAbsolutePath(location, options); const errors: Error[] = []; diff --git a/packages/loaders/code-file/tests/load-from-code-file.spec.ts b/packages/loaders/code-file/tests/load-from-code-file.spec.ts index 88e36b88ea0..109a27f16ac 100644 --- a/packages/loaders/code-file/tests/load-from-code-file.spec.ts +++ b/packages/loaders/code-file/tests/load-from-code-file.spec.ts @@ -117,30 +117,61 @@ describe('loadFromCodeFileSync', () => { it('should support loading many in same file', () => { const loaded = loader.loadSync('./test-files/multiple-from-file.ts', { cwd: __dirname, + pluckConfig: { + skipIndent: true, + }, }); expect(loaded?.length).toEqual(3); expect(loaded?.[0].rawSDL).toBeDefined(); expect(loaded?.[0].rawSDL).toMatchInlineSnapshot(` - "query Foo { - Tweets { - id + " + query Foo { + Tweets { + id + } } - }" + " `); expect(loaded?.[1].rawSDL).toBeDefined(); expect(loaded?.[1].rawSDL).toMatchInlineSnapshot(` -"fragment Lel on Tweet { - id - body -}" -`); + " + fragment Lel on Tweet { + id + body + } + " + `); expect(loaded?.[2].rawSDL).toBeDefined(); expect(loaded?.[2].rawSDL).toMatchInlineSnapshot(` -"query Bar { - Tweets { - ...Lel - } -}" -`); + " + query Bar { + Tweets { + ...Lel + } + } + " + `); }); + + it('can inherit config options from constructor', () => { + const loader = new CodeFileLoader({ + pluckConfig: { + skipIndent: true + } + }) + const loaded = loader.loadSync('./test-files/multiple-from-file.ts', { + cwd: __dirname, + }); + expect(loaded?.length).toEqual(3); + expect(loaded?.[0].rawSDL).toBeDefined(); + expect(loaded?.[0].rawSDL).toMatchInlineSnapshot(` + " + query Foo { + Tweets { + id + } + } + " + `); + }) });