UNPKG

20 kBJavaScriptView Raw
1import { asArray, debugLog, printSchemaWithDirectives, isDocumentString, parseGraphQLSDL, fixSchemaAst, compareStrings } from '@graphql-tools/utils';
2import { cwd } from 'process';
3import { isSchema, parse, Kind, Source, print } from 'graphql';
4import isGlob from 'is-glob';
5import pLimit from 'p-limit';
6import importFrom from 'import-from';
7import unixify from 'unixify';
8import globby, { sync } from 'globby';
9import { printWithComments, resetComments, mergeSchemasAsync, mergeSchemas } from '@graphql-tools/merge';
10
11function normalizePointers(unnormalizedPointerOrPointers) {
12 return asArray(unnormalizedPointerOrPointers).reduce((normalizedPointers, unnormalizedPointer) => {
13 if (typeof unnormalizedPointer === 'string') {
14 normalizedPointers[unnormalizedPointer] = {};
15 }
16 else if (typeof unnormalizedPointer === 'object') {
17 Object.assign(normalizedPointers, unnormalizedPointer);
18 }
19 else {
20 throw new Error(`Invalid pointer ${unnormalizedPointer}`);
21 }
22 return normalizedPointers;
23 }, {});
24}
25
26function applyDefaultOptions(options) {
27 options.cache = options.cache || {};
28 options.cwd = options.cwd || cwd();
29 options.sort = 'sort' in options ? options.sort : true;
30}
31
32async function loadFile(pointer, options) {
33 const cached = useCache({ pointer, options });
34 if (cached) {
35 return cached;
36 }
37 for await (const loader of options.loaders) {
38 try {
39 const canLoad = await loader.canLoad(pointer, options);
40 if (canLoad) {
41 return await loader.load(pointer, options);
42 }
43 }
44 catch (error) {
45 debugLog(`Failed to find any GraphQL type definitions in: ${pointer} - ${error.message}`);
46 throw error;
47 }
48 }
49 return undefined;
50}
51function loadFileSync(pointer, options) {
52 const cached = useCache({ pointer, options });
53 if (cached) {
54 return cached;
55 }
56 for (const loader of options.loaders) {
57 try {
58 const canLoad = loader.canLoadSync && loader.loadSync && loader.canLoadSync(pointer, options);
59 if (canLoad) {
60 return loader.loadSync(pointer, options);
61 }
62 }
63 catch (error) {
64 debugLog(`Failed to find any GraphQL type definitions in: ${pointer} - ${error.message}`);
65 throw error;
66 }
67 }
68 return undefined;
69}
70function useCache({ pointer, options }) {
71 if (options['cache']) {
72 return options['cache'][pointer];
73 }
74}
75
76/**
77 * Converts a string to 32bit integer
78 */
79function stringToHash(str) {
80 let hash = 0;
81 if (str.length === 0) {
82 return hash;
83 }
84 let char;
85 for (let i = 0; i < str.length; i++) {
86 char = str.charCodeAt(i);
87 // tslint:disable-next-line: no-bitwise
88 hash = (hash << 5) - hash + char;
89 // tslint:disable-next-line: no-bitwise
90 hash = hash & hash;
91 }
92 return hash;
93}
94function useStack(...fns) {
95 return (input) => {
96 function createNext(i) {
97 if (i >= fns.length) {
98 return () => { };
99 }
100 return function next() {
101 fns[i](input, createNext(i + 1));
102 };
103 }
104 fns[0](input, createNext(1));
105 };
106}
107function useLimit(concurrency) {
108 return pLimit(concurrency);
109}
110
111function getCustomLoaderByPath(path, cwd) {
112 try {
113 const requiredModule = importFrom(cwd, path);
114 if (requiredModule) {
115 if (requiredModule.default && typeof requiredModule.default === 'function') {
116 return requiredModule.default;
117 }
118 if (typeof requiredModule === 'function') {
119 return requiredModule;
120 }
121 }
122 }
123 catch (e) { }
124 return null;
125}
126async function useCustomLoader(loaderPointer, cwd) {
127 let loader;
128 if (typeof loaderPointer === 'string') {
129 loader = await getCustomLoaderByPath(loaderPointer, cwd);
130 }
131 else if (typeof loaderPointer === 'function') {
132 loader = loaderPointer;
133 }
134 if (typeof loader !== 'function') {
135 throw new Error(`Failed to load custom loader: ${loaderPointer}`);
136 }
137 return loader;
138}
139function useCustomLoaderSync(loaderPointer, cwd) {
140 let loader;
141 if (typeof loaderPointer === 'string') {
142 loader = getCustomLoaderByPath(loaderPointer, cwd);
143 }
144 else if (typeof loaderPointer === 'function') {
145 loader = loaderPointer;
146 }
147 if (typeof loader !== 'function') {
148 throw new Error(`Failed to load custom loader: ${loaderPointer}`);
149 }
150 return loader;
151}
152
153function useQueue(options) {
154 const queue = [];
155 const limit = (options === null || options === void 0 ? void 0 : options.concurrency) ? pLimit(options.concurrency) : async (fn) => fn();
156 return {
157 add(fn) {
158 queue.push(() => limit(fn));
159 },
160 runAll() {
161 return Promise.all(queue.map(fn => fn()));
162 },
163 };
164}
165function useSyncQueue() {
166 const queue = [];
167 return {
168 add(fn) {
169 queue.push(fn);
170 },
171 runAll() {
172 queue.forEach(fn => fn());
173 },
174 };
175}
176
177const CONCURRENCY_LIMIT = 50;
178async function collectSources({ pointerOptionMap, options, }) {
179 var _a;
180 const sources = [];
181 const globs = [];
182 const globOptions = {};
183 const queue = useQueue({ concurrency: CONCURRENCY_LIMIT });
184 const { addSource, addGlob, collect } = createHelpers({
185 sources,
186 globs,
187 options,
188 globOptions,
189 stack: [collectDocumentString, collectGlob, collectCustomLoader, collectFallback],
190 });
191 for (const pointer in pointerOptionMap) {
192 const pointerOptions = {
193 ...((_a = pointerOptionMap[pointer]) !== null && _a !== void 0 ? _a : {}),
194 unixify,
195 };
196 collect({
197 pointer,
198 pointerOptions,
199 pointerOptionMap,
200 options,
201 addSource,
202 addGlob,
203 queue: queue.add,
204 });
205 }
206 if (globs.length) {
207 includeIgnored({
208 options,
209 globs,
210 });
211 const paths = await globby(globs, createGlobbyOptions(options));
212 collectSourcesFromGlobals({
213 filepaths: paths,
214 options,
215 globOptions,
216 pointerOptionMap,
217 addSource,
218 queue: queue.add,
219 });
220 }
221 await queue.runAll();
222 return sources;
223}
224function collectSourcesSync({ pointerOptionMap, options, }) {
225 var _a;
226 const sources = [];
227 const globs = [];
228 const globOptions = {};
229 const queue = useSyncQueue();
230 const { addSource, addGlob, collect } = createHelpers({
231 sources,
232 globs,
233 options,
234 globOptions,
235 stack: [collectDocumentString, collectGlob, collectCustomLoaderSync, collectFallbackSync],
236 });
237 for (const pointer in pointerOptionMap) {
238 const pointerOptions = {
239 ...((_a = pointerOptionMap[pointer]) !== null && _a !== void 0 ? _a : {}),
240 unixify,
241 };
242 collect({
243 pointer,
244 pointerOptions,
245 pointerOptionMap,
246 options,
247 addSource,
248 addGlob,
249 queue: queue.add,
250 });
251 }
252 if (globs.length) {
253 includeIgnored({
254 options,
255 globs,
256 });
257 const paths = sync(globs, createGlobbyOptions(options));
258 collectSourcesFromGlobalsSync({
259 filepaths: paths,
260 options,
261 globOptions,
262 pointerOptionMap,
263 addSource,
264 queue: queue.add,
265 });
266 }
267 queue.runAll();
268 return sources;
269}
270//
271function createHelpers({ sources, globs, options, globOptions, stack, }) {
272 const addSource = ({ pointer, source, noCache, }) => {
273 sources.push(source);
274 if (!noCache) {
275 options.cache[pointer] = source;
276 }
277 };
278 const collect = useStack(...stack);
279 const addGlob = ({ pointerOptions, pointer }) => {
280 globs.push(pointer);
281 Object.assign(globOptions, pointerOptions);
282 };
283 return {
284 addSource,
285 collect,
286 addGlob,
287 };
288}
289function includeIgnored({ options, globs }) {
290 if (options.ignore) {
291 const ignoreList = asArray(options.ignore)
292 .map(g => `!(${g})`)
293 .map(unixify);
294 if (ignoreList.length > 0) {
295 globs.push(...ignoreList);
296 }
297 }
298}
299function createGlobbyOptions(options) {
300 return { absolute: true, ...options, ignore: [] };
301}
302function collectSourcesFromGlobals({ filepaths, options, globOptions, pointerOptionMap, addSource, queue, }) {
303 const collectFromGlobs = useStack(collectCustomLoader, collectFallback);
304 for (let i = 0; i < filepaths.length; i++) {
305 const pointer = filepaths[i];
306 collectFromGlobs({
307 pointer,
308 pointerOptions: globOptions,
309 pointerOptionMap,
310 options,
311 addSource,
312 addGlob: () => {
313 throw new Error(`I don't accept any new globs!`);
314 },
315 queue,
316 });
317 }
318}
319function collectSourcesFromGlobalsSync({ filepaths, options, globOptions, pointerOptionMap, addSource, queue, }) {
320 const collectFromGlobs = useStack(collectCustomLoaderSync, collectFallbackSync);
321 for (let i = 0; i < filepaths.length; i++) {
322 const pointer = filepaths[i];
323 collectFromGlobs({
324 pointer,
325 pointerOptions: globOptions,
326 pointerOptionMap,
327 options,
328 addSource,
329 addGlob: () => {
330 throw new Error(`I don't accept any new globs!`);
331 },
332 queue,
333 });
334 }
335}
336function addResultOfCustomLoader({ pointer, result, addSource, }) {
337 if (isSchema(result)) {
338 addSource({
339 source: {
340 location: pointer,
341 schema: result,
342 document: parse(printSchemaWithDirectives(result)),
343 },
344 pointer,
345 noCache: true,
346 });
347 }
348 else if (result.kind && result.kind === Kind.DOCUMENT) {
349 addSource({
350 source: {
351 document: result,
352 location: pointer,
353 },
354 pointer,
355 });
356 }
357 else if (result.document) {
358 addSource({
359 source: {
360 location: pointer,
361 ...result,
362 },
363 pointer,
364 });
365 }
366}
367function collectDocumentString({ pointer, pointerOptions, options, addSource, queue }, next) {
368 if (isDocumentString(pointer)) {
369 return queue(() => {
370 const source = parseGraphQLSDL(`${stringToHash(pointer)}.graphql`, pointer, {
371 ...options,
372 ...pointerOptions,
373 });
374 addSource({
375 source,
376 pointer,
377 });
378 });
379 }
380 next();
381}
382function collectGlob({ pointer, pointerOptions, addGlob }, next) {
383 if (isGlob(pointerOptions.unixify(pointer))) {
384 return addGlob({
385 pointer: pointerOptions.unixify(pointer),
386 pointerOptions,
387 });
388 }
389 next();
390}
391function collectCustomLoader({ pointer, pointerOptions, queue, addSource, options, pointerOptionMap }, next) {
392 if (pointerOptions.loader) {
393 return queue(async () => {
394 const loader = await useCustomLoader(pointerOptions.loader, options.cwd);
395 const result = await loader(pointer, { ...options, ...pointerOptions }, pointerOptionMap);
396 if (!result) {
397 return;
398 }
399 addResultOfCustomLoader({ pointer, result, addSource });
400 });
401 }
402 next();
403}
404function collectCustomLoaderSync({ pointer, pointerOptions, queue, addSource, options, pointerOptionMap }, next) {
405 if (pointerOptions.loader) {
406 return queue(() => {
407 const loader = useCustomLoaderSync(pointerOptions.loader, options.cwd);
408 const result = loader(pointer, { ...options, ...pointerOptions }, pointerOptionMap);
409 if (result) {
410 addResultOfCustomLoader({ pointer, result, addSource });
411 }
412 });
413 }
414 next();
415}
416function collectFallback({ queue, pointer, options, pointerOptions, addSource }) {
417 return queue(async () => {
418 const source = await loadFile(pointer, {
419 ...options,
420 ...pointerOptions,
421 });
422 if (source) {
423 addSource({ source, pointer });
424 }
425 });
426}
427function collectFallbackSync({ queue, pointer, options, pointerOptions, addSource }) {
428 return queue(() => {
429 const source = loadFileSync(pointer, {
430 ...options,
431 ...pointerOptions,
432 });
433 if (source) {
434 addSource({ source, pointer });
435 }
436 });
437}
438
439const filterKind = (content, filterKinds) => {
440 if (content && content.definitions && content.definitions.length && filterKinds && filterKinds.length > 0) {
441 const invalidDefinitions = [];
442 const validDefinitions = [];
443 for (const definitionNode of content.definitions) {
444 if (filterKinds.includes(definitionNode.kind)) {
445 invalidDefinitions.push(definitionNode);
446 }
447 else {
448 validDefinitions.push(definitionNode);
449 }
450 }
451 if (invalidDefinitions.length > 0) {
452 invalidDefinitions.forEach(d => {
453 debugLog(`Filtered document of kind ${d.kind} due to filter policy (${filterKinds.join(', ')})`);
454 });
455 }
456 return {
457 kind: Kind.DOCUMENT,
458 definitions: validDefinitions,
459 };
460 }
461 return content;
462};
463
464function parseSource({ partialSource, options, globOptions, pointerOptionMap, addValidSource }) {
465 if (partialSource) {
466 const input = prepareInput({
467 source: partialSource,
468 options,
469 globOptions,
470 pointerOptionMap,
471 });
472 parseSchema(input);
473 parseRawSDL(input);
474 if (input.source.document) {
475 useKindsFilter(input);
476 useComments(input);
477 collectValidSources(input, addValidSource);
478 }
479 }
480}
481//
482function prepareInput({ source, options, globOptions, pointerOptionMap, }) {
483 const specificOptions = {
484 ...options,
485 ...(source.location in pointerOptionMap ? globOptions : pointerOptionMap[source.location]),
486 };
487 return { source: { ...source }, options: specificOptions };
488}
489function parseSchema(input) {
490 if (input.source.schema) {
491 input.source.schema = fixSchemaAst(input.source.schema, input.options);
492 input.source.rawSDL = printSchemaWithDirectives(input.source.schema, input.options);
493 }
494}
495function parseRawSDL(input) {
496 if (input.source.rawSDL) {
497 input.source.document = parse(new Source(input.source.rawSDL, input.source.location), input.options);
498 }
499}
500function useKindsFilter(input) {
501 if (input.options.filterKinds) {
502 input.source.document = filterKind(input.source.document, input.options.filterKinds);
503 }
504}
505function useComments(input) {
506 if (!input.source.rawSDL) {
507 input.source.rawSDL = printWithComments(input.source.document);
508 resetComments();
509 }
510}
511function collectValidSources(input, addValidSource) {
512 if (input.source.document.definitions && input.source.document.definitions.length > 0) {
513 addValidSource(input.source);
514 }
515}
516
517const CONCURRENCY_LIMIT$1 = 100;
518async function loadTypedefs(pointerOrPointers, options) {
519 const pointerOptionMap = normalizePointers(pointerOrPointers);
520 const globOptions = {};
521 applyDefaultOptions(options);
522 const sources = await collectSources({
523 pointerOptionMap,
524 options,
525 });
526 const validSources = [];
527 // If we have few k of files it may be an issue
528 const limit = useLimit(CONCURRENCY_LIMIT$1);
529 await Promise.all(sources.map(partialSource => limit(() => parseSource({
530 partialSource,
531 options,
532 globOptions,
533 pointerOptionMap,
534 addValidSource(source) {
535 validSources.push(source);
536 },
537 }))));
538 return prepareResult({ options, pointerOptionMap, validSources });
539}
540function loadTypedefsSync(pointerOrPointers, options) {
541 const pointerOptionMap = normalizePointers(pointerOrPointers);
542 const globOptions = {};
543 applyDefaultOptions(options);
544 const sources = collectSourcesSync({
545 pointerOptionMap,
546 options,
547 });
548 const validSources = [];
549 sources.forEach(partialSource => {
550 parseSource({
551 partialSource,
552 options,
553 globOptions,
554 pointerOptionMap,
555 addValidSource(source) {
556 validSources.push(source);
557 },
558 });
559 });
560 return prepareResult({ options, pointerOptionMap, validSources });
561}
562//
563function prepareResult({ options, pointerOptionMap, validSources, }) {
564 const pointerList = Object.keys(pointerOptionMap);
565 if (pointerList.length > 0 && validSources.length === 0) {
566 throw new Error(`
567 Unable to find any GraphQL type definitions for the following pointers:
568 ${pointerList.map(p => `
569 - ${p}
570 `)}`);
571 }
572 return options.sort
573 ? validSources.sort((left, right) => compareStrings(left.location, right.location))
574 : validSources;
575}
576
577const OPERATION_KINDS = [Kind.OPERATION_DEFINITION, Kind.FRAGMENT_DEFINITION];
578const NON_OPERATION_KINDS = Object.keys(Kind)
579 .reduce((prev, v) => [...prev, Kind[v]], [])
580 .filter(v => !OPERATION_KINDS.includes(v));
581function loadDocuments(documentDef, options) {
582 return loadTypedefs(documentDef, { noRequire: true, filterKinds: NON_OPERATION_KINDS, ...options });
583}
584function loadDocumentsSync(documentDef, options) {
585 return loadTypedefsSync(documentDef, { noRequire: true, filterKinds: NON_OPERATION_KINDS, ...options });
586}
587
588async function loadSchema(schemaPointers, options) {
589 const sources = await loadTypedefs(schemaPointers, {
590 filterKinds: OPERATION_KINDS,
591 ...options,
592 });
593 const { schemas, typeDefs } = collectSchemasAndTypeDefs(sources);
594 const mergeSchemasOptions = {
595 schemas,
596 typeDefs,
597 ...options,
598 };
599 const schema = await mergeSchemasAsync(mergeSchemasOptions);
600 if (options.includeSources) {
601 includeSources(schema, sources);
602 }
603 return schema;
604}
605function loadSchemaSync(schemaPointers, options) {
606 const sources = loadTypedefsSync(schemaPointers, {
607 filterKinds: OPERATION_KINDS,
608 ...options,
609 });
610 const { schemas, typeDefs } = collectSchemasAndTypeDefs(sources);
611 const mergeSchemasOptions = {
612 schemas,
613 typeDefs,
614 ...options,
615 };
616 const schema = mergeSchemas(mergeSchemasOptions);
617 if (options.includeSources) {
618 includeSources(schema, sources);
619 }
620 return schema;
621}
622function includeSources(schema, sources) {
623 schema.extensions = {
624 ...schema.extensions,
625 sources: sources
626 .filter(source => source.rawSDL || source.document)
627 .map(source => new Source(source.rawSDL || print(source.document), source.location)),
628 };
629}
630function collectSchemasAndTypeDefs(sources) {
631 const schemas = [];
632 const typeDefs = [];
633 sources.forEach(source => {
634 if (source.schema) {
635 schemas.push(source.schema);
636 }
637 else {
638 typeDefs.push(source.document);
639 }
640 });
641 return {
642 schemas,
643 typeDefs,
644 };
645}
646
647export { NON_OPERATION_KINDS, OPERATION_KINDS, filterKind, loadDocuments, loadDocumentsSync, loadSchema, loadSchemaSync, loadTypedefs, loadTypedefsSync };
648//# sourceMappingURL=index.esm.js.map