@grafana/create-plugin 6.2.0-canary.2233.19133609453.0 → 6.2.0-canary.2233.19424871609.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 (54) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/CONTRIBUTING.md +3 -0
  3. package/dist/codemods/additions/additions.js +6 -8
  4. package/dist/codemods/additions/scripts/example-addition.js +19 -33
  5. package/dist/codemods/migrations/manager.js +13 -40
  6. package/dist/codemods/migrations/migrations.js +33 -25
  7. package/dist/codemods/migrations/scripts/004-eslint9-flat-config.js +1 -2
  8. package/dist/codemods/migrations/scripts/005-react-18-3.js +20 -0
  9. package/dist/codemods/migrations/scripts/example-migration.js +7 -3
  10. package/dist/codemods/runner.js +38 -0
  11. package/dist/codemods/schema-parser.js +20 -0
  12. package/dist/codemods/utils.js +7 -4
  13. package/dist/commands/add.command.js +24 -55
  14. package/dist/commands/update.command.js +7 -41
  15. package/dist/utils/utils.checks.js +40 -0
  16. package/dist/utils/utils.config.js +1 -16
  17. package/package.json +3 -2
  18. package/src/codemods/additions/additions.test.ts +12 -0
  19. package/src/codemods/additions/additions.ts +7 -21
  20. package/src/codemods/additions/scripts/example-addition.test.ts +14 -33
  21. package/src/codemods/additions/scripts/example-addition.ts +27 -44
  22. package/src/codemods/migrations/fixtures/migrations.ts +19 -18
  23. package/src/codemods/migrations/manager.test.ts +67 -73
  24. package/src/codemods/migrations/manager.ts +17 -50
  25. package/src/codemods/migrations/migrations.test.ts +10 -5
  26. package/src/codemods/migrations/migrations.ts +37 -34
  27. package/src/codemods/migrations/scripts/004-eslint9-flat-config.ts +2 -2
  28. package/src/codemods/migrations/scripts/005-react-18-3.test.ts +145 -0
  29. package/src/codemods/migrations/scripts/005-react-18-3.ts +19 -0
  30. package/src/codemods/migrations/scripts/example-migration.test.ts +1 -1
  31. package/src/codemods/migrations/scripts/example-migration.ts +20 -3
  32. package/src/codemods/runner.ts +57 -0
  33. package/src/codemods/schema-parser.ts +27 -0
  34. package/src/codemods/types.ts +9 -14
  35. package/src/codemods/{migrations/utils.test.ts → utils.test.ts} +8 -7
  36. package/src/codemods/utils.ts +13 -35
  37. package/src/commands/add.command.ts +26 -62
  38. package/src/commands/update.command.ts +8 -47
  39. package/src/migrations/migrations.ts +44 -0
  40. package/src/utils/utils.checks.ts +47 -0
  41. package/src/utils/utils.config.ts +1 -28
  42. package/templates/common/_package.json +7 -5
  43. package/templates/github/workflows/bundle-stats.yml +1 -1
  44. package/templates/github/workflows/ci.yml +11 -11
  45. package/templates/github/workflows/cp-update.yml +9 -14
  46. package/templates/github/workflows/is-compatible.yml +3 -3
  47. package/templates/github/workflows/release.yml +1 -1
  48. package/vitest.config.ts +12 -0
  49. package/dist/codemods/additions/manager.js +0 -115
  50. package/dist/codemods/additions/utils.js +0 -10
  51. package/dist/codemods/migrations/utils.js +0 -10
  52. package/src/codemods/additions/manager.ts +0 -145
  53. package/src/codemods/additions/utils.ts +0 -12
  54. package/src/codemods/migrations/utils.ts +0 -12
package/CHANGELOG.md CHANGED
@@ -1,3 +1,64 @@
1
+ # v6.1.11 (Fri Nov 14 2025)
2
+
3
+ #### 🐛 Bug Fix
4
+
5
+ - chore(deps): update create-plugin template github actions [#2301](https://github.com/grafana/plugin-tools/pull/2301) ([@renovate-sh-app[bot]](https://github.com/renovate-sh-app[bot]))
6
+ - chore(deps): update dependency @grafana/eslint-config to v9 [#2285](https://github.com/grafana/plugin-tools/pull/2285) ([@renovate-sh-app[bot]](https://github.com/renovate-sh-app[bot]) [@jackw](https://github.com/jackw))
7
+
8
+ #### Authors: 2
9
+
10
+ - [@renovate-sh-app[bot]](https://github.com/renovate-sh-app[bot])
11
+ - Jack Westbrook ([@jackw](https://github.com/jackw))
12
+
13
+ ---
14
+
15
+ # v6.1.10 (Thu Nov 13 2025)
16
+
17
+ #### 🐛 Bug Fix
18
+
19
+ - Feat: use latest create-plugin-update workflow [#2299](https://github.com/grafana/plugin-tools/pull/2299) ([@jackw](https://github.com/jackw))
20
+
21
+ #### Authors: 1
22
+
23
+ - Jack Westbrook ([@jackw](https://github.com/jackw))
24
+
25
+ ---
26
+
27
+ # v6.1.9 (Wed Nov 12 2025)
28
+
29
+ #### 🐛 Bug Fix
30
+
31
+ - Create plugin: use react 18.3.x [#2283](https://github.com/grafana/plugin-tools/pull/2283) ([@jackw](https://github.com/jackw))
32
+
33
+ #### Authors: 1
34
+
35
+ - Jack Westbrook ([@jackw](https://github.com/jackw))
36
+
37
+ ---
38
+
39
+ # v6.1.8 (Fri Nov 07 2025)
40
+
41
+ ### Release Notes
42
+
43
+ #### chore(deps): update grafana patch dependencies ([#2217](https://github.com/grafana/plugin-tools/pull/2217))
44
+
45
+ <details>
46
+ <summary>grafana/grafana (@&#8203;grafana/data)</summary>
47
+
48
+ ---
49
+
50
+ #### 🐛 Bug Fix
51
+
52
+ - Create plugin: fix permissions issue in cp-update workflow [#2281](https://github.com/grafana/plugin-tools/pull/2281) ([@jackw](https://github.com/jackw))
53
+ - 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]))
54
+
55
+ #### Authors: 2
56
+
57
+ - [@renovate-sh-app[bot]](https://github.com/renovate-sh-app[bot])
58
+ - Jack Westbrook ([@jackw](https://github.com/jackw))
59
+
60
+ ---
61
+
1
62
  # v6.1.7 (Mon Nov 03 2025)
2
63
 
3
64
  :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.
@@ -1,11 +1,9 @@
1
- const additions = {
2
- i18n: {
3
- name: "i18n",
4
- description: "Add internationalization (i18n) support to your plugin",
5
- scriptPath: "./scripts/add-i18n.js",
6
- featureName: "i18nEnabled"
1
+ var defaultAdditions = [
2
+ {
3
+ name: "example-addition",
4
+ description: "Example addition demonstrating Valibot schema with type inference",
5
+ scriptPath: import.meta.resolve("./scripts/example-addition.js")
7
6
  }
8
- };
9
- var defaultAdditions = { additions };
7
+ ];
10
8
 
11
9
  export { defaultAdditions as default };
@@ -1,25 +1,20 @@
1
+ import * as v from 'valibot';
1
2
  import { addDependenciesToPackageJson } from '../../utils.js';
2
3
 
3
- const flags = [
4
- {
5
- name: "feature-name",
6
- description: "Name of the feature to add",
7
- required: false
8
- },
9
- {
10
- name: "enabled",
11
- description: "Whether the feature should be enabled by default",
12
- required: false
13
- }
14
- ];
15
- function parseFlags(argv) {
16
- return {
17
- featureName: argv["feature-name"] || "myFeature",
18
- enabled: argv.enabled === "true" || argv.enabled === true
19
- };
20
- }
21
- function migrate(context, options = { featureName: "myFeature", enabled: true }) {
22
- const { featureName, enabled } = options;
4
+ const schema = v.object({
5
+ featureName: v.pipe(
6
+ v.string(),
7
+ v.minLength(3, "Feature name must be at least 3 characters"),
8
+ v.maxLength(50, "Feature name must be at most 50 characters")
9
+ ),
10
+ enabled: v.optional(v.boolean(), true),
11
+ port: v.optional(
12
+ v.pipe(v.number(), v.minValue(1e3, "Port must be at least 1000"), v.maxValue(65535, "Port must be at most 65535"))
13
+ ),
14
+ frameworks: v.optional(v.array(v.string()), ["react"])
15
+ });
16
+ function exampleAddition(context, options) {
17
+ const { featureName, enabled, port, frameworks } = options;
23
18
  const rawPkgJson = context.getFile("./package.json") ?? "{}";
24
19
  const packageJson = JSON.parse(rawPkgJson);
25
20
  if (packageJson.scripts && !packageJson.scripts["example-script"]) {
@@ -27,23 +22,14 @@ function migrate(context, options = { featureName: "myFeature", enabled: true })
27
22
  context.updateFile("./package.json", JSON.stringify(packageJson, null, 2));
28
23
  }
29
24
  addDependenciesToPackageJson(context, {}, { "example-dev-dep": "^1.0.0" });
30
- if (!context.doesFileExist("./src/config.json")) {
31
- const config = {
32
- features: {
33
- [featureName]: {
34
- enabled,
35
- description: "Example feature configuration"
36
- }
37
- }
38
- };
39
- context.addFile("./src/config.json", JSON.stringify(config, null, 2));
40
- }
41
25
  if (!context.doesFileExist(`./src/features/${featureName}.ts`)) {
42
26
  const featureCode = `export const ${featureName} = {
43
27
  name: '${featureName}',
44
28
  enabled: ${enabled},
29
+ port: ${port ?? 3e3},
30
+ frameworks: ${JSON.stringify(frameworks)},
45
31
  init() {
46
- console.log('${featureName} initialized');
32
+ console.log('${featureName} initialized on port ${port ?? 3e3}');
47
33
  },
48
34
  };
49
35
  `;
@@ -58,4 +44,4 @@ function migrate(context, options = { featureName: "myFeature", enabled: true })
58
44
  return context;
59
45
  }
60
46
 
61
- export { migrate as default, flags, parseFlags };
47
+ export { exampleAddition as default, schema };
@@ -1,49 +1,26 @@
1
1
  import defaultMigrations from './migrations.js';
2
- import { formatFiles, flushChanges, installNPMDependencies } from '../utils.js';
3
- import { gte, satisfies } from 'semver';
4
- import { migrationsDebug, printChanges } from './utils.js';
2
+ import { runCodemod } from '../runner.js';
3
+ import { satisfies, gte } from 'semver';
5
4
  import { CURRENT_APP_VERSION } from '../../utils/utils.version.js';
6
- import { Context } from '../context.js';
7
5
  import { gitCommitNoVerify } from '../../utils/utils.git.js';
8
6
  import { output } from '../../utils/utils.console.js';
9
7
  import { setRootConfig } from '../../utils/utils.config.js';
10
8
 
11
- function getMigrationsToRun(fromVersion, toVersion, migrations = defaultMigrations.migrations) {
9
+ function getMigrationsToRun(fromVersion, toVersion, migrations = defaultMigrations) {
12
10
  const semverRange = `${fromVersion} - ${toVersion}`;
13
- const migrationsToRun = Object.entries(migrations).sort((a, b) => {
14
- return gte(a[1].version, b[1].version) ? 1 : -1;
15
- }).reduce((acc, [key, meta]) => {
16
- if (satisfies(meta.version, semverRange)) {
17
- acc[key] = meta;
18
- }
19
- return acc;
20
- }, {});
21
- return migrationsToRun;
11
+ return migrations.filter((meta) => satisfies(meta.version, semverRange)).sort((a, b) => {
12
+ return gte(a.version, b.version) ? 1 : -1;
13
+ });
22
14
  }
23
15
  async function runMigrations(migrations, options = {}) {
24
- const basePath = process.cwd();
25
- const migrationList = Object.entries(migrations).map(
26
- ([key, migrationMeta]) => `${key} (${migrationMeta.description})`
27
- );
16
+ const migrationList = migrations.map((meta) => `${meta.name} (${meta.description})`);
28
17
  const migrationListBody = migrationList.length > 0 ? output.bulletList(migrationList) : ["No migrations to run."];
29
18
  output.log({ title: "Running the following migrations:", body: migrationListBody });
30
- for (const [key, migration] of Object.entries(migrations)) {
31
- try {
32
- const context = await runMigration(migration, new Context(basePath));
33
- const shouldCommit = options.commitEachMigration && context.hasChanges();
34
- migrationsDebug(`context for "${key} (${migration.migrationScript})":`);
35
- migrationsDebug("%O", context.listChanges());
36
- await formatFiles(context);
37
- flushChanges(context);
38
- printChanges(context, key, migration);
39
- installNPMDependencies(context);
40
- if (shouldCommit) {
41
- await gitCommitNoVerify(`chore: run create-plugin migration - ${key} (${migration.migrationScript})`);
42
- }
43
- } catch (error) {
44
- if (error instanceof Error) {
45
- throw new Error(`Error running migration "${key} (${migration.migrationScript})": ${error.message}`);
46
- }
19
+ for (const migration of migrations) {
20
+ const context = await runCodemod(migration, options.codemodOptions);
21
+ const shouldCommit = options.commitEachMigration && context.hasChanges();
22
+ if (shouldCommit) {
23
+ await gitCommitNoVerify(`chore: run create-plugin migration - ${migration.name} (${migration.scriptPath})`);
47
24
  }
48
25
  }
49
26
  setRootConfig({ version: CURRENT_APP_VERSION });
@@ -51,9 +28,5 @@ async function runMigrations(migrations, options = {}) {
51
28
  await gitCommitNoVerify(`chore: update .config/.cprc.json to version ${CURRENT_APP_VERSION}.`);
52
29
  }
53
30
  }
54
- async function runMigration(migration, context) {
55
- const module = await import(migration.migrationScript);
56
- return module.default(context);
57
- }
58
31
 
59
- export { getMigrationsToRun, runMigration, runMigrations };
32
+ export { getMigrationsToRun, runMigrations };
@@ -1,30 +1,38 @@
1
1
  import { LEGACY_UPDATE_CUTOFF_VERSION } from '../../constants.js';
2
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
- // 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.
3
+ var defaultMigrations = [
4
+ {
5
+ name: "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
+ scriptPath: import.meta.resolve("./scripts/001-update-grafana-compose-extend.js")
9
+ },
10
+ {
11
+ name: "002-update-is-compatible-workflow",
12
+ version: LEGACY_UPDATE_CUTOFF_VERSION,
13
+ description: "Update ./.github/workflows/is-compatible.yml to use is-compatible github action instead of calling levitate directly",
14
+ scriptPath: import.meta.resolve("./scripts/002-update-is-compatible-workflow.js")
15
+ },
16
+ {
17
+ name: "003-update-eslint-deprecation-rule",
18
+ version: LEGACY_UPDATE_CUTOFF_VERSION,
19
+ description: "Replace deprecated eslint-plugin-deprecation with @typescript-eslint/no-deprecated rule.",
20
+ scriptPath: import.meta.resolve("./scripts/003-update-eslint-deprecation-rule.js")
21
+ },
22
+ {
23
+ name: "004-eslint9-flat-config",
24
+ version: LEGACY_UPDATE_CUTOFF_VERSION,
25
+ description: "Migrate eslint config to flat config format and update devDependencies to latest versions.",
26
+ scriptPath: import.meta.resolve("./scripts/004-eslint9-flat-config.js")
27
+ },
28
+ {
29
+ name: "005-react-18-3",
30
+ version: "6.1.9",
31
+ description: "Update React and ReactDOM 18.x versions to ^18.3.0 to surface React 19 compatibility issues.",
32
+ scriptPath: import.meta.resolve("./scripts/005-react-18-3.js")
27
33
  }
28
- };
34
+ // Do not use LEGACY_UPDATE_CUTOFF_VERSION for new migrations. It is only used above to force migrations to run
35
+ // for those written before the switch to updates as migrations.
36
+ ];
29
37
 
30
38
  export { defaultMigrations as default };
@@ -5,7 +5,6 @@ import minimist from 'minimist';
5
5
  import { resolve, dirname, relative } from 'node:path';
6
6
  import * as recast from 'recast';
7
7
  import { addDependenciesToPackageJson } from '../../utils.js';
8
- import { migrationsDebug } from '../utils.js';
9
8
 
10
9
  const { builders } = recast.types;
11
10
  const legacyKeysToCopy = ["rules", "settings"];
@@ -318,7 +317,7 @@ function getIgnorePaths(context) {
318
317
  }
319
318
  }
320
319
  } catch (error) {
321
- migrationsDebug("Error parsing package.json: %s", error);
320
+ console.log("Error parsing package.json: %s", error);
322
321
  }
323
322
  }
324
323
  return Array.from(result);
@@ -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,15 +1,19 @@
1
- function migrate(context) {
1
+ function migrate(context, options = {}) {
2
+ const { profile = false, skipBackup = false, verbose = false } = options;
3
+ if (verbose) {
4
+ console.log("Running migration with options:", options);
5
+ }
2
6
  const rawPkgJson = context.getFile("./package.json") ?? "{}";
3
7
  const packageJson = JSON.parse(rawPkgJson);
4
8
  if (packageJson.scripts && packageJson.scripts.build) {
5
9
  const buildScript = packageJson.scripts.build;
6
10
  const pattern = /(webpack.+-c\s.+\.ts)\s(.+)/;
7
- if (pattern.test(buildScript) && !buildScript.includes("--profile")) {
11
+ if (profile && pattern.test(buildScript) && !buildScript.includes("--profile")) {
8
12
  packageJson.scripts.build = buildScript.replace(pattern, `$1 --profile $2`);
9
13
  }
10
14
  context.updateFile("./package.json", JSON.stringify(packageJson, null, 2));
11
15
  }
12
- if (context.doesFileExist("./src/README.md")) {
16
+ if (!skipBackup && context.doesFileExist("./src/README.md")) {
13
17
  context.deleteFile("./src/README.md");
14
18
  }
15
19
  if (!context.doesFileExist("./src/foo.json")) {
@@ -0,0 +1,38 @@
1
+ import { Context } from './context.js';
2
+ import { formatFiles, flushChanges, printChanges, installNPMDependencies } from './utils.js';
3
+ import { parseAndValidateOptions } from './schema-parser.js';
4
+ import { output } from '../utils/utils.console.js';
5
+
6
+ async function runCodemod(codemod, options) {
7
+ const codemodModule = await import(codemod.scriptPath);
8
+ if (!codemodModule.default || typeof codemodModule.default !== "function") {
9
+ throw new Error(`Codemod ${codemod.name} must export a default function`);
10
+ }
11
+ let codemodOptions = {};
12
+ if (options && codemodModule.schema) {
13
+ codemodOptions = parseAndValidateOptions(options, codemodModule.schema);
14
+ }
15
+ const basePath = process.cwd();
16
+ const context = new Context(basePath);
17
+ try {
18
+ output.log({
19
+ title: `Running ${codemod.name}`,
20
+ body: [codemod.description]
21
+ });
22
+ const updatedContext = await codemodModule.default(context, codemodOptions);
23
+ await formatFiles(updatedContext);
24
+ flushChanges(updatedContext);
25
+ printChanges(updatedContext, codemod.name, codemod.description);
26
+ installNPMDependencies(updatedContext);
27
+ return updatedContext;
28
+ } catch (error) {
29
+ if (error instanceof Error) {
30
+ const newError = new Error(`Error running ${codemod.name}: ${error.message}`);
31
+ newError.cause = error;
32
+ throw newError;
33
+ }
34
+ throw error;
35
+ }
36
+ }
37
+
38
+ export { runCodemod };
@@ -0,0 +1,20 @@
1
+ import * as v from 'valibot';
2
+
3
+ function parseAndValidateOptions(options, schema) {
4
+ try {
5
+ return v.parse(schema, options);
6
+ } catch (error) {
7
+ if (v.isValiError(error)) {
8
+ const formattedErrors = error.issues.map((issue) => {
9
+ const path = issue.path?.map((p) => p.key).join(".") || "";
10
+ return ` --${path}: ${issue.message}`;
11
+ }).join("\n");
12
+ throw new Error(`Invalid flag(s) provided:
13
+
14
+ ${formattedErrors}`);
15
+ }
16
+ throw error;
17
+ }
18
+ }
19
+
20
+ export { parseAndValidateOptions };
@@ -5,9 +5,9 @@ import chalk from 'chalk';
5
5
  import { output } from '../utils/utils.console.js';
6
6
  import { getPackageManagerWithFallback, getPackageManagerSilentInstallCmd } from '../utils/utils.packageManager.js';
7
7
  import { execSync } from 'node:child_process';
8
- import { gt, clean, coerce } from 'semver';
8
+ import { gte, gt, clean, coerce } from 'semver';
9
9
 
10
- function printChanges(context, key, meta) {
10
+ function printChanges(context, key, description) {
11
11
  const changes = context.listChanges();
12
12
  const lines = [];
13
13
  for (const [filePath, { changeType }] of Object.entries(changes)) {
@@ -20,7 +20,7 @@ function printChanges(context, key, meta) {
20
20
  }
21
21
  }
22
22
  output.addHorizontalLine("gray");
23
- output.logSingleLine(`${key} (${meta.description})`);
23
+ output.logSingleLine(`${key} (${description})`);
24
24
  if (lines.length === 0) {
25
25
  output.logSingleLine("No changes were made");
26
26
  } else {
@@ -178,7 +178,7 @@ const DIST_TAGS = {
178
178
  next: 1,
179
179
  latest: 0
180
180
  };
181
- function isVersionGreater(incomingVersion, existingVersion) {
181
+ function isVersionGreater(incomingVersion, existingVersion, orEqualTo = false) {
182
182
  const incomingIsDistTag = incomingVersion in DIST_TAGS;
183
183
  const existingIsDistTag = existingVersion in DIST_TAGS;
184
184
  if (incomingIsDistTag && existingIsDistTag) {
@@ -192,6 +192,9 @@ function isVersionGreater(incomingVersion, existingVersion) {
192
192
  if (!incomingSemver || !existingSemver) {
193
193
  return true;
194
194
  }
195
+ if (orEqualTo) {
196
+ return gte(incomingSemver, existingSemver);
197
+ }
195
198
  return gt(incomingSemver, existingSemver);
196
199
  }
197
200
  function cleanSemver(version) {
@@ -1,6 +1,7 @@
1
- import { runAdditionByName, getAvailableAdditions, getAdditionFlags } from '../codemods/additions/manager.js';
2
- import { isGitDirectory, isGitDirectoryClean } from '../utils/utils.git.js';
3
- import { isPluginDirectory } from '../utils/utils.plugin.js';
1
+ import defaultAdditions from '../codemods/additions/additions.js';
2
+ import { runCodemod } from '../codemods/runner.js';
3
+ import { getPackageManagerFromUserAgent, getPackageManagerExecCmd } from '../utils/utils.packageManager.js';
4
+ import { performPreCodemodChecks } from '../utils/utils.checks.js';
4
5
  import { output } from '../utils/utils.console.js';
5
6
 
6
7
  const add = async (argv) => {
@@ -9,9 +10,24 @@ const add = async (argv) => {
9
10
  await showAdditionsHelp();
10
11
  process.exit(1);
11
12
  }
12
- await performPreAddChecks(argv);
13
+ await performPreCodemodChecks(argv);
13
14
  try {
14
- await runAdditionByName(subCommand, argv);
15
+ const addition = defaultAdditions.find((addition2) => addition2.name === subCommand);
16
+ if (!addition) {
17
+ const additionsList = defaultAdditions.map((addition2) => addition2.name);
18
+ throw new Error(`Unknown addition: ${subCommand}
19
+
20
+ Available additions: ${additionsList.join(", ")}`);
21
+ }
22
+ output.log({
23
+ title: `Running addition: ${addition.name}`,
24
+ body: [addition.description]
25
+ });
26
+ const { _, $0, ...codemodOptions } = argv;
27
+ await runCodemod(addition, codemodOptions);
28
+ output.success({
29
+ title: `Successfully added ${addition.name} to your plugin.`
30
+ });
15
31
  } catch (error) {
16
32
  if (error instanceof Error) {
17
33
  output.error({
@@ -23,64 +39,17 @@ const add = async (argv) => {
23
39
  }
24
40
  };
25
41
  async function showAdditionsHelp() {
26
- const availableAdditions = getAvailableAdditions();
27
- const additionsList = await Promise.all(
28
- Object.values(availableAdditions).map(async (addition) => {
29
- let info = `${addition.name} - ${addition.description}`;
30
- const flags = await getAdditionFlags(addition);
31
- if (flags.length > 0) {
32
- const flagDocs = flags.map((flag) => {
33
- const req = flag.required ? " (required)" : " (optional)";
34
- return ` --${flag.name}: ${flag.description}${req}`;
35
- });
36
- info += "\n" + flagDocs.join("\n");
37
- }
38
- return info;
39
- })
40
- );
42
+ const additionsList = defaultAdditions.map((addition) => addition.name);
43
+ const { packageManagerName, packageManagerVersion } = getPackageManagerFromUserAgent();
41
44
  output.error({
42
45
  title: "No addition specified",
43
46
  body: [
44
- "Usage: npx @grafana/create-plugin add <addition-name> [options]",
47
+ `Usage: ${getPackageManagerExecCmd(packageManagerName, packageManagerVersion)} add <addition-name> [options]`,
45
48
  "",
46
49
  "Available additions:",
47
50
  ...output.bulletList(additionsList)
48
51
  ]
49
52
  });
50
53
  }
51
- async function performPreAddChecks(argv) {
52
- if (!await isGitDirectory() && !argv.force) {
53
- output.error({
54
- title: "You are not inside a git directory",
55
- body: [
56
- `In order to proceed please run ${output.formatCode("git init")} in the root of your project and commit your changes.`,
57
- `(This check is necessary to make sure that the changes are easy to revert and don't interfere with any changes you currently have.`,
58
- `In case you want to proceed as is please use the ${output.formatCode("--force")} flag.)`
59
- ]
60
- });
61
- process.exit(1);
62
- }
63
- if (!await isGitDirectoryClean() && !argv.force) {
64
- output.error({
65
- title: "Please clean your repository working tree before adding features.",
66
- body: [
67
- "Commit your changes or stash them.",
68
- `(This check is necessary to make sure that the changes are easy to revert and don't mess with any changes you currently have.`,
69
- `In case you want to proceed as is please use the ${output.formatCode("--force")} flag.)`
70
- ]
71
- });
72
- process.exit(1);
73
- }
74
- if (!isPluginDirectory() && !argv.force) {
75
- output.error({
76
- title: "Are you inside a plugin directory?",
77
- body: [
78
- `We couldn't find a "src/plugin.json" file under your current directory.`,
79
- `(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.)`
80
- ]
81
- });
82
- process.exit(1);
83
- }
84
- }
85
54
 
86
55
  export { add };
@@ -1,16 +1,15 @@
1
1
  import { getMigrationsToRun, runMigrations } from '../codemods/migrations/manager.js';
2
2
  import { getPackageManagerWithFallback, getPackageManagerExecCmd, getPackageManagerSilentInstallCmd } from '../utils/utils.packageManager.js';
3
3
  import { lt, gte } from 'semver';
4
- import { isGitDirectory, isGitDirectoryClean } from '../utils/utils.git.js';
4
+ import { performPreCodemodChecks } from '../utils/utils.checks.js';
5
5
  import { CURRENT_APP_VERSION } from '../utils/utils.version.js';
6
6
  import { LEGACY_UPDATE_CUTOFF_VERSION } from '../constants.js';
7
7
  import { getConfig } from '../utils/utils.config.js';
8
- import { isPluginDirectory } from '../utils/utils.plugin.js';
9
8
  import { output } from '../utils/utils.console.js';
10
9
  import { spawnSync } from 'node:child_process';
11
10
 
12
11
  const update = async (argv) => {
13
- await performPreUpdateChecks(argv);
12
+ await performPreCodemodChecks(argv);
14
13
  const { version } = getConfig();
15
14
  if (lt(version, LEGACY_UPDATE_CUTOFF_VERSION)) {
16
15
  preparePluginForMigrations(argv);
@@ -22,9 +21,12 @@ const update = async (argv) => {
22
21
  });
23
22
  process.exit(0);
24
23
  }
25
- const commitEachMigration = argv.commit;
26
24
  const migrations = getMigrationsToRun(version, CURRENT_APP_VERSION);
27
- await runMigrations(migrations, { commitEachMigration });
25
+ const { _, $0, ...codemodOptions } = argv;
26
+ await runMigrations(migrations, {
27
+ commitEachMigration: !!argv.commit,
28
+ codemodOptions
29
+ });
28
30
  output.success({
29
31
  title: `Successfully updated create-plugin from ${version} to ${CURRENT_APP_VERSION}.`
30
32
  });
@@ -38,42 +40,6 @@ const update = async (argv) => {
38
40
  process.exit(1);
39
41
  }
40
42
  };
41
- async function performPreUpdateChecks(argv) {
42
- if (!await isGitDirectory() && !argv.force) {
43
- output.error({
44
- title: "You are not inside a git directory",
45
- body: [
46
- `In order to proceed please run ${output.formatCode("git init")} in the root of your project and commit your changes.`,
47
- `(This check is necessary to make sure that the updates are easy to revert and don't interfere with any changes you currently have.`,
48
- `In case you want to proceed as is please use the ${output.formatCode("--force")} flag.)`
49
- ]
50
- });
51
- process.exit(1);
52
- }
53
- if (!await isGitDirectoryClean() && !argv.force) {
54
- output.error({
55
- title: "Please clean your repository working tree before updating.",
56
- body: [
57
- "Commit your changes or stash them.",
58
- `(This check is necessary to make sure that the updates are easy to revert and don't mess 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
- process.exit(1);
63
- }
64
- if (!isPluginDirectory() && !argv.force) {
65
- output.error({
66
- title: "Are you inside a plugin directory?",
67
- body: [
68
- `We couldn't find a "src/plugin.json" file under your current directory.`,
69
- `(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(
70
- "--force"
71
- )} flag.)`
72
- ]
73
- });
74
- process.exit(1);
75
- }
76
- }
77
43
  function preparePluginForMigrations(argv) {
78
44
  const { packageManagerName, packageManagerVersion } = getPackageManagerWithFallback();
79
45
  const packageManagerExecCmd = getPackageManagerExecCmd(packageManagerName, packageManagerVersion);