UNPKG

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