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