@grafana/create-plugin 6.8.0-canary.2356.20815895884.0 → 6.8.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 +101 -0
- package/dist/codemods/additions/additions.js +3 -3
- package/dist/codemods/utils.js +2 -2
- package/dist/constants.js +1 -0
- package/dist/utils/utils.templates.js +4 -1
- package/package.json +3 -3
- package/src/codemods/additions/additions.ts +3 -3
- package/src/constants.ts +1 -0
- package/src/types.ts +1 -0
- package/src/utils/tests/utils.config.test.ts +25 -3
- package/src/utils/utils.config.ts +1 -0
- package/src/utils/utils.templates.ts +10 -1
- package/templates/backend/go.mod +30 -29
- package/templates/backend/go.sum +62 -62
- package/templates/backend-app/go.mod +30 -29
- package/templates/backend-app/go.sum +62 -62
- package/templates/common/.config/bundler/copyFiles.ts +23 -0
- package/templates/common/.config/docker-compose-base.yaml +1 -1
- package/templates/common/.config/rspack/BuildModeRspackPlugin.ts +4 -4
- package/templates/common/.config/rspack/{liveReloadPlugin.js → liveReloadPlugin.ts} +34 -11
- package/templates/common/.config/rspack/rspack.config.ts +8 -22
- package/templates/common/.config/webpack/BuildModeWebpackPlugin.ts +1 -1
- package/templates/common/.config/webpack/webpack.config.ts +4 -17
- package/templates/common/.cprc.json +1 -0
- package/templates/common/_package.json +46 -44
- package/templates/github/workflows/bundle-stats.yml +1 -1
- package/templates/github/workflows/ci.yml +17 -10
- package/templates/github/workflows/is-compatible.yml +2 -2
- package/templates/github/workflows/release.yml +1 -1
- package/dist/codemods/additions/scripts/bundle-grafana-ui/index.js +0 -174
- package/dist/codemods/utils.bundler-config.js +0 -19
- package/dist/codemods/utils.externals.js +0 -116
- package/src/codemods/additions/scripts/bundle-grafana-ui/README.md +0 -68
- package/src/codemods/additions/scripts/bundle-grafana-ui/index.test.ts +0 -511
- package/src/codemods/additions/scripts/bundle-grafana-ui/index.ts +0 -259
- package/src/codemods/utils.bundler-config.ts +0 -203
- package/src/codemods/utils.externals.test.ts +0 -87
- package/src/codemods/utils.externals.ts +0 -181
- package/templates/common/.config/rspack/utils.ts +0 -63
- package/templates/common/.config/webpack/constants.ts +0 -2
- /package/templates/common/.config/{rspack → bundler}/constants.ts +0 -0
- /package/templates/common/.config/{webpack → bundler}/utils.ts +0 -0
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
import * as v from 'valibot';
|
|
2
|
-
import * as recast from 'recast';
|
|
3
|
-
import { coerce, gte } from 'semver';
|
|
4
|
-
|
|
5
|
-
import type { Context } from '../../../context.js';
|
|
6
|
-
import { additionsDebug } from '../../../utils.js';
|
|
7
|
-
import { getBundlerConfig } from '../../../utils.bundler-config.js';
|
|
8
|
-
import { updateExternalsArray, type ExternalsArrayModifier } from '../../../utils.externals.js';
|
|
9
|
-
|
|
10
|
-
const { builders } = recast.types;
|
|
11
|
-
|
|
12
|
-
const PLUGIN_JSON_PATH = 'src/plugin.json';
|
|
13
|
-
const MIN_GRAFANA_VERSION = '10.2.0';
|
|
14
|
-
|
|
15
|
-
export const schema = v.object({});
|
|
16
|
-
type BundleGrafanaUIOptions = v.InferOutput<typeof schema>;
|
|
17
|
-
|
|
18
|
-
export default function bundleGrafanaUI(context: Context, _options: BundleGrafanaUIOptions): Context {
|
|
19
|
-
additionsDebug('Running bundle-grafana-ui addition...');
|
|
20
|
-
|
|
21
|
-
// Ensure minimum Grafana version requirement
|
|
22
|
-
ensureMinGrafanaVersion(context);
|
|
23
|
-
|
|
24
|
-
// Update externals array using the shared utility
|
|
25
|
-
updateExternalsArray(context, createBundleGrafanaUIModifier());
|
|
26
|
-
|
|
27
|
-
// Update bundler resolve configuration to handle ESM imports
|
|
28
|
-
updateResolveExtensions(context);
|
|
29
|
-
|
|
30
|
-
// Update module rules directly with simple string manipulation
|
|
31
|
-
updateModuleRules(context);
|
|
32
|
-
|
|
33
|
-
return context;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Checks if an AST node is a regex matching a Grafana package pattern
|
|
38
|
-
* @param element - The AST node to check
|
|
39
|
-
* @param pattern - The regex pattern to match (e.g., "^@grafana\\/ui" or "^@grafana\\/data")
|
|
40
|
-
*/
|
|
41
|
-
function isGrafanaRegex(element: recast.types.namedTypes.ASTNode, pattern: string): boolean {
|
|
42
|
-
// Handle RegExpLiteral (TypeScript parser)
|
|
43
|
-
if (element.type === 'RegExpLiteral') {
|
|
44
|
-
const regexNode = element as recast.types.namedTypes.RegExpLiteral;
|
|
45
|
-
return regexNode.pattern === pattern && regexNode.flags === 'i';
|
|
46
|
-
}
|
|
47
|
-
// Handle Literal with regex property (other parsers)
|
|
48
|
-
if (element.type === 'Literal' && 'regex' in element && element.regex) {
|
|
49
|
-
const regex = element.regex as { pattern: string; flags: string };
|
|
50
|
-
return regex.pattern === pattern && regex.flags === 'i';
|
|
51
|
-
}
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Removes /^@grafana\/ui/i regex from externals array and adds 'react-inlinesvg'
|
|
57
|
-
* @returns true if changes were made, false otherwise
|
|
58
|
-
*/
|
|
59
|
-
function removeGrafanaUiAndAddReactInlineSvg(externalsArray: recast.types.namedTypes.ArrayExpression): boolean {
|
|
60
|
-
let hasChanges = false;
|
|
61
|
-
let hasGrafanaUiExternal = false;
|
|
62
|
-
let hasReactInlineSvg = false;
|
|
63
|
-
|
|
64
|
-
// Check current state
|
|
65
|
-
for (const element of externalsArray.elements) {
|
|
66
|
-
if (!element) {
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Check for /^@grafana\/ui/i regex
|
|
71
|
-
if (isGrafanaRegex(element, '^@grafana\\/ui')) {
|
|
72
|
-
hasGrafanaUiExternal = true;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Check for 'react-inlinesvg' string
|
|
76
|
-
if (
|
|
77
|
-
(element.type === 'Literal' || element.type === 'StringLiteral') &&
|
|
78
|
-
'value' in element &&
|
|
79
|
-
typeof element.value === 'string' &&
|
|
80
|
-
element.value === 'react-inlinesvg'
|
|
81
|
-
) {
|
|
82
|
-
hasReactInlineSvg = true;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Remove /^@grafana\/ui/i if present
|
|
87
|
-
if (hasGrafanaUiExternal) {
|
|
88
|
-
externalsArray.elements = externalsArray.elements.filter((element) => {
|
|
89
|
-
if (!element) {
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
return !isGrafanaRegex(element, '^@grafana\\/ui');
|
|
93
|
-
});
|
|
94
|
-
hasChanges = true;
|
|
95
|
-
additionsDebug('Removed /^@grafana\\/ui/i from externals array');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Add 'react-inlinesvg' if not present
|
|
99
|
-
if (!hasReactInlineSvg) {
|
|
100
|
-
// Find the index of /^@grafana\/data/i to insert after it
|
|
101
|
-
let insertIndex = -1;
|
|
102
|
-
for (let i = 0; i < externalsArray.elements.length; i++) {
|
|
103
|
-
const element = externalsArray.elements[i];
|
|
104
|
-
if (element && isGrafanaRegex(element, '^@grafana\\/data')) {
|
|
105
|
-
insertIndex = i + 1;
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (insertIndex >= 0) {
|
|
111
|
-
externalsArray.elements.splice(insertIndex, 0, builders.literal('react-inlinesvg'));
|
|
112
|
-
} else {
|
|
113
|
-
// Fallback: append to end
|
|
114
|
-
externalsArray.elements.push(builders.literal('react-inlinesvg'));
|
|
115
|
-
}
|
|
116
|
-
hasChanges = true;
|
|
117
|
-
additionsDebug("Added 'react-inlinesvg' to externals array");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return hasChanges;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Creates a modifier function for updateExternalsArray that removes @grafana/ui
|
|
125
|
-
* and adds react-inlinesvg
|
|
126
|
-
*/
|
|
127
|
-
function createBundleGrafanaUIModifier(): ExternalsArrayModifier {
|
|
128
|
-
return (externalsArray: recast.types.namedTypes.ArrayExpression) => {
|
|
129
|
-
return removeGrafanaUiAndAddReactInlineSvg(externalsArray);
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Updates resolve extensions to add .mjs using string manipulation
|
|
135
|
-
*/
|
|
136
|
-
function updateResolveExtensions(context: Context): void {
|
|
137
|
-
const config = getBundlerConfig(context);
|
|
138
|
-
if (!config) {
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const { path: configPath, content } = config;
|
|
143
|
-
|
|
144
|
-
// Check if .mjs already exists
|
|
145
|
-
if (content.includes("'.mjs'") || content.includes('".mjs"')) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Add .mjs to extensions array
|
|
150
|
-
const updated = content.replace(/(extensions:\s*\[)([^\]]+)(\])/, (match, prefix, extensions, suffix) => {
|
|
151
|
-
if (extensions.includes('.mjs')) {
|
|
152
|
-
return match;
|
|
153
|
-
}
|
|
154
|
-
return `${prefix}${extensions}, '.mjs'${suffix}`;
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
if (updated !== content) {
|
|
158
|
-
context.updateFile(configPath, updated);
|
|
159
|
-
additionsDebug("Added '.mjs' to resolve.extensions");
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Updates module rules to add .mjs rule using string manipulation
|
|
165
|
-
*/
|
|
166
|
-
function updateModuleRules(context: Context): void {
|
|
167
|
-
const config = getBundlerConfig(context);
|
|
168
|
-
if (!config) {
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const { path: configPath, content } = config;
|
|
173
|
-
|
|
174
|
-
// Check if rule already exists
|
|
175
|
-
if (content.includes('test: /\\.mjs$') || content.includes('test: /\\\\.mjs$')) {
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const mjsRule = `{
|
|
180
|
-
test: /\\.mjs$/,
|
|
181
|
-
include: /node_modules/,
|
|
182
|
-
resolve: {
|
|
183
|
-
fullySpecified: false,
|
|
184
|
-
},
|
|
185
|
-
type: 'javascript/auto',
|
|
186
|
-
},`;
|
|
187
|
-
|
|
188
|
-
// Simple approach: find rules array and insert after first rule
|
|
189
|
-
let updated = content;
|
|
190
|
-
|
|
191
|
-
// Case 1: Empty array - insert at start
|
|
192
|
-
if (content.match(/rules:\s*\[\s*\]/)) {
|
|
193
|
-
updated = content.replace(/(rules:\s*\[\s*)(\])/, `$1${mjsRule}\n $2`);
|
|
194
|
-
}
|
|
195
|
-
// Case 2: Find first rule and insert after it
|
|
196
|
-
else {
|
|
197
|
-
// Match: rules: [ { ... }, and insert mjs rule after the first rule
|
|
198
|
-
// The regex finds the first complete rule object (balanced braces)
|
|
199
|
-
updated = content.replace(/(rules:\s*\[\s*)(\{[\s\S]*?\}),(\s*)/, (match, prefix, firstRule, suffix) => {
|
|
200
|
-
// Check if we already inserted (avoid double insertion)
|
|
201
|
-
if (match.includes('test: /\\.mjs$')) {
|
|
202
|
-
return match;
|
|
203
|
-
}
|
|
204
|
-
// Insert mjs rule after first rule
|
|
205
|
-
return `${prefix}${firstRule},\n ${mjsRule}${suffix}`;
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if (updated !== content) {
|
|
210
|
-
context.updateFile(configPath, updated);
|
|
211
|
-
additionsDebug('Added module rule for .mjs files in node_modules with resolve.fullySpecified: false');
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Ensures plugin.json has grafanaDependency >= 10.2.0
|
|
217
|
-
* Bundling @grafana/ui is only supported from Grafana 10.2.0 onwards
|
|
218
|
-
*/
|
|
219
|
-
function ensureMinGrafanaVersion(context: Context): void {
|
|
220
|
-
if (!context.doesFileExist(PLUGIN_JSON_PATH)) {
|
|
221
|
-
additionsDebug(`${PLUGIN_JSON_PATH} not found, skipping version check`);
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const pluginJsonRaw = context.getFile(PLUGIN_JSON_PATH);
|
|
226
|
-
if (!pluginJsonRaw) {
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
try {
|
|
231
|
-
const pluginJson = JSON.parse(pluginJsonRaw);
|
|
232
|
-
|
|
233
|
-
if (!pluginJson.dependencies) {
|
|
234
|
-
pluginJson.dependencies = {};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const currentGrafanaDep = pluginJson.dependencies.grafanaDependency || '>=9.0.0';
|
|
238
|
-
const currentVersion = coerce(currentGrafanaDep.replace(/^[><=]+/, ''));
|
|
239
|
-
const minVersion = coerce(MIN_GRAFANA_VERSION);
|
|
240
|
-
|
|
241
|
-
if (!currentVersion || !minVersion || !gte(currentVersion, minVersion)) {
|
|
242
|
-
const oldVersion = pluginJson.dependencies.grafanaDependency || 'not set';
|
|
243
|
-
pluginJson.dependencies.grafanaDependency = `>=${MIN_GRAFANA_VERSION}`;
|
|
244
|
-
context.updateFile(PLUGIN_JSON_PATH, JSON.stringify(pluginJson, null, 2));
|
|
245
|
-
additionsDebug(
|
|
246
|
-
`Updated grafanaDependency from "${oldVersion}" to ">=${MIN_GRAFANA_VERSION}" - bundling @grafana/ui requires Grafana ${MIN_GRAFANA_VERSION} or higher`
|
|
247
|
-
);
|
|
248
|
-
console.log(
|
|
249
|
-
`\n⚠️ Updated grafanaDependency to ">=${MIN_GRAFANA_VERSION}" because bundling @grafana/ui is only supported from Grafana ${MIN_GRAFANA_VERSION} onwards.\n`
|
|
250
|
-
);
|
|
251
|
-
} else {
|
|
252
|
-
additionsDebug(
|
|
253
|
-
`grafanaDependency "${currentGrafanaDep}" already meets minimum requirement of ${MIN_GRAFANA_VERSION}`
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
} catch (error) {
|
|
257
|
-
additionsDebug(`Error updating ${PLUGIN_JSON_PATH}:`, error);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import * as recast from 'recast';
|
|
2
|
-
import * as typeScriptParser from 'recast/parsers/typescript.js';
|
|
3
|
-
|
|
4
|
-
import type { Context } from './context.js';
|
|
5
|
-
import { additionsDebug } from './utils.js';
|
|
6
|
-
|
|
7
|
-
const WEBPACK_CONFIG_PATH = '.config/webpack/webpack.config.ts';
|
|
8
|
-
const RSPACK_CONFIG_PATH = '.config/rspack/rspack.config.ts';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Gets the bundler config file path and content, preferring rspack over webpack
|
|
12
|
-
* @returns Object with path and content, or null if no config file exists
|
|
13
|
-
*/
|
|
14
|
-
export function getBundlerConfig(context: Context): { path: string; content: string } | null {
|
|
15
|
-
const configPath = context.doesFileExist(RSPACK_CONFIG_PATH)
|
|
16
|
-
? RSPACK_CONFIG_PATH
|
|
17
|
-
: context.doesFileExist(WEBPACK_CONFIG_PATH)
|
|
18
|
-
? WEBPACK_CONFIG_PATH
|
|
19
|
-
: null;
|
|
20
|
-
|
|
21
|
-
if (!configPath) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const content = context.getFile(configPath);
|
|
26
|
-
if (!content) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return { path: configPath, content };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Type for a function that modifies a resolve object expression
|
|
35
|
-
* @param resolveObject - The AST node representing the resolve configuration
|
|
36
|
-
* @returns true if changes were made, false otherwise
|
|
37
|
-
*/
|
|
38
|
-
export type ResolveModifier = (resolveObject: recast.types.namedTypes.ObjectExpression) => boolean;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Type for a function that modifies a module rules array
|
|
42
|
-
* @param moduleObject - The AST node representing the module configuration
|
|
43
|
-
* @returns true if changes were made, false otherwise
|
|
44
|
-
*/
|
|
45
|
-
export type ModuleRulesModifier = (moduleObject: recast.types.namedTypes.ObjectExpression) => boolean;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Updates the bundler's resolve and module configuration.
|
|
49
|
-
*
|
|
50
|
-
* This utility handles both webpack and rspack configurations, preferring rspack when both exist.
|
|
51
|
-
*
|
|
52
|
-
* @param context - The codemod context
|
|
53
|
-
* @param resolveModifier - Optional function to modify the resolve configuration
|
|
54
|
-
* @param moduleRulesModifier - Optional function to modify the module rules configuration
|
|
55
|
-
*/
|
|
56
|
-
export function updateBundlerConfig(
|
|
57
|
-
context: Context,
|
|
58
|
-
resolveModifier?: ResolveModifier,
|
|
59
|
-
moduleRulesModifier?: ModuleRulesModifier
|
|
60
|
-
): void {
|
|
61
|
-
if (!resolveModifier && !moduleRulesModifier) {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Try rspack config first (newer structure)
|
|
66
|
-
if (context.doesFileExist(RSPACK_CONFIG_PATH)) {
|
|
67
|
-
additionsDebug(`Found ${RSPACK_CONFIG_PATH}, updating bundler configuration...`);
|
|
68
|
-
const rspackContent = context.getFile(RSPACK_CONFIG_PATH);
|
|
69
|
-
if (rspackContent) {
|
|
70
|
-
try {
|
|
71
|
-
const ast = recast.parse(rspackContent, {
|
|
72
|
-
parser: typeScriptParser,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
let hasChanges = false;
|
|
76
|
-
|
|
77
|
-
recast.visit(ast, {
|
|
78
|
-
visitObjectExpression(path) {
|
|
79
|
-
const { node } = path;
|
|
80
|
-
const properties = node.properties;
|
|
81
|
-
|
|
82
|
-
if (properties) {
|
|
83
|
-
for (const prop of properties) {
|
|
84
|
-
if (prop && (prop.type === 'Property' || prop.type === 'ObjectProperty')) {
|
|
85
|
-
const key = 'key' in prop ? prop.key : null;
|
|
86
|
-
const value = 'value' in prop ? prop.value : null;
|
|
87
|
-
|
|
88
|
-
// Find the resolve property
|
|
89
|
-
if (
|
|
90
|
-
resolveModifier &&
|
|
91
|
-
key &&
|
|
92
|
-
key.type === 'Identifier' &&
|
|
93
|
-
key.name === 'resolve' &&
|
|
94
|
-
value &&
|
|
95
|
-
value.type === 'ObjectExpression'
|
|
96
|
-
) {
|
|
97
|
-
hasChanges = resolveModifier(value) || hasChanges;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Find the module property
|
|
101
|
-
if (
|
|
102
|
-
moduleRulesModifier &&
|
|
103
|
-
key &&
|
|
104
|
-
key.type === 'Identifier' &&
|
|
105
|
-
key.name === 'module' &&
|
|
106
|
-
value &&
|
|
107
|
-
value.type === 'ObjectExpression'
|
|
108
|
-
) {
|
|
109
|
-
hasChanges = moduleRulesModifier(value) || hasChanges;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return this.traverse(path);
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
if (hasChanges) {
|
|
120
|
-
const output = recast.print(ast, {
|
|
121
|
-
tabWidth: 2,
|
|
122
|
-
trailingComma: true,
|
|
123
|
-
lineTerminator: '\n',
|
|
124
|
-
});
|
|
125
|
-
context.updateFile(RSPACK_CONFIG_PATH, output.code);
|
|
126
|
-
additionsDebug(`Updated ${RSPACK_CONFIG_PATH}`);
|
|
127
|
-
}
|
|
128
|
-
} catch (error) {
|
|
129
|
-
additionsDebug(`Error updating ${RSPACK_CONFIG_PATH}:`, error);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Fall back to webpack config (legacy structure)
|
|
136
|
-
if (context.doesFileExist(WEBPACK_CONFIG_PATH)) {
|
|
137
|
-
additionsDebug(`Found ${WEBPACK_CONFIG_PATH}, updating bundler configuration...`);
|
|
138
|
-
const webpackContent = context.getFile(WEBPACK_CONFIG_PATH);
|
|
139
|
-
if (webpackContent) {
|
|
140
|
-
try {
|
|
141
|
-
const ast = recast.parse(webpackContent, {
|
|
142
|
-
parser: typeScriptParser,
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
let hasChanges = false;
|
|
146
|
-
|
|
147
|
-
recast.visit(ast, {
|
|
148
|
-
visitObjectExpression(path) {
|
|
149
|
-
const { node } = path;
|
|
150
|
-
const properties = node.properties;
|
|
151
|
-
|
|
152
|
-
if (properties) {
|
|
153
|
-
for (const prop of properties) {
|
|
154
|
-
if (prop && (prop.type === 'Property' || prop.type === 'ObjectProperty')) {
|
|
155
|
-
const key = 'key' in prop ? prop.key : null;
|
|
156
|
-
const value = 'value' in prop ? prop.value : null;
|
|
157
|
-
|
|
158
|
-
// Find the resolve property
|
|
159
|
-
if (
|
|
160
|
-
resolveModifier &&
|
|
161
|
-
key &&
|
|
162
|
-
key.type === 'Identifier' &&
|
|
163
|
-
key.name === 'resolve' &&
|
|
164
|
-
value &&
|
|
165
|
-
value.type === 'ObjectExpression'
|
|
166
|
-
) {
|
|
167
|
-
hasChanges = resolveModifier(value) || hasChanges;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Find the module property
|
|
171
|
-
if (
|
|
172
|
-
moduleRulesModifier &&
|
|
173
|
-
key &&
|
|
174
|
-
key.type === 'Identifier' &&
|
|
175
|
-
key.name === 'module' &&
|
|
176
|
-
value &&
|
|
177
|
-
value.type === 'ObjectExpression'
|
|
178
|
-
) {
|
|
179
|
-
hasChanges = moduleRulesModifier(value) || hasChanges;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return this.traverse(path);
|
|
186
|
-
},
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
if (hasChanges) {
|
|
190
|
-
const output = recast.print(ast, {
|
|
191
|
-
tabWidth: 2,
|
|
192
|
-
trailingComma: true,
|
|
193
|
-
lineTerminator: '\n',
|
|
194
|
-
});
|
|
195
|
-
context.updateFile(WEBPACK_CONFIG_PATH, output.code);
|
|
196
|
-
additionsDebug(`Updated ${WEBPACK_CONFIG_PATH}`);
|
|
197
|
-
}
|
|
198
|
-
} catch (error) {
|
|
199
|
-
additionsDebug(`Error updating ${WEBPACK_CONFIG_PATH}:`, error);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import * as recast from 'recast';
|
|
3
|
-
|
|
4
|
-
import { Context } from './context.js';
|
|
5
|
-
import { updateExternalsArray, type ExternalsArrayModifier } from './utils.externals.js';
|
|
6
|
-
|
|
7
|
-
describe('updateExternalsArray', () => {
|
|
8
|
-
describe('new structure (.config/bundler/externals.ts)', () => {
|
|
9
|
-
it('should update externals array in externals.ts', () => {
|
|
10
|
-
const context = new Context('/virtual');
|
|
11
|
-
context.addFile('.config/bundler/externals.ts', `export const externals = ['react', 'react-dom'];`);
|
|
12
|
-
|
|
13
|
-
const modifier: ExternalsArrayModifier = (array) => {
|
|
14
|
-
array.elements.push(recast.types.builders.literal('i18next'));
|
|
15
|
-
return true;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const result = updateExternalsArray(context, modifier);
|
|
19
|
-
|
|
20
|
-
expect(result).toBe(true);
|
|
21
|
-
const content = context.getFile('.config/bundler/externals.ts') || '';
|
|
22
|
-
expect(content).toMatch(/['"]i18next['"]/);
|
|
23
|
-
expect(content).toContain("'react'");
|
|
24
|
-
expect(content).toContain("'react-dom'");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should return false if no changes were made', () => {
|
|
28
|
-
const context = new Context('/virtual');
|
|
29
|
-
context.addFile('.config/bundler/externals.ts', `export const externals = ['react', 'react-dom'];`);
|
|
30
|
-
|
|
31
|
-
const modifier: ExternalsArrayModifier = () => {
|
|
32
|
-
return false; // No changes
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const result = updateExternalsArray(context, modifier);
|
|
36
|
-
|
|
37
|
-
expect(result).toBe(false);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe('legacy structure (.config/webpack/webpack.config.ts)', () => {
|
|
42
|
-
it('should update externals array in webpack.config.ts when externals.ts does not exist', () => {
|
|
43
|
-
const context = new Context('/virtual');
|
|
44
|
-
context.addFile(
|
|
45
|
-
'.config/webpack/webpack.config.ts',
|
|
46
|
-
`import { Configuration } from 'webpack';
|
|
47
|
-
export const config: Configuration = {
|
|
48
|
-
externals: ['react', 'react-dom'],
|
|
49
|
-
};`
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
const modifier: ExternalsArrayModifier = (array) => {
|
|
53
|
-
array.elements.push(recast.types.builders.literal('i18next'));
|
|
54
|
-
return true;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const result = updateExternalsArray(context, modifier);
|
|
58
|
-
|
|
59
|
-
expect(result).toBe(true);
|
|
60
|
-
const content = context.getFile('.config/webpack/webpack.config.ts') || '';
|
|
61
|
-
expect(content).toMatch(/['"]i18next['"]/);
|
|
62
|
-
expect(content).toContain("'react'");
|
|
63
|
-
expect(content).toContain("'react-dom'");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should prefer externals.ts over webpack.config.ts', () => {
|
|
67
|
-
const context = new Context('/virtual');
|
|
68
|
-
context.addFile('.config/bundler/externals.ts', `export const externals = ['react'];`);
|
|
69
|
-
context.addFile('.config/webpack/webpack.config.ts', `export const config = { externals: ['react-dom'] };`);
|
|
70
|
-
|
|
71
|
-
const modifier: ExternalsArrayModifier = (array) => {
|
|
72
|
-
array.elements.push(recast.types.builders.literal('i18next'));
|
|
73
|
-
return true;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const result = updateExternalsArray(context, modifier);
|
|
77
|
-
|
|
78
|
-
expect(result).toBe(true);
|
|
79
|
-
// Should update externals.ts, not webpack.config.ts
|
|
80
|
-
const externalsContent = context.getFile('.config/bundler/externals.ts') || '';
|
|
81
|
-
expect(externalsContent).toMatch(/['"]i18next['"]/);
|
|
82
|
-
|
|
83
|
-
const webpackContent = context.getFile('.config/webpack/webpack.config.ts') || '';
|
|
84
|
-
expect(webpackContent).not.toMatch(/['"]i18next['"]/);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
});
|