@graphql-codegen/cli 2.6.0-alpha-88c2fecd3.0 → 2.6.0-alpha-a52c122aa.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,12 +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
+ const crypto = require('crypto');
57
57
  const Listr = _interopDefault(require('listr'));
58
58
  const isGlob = _interopDefault(require('is-glob'));
59
59
  const debounce = _interopDefault(require('debounce'));
60
60
  const mkdirp = _interopDefault(require('mkdirp'));
61
- const crypto = require('crypto');
62
61
  const inquirer = _interopDefault(require('inquirer'));
63
62
  const detectIndent = _interopDefault(require('detect-indent'));
64
63
  const getLatestVersion = _interopDefault(require('latest-version'));
@@ -779,23 +778,52 @@ class CodegenContext {
779
778
  const config = this.getConfig(defaultSchemaLoadOptions);
780
779
  if (this._graphqlConfig) {
781
780
  // TODO: SchemaWithLoader won't work here
782
- return this._graphqlConfig.getProject(this._project).loadSchema(pointer, 'GraphQLSchema', config);
781
+ return addHashToSchema(this._graphqlConfig.getProject(this._project).loadSchema(pointer, 'GraphQLSchema', config));
783
782
  }
784
- return loadSchema(pointer, config);
783
+ return addHashToSchema(loadSchema(pointer, config));
785
784
  }
786
785
  async loadDocuments(pointer) {
787
786
  const config = this.getConfig(defaultDocumentsLoadOptions);
788
787
  if (this._graphqlConfig) {
789
788
  // TODO: pointer won't work here
790
- const documents = await this._graphqlConfig.getProject(this._project).loadDocuments(pointer, config);
791
- return documents;
789
+ return addHashToDocumentFiles(this._graphqlConfig.getProject(this._project).loadDocuments(pointer, config));
792
790
  }
793
- return loadDocuments(pointer, config);
791
+ return addHashToDocumentFiles(loadDocuments(pointer, config));
794
792
  }
795
793
  }
796
794
  function ensureContext(input) {
797
795
  return input instanceof CodegenContext ? input : new CodegenContext({ config: input });
798
796
  }
797
+ function hashContent(content) {
798
+ return crypto.createHash('sha256').update(content).digest('hex');
799
+ }
800
+ function hashSchema(schema) {
801
+ return hashContent(utils.printSchemaWithDirectives(schema, {
802
+ assumeValid: true,
803
+ }));
804
+ }
805
+ function addHashToSchema(schemaPromise) {
806
+ return schemaPromise.then(schema => {
807
+ // It's consumed later on. The general purpose is to use it for caching.
808
+ schema.extensions['hash'] = hashSchema(schema);
809
+ return schema;
810
+ });
811
+ }
812
+ function hashDocument(doc) {
813
+ if (doc.rawSDL) {
814
+ return hashContent(doc.rawSDL);
815
+ }
816
+ if (doc.document) {
817
+ return hashContent(graphql.print(doc.document));
818
+ }
819
+ return null;
820
+ }
821
+ function addHashToDocumentFiles(documentFilesPromise) {
822
+ return documentFilesPromise.then(documentFiles => documentFiles.map(doc => {
823
+ doc.hash = hashDocument(doc);
824
+ return doc;
825
+ }));
826
+ }
799
827
 
800
828
  const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
801
829
  const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
@@ -833,18 +861,17 @@ const makeDefaultLoader = (from) => {
833
861
  return new Promise(function (resolve) { resolve(_interopNamespace(require(relativeRequire.resolve(mod)))); });
834
862
  };
835
863
  };
836
- // TODO: Replace any with types
837
- function createCache(loader) {
864
+ function createCache() {
838
865
  const cache = new Map();
839
- return {
840
- load(key) {
841
- if (cache.has(key)) {
842
- return cache.get(key);
843
- }
844
- const value = loader(key);
845
- cache.set(key, value);
846
- return value;
847
- },
866
+ return function ensure(namespace, key, factory) {
867
+ const cacheKey = `${namespace}:${key}`;
868
+ const cachedValue = cache.get(cacheKey);
869
+ if (cachedValue) {
870
+ return cachedValue;
871
+ }
872
+ const value = factory();
873
+ cache.set(cacheKey, value);
874
+ return value;
848
875
  };
849
876
  }
850
877
  async function executeCodegen(input) {
@@ -883,20 +910,7 @@ async function executeCodegen(input) {
883
910
  let rootSchemas;
884
911
  let rootDocuments;
885
912
  const generates = {};
886
- const schemaLoadingCache = createCache(async function (hash) {
887
- const outputSchemaAst = await context.loadSchema(JSON.parse(hash));
888
- const outputSchema = pluginHelpers.getCachedDocumentNodeFromSchema(outputSchemaAst);
889
- return {
890
- outputSchemaAst: outputSchemaAst,
891
- outputSchema: outputSchema,
892
- };
893
- });
894
- const documentsLoadingCache = createCache(async function (hash) {
895
- const documents = await context.loadDocuments(JSON.parse(hash));
896
- return {
897
- documents: documents,
898
- };
899
- });
913
+ const cache = createCache();
900
914
  function wrapTask(task, source, taskName) {
901
915
  return () => {
902
916
  return context.profiler.run(async () => {
@@ -1014,7 +1028,14 @@ async function executeCodegen(input) {
1014
1028
  }
1015
1029
  }
1016
1030
  const hash = JSON.stringify(schemaPointerMap);
1017
- const result = await schemaLoadingCache.load(hash);
1031
+ const result = await cache('schema', hash, async () => {
1032
+ const outputSchemaAst = await context.loadSchema(schemaPointerMap);
1033
+ const outputSchema = pluginHelpers.getCachedDocumentNodeFromSchema(outputSchemaAst);
1034
+ return {
1035
+ outputSchemaAst: outputSchemaAst,
1036
+ outputSchema: outputSchema,
1037
+ };
1038
+ });
1018
1039
  outputSchemaAst = await result.outputSchemaAst;
1019
1040
  outputSchema = result.outputSchema;
1020
1041
  }, filename, `Load GraphQL schemas: ${filename}`),
@@ -1026,7 +1047,12 @@ async function executeCodegen(input) {
1026
1047
  // get different cache for shared docs and output specific docs
1027
1048
  const results = await Promise.all([rootDocuments, outputSpecificDocuments].map(docs => {
1028
1049
  const hash = JSON.stringify(docs);
1029
- return documentsLoadingCache.load(hash);
1050
+ return cache('documents', hash, async () => {
1051
+ const documents = await context.loadDocuments(docs);
1052
+ return {
1053
+ documents: documents,
1054
+ };
1055
+ });
1030
1056
  }));
1031
1057
  const documents = [];
1032
1058
  results.forEach(source => documents.push(...source.documents));
@@ -1072,6 +1098,7 @@ async function executeCodegen(input) {
1072
1098
  pluginMap,
1073
1099
  pluginContext,
1074
1100
  profiler: context.profiler,
1101
+ cache,
1075
1102
  }), `Build Generates Section: ${filename}`);
1076
1103
  }
1077
1104
  else {
@@ -1086,6 +1113,7 @@ async function executeCodegen(input) {
1086
1113
  pluginMap,
1087
1114
  pluginContext,
1088
1115
  profiler: context.profiler,
1116
+ cache,
1089
1117
  },
1090
1118
  ];
1091
1119
  }
@@ -1109,7 +1137,8 @@ async function executeCodegen(input) {
1109
1137
  }), {
1110
1138
  // it doesn't stop when one of tasks failed, to finish at least some of outputs
1111
1139
  exitOnError: false,
1112
- concurrent: os.cpus().length,
1140
+ // run 4 at once
1141
+ concurrent: 4,
1113
1142
  });
1114
1143
  },
1115
1144
  });
@@ -1256,16 +1285,15 @@ const createWatcher = (initalContext, onNext) => {
1256
1285
  });
1257
1286
  };
1258
1287
 
1259
- const { writeFile: fsWriteFile, readFile: fsReadFile, stat: fsStat } = fs.promises;
1260
- function writeFile(filepath, content) {
1261
- return fsWriteFile(filepath, content);
1288
+ function writeSync(filepath, content) {
1289
+ return fs.writeFileSync(filepath, content);
1262
1290
  }
1263
- function readFile(filepath) {
1264
- return fsReadFile(filepath, 'utf-8');
1291
+ function readSync(filepath) {
1292
+ return fs.readFileSync(filepath, 'utf-8');
1265
1293
  }
1266
- async function fileExists(filePath) {
1294
+ function fileExists(filePath) {
1267
1295
  try {
1268
- return (await fsStat(filePath)).isFile();
1296
+ return fs.statSync(filePath).isFile();
1269
1297
  }
1270
1298
  catch (err) {
1271
1299
  return false;
@@ -1287,7 +1315,7 @@ async function generate(input, saveToFile = true) {
1287
1315
  const staleFilenames = previouslyGeneratedFilenames.filter(f => !filenames.includes(f));
1288
1316
  staleFilenames.forEach(filename => {
1289
1317
  if (shouldOverwrite(config, filename)) {
1290
- return unlinkFile(filename, err => {
1318
+ unlinkFile(filename, err => {
1291
1319
  const prettyFilename = filename.replace(`${input.cwd || process.cwd()}/`, '');
1292
1320
  if (err) {
1293
1321
  debugLog(`Cannot remove stale file: ${prettyFilename}\n${err}`);
@@ -1308,11 +1336,9 @@ async function generate(input, saveToFile = true) {
1308
1336
  if (config.watch) {
1309
1337
  removeStaleFiles(config, generationResult);
1310
1338
  }
1311
- await context.profiler.run(async () => {
1312
- await lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
1313
- }, 'Lifecycle: beforeAllFileWrite');
1339
+ await context.profiler.run(() => lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: beforeAllFileWrite');
1314
1340
  await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
1315
- const exists = await fileExists(result.filename);
1341
+ const exists = fileExists(result.filename);
1316
1342
  if (!shouldOverwrite(config, result.filename) && exists) {
1317
1343
  return;
1318
1344
  }
@@ -1320,10 +1346,10 @@ async function generate(input, saveToFile = true) {
1320
1346
  const currentHash = hash(content);
1321
1347
  let previousHash = recentOutputHash.get(result.filename);
1322
1348
  if (!previousHash && exists) {
1323
- previousHash = hash(await readFile(result.filename));
1349
+ previousHash = hash(readSync(result.filename));
1324
1350
  }
1325
1351
  if (previousHash && currentHash === previousHash) {
1326
- debugLog(`Skipping file (${result.filename}) writing due to indentical hash...`);
1352
+ debugLog(`Skipping file (${result.filename}) writing due to identical hash...`);
1327
1353
  return;
1328
1354
  }
1329
1355
  if (content.length === 0) {
@@ -1333,11 +1359,11 @@ async function generate(input, saveToFile = true) {
1333
1359
  const basedir = path.dirname(result.filename);
1334
1360
  await lifecycleHooks(result.hooks).beforeOneFileWrite(result.filename);
1335
1361
  await lifecycleHooks(config.hooks).beforeOneFileWrite(result.filename);
1336
- await mkdirp(basedir);
1362
+ mkdirp.sync(basedir);
1337
1363
  const absolutePath = path.isAbsolute(result.filename)
1338
1364
  ? result.filename
1339
1365
  : path.join(input.cwd || process.cwd(), result.filename);
1340
- await writeFile(absolutePath, result.content);
1366
+ writeSync(absolutePath, result.content);
1341
1367
  await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
1342
1368
  await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
1343
1369
  })), 'Write files');
@@ -1352,7 +1378,7 @@ async function generate(input, saveToFile = true) {
1352
1378
  await context.profiler.run(() => writeOutput(outputFiles), 'writeOutput');
1353
1379
  await context.profiler.run(() => lifecycleHooks(config.hooks).beforeDone(), 'Lifecycle: beforeDone');
1354
1380
  if (context.profilerOutput) {
1355
- await writeFile(path.join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1381
+ writeSync(path.join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1356
1382
  }
1357
1383
  return outputFiles;
1358
1384
  }
package/index.js CHANGED
@@ -53,13 +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
+ const crypto = require('crypto');
57
57
  const Listr = _interopDefault(require('listr'));
58
58
  const child_process = require('child_process');
59
59
  const isGlob = _interopDefault(require('is-glob'));
60
60
  const debounce = _interopDefault(require('debounce'));
61
61
  const mkdirp = _interopDefault(require('mkdirp'));
62
- const crypto = require('crypto');
63
62
  const inquirer = _interopDefault(require('inquirer'));
64
63
  const detectIndent = _interopDefault(require('detect-indent'));
65
64
  const getLatestVersion = _interopDefault(require('latest-version'));
@@ -697,23 +696,52 @@ class CodegenContext {
697
696
  const config = this.getConfig(defaultSchemaLoadOptions);
698
697
  if (this._graphqlConfig) {
699
698
  // TODO: SchemaWithLoader won't work here
700
- return this._graphqlConfig.getProject(this._project).loadSchema(pointer, 'GraphQLSchema', config);
699
+ return addHashToSchema(this._graphqlConfig.getProject(this._project).loadSchema(pointer, 'GraphQLSchema', config));
701
700
  }
702
- return loadSchema(pointer, config);
701
+ return addHashToSchema(loadSchema(pointer, config));
703
702
  }
704
703
  async loadDocuments(pointer) {
705
704
  const config = this.getConfig(defaultDocumentsLoadOptions);
706
705
  if (this._graphqlConfig) {
707
706
  // TODO: pointer won't work here
708
- const documents = await this._graphqlConfig.getProject(this._project).loadDocuments(pointer, config);
709
- return documents;
707
+ return addHashToDocumentFiles(this._graphqlConfig.getProject(this._project).loadDocuments(pointer, config));
710
708
  }
711
- return loadDocuments(pointer, config);
709
+ return addHashToDocumentFiles(loadDocuments(pointer, config));
712
710
  }
713
711
  }
714
712
  function ensureContext(input) {
715
713
  return input instanceof CodegenContext ? input : new CodegenContext({ config: input });
716
714
  }
715
+ function hashContent(content) {
716
+ return crypto.createHash('sha256').update(content).digest('hex');
717
+ }
718
+ function hashSchema(schema) {
719
+ return hashContent(utils.printSchemaWithDirectives(schema, {
720
+ assumeValid: true,
721
+ }));
722
+ }
723
+ function addHashToSchema(schemaPromise) {
724
+ return schemaPromise.then(schema => {
725
+ // It's consumed later on. The general purpose is to use it for caching.
726
+ schema.extensions['hash'] = hashSchema(schema);
727
+ return schema;
728
+ });
729
+ }
730
+ function hashDocument(doc) {
731
+ if (doc.rawSDL) {
732
+ return hashContent(doc.rawSDL);
733
+ }
734
+ if (doc.document) {
735
+ return hashContent(graphql.print(doc.document));
736
+ }
737
+ return null;
738
+ }
739
+ function addHashToDocumentFiles(documentFilesPromise) {
740
+ return documentFilesPromise.then(documentFiles => documentFiles.map(doc => {
741
+ doc.hash = hashDocument(doc);
742
+ return doc;
743
+ }));
744
+ }
717
745
 
718
746
  const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
719
747
  const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
@@ -751,18 +779,17 @@ const makeDefaultLoader = (from) => {
751
779
  return new Promise(function (resolve) { resolve(_interopNamespace(require(relativeRequire.resolve(mod)))); });
752
780
  };
753
781
  };
754
- // TODO: Replace any with types
755
- function createCache(loader) {
782
+ function createCache() {
756
783
  const cache = new Map();
757
- return {
758
- load(key) {
759
- if (cache.has(key)) {
760
- return cache.get(key);
761
- }
762
- const value = loader(key);
763
- cache.set(key, value);
764
- return value;
765
- },
784
+ return function ensure(namespace, key, factory) {
785
+ const cacheKey = `${namespace}:${key}`;
786
+ const cachedValue = cache.get(cacheKey);
787
+ if (cachedValue) {
788
+ return cachedValue;
789
+ }
790
+ const value = factory();
791
+ cache.set(cacheKey, value);
792
+ return value;
766
793
  };
767
794
  }
768
795
  async function executeCodegen(input) {
@@ -801,20 +828,7 @@ async function executeCodegen(input) {
801
828
  let rootSchemas;
802
829
  let rootDocuments;
803
830
  const generates = {};
804
- const schemaLoadingCache = createCache(async function (hash) {
805
- const outputSchemaAst = await context.loadSchema(JSON.parse(hash));
806
- const outputSchema = pluginHelpers.getCachedDocumentNodeFromSchema(outputSchemaAst);
807
- return {
808
- outputSchemaAst: outputSchemaAst,
809
- outputSchema: outputSchema,
810
- };
811
- });
812
- const documentsLoadingCache = createCache(async function (hash) {
813
- const documents = await context.loadDocuments(JSON.parse(hash));
814
- return {
815
- documents: documents,
816
- };
817
- });
831
+ const cache = createCache();
818
832
  function wrapTask(task, source, taskName) {
819
833
  return () => {
820
834
  return context.profiler.run(async () => {
@@ -932,7 +946,14 @@ async function executeCodegen(input) {
932
946
  }
933
947
  }
934
948
  const hash = JSON.stringify(schemaPointerMap);
935
- const result = await schemaLoadingCache.load(hash);
949
+ const result = await cache('schema', hash, async () => {
950
+ const outputSchemaAst = await context.loadSchema(schemaPointerMap);
951
+ const outputSchema = pluginHelpers.getCachedDocumentNodeFromSchema(outputSchemaAst);
952
+ return {
953
+ outputSchemaAst: outputSchemaAst,
954
+ outputSchema: outputSchema,
955
+ };
956
+ });
936
957
  outputSchemaAst = await result.outputSchemaAst;
937
958
  outputSchema = result.outputSchema;
938
959
  }, filename, `Load GraphQL schemas: ${filename}`),
@@ -944,7 +965,12 @@ async function executeCodegen(input) {
944
965
  // get different cache for shared docs and output specific docs
945
966
  const results = await Promise.all([rootDocuments, outputSpecificDocuments].map(docs => {
946
967
  const hash = JSON.stringify(docs);
947
- return documentsLoadingCache.load(hash);
968
+ return cache('documents', hash, async () => {
969
+ const documents = await context.loadDocuments(docs);
970
+ return {
971
+ documents: documents,
972
+ };
973
+ });
948
974
  }));
949
975
  const documents = [];
950
976
  results.forEach(source => documents.push(...source.documents));
@@ -990,6 +1016,7 @@ async function executeCodegen(input) {
990
1016
  pluginMap,
991
1017
  pluginContext,
992
1018
  profiler: context.profiler,
1019
+ cache,
993
1020
  }), `Build Generates Section: ${filename}`);
994
1021
  }
995
1022
  else {
@@ -1004,6 +1031,7 @@ async function executeCodegen(input) {
1004
1031
  pluginMap,
1005
1032
  pluginContext,
1006
1033
  profiler: context.profiler,
1034
+ cache,
1007
1035
  },
1008
1036
  ];
1009
1037
  }
@@ -1027,7 +1055,8 @@ async function executeCodegen(input) {
1027
1055
  }), {
1028
1056
  // it doesn't stop when one of tasks failed, to finish at least some of outputs
1029
1057
  exitOnError: false,
1030
- concurrent: os.cpus().length,
1058
+ // run 4 at once
1059
+ concurrent: 4,
1031
1060
  });
1032
1061
  },
1033
1062
  });
@@ -1257,16 +1286,15 @@ const createWatcher = (initalContext, onNext) => {
1257
1286
  });
1258
1287
  };
1259
1288
 
1260
- const { writeFile: fsWriteFile, readFile: fsReadFile, stat: fsStat } = fs.promises;
1261
- function writeFile(filepath, content) {
1262
- return fsWriteFile(filepath, content);
1289
+ function writeSync(filepath, content) {
1290
+ return fs.writeFileSync(filepath, content);
1263
1291
  }
1264
- function readFile(filepath) {
1265
- return fsReadFile(filepath, 'utf-8');
1292
+ function readSync(filepath) {
1293
+ return fs.readFileSync(filepath, 'utf-8');
1266
1294
  }
1267
- async function fileExists(filePath) {
1295
+ function fileExists(filePath) {
1268
1296
  try {
1269
- return (await fsStat(filePath)).isFile();
1297
+ return fs.statSync(filePath).isFile();
1270
1298
  }
1271
1299
  catch (err) {
1272
1300
  return false;
@@ -1288,7 +1316,7 @@ async function generate(input, saveToFile = true) {
1288
1316
  const staleFilenames = previouslyGeneratedFilenames.filter(f => !filenames.includes(f));
1289
1317
  staleFilenames.forEach(filename => {
1290
1318
  if (shouldOverwrite(config, filename)) {
1291
- return unlinkFile(filename, err => {
1319
+ unlinkFile(filename, err => {
1292
1320
  const prettyFilename = filename.replace(`${input.cwd || process.cwd()}/`, '');
1293
1321
  if (err) {
1294
1322
  debugLog(`Cannot remove stale file: ${prettyFilename}\n${err}`);
@@ -1309,11 +1337,9 @@ async function generate(input, saveToFile = true) {
1309
1337
  if (config.watch) {
1310
1338
  removeStaleFiles(config, generationResult);
1311
1339
  }
1312
- await context.profiler.run(async () => {
1313
- await lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
1314
- }, 'Lifecycle: beforeAllFileWrite');
1340
+ await context.profiler.run(() => lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: beforeAllFileWrite');
1315
1341
  await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
1316
- const exists = await fileExists(result.filename);
1342
+ const exists = fileExists(result.filename);
1317
1343
  if (!shouldOverwrite(config, result.filename) && exists) {
1318
1344
  return;
1319
1345
  }
@@ -1321,10 +1347,10 @@ async function generate(input, saveToFile = true) {
1321
1347
  const currentHash = hash(content);
1322
1348
  let previousHash = recentOutputHash.get(result.filename);
1323
1349
  if (!previousHash && exists) {
1324
- previousHash = hash(await readFile(result.filename));
1350
+ previousHash = hash(readSync(result.filename));
1325
1351
  }
1326
1352
  if (previousHash && currentHash === previousHash) {
1327
- debugLog(`Skipping file (${result.filename}) writing due to indentical hash...`);
1353
+ debugLog(`Skipping file (${result.filename}) writing due to identical hash...`);
1328
1354
  return;
1329
1355
  }
1330
1356
  if (content.length === 0) {
@@ -1334,11 +1360,11 @@ async function generate(input, saveToFile = true) {
1334
1360
  const basedir = path.dirname(result.filename);
1335
1361
  await lifecycleHooks(result.hooks).beforeOneFileWrite(result.filename);
1336
1362
  await lifecycleHooks(config.hooks).beforeOneFileWrite(result.filename);
1337
- await mkdirp(basedir);
1363
+ mkdirp.sync(basedir);
1338
1364
  const absolutePath = path.isAbsolute(result.filename)
1339
1365
  ? result.filename
1340
1366
  : path.join(input.cwd || process.cwd(), result.filename);
1341
- await writeFile(absolutePath, result.content);
1367
+ writeSync(absolutePath, result.content);
1342
1368
  await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
1343
1369
  await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
1344
1370
  })), 'Write files');
@@ -1353,7 +1379,7 @@ async function generate(input, saveToFile = true) {
1353
1379
  await context.profiler.run(() => writeOutput(outputFiles), 'writeOutput');
1354
1380
  await context.profiler.run(() => lifecycleHooks(config.hooks).beforeDone(), 'Lifecycle: beforeDone');
1355
1381
  if (context.profilerOutput) {
1356
- await writeFile(path.join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1382
+ writeSync(path.join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1357
1383
  }
1358
1384
  return outputFiles;
1359
1385
  }
package/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { isDetailedError, DetailedError, createNoopProfiler, createProfiler, normalizeInstanceOrArray, normalizeConfig, normalizeOutputParam, getCachedDocumentNodeFromSchema } from '@graphql-codegen/plugin-helpers';
2
2
  import { codegen } from '@graphql-codegen/core';
3
- import { AggregateError, isValidPath } from '@graphql-tools/utils';
3
+ import { printSchemaWithDirectives, AggregateError, isValidPath } from '@graphql-tools/utils';
4
4
  import chalk from 'chalk';
5
5
  import logSymbols from 'log-symbols';
6
6
  import ansiEscapes from 'ansi-escapes';
@@ -8,7 +8,7 @@ import wrapAnsi from 'wrap-ansi';
8
8
  import { stripIndent } from 'common-tags';
9
9
  import { dummyLogger } from 'ts-log';
10
10
  import UpdateRenderer from 'listr-update-renderer';
11
- import { GraphQLError } from 'graphql';
11
+ import { print, GraphQLError } from 'graphql';
12
12
  import path, { resolve, extname, join, delimiter, sep, dirname, isAbsolute, relative } from 'path';
13
13
  import { cosmiconfig, defaultLoaders } from 'cosmiconfig';
14
14
  import { env } from 'string-env-interpolation';
@@ -25,14 +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, unlink, writeFileSync, readFileSync } from 'fs';
29
- import { cpus } from 'os';
28
+ import fs, { promises, writeFileSync, readFileSync, statSync, unlink } from 'fs';
29
+ import { createHash } from 'crypto';
30
30
  import Listr from 'listr';
31
31
  import { exec } from 'child_process';
32
32
  import isGlob from 'is-glob';
33
33
  import debounce from 'debounce';
34
34
  import mkdirp from 'mkdirp';
35
- import { createHash } from 'crypto';
36
35
  import inquirer from 'inquirer';
37
36
  import detectIndent from 'detect-indent';
38
37
  import getLatestVersion from 'latest-version';
@@ -670,23 +669,52 @@ class CodegenContext {
670
669
  const config = this.getConfig(defaultSchemaLoadOptions);
671
670
  if (this._graphqlConfig) {
672
671
  // TODO: SchemaWithLoader won't work here
673
- return this._graphqlConfig.getProject(this._project).loadSchema(pointer, 'GraphQLSchema', config);
672
+ return addHashToSchema(this._graphqlConfig.getProject(this._project).loadSchema(pointer, 'GraphQLSchema', config));
674
673
  }
675
- return loadSchema(pointer, config);
674
+ return addHashToSchema(loadSchema(pointer, config));
676
675
  }
677
676
  async loadDocuments(pointer) {
678
677
  const config = this.getConfig(defaultDocumentsLoadOptions);
679
678
  if (this._graphqlConfig) {
680
679
  // TODO: pointer won't work here
681
- const documents = await this._graphqlConfig.getProject(this._project).loadDocuments(pointer, config);
682
- return documents;
680
+ return addHashToDocumentFiles(this._graphqlConfig.getProject(this._project).loadDocuments(pointer, config));
683
681
  }
684
- return loadDocuments(pointer, config);
682
+ return addHashToDocumentFiles(loadDocuments(pointer, config));
685
683
  }
686
684
  }
687
685
  function ensureContext(input) {
688
686
  return input instanceof CodegenContext ? input : new CodegenContext({ config: input });
689
687
  }
688
+ function hashContent(content) {
689
+ return createHash('sha256').update(content).digest('hex');
690
+ }
691
+ function hashSchema(schema) {
692
+ return hashContent(printSchemaWithDirectives(schema, {
693
+ assumeValid: true,
694
+ }));
695
+ }
696
+ function addHashToSchema(schemaPromise) {
697
+ return schemaPromise.then(schema => {
698
+ // It's consumed later on. The general purpose is to use it for caching.
699
+ schema.extensions['hash'] = hashSchema(schema);
700
+ return schema;
701
+ });
702
+ }
703
+ function hashDocument(doc) {
704
+ if (doc.rawSDL) {
705
+ return hashContent(doc.rawSDL);
706
+ }
707
+ if (doc.document) {
708
+ return hashContent(print(doc.document));
709
+ }
710
+ return null;
711
+ }
712
+ function addHashToDocumentFiles(documentFilesPromise) {
713
+ return documentFilesPromise.then(documentFiles => documentFiles.map(doc => {
714
+ doc.hash = hashDocument(doc);
715
+ return doc;
716
+ }));
717
+ }
690
718
 
691
719
  const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
692
720
  const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
@@ -724,18 +752,17 @@ const makeDefaultLoader = (from) => {
724
752
  return import(relativeRequire.resolve(mod));
725
753
  };
726
754
  };
727
- // TODO: Replace any with types
728
- function createCache(loader) {
755
+ function createCache() {
729
756
  const cache = new Map();
730
- return {
731
- load(key) {
732
- if (cache.has(key)) {
733
- return cache.get(key);
734
- }
735
- const value = loader(key);
736
- cache.set(key, value);
737
- return value;
738
- },
757
+ return function ensure(namespace, key, factory) {
758
+ const cacheKey = `${namespace}:${key}`;
759
+ const cachedValue = cache.get(cacheKey);
760
+ if (cachedValue) {
761
+ return cachedValue;
762
+ }
763
+ const value = factory();
764
+ cache.set(cacheKey, value);
765
+ return value;
739
766
  };
740
767
  }
741
768
  async function executeCodegen(input) {
@@ -774,20 +801,7 @@ async function executeCodegen(input) {
774
801
  let rootSchemas;
775
802
  let rootDocuments;
776
803
  const generates = {};
777
- const schemaLoadingCache = createCache(async function (hash) {
778
- const outputSchemaAst = await context.loadSchema(JSON.parse(hash));
779
- const outputSchema = getCachedDocumentNodeFromSchema(outputSchemaAst);
780
- return {
781
- outputSchemaAst: outputSchemaAst,
782
- outputSchema: outputSchema,
783
- };
784
- });
785
- const documentsLoadingCache = createCache(async function (hash) {
786
- const documents = await context.loadDocuments(JSON.parse(hash));
787
- return {
788
- documents: documents,
789
- };
790
- });
804
+ const cache = createCache();
791
805
  function wrapTask(task, source, taskName) {
792
806
  return () => {
793
807
  return context.profiler.run(async () => {
@@ -905,7 +919,14 @@ async function executeCodegen(input) {
905
919
  }
906
920
  }
907
921
  const hash = JSON.stringify(schemaPointerMap);
908
- const result = await schemaLoadingCache.load(hash);
922
+ const result = await cache('schema', hash, async () => {
923
+ const outputSchemaAst = await context.loadSchema(schemaPointerMap);
924
+ const outputSchema = getCachedDocumentNodeFromSchema(outputSchemaAst);
925
+ return {
926
+ outputSchemaAst: outputSchemaAst,
927
+ outputSchema: outputSchema,
928
+ };
929
+ });
909
930
  outputSchemaAst = await result.outputSchemaAst;
910
931
  outputSchema = result.outputSchema;
911
932
  }, filename, `Load GraphQL schemas: ${filename}`),
@@ -917,7 +938,12 @@ async function executeCodegen(input) {
917
938
  // get different cache for shared docs and output specific docs
918
939
  const results = await Promise.all([rootDocuments, outputSpecificDocuments].map(docs => {
919
940
  const hash = JSON.stringify(docs);
920
- return documentsLoadingCache.load(hash);
941
+ return cache('documents', hash, async () => {
942
+ const documents = await context.loadDocuments(docs);
943
+ return {
944
+ documents: documents,
945
+ };
946
+ });
921
947
  }));
922
948
  const documents = [];
923
949
  results.forEach(source => documents.push(...source.documents));
@@ -963,6 +989,7 @@ async function executeCodegen(input) {
963
989
  pluginMap,
964
990
  pluginContext,
965
991
  profiler: context.profiler,
992
+ cache,
966
993
  }), `Build Generates Section: ${filename}`);
967
994
  }
968
995
  else {
@@ -977,6 +1004,7 @@ async function executeCodegen(input) {
977
1004
  pluginMap,
978
1005
  pluginContext,
979
1006
  profiler: context.profiler,
1007
+ cache,
980
1008
  },
981
1009
  ];
982
1010
  }
@@ -1000,7 +1028,8 @@ async function executeCodegen(input) {
1000
1028
  }), {
1001
1029
  // it doesn't stop when one of tasks failed, to finish at least some of outputs
1002
1030
  exitOnError: false,
1003
- concurrent: cpus().length,
1031
+ // run 4 at once
1032
+ concurrent: 4,
1004
1033
  });
1005
1034
  },
1006
1035
  });
@@ -1230,16 +1259,15 @@ const createWatcher = (initalContext, onNext) => {
1230
1259
  });
1231
1260
  };
1232
1261
 
1233
- const { writeFile: fsWriteFile, readFile: fsReadFile, stat: fsStat } = promises;
1234
- function writeFile(filepath, content) {
1235
- return fsWriteFile(filepath, content);
1262
+ function writeSync(filepath, content) {
1263
+ return writeFileSync(filepath, content);
1236
1264
  }
1237
- function readFile(filepath) {
1238
- return fsReadFile(filepath, 'utf-8');
1265
+ function readSync(filepath) {
1266
+ return readFileSync(filepath, 'utf-8');
1239
1267
  }
1240
- async function fileExists(filePath) {
1268
+ function fileExists(filePath) {
1241
1269
  try {
1242
- return (await fsStat(filePath)).isFile();
1270
+ return statSync(filePath).isFile();
1243
1271
  }
1244
1272
  catch (err) {
1245
1273
  return false;
@@ -1261,7 +1289,7 @@ async function generate(input, saveToFile = true) {
1261
1289
  const staleFilenames = previouslyGeneratedFilenames.filter(f => !filenames.includes(f));
1262
1290
  staleFilenames.forEach(filename => {
1263
1291
  if (shouldOverwrite(config, filename)) {
1264
- return unlinkFile(filename, err => {
1292
+ unlinkFile(filename, err => {
1265
1293
  const prettyFilename = filename.replace(`${input.cwd || process.cwd()}/`, '');
1266
1294
  if (err) {
1267
1295
  debugLog(`Cannot remove stale file: ${prettyFilename}\n${err}`);
@@ -1282,11 +1310,9 @@ async function generate(input, saveToFile = true) {
1282
1310
  if (config.watch) {
1283
1311
  removeStaleFiles(config, generationResult);
1284
1312
  }
1285
- await context.profiler.run(async () => {
1286
- await lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
1287
- }, 'Lifecycle: beforeAllFileWrite');
1313
+ await context.profiler.run(() => lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: beforeAllFileWrite');
1288
1314
  await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
1289
- const exists = await fileExists(result.filename);
1315
+ const exists = fileExists(result.filename);
1290
1316
  if (!shouldOverwrite(config, result.filename) && exists) {
1291
1317
  return;
1292
1318
  }
@@ -1294,10 +1320,10 @@ async function generate(input, saveToFile = true) {
1294
1320
  const currentHash = hash(content);
1295
1321
  let previousHash = recentOutputHash.get(result.filename);
1296
1322
  if (!previousHash && exists) {
1297
- previousHash = hash(await readFile(result.filename));
1323
+ previousHash = hash(readSync(result.filename));
1298
1324
  }
1299
1325
  if (previousHash && currentHash === previousHash) {
1300
- debugLog(`Skipping file (${result.filename}) writing due to indentical hash...`);
1326
+ debugLog(`Skipping file (${result.filename}) writing due to identical hash...`);
1301
1327
  return;
1302
1328
  }
1303
1329
  if (content.length === 0) {
@@ -1307,11 +1333,11 @@ async function generate(input, saveToFile = true) {
1307
1333
  const basedir = dirname(result.filename);
1308
1334
  await lifecycleHooks(result.hooks).beforeOneFileWrite(result.filename);
1309
1335
  await lifecycleHooks(config.hooks).beforeOneFileWrite(result.filename);
1310
- await mkdirp(basedir);
1336
+ mkdirp.sync(basedir);
1311
1337
  const absolutePath = isAbsolute(result.filename)
1312
1338
  ? result.filename
1313
1339
  : join(input.cwd || process.cwd(), result.filename);
1314
- await writeFile(absolutePath, result.content);
1340
+ writeSync(absolutePath, result.content);
1315
1341
  await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
1316
1342
  await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
1317
1343
  })), 'Write files');
@@ -1326,7 +1352,7 @@ async function generate(input, saveToFile = true) {
1326
1352
  await context.profiler.run(() => writeOutput(outputFiles), 'writeOutput');
1327
1353
  await context.profiler.run(() => lifecycleHooks(config.hooks).beforeDone(), 'Lifecycle: beforeDone');
1328
1354
  if (context.profilerOutput) {
1329
- await writeFile(join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1355
+ writeSync(join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1330
1356
  }
1331
1357
  return outputFiles;
1332
1358
  }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@graphql-codegen/cli",
3
- "version": "2.6.0-alpha-88c2fecd3.0",
3
+ "version": "2.6.0-alpha-a52c122aa.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.0",
9
- "@graphql-codegen/plugin-helpers": "^2.4.0",
8
+ "@graphql-codegen/core": "2.6.0-alpha-a52c122aa.0",
9
+ "@graphql-codegen/plugin-helpers": "2.5.0-alpha-a52c122aa.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",
@@ -1,4 +1,4 @@
1
- export declare function writeFile(filepath: string, content: string): Promise<void>;
2
- export declare function readFile(filepath: string): Promise<string>;
3
- export declare function fileExists(filePath: string): Promise<boolean>;
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;
4
4
  export declare function unlinkFile(filePath: string, cb?: (err?: Error) => any): void;