@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
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # v6.2.0 (Thu Nov 20 2025)
2
+
3
+ #### 🚀 Enhancement
4
+
5
+ - Create Plugin: Support new add cmd [#2233](https://github.com/grafana/plugin-tools/pull/2233) ([@sunker](https://github.com/sunker))
6
+
7
+ #### Authors: 1
8
+
9
+ - Erik Sundell ([@sunker](https://github.com/sunker))
10
+
11
+ ---
12
+
1
13
  # v6.1.14 (Wed Nov 19 2025)
2
14
 
3
15
  #### 🐛 Bug Fix
package/dist/bin/run.js CHANGED
@@ -4,6 +4,7 @@ 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';
7
8
  import { isUnsupportedPlatform } from '../utils/utils.os.js';
8
9
  import { argv, commandName } from '../utils/utils.cli.js';
9
10
  import { output } from '../utils/utils.console.js';
@@ -21,7 +22,8 @@ const commands = {
21
22
  generate,
22
23
  update,
23
24
  version,
24
- provisioning
25
+ provisioning,
26
+ add
25
27
  };
26
28
  const command = commands[commandName] || "generate";
27
29
  command(argv);
@@ -0,0 +1,9 @@
1
+ var defaultAdditions = [
2
+ {
3
+ name: "example-addition",
4
+ description: "Adds an example addition to the plugin",
5
+ scriptPath: import.meta.resolve("./scripts/example-addition.js")
6
+ }
7
+ ];
8
+
9
+ export { defaultAdditions as default };
@@ -0,0 +1,47 @@
1
+ import * as v from 'valibot';
2
+ import { addDependenciesToPackageJson } from '../../utils.js';
3
+
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;
18
+ const rawPkgJson = context.getFile("./package.json") ?? "{}";
19
+ const packageJson = JSON.parse(rawPkgJson);
20
+ if (packageJson.scripts && !packageJson.scripts["example-script"]) {
21
+ packageJson.scripts["example-script"] = `echo "Running ${featureName}"`;
22
+ context.updateFile("./package.json", JSON.stringify(packageJson, null, 2));
23
+ }
24
+ addDependenciesToPackageJson(context, {}, { "@types/node": "^20.0.0" });
25
+ if (!context.doesFileExist(`./src/features/${featureName}.ts`)) {
26
+ const featureCode = `export const ${featureName} = {
27
+ name: '${featureName}',
28
+ enabled: ${enabled},
29
+ port: ${port ?? 3e3},
30
+ frameworks: ${JSON.stringify(frameworks)},
31
+ init() {
32
+ console.log('${featureName} initialized on port ${port ?? 3e3}');
33
+ },
34
+ };
35
+ `;
36
+ context.addFile(`./src/features/${featureName}.ts`, featureCode);
37
+ }
38
+ if (context.doesFileExist("./src/deprecated.ts")) {
39
+ context.deleteFile("./src/deprecated.ts");
40
+ }
41
+ if (context.doesFileExist("./src/old-config.json")) {
42
+ context.renameFile("./src/old-config.json", "./src/new-config.json");
43
+ }
44
+ return context;
45
+ }
46
+
47
+ export { exampleAddition as default, schema };
@@ -1,10 +1,11 @@
1
1
  import { accessSync, constants, readFileSync, readdirSync } from 'node:fs';
2
2
  import { join, dirname, normalize, relative } from 'node:path';
3
- import { migrationsDebug } from './utils.js';
3
+ import { debug } from '../utils/utils.cli.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");
8
9
  class Context {
9
10
  constructor(basePath) {
10
11
  __publicField(this, "files", {});
@@ -41,7 +42,7 @@ class Context {
41
42
  if (originalContent !== content) {
42
43
  this.files[path] = { content, changeType: "update" };
43
44
  } else {
44
- migrationsDebug(`Context.updateFile() - no updates for ${filePath}`);
45
+ codemodsDebug(`Context.updateFile() - no updates for ${filePath}`);
45
46
  }
46
47
  }
47
48
  doesFileExist(filePath) {
@@ -0,0 +1,32 @@
1
+ import defaultMigrations from './migrations.js';
2
+ import { runCodemod } from '../runner.js';
3
+ import { satisfies, gte } from 'semver';
4
+ import { CURRENT_APP_VERSION } from '../../utils/utils.version.js';
5
+ import { gitCommitNoVerify } from '../../utils/utils.git.js';
6
+ import { output } from '../../utils/utils.console.js';
7
+ import { setRootConfig } from '../../utils/utils.config.js';
8
+
9
+ function getMigrationsToRun(fromVersion, toVersion, migrations = defaultMigrations) {
10
+ const semverRange = `${fromVersion} - ${toVersion}`;
11
+ return migrations.filter((meta) => satisfies(meta.version, semverRange)).sort((a, b) => {
12
+ return gte(a.version, b.version) ? 1 : -1;
13
+ });
14
+ }
15
+ async function runMigrations(migrations, options = {}) {
16
+ const migrationList = migrations.map((meta) => `${meta.name} (${meta.description})`);
17
+ const migrationListBody = migrationList.length > 0 ? output.bulletList(migrationList) : ["No migrations to run."];
18
+ output.log({ title: "Running the following migrations:", body: migrationListBody });
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})`);
24
+ }
25
+ }
26
+ setRootConfig({ version: CURRENT_APP_VERSION });
27
+ if (options.commitEachMigration) {
28
+ await gitCommitNoVerify(`chore: update .config/.cprc.json to version ${CURRENT_APP_VERSION}.`);
29
+ }
30
+ }
31
+
32
+ export { getMigrationsToRun, runMigrations };
@@ -0,0 +1,50 @@
1
+ import { LEGACY_UPDATE_CUTOFF_VERSION } from '../../constants.js';
2
+
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")
33
+ },
34
+ {
35
+ name: "006-webpack-nested-fix",
36
+ version: "6.1.11",
37
+ description: "Fix webpack variable replacement in nested plugins files.",
38
+ scriptPath: import.meta.resolve("./scripts/006-webpack-nested-fix.js")
39
+ },
40
+ {
41
+ name: "007-remove-testing-library-types",
42
+ version: "6.1.13",
43
+ description: "Add setupTests.d.ts for @testing-library/jest-dom types and remove @types/testing-library__jest-dom npm package.",
44
+ scriptPath: import.meta.resolve("./scripts/007-remove-testing-library-types.js")
45
+ }
46
+ // Do not use LEGACY_UPDATE_CUTOFF_VERSION for new migrations. It is only used above to force migrations to run
47
+ // for those written before the switch to updates as migrations.
48
+ ];
49
+
50
+ export { defaultMigrations as default };
@@ -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,7 +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, migrationsDebug } from '../utils.js';
7
+ import { addDependenciesToPackageJson, migrationsDebug } from '../../utils.js';
8
8
 
9
9
  const { builders } = recast.types;
10
10
  const legacyKeysToCopy = ["rules", "settings"];
@@ -1,4 +1,4 @@
1
- import { isVersionGreater, addDependenciesToPackageJson } from '../utils.js';
1
+ import { isVersionGreater, addDependenciesToPackageJson } from '../../utils.js';
2
2
 
3
3
  function migrate(context) {
4
4
  if (context.doesFileExist("package.json")) {
@@ -1,4 +1,4 @@
1
- import { isVersionGreater, removeDependenciesFromPackageJson } from '../utils.js';
1
+ import { isVersionGreater, removeDependenciesFromPackageJson } from '../../utils.js';
2
2
 
3
3
  function migrate(context) {
4
4
  if (context.doesFileExist("package.json")) {
@@ -0,0 +1,33 @@
1
+ import { Context } from './context.js';
2
+ import { formatFiles, flushChanges, printChanges, installNPMDependencies } from './utils.js';
3
+ import { parseAndValidateOptions } from './schema-parser.js';
4
+
5
+ async function runCodemod(codemod, options) {
6
+ const codemodModule = await import(codemod.scriptPath);
7
+ if (!codemodModule.default || typeof codemodModule.default !== "function") {
8
+ throw new Error(`Codemod ${codemod.name} must export a default function`);
9
+ }
10
+ let codemodOptions = {};
11
+ if (options && codemodModule.schema) {
12
+ codemodOptions = parseAndValidateOptions(options, codemodModule.schema);
13
+ }
14
+ const basePath = process.cwd();
15
+ const context = new Context(basePath);
16
+ try {
17
+ const updatedContext = await codemodModule.default(context, codemodOptions);
18
+ await formatFiles(updatedContext);
19
+ flushChanges(updatedContext);
20
+ printChanges(updatedContext, codemod.name, codemod.description);
21
+ installNPMDependencies(updatedContext);
22
+ return updatedContext;
23
+ } catch (error) {
24
+ if (error instanceof Error) {
25
+ const newError = new Error(`Error running ${codemod.name}: ${error.message}`);
26
+ newError.cause = error;
27
+ throw newError;
28
+ }
29
+ throw error;
30
+ }
31
+ }
32
+
33
+ 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 };
@@ -1,14 +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';
5
4
  import chalk from 'chalk';
6
5
  import { output } from '../utils/utils.console.js';
7
6
  import { getPackageManagerWithFallback, getPackageManagerSilentInstallCmd } from '../utils/utils.packageManager.js';
8
7
  import { execSync } from 'node:child_process';
9
8
  import { gte, gt, clean, coerce } from 'semver';
9
+ import { debug } from '../utils/utils.cli.js';
10
10
 
11
- function printChanges(context, key, migration) {
11
+ function printChanges(context, key, description) {
12
12
  const changes = context.listChanges();
13
13
  const lines = [];
14
14
  for (const [filePath, { changeType }] of Object.entries(changes)) {
@@ -21,7 +21,7 @@ function printChanges(context, key, migration) {
21
21
  }
22
22
  }
23
23
  output.addHorizontalLine("gray");
24
- output.logSingleLine(`${key} (${migration.description})`);
24
+ output.logSingleLine(`${key} (${description})`);
25
25
  if (lines.length === 0) {
26
26
  output.logSingleLine("No changes were made");
27
27
  } else {
@@ -43,7 +43,6 @@ function flushChanges(context) {
43
43
  }
44
44
  }
45
45
  }
46
- const migrationsDebug = debug.extend("migrations");
47
46
  async function formatFiles(context) {
48
47
  let prettier;
49
48
  const require = createRequire(import.meta.url);
@@ -120,14 +119,10 @@ function addDependenciesToPackageJson(context, dependencies, devDependencies = {
120
119
  if (currentDeps[dep]) {
121
120
  if (isVersionGreater(newVersion, currentDeps[dep])) {
122
121
  currentDeps[dep] = newVersion;
123
- } else {
124
- migrationsDebug("would downgrade dependency %s to %s", dep, newVersion);
125
122
  }
126
123
  } else if (currentDevDeps[dep]) {
127
124
  if (isVersionGreater(newVersion, currentDevDeps[dep])) {
128
125
  currentDevDeps[dep] = newVersion;
129
- } else {
130
- migrationsDebug("would downgrade devDependency %s to %s", dep, newVersion);
131
126
  }
132
127
  } else {
133
128
  currentDeps[dep] = newVersion;
@@ -137,14 +132,10 @@ function addDependenciesToPackageJson(context, dependencies, devDependencies = {
137
132
  if (currentDeps[dep]) {
138
133
  if (isVersionGreater(newVersion, currentDeps[dep])) {
139
134
  currentDeps[dep] = newVersion;
140
- } else {
141
- migrationsDebug("would downgrade dependency %s to %s", dep, newVersion);
142
135
  }
143
136
  } else if (currentDevDeps[dep]) {
144
137
  if (isVersionGreater(newVersion, currentDevDeps[dep])) {
145
138
  currentDevDeps[dep] = newVersion;
146
- } else {
147
- migrationsDebug("would downgrade devDependency %s to %s", dep, newVersion);
148
139
  }
149
140
  } else {
150
141
  currentDevDeps[dep] = newVersion;
@@ -161,7 +152,6 @@ function addDependenciesToPackageJson(context, dependencies, devDependencies = {
161
152
  ...Object.keys(sortedDeps).length > 0 && { dependencies: sortedDeps },
162
153
  ...Object.keys(sortedDevDeps).length > 0 && { devDependencies: sortedDevDeps }
163
154
  };
164
- migrationsDebug("updated package.json", updatedPackageJson);
165
155
  context.updateFile(packageJsonPath, JSON.stringify(updatedPackageJson, null, 2));
166
156
  }
167
157
  function removeDependenciesFromPackageJson(context, dependencies, devDependencies = [], packageJsonPath = "package.json") {
@@ -170,21 +160,18 @@ function removeDependenciesFromPackageJson(context, dependencies, devDependencie
170
160
  for (const dep of dependencies) {
171
161
  if (currentPackageJson.dependencies?.[dep]) {
172
162
  delete currentPackageJson.dependencies[dep];
173
- migrationsDebug("removed dependency %s", dep);
174
163
  hasChanges = true;
175
164
  }
176
165
  }
177
166
  for (const dep of devDependencies) {
178
167
  if (currentPackageJson.devDependencies?.[dep]) {
179
168
  delete currentPackageJson.devDependencies[dep];
180
- migrationsDebug("removed devDependency %s", dep);
181
169
  hasChanges = true;
182
170
  }
183
171
  }
184
172
  if (!hasChanges) {
185
173
  return;
186
174
  }
187
- migrationsDebug("updated package.json", currentPackageJson);
188
175
  context.updateFile(packageJsonPath, JSON.stringify(currentPackageJson, null, 2));
189
176
  }
190
177
  const DIST_TAGS = {
@@ -217,5 +204,7 @@ function cleanSemver(version) {
217
204
  function sortObjectByKeys(obj) {
218
205
  return Object.keys(obj).sort().reduce((acc, key) => ({ ...acc, [key]: obj[key] }), {});
219
206
  }
207
+ const migrationsDebug = debug.extend("migrations");
208
+ debug.extend("additions");
220
209
 
221
210
  export { addDependenciesToPackageJson, flushChanges, formatFiles, installNPMDependencies, isVersionGreater, migrationsDebug, printChanges, readJsonFile, removeDependenciesFromPackageJson };
@@ -0,0 +1,51 @@
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';
5
+ import { output } from '../utils/utils.console.js';
6
+
7
+ const add = async (argv) => {
8
+ const subCommand = argv._[1];
9
+ if (!subCommand) {
10
+ await showAdditionsHelp();
11
+ process.exit(1);
12
+ }
13
+ await performPreCodemodChecks(argv);
14
+ try {
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
+ const { _, $0, ...codemodOptions } = argv;
23
+ await runCodemod(addition, codemodOptions);
24
+ output.success({
25
+ title: `Successfully added ${addition.name} to your plugin.`
26
+ });
27
+ } catch (error) {
28
+ if (error instanceof Error) {
29
+ output.error({
30
+ title: "Addition failed",
31
+ body: [error.message]
32
+ });
33
+ }
34
+ process.exit(1);
35
+ }
36
+ };
37
+ async function showAdditionsHelp() {
38
+ const additionsList = defaultAdditions.map((addition) => addition.name);
39
+ const { packageManagerName, packageManagerVersion } = getPackageManagerFromUserAgent();
40
+ output.error({
41
+ title: "No addition specified",
42
+ body: [
43
+ `Usage: ${getPackageManagerExecCmd(packageManagerName, packageManagerVersion)} add <addition-name> [options]`,
44
+ "",
45
+ "Available additions:",
46
+ ...output.bulletList(additionsList)
47
+ ]
48
+ });
49
+ }
50
+
51
+ export { add };
@@ -1,16 +1,15 @@
1
- import { getMigrationsToRun, runMigrations } from '../migrations/manager.js';
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);
@@ -0,0 +1,40 @@
1
+ import { isGitDirectory, isGitDirectoryClean } from './utils.git.js';
2
+ import { isPluginDirectory } from './utils.plugin.js';
3
+ import { output } from './utils.console.js';
4
+
5
+ async function performPreCodemodChecks(argv) {
6
+ if (!await isGitDirectory() && !argv.force) {
7
+ output.error({
8
+ title: "You are not inside a git directory",
9
+ body: [
10
+ `In order to proceed please run ${output.formatCode("git init")} in the root of your project and commit your changes.`,
11
+ `(This check is necessary to make sure that changes are easy to revert and don't interfere with any changes you currently have.`,
12
+ `In case you want to proceed as is please use the ${output.formatCode("--force")} flag.)`
13
+ ]
14
+ });
15
+ process.exit(1);
16
+ }
17
+ if (!await isGitDirectoryClean() && !argv.force) {
18
+ output.error({
19
+ title: "Please clean your repository working tree before making changes.",
20
+ body: [
21
+ "Commit your changes or stash them.",
22
+ `(This check is necessary to make sure that changes are easy to revert and don't mess with any changes you currently have.`,
23
+ `In case you want to proceed as is please use the ${output.formatCode("--force")} flag.)`
24
+ ]
25
+ });
26
+ process.exit(1);
27
+ }
28
+ if (!isPluginDirectory() && !argv.force) {
29
+ output.error({
30
+ title: "Are you inside a plugin directory?",
31
+ body: [
32
+ `We couldn't find a "src/plugin.json" file under your current directory.`,
33
+ `(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.)`
34
+ ]
35
+ });
36
+ process.exit(1);
37
+ }
38
+ }
39
+
40
+ export { performPreCodemodChecks };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grafana/create-plugin",
3
- "version": "6.2.0-canary.2314.19505018775.0",
3
+ "version": "6.2.0",
4
4
  "repository": {
5
5
  "directory": "packages/create-plugin",
6
6
  "url": "https://github.com/grafana/plugin-tools"
@@ -39,6 +39,7 @@
39
39
  "recast": "^0.23.11",
40
40
  "semver": "^7.3.5",
41
41
  "title-case": "^4.3.0",
42
+ "valibot": "^1.1.0",
42
43
  "which": "^5.0.0",
43
44
  "yaml": "^2.7.0"
44
45
  },
@@ -60,5 +61,5 @@
60
61
  "engines": {
61
62
  "node": ">=20"
62
63
  },
63
- "gitHead": "68ffe571c164963b0c82c2d1788c307914495307"
64
+ "gitHead": "0ce6b92fb308b79f5c201eee6456213910f53b77"
64
65
  }
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 { generate, update, migrate, version, provisioning } from '../commands/index.js';
4
+ import { add, 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,6 +23,7 @@ const commands: Record<string, (argv: minimist.ParsedArgs) => void> = {
23
23
  update,
24
24
  version,
25
25
  provisioning,
26
+ add,
26
27
  };
27
28
  const command = commands[commandName] || 'generate';
28
29
 
@@ -0,0 +1,17 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import defaultAdditions from './additions.js';
4
+
5
+ describe('additions json', () => {
6
+ // As addition scripts are imported dynamically when add is run we assert the path is valid
7
+ // Vitest 4 reimplemented its workers, which caused the previous dynamic import tests to fail.
8
+ // This test now only asserts that the addition script source file exists.
9
+ defaultAdditions.forEach((addition) => {
10
+ it(`should have a valid addition script path for ${addition.name}`, () => {
11
+ // import.meta.resolve() returns a file:// URL, convert to path
12
+ const filePath = fileURLToPath(addition.scriptPath);
13
+ const sourceFilePath = filePath.replace('.js', '.ts');
14
+ expect(existsSync(sourceFilePath)).toBe(true);
15
+ });
16
+ });
17
+ });
@@ -0,0 +1,9 @@
1
+ import { Codemod } from '../types.js';
2
+
3
+ export default [
4
+ {
5
+ name: 'example-addition',
6
+ description: 'Adds an example addition to the plugin',
7
+ scriptPath: import.meta.resolve('./scripts/example-addition.js'),
8
+ },
9
+ ] satisfies Codemod[];