@graphql-codegen/cli 7.3.1 → 7.4.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/cjs/codegen.js CHANGED
@@ -378,6 +378,15 @@ async function executeCodegen(input) {
378
378
  filename: outputArgs.filename,
379
379
  content: output,
380
380
  hooks: outputConfig.hooks || {},
381
+ // A preset sets `outputArgs` per output via `buildGeneratesSection`.
382
+ // Fall back to `outputConfig` so plugin outputs can opt in too.
383
+ contentComparison: outputArgs.contentComparison ?? outputConfig.contentComparison,
384
+ // Carry the entry's `overwrite` onto the file so the CLI can honor it per file.
385
+ // Unlike `contentComparison`, this is intentionally not
386
+ // `outputArgs.overwrite ?? outputConfig.overwrite`: presets should not
387
+ // be able to set `overwrite` per file from buildGeneratesSection
388
+ // (a capability this doesn't need).
389
+ overwrite: outputConfig.overwrite,
381
390
  });
382
391
  };
383
392
  await context.profiler.run(() => Promise.all(outputs.map(process)), `Codegen: ${filename}`);
@@ -17,15 +17,17 @@ async function generate(input, saveToFile = true) {
17
17
  const context = (0, config_js_1.ensureContext)(input);
18
18
  const config = context.getConfig();
19
19
  await context.profiler.run(() => (0, hooks_js_1.lifecycleHooks)(config.hooks).afterStart(), 'Lifecycle: afterStart');
20
- let previouslyGeneratedFilenames = [];
20
+ // Store only the projection (`filename` + `overwrite`) rather than full results, so a
21
+ // file that disappears is still judged by the `overwrite` of the entry that produced it.
22
+ let previouslyGeneratedFiles = [];
21
23
  function removeStaleFiles(config, generationResult) {
22
24
  const filenames = generationResult.map(o => o.filename);
23
25
  // find stale files from previous build which are not present in current build
24
- const staleFilenames = previouslyGeneratedFilenames.filter(f => !filenames.includes(f));
25
- for (const filename of staleFilenames) {
26
- if (normalizeOverwriteConfig(config, filename).removeStaleFiles) {
27
- (0, file_system_js_1.unlinkFile)(filename, err => {
28
- const prettyFilename = filename.replace(`${input.cwd || process.cwd()}/`, '');
26
+ const staleFiles = previouslyGeneratedFiles.filter(f => !filenames.includes(f.filename));
27
+ for (const staleFile of staleFiles) {
28
+ if (normalizeOverwriteConfig(config.overwrite, staleFile.overwrite).removeStaleFiles) {
29
+ (0, file_system_js_1.unlinkFile)(staleFile.filename, err => {
30
+ const prettyFilename = staleFile.filename.replace(`${input.cwd || process.cwd()}/`, '');
29
31
  if (err) {
30
32
  (0, debugging_js_1.debugLog)(`Cannot remove stale file: ${prettyFilename}\n${err}`);
31
33
  }
@@ -35,8 +37,15 @@ async function generate(input, saveToFile = true) {
35
37
  });
36
38
  }
37
39
  }
38
- previouslyGeneratedFilenames = filenames;
40
+ previouslyGeneratedFiles = generationResult.map(res => ({
41
+ filename: res.filename,
42
+ overwrite: res.overwrite,
43
+ }));
39
44
  }
45
+ // Records the hash of the content codegen last wrote per file. This is always
46
+ // kept up to date; `FileOutput.contentComparison` only decides whether the
47
+ // skip-check trusts this record ('cache-first') or re-reads the file from disk
48
+ // ('disk'), for outputs whose content depends on the file's existing content.
40
49
  const recentOutputHash = new Map();
41
50
  async function writeOutput(generationResult) {
42
51
  if (!saveToFile) {
@@ -49,13 +58,29 @@ async function generate(input, saveToFile = true) {
49
58
  await (0, hooks_js_1.lifecycleHooks)(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
50
59
  }, 'Lifecycle: beforeAllFileWrite');
51
60
  await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
52
- const previousHash = recentOutputHash.get(result.filename) || (await hashFile(result.filename));
61
+ // The "previous" hash the skip-check compares against:
62
+ // - 'cache-first' trusts the in-memory record of what codegen last wrote
63
+ // (falling back to disk when there's no entry).
64
+ // - 'disk' always re-reads the file, because the output's content
65
+ // depends on the file's existing content
66
+ // (e.g. a preset that reads the file and rewrites part of it), so the
67
+ // in-memory record could wrongly skip a write when the file was changed
68
+ // on disk but the regenerated content matches a previous run.
69
+ const previousHash = await (async function getPreviousHash() {
70
+ const { contentComparison = 'cache-first' } = result;
71
+ if (contentComparison === 'disk') {
72
+ return await hashFile(result.filename);
73
+ }
74
+ return recentOutputHash.get(result.filename) || (await hashFile(result.filename));
75
+ })();
53
76
  const exists = previousHash !== null;
54
- // Store previous hash to avoid reading from disk again
77
+ // Always update the cache, regardless of `cache-first` or `disk` option,
78
+ // so subsequent runs have consistent entry to compare against
55
79
  if (previousHash) {
56
80
  recentOutputHash.set(result.filename, previousHash);
57
81
  }
58
- if (!normalizeOverwriteConfig(config, result.filename).updateExistingFiles && exists) {
82
+ if (!normalizeOverwriteConfig(config.overwrite, result.overwrite).updateExistingFiles &&
83
+ exists) {
59
84
  return;
60
85
  }
61
86
  let content = result.content || '';
@@ -132,19 +157,8 @@ async function generate(input, saveToFile = true) {
132
157
  await writeProfilerOutput();
133
158
  return outputFiles;
134
159
  }
135
- function normalizeOverwriteConfig(config, outputPath) {
136
- const overwrite = (function getOverwriteOption() {
137
- const { overwrite: result = true } = config;
138
- const outputConfig = config.generates[outputPath];
139
- if (!outputConfig) {
140
- (0, debugging_js_1.debugLog)(`Couldn't find a config of ${outputPath}`);
141
- return result;
142
- }
143
- if (isConfiguredOutput(outputConfig) && outputConfig.overwrite !== undefined) {
144
- return outputConfig.overwrite;
145
- }
146
- return result;
147
- })();
160
+ function normalizeOverwriteConfig(configOverwrite, fileOverwrite) {
161
+ const overwrite = fileOverwrite ?? configOverwrite ?? true;
148
162
  if (overwrite === true) {
149
163
  return {
150
164
  removeStaleFiles: true,
@@ -160,9 +174,6 @@ function normalizeOverwriteConfig(config, outputPath) {
160
174
  const { removeStaleFiles = true, updateExistingFiles = true } = overwrite;
161
175
  return { removeStaleFiles, updateExistingFiles };
162
176
  }
163
- function isConfiguredOutput(output) {
164
- return typeof output.plugins !== 'undefined';
165
- }
166
177
  async function hashFile(filePath) {
167
178
  try {
168
179
  return hash(await (0, file_system_js_1.readFile)(filePath));
package/cjs/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.version = void 0;
4
- exports.version = '7.3.1';
4
+ exports.version = '7.4.0';
package/esm/codegen.js CHANGED
@@ -374,6 +374,15 @@ export async function executeCodegen(input) {
374
374
  filename: outputArgs.filename,
375
375
  content: output,
376
376
  hooks: outputConfig.hooks || {},
377
+ // A preset sets `outputArgs` per output via `buildGeneratesSection`.
378
+ // Fall back to `outputConfig` so plugin outputs can opt in too.
379
+ contentComparison: outputArgs.contentComparison ?? outputConfig.contentComparison,
380
+ // Carry the entry's `overwrite` onto the file so the CLI can honor it per file.
381
+ // Unlike `contentComparison`, this is intentionally not
382
+ // `outputArgs.overwrite ?? outputConfig.overwrite`: presets should not
383
+ // be able to set `overwrite` per file from buildGeneratesSection
384
+ // (a capability this doesn't need).
385
+ overwrite: outputConfig.overwrite,
377
386
  });
378
387
  };
379
388
  await context.profiler.run(() => Promise.all(outputs.map(process)), `Codegen: ${filename}`);
@@ -13,15 +13,17 @@ export async function generate(input, saveToFile = true) {
13
13
  const context = ensureContext(input);
14
14
  const config = context.getConfig();
15
15
  await context.profiler.run(() => lifecycleHooks(config.hooks).afterStart(), 'Lifecycle: afterStart');
16
- let previouslyGeneratedFilenames = [];
16
+ // Store only the projection (`filename` + `overwrite`) rather than full results, so a
17
+ // file that disappears is still judged by the `overwrite` of the entry that produced it.
18
+ let previouslyGeneratedFiles = [];
17
19
  function removeStaleFiles(config, generationResult) {
18
20
  const filenames = generationResult.map(o => o.filename);
19
21
  // 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 (normalizeOverwriteConfig(config, filename).removeStaleFiles) {
23
- unlinkFile(filename, err => {
24
- const prettyFilename = filename.replace(`${input.cwd || process.cwd()}/`, '');
22
+ const staleFiles = previouslyGeneratedFiles.filter(f => !filenames.includes(f.filename));
23
+ for (const staleFile of staleFiles) {
24
+ if (normalizeOverwriteConfig(config.overwrite, staleFile.overwrite).removeStaleFiles) {
25
+ unlinkFile(staleFile.filename, err => {
26
+ const prettyFilename = staleFile.filename.replace(`${input.cwd || process.cwd()}/`, '');
25
27
  if (err) {
26
28
  debugLog(`Cannot remove stale file: ${prettyFilename}\n${err}`);
27
29
  }
@@ -31,8 +33,15 @@ export async function generate(input, saveToFile = true) {
31
33
  });
32
34
  }
33
35
  }
34
- previouslyGeneratedFilenames = filenames;
36
+ previouslyGeneratedFiles = generationResult.map(res => ({
37
+ filename: res.filename,
38
+ overwrite: res.overwrite,
39
+ }));
35
40
  }
41
+ // Records the hash of the content codegen last wrote per file. This is always
42
+ // kept up to date; `FileOutput.contentComparison` only decides whether the
43
+ // skip-check trusts this record ('cache-first') or re-reads the file from disk
44
+ // ('disk'), for outputs whose content depends on the file's existing content.
36
45
  const recentOutputHash = new Map();
37
46
  async function writeOutput(generationResult) {
38
47
  if (!saveToFile) {
@@ -45,13 +54,29 @@ export async function generate(input, saveToFile = true) {
45
54
  await lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
46
55
  }, 'Lifecycle: beforeAllFileWrite');
47
56
  await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
48
- const previousHash = recentOutputHash.get(result.filename) || (await hashFile(result.filename));
57
+ // The "previous" hash the skip-check compares against:
58
+ // - 'cache-first' trusts the in-memory record of what codegen last wrote
59
+ // (falling back to disk when there's no entry).
60
+ // - 'disk' always re-reads the file, because the output's content
61
+ // depends on the file's existing content
62
+ // (e.g. a preset that reads the file and rewrites part of it), so the
63
+ // in-memory record could wrongly skip a write when the file was changed
64
+ // on disk but the regenerated content matches a previous run.
65
+ const previousHash = await (async function getPreviousHash() {
66
+ const { contentComparison = 'cache-first' } = result;
67
+ if (contentComparison === 'disk') {
68
+ return await hashFile(result.filename);
69
+ }
70
+ return recentOutputHash.get(result.filename) || (await hashFile(result.filename));
71
+ })();
49
72
  const exists = previousHash !== null;
50
- // Store previous hash to avoid reading from disk again
73
+ // Always update the cache, regardless of `cache-first` or `disk` option,
74
+ // so subsequent runs have consistent entry to compare against
51
75
  if (previousHash) {
52
76
  recentOutputHash.set(result.filename, previousHash);
53
77
  }
54
- if (!normalizeOverwriteConfig(config, result.filename).updateExistingFiles && exists) {
78
+ if (!normalizeOverwriteConfig(config.overwrite, result.overwrite).updateExistingFiles &&
79
+ exists) {
55
80
  return;
56
81
  }
57
82
  let content = result.content || '';
@@ -128,19 +153,8 @@ export async function generate(input, saveToFile = true) {
128
153
  await writeProfilerOutput();
129
154
  return outputFiles;
130
155
  }
131
- function normalizeOverwriteConfig(config, outputPath) {
132
- const overwrite = (function getOverwriteOption() {
133
- const { overwrite: result = true } = config;
134
- const outputConfig = config.generates[outputPath];
135
- if (!outputConfig) {
136
- debugLog(`Couldn't find a config of ${outputPath}`);
137
- return result;
138
- }
139
- if (isConfiguredOutput(outputConfig) && outputConfig.overwrite !== undefined) {
140
- return outputConfig.overwrite;
141
- }
142
- return result;
143
- })();
156
+ function normalizeOverwriteConfig(configOverwrite, fileOverwrite) {
157
+ const overwrite = fileOverwrite ?? configOverwrite ?? true;
144
158
  if (overwrite === true) {
145
159
  return {
146
160
  removeStaleFiles: true,
@@ -156,9 +170,6 @@ function normalizeOverwriteConfig(config, outputPath) {
156
170
  const { removeStaleFiles = true, updateExistingFiles = true } = overwrite;
157
171
  return { removeStaleFiles, updateExistingFiles };
158
172
  }
159
- function isConfiguredOutput(output) {
160
- return typeof output.plugins !== 'undefined';
161
- }
162
173
  async function hashFile(filePath) {
163
174
  try {
164
175
  return hash(await readFile(filePath));
package/esm/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '7.3.1';
1
+ export const version = '7.4.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphql-codegen/cli",
3
- "version": "7.3.1",
3
+ "version": "7.4.0",
4
4
  "peerDependenciesMeta": {
5
5
  "@parcel/watcher": {
6
6
  "optional": true
@@ -45,7 +45,7 @@
45
45
  "yargs": "^18.0.0",
46
46
  "@graphql-codegen/client-preset": "^6.1.3",
47
47
  "@graphql-codegen/core": "^6.2.0",
48
- "@graphql-codegen/plugin-helpers": "^7.2.1"
48
+ "@graphql-codegen/plugin-helpers": "^7.3.0"
49
49
  },
50
50
  "repository": {
51
51
  "type": "git",