@graphql-codegen/cli 2.6.0-alpha-bbecef04a.0 → 2.6.0-alpha-c9d165f6a.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin.js CHANGED
@@ -53,10 +53,11 @@ const yaml = _interopDefault(require('yaml'));
53
53
  const module$1 = require('module');
54
54
  const fs = require('fs');
55
55
  const fs__default = _interopDefault(fs);
56
+ const os = require('os');
56
57
  const Listr = _interopDefault(require('listr'));
57
58
  const isGlob = _interopDefault(require('is-glob'));
58
59
  const debounce = _interopDefault(require('debounce'));
59
- const mkdirp = _interopDefault(require('mkdirp'));
60
+ const fsExtra = require('fs-extra');
60
61
  const crypto = require('crypto');
61
62
  const inquirer = _interopDefault(require('inquirer'));
62
63
  const detectIndent = _interopDefault(require('detect-indent'));
@@ -505,30 +506,20 @@ async function loadSchema(schemaPointers, config) {
505
506
 
506
507
  ${e.message || e}
507
508
  ${e.stack || ''}
508
-
509
+
509
510
  GraphQL Code Generator supports:
510
511
  - ES Modules and CommonJS exports (export as default or named export "schema")
511
512
  - Introspection JSON File
512
513
  - URL of GraphQL endpoint
513
514
  - Multiple files with type definitions (glob expression)
514
515
  - String in config file
515
-
516
+
516
517
  Try to use one of above options and run codegen again.
517
-
518
+
518
519
  `);
519
520
  }
520
521
  }
521
- const requiredLoader = async (pointers, schema) => {
522
- return Promise.all(pointers.map(async (pointer) => {
523
- const userLoader = (await makeDefaultLoader(process.cwd())(pointer.require)).default;
524
- const documents = userLoader(pointer.config || {}, schema);
525
- return documents;
526
- })).then(results => [].concat(...results));
527
- };
528
- const isRequirePointer = (pointer) => {
529
- return typeof pointer === 'object' && 'require' in pointer;
530
- };
531
- async function loadDocuments(documentPointers, config, schema) {
522
+ async function loadDocuments(documentPointers, config) {
532
523
  const loaders = [
533
524
  new codeFileLoader.CodeFileLoader({
534
525
  pluckConfig: {
@@ -547,35 +538,14 @@ async function loadDocuments(documentPointers, config, schema) {
547
538
  }
548
539
  ignore.push(path.join(process.cwd(), generatePath));
549
540
  }
550
- const loaderPointers = [];
551
- const requestPointers = [];
552
- if (Array.isArray(documentPointers)) {
553
- documentPointers.forEach(pointer => {
554
- if (isRequirePointer(pointer)) {
555
- requestPointers.push(pointer);
556
- }
557
- else {
558
- loaderPointers.push(pointer);
559
- }
560
- });
561
- }
562
- else {
563
- if (isRequirePointer(documentPointers)) {
564
- requestPointers.push(documentPointers);
565
- }
566
- else {
567
- loaderPointers.push(documentPointers);
568
- }
569
- }
570
- const loadedFromToolkit = await load.loadDocuments(loaderPointers, {
541
+ const loadedFromToolkit = await load.loadDocuments(documentPointers, {
571
542
  ...defaultDocumentsLoadOptions,
572
543
  ignore,
573
544
  loaders,
574
545
  ...config,
575
546
  ...config.config,
576
547
  });
577
- const loadedAlterantively = await requiredLoader(requestPointers, schema);
578
- return [...loadedFromToolkit, ...loadedAlterantively];
548
+ return loadedFromToolkit;
579
549
  }
580
550
 
581
551
  const { lstat } = fs.promises;
@@ -760,6 +730,32 @@ function updateContextWithCliFlags(context, cliFlags) {
760
730
  class CodegenContext {
761
731
  constructor({ config, graphqlConfig, filepath, }) {
762
732
  this._pluginContext = {};
733
+ this.loadSchema = useCache((pointer) => {
734
+ const config = this.getConfig(defaultSchemaLoadOptions);
735
+ if (this._graphqlConfig) {
736
+ // TODO: SchemaWithLoader won't work here
737
+ return this._graphqlConfig.getProject(this._project).loadSchema(pointer, 'GraphQLSchema', config);
738
+ }
739
+ return loadSchema(pointer, config);
740
+ }, {
741
+ cacheKey(pointer) {
742
+ return JSON.stringify(pointer);
743
+ },
744
+ skip: () => { var _a; return !!((_a = this.config) === null || _a === void 0 ? void 0 : _a.watch); },
745
+ });
746
+ this.loadDocuments = useCache((pointer) => {
747
+ const config = this.getConfig(defaultDocumentsLoadOptions);
748
+ if (this._graphqlConfig) {
749
+ // TODO: pointer won't work here
750
+ return this._graphqlConfig.getProject(this._project).loadDocuments(pointer, config);
751
+ }
752
+ return loadDocuments(pointer, config);
753
+ }, {
754
+ cacheKey(pointer) {
755
+ return JSON.stringify(pointer);
756
+ },
757
+ skip: () => { var _a; return !!((_a = this.config) === null || _a === void 0 ? void 0 : _a.watch); },
758
+ });
763
759
  this._config = config;
764
760
  this._graphqlConfig = graphqlConfig;
765
761
  this.filepath = this._graphqlConfig ? this._graphqlConfig.filepath : filepath;
@@ -805,27 +801,28 @@ class CodegenContext {
805
801
  getPluginContext() {
806
802
  return this._pluginContext;
807
803
  }
808
- async loadSchema(pointer) {
809
- const config = this.getConfig(defaultSchemaLoadOptions);
810
- if (this._graphqlConfig) {
811
- // TODO: SchemaWithLoader won't work here
812
- return this._graphqlConfig.getProject(this._project).loadSchema(pointer, 'GraphQLSchema', config);
813
- }
814
- return loadSchema(pointer, config);
815
- }
816
- async loadDocuments(input) {
817
- const config = this.getConfig(defaultDocumentsLoadOptions);
818
- if (this._graphqlConfig) {
819
- // TODO: pointer won't work here
820
- const documents = await this._graphqlConfig.getProject(this._project).loadDocuments(input.pointer, config);
821
- return documents;
822
- }
823
- return loadDocuments(input.pointer, config, input.schema);
824
- }
825
804
  }
826
805
  function ensureContext(input) {
827
806
  return input instanceof CodegenContext ? input : new CodegenContext({ config: input });
828
807
  }
808
+ /**
809
+ * The goal here is to avoid loading the same schema or documents across projects.
810
+ */
811
+ function useCache(originalFunction, { cacheKey, skip = () => false }) {
812
+ if (skip()) {
813
+ return originalFunction;
814
+ }
815
+ const cache = new Map();
816
+ return (input) => {
817
+ const key = cacheKey(input);
818
+ if (cache.has(key)) {
819
+ return cache.get(key);
820
+ }
821
+ const result = originalFunction(input);
822
+ cache.set(key, result);
823
+ return result;
824
+ };
825
+ }
829
826
 
830
827
  const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
831
828
  const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
@@ -924,7 +921,7 @@ async function executeCodegen(input) {
924
921
  const documentsLoadingCache = createCache(async function (hash) {
925
922
  const documents = await context.loadDocuments(JSON.parse(hash));
926
923
  return {
927
- documents,
924
+ documents: documents,
928
925
  };
929
926
  });
930
927
  function wrapTask(task, source, taskName) {
@@ -1055,7 +1052,7 @@ async function executeCodegen(input) {
1055
1052
  debugLog(`[CLI] Loading Documents`);
1056
1053
  // get different cache for shared docs and output specific docs
1057
1054
  const results = await Promise.all([rootDocuments, outputSpecificDocuments].map(docs => {
1058
- const hash = JSON.stringify({ pointer: docs, schema: outputSchema });
1055
+ const hash = JSON.stringify(docs);
1059
1056
  return documentsLoadingCache.load(hash);
1060
1057
  }));
1061
1058
  const documents = [];
@@ -1139,8 +1136,7 @@ async function executeCodegen(input) {
1139
1136
  }), {
1140
1137
  // it doesn't stop when one of tasks failed, to finish at least some of outputs
1141
1138
  exitOnError: false,
1142
- // run 4 at once
1143
- concurrent: 4,
1139
+ concurrent: os.cpus().length,
1144
1140
  });
1145
1141
  },
1146
1142
  });
@@ -1287,22 +1283,22 @@ const createWatcher = (initalContext, onNext) => {
1287
1283
  });
1288
1284
  };
1289
1285
 
1290
- function writeSync(filepath, content) {
1291
- return fs.writeFileSync(filepath, content);
1286
+ function writeFile(filepath, content) {
1287
+ return fsExtra.writeFile(filepath, content);
1292
1288
  }
1293
- function readSync(filepath) {
1294
- return fs.readFileSync(filepath, 'utf-8');
1289
+ function readFile(filepath) {
1290
+ return fsExtra.readFile(filepath, 'utf-8');
1295
1291
  }
1296
- function fileExists(filePath) {
1292
+ async function fileExists(filePath) {
1297
1293
  try {
1298
- return fs.statSync(filePath).isFile();
1294
+ return (await fsExtra.stat(filePath)).isFile();
1299
1295
  }
1300
1296
  catch (err) {
1301
1297
  return false;
1302
1298
  }
1303
1299
  }
1304
1300
  function unlinkFile(filePath, cb) {
1305
- fs.unlink(filePath, cb);
1301
+ fsExtra.unlink(filePath, cb);
1306
1302
  }
1307
1303
 
1308
1304
  const hash = (content) => crypto.createHash('sha1').update(content).digest('base64');
@@ -1317,7 +1313,7 @@ async function generate(input, saveToFile = true) {
1317
1313
  const staleFilenames = previouslyGeneratedFilenames.filter(f => !filenames.includes(f));
1318
1314
  staleFilenames.forEach(filename => {
1319
1315
  if (shouldOverwrite(config, filename)) {
1320
- unlinkFile(filename, err => {
1316
+ return unlinkFile(filename, err => {
1321
1317
  const prettyFilename = filename.replace(`${input.cwd || process.cwd()}/`, '');
1322
1318
  if (err) {
1323
1319
  debugLog(`Cannot remove stale file: ${prettyFilename}\n${err}`);
@@ -1338,7 +1334,39 @@ async function generate(input, saveToFile = true) {
1338
1334
  if (config.watch) {
1339
1335
  removeStaleFiles(config, generationResult);
1340
1336
  }
1341
- await context.profiler.run(() => lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: beforeAllFileWrite');
1337
+ await context.profiler.run(async () => {
1338
+ await lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
1339
+ await Promise.all(generationResult.map(async (result) => {
1340
+ const exists = await fileExists(result.filename);
1341
+ if (!shouldOverwrite(config, result.filename) && exists) {
1342
+ return;
1343
+ }
1344
+ const content = result.content || '';
1345
+ const currentHash = hash(content);
1346
+ let previousHash = recentOutputHash.get(result.filename);
1347
+ if (!previousHash && exists) {
1348
+ previousHash = hash(await readFile(result.filename));
1349
+ }
1350
+ if (previousHash && currentHash === previousHash) {
1351
+ debugLog(`Skipping file (${result.filename}) writing due to indentical hash...`);
1352
+ return;
1353
+ }
1354
+ if (content.length === 0) {
1355
+ return;
1356
+ }
1357
+ recentOutputHash.set(result.filename, currentHash);
1358
+ const basedir = path.dirname(result.filename);
1359
+ await lifecycleHooks(result.hooks).beforeOneFileWrite(result.filename);
1360
+ await lifecycleHooks(config.hooks).beforeOneFileWrite(result.filename);
1361
+ await fsExtra.mkdirp(basedir);
1362
+ const absolutePath = path.isAbsolute(result.filename)
1363
+ ? result.filename
1364
+ : path.join(input.cwd || process.cwd(), result.filename);
1365
+ await writeFile(absolutePath, result.content);
1366
+ await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
1367
+ await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
1368
+ }));
1369
+ }, 'Lifecycle: beforeAllFileWrite');
1342
1370
  await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
1343
1371
  const exists = fileExists(result.filename);
1344
1372
  if (!shouldOverwrite(config, result.filename) && exists) {
@@ -1348,7 +1376,7 @@ async function generate(input, saveToFile = true) {
1348
1376
  const currentHash = hash(content);
1349
1377
  let previousHash = recentOutputHash.get(result.filename);
1350
1378
  if (!previousHash && exists) {
1351
- previousHash = hash(readSync(result.filename));
1379
+ previousHash = hash(await readFile(result.filename));
1352
1380
  }
1353
1381
  if (previousHash && currentHash === previousHash) {
1354
1382
  debugLog(`Skipping file (${result.filename}) writing due to identical hash...`);
@@ -1361,11 +1389,11 @@ async function generate(input, saveToFile = true) {
1361
1389
  const basedir = path.dirname(result.filename);
1362
1390
  await lifecycleHooks(result.hooks).beforeOneFileWrite(result.filename);
1363
1391
  await lifecycleHooks(config.hooks).beforeOneFileWrite(result.filename);
1364
- mkdirp.sync(basedir);
1392
+ fsExtra.mkdirp.sync(basedir);
1365
1393
  const absolutePath = path.isAbsolute(result.filename)
1366
1394
  ? result.filename
1367
1395
  : path.join(input.cwd || process.cwd(), result.filename);
1368
- writeSync(absolutePath, result.content);
1396
+ await writeFile(absolutePath, result.content);
1369
1397
  await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
1370
1398
  await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
1371
1399
  })), 'Write files');
@@ -1380,7 +1408,7 @@ async function generate(input, saveToFile = true) {
1380
1408
  await context.profiler.run(() => writeOutput(outputFiles), 'writeOutput');
1381
1409
  await context.profiler.run(() => lifecycleHooks(config.hooks).beforeDone(), 'Lifecycle: beforeDone');
1382
1410
  if (context.profilerOutput) {
1383
- writeSync(path.join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1411
+ await writeFile(path.join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1384
1412
  }
1385
1413
  return outputFiles;
1386
1414
  }
package/codegen.d.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  import { Types } from '@graphql-codegen/plugin-helpers';
2
2
  import { CodegenContext } from './config';
3
- export declare const makeDefaultLoader: (from: string) => (mod: string) => Promise<any>;
4
3
  export declare function executeCodegen(input: CodegenContext | Types.Config): Promise<Types.FileOutput[]>;
package/config.d.ts CHANGED
@@ -110,10 +110,7 @@ export declare class CodegenContext {
110
110
  getPluginContext(): {
111
111
  [key: string]: any;
112
112
  };
113
- loadSchema(pointer: Types.Schema): Promise<GraphQLSchema>;
114
- loadDocuments(input: {
115
- pointer: Types.OperationDocument[];
116
- schema: GraphQLSchema;
117
- }): Promise<Types.DocumentFile[]>;
113
+ loadSchema: (input: Types.Schema) => Promise<GraphQLSchema>;
114
+ loadDocuments: (input: Types.OperationDocument[]) => Promise<import("graphql-config").Source[]>;
118
115
  }
119
116
  export declare function ensureContext(input: CodegenContext | Types.Config): CodegenContext;
package/index.js CHANGED
@@ -53,11 +53,12 @@ const yaml = _interopDefault(require('yaml'));
53
53
  const module$1 = require('module');
54
54
  const fs = require('fs');
55
55
  const fs__default = _interopDefault(fs);
56
+ const os = require('os');
56
57
  const Listr = _interopDefault(require('listr'));
57
58
  const child_process = require('child_process');
58
59
  const isGlob = _interopDefault(require('is-glob'));
59
60
  const debounce = _interopDefault(require('debounce'));
60
- const mkdirp = _interopDefault(require('mkdirp'));
61
+ const fsExtra = require('fs-extra');
61
62
  const crypto = require('crypto');
62
63
  const inquirer = _interopDefault(require('inquirer'));
63
64
  const detectIndent = _interopDefault(require('detect-indent'));
@@ -423,30 +424,20 @@ async function loadSchema(schemaPointers, config) {
423
424
 
424
425
  ${e.message || e}
425
426
  ${e.stack || ''}
426
-
427
+
427
428
  GraphQL Code Generator supports:
428
429
  - ES Modules and CommonJS exports (export as default or named export "schema")
429
430
  - Introspection JSON File
430
431
  - URL of GraphQL endpoint
431
432
  - Multiple files with type definitions (glob expression)
432
433
  - String in config file
433
-
434
+
434
435
  Try to use one of above options and run codegen again.
435
-
436
+
436
437
  `);
437
438
  }
438
439
  }
439
- const requiredLoader = async (pointers, schema) => {
440
- return Promise.all(pointers.map(async (pointer) => {
441
- const userLoader = (await makeDefaultLoader(process.cwd())(pointer.require)).default;
442
- const documents = userLoader(pointer.config || {}, schema);
443
- return documents;
444
- })).then(results => [].concat(...results));
445
- };
446
- const isRequirePointer = (pointer) => {
447
- return typeof pointer === 'object' && 'require' in pointer;
448
- };
449
- async function loadDocuments(documentPointers, config, schema) {
440
+ async function loadDocuments(documentPointers, config) {
450
441
  const loaders = [
451
442
  new codeFileLoader.CodeFileLoader({
452
443
  pluckConfig: {
@@ -465,35 +456,14 @@ async function loadDocuments(documentPointers, config, schema) {
465
456
  }
466
457
  ignore.push(path.join(process.cwd(), generatePath));
467
458
  }
468
- const loaderPointers = [];
469
- const requestPointers = [];
470
- if (Array.isArray(documentPointers)) {
471
- documentPointers.forEach(pointer => {
472
- if (isRequirePointer(pointer)) {
473
- requestPointers.push(pointer);
474
- }
475
- else {
476
- loaderPointers.push(pointer);
477
- }
478
- });
479
- }
480
- else {
481
- if (isRequirePointer(documentPointers)) {
482
- requestPointers.push(documentPointers);
483
- }
484
- else {
485
- loaderPointers.push(documentPointers);
486
- }
487
- }
488
- const loadedFromToolkit = await load.loadDocuments(loaderPointers, {
459
+ const loadedFromToolkit = await load.loadDocuments(documentPointers, {
489
460
  ...defaultDocumentsLoadOptions,
490
461
  ignore,
491
462
  loaders,
492
463
  ...config,
493
464
  ...config.config,
494
465
  });
495
- const loadedAlterantively = await requiredLoader(requestPointers, schema);
496
- return [...loadedFromToolkit, ...loadedAlterantively];
466
+ return loadedFromToolkit;
497
467
  }
498
468
 
499
469
  const { lstat } = fs.promises;
@@ -678,6 +648,32 @@ function updateContextWithCliFlags(context, cliFlags) {
678
648
  class CodegenContext {
679
649
  constructor({ config, graphqlConfig, filepath, }) {
680
650
  this._pluginContext = {};
651
+ this.loadSchema = useCache((pointer) => {
652
+ const config = this.getConfig(defaultSchemaLoadOptions);
653
+ if (this._graphqlConfig) {
654
+ // TODO: SchemaWithLoader won't work here
655
+ return this._graphqlConfig.getProject(this._project).loadSchema(pointer, 'GraphQLSchema', config);
656
+ }
657
+ return loadSchema(pointer, config);
658
+ }, {
659
+ cacheKey(pointer) {
660
+ return JSON.stringify(pointer);
661
+ },
662
+ skip: () => { var _a; return !!((_a = this.config) === null || _a === void 0 ? void 0 : _a.watch); },
663
+ });
664
+ this.loadDocuments = useCache((pointer) => {
665
+ const config = this.getConfig(defaultDocumentsLoadOptions);
666
+ if (this._graphqlConfig) {
667
+ // TODO: pointer won't work here
668
+ return this._graphqlConfig.getProject(this._project).loadDocuments(pointer, config);
669
+ }
670
+ return loadDocuments(pointer, config);
671
+ }, {
672
+ cacheKey(pointer) {
673
+ return JSON.stringify(pointer);
674
+ },
675
+ skip: () => { var _a; return !!((_a = this.config) === null || _a === void 0 ? void 0 : _a.watch); },
676
+ });
681
677
  this._config = config;
682
678
  this._graphqlConfig = graphqlConfig;
683
679
  this.filepath = this._graphqlConfig ? this._graphqlConfig.filepath : filepath;
@@ -723,27 +719,28 @@ class CodegenContext {
723
719
  getPluginContext() {
724
720
  return this._pluginContext;
725
721
  }
726
- async loadSchema(pointer) {
727
- const config = this.getConfig(defaultSchemaLoadOptions);
728
- if (this._graphqlConfig) {
729
- // TODO: SchemaWithLoader won't work here
730
- return this._graphqlConfig.getProject(this._project).loadSchema(pointer, 'GraphQLSchema', config);
731
- }
732
- return loadSchema(pointer, config);
733
- }
734
- async loadDocuments(input) {
735
- const config = this.getConfig(defaultDocumentsLoadOptions);
736
- if (this._graphqlConfig) {
737
- // TODO: pointer won't work here
738
- const documents = await this._graphqlConfig.getProject(this._project).loadDocuments(input.pointer, config);
739
- return documents;
740
- }
741
- return loadDocuments(input.pointer, config, input.schema);
742
- }
743
722
  }
744
723
  function ensureContext(input) {
745
724
  return input instanceof CodegenContext ? input : new CodegenContext({ config: input });
746
725
  }
726
+ /**
727
+ * The goal here is to avoid loading the same schema or documents across projects.
728
+ */
729
+ function useCache(originalFunction, { cacheKey, skip = () => false }) {
730
+ if (skip()) {
731
+ return originalFunction;
732
+ }
733
+ const cache = new Map();
734
+ return (input) => {
735
+ const key = cacheKey(input);
736
+ if (cache.has(key)) {
737
+ return cache.get(key);
738
+ }
739
+ const result = originalFunction(input);
740
+ cache.set(key, result);
741
+ return result;
742
+ };
743
+ }
747
744
 
748
745
  const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
749
746
  const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
@@ -842,7 +839,7 @@ async function executeCodegen(input) {
842
839
  const documentsLoadingCache = createCache(async function (hash) {
843
840
  const documents = await context.loadDocuments(JSON.parse(hash));
844
841
  return {
845
- documents,
842
+ documents: documents,
846
843
  };
847
844
  });
848
845
  function wrapTask(task, source, taskName) {
@@ -973,7 +970,7 @@ async function executeCodegen(input) {
973
970
  debugLog(`[CLI] Loading Documents`);
974
971
  // get different cache for shared docs and output specific docs
975
972
  const results = await Promise.all([rootDocuments, outputSpecificDocuments].map(docs => {
976
- const hash = JSON.stringify({ pointer: docs, schema: outputSchema });
973
+ const hash = JSON.stringify(docs);
977
974
  return documentsLoadingCache.load(hash);
978
975
  }));
979
976
  const documents = [];
@@ -1057,8 +1054,7 @@ async function executeCodegen(input) {
1057
1054
  }), {
1058
1055
  // it doesn't stop when one of tasks failed, to finish at least some of outputs
1059
1056
  exitOnError: false,
1060
- // run 4 at once
1061
- concurrent: 4,
1057
+ concurrent: os.cpus().length,
1062
1058
  });
1063
1059
  },
1064
1060
  });
@@ -1288,22 +1284,22 @@ const createWatcher = (initalContext, onNext) => {
1288
1284
  });
1289
1285
  };
1290
1286
 
1291
- function writeSync(filepath, content) {
1292
- return fs.writeFileSync(filepath, content);
1287
+ function writeFile(filepath, content) {
1288
+ return fsExtra.writeFile(filepath, content);
1293
1289
  }
1294
- function readSync(filepath) {
1295
- return fs.readFileSync(filepath, 'utf-8');
1290
+ function readFile(filepath) {
1291
+ return fsExtra.readFile(filepath, 'utf-8');
1296
1292
  }
1297
- function fileExists(filePath) {
1293
+ async function fileExists(filePath) {
1298
1294
  try {
1299
- return fs.statSync(filePath).isFile();
1295
+ return (await fsExtra.stat(filePath)).isFile();
1300
1296
  }
1301
1297
  catch (err) {
1302
1298
  return false;
1303
1299
  }
1304
1300
  }
1305
1301
  function unlinkFile(filePath, cb) {
1306
- fs.unlink(filePath, cb);
1302
+ fsExtra.unlink(filePath, cb);
1307
1303
  }
1308
1304
 
1309
1305
  const hash = (content) => crypto.createHash('sha1').update(content).digest('base64');
@@ -1318,7 +1314,7 @@ async function generate(input, saveToFile = true) {
1318
1314
  const staleFilenames = previouslyGeneratedFilenames.filter(f => !filenames.includes(f));
1319
1315
  staleFilenames.forEach(filename => {
1320
1316
  if (shouldOverwrite(config, filename)) {
1321
- unlinkFile(filename, err => {
1317
+ return unlinkFile(filename, err => {
1322
1318
  const prettyFilename = filename.replace(`${input.cwd || process.cwd()}/`, '');
1323
1319
  if (err) {
1324
1320
  debugLog(`Cannot remove stale file: ${prettyFilename}\n${err}`);
@@ -1339,7 +1335,39 @@ async function generate(input, saveToFile = true) {
1339
1335
  if (config.watch) {
1340
1336
  removeStaleFiles(config, generationResult);
1341
1337
  }
1342
- await context.profiler.run(() => lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: beforeAllFileWrite');
1338
+ await context.profiler.run(async () => {
1339
+ await lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
1340
+ await Promise.all(generationResult.map(async (result) => {
1341
+ const exists = await fileExists(result.filename);
1342
+ if (!shouldOverwrite(config, result.filename) && exists) {
1343
+ return;
1344
+ }
1345
+ const content = result.content || '';
1346
+ const currentHash = hash(content);
1347
+ let previousHash = recentOutputHash.get(result.filename);
1348
+ if (!previousHash && exists) {
1349
+ previousHash = hash(await readFile(result.filename));
1350
+ }
1351
+ if (previousHash && currentHash === previousHash) {
1352
+ debugLog(`Skipping file (${result.filename}) writing due to indentical hash...`);
1353
+ return;
1354
+ }
1355
+ if (content.length === 0) {
1356
+ return;
1357
+ }
1358
+ recentOutputHash.set(result.filename, currentHash);
1359
+ const basedir = path.dirname(result.filename);
1360
+ await lifecycleHooks(result.hooks).beforeOneFileWrite(result.filename);
1361
+ await lifecycleHooks(config.hooks).beforeOneFileWrite(result.filename);
1362
+ await fsExtra.mkdirp(basedir);
1363
+ const absolutePath = path.isAbsolute(result.filename)
1364
+ ? result.filename
1365
+ : path.join(input.cwd || process.cwd(), result.filename);
1366
+ await writeFile(absolutePath, result.content);
1367
+ await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
1368
+ await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
1369
+ }));
1370
+ }, 'Lifecycle: beforeAllFileWrite');
1343
1371
  await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
1344
1372
  const exists = fileExists(result.filename);
1345
1373
  if (!shouldOverwrite(config, result.filename) && exists) {
@@ -1349,7 +1377,7 @@ async function generate(input, saveToFile = true) {
1349
1377
  const currentHash = hash(content);
1350
1378
  let previousHash = recentOutputHash.get(result.filename);
1351
1379
  if (!previousHash && exists) {
1352
- previousHash = hash(readSync(result.filename));
1380
+ previousHash = hash(await readFile(result.filename));
1353
1381
  }
1354
1382
  if (previousHash && currentHash === previousHash) {
1355
1383
  debugLog(`Skipping file (${result.filename}) writing due to identical hash...`);
@@ -1362,11 +1390,11 @@ async function generate(input, saveToFile = true) {
1362
1390
  const basedir = path.dirname(result.filename);
1363
1391
  await lifecycleHooks(result.hooks).beforeOneFileWrite(result.filename);
1364
1392
  await lifecycleHooks(config.hooks).beforeOneFileWrite(result.filename);
1365
- mkdirp.sync(basedir);
1393
+ fsExtra.mkdirp.sync(basedir);
1366
1394
  const absolutePath = path.isAbsolute(result.filename)
1367
1395
  ? result.filename
1368
1396
  : path.join(input.cwd || process.cwd(), result.filename);
1369
- writeSync(absolutePath, result.content);
1397
+ await writeFile(absolutePath, result.content);
1370
1398
  await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
1371
1399
  await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
1372
1400
  })), 'Write files');
@@ -1381,7 +1409,7 @@ async function generate(input, saveToFile = true) {
1381
1409
  await context.profiler.run(() => writeOutput(outputFiles), 'writeOutput');
1382
1410
  await context.profiler.run(() => lifecycleHooks(config.hooks).beforeDone(), 'Lifecycle: beforeDone');
1383
1411
  if (context.profilerOutput) {
1384
- writeSync(path.join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1412
+ await writeFile(path.join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1385
1413
  }
1386
1414
  return outputFiles;
1387
1415
  }
package/index.mjs CHANGED
@@ -25,12 +25,13 @@ import { JsonFileLoader } from '@graphql-tools/json-file-loader';
25
25
  import { UrlLoader } from '@graphql-tools/url-loader';
26
26
  import yaml from 'yaml';
27
27
  import { createRequire } from 'module';
28
- import fs, { promises, writeFileSync, readFileSync, statSync, unlink } from 'fs';
28
+ import fs, { promises, writeFileSync, readFileSync } from 'fs';
29
+ import { cpus } from 'os';
29
30
  import Listr from 'listr';
30
31
  import { exec } from 'child_process';
31
32
  import isGlob from 'is-glob';
32
33
  import debounce from 'debounce';
33
- import mkdirp from 'mkdirp';
34
+ import { writeFile as writeFile$1, readFile as readFile$1, stat, unlink, mkdirp } from 'fs-extra';
34
35
  import { createHash } from 'crypto';
35
36
  import inquirer from 'inquirer';
36
37
  import detectIndent from 'detect-indent';
@@ -396,30 +397,20 @@ async function loadSchema(schemaPointers, config) {
396
397
 
397
398
  ${e.message || e}
398
399
  ${e.stack || ''}
399
-
400
+
400
401
  GraphQL Code Generator supports:
401
402
  - ES Modules and CommonJS exports (export as default or named export "schema")
402
403
  - Introspection JSON File
403
404
  - URL of GraphQL endpoint
404
405
  - Multiple files with type definitions (glob expression)
405
406
  - String in config file
406
-
407
+
407
408
  Try to use one of above options and run codegen again.
408
-
409
+
409
410
  `);
410
411
  }
411
412
  }
412
- const requiredLoader = async (pointers, schema) => {
413
- return Promise.all(pointers.map(async (pointer) => {
414
- const userLoader = (await makeDefaultLoader(process.cwd())(pointer.require)).default;
415
- const documents = userLoader(pointer.config || {}, schema);
416
- return documents;
417
- })).then(results => [].concat(...results));
418
- };
419
- const isRequirePointer = (pointer) => {
420
- return typeof pointer === 'object' && 'require' in pointer;
421
- };
422
- async function loadDocuments(documentPointers, config, schema) {
413
+ async function loadDocuments(documentPointers, config) {
423
414
  const loaders = [
424
415
  new CodeFileLoader({
425
416
  pluckConfig: {
@@ -438,35 +429,14 @@ async function loadDocuments(documentPointers, config, schema) {
438
429
  }
439
430
  ignore.push(join(process.cwd(), generatePath));
440
431
  }
441
- const loaderPointers = [];
442
- const requestPointers = [];
443
- if (Array.isArray(documentPointers)) {
444
- documentPointers.forEach(pointer => {
445
- if (isRequirePointer(pointer)) {
446
- requestPointers.push(pointer);
447
- }
448
- else {
449
- loaderPointers.push(pointer);
450
- }
451
- });
452
- }
453
- else {
454
- if (isRequirePointer(documentPointers)) {
455
- requestPointers.push(documentPointers);
456
- }
457
- else {
458
- loaderPointers.push(documentPointers);
459
- }
460
- }
461
- const loadedFromToolkit = await loadDocuments$1(loaderPointers, {
432
+ const loadedFromToolkit = await loadDocuments$1(documentPointers, {
462
433
  ...defaultDocumentsLoadOptions,
463
434
  ignore,
464
435
  loaders,
465
436
  ...config,
466
437
  ...config.config,
467
438
  });
468
- const loadedAlterantively = await requiredLoader(requestPointers, schema);
469
- return [...loadedFromToolkit, ...loadedAlterantively];
439
+ return loadedFromToolkit;
470
440
  }
471
441
 
472
442
  const { lstat } = promises;
@@ -651,6 +621,32 @@ function updateContextWithCliFlags(context, cliFlags) {
651
621
  class CodegenContext {
652
622
  constructor({ config, graphqlConfig, filepath, }) {
653
623
  this._pluginContext = {};
624
+ this.loadSchema = useCache((pointer) => {
625
+ const config = this.getConfig(defaultSchemaLoadOptions);
626
+ if (this._graphqlConfig) {
627
+ // TODO: SchemaWithLoader won't work here
628
+ return this._graphqlConfig.getProject(this._project).loadSchema(pointer, 'GraphQLSchema', config);
629
+ }
630
+ return loadSchema(pointer, config);
631
+ }, {
632
+ cacheKey(pointer) {
633
+ return JSON.stringify(pointer);
634
+ },
635
+ skip: () => { var _a; return !!((_a = this.config) === null || _a === void 0 ? void 0 : _a.watch); },
636
+ });
637
+ this.loadDocuments = useCache((pointer) => {
638
+ const config = this.getConfig(defaultDocumentsLoadOptions);
639
+ if (this._graphqlConfig) {
640
+ // TODO: pointer won't work here
641
+ return this._graphqlConfig.getProject(this._project).loadDocuments(pointer, config);
642
+ }
643
+ return loadDocuments(pointer, config);
644
+ }, {
645
+ cacheKey(pointer) {
646
+ return JSON.stringify(pointer);
647
+ },
648
+ skip: () => { var _a; return !!((_a = this.config) === null || _a === void 0 ? void 0 : _a.watch); },
649
+ });
654
650
  this._config = config;
655
651
  this._graphqlConfig = graphqlConfig;
656
652
  this.filepath = this._graphqlConfig ? this._graphqlConfig.filepath : filepath;
@@ -696,27 +692,28 @@ class CodegenContext {
696
692
  getPluginContext() {
697
693
  return this._pluginContext;
698
694
  }
699
- async loadSchema(pointer) {
700
- const config = this.getConfig(defaultSchemaLoadOptions);
701
- if (this._graphqlConfig) {
702
- // TODO: SchemaWithLoader won't work here
703
- return this._graphqlConfig.getProject(this._project).loadSchema(pointer, 'GraphQLSchema', config);
704
- }
705
- return loadSchema(pointer, config);
706
- }
707
- async loadDocuments(input) {
708
- const config = this.getConfig(defaultDocumentsLoadOptions);
709
- if (this._graphqlConfig) {
710
- // TODO: pointer won't work here
711
- const documents = await this._graphqlConfig.getProject(this._project).loadDocuments(input.pointer, config);
712
- return documents;
713
- }
714
- return loadDocuments(input.pointer, config, input.schema);
715
- }
716
695
  }
717
696
  function ensureContext(input) {
718
697
  return input instanceof CodegenContext ? input : new CodegenContext({ config: input });
719
698
  }
699
+ /**
700
+ * The goal here is to avoid loading the same schema or documents across projects.
701
+ */
702
+ function useCache(originalFunction, { cacheKey, skip = () => false }) {
703
+ if (skip()) {
704
+ return originalFunction;
705
+ }
706
+ const cache = new Map();
707
+ return (input) => {
708
+ const key = cacheKey(input);
709
+ if (cache.has(key)) {
710
+ return cache.get(key);
711
+ }
712
+ const result = originalFunction(input);
713
+ cache.set(key, result);
714
+ return result;
715
+ };
716
+ }
720
717
 
721
718
  const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
722
719
  const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
@@ -815,7 +812,7 @@ async function executeCodegen(input) {
815
812
  const documentsLoadingCache = createCache(async function (hash) {
816
813
  const documents = await context.loadDocuments(JSON.parse(hash));
817
814
  return {
818
- documents,
815
+ documents: documents,
819
816
  };
820
817
  });
821
818
  function wrapTask(task, source, taskName) {
@@ -946,7 +943,7 @@ async function executeCodegen(input) {
946
943
  debugLog(`[CLI] Loading Documents`);
947
944
  // get different cache for shared docs and output specific docs
948
945
  const results = await Promise.all([rootDocuments, outputSpecificDocuments].map(docs => {
949
- const hash = JSON.stringify({ pointer: docs, schema: outputSchema });
946
+ const hash = JSON.stringify(docs);
950
947
  return documentsLoadingCache.load(hash);
951
948
  }));
952
949
  const documents = [];
@@ -1030,8 +1027,7 @@ async function executeCodegen(input) {
1030
1027
  }), {
1031
1028
  // it doesn't stop when one of tasks failed, to finish at least some of outputs
1032
1029
  exitOnError: false,
1033
- // run 4 at once
1034
- concurrent: 4,
1030
+ concurrent: cpus().length,
1035
1031
  });
1036
1032
  },
1037
1033
  });
@@ -1261,15 +1257,15 @@ const createWatcher = (initalContext, onNext) => {
1261
1257
  });
1262
1258
  };
1263
1259
 
1264
- function writeSync(filepath, content) {
1265
- return writeFileSync(filepath, content);
1260
+ function writeFile(filepath, content) {
1261
+ return writeFile$1(filepath, content);
1266
1262
  }
1267
- function readSync(filepath) {
1268
- return readFileSync(filepath, 'utf-8');
1263
+ function readFile(filepath) {
1264
+ return readFile$1(filepath, 'utf-8');
1269
1265
  }
1270
- function fileExists(filePath) {
1266
+ async function fileExists(filePath) {
1271
1267
  try {
1272
- return statSync(filePath).isFile();
1268
+ return (await stat(filePath)).isFile();
1273
1269
  }
1274
1270
  catch (err) {
1275
1271
  return false;
@@ -1291,7 +1287,7 @@ async function generate(input, saveToFile = true) {
1291
1287
  const staleFilenames = previouslyGeneratedFilenames.filter(f => !filenames.includes(f));
1292
1288
  staleFilenames.forEach(filename => {
1293
1289
  if (shouldOverwrite(config, filename)) {
1294
- unlinkFile(filename, err => {
1290
+ return unlinkFile(filename, err => {
1295
1291
  const prettyFilename = filename.replace(`${input.cwd || process.cwd()}/`, '');
1296
1292
  if (err) {
1297
1293
  debugLog(`Cannot remove stale file: ${prettyFilename}\n${err}`);
@@ -1312,7 +1308,39 @@ async function generate(input, saveToFile = true) {
1312
1308
  if (config.watch) {
1313
1309
  removeStaleFiles(config, generationResult);
1314
1310
  }
1315
- await context.profiler.run(() => lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: beforeAllFileWrite');
1311
+ await context.profiler.run(async () => {
1312
+ await lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
1313
+ await Promise.all(generationResult.map(async (result) => {
1314
+ const exists = await fileExists(result.filename);
1315
+ if (!shouldOverwrite(config, result.filename) && exists) {
1316
+ return;
1317
+ }
1318
+ const content = result.content || '';
1319
+ const currentHash = hash(content);
1320
+ let previousHash = recentOutputHash.get(result.filename);
1321
+ if (!previousHash && exists) {
1322
+ previousHash = hash(await readFile(result.filename));
1323
+ }
1324
+ if (previousHash && currentHash === previousHash) {
1325
+ debugLog(`Skipping file (${result.filename}) writing due to indentical hash...`);
1326
+ return;
1327
+ }
1328
+ if (content.length === 0) {
1329
+ return;
1330
+ }
1331
+ recentOutputHash.set(result.filename, currentHash);
1332
+ const basedir = dirname(result.filename);
1333
+ await lifecycleHooks(result.hooks).beforeOneFileWrite(result.filename);
1334
+ await lifecycleHooks(config.hooks).beforeOneFileWrite(result.filename);
1335
+ await mkdirp(basedir);
1336
+ const absolutePath = isAbsolute(result.filename)
1337
+ ? result.filename
1338
+ : join(input.cwd || process.cwd(), result.filename);
1339
+ await writeFile(absolutePath, result.content);
1340
+ await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
1341
+ await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
1342
+ }));
1343
+ }, 'Lifecycle: beforeAllFileWrite');
1316
1344
  await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
1317
1345
  const exists = fileExists(result.filename);
1318
1346
  if (!shouldOverwrite(config, result.filename) && exists) {
@@ -1322,7 +1350,7 @@ async function generate(input, saveToFile = true) {
1322
1350
  const currentHash = hash(content);
1323
1351
  let previousHash = recentOutputHash.get(result.filename);
1324
1352
  if (!previousHash && exists) {
1325
- previousHash = hash(readSync(result.filename));
1353
+ previousHash = hash(await readFile(result.filename));
1326
1354
  }
1327
1355
  if (previousHash && currentHash === previousHash) {
1328
1356
  debugLog(`Skipping file (${result.filename}) writing due to identical hash...`);
@@ -1339,7 +1367,7 @@ async function generate(input, saveToFile = true) {
1339
1367
  const absolutePath = isAbsolute(result.filename)
1340
1368
  ? result.filename
1341
1369
  : join(input.cwd || process.cwd(), result.filename);
1342
- writeSync(absolutePath, result.content);
1370
+ await writeFile(absolutePath, result.content);
1343
1371
  await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
1344
1372
  await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
1345
1373
  })), 'Write files');
@@ -1354,7 +1382,7 @@ async function generate(input, saveToFile = true) {
1354
1382
  await context.profiler.run(() => writeOutput(outputFiles), 'writeOutput');
1355
1383
  await context.profiler.run(() => lifecycleHooks(config.hooks).beforeDone(), 'Lifecycle: beforeDone');
1356
1384
  if (context.profilerOutput) {
1357
- writeSync(join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1385
+ await writeFile(join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1358
1386
  }
1359
1387
  return outputFiles;
1360
1388
  }
package/load.d.ts CHANGED
@@ -12,4 +12,4 @@ export declare const defaultDocumentsLoadOptions: {
12
12
  skipGraphQLImport: boolean;
13
13
  };
14
14
  export declare function loadSchema(schemaPointers: UnnormalizedTypeDefPointer, config: Types.Config): Promise<GraphQLSchema>;
15
- export declare function loadDocuments(documentPointers: UnnormalizedTypeDefPointer | UnnormalizedTypeDefPointer[], config: Types.Config, schema: GraphQLSchema): Promise<Types.DocumentFile[]>;
15
+ export declare function loadDocuments(documentPointers: UnnormalizedTypeDefPointer | UnnormalizedTypeDefPointer[], config: Types.Config): Promise<Types.DocumentFile[]>;
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@graphql-codegen/cli",
3
- "version": "2.6.0-alpha-bbecef04a.0",
3
+ "version": "2.6.0-alpha-c9d165f6a.0",
4
4
  "peerDependencies": {
5
5
  "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
6
6
  },
7
7
  "dependencies": {
8
- "@graphql-codegen/core": "2.5.1-alpha-bbecef04a.0",
9
- "@graphql-codegen/plugin-helpers": "2.4.1-alpha-bbecef04a.0",
8
+ "@graphql-codegen/core": "2.5.0",
9
+ "@graphql-codegen/plugin-helpers": "^2.4.0",
10
10
  "@graphql-tools/apollo-engine-loader": "^7.0.5",
11
11
  "@graphql-tools/code-file-loader": "^7.0.6",
12
12
  "@graphql-tools/git-loader": "^7.0.5",
@@ -26,6 +26,7 @@
26
26
  "debounce": "^1.2.0",
27
27
  "dependency-graph": "^0.11.0",
28
28
  "detect-indent": "^6.0.0",
29
+ "fs-extra": "^9.1.0",
29
30
  "glob": "^7.1.6",
30
31
  "globby": "^11.0.4",
31
32
  "graphql-config": "^4.1.0",
@@ -37,7 +38,6 @@
37
38
  "listr-update-renderer": "^0.5.0",
38
39
  "log-symbols": "^4.0.0",
39
40
  "minimatch": "^3.0.4",
40
- "mkdirp": "^1.0.4",
41
41
  "string-env-interpolation": "^1.0.1",
42
42
  "ts-log": "^2.2.3",
43
43
  "tslib": "~2.3.0",
@@ -1,4 +1,4 @@
1
- export declare function writeSync(filepath: string, content: string): void;
2
- export declare function readSync(filepath: string): string;
3
- export declare function fileExists(filePath: string): boolean;
1
+ export declare function writeFile(filepath: string, content: string): any;
2
+ export declare function readFile(filepath: string): any;
3
+ export declare function fileExists(filePath: string): Promise<boolean>;
4
4
  export declare function unlinkFile(filePath: string, cb?: (err?: Error) => any): void;