@graphql-codegen/cli 2.5.0-alpha-bc33c031d.0 → 2.6.0-alpha-bbecef04a.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
@@ -28,6 +28,7 @@ const path = require('path');
28
28
  const path__default = _interopDefault(path);
29
29
  const pluginHelpers = require('@graphql-codegen/plugin-helpers');
30
30
  const core = require('@graphql-codegen/core');
31
+ const utils = require('@graphql-tools/utils');
31
32
  const chalk = _interopDefault(require('chalk'));
32
33
  const logSymbols = _interopDefault(require('log-symbols'));
33
34
  const ansiEscapes = _interopDefault(require('ansi-escapes'));
@@ -55,7 +56,6 @@ const fs__default = _interopDefault(fs);
55
56
  const Listr = _interopDefault(require('listr'));
56
57
  const isGlob = _interopDefault(require('is-glob'));
57
58
  const debounce = _interopDefault(require('debounce'));
58
- const utils = require('@graphql-tools/utils');
59
59
  const mkdirp = _interopDefault(require('mkdirp'));
60
60
  const crypto = require('crypto');
61
61
  const inquirer = _interopDefault(require('inquirer'));
@@ -707,6 +707,10 @@ function buildOptions() {
707
707
  describe: 'Only print errors',
708
708
  type: 'boolean',
709
709
  },
710
+ profile: {
711
+ describe: 'Use profiler to measure performance',
712
+ type: 'boolean',
713
+ },
710
714
  p: {
711
715
  alias: 'project',
712
716
  describe: 'Name of a project in GraphQL Config',
@@ -748,6 +752,9 @@ function updateContextWithCliFlags(context, cliFlags) {
748
752
  if (cliFlags.project) {
749
753
  context.useProject(cliFlags.project);
750
754
  }
755
+ if (cliFlags.profile === true) {
756
+ context.useProfiler();
757
+ }
751
758
  context.updateConfig(config);
752
759
  }
753
760
  class CodegenContext {
@@ -757,6 +764,7 @@ class CodegenContext {
757
764
  this._graphqlConfig = graphqlConfig;
758
765
  this.filepath = this._graphqlConfig ? this._graphqlConfig.filepath : filepath;
759
766
  this.cwd = this._graphqlConfig ? this._graphqlConfig.dirpath : process.cwd();
767
+ this.profiler = pluginHelpers.createNoopProfiler();
760
768
  }
761
769
  useProject(name) {
762
770
  this._project = name;
@@ -787,6 +795,13 @@ class CodegenContext {
787
795
  ...config,
788
796
  };
789
797
  }
798
+ useProfiler() {
799
+ this.profiler = pluginHelpers.createProfiler();
800
+ const now = new Date(); // 2011-10-05T14:48:00.000Z
801
+ const datetime = now.toISOString().split('.')[0]; // 2011-10-05T14:48:00
802
+ const datetimeNormalized = datetime.replace(/-|:/g, ''); // 20111005T144800
803
+ this.profilerOutput = `codegen-${datetimeNormalized}.json`;
804
+ }
790
805
  getPluginContext() {
791
806
  return this._pluginContext;
792
807
  }
@@ -812,6 +827,33 @@ function ensureContext(input) {
812
827
  return input instanceof CodegenContext ? input : new CodegenContext({ config: input });
813
828
  }
814
829
 
830
+ const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
831
+ const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
832
+
833
+ function isListrError(err) {
834
+ return err.name === 'ListrError' && Array.isArray(err.errors) && err.errors.length > 0;
835
+ }
836
+ function cliError(err, exitOnError = true) {
837
+ let msg;
838
+ if (err instanceof Error) {
839
+ msg = err.message || err.toString();
840
+ }
841
+ else if (typeof err === 'string') {
842
+ msg = err;
843
+ }
844
+ else {
845
+ msg = JSON.stringify(err);
846
+ }
847
+ // eslint-disable-next-line no-console
848
+ console.error(msg);
849
+ if (exitOnError && isNode) {
850
+ process.exit(1);
851
+ }
852
+ else if (exitOnError && isBrowser) {
853
+ throw err;
854
+ }
855
+ }
856
+
815
857
  const makeDefaultLoader = (from) => {
816
858
  if (fs__default.statSync(from).isDirectory()) {
817
859
  from = path__default.join(from, '__fake.js');
@@ -836,19 +878,6 @@ function createCache(loader) {
836
878
  };
837
879
  }
838
880
  async function executeCodegen(input) {
839
- function wrapTask(task, source) {
840
- return async () => {
841
- try {
842
- await Promise.resolve().then(() => task());
843
- }
844
- catch (error) {
845
- if (source && !(error instanceof graphql.GraphQLError)) {
846
- error.source = source;
847
- }
848
- throw error;
849
- }
850
- };
851
- }
852
881
  const context = ensureContext(input);
853
882
  const config = context.getConfig();
854
883
  const pluginContext = context.getPluginContext();
@@ -898,6 +927,21 @@ async function executeCodegen(input) {
898
927
  documents,
899
928
  };
900
929
  });
930
+ function wrapTask(task, source, taskName) {
931
+ return () => {
932
+ return context.profiler.run(async () => {
933
+ try {
934
+ await Promise.resolve().then(() => task());
935
+ }
936
+ catch (error) {
937
+ if (source && !(error instanceof graphql.GraphQLError)) {
938
+ error.source = source;
939
+ }
940
+ throw error;
941
+ }
942
+ }, taskName);
943
+ };
944
+ }
901
945
  async function normalize() {
902
946
  /* Load Require extensions */
903
947
  const requireExtensions = pluginHelpers.normalizeInstanceOrArray(config.require);
@@ -1003,20 +1047,23 @@ async function executeCodegen(input) {
1003
1047
  const result = await schemaLoadingCache.load(hash);
1004
1048
  outputSchemaAst = await result.outputSchemaAst;
1005
1049
  outputSchema = result.outputSchema;
1006
- }, filename),
1050
+ }, filename, `Load GraphQL schemas: ${filename}`),
1007
1051
  },
1008
1052
  {
1009
1053
  title: 'Load GraphQL documents',
1010
1054
  task: wrapTask(async () => {
1011
1055
  debugLog(`[CLI] Loading Documents`);
1012
- const allDocuments = [...rootDocuments, ...outputSpecificDocuments];
1013
- const hash = JSON.stringify({ pointer: allDocuments, schema: outputSchema });
1014
- const result = await documentsLoadingCache.load(hash);
1015
- const documents = result.documents;
1056
+ // get different cache for shared docs and output specific docs
1057
+ const results = await Promise.all([rootDocuments, outputSpecificDocuments].map(docs => {
1058
+ const hash = JSON.stringify({ pointer: docs, schema: outputSchema });
1059
+ return documentsLoadingCache.load(hash);
1060
+ }));
1061
+ const documents = [];
1062
+ results.forEach(source => documents.push(...source.documents));
1016
1063
  if (documents.length > 0) {
1017
1064
  outputDocuments.push(...documents);
1018
1065
  }
1019
- }, filename),
1066
+ }, filename, `Load GraphQL documents: ${filename}`),
1020
1067
  },
1021
1068
  {
1022
1069
  title: 'Generate',
@@ -1044,7 +1091,7 @@ async function executeCodegen(input) {
1044
1091
  };
1045
1092
  let outputs = [];
1046
1093
  if (hasPreset) {
1047
- outputs = await preset.buildGeneratesSection({
1094
+ outputs = await context.profiler.run(async () => preset.buildGeneratesSection({
1048
1095
  baseOutputDir: filename,
1049
1096
  presetConfig: outputConfig.presetConfig || {},
1050
1097
  plugins: normalizedPluginsArray,
@@ -1054,7 +1101,8 @@ async function executeCodegen(input) {
1054
1101
  config: mergedConfig,
1055
1102
  pluginMap,
1056
1103
  pluginContext,
1057
- });
1104
+ profiler: context.profiler,
1105
+ }), `Build Generates Section: ${filename}`);
1058
1106
  }
1059
1107
  else {
1060
1108
  outputs = [
@@ -1067,6 +1115,7 @@ async function executeCodegen(input) {
1067
1115
  config: mergedConfig,
1068
1116
  pluginMap,
1069
1117
  pluginContext,
1118
+ profiler: context.profiler,
1070
1119
  },
1071
1120
  ];
1072
1121
  }
@@ -1078,8 +1127,8 @@ async function executeCodegen(input) {
1078
1127
  hooks: outputConfig.hooks || {},
1079
1128
  });
1080
1129
  };
1081
- await Promise.all(outputs.map(process));
1082
- }, filename),
1130
+ await context.profiler.run(() => Promise.all(outputs.map(process)), `Codegen: ${filename}`);
1131
+ }, filename, `Generate: ${filename}`),
1083
1132
  },
1084
1133
  ], {
1085
1134
  // it stops when one of tasks failed
@@ -1095,7 +1144,21 @@ async function executeCodegen(input) {
1095
1144
  });
1096
1145
  },
1097
1146
  });
1098
- await listr.run();
1147
+ try {
1148
+ await listr.run();
1149
+ }
1150
+ catch (err) {
1151
+ if (isListrError(err)) {
1152
+ const allErrs = err.errors.map(subErr => pluginHelpers.isDetailedError(subErr)
1153
+ ? `${subErr.message} for "${subErr.source}"${subErr.details}`
1154
+ : subErr.message || subErr.toString());
1155
+ const newErr = new utils.AggregateError(err.errors, `${err.message} ${allErrs.join('\n\n')}`);
1156
+ // Best-effort to all stack traces for debugging
1157
+ newErr.stack = `${newErr.stack}\n\n${err.errors.map(subErr => subErr.stack).join('\n\n')}`;
1158
+ throw newErr;
1159
+ }
1160
+ throw err;
1161
+ }
1099
1162
  return result;
1100
1163
  }
1101
1164
 
@@ -1246,7 +1309,7 @@ const hash = (content) => crypto.createHash('sha1').update(content).digest('base
1246
1309
  async function generate(input, saveToFile = true) {
1247
1310
  const context = ensureContext(input);
1248
1311
  const config = context.getConfig();
1249
- await lifecycleHooks(config.hooks).afterStart();
1312
+ await context.profiler.run(() => lifecycleHooks(config.hooks).afterStart(), 'Lifecycle: afterStart');
1250
1313
  let previouslyGeneratedFilenames = [];
1251
1314
  function removeStaleFiles(config, generationResult) {
1252
1315
  const filenames = generationResult.map(o => o.filename);
@@ -1275,8 +1338,8 @@ async function generate(input, saveToFile = true) {
1275
1338
  if (config.watch) {
1276
1339
  removeStaleFiles(config, generationResult);
1277
1340
  }
1278
- await lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
1279
- await Promise.all(generationResult.map(async (result) => {
1341
+ await context.profiler.run(() => lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: beforeAllFileWrite');
1342
+ await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
1280
1343
  const exists = fileExists(result.filename);
1281
1344
  if (!shouldOverwrite(config, result.filename) && exists) {
1282
1345
  return;
@@ -1288,7 +1351,7 @@ async function generate(input, saveToFile = true) {
1288
1351
  previousHash = hash(readSync(result.filename));
1289
1352
  }
1290
1353
  if (previousHash && currentHash === previousHash) {
1291
- debugLog(`Skipping file (${result.filename}) writing due to indentical hash...`);
1354
+ debugLog(`Skipping file (${result.filename}) writing due to identical hash...`);
1292
1355
  return;
1293
1356
  }
1294
1357
  if (content.length === 0) {
@@ -1305,17 +1368,20 @@ async function generate(input, saveToFile = true) {
1305
1368
  writeSync(absolutePath, result.content);
1306
1369
  await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
1307
1370
  await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
1308
- }));
1309
- await lifecycleHooks(config.hooks).afterAllFileWrite(generationResult.map(r => r.filename));
1371
+ })), 'Write files');
1372
+ await context.profiler.run(() => lifecycleHooks(config.hooks).afterAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: afterAllFileWrite');
1310
1373
  return generationResult;
1311
1374
  }
1312
1375
  // watch mode
1313
1376
  if (config.watch) {
1314
1377
  return createWatcher(context, writeOutput);
1315
1378
  }
1316
- const outputFiles = await executeCodegen(context);
1317
- await writeOutput(outputFiles);
1318
- lifecycleHooks(config.hooks).beforeDone();
1379
+ const outputFiles = await context.profiler.run(() => executeCodegen(context), 'executeCodegen');
1380
+ await context.profiler.run(() => writeOutput(outputFiles), 'writeOutput');
1381
+ await context.profiler.run(() => lifecycleHooks(config.hooks).beforeDone(), 'Lifecycle: beforeDone');
1382
+ if (context.profilerOutput) {
1383
+ writeSync(path.join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1384
+ }
1319
1385
  return outputFiles;
1320
1386
  }
1321
1387
  function shouldOverwrite(config, outputPath) {
@@ -1814,30 +1880,6 @@ async function ensureGraphQlPackage() {
1814
1880
  }
1815
1881
  }
1816
1882
 
1817
- const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
1818
- const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
1819
-
1820
- function cliError(err, exitOnError = true) {
1821
- let msg;
1822
- if (err instanceof Error) {
1823
- msg = err.message || err.toString();
1824
- }
1825
- else if (typeof err === 'string') {
1826
- msg = err;
1827
- }
1828
- else {
1829
- msg = JSON.stringify(err);
1830
- }
1831
- // eslint-disable-next-line no-console
1832
- console.error(msg);
1833
- if (exitOnError && isNode) {
1834
- process.exit(1);
1835
- }
1836
- else if (exitOnError && isBrowser) {
1837
- throw err;
1838
- }
1839
- }
1840
-
1841
1883
  const [, , cmd] = process.argv;
1842
1884
  runCli(cmd)
1843
1885
  .then(() => {
package/config.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Types } from '@graphql-codegen/plugin-helpers';
1
+ import { Types, Profiler } from '@graphql-codegen/plugin-helpers';
2
2
  import { GraphQLConfig } from 'graphql-config';
3
3
  import { GraphQLSchema } from 'graphql';
4
4
  export declare type YamlCliFlags = {
@@ -9,6 +9,7 @@ export declare type YamlCliFlags = {
9
9
  project: string;
10
10
  silent: boolean;
11
11
  errorsOnly: boolean;
12
+ profile: boolean;
12
13
  };
13
14
  export declare function generateSearchPlaces(moduleName: string): string[];
14
15
  export interface LoadCodegenConfigOptions {
@@ -74,6 +75,10 @@ export declare function buildOptions(): {
74
75
  describe: string;
75
76
  type: "boolean";
76
77
  };
78
+ profile: {
79
+ describe: string;
80
+ type: "boolean";
81
+ };
77
82
  p: {
78
83
  alias: string;
79
84
  describe: string;
@@ -91,6 +96,8 @@ export declare class CodegenContext {
91
96
  private _pluginContext;
92
97
  cwd: string;
93
98
  filepath: string;
99
+ profiler: Profiler;
100
+ profilerOutput?: string;
94
101
  constructor({ config, graphqlConfig, filepath, }: {
95
102
  config?: Types.Config;
96
103
  graphqlConfig?: GraphQLConfig;
@@ -99,6 +106,7 @@ export declare class CodegenContext {
99
106
  useProject(name?: string): void;
100
107
  getConfig<T>(extraConfig?: T): T & Types.Config;
101
108
  updateConfig(config: Partial<Types.Config>): void;
109
+ useProfiler(): void;
102
110
  getPluginContext(): {
103
111
  [key: string]: any;
104
112
  };
package/index.js CHANGED
@@ -25,6 +25,7 @@ function _interopNamespace(e) {
25
25
 
26
26
  const pluginHelpers = require('@graphql-codegen/plugin-helpers');
27
27
  const core = require('@graphql-codegen/core');
28
+ const utils = require('@graphql-tools/utils');
28
29
  const chalk = _interopDefault(require('chalk'));
29
30
  const logSymbols = _interopDefault(require('log-symbols'));
30
31
  const ansiEscapes = _interopDefault(require('ansi-escapes'));
@@ -56,7 +57,6 @@ const Listr = _interopDefault(require('listr'));
56
57
  const child_process = require('child_process');
57
58
  const isGlob = _interopDefault(require('is-glob'));
58
59
  const debounce = _interopDefault(require('debounce'));
59
- const utils = require('@graphql-tools/utils');
60
60
  const mkdirp = _interopDefault(require('mkdirp'));
61
61
  const crypto = require('crypto');
62
62
  const inquirer = _interopDefault(require('inquirer'));
@@ -625,6 +625,10 @@ function buildOptions() {
625
625
  describe: 'Only print errors',
626
626
  type: 'boolean',
627
627
  },
628
+ profile: {
629
+ describe: 'Use profiler to measure performance',
630
+ type: 'boolean',
631
+ },
628
632
  p: {
629
633
  alias: 'project',
630
634
  describe: 'Name of a project in GraphQL Config',
@@ -666,6 +670,9 @@ function updateContextWithCliFlags(context, cliFlags) {
666
670
  if (cliFlags.project) {
667
671
  context.useProject(cliFlags.project);
668
672
  }
673
+ if (cliFlags.profile === true) {
674
+ context.useProfiler();
675
+ }
669
676
  context.updateConfig(config);
670
677
  }
671
678
  class CodegenContext {
@@ -675,6 +682,7 @@ class CodegenContext {
675
682
  this._graphqlConfig = graphqlConfig;
676
683
  this.filepath = this._graphqlConfig ? this._graphqlConfig.filepath : filepath;
677
684
  this.cwd = this._graphqlConfig ? this._graphqlConfig.dirpath : process.cwd();
685
+ this.profiler = pluginHelpers.createNoopProfiler();
678
686
  }
679
687
  useProject(name) {
680
688
  this._project = name;
@@ -705,6 +713,13 @@ class CodegenContext {
705
713
  ...config,
706
714
  };
707
715
  }
716
+ useProfiler() {
717
+ this.profiler = pluginHelpers.createProfiler();
718
+ const now = new Date(); // 2011-10-05T14:48:00.000Z
719
+ const datetime = now.toISOString().split('.')[0]; // 2011-10-05T14:48:00
720
+ const datetimeNormalized = datetime.replace(/-|:/g, ''); // 20111005T144800
721
+ this.profilerOutput = `codegen-${datetimeNormalized}.json`;
722
+ }
708
723
  getPluginContext() {
709
724
  return this._pluginContext;
710
725
  }
@@ -730,6 +745,33 @@ function ensureContext(input) {
730
745
  return input instanceof CodegenContext ? input : new CodegenContext({ config: input });
731
746
  }
732
747
 
748
+ const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
749
+ const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
750
+
751
+ function isListrError(err) {
752
+ return err.name === 'ListrError' && Array.isArray(err.errors) && err.errors.length > 0;
753
+ }
754
+ function cliError(err, exitOnError = true) {
755
+ let msg;
756
+ if (err instanceof Error) {
757
+ msg = err.message || err.toString();
758
+ }
759
+ else if (typeof err === 'string') {
760
+ msg = err;
761
+ }
762
+ else {
763
+ msg = JSON.stringify(err);
764
+ }
765
+ // eslint-disable-next-line no-console
766
+ console.error(msg);
767
+ if (exitOnError && isNode) {
768
+ process.exit(1);
769
+ }
770
+ else if (exitOnError && isBrowser) {
771
+ throw err;
772
+ }
773
+ }
774
+
733
775
  const makeDefaultLoader = (from) => {
734
776
  if (fs__default.statSync(from).isDirectory()) {
735
777
  from = path__default.join(from, '__fake.js');
@@ -754,19 +796,6 @@ function createCache(loader) {
754
796
  };
755
797
  }
756
798
  async function executeCodegen(input) {
757
- function wrapTask(task, source) {
758
- return async () => {
759
- try {
760
- await Promise.resolve().then(() => task());
761
- }
762
- catch (error) {
763
- if (source && !(error instanceof graphql.GraphQLError)) {
764
- error.source = source;
765
- }
766
- throw error;
767
- }
768
- };
769
- }
770
799
  const context = ensureContext(input);
771
800
  const config = context.getConfig();
772
801
  const pluginContext = context.getPluginContext();
@@ -816,6 +845,21 @@ async function executeCodegen(input) {
816
845
  documents,
817
846
  };
818
847
  });
848
+ function wrapTask(task, source, taskName) {
849
+ return () => {
850
+ return context.profiler.run(async () => {
851
+ try {
852
+ await Promise.resolve().then(() => task());
853
+ }
854
+ catch (error) {
855
+ if (source && !(error instanceof graphql.GraphQLError)) {
856
+ error.source = source;
857
+ }
858
+ throw error;
859
+ }
860
+ }, taskName);
861
+ };
862
+ }
819
863
  async function normalize() {
820
864
  /* Load Require extensions */
821
865
  const requireExtensions = pluginHelpers.normalizeInstanceOrArray(config.require);
@@ -921,20 +965,23 @@ async function executeCodegen(input) {
921
965
  const result = await schemaLoadingCache.load(hash);
922
966
  outputSchemaAst = await result.outputSchemaAst;
923
967
  outputSchema = result.outputSchema;
924
- }, filename),
968
+ }, filename, `Load GraphQL schemas: ${filename}`),
925
969
  },
926
970
  {
927
971
  title: 'Load GraphQL documents',
928
972
  task: wrapTask(async () => {
929
973
  debugLog(`[CLI] Loading Documents`);
930
- const allDocuments = [...rootDocuments, ...outputSpecificDocuments];
931
- const hash = JSON.stringify({ pointer: allDocuments, schema: outputSchema });
932
- const result = await documentsLoadingCache.load(hash);
933
- const documents = result.documents;
974
+ // get different cache for shared docs and output specific docs
975
+ const results = await Promise.all([rootDocuments, outputSpecificDocuments].map(docs => {
976
+ const hash = JSON.stringify({ pointer: docs, schema: outputSchema });
977
+ return documentsLoadingCache.load(hash);
978
+ }));
979
+ const documents = [];
980
+ results.forEach(source => documents.push(...source.documents));
934
981
  if (documents.length > 0) {
935
982
  outputDocuments.push(...documents);
936
983
  }
937
- }, filename),
984
+ }, filename, `Load GraphQL documents: ${filename}`),
938
985
  },
939
986
  {
940
987
  title: 'Generate',
@@ -962,7 +1009,7 @@ async function executeCodegen(input) {
962
1009
  };
963
1010
  let outputs = [];
964
1011
  if (hasPreset) {
965
- outputs = await preset.buildGeneratesSection({
1012
+ outputs = await context.profiler.run(async () => preset.buildGeneratesSection({
966
1013
  baseOutputDir: filename,
967
1014
  presetConfig: outputConfig.presetConfig || {},
968
1015
  plugins: normalizedPluginsArray,
@@ -972,7 +1019,8 @@ async function executeCodegen(input) {
972
1019
  config: mergedConfig,
973
1020
  pluginMap,
974
1021
  pluginContext,
975
- });
1022
+ profiler: context.profiler,
1023
+ }), `Build Generates Section: ${filename}`);
976
1024
  }
977
1025
  else {
978
1026
  outputs = [
@@ -985,6 +1033,7 @@ async function executeCodegen(input) {
985
1033
  config: mergedConfig,
986
1034
  pluginMap,
987
1035
  pluginContext,
1036
+ profiler: context.profiler,
988
1037
  },
989
1038
  ];
990
1039
  }
@@ -996,8 +1045,8 @@ async function executeCodegen(input) {
996
1045
  hooks: outputConfig.hooks || {},
997
1046
  });
998
1047
  };
999
- await Promise.all(outputs.map(process));
1000
- }, filename),
1048
+ await context.profiler.run(() => Promise.all(outputs.map(process)), `Codegen: ${filename}`);
1049
+ }, filename, `Generate: ${filename}`),
1001
1050
  },
1002
1051
  ], {
1003
1052
  // it stops when one of tasks failed
@@ -1013,7 +1062,21 @@ async function executeCodegen(input) {
1013
1062
  });
1014
1063
  },
1015
1064
  });
1016
- await listr.run();
1065
+ try {
1066
+ await listr.run();
1067
+ }
1068
+ catch (err) {
1069
+ if (isListrError(err)) {
1070
+ const allErrs = err.errors.map(subErr => pluginHelpers.isDetailedError(subErr)
1071
+ ? `${subErr.message} for "${subErr.source}"${subErr.details}`
1072
+ : subErr.message || subErr.toString());
1073
+ const newErr = new utils.AggregateError(err.errors, `${err.message} ${allErrs.join('\n\n')}`);
1074
+ // Best-effort to all stack traces for debugging
1075
+ newErr.stack = `${newErr.stack}\n\n${err.errors.map(subErr => subErr.stack).join('\n\n')}`;
1076
+ throw newErr;
1077
+ }
1078
+ throw err;
1079
+ }
1017
1080
  return result;
1018
1081
  }
1019
1082
 
@@ -1247,7 +1310,7 @@ const hash = (content) => crypto.createHash('sha1').update(content).digest('base
1247
1310
  async function generate(input, saveToFile = true) {
1248
1311
  const context = ensureContext(input);
1249
1312
  const config = context.getConfig();
1250
- await lifecycleHooks(config.hooks).afterStart();
1313
+ await context.profiler.run(() => lifecycleHooks(config.hooks).afterStart(), 'Lifecycle: afterStart');
1251
1314
  let previouslyGeneratedFilenames = [];
1252
1315
  function removeStaleFiles(config, generationResult) {
1253
1316
  const filenames = generationResult.map(o => o.filename);
@@ -1276,8 +1339,8 @@ async function generate(input, saveToFile = true) {
1276
1339
  if (config.watch) {
1277
1340
  removeStaleFiles(config, generationResult);
1278
1341
  }
1279
- await lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
1280
- await Promise.all(generationResult.map(async (result) => {
1342
+ await context.profiler.run(() => lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: beforeAllFileWrite');
1343
+ await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
1281
1344
  const exists = fileExists(result.filename);
1282
1345
  if (!shouldOverwrite(config, result.filename) && exists) {
1283
1346
  return;
@@ -1289,7 +1352,7 @@ async function generate(input, saveToFile = true) {
1289
1352
  previousHash = hash(readSync(result.filename));
1290
1353
  }
1291
1354
  if (previousHash && currentHash === previousHash) {
1292
- debugLog(`Skipping file (${result.filename}) writing due to indentical hash...`);
1355
+ debugLog(`Skipping file (${result.filename}) writing due to identical hash...`);
1293
1356
  return;
1294
1357
  }
1295
1358
  if (content.length === 0) {
@@ -1306,17 +1369,20 @@ async function generate(input, saveToFile = true) {
1306
1369
  writeSync(absolutePath, result.content);
1307
1370
  await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
1308
1371
  await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
1309
- }));
1310
- await lifecycleHooks(config.hooks).afterAllFileWrite(generationResult.map(r => r.filename));
1372
+ })), 'Write files');
1373
+ await context.profiler.run(() => lifecycleHooks(config.hooks).afterAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: afterAllFileWrite');
1311
1374
  return generationResult;
1312
1375
  }
1313
1376
  // watch mode
1314
1377
  if (config.watch) {
1315
1378
  return createWatcher(context, writeOutput);
1316
1379
  }
1317
- const outputFiles = await executeCodegen(context);
1318
- await writeOutput(outputFiles);
1319
- lifecycleHooks(config.hooks).beforeDone();
1380
+ const outputFiles = await context.profiler.run(() => executeCodegen(context), 'executeCodegen');
1381
+ await context.profiler.run(() => writeOutput(outputFiles), 'writeOutput');
1382
+ await context.profiler.run(() => lifecycleHooks(config.hooks).beforeDone(), 'Lifecycle: beforeDone');
1383
+ if (context.profilerOutput) {
1384
+ writeSync(path.join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1385
+ }
1320
1386
  return outputFiles;
1321
1387
  }
1322
1388
  function shouldOverwrite(config, outputPath) {
@@ -1786,30 +1852,6 @@ function addIntrospection(config) {
1786
1852
  };
1787
1853
  }
1788
1854
 
1789
- const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
1790
- const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
1791
-
1792
- function cliError(err, exitOnError = true) {
1793
- let msg;
1794
- if (err instanceof Error) {
1795
- msg = err.message || err.toString();
1796
- }
1797
- else if (typeof err === 'string') {
1798
- msg = err;
1799
- }
1800
- else {
1801
- msg = JSON.stringify(err);
1802
- }
1803
- // eslint-disable-next-line no-console
1804
- console.error(msg);
1805
- if (exitOnError && isNode) {
1806
- process.exit(1);
1807
- }
1808
- else if (exitOnError && isBrowser) {
1809
- throw err;
1810
- }
1811
- }
1812
-
1813
1855
  async function runCli(cmd) {
1814
1856
  await ensureGraphQlPackage();
1815
1857
  switch (cmd) {
@@ -1851,6 +1893,7 @@ exports.findAndLoadGraphQLConfig = findAndLoadGraphQLConfig;
1851
1893
  exports.generate = generate;
1852
1894
  exports.generateSearchPlaces = generateSearchPlaces;
1853
1895
  exports.init = init;
1896
+ exports.isListrError = isListrError;
1854
1897
  exports.loadCodegenConfig = loadCodegenConfig;
1855
1898
  exports.loadContext = loadContext;
1856
1899
  exports.parseArgv = parseArgv;
package/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
- import { isDetailedError, DetailedError, normalizeInstanceOrArray, normalizeConfig, normalizeOutputParam, getCachedDocumentNodeFromSchema } from '@graphql-codegen/plugin-helpers';
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
4
  import chalk from 'chalk';
4
5
  import logSymbols from 'log-symbols';
5
6
  import ansiEscapes from 'ansi-escapes';
@@ -24,12 +25,11 @@ import { JsonFileLoader } from '@graphql-tools/json-file-loader';
24
25
  import { UrlLoader } from '@graphql-tools/url-loader';
25
26
  import yaml from 'yaml';
26
27
  import { createRequire } from 'module';
27
- import fs, { promises, readFileSync, writeFileSync, statSync, unlink } from 'fs';
28
+ import fs, { promises, writeFileSync, readFileSync, statSync, unlink } from 'fs';
28
29
  import Listr from 'listr';
29
30
  import { exec } from 'child_process';
30
31
  import isGlob from 'is-glob';
31
32
  import debounce from 'debounce';
32
- import { isValidPath } from '@graphql-tools/utils';
33
33
  import mkdirp from 'mkdirp';
34
34
  import { createHash } from 'crypto';
35
35
  import inquirer from 'inquirer';
@@ -598,6 +598,10 @@ function buildOptions() {
598
598
  describe: 'Only print errors',
599
599
  type: 'boolean',
600
600
  },
601
+ profile: {
602
+ describe: 'Use profiler to measure performance',
603
+ type: 'boolean',
604
+ },
601
605
  p: {
602
606
  alias: 'project',
603
607
  describe: 'Name of a project in GraphQL Config',
@@ -639,6 +643,9 @@ function updateContextWithCliFlags(context, cliFlags) {
639
643
  if (cliFlags.project) {
640
644
  context.useProject(cliFlags.project);
641
645
  }
646
+ if (cliFlags.profile === true) {
647
+ context.useProfiler();
648
+ }
642
649
  context.updateConfig(config);
643
650
  }
644
651
  class CodegenContext {
@@ -648,6 +655,7 @@ class CodegenContext {
648
655
  this._graphqlConfig = graphqlConfig;
649
656
  this.filepath = this._graphqlConfig ? this._graphqlConfig.filepath : filepath;
650
657
  this.cwd = this._graphqlConfig ? this._graphqlConfig.dirpath : process.cwd();
658
+ this.profiler = createNoopProfiler();
651
659
  }
652
660
  useProject(name) {
653
661
  this._project = name;
@@ -678,6 +686,13 @@ class CodegenContext {
678
686
  ...config,
679
687
  };
680
688
  }
689
+ useProfiler() {
690
+ this.profiler = createProfiler();
691
+ const now = new Date(); // 2011-10-05T14:48:00.000Z
692
+ const datetime = now.toISOString().split('.')[0]; // 2011-10-05T14:48:00
693
+ const datetimeNormalized = datetime.replace(/-|:/g, ''); // 20111005T144800
694
+ this.profilerOutput = `codegen-${datetimeNormalized}.json`;
695
+ }
681
696
  getPluginContext() {
682
697
  return this._pluginContext;
683
698
  }
@@ -703,6 +718,33 @@ function ensureContext(input) {
703
718
  return input instanceof CodegenContext ? input : new CodegenContext({ config: input });
704
719
  }
705
720
 
721
+ const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
722
+ const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
723
+
724
+ function isListrError(err) {
725
+ return err.name === 'ListrError' && Array.isArray(err.errors) && err.errors.length > 0;
726
+ }
727
+ function cliError(err, exitOnError = true) {
728
+ let msg;
729
+ if (err instanceof Error) {
730
+ msg = err.message || err.toString();
731
+ }
732
+ else if (typeof err === 'string') {
733
+ msg = err;
734
+ }
735
+ else {
736
+ msg = JSON.stringify(err);
737
+ }
738
+ // eslint-disable-next-line no-console
739
+ console.error(msg);
740
+ if (exitOnError && isNode) {
741
+ process.exit(1);
742
+ }
743
+ else if (exitOnError && isBrowser) {
744
+ throw err;
745
+ }
746
+ }
747
+
706
748
  const makeDefaultLoader = (from) => {
707
749
  if (fs.statSync(from).isDirectory()) {
708
750
  from = path.join(from, '__fake.js');
@@ -727,19 +769,6 @@ function createCache(loader) {
727
769
  };
728
770
  }
729
771
  async function executeCodegen(input) {
730
- function wrapTask(task, source) {
731
- return async () => {
732
- try {
733
- await Promise.resolve().then(() => task());
734
- }
735
- catch (error) {
736
- if (source && !(error instanceof GraphQLError)) {
737
- error.source = source;
738
- }
739
- throw error;
740
- }
741
- };
742
- }
743
772
  const context = ensureContext(input);
744
773
  const config = context.getConfig();
745
774
  const pluginContext = context.getPluginContext();
@@ -789,6 +818,21 @@ async function executeCodegen(input) {
789
818
  documents,
790
819
  };
791
820
  });
821
+ function wrapTask(task, source, taskName) {
822
+ return () => {
823
+ return context.profiler.run(async () => {
824
+ try {
825
+ await Promise.resolve().then(() => task());
826
+ }
827
+ catch (error) {
828
+ if (source && !(error instanceof GraphQLError)) {
829
+ error.source = source;
830
+ }
831
+ throw error;
832
+ }
833
+ }, taskName);
834
+ };
835
+ }
792
836
  async function normalize() {
793
837
  /* Load Require extensions */
794
838
  const requireExtensions = normalizeInstanceOrArray(config.require);
@@ -894,20 +938,23 @@ async function executeCodegen(input) {
894
938
  const result = await schemaLoadingCache.load(hash);
895
939
  outputSchemaAst = await result.outputSchemaAst;
896
940
  outputSchema = result.outputSchema;
897
- }, filename),
941
+ }, filename, `Load GraphQL schemas: ${filename}`),
898
942
  },
899
943
  {
900
944
  title: 'Load GraphQL documents',
901
945
  task: wrapTask(async () => {
902
946
  debugLog(`[CLI] Loading Documents`);
903
- const allDocuments = [...rootDocuments, ...outputSpecificDocuments];
904
- const hash = JSON.stringify({ pointer: allDocuments, schema: outputSchema });
905
- const result = await documentsLoadingCache.load(hash);
906
- const documents = result.documents;
947
+ // get different cache for shared docs and output specific docs
948
+ const results = await Promise.all([rootDocuments, outputSpecificDocuments].map(docs => {
949
+ const hash = JSON.stringify({ pointer: docs, schema: outputSchema });
950
+ return documentsLoadingCache.load(hash);
951
+ }));
952
+ const documents = [];
953
+ results.forEach(source => documents.push(...source.documents));
907
954
  if (documents.length > 0) {
908
955
  outputDocuments.push(...documents);
909
956
  }
910
- }, filename),
957
+ }, filename, `Load GraphQL documents: ${filename}`),
911
958
  },
912
959
  {
913
960
  title: 'Generate',
@@ -935,7 +982,7 @@ async function executeCodegen(input) {
935
982
  };
936
983
  let outputs = [];
937
984
  if (hasPreset) {
938
- outputs = await preset.buildGeneratesSection({
985
+ outputs = await context.profiler.run(async () => preset.buildGeneratesSection({
939
986
  baseOutputDir: filename,
940
987
  presetConfig: outputConfig.presetConfig || {},
941
988
  plugins: normalizedPluginsArray,
@@ -945,7 +992,8 @@ async function executeCodegen(input) {
945
992
  config: mergedConfig,
946
993
  pluginMap,
947
994
  pluginContext,
948
- });
995
+ profiler: context.profiler,
996
+ }), `Build Generates Section: ${filename}`);
949
997
  }
950
998
  else {
951
999
  outputs = [
@@ -958,6 +1006,7 @@ async function executeCodegen(input) {
958
1006
  config: mergedConfig,
959
1007
  pluginMap,
960
1008
  pluginContext,
1009
+ profiler: context.profiler,
961
1010
  },
962
1011
  ];
963
1012
  }
@@ -969,8 +1018,8 @@ async function executeCodegen(input) {
969
1018
  hooks: outputConfig.hooks || {},
970
1019
  });
971
1020
  };
972
- await Promise.all(outputs.map(process));
973
- }, filename),
1021
+ await context.profiler.run(() => Promise.all(outputs.map(process)), `Codegen: ${filename}`);
1022
+ }, filename, `Generate: ${filename}`),
974
1023
  },
975
1024
  ], {
976
1025
  // it stops when one of tasks failed
@@ -986,7 +1035,21 @@ async function executeCodegen(input) {
986
1035
  });
987
1036
  },
988
1037
  });
989
- await listr.run();
1038
+ try {
1039
+ await listr.run();
1040
+ }
1041
+ catch (err) {
1042
+ if (isListrError(err)) {
1043
+ const allErrs = err.errors.map(subErr => isDetailedError(subErr)
1044
+ ? `${subErr.message} for "${subErr.source}"${subErr.details}`
1045
+ : subErr.message || subErr.toString());
1046
+ const newErr = new AggregateError(err.errors, `${err.message} ${allErrs.join('\n\n')}`);
1047
+ // Best-effort to all stack traces for debugging
1048
+ newErr.stack = `${newErr.stack}\n\n${err.errors.map(subErr => subErr.stack).join('\n\n')}`;
1049
+ throw newErr;
1050
+ }
1051
+ throw err;
1052
+ }
990
1053
  return result;
991
1054
  }
992
1055
 
@@ -1220,7 +1283,7 @@ const hash = (content) => createHash('sha1').update(content).digest('base64');
1220
1283
  async function generate(input, saveToFile = true) {
1221
1284
  const context = ensureContext(input);
1222
1285
  const config = context.getConfig();
1223
- await lifecycleHooks(config.hooks).afterStart();
1286
+ await context.profiler.run(() => lifecycleHooks(config.hooks).afterStart(), 'Lifecycle: afterStart');
1224
1287
  let previouslyGeneratedFilenames = [];
1225
1288
  function removeStaleFiles(config, generationResult) {
1226
1289
  const filenames = generationResult.map(o => o.filename);
@@ -1249,8 +1312,8 @@ async function generate(input, saveToFile = true) {
1249
1312
  if (config.watch) {
1250
1313
  removeStaleFiles(config, generationResult);
1251
1314
  }
1252
- await lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
1253
- await Promise.all(generationResult.map(async (result) => {
1315
+ await context.profiler.run(() => lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: beforeAllFileWrite');
1316
+ await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
1254
1317
  const exists = fileExists(result.filename);
1255
1318
  if (!shouldOverwrite(config, result.filename) && exists) {
1256
1319
  return;
@@ -1262,7 +1325,7 @@ async function generate(input, saveToFile = true) {
1262
1325
  previousHash = hash(readSync(result.filename));
1263
1326
  }
1264
1327
  if (previousHash && currentHash === previousHash) {
1265
- debugLog(`Skipping file (${result.filename}) writing due to indentical hash...`);
1328
+ debugLog(`Skipping file (${result.filename}) writing due to identical hash...`);
1266
1329
  return;
1267
1330
  }
1268
1331
  if (content.length === 0) {
@@ -1279,17 +1342,20 @@ async function generate(input, saveToFile = true) {
1279
1342
  writeSync(absolutePath, result.content);
1280
1343
  await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
1281
1344
  await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
1282
- }));
1283
- await lifecycleHooks(config.hooks).afterAllFileWrite(generationResult.map(r => r.filename));
1345
+ })), 'Write files');
1346
+ await context.profiler.run(() => lifecycleHooks(config.hooks).afterAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: afterAllFileWrite');
1284
1347
  return generationResult;
1285
1348
  }
1286
1349
  // watch mode
1287
1350
  if (config.watch) {
1288
1351
  return createWatcher(context, writeOutput);
1289
1352
  }
1290
- const outputFiles = await executeCodegen(context);
1291
- await writeOutput(outputFiles);
1292
- lifecycleHooks(config.hooks).beforeDone();
1353
+ const outputFiles = await context.profiler.run(() => executeCodegen(context), 'executeCodegen');
1354
+ await context.profiler.run(() => writeOutput(outputFiles), 'writeOutput');
1355
+ await context.profiler.run(() => lifecycleHooks(config.hooks).beforeDone(), 'Lifecycle: beforeDone');
1356
+ if (context.profilerOutput) {
1357
+ writeSync(join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
1358
+ }
1293
1359
  return outputFiles;
1294
1360
  }
1295
1361
  function shouldOverwrite(config, outputPath) {
@@ -1759,30 +1825,6 @@ function addIntrospection(config) {
1759
1825
  };
1760
1826
  }
1761
1827
 
1762
- const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
1763
- const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
1764
-
1765
- function cliError(err, exitOnError = true) {
1766
- let msg;
1767
- if (err instanceof Error) {
1768
- msg = err.message || err.toString();
1769
- }
1770
- else if (typeof err === 'string') {
1771
- msg = err;
1772
- }
1773
- else {
1774
- msg = JSON.stringify(err);
1775
- }
1776
- // eslint-disable-next-line no-console
1777
- console.error(msg);
1778
- if (exitOnError && isNode) {
1779
- process.exit(1);
1780
- }
1781
- else if (exitOnError && isBrowser) {
1782
- throw err;
1783
- }
1784
- }
1785
-
1786
1828
  async function runCli(cmd) {
1787
1829
  await ensureGraphQlPackage();
1788
1830
  switch (cmd) {
@@ -1812,4 +1854,4 @@ async function ensureGraphQlPackage() {
1812
1854
  }
1813
1855
  }
1814
1856
 
1815
- export { CodegenContext, CodegenExtension, buildOptions, cliError, createContext, ensureContext, ensureGraphQlPackage, executeCodegen, findAndLoadGraphQLConfig, generate, generateSearchPlaces, init, loadCodegenConfig, loadContext, parseArgv, runCli, updateContextWithCliFlags };
1857
+ export { CodegenContext, CodegenExtension, buildOptions, cliError, createContext, ensureContext, ensureGraphQlPackage, executeCodegen, findAndLoadGraphQLConfig, generate, generateSearchPlaces, init, isListrError, loadCodegenConfig, loadContext, parseArgv, runCli, updateContextWithCliFlags };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@graphql-codegen/cli",
3
- "version": "2.5.0-alpha-bc33c031d.0",
3
+ "version": "2.6.0-alpha-bbecef04a.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.4.1-alpha-bc33c031d.0",
9
- "@graphql-codegen/plugin-helpers": "2.3.3-alpha-bc33c031d.0",
8
+ "@graphql-codegen/core": "2.5.1-alpha-bbecef04a.0",
9
+ "@graphql-codegen/plugin-helpers": "2.4.1-alpha-bbecef04a.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 +1,11 @@
1
+ import { DetailedError } from '@graphql-codegen/plugin-helpers';
2
+ declare type CompositeError = Error | DetailedError;
3
+ declare type ListrError = Error & {
4
+ errors: CompositeError[];
5
+ };
6
+ export declare function isListrError(err: Error & {
7
+ name?: unknown;
8
+ errors?: unknown;
9
+ }): err is ListrError;
1
10
  export declare function cliError(err: any, exitOnError?: boolean): void;
11
+ export {};