From af9a78deb33a728cd2d398b79f991954eca958e0 Mon Sep 17 00:00:00 2001 From: Arda TANRIKULU Date: Wed, 7 Jul 2021 21:18:55 +0300 Subject: [PATCH] handle glob in loaders and return Source[] instead of Source (#3157) * Handle glob in loaders and return Source[] instead of Source * Fix TS * include rawSDL in Source of plucked files (#3161) Co-authored-by: Laurin Quast --- .changeset/blue-ducks-stare.md | 20 ++ .changeset/khaki-balloons-check.md | 5 + packages/load/package.json | 6 +- packages/load/src/load-typedefs.ts | 19 +- .../load/src/load-typedefs/collect-sources.ts | 280 +----------------- packages/load/src/load-typedefs/load-file.ts | 4 +- packages/load/src/load-typedefs/parse.ts | 8 +- packages/load/src/utils/pointers.ts | 10 +- .../documents/documents-from-glob.spec.ts | 4 +- packages/loaders/apollo-engine/src/index.ts | 16 +- packages/loaders/code-file/src/index.ts | 112 ++++--- .../tests/load-from-code-file.spec.ts | 73 +++-- .../tests/schema-from-export.spec.ts | 25 +- .../tests/test-files/multiple-from-file.ts | 25 ++ packages/loaders/git/package.json | 1 + packages/loaders/git/src/index.ts | 85 +++--- .../tests/__snapshots__/loader.spec.ts.snap | 8 + packages/loaders/git/tests/loader.spec.ts | 10 +- packages/loaders/github/src/index.ts | 22 +- .../github/tests/schema-from-github.spec.ts | 6 +- packages/loaders/graphql-file/src/index.ts | 70 +++-- .../loaders/graphql-file/tests/loader.spec.ts | 9 +- packages/loaders/json-file/src/index.ts | 25 +- .../loaders/json-file/tests/loader.spec.ts | 8 +- packages/loaders/module/src/index.ts | 17 +- packages/loaders/module/tests/loader.spec.ts | 10 +- packages/loaders/url/package.json | 5 +- packages/loaders/url/src/index.ts | 27 +- packages/loaders/url/tests/url-loader.spec.ts | 26 +- packages/utils/src/loaders.ts | 40 +-- yarn.lock | 14 +- 31 files changed, 414 insertions(+), 576 deletions(-) create mode 100644 .changeset/blue-ducks-stare.md create mode 100644 .changeset/khaki-balloons-check.md create mode 100644 packages/loaders/code-file/tests/test-files/multiple-from-file.ts diff --git a/.changeset/blue-ducks-stare.md b/.changeset/blue-ducks-stare.md new file mode 100644 index 00000000000..376bf91ace4 --- /dev/null +++ b/.changeset/blue-ducks-stare.md @@ -0,0 +1,20 @@ +--- +'@graphql-tools/load': major +'@graphql-tools/apollo-engine-loader': major +'@graphql-tools/code-file-loader': major +'@graphql-tools/git-loader': major +'@graphql-tools/github-loader': major +'@graphql-tools/graphql-file-loader': major +'@graphql-tools/json-file-loader': major +'@graphql-tools/module-loader': major +'@graphql-tools/url-loader': major +'@graphql-tools/utils': major +--- + +BREAKING CHANGE + +- Now each loader handles glob patterns internally and returns an array of `Source` object instead of single `Source` + +- GraphQL Tag Pluck now respects code locations and returns graphql-js `Source` objects for each found code block + +- Thanks to the one above, `CodeFileLoader` now returns different `Source` objects for each found SDL code block. diff --git a/.changeset/khaki-balloons-check.md b/.changeset/khaki-balloons-check.md new file mode 100644 index 00000000000..94f22de2def --- /dev/null +++ b/.changeset/khaki-balloons-check.md @@ -0,0 +1,5 @@ +--- +'@graphql-tools/code-file-loader': minor +--- + +include rawSDL in Source of plucked files diff --git a/packages/load/package.json b/packages/load/package.json index 65438569349..86c747f81dd 100644 --- a/packages/load/package.json +++ b/packages/load/package.json @@ -30,8 +30,6 @@ "graphql": "^14.0.0 || ^15.0.0" }, "devDependencies": { - "@types/is-glob": "4.0.2", - "@types/valid-url": "1.0.3", "graphql-tag": "2.12.5", "graphql-type-json": "0.3.2" }, @@ -39,10 +37,8 @@ "@graphql-tools/utils": "^7.5.0", "@graphql-tools/merge": "^6.2.12", "import-from": "4.0.0", - "is-glob": "4.0.1", "p-limit": "3.1.0", - "tslib": "~2.3.0", - "valid-url": "1.0.9" + "tslib": "~2.3.0" }, "publishConfig": { "access": "public", diff --git a/packages/load/src/load-typedefs.ts b/packages/load/src/load-typedefs.ts index defb838b0af..2defd60cd59 100644 --- a/packages/load/src/load-typedefs.ts +++ b/packages/load/src/load-typedefs.ts @@ -1,4 +1,4 @@ -import { Source, SingleFileOptions, Loader, compareStrings } from '@graphql-tools/utils'; +import { Source, BaseLoaderOptions, Loader, compareStrings, asArray } from '@graphql-tools/utils'; import { normalizePointers } from './utils/pointers'; import { applyDefaultOptions } from './load-typedefs/options'; import { collectSources, collectSourcesSync } from './load-typedefs/collect-sources'; @@ -7,12 +7,11 @@ import { useLimit } from './utils/helpers'; const CONCURRENCY_LIMIT = 100; -export type LoadTypedefsOptions = SingleFileOptions & +export type LoadTypedefsOptions = BaseLoaderOptions & ExtraConfig & { cache?: { [key: string]: Source }; loaders: Loader[]; filterKinds?: string[]; - ignore?: string | string[]; sort?: boolean; }; @@ -29,8 +28,10 @@ export async function loadTypedefs>( pointerOrPointers: UnnormalizedTypeDefPointer | UnnormalizedTypeDefPointer[], options: LoadTypedefsOptions> ): Promise { - const pointerOptionMap = normalizePointers(pointerOrPointers); - const globOptions: any = {}; + const { ignore, pointerOptionMap } = normalizePointers(pointerOrPointers); + + options.ignore = asArray(options.ignore || []); + options.ignore.push(...ignore); applyDefaultOptions(options); @@ -50,7 +51,6 @@ export async function loadTypedefs>( parseSource({ partialSource, options, - globOptions, pointerOptionMap, addValidSource(source) { validSources.push(source); @@ -74,8 +74,10 @@ export function loadTypedefsSync>( pointerOrPointers: UnnormalizedTypeDefPointer | UnnormalizedTypeDefPointer[], options: LoadTypedefsOptions> ): Source[] { - const pointerOptionMap = normalizePointers(pointerOrPointers); - const globOptions: any = {}; + const { ignore, pointerOptionMap } = normalizePointers(pointerOrPointers); + + options.ignore = asArray(options.ignore || []); + options.ignore.push(...ignore); applyDefaultOptions(options); @@ -90,7 +92,6 @@ export function loadTypedefsSync>( parseSource({ partialSource, options, - globOptions, pointerOptionMap, addValidSource(source) { validSources.push(source); diff --git a/packages/load/src/load-typedefs/collect-sources.ts b/packages/load/src/load-typedefs/collect-sources.ts index 1bdd8e02d6e..1a330a4319d 100644 --- a/packages/load/src/load-typedefs/collect-sources.ts +++ b/packages/load/src/load-typedefs/collect-sources.ts @@ -1,15 +1,5 @@ -import { - Source, - isDocumentString, - parseGraphQLSDL, - asArray, - getDocumentNodeFromSchema, - Loader, - ResolverGlobs, - isSome, -} from '@graphql-tools/utils'; +import { Source, isDocumentString, parseGraphQLSDL, getDocumentNodeFromSchema } from '@graphql-tools/utils'; import { isSchema, Kind } from 'graphql'; -import isGlob from 'is-glob'; import { LoadTypedefsOptions } from '../load-typedefs'; import { loadFile, loadFileSync } from './load-file'; import { stringToHash, useStack, StackNext, StackFn } from '../utils/helpers'; @@ -17,7 +7,6 @@ import { useCustomLoader, useCustomLoaderSync } from '../utils/custom-loader'; import { useQueue, useSyncQueue } from '../utils/queue'; type AddSource = (data: { pointer: string; source: Source; noCache?: boolean }) => void; -type AddGlob = (data: { pointer: string; pointerOptions: any }) => void; type AddToQueue = (fn: () => Promise | T) => void; const CONCURRENCY_LIMIT = 50; @@ -32,16 +21,12 @@ export async function collectSources({ options: LoadTypedefsOptions>; }): Promise { const sources: Source[] = []; - const globs: string[] = []; - const globOptions: any = {}; const queue = useQueue({ concurrency: CONCURRENCY_LIMIT }); - const { addSource, addGlob, collect } = createHelpers({ + const { addSource, collect } = createHelpers({ sources, - globs, options, - globOptions, - stack: [collectDocumentString, collectGlob, collectCustomLoader, collectFallback], + stack: [collectDocumentString, collectCustomLoader, collectFallback], }); for (const pointer in pointerOptionMap) { @@ -53,21 +38,6 @@ export async function collectSources({ pointerOptionMap, options, addSource, - addGlob, - queue: queue.add as AddToQueue, - }); - } - - if (globs.length) { - // TODO: use the queue? - const paths = await collectPathsFromGlobs(globs, options); - - collectSourcesFromGlobals({ - filepaths: paths, - options, - globOptions, - pointerOptionMap, - addSource, queue: queue.add as AddToQueue, }); } @@ -87,16 +57,12 @@ export function collectSourcesSync({ options: LoadTypedefsOptions>; }): Source[] { const sources: Source[] = []; - const globs: string[] = []; - const globOptions: any = {}; const queue = useSyncQueue(); - const { addSource, addGlob, collect } = createHelpers({ + const { addSource, collect } = createHelpers({ sources, - globs, options, - globOptions, - stack: [collectDocumentString, collectGlob, collectCustomLoaderSync, collectFallbackSync], + stack: [collectDocumentString, collectCustomLoaderSync, collectFallbackSync], }); for (const pointer in pointerOptionMap) { @@ -108,20 +74,6 @@ export function collectSourcesSync({ pointerOptionMap, options, addSource, - addGlob, - queue: queue.add, - }); - } - - if (globs.length) { - const paths = collectPathsFromGlobsSync(globs, options); - - collectSourcesFromGlobalsSync({ - filepaths: paths, - options, - globOptions, - pointerOptionMap, - addSource, queue: queue.add, }); } @@ -133,15 +85,11 @@ export function collectSourcesSync({ function createHelpers({ sources, - globs, options, - globOptions, stack, }: { sources: Source[]; - globs: string[]; options: LoadTypedefsOptions>; - globOptions: any; stack: StackFn>[]; }) { const addSource: AddSource = ({ @@ -162,209 +110,18 @@ function createHelpers({ const collect = useStack(...stack); - const addGlob: AddGlob = ({ pointerOptions, pointer }) => { - globs.push(pointer); - Object.assign(globOptions, pointerOptions); - }; - return { addSource, collect, - addGlob, }; } -async function addGlobsToLoaders({ - options, - loadersForGlobs, - globs, - type, -}: { - options: LoadTypedefsOptions; - loadersForGlobs: Map; - globs: string[]; - type: 'globs' | 'ignores'; -}) { - for (const glob of globs) { - let loader; - for await (const candidateLoader of options.loaders) { - if (candidateLoader.resolveGlobs && (await candidateLoader.canLoad(glob, options))) { - loader = candidateLoader; - break; - } - } - if (!loader) { - throw new Error(`unable to find loader for glob "${glob}"`); - } - let resolverGlobs = loadersForGlobs.get(loader); - if (!isSome(resolverGlobs)) { - resolverGlobs = { globs: [], ignores: [] }; - loadersForGlobs.set(loader, resolverGlobs); - } - resolverGlobs[type].push(glob); - } -} - -function addGlobsToLoadersSync({ - options, - loadersForGlobs, - globs, - type, -}: { - options: LoadTypedefsOptions; - loadersForGlobs: Map; - globs: string[]; - type: 'globs' | 'ignores'; -}) { - for (const glob of globs) { - let loader; - for (const candidateLoader of options.loaders) { - if ( - isSome(candidateLoader.resolveGlobsSync) && - isSome(candidateLoader.canLoadSync) && - candidateLoader.canLoadSync(glob, options) - ) { - loader = candidateLoader; - break; - } - } - if (!loader) { - throw new Error(`unable to find loader for glob "${glob}"`); - } - let resolverGlobs = loadersForGlobs.get(loader); - if (!isSome(resolverGlobs)) { - resolverGlobs = { globs: [], ignores: [] }; - loadersForGlobs.set(loader, resolverGlobs); - } - resolverGlobs[type].push(glob); - } -} - -async function collectPathsFromGlobs(globs: string[], options: LoadTypedefsOptions): Promise { - const paths: string[] = []; - - const loadersForGlobs: Map = new Map(); - - await addGlobsToLoaders({ options, loadersForGlobs, globs, type: 'globs' }); - await addGlobsToLoaders({ - options, - loadersForGlobs, - globs: isSome(options.ignore) ? asArray(options.ignore) : [], - type: 'ignores', - }); - - for await (const [loader, globsAndIgnores] of loadersForGlobs.entries()) { - if (isSome(loader.resolveGlobs)) { - const resolvedPaths = await loader.resolveGlobs(globsAndIgnores, options); - if (resolvedPaths) { - paths.push(...resolvedPaths); - } - } - } - - return paths; -} - -function collectPathsFromGlobsSync(globs: string[], options: LoadTypedefsOptions): string[] { - const paths: string[] = []; - - const loadersForGlobs: Map = new Map(); - - addGlobsToLoadersSync({ options, loadersForGlobs, globs, type: 'globs' }); - addGlobsToLoadersSync({ - options, - loadersForGlobs, - globs: isSome(options.ignore) ? asArray(options.ignore) : [], - type: 'ignores', - }); - - for (const [loader, globsAndIgnores] of loadersForGlobs.entries()) { - if (isSome(loader.resolveGlobsSync)) { - const resolvedPaths = loader.resolveGlobsSync(globsAndIgnores, options); - if (resolvedPaths) { - paths.push(...resolvedPaths); - } - } - } - - return paths; -} - -function collectSourcesFromGlobals({ - filepaths, - options, - globOptions, - pointerOptionMap, - addSource, - queue, -}: { - filepaths: string[]; - options: LoadTypedefsOptions>; - globOptions: any; - pointerOptionMap: P; - addSource: AddSource; - queue: AddToQueue; -}) { - const collectFromGlobs = useStack(collectCustomLoader, collectFallback); - - for (let i = 0; i < filepaths.length; i++) { - const pointer = filepaths[i]; - - collectFromGlobs({ - pointer, - pointerOptions: globOptions, - pointerOptionMap, - options, - addSource, - addGlob: () => { - throw new Error(`I don't accept any new globs!`); - }, - queue, - }); - } -} - -function collectSourcesFromGlobalsSync({ - filepaths, - options, - globOptions, - pointerOptionMap, - addSource, - queue, -}: { - filepaths: string[]; - options: LoadTypedefsOptions>; - globOptions: any; - pointerOptionMap: P; - addSource: AddSource; - queue: AddToQueue; -}) { - const collectFromGlobs = useStack(collectCustomLoaderSync, collectFallbackSync); - - for (let i = 0; i < filepaths.length; i++) { - const pointer = filepaths[i]; - - collectFromGlobs({ - pointer, - pointerOptions: globOptions, - pointerOptionMap, - options, - addSource, - addGlob: () => { - throw new Error(`I don't accept any new globs!`); - }, - queue, - }); - } -} - type CollectOptions = { pointer: string; pointerOptions: any; options: LoadTypedefsOptions>; pointerOptionMap: Record; addSource: AddSource; - addGlob: AddGlob; queue: AddToQueue; }; @@ -427,17 +184,6 @@ function collectDocumentString( next(); } -function collectGlob({ pointer, pointerOptions, addGlob }: CollectOptions, next: StackNext) { - if (isGlob(pointer)) { - return addGlob({ - pointer, - pointerOptions, - }); - } - - next(); -} - function collectCustomLoader( { pointer, pointerOptions, queue, addSource, options, pointerOptionMap }: CollectOptions, next: StackNext @@ -482,26 +228,30 @@ function collectCustomLoaderSync( function collectFallback({ queue, pointer, options, pointerOptions, addSource }: CollectOptions) { return queue(async () => { - const source = await loadFile(pointer, { + const sources = await loadFile(pointer, { ...options, ...pointerOptions, }); - if (source) { - addSource({ source, pointer }); + if (sources) { + for (const source of sources) { + addSource({ source, pointer }); + } } }); } function collectFallbackSync({ queue, pointer, options, pointerOptions, addSource }: CollectOptions) { return queue(() => { - const source = loadFileSync(pointer, { + const sources = loadFileSync(pointer, { ...options, ...pointerOptions, }); - if (source) { - addSource({ source, pointer }); + if (sources) { + for (const source of sources) { + addSource({ source, pointer }); + } } }); } diff --git a/packages/load/src/load-typedefs/load-file.ts b/packages/load/src/load-typedefs/load-file.ts index 7f6acf067e8..ee7944a7426 100644 --- a/packages/load/src/load-typedefs/load-file.ts +++ b/packages/load/src/load-typedefs/load-file.ts @@ -2,7 +2,7 @@ import { Source, Maybe } from '@graphql-tools/utils'; import { env } from 'process'; import { LoadTypedefsOptions } from '../load-typedefs'; -export async function loadFile(pointer: string, options: LoadTypedefsOptions): Promise> { +export async function loadFile(pointer: string, options: LoadTypedefsOptions): Promise> { const cached = useCache({ pointer, options }); if (cached) { @@ -28,7 +28,7 @@ export async function loadFile(pointer: string, options: LoadTypedefsOptions): P return undefined; } -export function loadFileSync(pointer: string, options: LoadTypedefsOptions): Maybe { +export function loadFileSync(pointer: string, options: LoadTypedefsOptions): Maybe { const cached = useCache({ pointer, options }); if (cached) { diff --git a/packages/load/src/load-typedefs/parse.ts b/packages/load/src/load-typedefs/parse.ts index 87ae377be06..ca85e03d9c3 100644 --- a/packages/load/src/load-typedefs/parse.ts +++ b/packages/load/src/load-typedefs/parse.ts @@ -11,17 +11,15 @@ type AddValidSource = (source: Source) => void; type ParseOptions = { partialSource: Source; options: any; - globOptions: any; pointerOptionMap: any; addValidSource: AddValidSource; }; -export function parseSource({ partialSource, options, globOptions, pointerOptionMap, addValidSource }: ParseOptions) { +export function parseSource({ partialSource, options, pointerOptionMap, addValidSource }: ParseOptions) { if (partialSource) { const input = prepareInput({ source: partialSource, options, - globOptions, pointerOptionMap, }); @@ -41,12 +39,10 @@ export function parseSource({ partialSource, options, globOptions, pointerOption function prepareInput({ source, options, - globOptions, pointerOptionMap, }: { source: Source; options: any; - globOptions: any; pointerOptionMap: any; }): Input { let specificOptions = { @@ -56,7 +52,7 @@ function prepareInput({ if (source.location) { specificOptions = { ...specificOptions, - ...(source.location in pointerOptionMap ? globOptions : pointerOptionMap[source.location]), + ...pointerOptionMap[source.location], }; } diff --git a/packages/load/src/utils/pointers.ts b/packages/load/src/utils/pointers.ts index 87534c20b33..1261576e905 100644 --- a/packages/load/src/utils/pointers.ts +++ b/packages/load/src/utils/pointers.ts @@ -4,10 +4,15 @@ import { UnnormalizedTypeDefPointer } from './../load-typedefs'; export function normalizePointers( unnormalizedPointerOrPointers: UnnormalizedTypeDefPointer | UnnormalizedTypeDefPointer[] ) { - return asArray(unnormalizedPointerOrPointers).reduce<{ [key: string]: any }>( + const ignore: string[] = []; + const pointerOptionMap = asArray(unnormalizedPointerOrPointers).reduce<{ [key: string]: any }>( (normalizedPointers, unnormalizedPointer) => { if (typeof unnormalizedPointer === 'string') { - normalizedPointers[unnormalizedPointer] = {}; + if (unnormalizedPointer.startsWith('!')) { + ignore.push(unnormalizedPointer.replace('!', '')); + } else { + normalizedPointers[unnormalizedPointer] = {}; + } } else if (typeof unnormalizedPointer === 'object') { Object.assign(normalizedPointers, unnormalizedPointer); } else { @@ -18,4 +23,5 @@ export function normalizePointers( }, {} ); + return { ignore, pointerOptionMap }; } 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 f7fe2ce3532..d8ebedf3283 100644 --- a/packages/load/tests/loaders/documents/documents-from-glob.spec.ts +++ b/packages/load/tests/loaders/documents/documents-from-glob.spec.ts @@ -34,10 +34,8 @@ describe('documentsFromGlob', () => { const result = await load(glob, { loaders: [new CodeFileLoader()] }); - const { document } = result[0]; - const operations = document && separateOperations(document); - expect(operations && Object.keys(operations)).toHaveLength(2); + expect(result).toHaveLength(2); }); test(`Should load GraphQL documents that match custom settings`, async () => { diff --git a/packages/loaders/apollo-engine/src/index.ts b/packages/loaders/apollo-engine/src/index.ts index 2c1c4b47ba5..854eb7a06d7 100644 --- a/packages/loaders/apollo-engine/src/index.ts +++ b/packages/loaders/apollo-engine/src/index.ts @@ -1,11 +1,11 @@ -import { SchemaLoader, Source, SingleFileOptions, parseGraphQLSDL, AggregateError } from '@graphql-tools/utils'; +import { Source, parseGraphQLSDL, AggregateError, BaseLoaderOptions, Loader } from '@graphql-tools/utils'; import { fetch } from 'cross-fetch'; import syncFetch from 'sync-fetch'; /** * Additional options for loading from Apollo Engine */ -export interface ApolloEngineOptions extends SingleFileOptions { +export interface ApolloEngineOptions extends BaseLoaderOptions { engine: { endpoint?: string; apiKey: string; @@ -20,7 +20,7 @@ const DEFAULT_APOLLO_ENDPOINT = 'https://engine-graphql.apollographql.com/api/gr /** * This loader loads a schema from Apollo Engine */ -export class ApolloEngineLoader implements SchemaLoader { +export class ApolloEngineLoader implements Loader { loaderId() { return 'apollo-engine'; } @@ -58,7 +58,7 @@ export class ApolloEngineLoader implements SchemaLoader { return typeof ptr === 'string' && ptr === 'apollo-engine'; } - async load(pointer: 'apollo-engine', options: ApolloEngineOptions): Promise { + async load(pointer: 'apollo-engine', options: ApolloEngineOptions): Promise { const fetchArgs = this.getFetchArgs(options); const response = await fetch(...fetchArgs); @@ -68,10 +68,11 @@ export class ApolloEngineLoader implements SchemaLoader { throw new AggregateError(errors, 'Introspection from Apollo Engine failed'); } - return parseGraphQLSDL(pointer, data.service.schema.document, options); + const source = parseGraphQLSDL(pointer, data.service.schema.document, options); + return [source]; } - loadSync(pointer: 'apollo-engine', options: ApolloEngineOptions): Source { + loadSync(pointer: 'apollo-engine', options: ApolloEngineOptions): Source[] { const fetchArgs = this.getFetchArgs(options); const response = syncFetch(...fetchArgs); @@ -81,7 +82,8 @@ export class ApolloEngineLoader implements SchemaLoader { throw new AggregateError(errors, 'Introspection from Apollo Engine failed'); } - return parseGraphQLSDL(pointer, data.service.schema.document, options); + const source = parseGraphQLSDL(pointer, data.service.schema.document, options); + return [source]; } } diff --git a/packages/loaders/code-file/src/index.ts b/packages/loaders/code-file/src/index.ts index 1299c2c61f7..68274a89587 100644 --- a/packages/loaders/code-file/src/index.ts +++ b/packages/loaders/code-file/src/index.ts @@ -1,17 +1,14 @@ import type { GlobbyOptions } from 'globby'; -import { isSchema, GraphQLSchema, DocumentNode, concatAST, parse } from 'graphql'; +import { isSchema, GraphQLSchema, DocumentNode, parse } from 'graphql'; import { - SchemaPointerSingle, - DocumentPointerSingle, - SingleFileOptions, Source, - UniversalLoader, asArray, isValidPath, parseGraphQLSDL, isDocumentNode, - ResolverGlobs, + BaseLoaderOptions, + Loader, } from '@graphql-tools/utils'; import { GraphQLTagPluckOptions, @@ -37,7 +34,7 @@ export type CodeFileLoaderOptions = { pluckConfig?: GraphQLTagPluckOptions; noPluck?: boolean; noRequire?: boolean; -} & SingleFileOptions; +} & BaseLoaderOptions; const FILE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.vue']; @@ -59,15 +56,12 @@ function createGlobbyOptions(options: CodeFileLoaderOptions): GlobbyOptions { * * Supported extensions include: `.ts`, `.tsx`, `.js`, `.jsx`, `.vue` */ -export class CodeFileLoader implements UniversalLoader { +export class CodeFileLoader implements Loader { loaderId(): string { return 'code-file'; } - async canLoad( - pointer: SchemaPointerSingle | DocumentPointerSingle, - options: CodeFileLoaderOptions - ): Promise { + async canLoad(pointer: string, options: CodeFileLoaderOptions): Promise { if (isGlob(pointer)) { // FIXME: parse to find and check the file extensions? return true; @@ -88,7 +82,7 @@ export class CodeFileLoader implements UniversalLoader { return false; } - canLoadSync(pointer: SchemaPointerSingle | DocumentPointerSingle, options: CodeFileLoaderOptions): boolean { + canLoadSync(pointer: string, options: CodeFileLoaderOptions): boolean { if (isGlob(pointer)) { // FIXME: parse to find and check the file extensions? return true; @@ -104,25 +98,52 @@ export class CodeFileLoader implements UniversalLoader { return false; } - async resolveGlobs({ globs, ignores }: ResolverGlobs, options: CodeFileLoaderOptions) { - return globby( - globs.concat(ignores.map(v => `!(${v})`)).map(v => unixify(v)), - createGlobbyOptions(options) - ); + async resolveGlobs(glob: string, options: CodeFileLoaderOptions) { + const ignores = asArray(options.ignore || []); + return globby([glob, ...ignores.map(v => `!(${v})`).map(v => unixify(v))], createGlobbyOptions(options)); } - resolveGlobsSync({ globs, ignores }: ResolverGlobs, options: CodeFileLoaderOptions) { - return globby.sync( - globs.concat(ignores.map(v => `!(${v})`)).map(v => unixify(v)), - createGlobbyOptions(options) - ); + resolveGlobsSync(glob: string, options: CodeFileLoaderOptions) { + const ignores = asArray(options.ignore || []); + return globby.sync([glob, ...ignores.map(v => `!(${v})`).map(v => unixify(v))], createGlobbyOptions(options)); } - async load( - pointer: SchemaPointerSingle | DocumentPointerSingle, - options: CodeFileLoaderOptions - ): Promise { - const normalizedFilePath = ensureAbsolutePath(pointer, options); + async load(pointer: string, options: CodeFileLoaderOptions): Promise { + if (isGlob(pointer)) { + const resolvedPaths = await this.resolveGlobs(pointer, options); + const finalResult: Source[] = []; + await Promise.all( + resolvedPaths.map(async path => { + if (await this.canLoad(path, options)) { + const result = await this.handleSinglePath(path, options); + result?.forEach(result => finalResult.push(result)); + } + }) + ); + return finalResult; + } + + return this.handleSinglePath(pointer, options); + } + + loadSync(pointer: string, options: CodeFileLoaderOptions): Source[] | null { + if (isGlob(pointer)) { + const resolvedPaths = this.resolveGlobsSync(pointer, options); + const finalResult: Source[] = []; + for (const path of resolvedPaths) { + if (this.canLoadSync(path, options)) { + const result = this.handleSinglePathSync(path, options); + result?.forEach(result => finalResult.push(result)); + } + } + return finalResult; + } + + return this.handleSinglePathSync(pointer, options); + } + + async handleSinglePath(location: string, options: CodeFileLoaderOptions): Promise { + const normalizedFilePath = ensureAbsolutePath(location, options); const errors: Error[] = []; @@ -132,10 +153,11 @@ export class CodeFileLoader implements UniversalLoader { const sources = await gqlPluckFromCodeString(normalizedFilePath, content, options.pluckConfig); if (sources.length) { - return { - document: concatAST(sources.map(source => parse(source, options))), - location: pointer, - }; + return sources.map(source => ({ + document: parse(source, options), + location, + rawSDL: source.body, + })); } } catch (e) { if (env['DEBUG']) { @@ -152,10 +174,10 @@ export class CodeFileLoader implements UniversalLoader { } const loaded = await tryToLoadFromExport(normalizedFilePath); - const source = resolveSource(pointer, loaded, options); + const source = resolveSource(location, loaded, options); if (source) { - return source; + return [source]; } } catch (e) { errors.push(e); @@ -169,8 +191,8 @@ export class CodeFileLoader implements UniversalLoader { return null; } - loadSync(pointer: SchemaPointerSingle | DocumentPointerSingle, options: CodeFileLoaderOptions): Source | null { - const normalizedFilePath = ensureAbsolutePath(pointer, options); + handleSinglePathSync(location: string, options: CodeFileLoaderOptions): Source[] | null { + const normalizedFilePath = ensureAbsolutePath(location, options); const errors: Error[] = []; @@ -180,10 +202,11 @@ export class CodeFileLoader implements UniversalLoader { const sources = gqlPluckFromCodeStringSync(normalizedFilePath, content, options.pluckConfig); if (sources.length) { - return { - document: concatAST(sources.map(source => parse(source, options))), - location: pointer, - }; + return sources.map(source => ({ + document: parse(source, options), + location, + rawSDL: source.body, + })); } } catch (e) { if (env['DEBUG']) { @@ -203,10 +226,10 @@ export class CodeFileLoader implements UniversalLoader { } const loaded = tryToLoadFromExportSync(normalizedFilePath); - const source = resolveSource(pointer, loaded, options); + const source = resolveSource(location, loaded, options); if (source) { - return source; + return [source]; } } catch (e) { errors.push(e); @@ -243,9 +266,6 @@ function resolveSource( return null; } -function ensureAbsolutePath( - pointer: SchemaPointerSingle | DocumentPointerSingle, - options: CodeFileLoaderOptions -): string { +function ensureAbsolutePath(pointer: string, options: CodeFileLoaderOptions): string { return isAbsolute(pointer) ? pointer : resolve(options.cwd || cwd(), pointer); } 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 06e273905a3..88e36b88ea0 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 @@ -8,10 +8,11 @@ describe('loadFromCodeFile', () => { it('Should throw an error when a document is loaded using AST and the document is not valid', async () => { try { - const loaded = await loader.load('./test-files/invalid-anon-doc.js', { + const result = await loader.load('./test-files/invalid-anon-doc.js', { noRequire: true, - cwd: __dirname + cwd: __dirname, }); + const loaded = result?.[0]; const doc = loaded?.document ? loaded?.document : parse(loaded?.rawSDL!); expect(doc).toBeFalsy(); @@ -21,39 +22,43 @@ describe('loadFromCodeFile', () => { }); it('should load a valid file', async () => { - const loaded = await loader.load('./test-files/valid-doc.js', { + const result = await loader.load('./test-files/valid-doc.js', { noRequire: true, - cwd: __dirname + cwd: __dirname, }); + const loaded = result?.[0]; const doc = loaded?.document ? loaded?.document : parse(loaded?.rawSDL!); expect(doc?.kind).toEqual('Document'); }); it('should consider options.cwd', async () => { - const loaded = await loader.load('valid-doc.js', { + const result = await loader.load('valid-doc.js', { cwd: path.resolve(__dirname, 'test-files'), noRequire: true, }); + const loaded = result?.[0]; const doc = loaded?.document ? loaded?.document : parse(loaded?.rawSDL!); expect(doc?.kind).toEqual('Document'); }); it('should load a TypeScript file using decorator', async () => { - const loaded = await loader.load('./test-files/with-decorator-doc.ts', { + const result = await loader.load('./test-files/with-decorator-doc.ts', { noRequire: true, - cwd: __dirname + cwd: __dirname, }); + const loaded = result?.[0]; const doc = loaded?.document ? loaded?.document : parse(loaded?.rawSDL!); expect(doc?.kind).toEqual('Document'); }); it('should support string interpolation', async () => { - const loaded = await loader.load('./test-files/string-interpolation.js', { - cwd: __dirname + const result = await loader.load('./test-files/string-interpolation.js', { + cwd: __dirname, }); + const loaded = result?.[0]; const doc = loaded?.document ? loaded?.document : parse(loaded?.rawSDL!); expect(doc?.kind).toEqual('Document'); @@ -65,43 +70,77 @@ describe('loadFromCodeFileSync', () => { it('Should throw an error when a document is loaded using AST and the document is not valid', () => { expect(() => { - const loaded = loader.loadSync('./test-files/invalid-anon-doc.js', { + const result = loader.loadSync('./test-files/invalid-anon-doc.js', { noRequire: true, - cwd: __dirname + cwd: __dirname, }); + const loaded = result?.[0]; const doc = loaded?.document ? loaded?.document : parse(loaded?.rawSDL!); expect(doc?.kind).toEqual('Document'); - }).toThrowError('Syntax Error: Unexpected Name "InvalidGetUser"') + }).toThrowError('Syntax Error: Unexpected Name "InvalidGetUser"'); }); it('should load a valid file', () => { - const loaded = loader.loadSync('./test-files/valid-doc.js', { + const result = loader.loadSync('./test-files/valid-doc.js', { noRequire: true, - cwd: __dirname + cwd: __dirname, }); + const loaded = result?.[0]; const doc = loaded?.document; expect(doc?.kind).toEqual('Document'); }); it('should consider options.cwd', () => { - const loaded = loader.loadSync('valid-doc.js', { + const result = loader.loadSync('valid-doc.js', { cwd: path.resolve(__dirname, 'test-files'), noRequire: true, }); + const loaded = result?.[0]; const doc = loaded?.document; expect(doc?.kind).toEqual('Document'); }); it('should support string interpolation', () => { - const loaded = loader.loadSync('./test-files/string-interpolation.js', { - cwd: __dirname + const result = loader.loadSync('./test-files/string-interpolation.js', { + cwd: __dirname, }); + const loaded = result?.[0]; const doc = loaded?.document; expect(doc?.kind).toEqual('Document'); }); + + it('should support loading many in same file', () => { + 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 + } + }" + `); + expect(loaded?.[1].rawSDL).toBeDefined(); + expect(loaded?.[1].rawSDL).toMatchInlineSnapshot(` +"fragment Lel on Tweet { + id + body +}" +`); + expect(loaded?.[2].rawSDL).toBeDefined(); + expect(loaded?.[2].rawSDL).toMatchInlineSnapshot(` +"query Bar { + Tweets { + ...Lel + } +}" +`); + }); }); diff --git a/packages/loaders/code-file/tests/schema-from-export.spec.ts b/packages/loaders/code-file/tests/schema-from-export.spec.ts index 6cc205700c7..ceb708b0ac4 100644 --- a/packages/loaders/code-file/tests/schema-from-export.spec.ts +++ b/packages/loaders/code-file/tests/schema-from-export.spec.ts @@ -7,35 +7,35 @@ describe('Schema From Export', () => { const result = await loader.load('./test-files/loaders/module-exports.js', { cwd: __dirname }); - expect(result).toBeDefined(); + expect(result?.[0]).toBeDefined(); }); it('should load the schema (with extend) correctly from module.exports', async () => { const result = await loader.load('./test-files/loaders/with-extend.js', { cwd: __dirname }); - expect(result).toBeDefined(); + expect(result?.[0]).toBeDefined(); }); it('should load the schema correctly from variable export', async () => { const result = await loader.load('./test-files/loaders/schema-export.js', { cwd: __dirname }); - expect(result).toBeDefined(); + expect(result?.[0]).toBeDefined(); }); it('should load the schema correctly from default export', async () => { const result = await loader.load('./test-files/loaders/default-export.js', { cwd: __dirname }); - expect(result).toBeDefined(); + expect(result?.[0]).toBeDefined(); }); it('should load the schema correctly from promise export', async () => { const result = await loader.load('./test-files/loaders/promise-export.js', { cwd: __dirname }); - expect(result).toBeDefined(); + expect(result?.[0]).toBeDefined(); }); }); @@ -46,34 +46,27 @@ describe('Schema From Export (sync)', () => { const result = loader.loadSync('./test-files/loaders/module-exports.js', { cwd: __dirname }); - expect(result).toBeDefined(); + expect(result?.[0]).toBeDefined(); }); it('should load the schema (with extend) correctly from module.exports', () => { const result = loader.loadSync('./test-files/loaders/with-extend.js', { cwd: __dirname }); - expect(result).toBeDefined(); + expect(result?.[0]).toBeDefined(); }); it('should load the schema correctly from variable export', () => { const result = loader.loadSync('./test-files/loaders/schema-export.js', { cwd: __dirname }); - expect(result).toBeDefined(); + expect(result?.[0]).toBeDefined(); }); it('should load the schema correctly from default export', () => { const result = loader.loadSync('./test-files/loaders/default-export.js', { cwd: __dirname }); - expect(result).toBeDefined(); - }); - - it('should load the schema correctly from promise export', () => { - const result = loader.loadSync('./test-files/loaders/promise-export.js', { - cwd: __dirname - }); - expect(result).toBeDefined(); + expect(result?.[0]).toBeDefined(); }); }); diff --git a/packages/loaders/code-file/tests/test-files/multiple-from-file.ts b/packages/loaders/code-file/tests/test-files/multiple-from-file.ts new file mode 100644 index 00000000000..aea200b0ee9 --- /dev/null +++ b/packages/loaders/code-file/tests/test-files/multiple-from-file.ts @@ -0,0 +1,25 @@ +//@ts-ignore +import { gql } from "@app/gql"; + +const FooQuery = gql(/* GraphQL */ ` + query Foo { + Tweets { + id + } + } +`); + +const LelFragment = gql(/* GraphQL */ ` + fragment Lel on Tweet { + id + body + } +`); + +const BarQuery = gql(/* GraphQL */ ` + query Bar { + Tweets { + ...Lel + } + } +`); diff --git a/packages/loaders/git/package.json b/packages/loaders/git/package.json index 146ee3b7402..c6104a55d7e 100644 --- a/packages/loaders/git/package.json +++ b/packages/loaders/git/package.json @@ -32,6 +32,7 @@ "dependencies": { "@graphql-tools/graphql-tag-pluck": "^6.2.6", "@graphql-tools/utils": "^7.0.0", + "is-glob": "4.0.1", "micromatch": "^4.0.4", "tslib": "~2.3.0", "unixify": "^1.0.0" diff --git a/packages/loaders/git/src/index.ts b/packages/loaders/git/src/index.ts index f5b3af9ee35..acc6f9ddba4 100644 --- a/packages/loaders/git/src/index.ts +++ b/packages/loaders/git/src/index.ts @@ -1,4 +1,3 @@ -import { UniversalLoader, SingleFileOptions, ResolverGlobs } from '@graphql-tools/utils'; import { GraphQLTagPluckOptions, gqlPluckFromCodeString, @@ -9,7 +8,9 @@ import unixify from 'unixify'; import { loadFromGit, loadFromGitSync, readTreeAtRef, readTreeAtRefSync } from './load-git'; import { parse as handleStuff } from './parse'; -import { concatAST, parse } from 'graphql'; +import { parse } from 'graphql'; +import { asArray, BaseLoaderOptions, Loader, Source } from '@graphql-tools/utils'; +import isGlob from 'is-glob'; // git:branch:path/to/file function extractData(pointer: string): { @@ -31,7 +32,7 @@ function extractData(pointer: string): { /** * Additional options for loading from git */ -export type GitLoaderOptions = SingleFileOptions & { +export type GitLoaderOptions = BaseLoaderOptions & { /** * Additional options to pass to `graphql-tag-pluck` */ @@ -47,7 +48,7 @@ export type GitLoaderOptions = SingleFileOptions & { * }) * ``` */ -export class GitLoader implements UniversalLoader { +export class GitLoader implements Loader { loaderId() { return 'git-loader'; } @@ -60,16 +61,14 @@ export class GitLoader implements UniversalLoader { return typeof pointer === 'string' && pointer.toLowerCase().startsWith('git:'); } - async resolveGlobs({ globs, ignores }: ResolverGlobs) { + async resolveGlobs(glob: string, ignores: string[]) { const refsForPaths = new Map(); - for (const glob of globs) { - const { ref, path } = extractData(glob); - if (!refsForPaths.has(ref)) { - refsForPaths.set(ref, []); - } - refsForPaths.get(ref).push(unixify(path)); + const { ref, path } = extractData(glob); + if (!refsForPaths.has(ref)) { + refsForPaths.set(ref, []); } + refsForPaths.get(ref).push(unixify(path)); for (const ignore of ignores) { const { ref, path } = extractData(ignore); @@ -80,22 +79,22 @@ export class GitLoader implements UniversalLoader { } const resolved: string[] = []; - for await (const [ref, paths] of refsForPaths.entries()) { - resolved.push(...micromatch(await readTreeAtRef(ref), paths).map(filePath => `git:${ref}:${filePath}`)); - } + await Promise.all( + [...refsForPaths.entries()].map(async ([ref, paths]) => { + resolved.push(...micromatch(await readTreeAtRef(ref), paths).map(filePath => `git:${ref}:${filePath}`)); + }) + ); return resolved; } - resolveGlobsSync({ globs, ignores }: ResolverGlobs) { + resolveGlobsSync(glob: string, ignores: string[]) { const refsForPaths = new Map(); - for (const glob of globs) { - const { ref, path } = extractData(glob); - if (!refsForPaths.has(ref)) { - refsForPaths.set(ref, []); - } - refsForPaths.get(ref).push(unixify(path)); + const { ref, path } = extractData(glob); + if (!refsForPaths.has(ref)) { + refsForPaths.set(ref, []); } + refsForPaths.get(ref).push(unixify(path)); for (const ignore of ignores) { const { ref, path } = extractData(ignore); @@ -112,41 +111,57 @@ export class GitLoader implements UniversalLoader { return resolved; } - async load(pointer: string, options: GitLoaderOptions) { + async load(pointer: string, options: GitLoaderOptions): Promise { const { ref, path } = extractData(pointer); + if (isGlob(path)) { + const resolvedPaths = await this.resolveGlobs(pointer, asArray(options.ignore || [])); + const finalResult: Source[] = []; + + await Promise.all( + resolvedPaths.map(async path => { + const results = await this.load(path, options); + results?.forEach(result => finalResult.push(result)); + }) + ); + } const content = await loadFromGit({ ref, path }); const parsed = handleStuff({ path, options, pointer, content }); if (parsed) { - return parsed; + return [parsed]; } const sources = await gqlPluckFromCodeString(pointer, content, options.pluckConfig); - const documents = sources.map(source => parse(source, options)); - - return { + return sources.map(source => ({ location: pointer, - document: concatAST(documents), - }; + document: parse(source, options), + })); } - loadSync(pointer: string, options: GitLoaderOptions) { + loadSync(pointer: string, options: GitLoaderOptions): Source[] { const { ref, path } = extractData(pointer); + if (isGlob(path)) { + const resolvedPaths = this.resolveGlobsSync(pointer, asArray(options.ignore || [])); + const finalResult: Source[] = []; + + resolvedPaths.forEach(path => { + const results = this.loadSync(path, options); + results?.forEach(result => finalResult.push(result)); + }); + } const content = loadFromGitSync({ ref, path }); const parsed = handleStuff({ path, options, pointer, content }); if (parsed) { - return parsed; + return [parsed]; } const sources = gqlPluckFromCodeStringSync(pointer, content, options.pluckConfig); - const documents = sources.map(source => parse(source, options)); - - return { + return sources.map(source => ({ location: pointer, - document: concatAST(documents), - }; + document: parse(source, options), + })); } } diff --git a/packages/loaders/git/tests/__snapshots__/loader.spec.ts.snap b/packages/loaders/git/tests/__snapshots__/loader.spec.ts.snap index f196ba19681..1b03a705348 100644 --- a/packages/loaders/git/tests/__snapshots__/loader.spec.ts.snap +++ b/packages/loaders/git/tests/__snapshots__/loader.spec.ts.snap @@ -58,6 +58,10 @@ Object { }, ], "kind": "Document", + "loc": Object { + "end": 30, + "start": 0, + }, } `; @@ -119,5 +123,9 @@ Object { }, ], "kind": "Document", + "loc": Object { + "end": 30, + "start": 0, + }, } `; diff --git a/packages/loaders/git/tests/loader.spec.ts b/packages/loaders/git/tests/loader.spec.ts index ba4969380ef..54b704f50fd 100644 --- a/packages/loaders/git/tests/loader.spec.ts +++ b/packages/loaders/git/tests/loader.spec.ts @@ -1,7 +1,5 @@ import { execSync } from 'child_process'; -import { Source } from '@graphql-tools/utils'; - import { GitLoader } from '../src'; import { runTests } from '../../../testing/utils'; @@ -43,22 +41,22 @@ describe('GitLoader', () => { sync: loader.loadSync.bind(loader), })(load => { it('should load document from a .graphql file', async () => { - const result: Source = await load(getPointer('type-defs.graphql'), {}); + const [result]= await load(getPointer('type-defs.graphql'), {}); expect(result.document).toBeDefined(); }); it('should load introspection data from a .json file', async () => { - const result: Source = await load(getPointer('introspection.json'), {}); + const [result]= await load(getPointer('introspection.json'), {}); expect(result.schema).toBeDefined(); }); it('should load type definitions from a .json file', async () => { - const result: Source = await load(getPointer('type-defs.json'), {}); + const [result]= await load(getPointer('type-defs.json'), {}); expect(result.document).toBeDefined(); }); it('should load type definitions from a pluckable file', async () => { - const result: Source = await load(getPointer('pluckable.ts'), {}); + const [result]= await load(getPointer('pluckable.ts'), {}); expect(result.document).toMatchSnapshot(); }); diff --git a/packages/loaders/github/src/index.ts b/packages/loaders/github/src/index.ts index 983f972b8b9..cf28b8cdc25 100644 --- a/packages/loaders/github/src/index.ts +++ b/packages/loaders/github/src/index.ts @@ -1,7 +1,7 @@ -import { UniversalLoader, parseGraphQLSDL, parseGraphQLJSON, SingleFileOptions } from '@graphql-tools/utils'; +import { Loader, parseGraphQLSDL, parseGraphQLJSON, BaseLoaderOptions, Source } from '@graphql-tools/utils'; import { fetch } from 'cross-fetch'; import { GraphQLTagPluckOptions, gqlPluckFromCodeString } from '@graphql-tools/graphql-tag-pluck'; -import { concatAST, parse } from 'graphql'; +import { parse } from 'graphql'; // github:owner/name#ref:path/to/file function extractData(pointer: string): { @@ -25,7 +25,7 @@ function extractData(pointer: string): { /** * Additional options for loading from GitHub */ -export interface GithubLoaderOptions extends SingleFileOptions { +export interface GithubLoaderOptions extends BaseLoaderOptions { /** * A GitHub access token */ @@ -46,7 +46,7 @@ export interface GithubLoaderOptions extends SingleFileOptions { * }) * ``` */ -export class GithubLoader implements UniversalLoader { +export class GithubLoader implements Loader { loaderId() { return 'github-loader'; } @@ -59,7 +59,7 @@ export class GithubLoader implements UniversalLoader { return false; } - async load(pointer: string, options: GithubLoaderOptions) { + async load(pointer: string, options: GithubLoaderOptions): Promise { const { owner, name, ref, path } = extractData(pointer); const request = await fetch('https://api.github.com/graphql', { method: 'POST', @@ -104,19 +104,19 @@ export class GithubLoader implements UniversalLoader { const content = response.data.repository.object.text; if (/\.(gql|graphql)s?$/i.test(path)) { - return parseGraphQLSDL(pointer, content, options); + return [parseGraphQLSDL(pointer, content, options)]; } if (/\.json$/i.test(path)) { - return parseGraphQLJSON(pointer, content, options); + return [parseGraphQLJSON(pointer, content, options)]; } if (path.endsWith('.tsx') || path.endsWith('.ts') || path.endsWith('.js') || path.endsWith('.jsx')) { const sources = await gqlPluckFromCodeString(pointer, content, options.pluckConfig); - return { - location: path, - document: concatAST(sources.map(source => parse(source, options))), - }; + return sources.map(source => ({ + location: pointer, + document: parse(source, options), + })); } throw new Error(`Invalid file extension: ${path}`); diff --git a/packages/loaders/github/tests/schema-from-github.spec.ts b/packages/loaders/github/tests/schema-from-github.spec.ts index 5c9e150ebb1..b7020962b11 100644 --- a/packages/loaders/github/tests/schema-from-github.spec.ts +++ b/packages/loaders/github/tests/schema-from-github.spec.ts @@ -57,7 +57,7 @@ test('load schema from GitHub', async () => { const loader = new GithubLoader(); - const schema = await loader.load(pointer, { + const [source] = await loader.load(pointer, { token, }); @@ -94,7 +94,7 @@ test('load schema from GitHub', async () => { // name expect(params.operationName).toEqual('GetGraphQLSchemaForGraphQLtools'); - assertNonMaybe(schema.document) + assertNonMaybe(source.document) // schema - expect(print(schema.document)).toEqual(printSchema(buildSchema(typeDefs))); + expect(print(source.document)).toEqual(printSchema(buildSchema(typeDefs))); }); diff --git a/packages/loaders/graphql-file/src/index.ts b/packages/loaders/graphql-file/src/index.ts index 6a036156527..79dd1bdc391 100644 --- a/packages/loaders/graphql-file/src/index.ts +++ b/packages/loaders/graphql-file/src/index.ts @@ -1,15 +1,6 @@ import type { GlobbyOptions } from 'globby'; -import { - Source, - UniversalLoader, - DocumentPointerSingle, - SchemaPointerSingle, - isValidPath, - parseGraphQLSDL, - SingleFileOptions, - ResolverGlobs, -} from '@graphql-tools/utils'; +import { Source, Loader, isValidPath, parseGraphQLSDL, BaseLoaderOptions, asArray } from '@graphql-tools/utils'; import { isAbsolute, resolve } from 'path'; import { readFileSync, promises as fsPromises, existsSync } from 'fs'; import { cwd as processCwd } from 'process'; @@ -25,7 +16,7 @@ const FILE_EXTENSIONS = ['.gql', '.gqls', '.graphql', '.graphqls']; /** * Additional options for loading from a GraphQL file */ -export interface GraphQLFileLoaderOptions extends SingleFileOptions { +export interface GraphQLFileLoaderOptions extends BaseLoaderOptions { /** * Set to `true` to disable handling `#import` syntax */ @@ -64,15 +55,12 @@ function createGlobbyOptions(options: GraphQLFileLoaderOptions): GlobbyOptions { * }); * ``` */ -export class GraphQLFileLoader implements UniversalLoader { +export class GraphQLFileLoader implements Loader { loaderId(): string { return 'graphql-file'; } - async canLoad( - pointer: SchemaPointerSingle | DocumentPointerSingle, - options: GraphQLFileLoaderOptions - ): Promise { + async canLoad(pointer: string, options: GraphQLFileLoaderOptions): Promise { if (isGlob(pointer)) { // FIXME: parse to find and check the file extensions? return true; @@ -93,7 +81,7 @@ export class GraphQLFileLoader implements UniversalLoader `!(${v})`)).map(v => unixify(v)), - createGlobbyOptions(options) - ); + async resolveGlobs(glob: string, options: GraphQLFileLoaderOptions) { + const ignores = asArray(options.ignore || []); + return globby([glob, ...ignores.map(v => `!(${v})`).map(v => unixify(v))], createGlobbyOptions(options)); } - resolveGlobsSync({ globs, ignores }: ResolverGlobs, options: GraphQLFileLoaderOptions) { - return globby.sync( - globs.concat(ignores.map(v => `!(${v})`)).map(v => unixify(v)), - createGlobbyOptions(options) - ); + resolveGlobsSync(glob: string, options: GraphQLFileLoaderOptions) { + const ignores = asArray(options.ignore || []); + return globby.sync([glob, ...ignores.map(v => `!(${v})`).map(v => unixify(v))], createGlobbyOptions(options)); } - async load(pointer: SchemaPointerSingle | DocumentPointerSingle, options: GraphQLFileLoaderOptions): Promise { + async load(pointer: string, options: GraphQLFileLoaderOptions): Promise { + if (isGlob(pointer)) { + const resolvedPaths = await this.resolveGlobs(pointer, options); + const finalResult: Source[] = []; + await Promise.all( + resolvedPaths.map(async path => { + if (await this.canLoad(path, options)) { + const result = await this.load(path, options); + result?.forEach(result => finalResult.push(result)); + } + }) + ); + return finalResult; + } const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || processCwd(), pointer); const rawSDL: string = await readFile(normalizedFilePath, { encoding: 'utf8' }); - return this.handleFileContent(rawSDL, normalizedFilePath, options); + return [this.handleFileContent(rawSDL, normalizedFilePath, options)]; } - loadSync(pointer: SchemaPointerSingle | DocumentPointerSingle, options: GraphQLFileLoaderOptions): Source { + loadSync(pointer: string, options: GraphQLFileLoaderOptions): Source[] { + if (isGlob(pointer)) { + const resolvedPaths = this.resolveGlobsSync(pointer, options); + const finalResult: Source[] = []; + for (const path of resolvedPaths) { + if (this.canLoadSync(path, options)) { + const result = this.loadSync(path, options); + result?.forEach(result => finalResult.push(result)); + } + } + return finalResult; + } const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || processCwd(), pointer); const rawSDL = readFileSync(normalizedFilePath, { encoding: 'utf8' }); - return this.handleFileContent(rawSDL, normalizedFilePath, options); + return [this.handleFileContent(rawSDL, normalizedFilePath, options)]; } handleFileContent(rawSDL: string, pointer: string, options: GraphQLFileLoaderOptions) { diff --git a/packages/loaders/graphql-file/tests/loader.spec.ts b/packages/loaders/graphql-file/tests/loader.spec.ts index ccfcc914d9d..05069ddc290 100644 --- a/packages/loaders/graphql-file/tests/loader.spec.ts +++ b/packages/loaders/graphql-file/tests/loader.spec.ts @@ -1,7 +1,6 @@ import { join } from 'path'; import { print } from 'graphql' -import { Source } from '@graphql-tools/utils'; import { GraphQLFileLoader } from '../src'; import { runTests } from '../../../testing/utils'; @@ -52,17 +51,17 @@ describe('GraphQLFileLoader', () => { sync: loader.loadSync.bind(loader), })(load => { it('should load type definitions from a .graphql file', async () => { - const result: Source = await load(getPointer('type-defs.graphql'), {}); + const [result] = await load(getPointer('type-defs.graphql'), {}); expect(result.document).toBeDefined(); }); it('should load file from absolute path', async () => { - const result: Source = await load(join(process.cwd(), getPointer('type-defs.graphql')), {}); + const [result] = await load(join(process.cwd(), getPointer('type-defs.graphql')), {}); expect(result.document).toBeDefined(); }); it('should load type definitions document with #import expression', async () => { - const result: Source = await load(getPointer('type-defs-with-import.graphql'), {}); + const [result] = await load(getPointer('type-defs-with-import.graphql'), {}); expect(print(result.document!)).toBeSimilarGqlDoc(/* GraphQL */` type Query { a: A @@ -75,7 +74,7 @@ describe('GraphQLFileLoader', () => { }); it('should load executable document with #import expression', async () => { - const result: Source = await load(getPointer('executable.graphql'), {}); + const [result] = await load(getPointer('executable.graphql'), {}); expect(print(result.document!)).toBeSimilarGqlDoc(/* GraphQL */` query MyQuery { a { diff --git a/packages/loaders/json-file/src/index.ts b/packages/loaders/json-file/src/index.ts index a9feb325935..69d0a14c2c8 100644 --- a/packages/loaders/json-file/src/index.ts +++ b/packages/loaders/json-file/src/index.ts @@ -1,11 +1,4 @@ -import { - Source, - parseGraphQLJSON, - SchemaPointerSingle, - DocumentLoader, - isValidPath, - SingleFileOptions, -} from '@graphql-tools/utils'; +import { Source, parseGraphQLJSON, Loader, isValidPath, BaseLoaderOptions } from '@graphql-tools/utils'; import { isAbsolute, resolve } from 'path'; import { readFileSync, promises as fsPromises, existsSync } from 'fs'; import { cwd } from 'process'; @@ -17,7 +10,7 @@ const FILE_EXTENSIONS = ['.json']; /** * Additional options for loading from a JSON file */ -export interface JsonFileLoaderOptions extends SingleFileOptions {} +export interface JsonFileLoaderOptions extends BaseLoaderOptions {} /** * This loader loads documents and type definitions from JSON files. @@ -42,12 +35,12 @@ export interface JsonFileLoaderOptions extends SingleFileOptions {} * }); * ``` */ -export class JsonFileLoader implements DocumentLoader { +export class JsonFileLoader implements Loader { loaderId(): string { return 'json-file'; } - async canLoad(pointer: SchemaPointerSingle, options: JsonFileLoaderOptions): Promise { + async canLoad(pointer: string, options: JsonFileLoaderOptions): Promise { if (isValidPath(pointer)) { if (FILE_EXTENSIONS.find(extension => pointer.endsWith(extension))) { const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || cwd(), pointer); @@ -63,7 +56,7 @@ export class JsonFileLoader implements DocumentLoader { return false; } - canLoadSync(pointer: SchemaPointerSingle, options: JsonFileLoaderOptions): boolean { + canLoadSync(pointer: string, options: JsonFileLoaderOptions): boolean { if (isValidPath(pointer)) { if (FILE_EXTENSIONS.find(extension => pointer.endsWith(extension))) { const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || cwd(), pointer); @@ -75,23 +68,23 @@ export class JsonFileLoader implements DocumentLoader { return false; } - async load(pointer: SchemaPointerSingle, options: JsonFileLoaderOptions): Promise { + async load(pointer: string, options: JsonFileLoaderOptions): Promise { const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || cwd(), pointer); try { const jsonContent: string = await readFile(normalizedFilePath, { encoding: 'utf8' }); - return parseGraphQLJSON(pointer, jsonContent, options); + return [parseGraphQLJSON(pointer, jsonContent, options)]; } catch (e) { throw new Error(`Unable to read JSON file: ${normalizedFilePath}: ${e.message || /* istanbul ignore next */ e}`); } } - loadSync(pointer: SchemaPointerSingle, options: JsonFileLoaderOptions): Source { + loadSync(pointer: string, options: JsonFileLoaderOptions): Source[] { const normalizedFilepath = isAbsolute(pointer) ? pointer : resolve(options.cwd || cwd(), pointer); try { const jsonContent = readFileSync(normalizedFilepath, 'utf8'); - return parseGraphQLJSON(pointer, jsonContent, options); + return [parseGraphQLJSON(pointer, jsonContent, options)]; } catch (e) { throw new Error(`Unable to read JSON file: ${normalizedFilepath}: ${e.message || /* istanbul ignore next */ e}`); } diff --git a/packages/loaders/json-file/tests/loader.spec.ts b/packages/loaders/json-file/tests/loader.spec.ts index 4f14e46daf4..7492bae5602 100644 --- a/packages/loaders/json-file/tests/loader.spec.ts +++ b/packages/loaders/json-file/tests/loader.spec.ts @@ -1,7 +1,5 @@ import { join } from 'path'; -import { Source } from '@graphql-tools/utils'; - import { JsonFileLoader } from '../src'; import { runTests } from '../../../testing/utils'; @@ -50,17 +48,17 @@ describe('JsonFileLoader', () => { sync: loader.loadSync.bind(loader), })(load => { it('should load introspection data from a .json file', async () => { - const result: Source = await load(getPointer('introspection.json'), {}); + const [result] = await load(getPointer('introspection.json'), {}); expect(result.schema).toBeDefined(); }); it('should load type definitions from a .json file', async () => { - const result: Source = await load(getPointer('type-defs.json'), {}); + const [result] = await load(getPointer('type-defs.json'), {}); expect(result.document).toBeDefined(); }); it('should load file from absolute path', async () => { - const result: Source = await load(join(process.cwd(), getPointer('type-defs.json')), {}); + const [result] = await load(join(process.cwd(), getPointer('type-defs.json')), {}); expect(result.document).toBeDefined(); }); diff --git a/packages/loaders/module/src/index.ts b/packages/loaders/module/src/index.ts index 3ed2e72a53b..db7840a5770 100644 --- a/packages/loaders/module/src/index.ts +++ b/packages/loaders/module/src/index.ts @@ -1,8 +1,5 @@ import { DocumentNode, GraphQLSchema, isSchema } from 'graphql'; -import { - UniversalLoader, - Source, -} from '@graphql-tools/utils'; +import { Loader, Source } from '@graphql-tools/utils'; import { existsSync, promises as fsPromises } from 'fs'; const { access } = fsPromises; @@ -12,9 +9,7 @@ const createLoadError = (error: any) => new Error('Unable to load schema from module: ' + `${error.message || /* istanbul ignore next */ error}`); // module:node/module#export -function extractData( - pointer: string -): { +function extractData(pointer: string): { modulePath: string; exportName?: string; } { @@ -39,7 +34,7 @@ function extractData( * }) * ``` */ -export class ModuleLoader implements UniversalLoader { +export class ModuleLoader implements Loader { loaderId() { return 'module-loader'; } @@ -68,7 +63,7 @@ export class ModuleLoader implements UniversalLoader { try { const moduleAbsolutePath = require.resolve(modulePath); return existsSync(moduleAbsolutePath); - } catch(e) { + } catch (e) { return false; } } @@ -80,7 +75,7 @@ export class ModuleLoader implements UniversalLoader { const result = this.parse(pointer, await this.importModule(pointer)); if (result) { - return result; + return [result]; } throw InvalidError; @@ -94,7 +89,7 @@ export class ModuleLoader implements UniversalLoader { const result = this.parse(pointer, this.importModuleSync(pointer)); if (result) { - return result; + return [result]; } throw InvalidError; diff --git a/packages/loaders/module/tests/loader.spec.ts b/packages/loaders/module/tests/loader.spec.ts index b5b821dfd90..220af74d42f 100644 --- a/packages/loaders/module/tests/loader.spec.ts +++ b/packages/loaders/module/tests/loader.spec.ts @@ -1,7 +1,5 @@ import { join } from 'path'; -import { Source } from '@graphql-tools/utils'; - import { ModuleLoader } from '../src'; import { runTests } from '../../../testing/utils'; @@ -43,22 +41,22 @@ describe('ModuleLoader', () => { sync: loader.loadSync.bind(loader), })(load => { it('should load GraphQLSchema object from a file', async () => { - const result: Source = await load(getPointer('schema')); + const [result] = await load(getPointer('schema')); expect(result.schema).toBeDefined(); }); it('should load DocumentNode object from a file', async () => { - const result: Source = await load(getPointer('type-defs')); + const [result] = await load(getPointer('type-defs')); expect(result.document).toBeDefined(); }); it('should load string from a file', async () => { - const result: Source = await load(getPointer('type-defs-string')); + const [result] = await load(getPointer('type-defs-string')); expect(result.rawSDL).toBeDefined(); }); it('should load using a named export', async () => { - const result: Source = await load(getPointer('type-defs-named-export', 'typeDefs')); + const [result] = await load(getPointer('type-defs-named-export', 'typeDefs')); expect(result.document).toBeDefined(); }); diff --git a/packages/loaders/url/package.json b/packages/loaders/url/package.json index c43092cd922..f7d526b6767 100644 --- a/packages/loaders/url/package.json +++ b/packages/loaders/url/package.json @@ -11,8 +11,8 @@ "license": "MIT", "sideEffects": false, "main": "dist/index.js", - "module": "dist/index.mjs", - "exports": { + "module": "dist/index.mjs", + "exports": { ".": { "require": "./dist/index.js", "import": "./dist/index.mjs" @@ -35,6 +35,7 @@ "graphql": "^14.0.0 || ^15.0.0" }, "devDependencies": { + "@types/valid-url": "1.0.3", "@types/extract-files": "8.1.1", "@types/ws": "7.4.6", "graphql-upload": "12.0.0", diff --git a/packages/loaders/url/src/index.ts b/packages/loaders/url/src/index.ts index f5cd4c9f5cf..f2411f107d7 100644 --- a/packages/loaders/url/src/index.ts +++ b/packages/loaders/url/src/index.ts @@ -6,10 +6,9 @@ import { AsyncExecutor, Executor, SyncExecutor, - SchemaPointerSingle, Source, - DocumentLoader, - SingleFileOptions, + Loader, + BaseLoaderOptions, observableToAsyncIterable, isAsyncIterable, ExecutionParams, @@ -86,7 +85,7 @@ export enum SubscriptionProtocol { /** * Additional options for loading from a URL */ -export interface LoadFromUrlOptions extends SingleFileOptions, Partial { +export interface LoadFromUrlOptions extends BaseLoaderOptions, Partial { /** * Additional headers to include when querying the original schema */ @@ -147,16 +146,16 @@ export interface LoadFromUrlOptions extends SingleFileOptions, Partial { +export class UrlLoader implements Loader { loaderId(): string { return 'url'; } - async canLoad(pointer: SchemaPointerSingle, options: LoadFromUrlOptions): Promise { + async canLoad(pointer: string, options: LoadFromUrlOptions): Promise { return this.canLoadSync(pointer, options); } - canLoadSync(pointer: SchemaPointerSingle, _options: LoadFromUrlOptions): boolean { + canLoadSync(pointer: string, _options: LoadFromUrlOptions): boolean { return !!isWebUri(pointer); } @@ -619,9 +618,9 @@ export class UrlLoader implements DocumentLoader { return executor; } - handleSDL(pointer: SchemaPointerSingle, fetch: SyncFetchFn, options: LoadFromUrlOptions): Source; - handleSDL(pointer: SchemaPointerSingle, fetch: AsyncFetchFn, options: LoadFromUrlOptions): Promise; - handleSDL(pointer: SchemaPointerSingle, fetch: FetchFn, options: LoadFromUrlOptions): Source | Promise { + handleSDL(pointer: string, fetch: SyncFetchFn, options: LoadFromUrlOptions): Source; + handleSDL(pointer: string, fetch: AsyncFetchFn, options: LoadFromUrlOptions): Promise; + handleSDL(pointer: string, fetch: FetchFn, options: LoadFromUrlOptions): Source | Promise { const defaultMethod = this.getDefaultMethodFromOptions(options?.method, 'GET'); return new ValueOrPromise(() => fetch(pointer, { @@ -634,7 +633,7 @@ export class UrlLoader implements DocumentLoader { .resolve(); } - async load(pointer: SchemaPointerSingle, options: LoadFromUrlOptions): Promise { + async load(pointer: string, options: LoadFromUrlOptions): Promise { let source: Source = { location: pointer, }; @@ -669,10 +668,10 @@ export class UrlLoader implements DocumentLoader { executor, }); - return source; + return [source]; } - loadSync(pointer: SchemaPointerSingle, options: LoadFromUrlOptions): Source { + loadSync(pointer: string, options: LoadFromUrlOptions): Source[] { let source: Source = { location: pointer, }; @@ -707,7 +706,7 @@ export class UrlLoader implements DocumentLoader { executor, }); - return source; + return [source]; } } diff --git a/packages/loaders/url/tests/url-loader.spec.ts b/packages/loaders/url/tests/url-loader.spec.ts index eb99a2e963e..659865c4e0e 100644 --- a/packages/loaders/url/tests/url-loader.spec.ts +++ b/packages/loaders/url/tests/url-loader.spec.ts @@ -133,7 +133,7 @@ input TestInput { it('Should return a valid schema when request is valid', async () => { scope = mockGraphQLServer({ schema: testSchema, host: testHost, path: testPathChecker }); - const source = await loader.load(testUrl, {}); + const [source] = await loader.load(testUrl, {}); assertNonMaybe(source.schema) expect(printSchemaWithDirectives(source.schema)).toBeSimilarGqlDoc(testTypeDefs); }); @@ -150,7 +150,7 @@ input TestInput { }, }); - const source = await loader.load(testUrl, {}); + const [source] = await loader.load(testUrl, {}); expect(source).toBeDefined(); assertNonMaybe(source.schema) @@ -171,7 +171,7 @@ input TestInput { }, }); - const source = await loader.load(testUrl, { headers: { Auth: '1' } }); + const [source] = await loader.load(testUrl, { headers: { Auth: '1' } }); expect(source).toBeDefined(); assertNonMaybe(source.schema) @@ -184,7 +184,7 @@ input TestInput { it('Should utilize extra introspection options', async () => { scope = mockGraphQLServer({ schema: testSchema, host: testHost, path: testPathChecker }); - const source = await loader.load(testUrl, { descriptions: false }); + const [source] = await loader.load(testUrl, { descriptions: false }); expect(source).toBeDefined(); assertNonMaybe(source.schema) @@ -197,7 +197,7 @@ input TestInput { it('should handle useGETForQueries correctly', async () => { scope = mockGraphQLServer({ schema: testSchema, host: testHost, path: testPathChecker, method: 'GET' }); - const source = await loader.load(testUrl, { + const [source] = await loader.load(testUrl, { descriptions: false, useGETForQueries: true, }); @@ -234,7 +234,7 @@ input TestInput { }; const url = address.host + address.path; scope = mockGraphQLServer({ schema: testSchema, host: address.host, path: address.path }); - const result = await loader.load(url, {}); + const [result] = await loader.load(url, {}); assertNonMaybe(result.schema) expect(printSchemaWithDirectives(result.schema)).toBeSimilarGqlDoc(testTypeDefs); @@ -251,7 +251,7 @@ input TestInput { host: address.host.replace('ws', 'http'), path: address.path, }); - const result = await loader.load(url, {}); + const [result] = await loader.load(url, {}); assertNonMaybe(result.schema) expect(printSchemaWithDirectives(result.schema)).toBeSimilarGqlDoc(testTypeDefs); @@ -268,7 +268,7 @@ input TestInput { host: address.host.replace('wss', 'https'), path: address.path, }); - const result = await loader.load(url, {}); + const [result] = await loader.load(url, {}); assertNonMaybe(result.schema) expect(printSchemaWithDirectives(result.schema)).toBeSimilarGqlDoc(testTypeDefs); @@ -277,7 +277,7 @@ input TestInput { const testHost = 'http://localhost:3000'; const testPath = '/schema.graphql'; scope = nock(testHost).get(testPath).reply(200, testTypeDefs); - const result = await loader.load(testHost + testPath, {}); + const [result] = await loader.load(testHost + testPath, {}); assertNonMaybe(result.document) expect(print(result.document)).toBeSimilarGqlDoc(testTypeDefs); @@ -286,7 +286,7 @@ input TestInput { const testHost = 'http://localhost:3000'; const testPath = '/sdl'; scope = nock(testHost).get(testPath).reply(200, testTypeDefs); - const result = await loader.load(testHost + testPath, { + const [result] = await loader.load(testHost + testPath, { handleAsSDL: true, }); @@ -296,7 +296,7 @@ input TestInput { it('should handle subscriptions - new protocol', (done) => { Promise.resolve().then(async () => { const testUrl = 'http://localhost:8081/graphql'; - const { schema } = await loader.load(testUrl, { + const [{ schema }] = await loader.load(testUrl, { customFetch: async () => ({ headers: { 'content-type': 'application/json' @@ -366,7 +366,7 @@ input TestInput { it('should handle subscriptions - legacy protocol', (done) => { Promise.resolve().then(async () => { const testUrl = 'http://localhost:8081/graphql'; - const { schema } = await loader.load(testUrl, { + const [{ schema }] = await loader.load(testUrl, { customFetch: async () => ({ headers: { 'content-type': 'application/json' @@ -433,7 +433,7 @@ input TestInput { it('should handle multipart requests', async () => { scope = mockGraphQLServer({ schema: testSchema, host: testHost, path: testPathChecker, method: 'POST' }); - const { schema } = await loader.load(testUrl, { + const [{ schema }] = await loader.load(testUrl, { multipart: true, }); diff --git a/packages/utils/src/loaders.ts b/packages/utils/src/loaders.ts index 56efc329de4..73b1d6fac24 100644 --- a/packages/utils/src/loaders.ts +++ b/packages/utils/src/loaders.ts @@ -8,45 +8,19 @@ export interface Source { location?: string; } -export type SingleFileOptions = GraphQLParseOptions & +export type BaseLoaderOptions = GraphQLParseOptions & BuildSchemaOptions & { cwd?: string; + ignore?: string | string[]; }; export type WithList = T | T[]; export type ElementOf = TList extends Array ? TElement : never; -export type SchemaPointer = WithList; -export type SchemaPointerSingle = ElementOf; -export type DocumentGlobPathPointer = string; -export type DocumentPointer = WithList; -export type DocumentPointerSingle = ElementOf; -export interface Loader { +export interface Loader { loaderId(): string; - canLoad(pointer: TPointer, options?: TOptions): Promise; - canLoadSync?(pointer: TPointer, options?: TOptions): boolean; - resolveGlobs?(globs: ResolverGlobs, options?: TOptions): Promise; - resolveGlobsSync?(globs: ResolverGlobs, options?: TOptions): TPointer[]; - load(pointer: TPointer, options?: TOptions): Promise; - loadSync?(pointer: TPointer, options?: TOptions): Source | null | never; + canLoad(pointer: string, options?: TOptions): Promise; + canLoadSync?(pointer: string, options?: TOptions): boolean; + load(pointer: string, options?: TOptions): Promise; + loadSync?(pointer: string, options?: TOptions): Source[] | null | never; } - -export type SchemaLoader = Loader< - SchemaPointerSingle, - TOptions ->; - -export type DocumentLoader = Loader< - DocumentPointerSingle, - TOptions ->; - -export type UniversalLoader = Loader< - SchemaPointerSingle | DocumentPointerSingle, - TOptions ->; - -export type ResolverGlobs = { - globs: string[]; - ignores: string[]; -}; diff --git a/yarn.lock b/yarn.lock index 6a55f4945f0..ad8e0795d0e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7599,13 +7599,6 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@4.0.1, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - is-glob@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" @@ -7620,6 +7613,13 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + is-hexadecimal@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7"