@depup/graphql-codegen__cli 6.2.1-depup.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.
Files changed (112) hide show
  1. package/README.md +51 -0
  2. package/changes.json +90 -0
  3. package/cjs/bin.js +13 -0
  4. package/cjs/cli.js +64 -0
  5. package/cjs/codegen.js +388 -0
  6. package/cjs/config.js +370 -0
  7. package/cjs/documentTransforms.js +63 -0
  8. package/cjs/generate-and-save.js +153 -0
  9. package/cjs/graphql-config.js +67 -0
  10. package/cjs/hooks.js +93 -0
  11. package/cjs/index.js +13 -0
  12. package/cjs/init/helpers.js +90 -0
  13. package/cjs/init/index.js +62 -0
  14. package/cjs/init/plugins.js +136 -0
  15. package/cjs/init/questions.js +177 -0
  16. package/cjs/init/targets.js +45 -0
  17. package/cjs/init/types.js +15 -0
  18. package/cjs/load.js +102 -0
  19. package/cjs/package.json +1 -0
  20. package/cjs/plugins.js +43 -0
  21. package/cjs/presets.js +50 -0
  22. package/cjs/utils/abort-controller-polyfill.js +99 -0
  23. package/cjs/utils/cli-error.js +24 -0
  24. package/cjs/utils/debugging.js +22 -0
  25. package/cjs/utils/file-system.js +24 -0
  26. package/cjs/utils/get-latest-version.js +14 -0
  27. package/cjs/utils/helpers.js +12 -0
  28. package/cjs/utils/logger.js +24 -0
  29. package/cjs/utils/patterns.js +235 -0
  30. package/cjs/utils/watcher.js +244 -0
  31. package/esm/bin.js +11 -0
  32. package/esm/cli.js +37 -0
  33. package/esm/codegen.js +384 -0
  34. package/esm/config.js +357 -0
  35. package/esm/documentTransforms.js +59 -0
  36. package/esm/generate-and-save.js +149 -0
  37. package/esm/graphql-config.js +62 -0
  38. package/esm/hooks.js +89 -0
  39. package/esm/index.js +7 -0
  40. package/esm/init/helpers.js +82 -0
  41. package/esm/init/index.js +59 -0
  42. package/esm/init/plugins.js +133 -0
  43. package/esm/init/questions.js +172 -0
  44. package/esm/init/targets.js +42 -0
  45. package/esm/init/types.js +12 -0
  46. package/esm/load.js +97 -0
  47. package/esm/plugins.js +40 -0
  48. package/esm/presets.js +47 -0
  49. package/esm/utils/abort-controller-polyfill.js +96 -0
  50. package/esm/utils/cli-error.js +20 -0
  51. package/esm/utils/debugging.js +17 -0
  52. package/esm/utils/file-system.js +17 -0
  53. package/esm/utils/get-latest-version.js +11 -0
  54. package/esm/utils/helpers.js +9 -0
  55. package/esm/utils/logger.js +18 -0
  56. package/esm/utils/patterns.js +226 -0
  57. package/esm/utils/watcher.js +239 -0
  58. package/package.json +205 -0
  59. package/typings/bin.d.cts +2 -0
  60. package/typings/bin.d.ts +2 -0
  61. package/typings/cli.d.cts +2 -0
  62. package/typings/cli.d.ts +2 -0
  63. package/typings/codegen.d.cts +6 -0
  64. package/typings/codegen.d.ts +6 -0
  65. package/typings/config.d.cts +152 -0
  66. package/typings/config.d.ts +152 -0
  67. package/typings/documentTransforms.d.cts +3 -0
  68. package/typings/documentTransforms.d.ts +3 -0
  69. package/typings/generate-and-save.d.cts +10 -0
  70. package/typings/generate-and-save.d.ts +10 -0
  71. package/typings/graphql-config.d.cts +3 -0
  72. package/typings/graphql-config.d.ts +3 -0
  73. package/typings/hooks.d.cts +11 -0
  74. package/typings/hooks.d.ts +11 -0
  75. package/typings/index.d.cts +8 -0
  76. package/typings/index.d.ts +8 -0
  77. package/typings/init/helpers.d.cts +10 -0
  78. package/typings/init/helpers.d.ts +10 -0
  79. package/typings/init/index.d.cts +1 -0
  80. package/typings/init/index.d.ts +1 -0
  81. package/typings/init/plugins.d.cts +2 -0
  82. package/typings/init/plugins.d.ts +2 -0
  83. package/typings/init/questions.d.cts +13 -0
  84. package/typings/init/questions.d.ts +13 -0
  85. package/typings/init/targets.d.cts +2 -0
  86. package/typings/init/targets.d.ts +2 -0
  87. package/typings/init/types.d.cts +30 -0
  88. package/typings/init/types.d.ts +30 -0
  89. package/typings/load.d.cts +15 -0
  90. package/typings/load.d.ts +15 -0
  91. package/typings/plugins.d.cts +2 -0
  92. package/typings/plugins.d.ts +2 -0
  93. package/typings/presets.d.cts +5 -0
  94. package/typings/presets.d.ts +5 -0
  95. package/typings/utils/abort-controller-polyfill.d.cts +5 -0
  96. package/typings/utils/abort-controller-polyfill.d.ts +5 -0
  97. package/typings/utils/cli-error.d.cts +10 -0
  98. package/typings/utils/cli-error.d.ts +10 -0
  99. package/typings/utils/debugging.d.cts +3 -0
  100. package/typings/utils/debugging.d.ts +3 -0
  101. package/typings/utils/file-system.d.cts +8 -0
  102. package/typings/utils/file-system.d.ts +8 -0
  103. package/typings/utils/get-latest-version.d.cts +6 -0
  104. package/typings/utils/get-latest-version.d.ts +6 -0
  105. package/typings/utils/helpers.d.cts +1 -0
  106. package/typings/utils/helpers.d.ts +1 -0
  107. package/typings/utils/logger.d.cts +5 -0
  108. package/typings/utils/logger.d.ts +5 -0
  109. package/typings/utils/patterns.d.cts +91 -0
  110. package/typings/utils/patterns.d.ts +91 -0
  111. package/typings/utils/watcher.d.cts +16 -0
  112. package/typings/utils/watcher.d.ts +16 -0
package/esm/config.js ADDED
@@ -0,0 +1,357 @@
1
+ import { createHash } from 'crypto';
2
+ import { promises } from 'fs';
3
+ import { createRequire } from 'module';
4
+ import { resolve } from 'path';
5
+ import { createNoopProfiler, createProfiler, getCachedDocumentNodeFromSchema, } from '@graphql-codegen/plugin-helpers';
6
+ import { cosmiconfig, defaultLoaders } from 'cosmiconfig';
7
+ import { createJiti } from 'jiti';
8
+ import { print } from 'graphql';
9
+ import { env } from 'string-env-interpolation';
10
+ import yaml from 'yaml';
11
+ import yargs from 'yargs';
12
+ import { findAndLoadGraphQLConfig } from './graphql-config.js';
13
+ import { defaultDocumentsLoadOptions, defaultSchemaLoadOptions, loadDocuments, loadSchema } from './load.js';
14
+ const { lstat } = promises;
15
+ export function generateSearchPlaces(moduleName) {
16
+ const extensions = ['json', 'yaml', 'yml', 'js', 'ts', 'config.js'];
17
+ // gives codegen.json...
18
+ const regular = extensions.map(ext => `${moduleName}.${ext}`);
19
+ // gives .codegenrc.json... but no .codegenrc.config.js
20
+ const dot = extensions.filter(ext => ext !== 'config.js').map(ext => `.${moduleName}rc.${ext}`);
21
+ return [...regular.concat(dot), 'package.json'];
22
+ }
23
+ function customLoader(ext) {
24
+ return async function loader(filepath, content) {
25
+ if (typeof process !== 'undefined' && 'env' in process) {
26
+ content = env(content);
27
+ }
28
+ if (ext === 'json') {
29
+ return defaultLoaders['.json'](filepath, content);
30
+ }
31
+ if (ext === 'yaml') {
32
+ try {
33
+ const result = yaml.parse(content, { prettyErrors: true, merge: true });
34
+ return result;
35
+ }
36
+ catch (error) {
37
+ error.message = `YAML Error in ${filepath}:\n${error.message}`;
38
+ throw error;
39
+ }
40
+ }
41
+ if (ext === 'js') {
42
+ return defaultLoaders['.js'](filepath, content);
43
+ }
44
+ if (ext === 'ts') {
45
+ const jitiLoader = createJiti('');
46
+ return jitiLoader.import(filepath, { default: true });
47
+ }
48
+ };
49
+ }
50
+ export async function loadCodegenConfig({ configFilePath, moduleName, searchPlaces: additionalSearchPlaces, packageProp, loaders: customLoaders, }) {
51
+ configFilePath ||= process.cwd();
52
+ moduleName ||= 'codegen';
53
+ packageProp ||= moduleName;
54
+ const cosmi = cosmiconfig(moduleName, {
55
+ searchPlaces: generateSearchPlaces(moduleName).concat(additionalSearchPlaces || []),
56
+ packageProp,
57
+ loaders: {
58
+ '.json': customLoader('json'),
59
+ '.yaml': customLoader('yaml'),
60
+ '.yml': customLoader('yaml'),
61
+ '.js': customLoader('js'),
62
+ '.ts': customLoader('ts'),
63
+ '.mts': customLoader('ts'),
64
+ '.cts': customLoader('ts'),
65
+ noExt: customLoader('yaml'),
66
+ ...customLoaders,
67
+ },
68
+ });
69
+ const pathStats = await lstat(configFilePath);
70
+ return pathStats.isDirectory() ? cosmi.search(configFilePath) : cosmi.load(configFilePath);
71
+ }
72
+ export async function loadContext(configFilePath) {
73
+ const graphqlConfig = await findAndLoadGraphQLConfig(configFilePath);
74
+ if (graphqlConfig) {
75
+ return new CodegenContext({ graphqlConfig });
76
+ }
77
+ const result = await loadCodegenConfig({ configFilePath });
78
+ if (!result) {
79
+ if (configFilePath) {
80
+ throw new Error(`
81
+ Config ${configFilePath} does not exist.
82
+
83
+ $ graphql-codegen --config ${configFilePath}
84
+
85
+ Please make sure the --config points to a correct file.
86
+ `);
87
+ }
88
+ throw new Error(`Unable to find Codegen config file! \n
89
+ Please make sure that you have a configuration file under the current directory!
90
+ `);
91
+ }
92
+ if (result.isEmpty) {
93
+ throw new Error(`Found Codegen config file but it was empty! \n
94
+ Please make sure that you have a valid configuration file under the current directory!
95
+ `);
96
+ }
97
+ return new CodegenContext({
98
+ filepath: result.filepath,
99
+ config: result.config,
100
+ });
101
+ }
102
+ function getCustomConfigPath(cliFlags) {
103
+ const configFile = cliFlags.config;
104
+ return configFile ? resolve(process.cwd(), configFile) : null;
105
+ }
106
+ export function buildOptions() {
107
+ return {
108
+ c: {
109
+ alias: 'config',
110
+ type: 'string',
111
+ describe: 'Path to GraphQL codegen YAML config file, defaults to "codegen.yml" on the current directory',
112
+ },
113
+ w: {
114
+ alias: 'watch',
115
+ describe: 'Watch for changes and execute generation automatically. You can also specify a glob expression for custom watch list.',
116
+ coerce(watch) {
117
+ if (watch === 'false') {
118
+ return false;
119
+ }
120
+ if (typeof watch === 'string' || Array.isArray(watch)) {
121
+ return watch;
122
+ }
123
+ return !!watch;
124
+ },
125
+ },
126
+ r: {
127
+ alias: 'require',
128
+ describe: 'Loads specific require.extensions before running the codegen and reading the configuration',
129
+ type: 'array',
130
+ default: [],
131
+ },
132
+ o: {
133
+ alias: 'overwrite',
134
+ describe: 'Overwrites existing files',
135
+ type: 'boolean',
136
+ },
137
+ s: {
138
+ alias: 'silent',
139
+ describe: 'Suppresses printing errors',
140
+ type: 'boolean',
141
+ },
142
+ e: {
143
+ alias: 'errors-only',
144
+ describe: 'Only print errors',
145
+ type: 'boolean',
146
+ },
147
+ profile: {
148
+ describe: 'Use profiler to measure performance',
149
+ type: 'boolean',
150
+ },
151
+ p: {
152
+ alias: 'project',
153
+ describe: 'Name of a project in GraphQL Config',
154
+ type: 'string',
155
+ },
156
+ v: {
157
+ alias: 'verbose',
158
+ describe: 'output more detailed information about performed tasks',
159
+ type: 'boolean',
160
+ default: false,
161
+ },
162
+ d: {
163
+ alias: 'debug',
164
+ describe: 'Print debug logs to stdout',
165
+ type: 'boolean',
166
+ default: false,
167
+ },
168
+ 'emit-legacy-common-js-imports': {
169
+ describe: 'Emit legacy CommonJS imports (deprecated, use import-extension instead)',
170
+ type: 'boolean',
171
+ },
172
+ 'import-extension': {
173
+ describe: 'Extension to append to imports (e.g., .js, .mjs, or empty string for no extension)',
174
+ type: 'string',
175
+ },
176
+ 'ignore-no-documents': {
177
+ describe: 'Suppress errors for no documents',
178
+ type: 'boolean',
179
+ },
180
+ };
181
+ }
182
+ export function parseArgv(argv = process.argv) {
183
+ return yargs(argv).options(buildOptions()).parse(argv);
184
+ }
185
+ export async function createContext(cliFlags = parseArgv(process.argv)) {
186
+ if (cliFlags.require && cliFlags.require.length > 0) {
187
+ const relativeRequire = createRequire(process.cwd());
188
+ await Promise.all(cliFlags.require.map(mod => import(relativeRequire.resolve(mod, {
189
+ paths: [process.cwd()],
190
+ }))));
191
+ }
192
+ const customConfigPath = getCustomConfigPath(cliFlags);
193
+ const context = await loadContext(customConfigPath);
194
+ updateContextWithCliFlags(context, cliFlags);
195
+ return context;
196
+ }
197
+ export function updateContextWithCliFlags(context, cliFlags) {
198
+ const config = {
199
+ configFilePath: context.filepath,
200
+ };
201
+ if (cliFlags.watch !== undefined) {
202
+ config.watch = cliFlags.watch;
203
+ }
204
+ if (cliFlags.overwrite === true) {
205
+ config.overwrite = cliFlags.overwrite;
206
+ }
207
+ if (cliFlags.silent === true) {
208
+ config.silent = cliFlags.silent;
209
+ }
210
+ if (cliFlags.verbose === true || process.env.VERBOSE) {
211
+ config.verbose = true;
212
+ }
213
+ if (cliFlags.debug === true || process.env.DEBUG) {
214
+ config.debug = true;
215
+ }
216
+ if (cliFlags.errorsOnly === true) {
217
+ config.errorsOnly = cliFlags.errorsOnly;
218
+ }
219
+ if (cliFlags['ignore-no-documents'] !== undefined) {
220
+ // for some reason parsed value is `'false'` string so this ensure it always is a boolean.
221
+ config.ignoreNoDocuments = cliFlags['ignore-no-documents'] === true;
222
+ }
223
+ if (cliFlags['emit-legacy-common-js-imports'] !== undefined) {
224
+ // for some reason parsed value is `'false'` string so this ensure it always is a boolean.
225
+ config.emitLegacyCommonJSImports = cliFlags['emit-legacy-common-js-imports'] === true;
226
+ }
227
+ if (cliFlags['import-extension'] !== undefined) {
228
+ config.importExtension = cliFlags['import-extension'];
229
+ }
230
+ if (cliFlags.project) {
231
+ context.useProject(cliFlags.project);
232
+ }
233
+ if (cliFlags.profile === true) {
234
+ context.useProfiler();
235
+ }
236
+ if (cliFlags.check === true) {
237
+ context.enableCheckMode();
238
+ }
239
+ context.updateConfig(config);
240
+ }
241
+ export class CodegenContext {
242
+ _config;
243
+ _graphqlConfig;
244
+ config;
245
+ _project;
246
+ _checkMode = false;
247
+ _pluginContext = {};
248
+ cwd;
249
+ filepath;
250
+ profiler;
251
+ profilerOutput;
252
+ checkModeStaleFiles = [];
253
+ constructor({ config, graphqlConfig, filepath, }) {
254
+ this._config = config;
255
+ this._graphqlConfig = graphqlConfig;
256
+ this.filepath = this._graphqlConfig ? this._graphqlConfig.filepath : filepath;
257
+ this.cwd = this._graphqlConfig ? this._graphqlConfig.dirpath : process.cwd();
258
+ this.profiler = createNoopProfiler();
259
+ }
260
+ useProject(name) {
261
+ this._project = name;
262
+ }
263
+ getConfig(extraConfig) {
264
+ if (!this.config) {
265
+ if (this._graphqlConfig) {
266
+ const project = this._graphqlConfig.getProject(this._project);
267
+ this.config = {
268
+ ...project.extension('codegen'),
269
+ schema: project.schema,
270
+ documents: project.documents,
271
+ pluginContext: this._pluginContext,
272
+ };
273
+ }
274
+ else {
275
+ this.config = { ...this._config, pluginContext: this._pluginContext };
276
+ }
277
+ }
278
+ return {
279
+ ...extraConfig,
280
+ ...this.config,
281
+ };
282
+ }
283
+ updateConfig(config) {
284
+ this.config = {
285
+ ...this.getConfig(),
286
+ ...config,
287
+ };
288
+ }
289
+ enableCheckMode() {
290
+ this._checkMode = true;
291
+ }
292
+ get checkMode() {
293
+ return this._checkMode;
294
+ }
295
+ useProfiler() {
296
+ this.profiler = createProfiler();
297
+ const now = new Date(); // 2011-10-05T14:48:00.000Z
298
+ const datetime = now.toISOString().split('.')[0]; // 2011-10-05T14:48:00
299
+ const datetimeNormalized = datetime.replace(/-|:/g, ''); // 20111005T144800
300
+ this.profilerOutput = `codegen-${datetimeNormalized}.json`;
301
+ }
302
+ getPluginContext() {
303
+ return this._pluginContext;
304
+ }
305
+ async loadSchema(pointer) {
306
+ const config = this.getConfig(defaultSchemaLoadOptions);
307
+ if (this._graphqlConfig) {
308
+ // TODO: SchemaWithLoader won't work here
309
+ return addHashToSchema(this._graphqlConfig
310
+ .getProject(this._project)
311
+ .loadSchema(pointer, 'GraphQLSchema', { ...config, ...config.config }));
312
+ }
313
+ return addHashToSchema(loadSchema(pointer, config));
314
+ }
315
+ async loadDocuments(pointer) {
316
+ const config = this.getConfig(defaultDocumentsLoadOptions);
317
+ if (this._graphqlConfig) {
318
+ // TODO: pointer won't work here
319
+ return addHashToDocumentFiles(this._graphqlConfig.getProject(this._project).loadDocuments(pointer, { ...config, ...config.config }));
320
+ }
321
+ return addHashToDocumentFiles(loadDocuments(pointer, config));
322
+ }
323
+ }
324
+ export function ensureContext(input) {
325
+ return input instanceof CodegenContext ? input : new CodegenContext({ config: input });
326
+ }
327
+ function hashContent(content, encoding = 'hex') {
328
+ return createHash('sha256').update(content).digest(encoding);
329
+ }
330
+ function hashSchema(schema) {
331
+ return hashContent(print(getCachedDocumentNodeFromSchema(schema)));
332
+ }
333
+ function addHashToSchema(schemaPromise) {
334
+ return schemaPromise.then(schema => {
335
+ // It's consumed later on. The general purpose is to use it for caching.
336
+ if (!schema.extensions) {
337
+ schema.extensions = {};
338
+ }
339
+ schema.extensions['hash'] = hashSchema(schema);
340
+ return schema;
341
+ });
342
+ }
343
+ function hashDocument(doc) {
344
+ if (doc.rawSDL) {
345
+ return hashContent(doc.rawSDL);
346
+ }
347
+ if (doc.document) {
348
+ return hashContent(print(doc.document));
349
+ }
350
+ return null;
351
+ }
352
+ function addHashToDocumentFiles(documentFilesPromise) {
353
+ return documentFilesPromise.then(documentFiles => documentFiles.map(doc => {
354
+ doc.hash = hashDocument(doc);
355
+ return doc;
356
+ }));
357
+ }
@@ -0,0 +1,59 @@
1
+ import { resolve } from 'path';
2
+ export async function getDocumentTransform(documentTransform, loader, defaultName) {
3
+ if (typeof documentTransform === 'string') {
4
+ const transformObject = await getDocumentTransformByName(documentTransform, loader);
5
+ return { name: documentTransform, transformObject };
6
+ }
7
+ if (isTransformObject(documentTransform)) {
8
+ return { name: defaultName, transformObject: documentTransform };
9
+ }
10
+ if (isTransformFileConfig(documentTransform)) {
11
+ const name = Object.keys(documentTransform)[0];
12
+ const transformObject = await getDocumentTransformByName(name, loader);
13
+ return { name, transformObject, config: Object.values(documentTransform)[0] };
14
+ }
15
+ throw new Error(`
16
+ An unknown format document transform: '${defaultName}'.
17
+ `);
18
+ }
19
+ function isTransformObject(config) {
20
+ return typeof config === 'object' && config.transform && typeof config.transform === 'function';
21
+ }
22
+ function isTransformFileConfig(config) {
23
+ const keys = Object.keys(config);
24
+ return keys.length === 1 && typeof keys[0] === 'string';
25
+ }
26
+ export async function getDocumentTransformByName(name, loader) {
27
+ const possibleNames = [
28
+ `@graphql-codegen/${name}`,
29
+ `@graphql-codegen/${name}-document-transform`,
30
+ name,
31
+ resolve(process.cwd(), name),
32
+ ];
33
+ const possibleModules = possibleNames.concat(resolve(process.cwd(), name));
34
+ for (const moduleName of possibleModules) {
35
+ try {
36
+ return await loader(moduleName);
37
+ }
38
+ catch (err) {
39
+ if (err.code !== 'MODULE_NOT_FOUND' && err.code !== 'ERR_MODULE_NOT_FOUND') {
40
+ throw new Error(`
41
+ Unable to load document transform matching '${name}'.
42
+ Reason:
43
+ ${err.message}
44
+ `);
45
+ }
46
+ }
47
+ }
48
+ const possibleNamesMsg = possibleNames
49
+ .map(name => `
50
+ - ${name}
51
+ `.trimEnd())
52
+ .join('');
53
+ throw new Error(`
54
+ Unable to find document transform matching '${name}'
55
+ Install one of the following packages:
56
+
57
+ ${possibleNamesMsg}
58
+ `);
59
+ }
@@ -0,0 +1,149 @@
1
+ import { createHash } from 'crypto';
2
+ import { dirname, isAbsolute, join } from 'path';
3
+ import logSymbols from 'log-symbols';
4
+ import { executeCodegen } from './codegen.js';
5
+ import { ensureContext } from './config.js';
6
+ import { lifecycleHooks } from './hooks.js';
7
+ import { debugLog } from './utils/debugging.js';
8
+ import { mkdirp, readFile, unlinkFile, writeFile } from './utils/file-system.js';
9
+ import { createWatcher } from './utils/watcher.js';
10
+ import { getLogger } from './utils/logger.js';
11
+ const hash = (content) => createHash('sha1').update(content).digest('base64');
12
+ export async function generate(input, saveToFile = true) {
13
+ const context = ensureContext(input);
14
+ const config = context.getConfig();
15
+ await context.profiler.run(() => lifecycleHooks(config.hooks).afterStart(), 'Lifecycle: afterStart');
16
+ let previouslyGeneratedFilenames = [];
17
+ function removeStaleFiles(config, generationResult) {
18
+ const filenames = generationResult.map(o => o.filename);
19
+ // find stale files from previous build which are not present in current build
20
+ const staleFilenames = previouslyGeneratedFilenames.filter(f => !filenames.includes(f));
21
+ for (const filename of staleFilenames) {
22
+ if (shouldOverwrite(config, filename)) {
23
+ unlinkFile(filename, err => {
24
+ const prettyFilename = filename.replace(`${input.cwd || process.cwd()}/`, '');
25
+ if (err) {
26
+ debugLog(`Cannot remove stale file: ${prettyFilename}\n${err}`);
27
+ }
28
+ else {
29
+ debugLog(`Removed stale file: ${prettyFilename}`);
30
+ }
31
+ });
32
+ }
33
+ }
34
+ previouslyGeneratedFilenames = filenames;
35
+ }
36
+ const recentOutputHash = new Map();
37
+ async function writeOutput(generationResult) {
38
+ if (!saveToFile) {
39
+ return generationResult;
40
+ }
41
+ if (config.watch) {
42
+ removeStaleFiles(config, generationResult);
43
+ }
44
+ await context.profiler.run(async () => {
45
+ await lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
46
+ }, 'Lifecycle: beforeAllFileWrite');
47
+ await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
48
+ const previousHash = recentOutputHash.get(result.filename) || (await hashFile(result.filename));
49
+ const exists = previousHash !== null;
50
+ // Store previous hash to avoid reading from disk again
51
+ if (previousHash) {
52
+ recentOutputHash.set(result.filename, previousHash);
53
+ }
54
+ if (!shouldOverwrite(config, result.filename) && exists) {
55
+ return;
56
+ }
57
+ let content = result.content || '';
58
+ const currentHash = hash(content);
59
+ if (previousHash && currentHash === previousHash) {
60
+ debugLog(`Skipping file (${result.filename}) writing due to indentical hash...`);
61
+ return;
62
+ }
63
+ // skip updating file in dry mode
64
+ if (context.checkMode) {
65
+ context.checkModeStaleFiles.push(result.filename);
66
+ return;
67
+ }
68
+ if (content.length === 0) {
69
+ return;
70
+ }
71
+ const absolutePath = isAbsolute(result.filename)
72
+ ? result.filename
73
+ : join(input.cwd || process.cwd(), result.filename);
74
+ const basedir = dirname(absolutePath);
75
+ await mkdirp(basedir);
76
+ content = await lifecycleHooks(result.hooks).beforeOneFileWrite(absolutePath, content);
77
+ content = await lifecycleHooks(config.hooks).beforeOneFileWrite(absolutePath, content);
78
+ if (content !== result.content) {
79
+ result.content = content;
80
+ // compare the prettified content with the previous hash
81
+ // to compare the content with an existing prettified file
82
+ if (hash(content) === previousHash) {
83
+ debugLog(`Skipping file (${result.filename}) writing due to indentical hash after prettier...`);
84
+ // the modified content is NOT stored in recentOutputHash
85
+ // so a diff can already be detected before executing the hook
86
+ return;
87
+ }
88
+ }
89
+ await writeFile(absolutePath, result.content);
90
+ recentOutputHash.set(result.filename, currentHash);
91
+ await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
92
+ await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
93
+ })), 'Write files');
94
+ await context.profiler.run(() => lifecycleHooks(config.hooks).afterAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: afterAllFileWrite');
95
+ return generationResult;
96
+ }
97
+ // watch mode
98
+ if (config.watch) {
99
+ return createWatcher(context, writeOutput).runningWatcher;
100
+ }
101
+ const { result: outputFiles, error } = await context.profiler.run(() => executeCodegen(context), 'executeCodegen');
102
+ if (error) {
103
+ // If all generation failed, just throw to return non-zero code.
104
+ if (outputFiles.length === 0) {
105
+ throw error;
106
+ }
107
+ // If partial success, but partial output is not allowed, throw to return non-zero code.
108
+ if (!config.allowPartialOutputs) {
109
+ getLogger().error(` ${logSymbols.error} One or more errors occurred, no files were generated. To allow output on errors, set config.allowPartialOutputs=true`);
110
+ throw error;
111
+ }
112
+ // If partial success, and partial output is allowed, warn and proceed to write to files.
113
+ getLogger().warn(` ${logSymbols.warning} One or more errors occurred, some files were generated. To prevent any output on errors, set config.allowPartialOutputs=false`);
114
+ }
115
+ await context.profiler.run(() => writeOutput(outputFiles), 'writeOutput');
116
+ await context.profiler.run(() => lifecycleHooks(config.hooks).beforeDone(), 'Lifecycle: beforeDone');
117
+ if (context.profilerOutput) {
118
+ await writeFile(join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
119
+ }
120
+ return outputFiles;
121
+ }
122
+ function shouldOverwrite(config, outputPath) {
123
+ const globalValue = config.overwrite === undefined ? true : !!config.overwrite;
124
+ const outputConfig = config.generates[outputPath];
125
+ if (!outputConfig) {
126
+ debugLog(`Couldn't find a config of ${outputPath}`);
127
+ return globalValue;
128
+ }
129
+ if (isConfiguredOutput(outputConfig) && typeof outputConfig.overwrite === 'boolean') {
130
+ return outputConfig.overwrite;
131
+ }
132
+ return globalValue;
133
+ }
134
+ function isConfiguredOutput(output) {
135
+ return typeof output.plugins !== 'undefined';
136
+ }
137
+ async function hashFile(filePath) {
138
+ try {
139
+ return hash(await readFile(filePath));
140
+ }
141
+ catch (err) {
142
+ if (err && err.code === 'ENOENT') {
143
+ // return null if file does not exist
144
+ return null;
145
+ }
146
+ // rethrow unexpected errors
147
+ throw err;
148
+ }
149
+ }
@@ -0,0 +1,62 @@
1
+ import { ApolloEngineLoader } from '@graphql-tools/apollo-engine-loader';
2
+ import { CodeFileLoader } from '@graphql-tools/code-file-loader';
3
+ import { GitLoader } from '@graphql-tools/git-loader';
4
+ import { GithubLoader } from '@graphql-tools/github-loader';
5
+ import { loadConfig } from 'graphql-config';
6
+ export const CodegenExtension = (api) => {
7
+ // Schema
8
+ api.loaders.schema.register(new CodeFileLoader({
9
+ pluckConfig: {
10
+ skipIndent: true,
11
+ },
12
+ }));
13
+ api.loaders.schema.register(new GitLoader());
14
+ api.loaders.schema.register(new GithubLoader());
15
+ api.loaders.schema.register(new ApolloEngineLoader());
16
+ // Documents
17
+ api.loaders.documents.register(new CodeFileLoader({
18
+ pluckConfig: {
19
+ skipIndent: true,
20
+ },
21
+ }));
22
+ api.loaders.documents.register(new GitLoader());
23
+ api.loaders.documents.register(new GithubLoader());
24
+ return {
25
+ name: 'codegen',
26
+ };
27
+ };
28
+ export async function findAndLoadGraphQLConfig(filepath) {
29
+ const config = await loadConfig({
30
+ filepath,
31
+ rootDir: process.cwd(),
32
+ extensions: [CodegenExtension],
33
+ throwOnEmpty: false,
34
+ throwOnMissing: false,
35
+ });
36
+ if (isGraphQLConfig(config)) {
37
+ return config;
38
+ }
39
+ }
40
+ // Kamil: user might load a config that is not GraphQL Config
41
+ // so we need to check if it's a regular config or not
42
+ function isGraphQLConfig(config) {
43
+ if (!config) {
44
+ return false;
45
+ }
46
+ try {
47
+ return config.getDefault().hasExtension('codegen');
48
+ }
49
+ catch { }
50
+ try {
51
+ for (const projectName in config.projects) {
52
+ if (Object.prototype.hasOwnProperty.call(config.projects, projectName)) {
53
+ const project = config.projects[projectName];
54
+ if (project.hasExtension('codegen')) {
55
+ return true;
56
+ }
57
+ }
58
+ }
59
+ }
60
+ catch { }
61
+ return false;
62
+ }