@grafana/create-plugin 6.2.0-canary.2283.19289604632.0 → 6.2.0-canary.2314.19503588692.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -0
- package/dist/migrations/migrations.js +10 -0
- package/dist/migrations/scripts/006-webpack-nested-fix.js +80 -0
- package/dist/migrations/scripts/007-remove-testing-library-types.js +25 -0
- package/package.json +3 -2
- package/src/migrations/migrations.ts +11 -0
- package/src/migrations/scripts/006-webpack-nested-fix.test.ts +169 -0
- package/src/migrations/scripts/006-webpack-nested-fix.ts +117 -0
- package/src/migrations/scripts/007-remove-testing-library-types.test.ts +137 -0
- package/src/migrations/scripts/007-remove-testing-library-types.ts +25 -0
- package/templates/backend/go.mod +67 -47
- package/templates/backend/go.sum +197 -222
- package/templates/backend-app/go.mod +67 -48
- package/templates/backend-app/go.sum +197 -222
- package/templates/common/.config/types/setupTests.d.ts +1 -0
- package/templates/common/.config/webpack/webpack.config.ts +1 -1
- package/templates/common/_package.json +2 -3
- package/templates/github/workflows/bundle-stats.yml +1 -1
- package/templates/github/workflows/ci.yml +11 -11
- package/templates/github/workflows/cp-update.yml +1 -1
- package/templates/github/workflows/is-compatible.yml +3 -3
- package/templates/github/workflows/release.yml +1 -1
- package/templates/panel/.config/AGENTS/fundamentals.md +66 -0
- package/templates/panel/AGENTS.md +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,77 @@
|
|
|
1
|
+
# v6.1.14 (Wed Nov 19 2025)
|
|
2
|
+
|
|
3
|
+
#### 🐛 Bug Fix
|
|
4
|
+
|
|
5
|
+
- Create plugin: Update go templates [#2313](https://github.com/grafana/plugin-tools/pull/2313) ([@jackw](https://github.com/jackw))
|
|
6
|
+
|
|
7
|
+
#### Authors: 1
|
|
8
|
+
|
|
9
|
+
- Jack Westbrook ([@jackw](https://github.com/jackw))
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# v6.1.13 (Wed Nov 19 2025)
|
|
14
|
+
|
|
15
|
+
#### 🐛 Bug Fix
|
|
16
|
+
|
|
17
|
+
- Create Plugin: remove types/testing-library__jest-dom [#2250](https://github.com/grafana/plugin-tools/pull/2250) ([@jackw](https://github.com/jackw))
|
|
18
|
+
|
|
19
|
+
#### Authors: 1
|
|
20
|
+
|
|
21
|
+
- Jack Westbrook ([@jackw](https://github.com/jackw))
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# v6.1.12 (Mon Nov 17 2025)
|
|
26
|
+
|
|
27
|
+
#### 🐛 Bug Fix
|
|
28
|
+
|
|
29
|
+
- Create Plugin: fix replacing version and plugin id in nested plugins [#2287](https://github.com/grafana/plugin-tools/pull/2287) ([@jackw](https://github.com/jackw))
|
|
30
|
+
|
|
31
|
+
#### Authors: 1
|
|
32
|
+
|
|
33
|
+
- Jack Westbrook ([@jackw](https://github.com/jackw))
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
# v6.1.11 (Fri Nov 14 2025)
|
|
38
|
+
|
|
39
|
+
#### 🐛 Bug Fix
|
|
40
|
+
|
|
41
|
+
- 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]))
|
|
42
|
+
- 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))
|
|
43
|
+
|
|
44
|
+
#### Authors: 2
|
|
45
|
+
|
|
46
|
+
- [@renovate-sh-app[bot]](https://github.com/renovate-sh-app[bot])
|
|
47
|
+
- Jack Westbrook ([@jackw](https://github.com/jackw))
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
# v6.1.10 (Thu Nov 13 2025)
|
|
52
|
+
|
|
53
|
+
#### 🐛 Bug Fix
|
|
54
|
+
|
|
55
|
+
- Feat: use latest create-plugin-update workflow [#2299](https://github.com/grafana/plugin-tools/pull/2299) ([@jackw](https://github.com/jackw))
|
|
56
|
+
|
|
57
|
+
#### Authors: 1
|
|
58
|
+
|
|
59
|
+
- Jack Westbrook ([@jackw](https://github.com/jackw))
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
# v6.1.9 (Wed Nov 12 2025)
|
|
64
|
+
|
|
65
|
+
#### 🐛 Bug Fix
|
|
66
|
+
|
|
67
|
+
- Create plugin: use react 18.3.x [#2283](https://github.com/grafana/plugin-tools/pull/2283) ([@jackw](https://github.com/jackw))
|
|
68
|
+
|
|
69
|
+
#### Authors: 1
|
|
70
|
+
|
|
71
|
+
- Jack Westbrook ([@jackw](https://github.com/jackw))
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
1
75
|
# v6.1.8 (Fri Nov 07 2025)
|
|
2
76
|
|
|
3
77
|
### Release Notes
|
|
@@ -26,6 +26,16 @@ var defaultMigrations = {
|
|
|
26
26
|
version: "6.1.9",
|
|
27
27
|
description: "Update React and ReactDOM 18.x versions to ^18.3.0 to surface React 19 compatibility issues.",
|
|
28
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"
|
|
29
39
|
}
|
|
30
40
|
}
|
|
31
41
|
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import * as recast from 'recast';
|
|
3
|
+
import * as typeScriptParser from 'recast/parsers/typescript.js';
|
|
4
|
+
|
|
5
|
+
const { builders } = recast.types;
|
|
6
|
+
function migrate(context) {
|
|
7
|
+
const webpackConfigPath = join(".config", "webpack", "webpack.config.ts");
|
|
8
|
+
if (!context.doesFileExist(webpackConfigPath)) {
|
|
9
|
+
return context;
|
|
10
|
+
}
|
|
11
|
+
const webpackConfigContent = context.getFile(webpackConfigPath);
|
|
12
|
+
if (!webpackConfigContent) {
|
|
13
|
+
return context;
|
|
14
|
+
}
|
|
15
|
+
let hasChanges = false;
|
|
16
|
+
const ast = recast.parse(webpackConfigContent, {
|
|
17
|
+
parser: typeScriptParser
|
|
18
|
+
});
|
|
19
|
+
recast.visit(ast, {
|
|
20
|
+
visitNewExpression(path) {
|
|
21
|
+
const { node } = path;
|
|
22
|
+
if (node.callee.type === "Identifier" && node.callee.name === "ReplaceInFileWebpackPlugin" && node.arguments.length > 0) {
|
|
23
|
+
const firstArg = node.arguments[0];
|
|
24
|
+
if (firstArg.type === "ArrayExpression" && firstArg.elements) {
|
|
25
|
+
firstArg.elements.forEach((element) => {
|
|
26
|
+
if (element && element.type === "ObjectExpression") {
|
|
27
|
+
const changed = transformFilesProperty(element);
|
|
28
|
+
if (changed) {
|
|
29
|
+
hasChanges = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return this.traverse(path);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
if (hasChanges) {
|
|
39
|
+
const output = recast.print(ast, {
|
|
40
|
+
tabWidth: 2,
|
|
41
|
+
trailingComma: true,
|
|
42
|
+
lineTerminator: "\n"
|
|
43
|
+
});
|
|
44
|
+
context.updateFile(webpackConfigPath, output.code);
|
|
45
|
+
}
|
|
46
|
+
return context;
|
|
47
|
+
}
|
|
48
|
+
function transformFilesProperty(objectExpression) {
|
|
49
|
+
const properties = objectExpression.properties;
|
|
50
|
+
if (!properties) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const filesPropertyIndex = properties.findIndex(
|
|
54
|
+
(prop) => (prop.type === "Property" || prop.type === "ObjectProperty") && prop.key.type === "Identifier" && prop.key.name === "files"
|
|
55
|
+
);
|
|
56
|
+
if (filesPropertyIndex === -1) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
const filesProperty = properties[filesPropertyIndex];
|
|
60
|
+
if (filesProperty.type !== "Property" && filesProperty.type !== "ObjectProperty" || !("value" in filesProperty)) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (filesProperty.value.type === "ArrayExpression" && filesProperty.value.elements.length === 2) {
|
|
64
|
+
const elements = filesProperty.value.elements;
|
|
65
|
+
const values = elements.map((el) => el?.type === "Literal" || el?.type === "StringLiteral" ? el?.value : null).filter((v) => v !== null);
|
|
66
|
+
if (values.length === 2 && values.includes("plugin.json") && values.includes("README.md")) {
|
|
67
|
+
properties.splice(filesPropertyIndex, 1);
|
|
68
|
+
const testProperty = builders.property(
|
|
69
|
+
"init",
|
|
70
|
+
builders.identifier("test"),
|
|
71
|
+
builders.arrayExpression([builders.literal(/(^|\/)plugin\.json$/), builders.literal(/(^|\/)README\.md$/)])
|
|
72
|
+
);
|
|
73
|
+
properties.splice(filesPropertyIndex, 0, testProperty);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { migrate as default };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { isVersionGreater, removeDependenciesFromPackageJson } 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 (isVersionGreater(packageJson.devDependencies["@testing-library/jest-dom"], "6.0.0", true)) {
|
|
7
|
+
if (context.doesFileExist("./.config/types/setupTests.d.ts")) {
|
|
8
|
+
const setupTestsContent = context.getFile("./.config/types/setupTests.d.ts");
|
|
9
|
+
if (!setupTestsContent?.includes("@testing-library/jest-dom")) {
|
|
10
|
+
context.updateFile(
|
|
11
|
+
"./.config/types/setupTests.d.ts",
|
|
12
|
+
`import '@testing-library/jest-dom';
|
|
13
|
+
${setupTestsContent}`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
} else {
|
|
17
|
+
context.addFile("./.config/types/setupTests.d.ts", "import '@testing-library/jest-dom';\n");
|
|
18
|
+
}
|
|
19
|
+
removeDependenciesFromPackageJson(context, [], ["@types/testing-library__jest-dom"]);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return context;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { migrate as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grafana/create-plugin",
|
|
3
|
-
"version": "6.2.0-canary.
|
|
3
|
+
"version": "6.2.0-canary.2314.19503588692.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"directory": "packages/create-plugin",
|
|
6
6
|
"url": "https://github.com/grafana/plugin-tools"
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"typecheck": "tsc --noEmit"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
+
"@babel/parser": "^7.28.5",
|
|
28
29
|
"@ivanmaxlogiudice/gitignore": "^0.0.2",
|
|
29
30
|
"chalk": "^5.3.0",
|
|
30
31
|
"change-case": "^5.4.0",
|
|
@@ -59,5 +60,5 @@
|
|
|
59
60
|
"engines": {
|
|
60
61
|
"node": ">=20"
|
|
61
62
|
},
|
|
62
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "b9573cf309c5c96eb18b90a871862c4279398902"
|
|
63
64
|
}
|
|
@@ -40,5 +40,16 @@ export default {
|
|
|
40
40
|
description: 'Update React and ReactDOM 18.x versions to ^18.3.0 to surface React 19 compatibility issues.',
|
|
41
41
|
migrationScript: './scripts/005-react-18-3.js',
|
|
42
42
|
},
|
|
43
|
+
'006-webpack-nested-fix': {
|
|
44
|
+
version: '6.1.11',
|
|
45
|
+
description: 'Fix webpack variable replacement in nested plugins files.',
|
|
46
|
+
migrationScript: './scripts/006-webpack-nested-fix.js',
|
|
47
|
+
},
|
|
48
|
+
'007-remove-testing-library-types': {
|
|
49
|
+
version: '6.1.13',
|
|
50
|
+
description:
|
|
51
|
+
'Add setupTests.d.ts for @testing-library/jest-dom types and remove @types/testing-library__jest-dom npm package.',
|
|
52
|
+
migrationScript: './scripts/007-remove-testing-library-types.js',
|
|
53
|
+
},
|
|
43
54
|
},
|
|
44
55
|
} as Migrations;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import migrate from './006-webpack-nested-fix.js';
|
|
2
|
+
import { createDefaultContext } from '../test-utils.js';
|
|
3
|
+
|
|
4
|
+
describe('Migration - webpack nested fix', () => {
|
|
5
|
+
test('should transform files property to test property', () => {
|
|
6
|
+
const context = createDefaultContext();
|
|
7
|
+
|
|
8
|
+
const webpackConfigContent = `
|
|
9
|
+
import ReplaceInFileWebpackPlugin from 'replace-in-file-webpack-plugin';
|
|
10
|
+
|
|
11
|
+
const config = {
|
|
12
|
+
plugins: [
|
|
13
|
+
new ReplaceInFileWebpackPlugin([
|
|
14
|
+
{
|
|
15
|
+
dir: 'dist',
|
|
16
|
+
files: ['plugin.json', 'README.md'],
|
|
17
|
+
rules: [
|
|
18
|
+
{ search: /VERSION/g, replace: '1.0.0' }
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
])
|
|
22
|
+
]
|
|
23
|
+
};
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
context.addFile('.config/webpack/webpack.config.ts', webpackConfigContent);
|
|
27
|
+
|
|
28
|
+
const updatedContext = migrate(context);
|
|
29
|
+
const webpackConfig = updatedContext.getFile('.config/webpack/webpack.config.ts');
|
|
30
|
+
|
|
31
|
+
expect(webpackConfig).toContain('test: [/(^|\\/)plugin\\.json$/, /(^|\\/)README\\.md$/]');
|
|
32
|
+
expect(webpackConfig).not.toContain('files:');
|
|
33
|
+
expect(webpackConfig).toContain('dir:');
|
|
34
|
+
expect(webpackConfig).toContain('rules:');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('should not transform if files array has different values', () => {
|
|
38
|
+
const context = createDefaultContext();
|
|
39
|
+
|
|
40
|
+
const webpackConfigContent = `
|
|
41
|
+
import ReplaceInFileWebpackPlugin from 'replace-in-file-webpack-plugin';
|
|
42
|
+
|
|
43
|
+
const config = {
|
|
44
|
+
plugins: [
|
|
45
|
+
new ReplaceInFileWebpackPlugin([
|
|
46
|
+
{
|
|
47
|
+
dir: 'dist',
|
|
48
|
+
files: ['custom.json', 'other.md'],
|
|
49
|
+
rules: []
|
|
50
|
+
}
|
|
51
|
+
])
|
|
52
|
+
]
|
|
53
|
+
};
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
context.addFile('.config/webpack/webpack.config.ts', webpackConfigContent);
|
|
57
|
+
|
|
58
|
+
const updatedContext = migrate(context);
|
|
59
|
+
const webpackConfig = updatedContext.getFile('.config/webpack/webpack.config.ts');
|
|
60
|
+
|
|
61
|
+
expect(webpackConfig).toBe(webpackConfigContent);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('should not transform if already using test property', () => {
|
|
65
|
+
const context = createDefaultContext();
|
|
66
|
+
|
|
67
|
+
const webpackConfigContent = `
|
|
68
|
+
import ReplaceInFileWebpackPlugin from 'replace-in-file-webpack-plugin';
|
|
69
|
+
|
|
70
|
+
const config = {
|
|
71
|
+
plugins: [
|
|
72
|
+
new ReplaceInFileWebpackPlugin([
|
|
73
|
+
{
|
|
74
|
+
dir: 'dist',
|
|
75
|
+
test: [/(^|\\/)plugin\\.json$/, /(^|\\/)README\\.md$/],
|
|
76
|
+
rules: []
|
|
77
|
+
}
|
|
78
|
+
])
|
|
79
|
+
]
|
|
80
|
+
};
|
|
81
|
+
`;
|
|
82
|
+
|
|
83
|
+
context.addFile('.config/webpack/webpack.config.ts', webpackConfigContent);
|
|
84
|
+
|
|
85
|
+
const updatedContext = migrate(context);
|
|
86
|
+
const webpackConfig = updatedContext.getFile('.config/webpack/webpack.config.ts');
|
|
87
|
+
|
|
88
|
+
expect(webpackConfig).toBe(webpackConfigContent);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('should handle multiple ReplaceInFileWebpackPlugin instances', () => {
|
|
92
|
+
const context = createDefaultContext();
|
|
93
|
+
|
|
94
|
+
const webpackConfigContent = `
|
|
95
|
+
import ReplaceInFileWebpackPlugin from 'replace-in-file-webpack-plugin';
|
|
96
|
+
|
|
97
|
+
const config = {
|
|
98
|
+
plugins: [
|
|
99
|
+
new ReplaceInFileWebpackPlugin([
|
|
100
|
+
{
|
|
101
|
+
dir: 'dist',
|
|
102
|
+
files: ['plugin.json', 'README.md'],
|
|
103
|
+
rules: [],
|
|
104
|
+
}
|
|
105
|
+
]),
|
|
106
|
+
new ReplaceInFileWebpackPlugin([
|
|
107
|
+
{
|
|
108
|
+
dir: 'other',
|
|
109
|
+
files: ['custom.json'],
|
|
110
|
+
rules: [],
|
|
111
|
+
}
|
|
112
|
+
])
|
|
113
|
+
]
|
|
114
|
+
};
|
|
115
|
+
`;
|
|
116
|
+
|
|
117
|
+
const expectedConfig = `
|
|
118
|
+
import ReplaceInFileWebpackPlugin from 'replace-in-file-webpack-plugin';
|
|
119
|
+
|
|
120
|
+
const config = {
|
|
121
|
+
plugins: [
|
|
122
|
+
new ReplaceInFileWebpackPlugin([
|
|
123
|
+
{
|
|
124
|
+
dir: 'dist',
|
|
125
|
+
test: [/(^|\\/)plugin\\.json$/, /(^|\\/)README\\.md$/],
|
|
126
|
+
rules: [],
|
|
127
|
+
}
|
|
128
|
+
]),
|
|
129
|
+
new ReplaceInFileWebpackPlugin([
|
|
130
|
+
{
|
|
131
|
+
dir: 'other',
|
|
132
|
+
files: ['custom.json'],
|
|
133
|
+
rules: [],
|
|
134
|
+
}
|
|
135
|
+
])
|
|
136
|
+
]
|
|
137
|
+
};
|
|
138
|
+
`;
|
|
139
|
+
|
|
140
|
+
context.addFile('.config/webpack/webpack.config.ts', webpackConfigContent);
|
|
141
|
+
const updatedContext = migrate(context);
|
|
142
|
+
const webpackConfig = updatedContext.getFile('.config/webpack/webpack.config.ts');
|
|
143
|
+
expect(webpackConfig).toBe(expectedConfig);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('should be idempotent', () => {
|
|
147
|
+
const context = createDefaultContext();
|
|
148
|
+
|
|
149
|
+
const webpackConfigContent = `
|
|
150
|
+
import ReplaceInFileWebpackPlugin from 'replace-in-file-webpack-plugin';
|
|
151
|
+
|
|
152
|
+
const config = {
|
|
153
|
+
plugins: [
|
|
154
|
+
new ReplaceInFileWebpackPlugin([
|
|
155
|
+
{
|
|
156
|
+
dir: 'dist',
|
|
157
|
+
files: ['plugin.json', 'README.md'],
|
|
158
|
+
rules: []
|
|
159
|
+
}
|
|
160
|
+
])
|
|
161
|
+
]
|
|
162
|
+
};
|
|
163
|
+
`;
|
|
164
|
+
|
|
165
|
+
context.addFile('.config/webpack/webpack.config.ts', webpackConfigContent);
|
|
166
|
+
|
|
167
|
+
expect(migrate).toBeIdempotent(context);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import * as recast from 'recast';
|
|
3
|
+
import * as typeScriptParser from 'recast/parsers/typescript.js';
|
|
4
|
+
import type { Context } from '../context.js';
|
|
5
|
+
|
|
6
|
+
const { builders } = recast.types;
|
|
7
|
+
|
|
8
|
+
export default function migrate(context: Context): Context {
|
|
9
|
+
const webpackConfigPath = join('.config', 'webpack', 'webpack.config.ts');
|
|
10
|
+
if (!context.doesFileExist(webpackConfigPath)) {
|
|
11
|
+
return context;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const webpackConfigContent = context.getFile(webpackConfigPath);
|
|
15
|
+
if (!webpackConfigContent) {
|
|
16
|
+
return context;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let hasChanges = false;
|
|
20
|
+
const ast = recast.parse(webpackConfigContent, {
|
|
21
|
+
parser: typeScriptParser,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
recast.visit(ast, {
|
|
25
|
+
visitNewExpression(path) {
|
|
26
|
+
const { node } = path;
|
|
27
|
+
|
|
28
|
+
// Check if this is a ReplaceInFileWebpackPlugin constructor
|
|
29
|
+
if (
|
|
30
|
+
node.callee.type === 'Identifier' &&
|
|
31
|
+
node.callee.name === 'ReplaceInFileWebpackPlugin' &&
|
|
32
|
+
node.arguments.length > 0
|
|
33
|
+
) {
|
|
34
|
+
const firstArg = node.arguments[0];
|
|
35
|
+
|
|
36
|
+
// The first argument should be an array of config objects
|
|
37
|
+
if (firstArg.type === 'ArrayExpression' && firstArg.elements) {
|
|
38
|
+
firstArg.elements.forEach((element) => {
|
|
39
|
+
if (element && element.type === 'ObjectExpression') {
|
|
40
|
+
const changed = transformFilesProperty(element);
|
|
41
|
+
if (changed) {
|
|
42
|
+
hasChanges = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return this.traverse(path);
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Only update the file if we made changes
|
|
54
|
+
if (hasChanges) {
|
|
55
|
+
const output = recast.print(ast, {
|
|
56
|
+
tabWidth: 2,
|
|
57
|
+
trailingComma: true,
|
|
58
|
+
lineTerminator: '\n',
|
|
59
|
+
});
|
|
60
|
+
context.updateFile(webpackConfigPath, output.code);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return context;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function transformFilesProperty(objectExpression: recast.types.namedTypes.ObjectExpression): boolean {
|
|
67
|
+
const properties = objectExpression.properties;
|
|
68
|
+
if (!properties) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Find the 'files' property
|
|
73
|
+
const filesPropertyIndex = properties.findIndex(
|
|
74
|
+
(prop) =>
|
|
75
|
+
(prop.type === 'Property' || prop.type === 'ObjectProperty') &&
|
|
76
|
+
prop.key.type === 'Identifier' &&
|
|
77
|
+
prop.key.name === 'files'
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if (filesPropertyIndex === -1) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const filesProperty = properties[filesPropertyIndex];
|
|
85
|
+
|
|
86
|
+
// Type guard: ensure it's a Property or ObjectProperty (which have a value)
|
|
87
|
+
if ((filesProperty.type !== 'Property' && filesProperty.type !== 'ObjectProperty') || !('value' in filesProperty)) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Check if it's an array with the expected values
|
|
92
|
+
if (filesProperty.value.type === 'ArrayExpression' && filesProperty.value.elements.length === 2) {
|
|
93
|
+
const elements = filesProperty.value.elements;
|
|
94
|
+
const values = elements
|
|
95
|
+
.map((el) => (el?.type === 'Literal' || el?.type === 'StringLiteral' ? el?.value : null))
|
|
96
|
+
.filter((v) => v !== null);
|
|
97
|
+
|
|
98
|
+
// Only transform if it matches the exact pattern we're looking for
|
|
99
|
+
if (values.length === 2 && values.includes('plugin.json') && values.includes('README.md')) {
|
|
100
|
+
// Remove the 'files' property
|
|
101
|
+
properties.splice(filesPropertyIndex, 1);
|
|
102
|
+
|
|
103
|
+
// Add the 'test' property with regex array
|
|
104
|
+
const testProperty = builders.property(
|
|
105
|
+
'init',
|
|
106
|
+
builders.identifier('test'),
|
|
107
|
+
builders.arrayExpression([builders.literal(/(^|\/)plugin\.json$/), builders.literal(/(^|\/)README\.md$/)])
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Insert at the same position
|
|
111
|
+
properties.splice(filesPropertyIndex, 0, testProperty);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import migrate from './007-remove-testing-library-types.js';
|
|
3
|
+
import { Context } from '../context.js';
|
|
4
|
+
|
|
5
|
+
describe('006-remove-testing-library-types', () => {
|
|
6
|
+
it('should create setupTests.d.ts, remove types package, and add @testing-library/jest-dom when file does not exist', () => {
|
|
7
|
+
const context = new Context('/virtual');
|
|
8
|
+
|
|
9
|
+
context.addFile(
|
|
10
|
+
'package.json',
|
|
11
|
+
JSON.stringify({
|
|
12
|
+
devDependencies: {
|
|
13
|
+
'@types/testing-library__jest-dom': '^6.0.0',
|
|
14
|
+
'@testing-library/jest-dom': '6.0.0',
|
|
15
|
+
'@testing-library/react': '14.0.0',
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const result = migrate(context);
|
|
21
|
+
const setupTestsContent = result.getFile('./.config/types/setupTests.d.ts');
|
|
22
|
+
expect(setupTestsContent).toBe("import '@testing-library/jest-dom';\n");
|
|
23
|
+
|
|
24
|
+
const packageJson = JSON.parse(result.getFile('package.json') || '{}');
|
|
25
|
+
expect(packageJson.devDependencies).toEqual({
|
|
26
|
+
'@testing-library/jest-dom': '6.0.0',
|
|
27
|
+
'@testing-library/react': '14.0.0',
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should add import to existing setupTests.d.ts, remove types package, and add @testing-library/jest-dom if missing', () => {
|
|
32
|
+
const context = new Context('/virtual');
|
|
33
|
+
|
|
34
|
+
const existingContent = '// Some other type declarations\n';
|
|
35
|
+
context.addFile('./.config/types/setupTests.d.ts', existingContent);
|
|
36
|
+
context.addFile(
|
|
37
|
+
'package.json',
|
|
38
|
+
JSON.stringify({
|
|
39
|
+
devDependencies: {
|
|
40
|
+
'@types/testing-library__jest-dom': '^6.0.0',
|
|
41
|
+
'@testing-library/jest-dom': '6.1.4',
|
|
42
|
+
'@testing-library/react': '14.0.0',
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const result = migrate(context);
|
|
48
|
+
const setupTestsContent = result.getFile('./.config/types/setupTests.d.ts');
|
|
49
|
+
expect(setupTestsContent).toBe(`import '@testing-library/jest-dom';\n${existingContent}`);
|
|
50
|
+
|
|
51
|
+
const packageJson = JSON.parse(result.getFile('package.json') || '{}');
|
|
52
|
+
expect(packageJson.devDependencies).toEqual({
|
|
53
|
+
'@testing-library/jest-dom': '6.1.4',
|
|
54
|
+
'@testing-library/react': '14.0.0',
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should not modify setupTests.d.ts if import already exists', () => {
|
|
59
|
+
const context = new Context('/virtual');
|
|
60
|
+
|
|
61
|
+
const existingContent = "// Other content\nimport 'react';\nimport '@testing-library/jest-dom';\n";
|
|
62
|
+
context.addFile('./.config/types/setupTests.d.ts', existingContent);
|
|
63
|
+
context.addFile(
|
|
64
|
+
'package.json',
|
|
65
|
+
JSON.stringify({
|
|
66
|
+
devDependencies: {
|
|
67
|
+
'@types/testing-library__jest-dom': '^6.0.0',
|
|
68
|
+
'@testing-library/jest-dom': '6.1.4',
|
|
69
|
+
'@testing-library/react': '14.0.0',
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const result = migrate(context);
|
|
75
|
+
|
|
76
|
+
const setupTestsContent = result.getFile('./.config/types/setupTests.d.ts');
|
|
77
|
+
expect(setupTestsContent).toBe(existingContent);
|
|
78
|
+
|
|
79
|
+
const packageJson = JSON.parse(result.getFile('package.json') || '{}');
|
|
80
|
+
expect(packageJson.devDependencies).toEqual({
|
|
81
|
+
'@testing-library/jest-dom': '6.1.4',
|
|
82
|
+
'@testing-library/react': '14.0.0',
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should not modify anything if @testing-library/jest-dom is not greater than 6.0.0', () => {
|
|
87
|
+
const context = new Context('/virtual');
|
|
88
|
+
const packageJsonContent = JSON.stringify({
|
|
89
|
+
devDependencies: { '@testing-library/jest-dom': '5.14.2' },
|
|
90
|
+
});
|
|
91
|
+
context.addFile('package.json', packageJsonContent);
|
|
92
|
+
|
|
93
|
+
const result = migrate(context);
|
|
94
|
+
expect(result.getFile('package.json')).toEqual(packageJsonContent);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should handle package.json without the types package', () => {
|
|
98
|
+
const context = new Context('/virtual');
|
|
99
|
+
|
|
100
|
+
context.addFile(
|
|
101
|
+
'package.json',
|
|
102
|
+
JSON.stringify({
|
|
103
|
+
devDependencies: {
|
|
104
|
+
'@testing-library/react': '14.0.0',
|
|
105
|
+
'@testing-library/jest-dom': '6.1.4',
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const result = migrate(context);
|
|
111
|
+
const setupTestsContent = result.getFile('./.config/types/setupTests.d.ts');
|
|
112
|
+
expect(setupTestsContent).toBe("import '@testing-library/jest-dom';\n");
|
|
113
|
+
|
|
114
|
+
const packageJson = JSON.parse(result.getFile('package.json') || '{}');
|
|
115
|
+
expect(packageJson.devDependencies).toEqual({
|
|
116
|
+
'@testing-library/jest-dom': '6.1.4',
|
|
117
|
+
'@testing-library/react': '14.0.0',
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should be idempotent', async () => {
|
|
122
|
+
const context = new Context('/virtual');
|
|
123
|
+
|
|
124
|
+
context.addFile(
|
|
125
|
+
'package.json',
|
|
126
|
+
JSON.stringify({
|
|
127
|
+
devDependencies: {
|
|
128
|
+
'@types/testing-library__jest-dom': '^6.0.0',
|
|
129
|
+
'@testing-library/jest-dom': '6.1.4',
|
|
130
|
+
'@testing-library/react': '14.0.0',
|
|
131
|
+
},
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
await expect(migrate).toBeIdempotent(context);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Context } from '../context.js';
|
|
2
|
+
import { removeDependenciesFromPackageJson, isVersionGreater } from '../utils.js';
|
|
3
|
+
|
|
4
|
+
export default function migrate(context: Context) {
|
|
5
|
+
if (context.doesFileExist('package.json')) {
|
|
6
|
+
const packageJson = JSON.parse(context.getFile('package.json') || '{}');
|
|
7
|
+
if (isVersionGreater(packageJson.devDependencies['@testing-library/jest-dom'], '6.0.0', true)) {
|
|
8
|
+
if (context.doesFileExist('./.config/types/setupTests.d.ts')) {
|
|
9
|
+
const setupTestsContent = context.getFile('./.config/types/setupTests.d.ts');
|
|
10
|
+
if (!setupTestsContent?.includes('@testing-library/jest-dom')) {
|
|
11
|
+
context.updateFile(
|
|
12
|
+
'./.config/types/setupTests.d.ts',
|
|
13
|
+
`import '@testing-library/jest-dom';\n${setupTestsContent}`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
} else {
|
|
17
|
+
context.addFile('./.config/types/setupTests.d.ts', "import '@testing-library/jest-dom';\n");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
removeDependenciesFromPackageJson(context, [], ['@types/testing-library__jest-dom']);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return context;
|
|
25
|
+
}
|