UNPKG

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