@grafana/create-plugin 6.2.0-canary.2233.19133609453.0 → 6.2.0-canary.2283.19225164741.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 (62) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/CONTRIBUTING.md +3 -0
  3. package/dist/bin/run.js +1 -3
  4. package/dist/commands/update.command.js +1 -1
  5. package/dist/{codemods → migrations}/context.js +2 -3
  6. package/dist/{codemods/migrations → migrations}/manager.js +7 -8
  7. package/dist/{codemods/migrations → migrations}/migrations.js +6 -3
  8. package/dist/{codemods/migrations → migrations}/scripts/003-update-eslint-deprecation-rule.js +1 -1
  9. package/dist/{codemods/migrations → migrations}/scripts/004-eslint9-flat-config.js +1 -2
  10. package/dist/migrations/scripts/005-react-18-3.js +20 -0
  11. package/dist/{codemods → migrations}/utils.js +22 -5
  12. package/dist/utils/utils.config.js +1 -16
  13. package/package.json +2 -2
  14. package/src/bin/run.ts +1 -2
  15. package/src/commands/index.ts +0 -1
  16. package/src/commands/update.command.ts +1 -1
  17. package/src/{codemods → migrations}/context.test.ts +10 -10
  18. package/src/{codemods → migrations}/context.ts +2 -4
  19. package/src/{codemods/migrations → migrations}/manager.test.ts +18 -23
  20. package/src/{codemods/migrations → migrations}/manager.ts +9 -10
  21. package/src/migrations/migrations.test.ts +16 -0
  22. package/src/{codemods/migrations → migrations}/migrations.ts +8 -3
  23. package/src/{codemods/migrations → migrations}/scripts/001-update-grafana-compose-extend.test.ts +1 -1
  24. package/src/{codemods/migrations → migrations}/scripts/001-update-grafana-compose-extend.ts +1 -1
  25. package/src/{codemods/migrations → migrations}/scripts/002-update-is-compatible-workflow.test.ts +1 -1
  26. package/src/{codemods/migrations → migrations}/scripts/002-update-is-compatible-workflow.ts +1 -1
  27. package/src/{codemods/migrations → migrations}/scripts/003-update-eslint-deprecation-rule.test.ts +1 -1
  28. package/src/{codemods/migrations → migrations}/scripts/003-update-eslint-deprecation-rule.ts +2 -2
  29. package/src/{codemods/migrations → migrations}/scripts/004-eslint9-flat-config.test.ts +1 -1
  30. package/src/{codemods/migrations → migrations}/scripts/004-eslint9-flat-config.ts +2 -3
  31. package/src/migrations/scripts/005-react-18-3.test.ts +145 -0
  32. package/src/migrations/scripts/005-react-18-3.ts +19 -0
  33. package/src/{codemods/migrations → migrations}/scripts/example-migration.test.ts +1 -1
  34. package/src/{codemods/migrations → migrations}/scripts/example-migration.ts +1 -1
  35. package/src/{codemods/migrations → migrations}/utils.test.ts +4 -4
  36. package/src/{codemods → migrations}/utils.ts +33 -37
  37. package/src/utils/utils.config.ts +1 -28
  38. package/templates/common/_package.json +5 -3
  39. package/templates/github/workflows/cp-update.yml +8 -13
  40. package/vitest.config.ts +12 -0
  41. package/dist/codemods/additions/additions.js +0 -11
  42. package/dist/codemods/additions/manager.js +0 -115
  43. package/dist/codemods/additions/scripts/example-addition.js +0 -61
  44. package/dist/codemods/additions/utils.js +0 -10
  45. package/dist/codemods/migrations/utils.js +0 -10
  46. package/dist/commands/add.command.js +0 -86
  47. package/src/codemods/additions/additions.ts +0 -23
  48. package/src/codemods/additions/manager.ts +0 -145
  49. package/src/codemods/additions/scripts/example-addition.test.ts +0 -111
  50. package/src/codemods/additions/scripts/example-addition.ts +0 -79
  51. package/src/codemods/additions/utils.ts +0 -12
  52. package/src/codemods/migrations/migrations.test.ts +0 -12
  53. package/src/codemods/migrations/utils.ts +0 -12
  54. package/src/codemods/types.ts +0 -21
  55. package/src/commands/add.command.ts +0 -97
  56. /package/dist/{codemods/migrations → migrations}/scripts/001-update-grafana-compose-extend.js +0 -0
  57. /package/dist/{codemods/migrations → migrations}/scripts/002-update-is-compatible-workflow.js +0 -0
  58. /package/dist/{codemods/migrations → migrations}/scripts/example-migration.js +0 -0
  59. /package/src/{codemods/migrations → migrations}/fixtures/foo/bar.ts +0 -0
  60. /package/src/{codemods/migrations → migrations}/fixtures/foo/baz.ts +0 -0
  61. /package/src/{codemods/migrations → migrations}/fixtures/migrations.ts +0 -0
  62. /package/src/{codemods → migrations}/test-utils.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,26 @@
1
+ # v6.1.8 (Fri Nov 07 2025)
2
+
3
+ ### Release Notes
4
+
5
+ #### chore(deps): update grafana patch dependencies ([#2217](https://github.com/grafana/plugin-tools/pull/2217))
6
+
7
+ <details>
8
+ <summary>grafana/grafana (@&#8203;grafana/data)</summary>
9
+
10
+ ---
11
+
12
+ #### 🐛 Bug Fix
13
+
14
+ - Create plugin: fix permissions issue in cp-update workflow [#2281](https://github.com/grafana/plugin-tools/pull/2281) ([@jackw](https://github.com/jackw))
15
+ - chore(deps): update grafana patch dependencies [#2217](https://github.com/grafana/plugin-tools/pull/2217) ([@renovate-sh-app[bot]](https://github.com/renovate-sh-app[bot]))
16
+
17
+ #### Authors: 2
18
+
19
+ - [@renovate-sh-app[bot]](https://github.com/renovate-sh-app[bot])
20
+ - Jack Westbrook ([@jackw](https://github.com/jackw))
21
+
22
+ ---
23
+
1
24
  # v6.1.7 (Mon Nov 03 2025)
2
25
 
3
26
  :tada: This release contains work from a new contributor! :tada:
package/CONTRIBUTING.md CHANGED
@@ -222,6 +222,9 @@ describe('Migration - append profile to webpack', () => {
222
222
  });
223
223
  ```
224
224
 
225
+ > [!TIP]
226
+ > A lot of the code used by migrations has debug messaging. You can output this in tests to help debugging by using `DEBUG="create-plugin:migrations" npm run test -w @grafana/create-plugin`
227
+
225
228
  #### How to test a migration locally
226
229
 
227
230
  To test a migration locally you'll need a plugin to test on.
package/dist/bin/run.js CHANGED
@@ -4,7 +4,6 @@ import { update } from '../commands/update.command.js';
4
4
  import { migrate } from '../commands/migrate.command.js';
5
5
  import { version } from '../commands/version.command.js';
6
6
  import { provisioning } from '../commands/provisioning.command.js';
7
- import { add } from '../commands/add.command.js';
8
7
  import { isUnsupportedPlatform } from '../utils/utils.os.js';
9
8
  import { argv, commandName } from '../utils/utils.cli.js';
10
9
  import { output } from '../utils/utils.console.js';
@@ -22,8 +21,7 @@ const commands = {
22
21
  generate,
23
22
  update,
24
23
  version,
25
- provisioning,
26
- add
24
+ provisioning
27
25
  };
28
26
  const command = commands[commandName] || "generate";
29
27
  command(argv);
@@ -1,4 +1,4 @@
1
- import { getMigrationsToRun, runMigrations } from '../codemods/migrations/manager.js';
1
+ import { getMigrationsToRun, runMigrations } from '../migrations/manager.js';
2
2
  import { getPackageManagerWithFallback, getPackageManagerExecCmd, getPackageManagerSilentInstallCmd } from '../utils/utils.packageManager.js';
3
3
  import { lt, gte } from 'semver';
4
4
  import { isGitDirectory, isGitDirectoryClean } from '../utils/utils.git.js';
@@ -1,11 +1,10 @@
1
1
  import { accessSync, constants, readFileSync, readdirSync } from 'node:fs';
2
2
  import { join, dirname, normalize, relative } from 'node:path';
3
- import { debug } from '../utils/utils.cli.js';
3
+ import { migrationsDebug } from './utils.js';
4
4
 
5
5
  var __defProp = Object.defineProperty;
6
6
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
7
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
8
- const codemodsDebug = debug.extend("codemods");
9
8
  class Context {
10
9
  constructor(basePath) {
11
10
  __publicField(this, "files", {});
@@ -42,7 +41,7 @@ class Context {
42
41
  if (originalContent !== content) {
43
42
  this.files[path] = { content, changeType: "update" };
44
43
  } else {
45
- codemodsDebug(`Context.updateFile() - no updates for ${filePath}`);
44
+ migrationsDebug(`Context.updateFile() - no updates for ${filePath}`);
46
45
  }
47
46
  }
48
47
  doesFileExist(filePath) {
@@ -1,12 +1,11 @@
1
- import defaultMigrations from './migrations.js';
2
- import { formatFiles, flushChanges, installNPMDependencies } from '../utils.js';
3
1
  import { gte, satisfies } from 'semver';
4
- import { migrationsDebug, printChanges } from './utils.js';
5
- import { CURRENT_APP_VERSION } from '../../utils/utils.version.js';
6
- import { Context } from '../context.js';
7
- import { gitCommitNoVerify } from '../../utils/utils.git.js';
8
- import { output } from '../../utils/utils.console.js';
9
- import { setRootConfig } from '../../utils/utils.config.js';
2
+ import { Context } from './context.js';
3
+ import defaultMigrations from './migrations.js';
4
+ import { migrationsDebug, formatFiles, flushChanges, printChanges, installNPMDependencies } from './utils.js';
5
+ import { gitCommitNoVerify } from '../utils/utils.git.js';
6
+ import { setRootConfig } from '../utils/utils.config.js';
7
+ import { output } from '../utils/utils.console.js';
8
+ import { CURRENT_APP_VERSION } from '../utils/utils.version.js';
10
9
 
11
10
  function getMigrationsToRun(fromVersion, toVersion, migrations = defaultMigrations.migrations) {
12
11
  const semverRange = `${fromVersion} - ${toVersion}`;
@@ -1,4 +1,4 @@
1
- import { LEGACY_UPDATE_CUTOFF_VERSION } from '../../constants.js';
1
+ import { LEGACY_UPDATE_CUTOFF_VERSION } from '../constants.js';
2
2
 
3
3
  var defaultMigrations = {
4
4
  migrations: {
@@ -21,9 +21,12 @@ var defaultMigrations = {
21
21
  version: LEGACY_UPDATE_CUTOFF_VERSION,
22
22
  description: "Migrate eslint config to flat config format and update devDependencies to latest versions.",
23
23
  migrationScript: "./scripts/004-eslint9-flat-config.js"
24
+ },
25
+ "005-react-18-3": {
26
+ version: "6.1.7-beta.1",
27
+ description: "Update React and ReactDOM 18.x versions to ^18.3.0 to surface React 19 compatibility issues.",
28
+ migrationScript: "./scripts/005-react-18-3.js"
24
29
  }
25
- // Do not use LEGACY_UPDATE_CUTOFF_VERSION for new migrations. It is only used above to force migrations to run
26
- // for those written before the switch to updates as migrations.
27
30
  }
28
31
  };
29
32
 
@@ -1,4 +1,4 @@
1
- import { addDependenciesToPackageJson, removeDependenciesFromPackageJson } from '../../utils.js';
1
+ import { addDependenciesToPackageJson, removeDependenciesFromPackageJson } from '../utils.js';
2
2
 
3
3
  function migrate(context) {
4
4
  if (context.doesFileExist(".config/.eslintrc") && context.doesFileExist("package.json")) {
@@ -4,8 +4,7 @@ import { parse } from 'jsonc-parser';
4
4
  import minimist from 'minimist';
5
5
  import { resolve, dirname, relative } from 'node:path';
6
6
  import * as recast from 'recast';
7
- import { addDependenciesToPackageJson } from '../../utils.js';
8
- import { migrationsDebug } from '../utils.js';
7
+ import { addDependenciesToPackageJson, migrationsDebug } from '../utils.js';
9
8
 
10
9
  const { builders } = recast.types;
11
10
  const legacyKeysToCopy = ["rules", "settings"];
@@ -0,0 +1,20 @@
1
+ import { isVersionGreater, addDependenciesToPackageJson } from '../utils.js';
2
+
3
+ function migrate(context) {
4
+ if (context.doesFileExist("package.json")) {
5
+ const packageJson = JSON.parse(context.getFile("package.json") || "{}");
6
+ if (packageJson.dependencies?.react) {
7
+ if (isVersionGreater(packageJson.dependencies.react, "18.0.0", true)) {
8
+ addDependenciesToPackageJson(context, { react: "^18.3.0" }, { "@types/react": "^18.3.0" });
9
+ }
10
+ }
11
+ if (packageJson.dependencies?.["react-dom"]) {
12
+ if (isVersionGreater(packageJson.dependencies["react-dom"], "18.0.0", true)) {
13
+ addDependenciesToPackageJson(context, { "react-dom": "^18.3.0" }, { "@types/react-dom": "^18.3.0" });
14
+ }
15
+ }
16
+ }
17
+ return context;
18
+ }
19
+
20
+ export { migrate as default };
@@ -1,13 +1,14 @@
1
1
  import { join, dirname } from 'node:path';
2
2
  import { createRequire } from 'node:module';
3
3
  import { mkdirSync, writeFileSync, rmSync } from 'node:fs';
4
+ import { debug } from '../utils/utils.cli.js';
4
5
  import chalk from 'chalk';
5
6
  import { output } from '../utils/utils.console.js';
6
7
  import { getPackageManagerWithFallback, getPackageManagerSilentInstallCmd } from '../utils/utils.packageManager.js';
7
8
  import { execSync } from 'node:child_process';
8
- import { gt, clean, coerce } from 'semver';
9
+ import { gte, gt, clean, coerce } from 'semver';
9
10
 
10
- function printChanges(context, key, meta) {
11
+ function printChanges(context, key, migration) {
11
12
  const changes = context.listChanges();
12
13
  const lines = [];
13
14
  for (const [filePath, { changeType }] of Object.entries(changes)) {
@@ -20,7 +21,7 @@ function printChanges(context, key, meta) {
20
21
  }
21
22
  }
22
23
  output.addHorizontalLine("gray");
23
- output.logSingleLine(`${key} (${meta.description})`);
24
+ output.logSingleLine(`${key} (${migration.description})`);
24
25
  if (lines.length === 0) {
25
26
  output.logSingleLine("No changes were made");
26
27
  } else {
@@ -42,6 +43,7 @@ function flushChanges(context) {
42
43
  }
43
44
  }
44
45
  }
46
+ const migrationsDebug = debug.extend("migrations");
45
47
  async function formatFiles(context) {
46
48
  let prettier;
47
49
  const require = createRequire(import.meta.url);
@@ -118,10 +120,14 @@ function addDependenciesToPackageJson(context, dependencies, devDependencies = {
118
120
  if (currentDeps[dep]) {
119
121
  if (isVersionGreater(newVersion, currentDeps[dep])) {
120
122
  currentDeps[dep] = newVersion;
123
+ } else {
124
+ migrationsDebug("would downgrade dependency %s to %s", dep, newVersion);
121
125
  }
122
126
  } else if (currentDevDeps[dep]) {
123
127
  if (isVersionGreater(newVersion, currentDevDeps[dep])) {
124
128
  currentDevDeps[dep] = newVersion;
129
+ } else {
130
+ migrationsDebug("would downgrade devDependency %s to %s", dep, newVersion);
125
131
  }
126
132
  } else {
127
133
  currentDeps[dep] = newVersion;
@@ -131,10 +137,14 @@ function addDependenciesToPackageJson(context, dependencies, devDependencies = {
131
137
  if (currentDeps[dep]) {
132
138
  if (isVersionGreater(newVersion, currentDeps[dep])) {
133
139
  currentDeps[dep] = newVersion;
140
+ } else {
141
+ migrationsDebug("would downgrade dependency %s to %s", dep, newVersion);
134
142
  }
135
143
  } else if (currentDevDeps[dep]) {
136
144
  if (isVersionGreater(newVersion, currentDevDeps[dep])) {
137
145
  currentDevDeps[dep] = newVersion;
146
+ } else {
147
+ migrationsDebug("would downgrade devDependency %s to %s", dep, newVersion);
138
148
  }
139
149
  } else {
140
150
  currentDevDeps[dep] = newVersion;
@@ -151,6 +161,7 @@ function addDependenciesToPackageJson(context, dependencies, devDependencies = {
151
161
  ...Object.keys(sortedDeps).length > 0 && { dependencies: sortedDeps },
152
162
  ...Object.keys(sortedDevDeps).length > 0 && { devDependencies: sortedDevDeps }
153
163
  };
164
+ migrationsDebug("updated package.json", updatedPackageJson);
154
165
  context.updateFile(packageJsonPath, JSON.stringify(updatedPackageJson, null, 2));
155
166
  }
156
167
  function removeDependenciesFromPackageJson(context, dependencies, devDependencies = [], packageJsonPath = "package.json") {
@@ -159,18 +170,21 @@ function removeDependenciesFromPackageJson(context, dependencies, devDependencie
159
170
  for (const dep of dependencies) {
160
171
  if (currentPackageJson.dependencies?.[dep]) {
161
172
  delete currentPackageJson.dependencies[dep];
173
+ migrationsDebug("removed dependency %s", dep);
162
174
  hasChanges = true;
163
175
  }
164
176
  }
165
177
  for (const dep of devDependencies) {
166
178
  if (currentPackageJson.devDependencies?.[dep]) {
167
179
  delete currentPackageJson.devDependencies[dep];
180
+ migrationsDebug("removed devDependency %s", dep);
168
181
  hasChanges = true;
169
182
  }
170
183
  }
171
184
  if (!hasChanges) {
172
185
  return;
173
186
  }
187
+ migrationsDebug("updated package.json", currentPackageJson);
174
188
  context.updateFile(packageJsonPath, JSON.stringify(currentPackageJson, null, 2));
175
189
  }
176
190
  const DIST_TAGS = {
@@ -178,7 +192,7 @@ const DIST_TAGS = {
178
192
  next: 1,
179
193
  latest: 0
180
194
  };
181
- function isVersionGreater(incomingVersion, existingVersion) {
195
+ function isVersionGreater(incomingVersion, existingVersion, orEqualTo = false) {
182
196
  const incomingIsDistTag = incomingVersion in DIST_TAGS;
183
197
  const existingIsDistTag = existingVersion in DIST_TAGS;
184
198
  if (incomingIsDistTag && existingIsDistTag) {
@@ -192,6 +206,9 @@ function isVersionGreater(incomingVersion, existingVersion) {
192
206
  if (!incomingSemver || !existingSemver) {
193
207
  return true;
194
208
  }
209
+ if (orEqualTo) {
210
+ return gte(incomingSemver, existingSemver);
211
+ }
195
212
  return gt(incomingSemver, existingSemver);
196
213
  }
197
214
  function cleanSemver(version) {
@@ -201,4 +218,4 @@ function sortObjectByKeys(obj) {
201
218
  return Object.keys(obj).sort().reduce((acc, key) => ({ ...acc, [key]: obj[key] }), {});
202
219
  }
203
220
 
204
- export { addDependenciesToPackageJson, flushChanges, formatFiles, installNPMDependencies, isVersionGreater, printChanges, readJsonFile, removeDependenciesFromPackageJson };
221
+ export { addDependenciesToPackageJson, flushChanges, formatFiles, installNPMDependencies, isVersionGreater, migrationsDebug, printChanges, readJsonFile, removeDependenciesFromPackageJson };
@@ -8,9 +8,6 @@ import path from 'node:path';
8
8
  import { writeFile } from 'node:fs/promises';
9
9
  import { EOL } from 'node:os';
10
10
 
11
- function isFeatureEnabled(features, featureName) {
12
- return features[featureName] === true;
13
- }
14
11
  let hasShownConfigWarnings = false;
15
12
  function getConfig(workDir = process.cwd()) {
16
13
  const rootConfig = getRootConfig(workDir);
@@ -95,17 +92,5 @@ async function setRootConfig(configOverride = {}) {
95
92
  await writeFile(rootConfigPath, JSON.stringify(updatedConfig, null, 2) + EOL);
96
93
  return updatedConfig;
97
94
  }
98
- async function setFeatureFlag(featureName, enabled = true) {
99
- const userConfig = getUserConfig() || { features: {} };
100
- const userConfigPath = path.resolve(process.cwd(), ".cprc.json");
101
- const updatedConfig = {
102
- ...userConfig,
103
- features: {
104
- ...userConfig.features,
105
- [featureName]: enabled
106
- }
107
- };
108
- await writeFile(userConfigPath, JSON.stringify(updatedConfig, null, 2) + EOL);
109
- }
110
95
 
111
- export { getConfig, isFeatureEnabled, setFeatureFlag, setRootConfig };
96
+ export { getConfig, setRootConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grafana/create-plugin",
3
- "version": "6.2.0-canary.2233.19133609453.0",
3
+ "version": "6.2.0-canary.2283.19225164741.0",
4
4
  "repository": {
5
5
  "directory": "packages/create-plugin",
6
6
  "url": "https://github.com/grafana/plugin-tools"
@@ -59,5 +59,5 @@
59
59
  "engines": {
60
60
  "node": ">=20"
61
61
  },
62
- "gitHead": "d84f5bcb2be40efa1f28ad4a86b52a8a1566e85c"
62
+ "gitHead": "2771e041152fce899b6ecbbf3a37dce085a4a563"
63
63
  }
package/src/bin/run.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import minimist from 'minimist';
4
- import { add, generate, update, migrate, version, provisioning } from '../commands/index.js';
4
+ import { generate, update, migrate, version, provisioning } from '../commands/index.js';
5
5
  import { isUnsupportedPlatform } from '../utils/utils.os.js';
6
6
  import { argv, commandName } from '../utils/utils.cli.js';
7
7
  import { output } from '../utils/utils.console.js';
@@ -23,7 +23,6 @@ const commands: Record<string, (argv: minimist.ParsedArgs) => void> = {
23
23
  update,
24
24
  version,
25
25
  provisioning,
26
- add,
27
26
  };
28
27
  const command = commands[commandName] || 'generate';
29
28
 
@@ -3,4 +3,3 @@ export * from './update.command.js';
3
3
  export * from './migrate.command.js';
4
4
  export * from './version.command.js';
5
5
  export * from './provisioning.command.js';
6
- export * from './add.command.js';
@@ -1,4 +1,4 @@
1
- import { getMigrationsToRun, runMigrations } from '../codemods/migrations/manager.js';
1
+ import { getMigrationsToRun, runMigrations } from '../migrations/manager.js';
2
2
  import {
3
3
  getPackageManagerExecCmd,
4
4
  getPackageManagerSilentInstallCmd,
@@ -3,7 +3,7 @@ import { Context } from './context.js';
3
3
  describe('Context', () => {
4
4
  describe('getFile', () => {
5
5
  it('should read a file from the file system', () => {
6
- const context = new Context(`${__dirname}/migrations/fixtures`);
6
+ const context = new Context(`${__dirname}/fixtures`);
7
7
  const content = context.getFile('foo/bar.ts');
8
8
  expect(content).toEqual("console.log('foo/bar.ts');\n");
9
9
  });
@@ -16,14 +16,14 @@ describe('Context', () => {
16
16
  });
17
17
 
18
18
  it('should get a file that was updated in the current context', () => {
19
- const context = new Context(`${__dirname}/migrations/fixtures`);
19
+ const context = new Context(`${__dirname}/fixtures`);
20
20
  context.updateFile('foo/bar.ts', 'content');
21
21
  const content = context.getFile('foo/bar.ts');
22
22
  expect(content).toEqual('content');
23
23
  });
24
24
 
25
25
  it('should not return a file that was marked for deletion', () => {
26
- const context = new Context(`${__dirname}/migrations/fixtures`);
26
+ const context = new Context(`${__dirname}/fixtures`);
27
27
  context.deleteFile('foo/bar.ts');
28
28
  const content = context.getFile('foo/bar.ts');
29
29
  expect(content).toEqual(undefined);
@@ -77,7 +77,7 @@ describe('Context', () => {
77
77
 
78
78
  describe('renameFile', () => {
79
79
  it('should rename a file', () => {
80
- const context = new Context(`${__dirname}/migrations/fixtures`);
80
+ const context = new Context(`${__dirname}/fixtures`);
81
81
  context.renameFile('foo/bar.ts', 'new-file.txt');
82
82
  expect(context.listChanges()).toEqual({
83
83
  'new-file.txt': { content: "console.log('foo/bar.ts');\n", changeType: 'add' },
@@ -102,20 +102,20 @@ describe('Context', () => {
102
102
 
103
103
  describe('readDir', () => {
104
104
  it('should read the directory', () => {
105
- const context = new Context(`${__dirname}/migrations/fixtures`);
105
+ const context = new Context(`${__dirname}/fixtures`);
106
106
  const files = context.readDir('foo');
107
107
  expect(files).toEqual(['foo/bar.ts', 'foo/baz.ts']);
108
108
  });
109
109
 
110
110
  it('should filter out deleted files', () => {
111
- const context = new Context(`${__dirname}/migrations/fixtures`);
111
+ const context = new Context(`${__dirname}/fixtures`);
112
112
  context.deleteFile('foo/bar.ts');
113
113
  const files = context.readDir('foo');
114
114
  expect(files).toEqual(['foo/baz.ts']);
115
115
  });
116
116
 
117
117
  it('should include files that are only added to the context', () => {
118
- const context = new Context(`${__dirname}/migrations/fixtures`);
118
+ const context = new Context(`${__dirname}/fixtures`);
119
119
  context.addFile('foo/foo.txt', '');
120
120
  const files = context.readDir('foo');
121
121
  expect(files).toEqual(['foo/bar.ts', 'foo/baz.ts', 'foo/foo.txt']);
@@ -124,7 +124,7 @@ describe('Context', () => {
124
124
 
125
125
  describe('normalisePath', () => {
126
126
  it('should normalise the path', () => {
127
- const context = new Context(`${__dirname}/migrations/fixtures`);
127
+ const context = new Context(`${__dirname}/fixtures`);
128
128
  expect(context.normalisePath('foo/bar.ts')).toEqual('foo/bar.ts');
129
129
  expect(context.normalisePath('./foo/bar.ts')).toEqual('foo/bar.ts');
130
130
  expect(context.normalisePath('/foo/bar.ts')).toEqual('foo/bar.ts');
@@ -133,12 +133,12 @@ describe('Context', () => {
133
133
 
134
134
  describe('hasChanges', () => {
135
135
  it('should return FALSE if the context has no changes', () => {
136
- const context = new Context(`${__dirname}/migrations/fixtures`);
136
+ const context = new Context(`${__dirname}/fixtures`);
137
137
  expect(context.hasChanges()).toEqual(false);
138
138
  });
139
139
 
140
140
  it('should return TRUE if the context has changes', () => {
141
- const context = new Context(`${__dirname}/migrations/fixtures`);
141
+ const context = new Context(`${__dirname}/fixtures`);
142
142
 
143
143
  context.addFile('foo.ts', '');
144
144
 
@@ -1,8 +1,6 @@
1
1
  import { constants, accessSync, readFileSync, readdirSync } from 'node:fs';
2
2
  import { relative, normalize, join, dirname } from 'node:path';
3
- import { debug } from '../utils/utils.cli.js';
4
-
5
- const codemodsDebug = debug.extend('codemods');
3
+ import { migrationsDebug } from './utils.js';
6
4
 
7
5
  export type ContextFile = Record<
8
6
  string,
@@ -60,7 +58,7 @@ export class Context {
60
58
  if (originalContent !== content) {
61
59
  this.files[path] = { content, changeType: 'update' };
62
60
  } else {
63
- codemodsDebug(`Context.updateFile() - no updates for ${filePath}`);
61
+ migrationsDebug(`Context.updateFile() - no updates for ${filePath}`);
64
62
  }
65
63
  }
66
64
 
@@ -1,27 +1,22 @@
1
- import { flushChanges, formatFiles } from '../utils.js';
1
+ import { vi } from 'vitest';
2
2
  import { getMigrationsToRun, runMigration, runMigrations } from './manager.js';
3
-
4
- import { Context } from '../context.js';
5
- import { MigrationMeta } from './migrations.js';
6
- import { gitCommitNoVerify } from '../../utils/utils.git.js';
7
3
  import migrationFixtures from './fixtures/migrations.js';
8
- import { printChanges } from './utils.js';
9
- import { setRootConfig } from '../../utils/utils.config.js';
10
- import { vi } from 'vitest';
4
+ import { Context } from './context.js';
5
+ import { gitCommitNoVerify } from '../utils/utils.git.js';
6
+ import { flushChanges, printChanges, formatFiles } from './utils.js';
7
+ import { setRootConfig } from '../utils/utils.config.js';
8
+ import { MigrationMeta } from './migrations.js';
11
9
 
12
10
  vi.mock('./utils.js', () => ({
11
+ flushChanges: vi.fn(),
13
12
  printChanges: vi.fn(),
14
13
  migrationsDebug: vi.fn(),
15
- }));
16
-
17
- vi.mock('../utils.js', () => ({
18
- flushChanges: vi.fn(),
19
14
  formatFiles: vi.fn(),
20
15
  installNPMDependencies: vi.fn(),
21
16
  }));
22
17
 
23
18
  // Silence terminal output during tests.
24
- vi.mock('../../utils/utils.console.js', () => ({
19
+ vi.mock('../utils/utils.console.js', () => ({
25
20
  output: {
26
21
  log: vi.fn(),
27
22
  addHorizontalLine: vi.fn(),
@@ -30,10 +25,10 @@ vi.mock('../../utils/utils.console.js', () => ({
30
25
  },
31
26
  }));
32
27
 
33
- vi.mock('../../utils/utils.config.js', () => ({
28
+ vi.mock('../utils/utils.config.js', () => ({
34
29
  setRootConfig: vi.fn(),
35
30
  }));
36
- vi.mock('../../utils/utils.git.js', () => ({
31
+ vi.mock('../utils/utils.git.js', () => ({
37
32
  gitCommitNoVerify: vi.fn(),
38
33
  }));
39
34
 
@@ -122,14 +117,14 @@ describe('Migrations', () => {
122
117
  const mockContext = new Context('/virtual');
123
118
  const migrationFn = vi.fn().mockResolvedValue(mockContext);
124
119
 
125
- vi.doMock('./test-migration.js', () => ({
120
+ vi.doMock('virtual-test-migration.js', () => ({
126
121
  default: migrationFn,
127
122
  }));
128
123
 
129
124
  const migration: MigrationMeta = {
130
125
  version: '1.0.0',
131
126
  description: 'test migration',
132
- migrationScript: './test-migration.js',
127
+ migrationScript: 'virtual-test-migration.js',
133
128
  };
134
129
 
135
130
  const result = await runMigration(migration, mockContext);
@@ -144,10 +139,10 @@ describe('Migrations', () => {
144
139
  const migrationTwoFn = vi.fn();
145
140
  const consoleMock = vi.spyOn(console, 'log').mockImplementation(() => undefined);
146
141
 
147
- vi.doMock('./migration-one.js', () => ({
142
+ vi.doMock('virtual-test-migration.js', async () => ({
148
143
  default: migrationOneFn,
149
144
  }));
150
- vi.doMock('./migration-two.js', () => ({
145
+ vi.doMock('virtual-test-migration2.js', async () => ({
151
146
  default: migrationTwoFn,
152
147
  }));
153
148
 
@@ -155,12 +150,12 @@ describe('Migrations', () => {
155
150
  'migration-one': {
156
151
  version: '1.0.0',
157
152
  description: '...',
158
- migrationScript: './migration-one.js',
153
+ migrationScript: 'virtual-test-migration.js',
159
154
  },
160
155
  'migration-two': {
161
156
  version: '1.2.0',
162
157
  description: '...',
163
- migrationScript: './migration-two.js',
158
+ migrationScript: 'virtual-test-migration2.js',
164
159
  },
165
160
  };
166
161
 
@@ -208,7 +203,7 @@ describe('Migrations', () => {
208
203
  it('should commit the changes for each migration if the CLI arg is present', async () => {
209
204
  await runMigrations(migrations, { commitEachMigration: true });
210
205
 
211
- expect(gitCommitNoVerify).toHaveBeenCalledTimes(3);
206
+ expect(gitCommitNoVerify).toHaveBeenCalledTimes(2);
212
207
  });
213
208
 
214
209
  it('should not create a commit for a migration that has no changes', async () => {
@@ -216,7 +211,7 @@ describe('Migrations', () => {
216
211
 
217
212
  await runMigrations(migrations, { commitEachMigration: true });
218
213
 
219
- expect(gitCommitNoVerify).toHaveBeenCalledTimes(2);
214
+ expect(gitCommitNoVerify).toHaveBeenCalledTimes(1);
220
215
  });
221
216
 
222
217
  it('should update version in ".config/.cprc.json" on a successful update', async () => {
@@ -1,14 +1,13 @@
1
+ import { satisfies, gte } from 'semver';
2
+ import { Context } from './context.js';
1
3
  import defaultMigrations, { MigrationMeta } from './migrations.js';
2
- import { flushChanges, formatFiles, installNPMDependencies } from '../utils.js';
3
- import { gte, satisfies } from 'semver';
4
- import { migrationsDebug, printChanges } from './utils.js';
4
+ import { flushChanges, printChanges, migrationsDebug, formatFiles, installNPMDependencies } from './utils.js';
5
+ import { gitCommitNoVerify } from '../utils/utils.git.js';
6
+ import { setRootConfig } from '../utils/utils.config.js';
7
+ import { output } from '../utils/utils.console.js';
8
+ import { CURRENT_APP_VERSION } from '../utils/utils.version.js';
5
9
 
6
- import { CURRENT_APP_VERSION } from '../../utils/utils.version.js';
7
- import { Context } from '../context.js';
8
- import type { MigrationModule } from '../types.js';
9
- import { gitCommitNoVerify } from '../../utils/utils.git.js';
10
- import { output } from '../../utils/utils.console.js';
11
- import { setRootConfig } from '../../utils/utils.config.js';
10
+ export type MigrationFn = (context: Context) => Context | Promise<Context>;
12
11
 
13
12
  export function getMigrationsToRun(
14
13
  fromVersion: string,
@@ -77,7 +76,7 @@ export async function runMigrations(migrations: Record<string, MigrationMeta>, o
77
76
  }
78
77
 
79
78
  export async function runMigration(migration: MigrationMeta, context: Context): Promise<Context> {
80
- const module = (await import(migration.migrationScript)) as MigrationModule;
79
+ const module: { default: MigrationFn } = await import(migration.migrationScript);
81
80
 
82
81
  return module.default(context);
83
82
  }
@@ -0,0 +1,16 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import defaultMigrations from './migrations.js';
4
+
5
+ describe('migrations json', () => {
6
+ // As migration scripts are imported dynamically when update is run we assert the path is valid
7
+ // Vitest 4 reimplemented it's workers which caused the previous dynamic import tests to fail.
8
+ // This test now only asserts that the source file exists.
9
+ Object.entries(defaultMigrations.migrations).forEach(([key, migration]) => {
10
+ it(`should have a valid migration script path for ${key}`, () => {
11
+ const migrationPathString = migration.migrationScript.replace('.js', '.ts');
12
+ const path = join(__dirname, migrationPathString);
13
+ expect(existsSync(path)).toBe(true);
14
+ });
15
+ });
16
+ });
@@ -1,4 +1,4 @@
1
- import { LEGACY_UPDATE_CUTOFF_VERSION } from '../../constants.js';
1
+ import { LEGACY_UPDATE_CUTOFF_VERSION } from '../constants.js';
2
2
 
3
3
  export type MigrationMeta = {
4
4
  version: string;
@@ -10,6 +10,8 @@ type Migrations = {
10
10
  migrations: Record<string, MigrationMeta>;
11
11
  };
12
12
 
13
+ // Do not use LEGACY_UPDATE_CUTOFF_VERSION for new migrations. It was used to force migrations to run
14
+ // for those written before the switch to updates as migrations.
13
15
  export default {
14
16
  migrations: {
15
17
  '001-update-grafana-compose-extend': {
@@ -33,7 +35,10 @@ export default {
33
35
  description: 'Migrate eslint config to flat config format and update devDependencies to latest versions.',
34
36
  migrationScript: './scripts/004-eslint9-flat-config.js',
35
37
  },
36
- // Do not use LEGACY_UPDATE_CUTOFF_VERSION for new migrations. It is only used above to force migrations to run
37
- // for those written before the switch to updates as migrations.
38
+ '005-react-18-3': {
39
+ version: '6.1.7-beta.1',
40
+ description: 'Update React and ReactDOM 18.x versions to ^18.3.0 to surface React 19 compatibility issues.',
41
+ migrationScript: './scripts/005-react-18-3.js',
42
+ },
38
43
  },
39
44
  } as Migrations;