Skip to content

Commit 1ec36d1

Browse files
committed
fix(cli): verify Rsbuild plugin presence via plugins array AST instead of whole-file regex
1 parent a76cc66 commit 1ec36d1

2 files changed

Lines changed: 75 additions & 15 deletions

File tree

packages/arkenv/src/adapters/node-workspace/utils/bootstrappers.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,5 +464,34 @@ describe("bootstrappers", () => {
464464
expect(result.success).toBe(true);
465465
expect(result.updated).toBe(true);
466466
});
467+
468+
it("injects plugin into plugins array even if import already exists but is unregistered", async () => {
469+
const initialContent = dedent`
470+
import { arkenvRsbuildPlugin } from "@arkenv/rsbuild-plugin"
471+
export default {
472+
plugins: []
473+
}
474+
`;
475+
476+
const result = transformRsbuildConfig({ code: initialContent });
477+
expect(result.success).toBe(true);
478+
expect(result.updated).toBe(true);
479+
expect(result.code).toContain("plugins: [arkenvRsbuildPlugin()]");
480+
});
481+
482+
it("injects aliased plugin call when an unregistered aliased import exists", async () => {
483+
const initialContent = dedent`
484+
import { arkenvRsbuildPlugin as customPlugin } from "@arkenv/rsbuild-plugin"
485+
export default {
486+
plugins: []
487+
}
488+
`;
489+
490+
const result = transformRsbuildConfig({ code: initialContent });
491+
expect(result.success).toBe(true);
492+
expect(result.updated).toBe(true);
493+
expect(result.code).toContain("plugins: [customPlugin()]");
494+
expect(result.code).not.toContain("arkenvRsbuildPlugin()");
495+
});
467496
});
468497
});

packages/arkenv/src/adapters/node-workspace/utils/bootstrappers.ts

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -235,22 +235,52 @@ export function transformRsbuildConfig(
235235
}
236236

237237
if (Array.isArray(config.plugins)) {
238-
// Check if already exists using a regex to avoid false positives and support aliases
239-
const hasPlugin =
240-
/\barkenv(?:Rsbuild)?Plugin\b/.test(initialCode) ||
241-
/from\s+['"]@arkenv\/rsbuild-plugin(?:\/standard)?['"]/.test(
242-
initialCode,
243-
);
238+
// Find identifier names imported from @arkenv/rsbuild-plugin (supports aliases)
239+
const rsbuildPluginImports = new Set<string>();
240+
for (const item of mod.imports.$items || []) {
241+
if (item.from && item.from.startsWith("@arkenv/rsbuild-plugin")) {
242+
rsbuildPluginImports.add(item.local);
243+
}
244+
}
245+
rsbuildPluginImports.add("arkenvRsbuildPlugin");
246+
247+
// Check if already registered in the plugins array AST
248+
const pluginsList = Array.from(config.plugins as any[]);
249+
const hasPlugin = pluginsList.some((p) => {
250+
if (typeof p !== "object" || !p || !("$type" in p)) return false;
251+
if (p.$type === "function-call") {
252+
return (
253+
typeof p.$callee === "string" && rsbuildPluginImports.has(p.$callee)
254+
);
255+
}
256+
if (p.$type === "identifier") {
257+
return (
258+
p.$ast?.type === "Identifier" &&
259+
rsbuildPluginImports.has(p.$ast.name)
260+
);
261+
}
262+
return false;
263+
});
244264

245265
if (!hasPlugin) {
246-
// Add imports
247-
mod.imports.$add({
248-
from: "@arkenv/rsbuild-plugin",
249-
local: "arkenvRsbuildPlugin",
250-
imported: "arkenvRsbuildPlugin",
251-
});
266+
const existingImport = (mod.imports.$items || []).find(
267+
(item) => item.from && item.from.startsWith("@arkenv/rsbuild-plugin"),
268+
);
269+
const localName = existingImport?.local || "arkenvRsbuildPlugin";
270+
271+
if (!existingImport) {
272+
mod.imports.$add({
273+
from: "@arkenv/rsbuild-plugin",
274+
local: "arkenvRsbuildPlugin",
275+
imported: "arkenvRsbuildPlugin",
276+
});
277+
}
252278

253-
config.plugins.push("__ARK_PLUGIN_PLACEHOLDER__");
279+
const placeholder =
280+
localName === "arkenvRsbuildPlugin"
281+
? "__ARK_PLUGIN__"
282+
: `__ARK_PLUGIN__:${localName}`;
283+
config.plugins.push(placeholder);
254284
} else {
255285
// Already has plugin, nothing to do
256286
return { success: true, updated: false };
@@ -266,8 +296,9 @@ export function transformRsbuildConfig(
266296
let code = generateCode(mod, {
267297
format: detectCodeFormat(initialCode),
268298
}).code;
269-
const pluginCall = "arkenvRsbuildPlugin()";
270-
code = code.replace(/['"]__ARK_PLUGIN_PLACEHOLDER__['"]/g, pluginCall);
299+
code = code.replace(/['"]__ARK_PLUGIN__(?::(.+?))?['"]/g, (_, name) =>
300+
name ? `${name}()` : "arkenvRsbuildPlugin()",
301+
);
271302
code = normalizeImportSpacing(code);
272303
code = preserveTrailingNewline(code, initialCode);
273304

0 commit comments

Comments
 (0)