@grafana/create-plugin 6.2.0-canary.2314.19505018775.0 → 6.2.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 (71) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/bin/run.js +3 -1
  3. package/dist/codemods/additions/additions.js +9 -0
  4. package/dist/codemods/additions/scripts/example-addition.js +47 -0
  5. package/dist/{migrations → codemods}/context.js +3 -2
  6. package/dist/codemods/migrations/manager.js +32 -0
  7. package/dist/codemods/migrations/migrations.js +50 -0
  8. package/dist/{migrations → codemods/migrations}/scripts/003-update-eslint-deprecation-rule.js +1 -1
  9. package/dist/{migrations → codemods/migrations}/scripts/004-eslint9-flat-config.js +1 -1
  10. package/dist/{migrations → codemods/migrations}/scripts/005-react-18-3.js +1 -1
  11. package/dist/{migrations → codemods/migrations}/scripts/007-remove-testing-library-types.js +1 -1
  12. package/dist/codemods/runner.js +33 -0
  13. package/dist/codemods/schema-parser.js +20 -0
  14. package/dist/{migrations → codemods}/utils.js +5 -16
  15. package/dist/commands/add.command.js +51 -0
  16. package/dist/commands/update.command.js +8 -42
  17. package/dist/utils/utils.checks.js +40 -0
  18. package/package.json +3 -2
  19. package/src/bin/run.ts +2 -1
  20. package/src/codemods/additions/additions.test.ts +17 -0
  21. package/src/codemods/additions/additions.ts +9 -0
  22. package/src/codemods/additions/scripts/example-addition.test.ts +92 -0
  23. package/src/codemods/additions/scripts/example-addition.ts +62 -0
  24. package/src/{migrations → codemods}/context.test.ts +10 -10
  25. package/src/{migrations → codemods}/context.ts +4 -2
  26. package/src/codemods/migrations/fixtures/migrations.ts +20 -0
  27. package/src/{migrations → codemods/migrations}/manager.test.ts +76 -77
  28. package/src/codemods/migrations/manager.ts +50 -0
  29. package/src/codemods/migrations/migrations.test.ts +17 -0
  30. package/src/codemods/migrations/migrations.ts +55 -0
  31. package/src/{migrations → codemods/migrations}/scripts/001-update-grafana-compose-extend.test.ts +1 -1
  32. package/src/{migrations → codemods/migrations}/scripts/001-update-grafana-compose-extend.ts +1 -1
  33. package/src/{migrations → codemods/migrations}/scripts/002-update-is-compatible-workflow.test.ts +1 -1
  34. package/src/{migrations → codemods/migrations}/scripts/002-update-is-compatible-workflow.ts +1 -1
  35. package/src/{migrations → codemods/migrations}/scripts/003-update-eslint-deprecation-rule.test.ts +1 -1
  36. package/src/{migrations → codemods/migrations}/scripts/003-update-eslint-deprecation-rule.ts +2 -2
  37. package/src/{migrations → codemods/migrations}/scripts/004-eslint9-flat-config.test.ts +1 -1
  38. package/src/{migrations → codemods/migrations}/scripts/004-eslint9-flat-config.ts +2 -2
  39. package/src/{migrations → codemods/migrations}/scripts/005-react-18-3.test.ts +1 -1
  40. package/src/{migrations → codemods/migrations}/scripts/005-react-18-3.ts +2 -2
  41. package/src/{migrations → codemods/migrations}/scripts/006-webpack-nested-fix.test.ts +1 -1
  42. package/src/{migrations → codemods/migrations}/scripts/006-webpack-nested-fix.ts +1 -1
  43. package/src/{migrations → codemods/migrations}/scripts/007-remove-testing-library-types.test.ts +1 -1
  44. package/src/{migrations → codemods/migrations}/scripts/007-remove-testing-library-types.ts +2 -2
  45. package/src/codemods/runner.ts +51 -0
  46. package/src/codemods/schema-parser.ts +27 -0
  47. package/src/codemods/types.ts +16 -0
  48. package/src/{migrations → codemods}/utils.test.ts +5 -4
  49. package/src/{migrations → codemods}/utils.ts +8 -22
  50. package/src/commands/add.command.ts +56 -0
  51. package/src/commands/index.ts +1 -0
  52. package/src/commands/update.command.ts +9 -48
  53. package/src/utils/utils.checks.ts +47 -0
  54. package/dist/migrations/manager.js +0 -58
  55. package/dist/migrations/migrations.js +0 -43
  56. package/dist/migrations/scripts/example-migration.js +0 -25
  57. package/src/migrations/fixtures/migrations.ts +0 -19
  58. package/src/migrations/manager.ts +0 -82
  59. package/src/migrations/migrations.test.ts +0 -16
  60. package/src/migrations/migrations.ts +0 -55
  61. package/src/migrations/scripts/example-migration.test.ts +0 -40
  62. package/src/migrations/scripts/example-migration.ts +0 -34
  63. package/templates/panel/.config/AGENTS/fundamentals.md +0 -81
  64. package/templates/panel/.config/AGENTS/howto/add-panel-options.md +0 -130
  65. package/templates/panel/AGENTS.md +0 -3
  66. /package/dist/{migrations → codemods/migrations}/scripts/001-update-grafana-compose-extend.js +0 -0
  67. /package/dist/{migrations → codemods/migrations}/scripts/002-update-is-compatible-workflow.js +0 -0
  68. /package/dist/{migrations → codemods/migrations}/scripts/006-webpack-nested-fix.js +0 -0
  69. /package/src/{migrations → codemods/migrations}/fixtures/foo/bar.ts +0 -0
  70. /package/src/{migrations → codemods/migrations}/fixtures/foo/baz.ts +0 -0
  71. /package/src/{migrations → codemods}/test-utils.ts +0 -0
@@ -1,4 +1,4 @@
1
- import { type Context } from '../context.js';
1
+ import { type Context } from '../../context.js';
2
2
  import { parseDocument, stringify, YAMLSeq } from 'yaml';
3
3
 
4
4
  export default async function migrate(context: Context) {
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import migrate from './003-update-eslint-deprecation-rule.js';
3
- import { Context } from '../context.js';
3
+ import { Context } from '../../context.js';
4
4
 
5
5
  describe('003-update-eslint-deprecation-rule', () => {
6
6
  it('should not update ESLint config if no deprecation rule is present', () => {
@@ -1,5 +1,5 @@
1
- import type { Context } from '../context.js';
2
- import { addDependenciesToPackageJson, removeDependenciesFromPackageJson } from '../utils.js';
1
+ import type { Context } from '../../context.js';
2
+ import { addDependenciesToPackageJson, removeDependenciesFromPackageJson } from '../../utils.js';
3
3
 
4
4
  export default function migrate(context: Context) {
5
5
  if (context.doesFileExist('.config/.eslintrc') && context.doesFileExist('package.json')) {
@@ -1,5 +1,5 @@
1
1
  import migrate from './004-eslint9-flat-config.js';
2
- import { Context } from '../context.js';
2
+ import { Context } from '../../context.js';
3
3
 
4
4
  describe('004-eslint9-flat-config', () => {
5
5
  describe('migration', () => {
@@ -5,8 +5,8 @@ import { parse } from 'jsonc-parser';
5
5
  import minimist from 'minimist';
6
6
  import { dirname, relative, resolve } from 'node:path';
7
7
  import * as recast from 'recast';
8
- import type { Context } from '../context.js';
9
- import { addDependenciesToPackageJson, migrationsDebug } from '../utils.js';
8
+ import type { Context } from '../../context.js';
9
+ import { addDependenciesToPackageJson, migrationsDebug } from '../../utils.js';
10
10
 
11
11
  type Imports = Map<string, { name?: string; bindings?: string[] }>;
12
12
 
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { Context } from '../context.js';
2
+ import { Context } from '../../context.js';
3
3
  import migrate from './005-react-18-3.js';
4
4
 
5
5
  describe('005-react-18-3', () => {
@@ -1,5 +1,5 @@
1
- import { Context } from '../context.js';
2
- import { addDependenciesToPackageJson, isVersionGreater } from '../utils.js';
1
+ import type { Context } from '../../context.js';
2
+ import { addDependenciesToPackageJson, isVersionGreater } from '../../utils.js';
3
3
 
4
4
  export default function migrate(context: Context) {
5
5
  if (context.doesFileExist('package.json')) {
@@ -1,5 +1,5 @@
1
1
  import migrate from './006-webpack-nested-fix.js';
2
- import { createDefaultContext } from '../test-utils.js';
2
+ import { createDefaultContext } from '../../test-utils.js';
3
3
 
4
4
  describe('Migration - webpack nested fix', () => {
5
5
  test('should transform files property to test property', () => {
@@ -1,7 +1,7 @@
1
1
  import { join } from 'node:path';
2
2
  import * as recast from 'recast';
3
3
  import * as typeScriptParser from 'recast/parsers/typescript.js';
4
- import type { Context } from '../context.js';
4
+ import type { Context } from '../../context.js';
5
5
 
6
6
  const { builders } = recast.types;
7
7
 
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import migrate from './007-remove-testing-library-types.js';
3
- import { Context } from '../context.js';
3
+ import { Context } from '../../context.js';
4
4
 
5
5
  describe('006-remove-testing-library-types', () => {
6
6
  it('should create setupTests.d.ts, remove types package, and add @testing-library/jest-dom when file does not exist', () => {
@@ -1,5 +1,5 @@
1
- import type { Context } from '../context.js';
2
- import { removeDependenciesFromPackageJson, isVersionGreater } from '../utils.js';
1
+ import type { Context } from '../../context.js';
2
+ import { removeDependenciesFromPackageJson, isVersionGreater } from '../../utils.js';
3
3
 
4
4
  export default function migrate(context: Context) {
5
5
  if (context.doesFileExist('package.json')) {
@@ -0,0 +1,51 @@
1
+ import { Context } from './context.js';
2
+ import { formatFiles, flushChanges, installNPMDependencies, printChanges } from './utils.js';
3
+ import { parseAndValidateOptions } from './schema-parser.js';
4
+ import { Codemod } from './types.js';
5
+
6
+ /**
7
+ * Run a single codemod
8
+ *
9
+ * Steps:
10
+ * 1. Load codemod module from scriptPath
11
+ * 2. Parse and validate options from schema
12
+ * 3. Execute codemod transformation
13
+ * 4. Format files
14
+ * 5. Flush changes to disk
15
+ * 6. Print summary
16
+ * 7. Install dependencies if needed
17
+ */
18
+ export async function runCodemod(codemod: Codemod, options?: Record<string, any>): Promise<Context> {
19
+ const codemodModule = await import(codemod.scriptPath);
20
+ if (!codemodModule.default || typeof codemodModule.default !== 'function') {
21
+ throw new Error(`Codemod ${codemod.name} must export a default function`);
22
+ }
23
+
24
+ let codemodOptions = {};
25
+
26
+ if (options && codemodModule.schema) {
27
+ codemodOptions = parseAndValidateOptions(options, codemodModule.schema);
28
+ }
29
+
30
+ const basePath = process.cwd();
31
+ const context = new Context(basePath);
32
+
33
+ try {
34
+ const updatedContext = await codemodModule.default(context, codemodOptions);
35
+
36
+ // standard post-processing pipeline
37
+ await formatFiles(updatedContext);
38
+ flushChanges(updatedContext);
39
+ printChanges(updatedContext, codemod.name, codemod.description);
40
+ installNPMDependencies(updatedContext);
41
+
42
+ return updatedContext;
43
+ } catch (error) {
44
+ if (error instanceof Error) {
45
+ const newError = new Error(`Error running ${codemod.name}: ${error.message}`);
46
+ newError.cause = error;
47
+ throw newError;
48
+ }
49
+ throw error;
50
+ }
51
+ }
@@ -0,0 +1,27 @@
1
+ import * as v from 'valibot';
2
+
3
+ /**
4
+ * Parse and validate options using Valibot schema
5
+ * Valibot handles parsing, validation, type coercion, and defaults automatically
6
+ */
7
+ export function parseAndValidateOptions<T extends v.BaseSchema<any, any, any>>(
8
+ options: Record<string, any>,
9
+ schema: T
10
+ ): v.InferOutput<T> {
11
+ try {
12
+ return v.parse(schema, options);
13
+ } catch (error) {
14
+ if (v.isValiError(error)) {
15
+ // format Valibot validation errors
16
+ const formattedErrors = error.issues
17
+ .map((issue) => {
18
+ const path = issue.path?.map((p) => p.key).join('.') || '';
19
+ return ` --${path}: ${issue.message}`;
20
+ })
21
+ .join('\n');
22
+
23
+ throw new Error(`Invalid flag(s) provided:\n\n${formattedErrors}`);
24
+ }
25
+ throw error;
26
+ }
27
+ }
@@ -0,0 +1,16 @@
1
+ import type { Context } from './context.js';
2
+ import type * as v from 'valibot';
3
+
4
+ // used as a generic constraint for codemod schemas. accepts any input, output and error types
5
+ type AnySchema = v.BaseSchema<any, any, any>;
6
+
7
+ export interface CodemodModule<TSchema extends AnySchema = AnySchema> {
8
+ default: (context: Context, options: v.InferOutput<TSchema>) => Context | Promise<Context>;
9
+ schema?: TSchema;
10
+ }
11
+
12
+ export interface Codemod {
13
+ name: string;
14
+ description: string;
15
+ scriptPath: string;
16
+ }
@@ -5,14 +5,15 @@ import {
5
5
  removeDependenciesFromPackageJson,
6
6
  flushChanges,
7
7
  formatFiles,
8
- printChanges,
9
8
  readJsonFile,
10
9
  isVersionGreater,
10
+ printChanges,
11
11
  } from './utils.js';
12
12
  import { join } from 'node:path';
13
13
  import { mkdir, rm, writeFile } from 'node:fs/promises';
14
14
  import { readFileSync } from 'node:fs';
15
15
  import { output } from '../utils/utils.console.js';
16
+ import { vi } from 'vitest';
16
17
 
17
18
  describe('utils', () => {
18
19
  const tmpObj = dirSync({ unsafeCleanup: true });
@@ -80,7 +81,7 @@ describe('utils', () => {
80
81
  context.updateFile('baz.ts', 'new content');
81
82
  context.deleteFile('bar.ts');
82
83
 
83
- printChanges(context, 'key', { migrationScript: 'test', description: 'test', version: '1.0.0' });
84
+ printChanges(context, 'key', 'test');
84
85
 
85
86
  expect(outputMock.addHorizontalLine).toHaveBeenCalledWith('gray');
86
87
  expect(outputMock.logSingleLine).toHaveBeenCalledWith('key (test)');
@@ -102,7 +103,7 @@ describe('utils', () => {
102
103
  it('should print no changes', async () => {
103
104
  const context = new Context(tmpDir);
104
105
 
105
- printChanges(context, 'key', { migrationScript: 'test', description: 'test', version: '1.0.0' });
106
+ printChanges(context, 'key', 'test');
106
107
 
107
108
  expect(outputMock.logSingleLine).toHaveBeenCalledWith('No changes were made');
108
109
  });
@@ -271,7 +272,7 @@ describe('utils', () => {
271
272
  });
272
273
  });
273
274
 
274
- describe('isIncomingVersionGreater', () => {
275
+ describe('isVersionGreater', () => {
275
276
  describe('dist tag comparison', () => {
276
277
  it('should return false when incoming is "latest" and existing is "next"', () => {
277
278
  expect(isVersionGreater('latest', 'next')).toBe(false);
@@ -2,15 +2,14 @@ import { dirname, join } from 'node:path';
2
2
  import { createRequire } from 'node:module';
3
3
  import { Context } from './context.js';
4
4
  import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
5
- import { debug } from '../utils/utils.cli.js';
6
5
  import chalk from 'chalk';
7
- import { MigrationMeta } from './migrations.js';
8
6
  import { output } from '../utils/utils.console.js';
9
7
  import { getPackageManagerSilentInstallCmd, getPackageManagerWithFallback } from '../utils/utils.packageManager.js';
10
8
  import { execSync } from 'node:child_process';
11
9
  import { clean, coerce, gt, gte } from 'semver';
10
+ import { debug } from '../utils/utils.cli.js';
12
11
 
13
- export function printChanges(context: Context, key: string, migration: MigrationMeta) {
12
+ export function printChanges(context: Context, key: string, description: string) {
14
13
  const changes = context.listChanges();
15
14
  const lines = [];
16
15
 
@@ -25,7 +24,7 @@ export function printChanges(context: Context, key: string, migration: Migration
25
24
  }
26
25
 
27
26
  output.addHorizontalLine('gray');
28
- output.logSingleLine(`${key} (${migration.description})`);
27
+ output.logSingleLine(`${key} (${description})`);
29
28
 
30
29
  if (lines.length === 0) {
31
30
  output.logSingleLine('No changes were made');
@@ -51,10 +50,8 @@ export function flushChanges(context: Context) {
51
50
  }
52
51
  }
53
52
 
54
- export const migrationsDebug = debug.extend('migrations');
55
-
56
53
  /**
57
- * Formats the files in the migration context using the version of prettier found in the local node_modules.
54
+ * Formats the files in the context using the version of prettier found in the local node_modules.
58
55
  * If prettier isn't installed or the file is ignored or has no parser, it will not be formatted.
59
56
  *
60
57
  * @param context - The context to format.
@@ -104,7 +101,7 @@ export async function formatFiles(context: Context) {
104
101
  }
105
102
 
106
103
  // Cache the package.json contents to avoid re-installing dependencies if the package.json hasn't changed
107
- // (This runs for each migration used in an update)
104
+ // (This runs for each codemod used in an update)
108
105
  let packageJsonInstallCache: string;
109
106
 
110
107
  export function installNPMDependencies(context: Context) {
@@ -160,14 +157,10 @@ export function addDependenciesToPackageJson(
160
157
  if (currentDeps[dep]) {
161
158
  if (isVersionGreater(newVersion, currentDeps[dep])) {
162
159
  currentDeps[dep] = newVersion;
163
- } else {
164
- migrationsDebug('would downgrade dependency %s to %s', dep, newVersion);
165
160
  }
166
161
  } else if (currentDevDeps[dep]) {
167
162
  if (isVersionGreater(newVersion, currentDevDeps[dep])) {
168
163
  currentDevDeps[dep] = newVersion;
169
- } else {
170
- migrationsDebug('would downgrade devDependency %s to %s', dep, newVersion);
171
164
  }
172
165
  } else {
173
166
  // Not present, add to dependencies
@@ -180,14 +173,10 @@ export function addDependenciesToPackageJson(
180
173
  if (currentDeps[dep]) {
181
174
  if (isVersionGreater(newVersion, currentDeps[dep])) {
182
175
  currentDeps[dep] = newVersion;
183
- } else {
184
- migrationsDebug('would downgrade dependency %s to %s', dep, newVersion);
185
176
  }
186
177
  } else if (currentDevDeps[dep]) {
187
178
  if (isVersionGreater(newVersion, currentDevDeps[dep])) {
188
179
  currentDevDeps[dep] = newVersion;
189
- } else {
190
- migrationsDebug('would downgrade devDependency %s to %s', dep, newVersion);
191
180
  }
192
181
  } else {
193
182
  // Not present, add to devDependencies
@@ -214,8 +203,6 @@ export function addDependenciesToPackageJson(
214
203
  ...(Object.keys(sortedDevDeps).length > 0 && { devDependencies: sortedDevDeps }),
215
204
  };
216
205
 
217
- migrationsDebug('updated package.json', updatedPackageJson);
218
-
219
206
  context.updateFile(packageJsonPath, JSON.stringify(updatedPackageJson, null, 2));
220
207
  }
221
208
 
@@ -232,7 +219,6 @@ export function removeDependenciesFromPackageJson(
232
219
  for (const dep of dependencies) {
233
220
  if (currentPackageJson.dependencies?.[dep]) {
234
221
  delete currentPackageJson.dependencies[dep];
235
- migrationsDebug('removed dependency %s', dep);
236
222
  hasChanges = true;
237
223
  }
238
224
  }
@@ -240,7 +226,6 @@ export function removeDependenciesFromPackageJson(
240
226
  for (const dep of devDependencies) {
241
227
  if (currentPackageJson.devDependencies?.[dep]) {
242
228
  delete currentPackageJson.devDependencies[dep];
243
- migrationsDebug('removed devDependency %s', dep);
244
229
  hasChanges = true;
245
230
  }
246
231
  }
@@ -249,8 +234,6 @@ export function removeDependenciesFromPackageJson(
249
234
  return;
250
235
  }
251
236
 
252
- migrationsDebug('updated package.json', currentPackageJson);
253
-
254
237
  context.updateFile(packageJsonPath, JSON.stringify(currentPackageJson, null, 2));
255
238
  }
256
239
 
@@ -315,3 +298,6 @@ function sortObjectByKeys<T extends Record<string, any>>(obj: T): T {
315
298
  .sort()
316
299
  .reduce((acc, key) => ({ ...acc, [key]: obj[key] }), {} as T);
317
300
  }
301
+
302
+ export const migrationsDebug = debug.extend('migrations');
303
+ export const additionsDebug = debug.extend('additions');
@@ -0,0 +1,56 @@
1
+ import defaultAdditions from '../codemods/additions/additions.js';
2
+ import { runCodemod } from '../codemods/runner.js';
3
+ import { getPackageManagerExecCmd, getPackageManagerFromUserAgent } from '../utils/utils.packageManager.js';
4
+ import { performPreCodemodChecks } from '../utils/utils.checks.js';
5
+ import minimist from 'minimist';
6
+ import { output } from '../utils/utils.console.js';
7
+
8
+ export const add = async (argv: minimist.ParsedArgs) => {
9
+ const subCommand = argv._[1];
10
+
11
+ if (!subCommand) {
12
+ await showAdditionsHelp();
13
+ process.exit(1);
14
+ }
15
+
16
+ await performPreCodemodChecks(argv);
17
+
18
+ try {
19
+ const addition = defaultAdditions.find((addition) => addition.name === subCommand);
20
+ if (!addition) {
21
+ const additionsList = defaultAdditions.map((addition) => addition.name);
22
+ throw new Error(`Unknown addition: ${subCommand}\n\nAvailable additions: ${additionsList.join(', ')}`);
23
+ }
24
+
25
+ // filter out minimist internal properties (_ and $0) before passing to codemod
26
+ const { _, $0, ...codemodOptions } = argv;
27
+ await runCodemod(addition, codemodOptions);
28
+
29
+ output.success({
30
+ title: `Successfully added ${addition.name} to your plugin.`,
31
+ });
32
+ } catch (error) {
33
+ if (error instanceof Error) {
34
+ output.error({
35
+ title: 'Addition failed',
36
+ body: [error.message],
37
+ });
38
+ }
39
+ process.exit(1);
40
+ }
41
+ };
42
+
43
+ async function showAdditionsHelp() {
44
+ const additionsList = defaultAdditions.map((addition) => addition.name);
45
+ const { packageManagerName, packageManagerVersion } = getPackageManagerFromUserAgent();
46
+
47
+ output.error({
48
+ title: 'No addition specified',
49
+ body: [
50
+ `Usage: ${getPackageManagerExecCmd(packageManagerName, packageManagerVersion)} add <addition-name> [options]`,
51
+ '',
52
+ 'Available additions:',
53
+ ...output.bulletList(additionsList),
54
+ ],
55
+ });
56
+ }
@@ -3,3 +3,4 @@ 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,22 +1,21 @@
1
- import { getMigrationsToRun, runMigrations } from '../migrations/manager.js';
1
+ import { getMigrationsToRun, runMigrations } from '../codemods/migrations/manager.js';
2
2
  import {
3
3
  getPackageManagerExecCmd,
4
4
  getPackageManagerSilentInstallCmd,
5
5
  getPackageManagerWithFallback,
6
6
  } from '../utils/utils.packageManager.js';
7
7
  import { gte, lt } from 'semver';
8
- import { isGitDirectory, isGitDirectoryClean } from '../utils/utils.git.js';
8
+ import { performPreCodemodChecks } from '../utils/utils.checks.js';
9
9
 
10
10
  import { CURRENT_APP_VERSION } from '../utils/utils.version.js';
11
11
  import { LEGACY_UPDATE_CUTOFF_VERSION } from '../constants.js';
12
12
  import { getConfig } from '../utils/utils.config.js';
13
- import { isPluginDirectory } from '../utils/utils.plugin.js';
14
13
  import minimist from 'minimist';
15
14
  import { output } from '../utils/utils.console.js';
16
15
  import { spawnSync } from 'node:child_process';
17
16
 
18
17
  export const update = async (argv: minimist.ParsedArgs) => {
19
- await performPreUpdateChecks(argv);
18
+ await performPreCodemodChecks(argv);
20
19
  const { version } = getConfig();
21
20
 
22
21
  if (lt(version, LEGACY_UPDATE_CUTOFF_VERSION)) {
@@ -32,9 +31,13 @@ export const update = async (argv: minimist.ParsedArgs) => {
32
31
  process.exit(0);
33
32
  }
34
33
 
35
- const commitEachMigration = argv.commit;
36
34
  const migrations = getMigrationsToRun(version, CURRENT_APP_VERSION);
37
- await runMigrations(migrations, { commitEachMigration });
35
+ // filter out minimist internal properties (_ and $0) before passing to codemod
36
+ const { _, $0, ...codemodOptions } = argv;
37
+ await runMigrations(migrations, {
38
+ commitEachMigration: !!argv.commit,
39
+ codemodOptions,
40
+ });
38
41
  output.success({
39
42
  title: `Successfully updated create-plugin from ${version} to ${CURRENT_APP_VERSION}.`,
40
43
  });
@@ -49,48 +52,6 @@ export const update = async (argv: minimist.ParsedArgs) => {
49
52
  }
50
53
  };
51
54
 
52
- async function performPreUpdateChecks(argv: minimist.ParsedArgs) {
53
- if (!(await isGitDirectory()) && !argv.force) {
54
- output.error({
55
- title: 'You are not inside a git directory',
56
- body: [
57
- `In order to proceed please run ${output.formatCode('git init')} in the root of your project and commit your changes.`,
58
- `(This check is necessary to make sure that the updates are easy to revert and don't interfere with any changes you currently have.`,
59
- `In case you want to proceed as is please use the ${output.formatCode('--force')} flag.)`,
60
- ],
61
- });
62
-
63
- process.exit(1);
64
- }
65
-
66
- if (!(await isGitDirectoryClean()) && !argv.force) {
67
- output.error({
68
- title: 'Please clean your repository working tree before updating.',
69
- body: [
70
- 'Commit your changes or stash them.',
71
- `(This check is necessary to make sure that the updates are easy to revert and don't mess with any changes you currently have.`,
72
- `In case you want to proceed as is please use the ${output.formatCode('--force')} flag.)`,
73
- ],
74
- });
75
-
76
- process.exit(1);
77
- }
78
-
79
- if (!isPluginDirectory() && !argv.force) {
80
- output.error({
81
- title: 'Are you inside a plugin directory?',
82
- body: [
83
- `We couldn't find a "src/plugin.json" file under your current directory.`,
84
- `(Please make sure to run this command from the root of your plugin folder. In case you want to proceed as is please use the ${output.formatCode(
85
- '--force'
86
- )} flag.)`,
87
- ],
88
- });
89
-
90
- process.exit(1);
91
- }
92
- }
93
-
94
55
  /**
95
56
  * Prepares a plugin for migrations by running the legacy update command and installing dependencies.
96
57
  * This is a one time operation that ensures the plugin configs are "as expected" by the new migration system.
@@ -0,0 +1,47 @@
1
+ import minimist from 'minimist';
2
+ import { isGitDirectory, isGitDirectoryClean } from './utils.git.js';
3
+ import { isPluginDirectory } from './utils.plugin.js';
4
+ import { output } from './utils.console.js';
5
+
6
+ /**
7
+ * Ensures git directory exists, is clean, and we're in a plugin directory
8
+ */
9
+ export async function performPreCodemodChecks(argv: minimist.ParsedArgs): Promise<void> {
10
+ if (!(await isGitDirectory()) && !argv.force) {
11
+ output.error({
12
+ title: 'You are not inside a git directory',
13
+ body: [
14
+ `In order to proceed please run ${output.formatCode('git init')} in the root of your project and commit your changes.`,
15
+ `(This check is necessary to make sure that changes are easy to revert and don't interfere with any changes you currently have.`,
16
+ `In case you want to proceed as is please use the ${output.formatCode('--force')} flag.)`,
17
+ ],
18
+ });
19
+
20
+ process.exit(1);
21
+ }
22
+
23
+ if (!(await isGitDirectoryClean()) && !argv.force) {
24
+ output.error({
25
+ title: 'Please clean your repository working tree before making changes.',
26
+ body: [
27
+ 'Commit your changes or stash them.',
28
+ `(This check is necessary to make sure that changes are easy to revert and don't mess with any changes you currently have.`,
29
+ `In case you want to proceed as is please use the ${output.formatCode('--force')} flag.)`,
30
+ ],
31
+ });
32
+
33
+ process.exit(1);
34
+ }
35
+
36
+ if (!isPluginDirectory() && !argv.force) {
37
+ output.error({
38
+ title: 'Are you inside a plugin directory?',
39
+ body: [
40
+ `We couldn't find a "src/plugin.json" file under your current directory.`,
41
+ `(Please make sure to run this command from the root of your plugin folder. In case you want to proceed as is please use the ${output.formatCode('--force')} flag.)`,
42
+ ],
43
+ });
44
+
45
+ process.exit(1);
46
+ }
47
+ }
@@ -1,58 +0,0 @@
1
- import { gte, satisfies } from 'semver';
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';
9
-
10
- function getMigrationsToRun(fromVersion, toVersion, migrations = defaultMigrations.migrations) {
11
- const semverRange = `${fromVersion} - ${toVersion}`;
12
- const migrationsToRun = Object.entries(migrations).sort((a, b) => {
13
- return gte(a[1].version, b[1].version) ? 1 : -1;
14
- }).reduce((acc, [key, meta]) => {
15
- if (satisfies(meta.version, semverRange)) {
16
- acc[key] = meta;
17
- }
18
- return acc;
19
- }, {});
20
- return migrationsToRun;
21
- }
22
- async function runMigrations(migrations, options = {}) {
23
- const basePath = process.cwd();
24
- const migrationList = Object.entries(migrations).map(
25
- ([key, migrationMeta]) => `${key} (${migrationMeta.description})`
26
- );
27
- const migrationListBody = migrationList.length > 0 ? output.bulletList(migrationList) : ["No migrations to run."];
28
- output.log({ title: "Running the following migrations:", body: migrationListBody });
29
- for (const [key, migration] of Object.entries(migrations)) {
30
- try {
31
- const context = await runMigration(migration, new Context(basePath));
32
- const shouldCommit = options.commitEachMigration && context.hasChanges();
33
- migrationsDebug(`context for "${key} (${migration.migrationScript})":`);
34
- migrationsDebug("%O", context.listChanges());
35
- await formatFiles(context);
36
- flushChanges(context);
37
- printChanges(context, key, migration);
38
- installNPMDependencies(context);
39
- if (shouldCommit) {
40
- await gitCommitNoVerify(`chore: run create-plugin migration - ${key} (${migration.migrationScript})`);
41
- }
42
- } catch (error) {
43
- if (error instanceof Error) {
44
- throw new Error(`Error running migration "${key} (${migration.migrationScript})": ${error.message}`);
45
- }
46
- }
47
- }
48
- setRootConfig({ version: CURRENT_APP_VERSION });
49
- if (options.commitEachMigration) {
50
- await gitCommitNoVerify(`chore: update .config/.cprc.json to version ${CURRENT_APP_VERSION}.`);
51
- }
52
- }
53
- async function runMigration(migration, context) {
54
- const module = await import(migration.migrationScript);
55
- return module.default(context);
56
- }
57
-
58
- export { getMigrationsToRun, runMigration, runMigrations };
@@ -1,43 +0,0 @@
1
- import { LEGACY_UPDATE_CUTOFF_VERSION } from '../constants.js';
2
-
3
- var defaultMigrations = {
4
- migrations: {
5
- "001-update-grafana-compose-extend": {
6
- version: LEGACY_UPDATE_CUTOFF_VERSION,
7
- description: "Update ./docker-compose.yaml to extend from ./.config/docker-compose-base.yaml.",
8
- migrationScript: "./scripts/001-update-grafana-compose-extend.js"
9
- },
10
- "002-update-is-compatible-workflow": {
11
- version: LEGACY_UPDATE_CUTOFF_VERSION,
12
- description: "Update ./.github/workflows/is-compatible.yml to use is-compatible github action instead of calling levitate directly",
13
- migrationScript: "./scripts/002-update-is-compatible-workflow.js"
14
- },
15
- "003-update-eslint-deprecation-rule": {
16
- version: LEGACY_UPDATE_CUTOFF_VERSION,
17
- description: "Replace deprecated eslint-plugin-deprecation with @typescript-eslint/no-deprecated rule.",
18
- migrationScript: "./scripts/003-update-eslint-deprecation-rule.js"
19
- },
20
- "004-eslint9-flat-config": {
21
- version: LEGACY_UPDATE_CUTOFF_VERSION,
22
- description: "Migrate eslint config to flat config format and update devDependencies to latest versions.",
23
- migrationScript: "./scripts/004-eslint9-flat-config.js"
24
- },
25
- "005-react-18-3": {
26
- version: "6.1.9",
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"
29
- },
30
- "006-webpack-nested-fix": {
31
- version: "6.1.11",
32
- description: "Fix webpack variable replacement in nested plugins files.",
33
- migrationScript: "./scripts/006-webpack-nested-fix.js"
34
- },
35
- "007-remove-testing-library-types": {
36
- version: "6.1.13",
37
- description: "Add setupTests.d.ts for @testing-library/jest-dom types and remove @types/testing-library__jest-dom npm package.",
38
- migrationScript: "./scripts/007-remove-testing-library-types.js"
39
- }
40
- }
41
- };
42
-
43
- export { defaultMigrations as default };