@dx-labs/template-designer-setup 0.1.2 → 0.1.3
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 +7 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -1
- package/dist/steps/patchAppTsx.js +79 -0
- package/dist/steps/patchAppTsx.js.map +1 -1
- package/dist/steps/patchRootTsx.js +4 -0
- package/dist/steps/patchRootTsx.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/ast.d.ts +1 -0
- package/dist/utils/ast.js +17 -0
- package/dist/utils/ast.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [0.1.3](https://github.com/dx-labs-com/template-designer-pro-setup/compare/template-designer-pro-setup-v0.1.2...template-designer-pro-setup-v0.1.3) (2026-02-15)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* new frontend system autodetection ([#3](https://github.com/dx-labs-com/template-designer-pro-setup/issues/3)) ([dce94f9](https://github.com/dx-labs-com/template-designer-pro-setup/commit/dce94f92c00066107fb4463d7f68a450599f3bbd))
|
|
7
|
+
|
|
1
8
|
## [0.1.2](https://github.com/dx-labs-com/template-designer-pro-setup/compare/template-designer-pro-setup-v0.1.1...template-designer-pro-setup-v0.1.2) (2026-02-03)
|
|
2
9
|
|
|
3
10
|
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ const patchBackendIndex_1 = require("./steps/patchBackendIndex");
|
|
|
16
16
|
const exec_1 = require("./utils/exec");
|
|
17
17
|
const fs_1 = require("./utils/fs");
|
|
18
18
|
const log_1 = require("./utils/log");
|
|
19
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
19
20
|
const DEFAULT_SCOPES = ["@tduniec", "@dx-labs"];
|
|
20
21
|
const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
21
22
|
async function main() {
|
|
@@ -28,6 +29,7 @@ async function main() {
|
|
|
28
29
|
.option("--npmToken <token>", "Token for accessing the private npm registry")
|
|
29
30
|
.option("--registryUrl <url>", "Registry URL", DEFAULT_REGISTRY)
|
|
30
31
|
.option("--scopes <scopes>", "Comma-separated scopes to configure", (value) => value, DEFAULT_SCOPES.join(","))
|
|
32
|
+
.option("--frontendSystem <mode>", "auto (default), legacy, or frontend-system (new Backstage frontend)", "auto")
|
|
31
33
|
.option("--dryRun", "Show actions without writing files or running commands", false)
|
|
32
34
|
.option("--skipInstall", "Skip running yarn install at the end", false)
|
|
33
35
|
.parse(process.argv);
|
|
@@ -45,6 +47,7 @@ async function main() {
|
|
|
45
47
|
: path_1.default.join(root, "app-config.yaml");
|
|
46
48
|
const licenseId = await resolveLicenseId(options.licenseId);
|
|
47
49
|
const npmToken = await resolveNpmToken(options.npmToken);
|
|
50
|
+
const frontendSystem = await resolveFrontendSystem(options.frontendSystem, root);
|
|
48
51
|
const context = {
|
|
49
52
|
root,
|
|
50
53
|
dryRun,
|
|
@@ -54,6 +57,7 @@ async function main() {
|
|
|
54
57
|
registryUrl: options.registryUrl ?? DEFAULT_REGISTRY,
|
|
55
58
|
scopes,
|
|
56
59
|
configPath,
|
|
60
|
+
frontendSystem,
|
|
57
61
|
};
|
|
58
62
|
await (0, ensureNpmrc_1.ensureNpmrc)(context);
|
|
59
63
|
await (0, installPackages_1.installPackages)(context);
|
|
@@ -133,4 +137,33 @@ async function runYarnInstall(context) {
|
|
|
133
137
|
(0, log_1.success)("yarn install completed");
|
|
134
138
|
}
|
|
135
139
|
void main();
|
|
140
|
+
async function resolveFrontendSystem(value, root) {
|
|
141
|
+
if (value) {
|
|
142
|
+
const normalized = value.toLowerCase();
|
|
143
|
+
if (["frontend-system", "fs", "new"].includes(normalized))
|
|
144
|
+
return "frontend-system";
|
|
145
|
+
if (["legacy", "classic"].includes(normalized))
|
|
146
|
+
return "legacy";
|
|
147
|
+
if (normalized !== "auto") {
|
|
148
|
+
throw new Error(`Invalid --frontendSystem value "${value}". Use "auto" (default), "legacy", or "frontend-system".`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Auto-detect: inspect packages/app/src/App.tsx contents when present.
|
|
152
|
+
const appTsxPath = path_1.default.join(root, "packages", "app", "src", "App.tsx");
|
|
153
|
+
if (await (0, fs_1.fileExists)(appTsxPath)) {
|
|
154
|
+
try {
|
|
155
|
+
const contents = await promises_1.default.readFile(appTsxPath, "utf8");
|
|
156
|
+
if (contents.includes("FlatRoutes") || contents.includes("<FlatRoutes")) {
|
|
157
|
+
return "legacy";
|
|
158
|
+
}
|
|
159
|
+
// New frontend system App.tsx typically uses createApp + app.createRoot() without FlatRoutes.
|
|
160
|
+
return "frontend-system";
|
|
161
|
+
}
|
|
162
|
+
catch (_e) {
|
|
163
|
+
// Fallback to legacy if unreadable but present.
|
|
164
|
+
return "legacy";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return "frontend-system";
|
|
168
|
+
}
|
|
136
169
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport path from \"path\";\nimport prompts from \"prompts\";\nimport { CliContext } from \"./types\";\nimport { ensureNpmrc } from \"./steps/ensureNpmrc\";\nimport { installPackages } from \"./steps/installPackages\";\nimport { patchAppConfig } from \"./steps/patchAppConfig\";\nimport { patchAppTsx } from \"./steps/patchAppTsx\";\nimport { patchRootTsx } from \"./steps/patchRootTsx\";\nimport { patchBackendIndex } from \"./steps/patchBackendIndex\";\nimport { runCommand } from \"./utils/exec\";\nimport { fileExists } from \"./utils/fs\";\nimport { error, info, success } from \"./utils/log\";\n\nconst DEFAULT_SCOPES = [\"@tduniec\", \"@dx-labs\"];\nconst DEFAULT_REGISTRY = \"https://registry.npmjs.org\";\n\nasync function main() {\n const program = new Command();\n\n program\n .name(\"template-designer-setup\")\n .description(\"Install and configure Template Designer and License Box plugins for Backstage\")\n .option(\n \"--backstageDir <path>\",\n \"Path to Backstage repo root (defaults to current working directory)\",\n )\n .option(\"--licenseId <id>\", \"License Box license identifier\")\n .option(\"--npmToken <token>\", \"Token for accessing the private npm registry\")\n .option(\"--registryUrl <url>\", \"Registry URL\", DEFAULT_REGISTRY)\n .option(\n \"--scopes <scopes>\",\n \"Comma-separated scopes to configure\",\n (value: string) => value,\n DEFAULT_SCOPES.join(\",\"),\n )\n .option(\"--dryRun\", \"Show actions without writing files or running commands\", false)\n .option(\"--skipInstall\", \"Skip running yarn install at the end\", false)\n .parse(process.argv);\n\n const options = program.opts<{\n licenseId?: string;\n npmToken?: string;\n registryUrl?: string;\n scopes?: string;\n dryRun?: boolean;\n skipInstall?: boolean;\n backstageDir?: string;\n }>();\n\n try {\n const root = options.backstageDir\n ? path.resolve(process.cwd(), options.backstageDir)\n : process.cwd();\n const scopes = parseScopes(options.scopes);\n const dryRun = Boolean(options.dryRun);\n const skipInstall = Boolean(options.skipInstall);\n\n await validateBackstageRoot(root);\n\n const configPath = (await fileExists(path.join(root, \"app-config.local.yaml\")))\n ? path.join(root, \"app-config.local.yaml\")\n : path.join(root, \"app-config.yaml\");\n\n const licenseId = await resolveLicenseId(options.licenseId);\n const npmToken = await resolveNpmToken(options.npmToken);\n\n const context: CliContext = {\n root,\n dryRun,\n skipInstall,\n licenseId,\n npmToken,\n registryUrl: options.registryUrl ?? DEFAULT_REGISTRY,\n scopes,\n configPath,\n };\n\n await ensureNpmrc(context);\n await installPackages(context);\n await patchAppTsx(context);\n await patchRootTsx(context);\n await patchBackendIndex(context);\n await patchAppConfig(context);\n\n if (!skipInstall) {\n await runYarnInstall(context);\n } else {\n info(\"Skipping yarn install (--skipInstall provided)\");\n }\n\n success(\"Template Designer setup completed\");\n info(\"Next: start Backstage to verify → yarn start\");\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n error(message);\n process.exitCode = 1;\n }\n}\n\nasync function validateBackstageRoot(root: string) {\n const requiredPaths = [\n path.join(root, \"packages\", \"app\"),\n path.join(root, \"packages\", \"backend\"),\n path.join(root, \"app-config.yaml\"),\n ];\n\n for (const required of requiredPaths) {\n if (!(await fileExists(required))) {\n throw new Error(\n `This tool must be run from a Backstage repository root. Missing: ${path.relative(\n root,\n required,\n )}`,\n );\n }\n }\n}\n\nfunction parseScopes(scopes?: string): string[] {\n if (!scopes) return DEFAULT_SCOPES;\n return scopes\n .split(\",\")\n .map((scope) => scope.trim())\n .filter((scope) => scope.length > 0);\n}\n\nasync function resolveLicenseId(provided?: string): Promise<string> {\n if (provided) return provided;\n\n const validateLicenseId = (value: string) =>\n value.trim().length > 0 ? true : \"licenseId is required\";\n\n const response = (await prompts({\n type: \"text\",\n name: \"licenseId\",\n message: \"Enter the License Box licenseId\",\n validate: validateLicenseId,\n })) as { licenseId?: string };\n\n const licenseIdRaw: unknown = (response as { licenseId?: unknown }).licenseId;\n const licenseId = typeof licenseIdRaw === \"string\" ? licenseIdRaw.trim() : \"\";\n if (!licenseId) {\n throw new Error(\"licenseId is required\");\n }\n\n return licenseId;\n}\n\nasync function resolveNpmToken(provided?: string): Promise<string | undefined> {\n if (provided) return provided;\n\n const response = (await prompts({\n type: \"password\",\n name: \"npmToken\",\n message: \"Enter npm token for the private registry (optional)\",\n })) as { npmToken?: string };\n\n const tokenRaw: unknown = (response as { npmToken?: unknown }).npmToken;\n const token = typeof tokenRaw === \"string\" ? tokenRaw.trim() : \"\";\n return token.length > 0 ? token : undefined;\n}\n\nasync function runYarnInstall(context: CliContext) {\n await runCommand(\"yarn\", [\"install\"], { cwd: context.root, dryRun: context.dryRun });\n if (context.dryRun) {\n return;\n }\n success(\"yarn install completed\");\n}\n\nvoid main();\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AACA,yCAAoC;AACpC,gDAAwB;AACxB,sDAA8B;AAE9B,qDAAkD;AAClD,6DAA0D;AAC1D,2DAAwD;AACxD,qDAAkD;AAClD,uDAAoD;AACpD,iEAA8D;AAC9D,uCAA0C;AAC1C,mCAAwC;AACxC,qCAAmD;AACnD,2DAA6B;AAE7B,MAAM,cAAc,GAAG,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;AAChD,MAAM,gBAAgB,GAAG,4BAA4B,CAAC;AAEtD,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,yBAAyB,CAAC;SAC/B,WAAW,CAAC,+EAA+E,CAAC;SAC5F,MAAM,CACL,uBAAuB,EACvB,qEAAqE,CACtE;SACA,MAAM,CAAC,kBAAkB,EAAE,gCAAgC,CAAC;SAC5D,MAAM,CAAC,oBAAoB,EAAE,8CAA8C,CAAC;SAC5E,MAAM,CAAC,qBAAqB,EAAE,cAAc,EAAE,gBAAgB,CAAC;SAC/D,MAAM,CACL,mBAAmB,EACnB,qCAAqC,EACrC,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,EACxB,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CACzB;SACA,MAAM,CACL,yBAAyB,EACzB,qEAAqE,EACrE,MAAM,CACP;SACA,MAAM,CAAC,UAAU,EAAE,wDAAwD,EAAE,KAAK,CAAC;SACnF,MAAM,CAAC,eAAe,EAAE,sCAAsC,EAAE,KAAK,CAAC;SACtE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EASxB,CAAC;IAEL,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY;YAC/B,CAAC,CAAC,cAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,YAAY,CAAC;YACnD,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAEjD,MAAM,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAElC,MAAM,UAAU,GAAG,CAAC,MAAM,IAAA,eAAU,EAAC,cAAI,CAAC,IAAI,CAAC,IAAI,EAAE,uBAAuB,CAAC,CAAC,CAAC;YAC7E,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,IAAI,EAAE,uBAAuB,CAAC;YAC1C,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;QAEvC,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAEzD,MAAM,cAAc,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;QAEjF,MAAM,OAAO,GAAe;YAC1B,IAAI;YACJ,MAAM;YACN,WAAW;YACX,SAAS;YACT,QAAQ;YACR,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,gBAAgB;YACpD,MAAM;YACN,UAAU;YACV,cAAc;SACf,CAAC;QAEF,MAAM,IAAA,yBAAW,EAAC,OAAO,CAAC,CAAC;QAC3B,MAAM,IAAA,iCAAe,EAAC,OAAO,CAAC,CAAC;QAC/B,MAAM,IAAA,yBAAW,EAAC,OAAO,CAAC,CAAC;QAC3B,MAAM,IAAA,2BAAY,EAAC,OAAO,CAAC,CAAC;QAC5B,MAAM,IAAA,qCAAiB,EAAC,OAAO,CAAC,CAAC;QACjC,MAAM,IAAA,+BAAc,EAAC,OAAO,CAAC,CAAC;QAE9B,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,IAAA,UAAI,EAAC,gDAAgD,CAAC,CAAC;QACzD,CAAC;QAED,IAAA,aAAO,EAAC,mCAAmC,CAAC,CAAC;QAC7C,IAAA,UAAI,EAAC,8CAA8C,CAAC,CAAC;IACvD,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,IAAA,WAAK,EAAC,OAAO,CAAC,CAAC;QACf,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAC,IAAY;IAC/C,MAAM,aAAa,GAAG;QACpB,cAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC;QAClC,cAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC;QACtC,cAAI,CAAC,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC;KACnC,CAAC;IAEF,KAAK,MAAM,QAAQ,IAAI,aAAa,EAAE,CAAC;QACrC,IAAI,CAAC,CAAC,MAAM,IAAA,eAAU,EAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CACb,oEAAoE,cAAI,CAAC,QAAQ,CAC/E,IAAI,EACJ,QAAQ,CACT,EAAE,CACJ,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,MAAe;IAClC,IAAI,CAAC,MAAM;QAAE,OAAO,cAAc,CAAC;IACnC,OAAO,MAAM;SACV,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACzC,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,QAAiB;IAC/C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,iBAAiB,GAAG,CAAC,KAAa,EAAE,EAAE,CAC1C,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,uBAAuB,CAAC;IAE3D,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAA,iBAAO,EAAC;QAC9B,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,iCAAiC;QAC1C,QAAQ,EAAE,iBAAiB;KAC5B,CAAC,CAA2B,CAAC;IAE9B,MAAM,YAAY,GAAa,QAAoC,CAAC,SAAS,CAAC;IAC9E,MAAM,SAAS,GAAG,OAAO,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,QAAiB;IAC9C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAA,iBAAO,EAAC;QAC9B,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,qDAAqD;KAC/D,CAAC,CAA0B,CAAC;IAE7B,MAAM,QAAQ,GAAa,QAAmC,CAAC,QAAQ,CAAC;IACxE,MAAM,KAAK,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAClE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9C,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,OAAmB;IAC/C,MAAM,IAAA,iBAAU,EAAC,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACrF,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO;IACT,CAAC;IACD,IAAA,aAAO,EAAC,wBAAwB,CAAC,CAAC;AACpC,CAAC;AAED,KAAK,IAAI,EAAE,CAAC;AAEZ,KAAK,UAAU,qBAAqB,CAClC,KAAyB,EACzB,IAAY;IAEZ,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QACvC,IAAI,CAAC,iBAAiB,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;YAAE,OAAO,iBAAiB,CAAC;QACpF,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;YAAE,OAAO,QAAQ,CAAC;QAChE,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,mCAAmC,KAAK,0DAA0D,CACnG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,uEAAuE;IACvE,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACxE,IAAI,MAAM,IAAA,eAAU,EAAC,UAAU,CAAC,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YACvD,IAAI,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;gBACxE,OAAO,QAAQ,CAAC;YAClB,CAAC;YACD,8FAA8F;YAC9F,OAAO,iBAAiB,CAAC;QAC3B,CAAC;QAAC,OAAO,EAAE,EAAE,CAAC;YACZ,gDAAgD;YAChD,OAAO,QAAQ,CAAC;QAClB,CAAC;IACH,CAAC;IAED,OAAO,iBAAiB,CAAC;AAC3B,CAAC","sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport path from \"path\";\nimport prompts from \"prompts\";\nimport { CliContext } from \"./types\";\nimport { ensureNpmrc } from \"./steps/ensureNpmrc\";\nimport { installPackages } from \"./steps/installPackages\";\nimport { patchAppConfig } from \"./steps/patchAppConfig\";\nimport { patchAppTsx } from \"./steps/patchAppTsx\";\nimport { patchRootTsx } from \"./steps/patchRootTsx\";\nimport { patchBackendIndex } from \"./steps/patchBackendIndex\";\nimport { runCommand } from \"./utils/exec\";\nimport { fileExists } from \"./utils/fs\";\nimport { error, info, success } from \"./utils/log\";\nimport fs from \"fs/promises\";\n\nconst DEFAULT_SCOPES = [\"@tduniec\", \"@dx-labs\"];\nconst DEFAULT_REGISTRY = \"https://registry.npmjs.org\";\n\nasync function main() {\n const program = new Command();\n\n program\n .name(\"template-designer-setup\")\n .description(\"Install and configure Template Designer and License Box plugins for Backstage\")\n .option(\n \"--backstageDir <path>\",\n \"Path to Backstage repo root (defaults to current working directory)\",\n )\n .option(\"--licenseId <id>\", \"License Box license identifier\")\n .option(\"--npmToken <token>\", \"Token for accessing the private npm registry\")\n .option(\"--registryUrl <url>\", \"Registry URL\", DEFAULT_REGISTRY)\n .option(\n \"--scopes <scopes>\",\n \"Comma-separated scopes to configure\",\n (value: string) => value,\n DEFAULT_SCOPES.join(\",\"),\n )\n .option(\n \"--frontendSystem <mode>\",\n \"auto (default), legacy, or frontend-system (new Backstage frontend)\",\n \"auto\",\n )\n .option(\"--dryRun\", \"Show actions without writing files or running commands\", false)\n .option(\"--skipInstall\", \"Skip running yarn install at the end\", false)\n .parse(process.argv);\n\n const options = program.opts<{\n licenseId?: string;\n npmToken?: string;\n registryUrl?: string;\n scopes?: string;\n dryRun?: boolean;\n skipInstall?: boolean;\n backstageDir?: string;\n frontendSystem?: string;\n }>();\n\n try {\n const root = options.backstageDir\n ? path.resolve(process.cwd(), options.backstageDir)\n : process.cwd();\n const scopes = parseScopes(options.scopes);\n const dryRun = Boolean(options.dryRun);\n const skipInstall = Boolean(options.skipInstall);\n\n await validateBackstageRoot(root);\n\n const configPath = (await fileExists(path.join(root, \"app-config.local.yaml\")))\n ? path.join(root, \"app-config.local.yaml\")\n : path.join(root, \"app-config.yaml\");\n\n const licenseId = await resolveLicenseId(options.licenseId);\n const npmToken = await resolveNpmToken(options.npmToken);\n\n const frontendSystem = await resolveFrontendSystem(options.frontendSystem, root);\n\n const context: CliContext = {\n root,\n dryRun,\n skipInstall,\n licenseId,\n npmToken,\n registryUrl: options.registryUrl ?? DEFAULT_REGISTRY,\n scopes,\n configPath,\n frontendSystem,\n };\n\n await ensureNpmrc(context);\n await installPackages(context);\n await patchAppTsx(context);\n await patchRootTsx(context);\n await patchBackendIndex(context);\n await patchAppConfig(context);\n\n if (!skipInstall) {\n await runYarnInstall(context);\n } else {\n info(\"Skipping yarn install (--skipInstall provided)\");\n }\n\n success(\"Template Designer setup completed\");\n info(\"Next: start Backstage to verify → yarn start\");\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n error(message);\n process.exitCode = 1;\n }\n}\n\nasync function validateBackstageRoot(root: string) {\n const requiredPaths = [\n path.join(root, \"packages\", \"app\"),\n path.join(root, \"packages\", \"backend\"),\n path.join(root, \"app-config.yaml\"),\n ];\n\n for (const required of requiredPaths) {\n if (!(await fileExists(required))) {\n throw new Error(\n `This tool must be run from a Backstage repository root. Missing: ${path.relative(\n root,\n required,\n )}`,\n );\n }\n }\n}\n\nfunction parseScopes(scopes?: string): string[] {\n if (!scopes) return DEFAULT_SCOPES;\n return scopes\n .split(\",\")\n .map((scope) => scope.trim())\n .filter((scope) => scope.length > 0);\n}\n\nasync function resolveLicenseId(provided?: string): Promise<string> {\n if (provided) return provided;\n\n const validateLicenseId = (value: string) =>\n value.trim().length > 0 ? true : \"licenseId is required\";\n\n const response = (await prompts({\n type: \"text\",\n name: \"licenseId\",\n message: \"Enter the License Box licenseId\",\n validate: validateLicenseId,\n })) as { licenseId?: string };\n\n const licenseIdRaw: unknown = (response as { licenseId?: unknown }).licenseId;\n const licenseId = typeof licenseIdRaw === \"string\" ? licenseIdRaw.trim() : \"\";\n if (!licenseId) {\n throw new Error(\"licenseId is required\");\n }\n\n return licenseId;\n}\n\nasync function resolveNpmToken(provided?: string): Promise<string | undefined> {\n if (provided) return provided;\n\n const response = (await prompts({\n type: \"password\",\n name: \"npmToken\",\n message: \"Enter npm token for the private registry (optional)\",\n })) as { npmToken?: string };\n\n const tokenRaw: unknown = (response as { npmToken?: unknown }).npmToken;\n const token = typeof tokenRaw === \"string\" ? tokenRaw.trim() : \"\";\n return token.length > 0 ? token : undefined;\n}\n\nasync function runYarnInstall(context: CliContext) {\n await runCommand(\"yarn\", [\"install\"], { cwd: context.root, dryRun: context.dryRun });\n if (context.dryRun) {\n return;\n }\n success(\"yarn install completed\");\n}\n\nvoid main();\n\nasync function resolveFrontendSystem(\n value: string | undefined,\n root: string,\n): Promise<\"legacy\" | \"frontend-system\"> {\n if (value) {\n const normalized = value.toLowerCase();\n if ([\"frontend-system\", \"fs\", \"new\"].includes(normalized)) return \"frontend-system\";\n if ([\"legacy\", \"classic\"].includes(normalized)) return \"legacy\";\n if (normalized !== \"auto\") {\n throw new Error(\n `Invalid --frontendSystem value \"${value}\". Use \"auto\" (default), \"legacy\", or \"frontend-system\".`,\n );\n }\n }\n\n // Auto-detect: inspect packages/app/src/App.tsx contents when present.\n const appTsxPath = path.join(root, \"packages\", \"app\", \"src\", \"App.tsx\");\n if (await fileExists(appTsxPath)) {\n try {\n const contents = await fs.readFile(appTsxPath, \"utf8\");\n if (contents.includes(\"FlatRoutes\") || contents.includes(\"<FlatRoutes\")) {\n return \"legacy\";\n }\n // New frontend system App.tsx typically uses createApp + app.createRoot() without FlatRoutes.\n return \"frontend-system\";\n } catch (_e) {\n // Fallback to legacy if unreadable but present.\n return \"legacy\";\n }\n }\n\n return \"frontend-system\";\n}\n"]}
|
|
@@ -13,7 +13,86 @@ const ROUTE_PATH = "/template-designer";
|
|
|
13
13
|
const ROUTE_SNIPPET = `<Route path="${ROUTE_PATH}" element={<TemplateDesignerProPage />} />`;
|
|
14
14
|
const IMPORT_NAME = "TemplateDesignerProPage";
|
|
15
15
|
const IMPORT_SOURCE = "@dx-labs/plugin-template-designer-pro";
|
|
16
|
+
const FS_FEATURE_IMPORT = "templateDesignerPro";
|
|
17
|
+
const FS_FEATURE_SOURCE = "@dx-labs/plugin-template-designer-pro/alpha";
|
|
16
18
|
async function patchAppTsx(context) {
|
|
19
|
+
if (context.frontendSystem === "frontend-system") {
|
|
20
|
+
const appTsxPath = path_1.default.join(context.root, "packages", "app", "src", "App.tsx");
|
|
21
|
+
if (!(await (0, fs_1.fileExists)(appTsxPath))) {
|
|
22
|
+
throw new Error(`Could not find App.tsx at ${appTsxPath}`);
|
|
23
|
+
}
|
|
24
|
+
(0, log_1.step)("Patching packages/app/src/App.tsx for Frontend System (feature injection)");
|
|
25
|
+
const { sourceFile } = (0, ast_1.loadSourceFile)(appTsxPath);
|
|
26
|
+
const importChanged = (0, ast_1.ensureDefaultImport)(sourceFile, FS_FEATURE_IMPORT, FS_FEATURE_SOURCE);
|
|
27
|
+
const createAppCall = sourceFile
|
|
28
|
+
.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression)
|
|
29
|
+
.find((call) => call.getExpression().getText() === "createApp");
|
|
30
|
+
if (!createAppCall) {
|
|
31
|
+
(0, log_1.warn)("Could not find createApp(...) call in App.tsx. Add templateDesignerPro() to features manually.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const firstArg = createAppCall.getArguments()[0];
|
|
35
|
+
if (!ts_morph_1.Node.isObjectLiteralExpression(firstArg)) {
|
|
36
|
+
(0, log_1.warn)("createApp call does not have an object literal config. Please add feature manually.");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const featuresProp = firstArg.getProperties().find((p) => {
|
|
40
|
+
if (!ts_morph_1.Node.isPropertyAssignment(p))
|
|
41
|
+
return false;
|
|
42
|
+
if (p.getNameNode().getText() !== "features")
|
|
43
|
+
return false;
|
|
44
|
+
const init = p.getInitializer();
|
|
45
|
+
return Boolean(init && ts_morph_1.Node.isArrayLiteralExpression(init));
|
|
46
|
+
});
|
|
47
|
+
let featuresArray;
|
|
48
|
+
if (!featuresProp) {
|
|
49
|
+
const init = firstArg.addPropertyAssignment({
|
|
50
|
+
name: "features",
|
|
51
|
+
initializer: "[]",
|
|
52
|
+
}).getInitializer();
|
|
53
|
+
if (ts_morph_1.Node.isArrayLiteralExpression(init)) {
|
|
54
|
+
featuresArray = init;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
(0, log_1.warn)("Unable to create features array in createApp config.");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const init = featuresProp.getInitializer();
|
|
63
|
+
if (ts_morph_1.Node.isArrayLiteralExpression(init)) {
|
|
64
|
+
featuresArray = init;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
(0, log_1.warn)("features property exists but is not an array; cannot inject feature automatically.");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!featuresArray) {
|
|
72
|
+
(0, log_1.warn)("Could not locate features array to inject templateDesignerPro().");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const alreadyPresent = featuresArray
|
|
76
|
+
.getElements()
|
|
77
|
+
.some((el) => el.getText().startsWith(`${FS_FEATURE_IMPORT}(`));
|
|
78
|
+
if (!alreadyPresent) {
|
|
79
|
+
featuresArray.addElement(`${FS_FEATURE_IMPORT}()`);
|
|
80
|
+
}
|
|
81
|
+
if (context.dryRun) {
|
|
82
|
+
(0, log_1.dryRun)(importChanged || !alreadyPresent
|
|
83
|
+
? "Would inject templateDesignerPro() feature into createApp"
|
|
84
|
+
: "No changes needed in App.tsx for frontend-system");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (importChanged || !alreadyPresent) {
|
|
88
|
+
await sourceFile.save();
|
|
89
|
+
(0, log_1.success)("Injected templateDesignerPro() feature into createApp");
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
(0, log_1.success)("App.tsx already includes templateDesignerPro() feature");
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
17
96
|
const appTsxPath = path_1.default.join(context.root, "packages", "app", "src", "App.tsx");
|
|
18
97
|
if (!(await (0, fs_1.fileExists)(appTsxPath))) {
|
|
19
98
|
throw new Error(`Could not find App.tsx at ${appTsxPath}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patchAppTsx.js","sourceRoot":"","sources":["../../src/steps/patchAppTsx.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"path\";\nimport { CliContext } from \"../types\";\nimport { fileExists } from \"../utils/fs\";\nimport { findJsxElements, hasJsxAttribute, ensureNamedImport, loadSourceFile } from \"../utils/ast\";\nimport { dryRun, success, step } from \"../utils/log\";\nimport { SyntaxKind, JsxElement, JsxSelfClosingElement } from \"ts-morph\";\n\nconst ROUTE_PATH = \"/template-designer\";\nconst ROUTE_SNIPPET = `<Route path=\"${ROUTE_PATH}\" element={<TemplateDesignerProPage />} />`;\nconst IMPORT_NAME = \"TemplateDesignerProPage\";\nconst IMPORT_SOURCE = \"@dx-labs/plugin-template-designer-pro\";\n\nexport async function patchAppTsx(context: CliContext) {\n const appTsxPath = path.join(context.root, \"packages\", \"app\", \"src\", \"App.tsx\");\n if (!(await fileExists(appTsxPath))) {\n throw new Error(`Could not find App.tsx at ${appTsxPath}`);\n }\n\n step(\"Patching packages/app/src/App.tsx\");\n const { sourceFile } = loadSourceFile(appTsxPath);\n\n const importsChanged = ensureNamedImport(sourceFile, IMPORT_NAME, IMPORT_SOURCE);\n const flatRoutes = findJsxElements(sourceFile, \"FlatRoutes\");\n\n const targetFlatRoutes =\n flatRoutes.find((el) =>\n el.getDescendants().some((child) => child.getKindName().includes(\"Route\")),\n ) || flatRoutes[0];\n\n if (!targetFlatRoutes) {\n throw new Error(\n \"Could not locate <FlatRoutes> in App.tsx. Please add the route manually and rerun.\",\n );\n }\n\n const descendantRoutes: Array<JsxElement | JsxSelfClosingElement> = [\n ...targetFlatRoutes.getDescendantsOfKind(SyntaxKind.JsxElement),\n ...targetFlatRoutes.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement),\n ].filter((node) => {\n const tagName =\n node.getKind() === SyntaxKind.JsxElement\n ? (node as JsxElement).getOpeningElement().getTagNameNode().getText()\n : (node as JsxSelfClosingElement).getTagNameNode().getText();\n return tagName === \"Route\";\n });\n\n const existingRoute = descendantRoutes.some((route) =>\n hasJsxAttribute(route, \"path\", (value) => value === ROUTE_PATH),\n );\n\n let routeAdded = false;\n if (!existingRoute) {\n const closing = targetFlatRoutes.getClosingElement();\n if (!closing) {\n throw new Error(\"FlatRoutes element is not well-formed (missing closing tag).\");\n }\n const insertion = `\\n ${ROUTE_SNIPPET}\\n `;\n sourceFile.insertText(closing.getStart(), insertion);\n routeAdded = true;\n }\n\n if (context.dryRun) {\n dryRun(\n importsChanged || routeAdded\n ? \"Would update App.tsx with Template Designer import and route\"\n : \"No changes needed in App.tsx\",\n );\n return;\n }\n\n if (importsChanged || routeAdded) {\n await sourceFile.save();\n success(\"Updated App.tsx with Template Designer route\");\n } else {\n success(\"App.tsx already has Template Designer route\");\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"patchAppTsx.js","sourceRoot":"","sources":["../../src/steps/patchAppTsx.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"path\";\nimport { CliContext } from \"../types\";\nimport { fileExists } from \"../utils/fs\";\nimport {\n findJsxElements,\n hasJsxAttribute,\n ensureNamedImport,\n ensureDefaultImport,\n loadSourceFile,\n} from \"../utils/ast\";\nimport { dryRun, success, step, warn } from \"../utils/log\";\nimport { SyntaxKind, JsxElement, JsxSelfClosingElement, Node, ArrayLiteralExpression } from \"ts-morph\";\n\nconst ROUTE_PATH = \"/template-designer\";\nconst ROUTE_SNIPPET = `<Route path=\"${ROUTE_PATH}\" element={<TemplateDesignerProPage />} />`;\nconst IMPORT_NAME = \"TemplateDesignerProPage\";\nconst IMPORT_SOURCE = \"@dx-labs/plugin-template-designer-pro\";\nconst FS_FEATURE_IMPORT = \"templateDesignerPro\";\nconst FS_FEATURE_SOURCE = \"@dx-labs/plugin-template-designer-pro/alpha\";\n\nexport async function patchAppTsx(context: CliContext) {\n if (context.frontendSystem === \"frontend-system\") {\n const appTsxPath = path.join(context.root, \"packages\", \"app\", \"src\", \"App.tsx\");\n if (!(await fileExists(appTsxPath))) {\n throw new Error(`Could not find App.tsx at ${appTsxPath}`);\n }\n step(\"Patching packages/app/src/App.tsx for Frontend System (feature injection)\");\n const { sourceFile } = loadSourceFile(appTsxPath);\n\n const importChanged = ensureDefaultImport(sourceFile, FS_FEATURE_IMPORT, FS_FEATURE_SOURCE);\n\n const createAppCall = sourceFile\n .getDescendantsOfKind(SyntaxKind.CallExpression)\n .find((call) => call.getExpression().getText() === \"createApp\");\n\n if (!createAppCall) {\n warn(\n \"Could not find createApp(...) call in App.tsx. Add templateDesignerPro() to features manually.\",\n );\n return;\n }\n\n const firstArg = createAppCall.getArguments()[0];\n if (!Node.isObjectLiteralExpression(firstArg)) {\n warn(\"createApp call does not have an object literal config. Please add feature manually.\");\n return;\n }\n\n const featuresProp = firstArg.getProperties().find((p): p is typeof p & import(\"ts-morph\").PropertyAssignment => {\n if (!Node.isPropertyAssignment(p)) return false;\n if (p.getNameNode().getText() !== \"features\") return false;\n const init = p.getInitializer();\n return Boolean(init && Node.isArrayLiteralExpression(init));\n });\n\n let featuresArray: ArrayLiteralExpression | undefined;\n\n if (!featuresProp) {\n const init = firstArg.addPropertyAssignment({\n name: \"features\",\n initializer: \"[]\",\n }).getInitializer();\n if (Node.isArrayLiteralExpression(init)) {\n featuresArray = init;\n } else {\n warn(\"Unable to create features array in createApp config.\");\n return;\n }\n } else {\n const init = featuresProp.getInitializer();\n if (Node.isArrayLiteralExpression(init)) {\n featuresArray = init;\n } else {\n warn(\"features property exists but is not an array; cannot inject feature automatically.\");\n return;\n }\n }\n\n if (!featuresArray) {\n warn(\"Could not locate features array to inject templateDesignerPro().\");\n return;\n }\n\n const alreadyPresent = featuresArray\n .getElements()\n .some((el) => el.getText().startsWith(`${FS_FEATURE_IMPORT}(`));\n\n if (!alreadyPresent) {\n featuresArray.addElement(`${FS_FEATURE_IMPORT}()`);\n }\n\n if (context.dryRun) {\n dryRun(\n importChanged || !alreadyPresent\n ? \"Would inject templateDesignerPro() feature into createApp\"\n : \"No changes needed in App.tsx for frontend-system\",\n );\n return;\n }\n\n if (importChanged || !alreadyPresent) {\n await sourceFile.save();\n success(\"Injected templateDesignerPro() feature into createApp\");\n } else {\n success(\"App.tsx already includes templateDesignerPro() feature\");\n }\n return;\n }\n\n const appTsxPath = path.join(context.root, \"packages\", \"app\", \"src\", \"App.tsx\");\n if (!(await fileExists(appTsxPath))) {\n throw new Error(`Could not find App.tsx at ${appTsxPath}`);\n }\n\n step(\"Patching packages/app/src/App.tsx\");\n const { sourceFile } = loadSourceFile(appTsxPath);\n\n const importsChanged = ensureNamedImport(sourceFile, IMPORT_NAME, IMPORT_SOURCE);\n const flatRoutes = findJsxElements(sourceFile, \"FlatRoutes\");\n\n const targetFlatRoutes =\n flatRoutes.find((el) =>\n el.getDescendants().some((child) => child.getKindName().includes(\"Route\")),\n ) || flatRoutes[0];\n\n if (!targetFlatRoutes) {\n throw new Error(\n \"Could not locate <FlatRoutes> in App.tsx. Please add the route manually and rerun.\",\n );\n }\n\n const descendantRoutes: Array<JsxElement | JsxSelfClosingElement> = [\n ...targetFlatRoutes.getDescendantsOfKind(SyntaxKind.JsxElement),\n ...targetFlatRoutes.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement),\n ].filter((node) => {\n const tagName =\n node.getKind() === SyntaxKind.JsxElement\n ? (node as JsxElement).getOpeningElement().getTagNameNode().getText()\n : (node as JsxSelfClosingElement).getTagNameNode().getText();\n return tagName === \"Route\";\n });\n\n const existingRoute = descendantRoutes.some((route) =>\n hasJsxAttribute(route, \"path\", (value) => value === ROUTE_PATH),\n );\n\n let routeAdded = false;\n if (!existingRoute) {\n const closing = targetFlatRoutes.getClosingElement();\n if (!closing) {\n throw new Error(\"FlatRoutes element is not well-formed (missing closing tag).\");\n }\n const insertion = `\\n ${ROUTE_SNIPPET}\\n `;\n sourceFile.insertText(closing.getStart(), insertion);\n routeAdded = true;\n }\n\n if (context.dryRun) {\n dryRun(\n importsChanged || routeAdded\n ? \"Would update App.tsx with Template Designer import and route\"\n : \"No changes needed in App.tsx\",\n );\n return;\n }\n\n if (importsChanged || routeAdded) {\n await sourceFile.save();\n success(\"Updated App.tsx with Template Designer route\");\n } else {\n success(\"App.tsx already has Template Designer route\");\n }\n}\n"]}
|
|
@@ -15,6 +15,10 @@ const ICON_SOURCE = "@dx-labs/plugin-template-designer-pro";
|
|
|
15
15
|
const SIDEBAR_ITEM_IMPORT = "SidebarItem";
|
|
16
16
|
const SIDEBAR_ITEM_SOURCE = "@backstage/core-components";
|
|
17
17
|
async function patchRootTsx(context) {
|
|
18
|
+
if (context.frontendSystem === "frontend-system") {
|
|
19
|
+
(0, log_1.info)("Skipping Root.tsx patch (frontend-system autowires nav items from plugin).");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
18
22
|
const rootTsxPath = path_1.default.join(context.root, "packages", "app", "src", "components", "Root", "Root.tsx");
|
|
19
23
|
if (!(await (0, fs_1.fileExists)(rootTsxPath))) {
|
|
20
24
|
throw new Error(`Could not find Root.tsx at ${rootTsxPath}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patchRootTsx.js","sourceRoot":"","sources":["../../src/steps/patchRootTsx.ts"],"names":[],"mappings":";;;;;AAcA,
|
|
1
|
+
{"version":3,"file":"patchRootTsx.js","sourceRoot":"","sources":["../../src/steps/patchRootTsx.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"path\";\nimport { SyntaxKind, JsxElement, JsxSelfClosingElement } from \"ts-morph\";\nimport { CliContext } from \"../types\";\nimport { fileExists } from \"../utils/fs\";\nimport { ensureNamedImport, findJsxElements, hasJsxAttribute, loadSourceFile } from \"../utils/ast\";\nimport { dryRun, info, success, step } from \"../utils/log\";\n\nconst ITEM_SNIPPET =\n '<SidebarItem icon={TemplateDesignerIcon} to=\"template-designer\" text=\"Template Designer\" />';\nconst ICON_IMPORT = \"TemplateDesignerIcon\";\nconst ICON_SOURCE = \"@dx-labs/plugin-template-designer-pro\";\nconst SIDEBAR_ITEM_IMPORT = \"SidebarItem\";\nconst SIDEBAR_ITEM_SOURCE = \"@backstage/core-components\";\n\nexport async function patchRootTsx(context: CliContext) {\n if (context.frontendSystem === \"frontend-system\") {\n info(\"Skipping Root.tsx patch (frontend-system autowires nav items from plugin).\");\n return;\n }\n\n const rootTsxPath = path.join(\n context.root,\n \"packages\",\n \"app\",\n \"src\",\n \"components\",\n \"Root\",\n \"Root.tsx\",\n );\n if (!(await fileExists(rootTsxPath))) {\n throw new Error(`Could not find Root.tsx at ${rootTsxPath}`);\n }\n\n step(\"Patching packages/app/src/components/Root/Root.tsx\");\n const { sourceFile } = loadSourceFile(rootTsxPath);\n\n const sidebarItemImportChanged = ensureNamedImport(\n sourceFile,\n SIDEBAR_ITEM_IMPORT,\n SIDEBAR_ITEM_SOURCE,\n );\n const iconImportChanged = ensureNamedImport(sourceFile, ICON_IMPORT, ICON_SOURCE);\n const importsChanged = sidebarItemImportChanged || iconImportChanged;\n\n const sidebarItems: Array<JsxElement | JsxSelfClosingElement> = [\n ...sourceFile.getDescendantsOfKind(SyntaxKind.JsxElement),\n ...sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement),\n ].filter((node) => {\n const tagName =\n node.getKind() === SyntaxKind.JsxElement\n ? (node as JsxElement).getOpeningElement().getTagNameNode().getText()\n : (node as JsxSelfClosingElement).getTagNameNode().getText();\n return tagName === \"SidebarItem\";\n });\n\n const existingItem = sidebarItems.some(\n (item) =>\n hasJsxAttribute(item, \"to\", (value) => value.includes(\"template-designer\")) ||\n hasJsxAttribute(item, \"text\", (value) => value.toLowerCase().includes(\"template designer\")),\n );\n\n let itemAdded = false;\n if (!existingItem) {\n const sidebarGroups = findJsxElements(sourceFile, \"SidebarGroup\");\n const menuGroup =\n sidebarGroups.find((group) =>\n hasJsxAttribute(group, \"label\", (value) => value.toLowerCase().includes(\"menu\")),\n ) ||\n sidebarGroups.find((group) =>\n group.getJsxChildren().some((child) => child.getText().includes(\"SidebarItem\")),\n ) ||\n sidebarGroups[0];\n\n const sidebar = findJsxElements(sourceFile, \"Sidebar\")[0];\n\n const targetContainer = menuGroup || sidebar;\n\n if (!targetContainer) {\n throw new Error(\n \"Could not find a Sidebar or SidebarGroup in Root.tsx. Add the sidebar item manually and rerun.\",\n );\n }\n\n const closing = targetContainer.getClosingElement();\n if (!closing) {\n throw new Error(\"Sidebar container is not well-formed (missing closing tag).\");\n }\n const baseIndent = sourceFile.getIndentationText() ?? \" \";\n const insertion = `\\n${baseIndent}${ITEM_SNIPPET}\\n`;\n sourceFile.insertText(closing.getStart(), insertion);\n itemAdded = true;\n }\n\n if (context.dryRun) {\n dryRun(\n importsChanged || itemAdded\n ? \"Would update Root.tsx with Template Designer sidebar link\"\n : \"No changes needed in Root.tsx\",\n );\n return;\n }\n\n if (importsChanged || itemAdded) {\n await sourceFile.save();\n success(\"Updated Root.tsx with Template Designer sidebar item\");\n } else {\n success(\"Root.tsx already has Template Designer sidebar item\");\n }\n}\n"]}
|
package/dist/types.d.ts
CHANGED
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type CliContext = {\n root: string;\n dryRun: boolean;\n skipInstall: boolean;\n licenseId: string;\n npmToken?: string;\n registryUrl: string;\n scopes: string[];\n configPath: string;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type CliContext = {\n root: string;\n dryRun: boolean;\n skipInstall: boolean;\n licenseId: string;\n npmToken?: string;\n registryUrl: string;\n scopes: string[];\n configPath: string;\n frontendSystem: \"legacy\" | \"frontend-system\";\n};\n"]}
|
package/dist/utils/ast.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export declare function loadSourceFile(filePath: string): {
|
|
|
4
4
|
sourceFile: SourceFile;
|
|
5
5
|
};
|
|
6
6
|
export declare function ensureNamedImport(sourceFile: SourceFile, imported: string, moduleSpecifier: string): boolean;
|
|
7
|
+
export declare function ensureDefaultImport(sourceFile: SourceFile, alias: string, moduleSpecifier: string): boolean;
|
|
7
8
|
export declare function findJsxElements(sourceFile: SourceFile, tagName: string): JsxElement[];
|
|
8
9
|
export declare function findJsxSelfClosingElements(sourceFile: SourceFile, tagName: string): JsxSelfClosingElement[];
|
|
9
10
|
export declare function hasJsxAttribute(element: JsxElement | JsxSelfClosingElement, attributeName: string, predicate?: (value: string) => boolean): boolean;
|
package/dist/utils/ast.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.loadSourceFile = loadSourceFile;
|
|
4
4
|
exports.ensureNamedImport = ensureNamedImport;
|
|
5
|
+
exports.ensureDefaultImport = ensureDefaultImport;
|
|
5
6
|
exports.findJsxElements = findJsxElements;
|
|
6
7
|
exports.findJsxSelfClosingElements = findJsxSelfClosingElements;
|
|
7
8
|
exports.hasJsxAttribute = hasJsxAttribute;
|
|
@@ -32,6 +33,22 @@ function ensureNamedImport(sourceFile, imported, moduleSpecifier) {
|
|
|
32
33
|
});
|
|
33
34
|
return true;
|
|
34
35
|
}
|
|
36
|
+
function ensureDefaultImport(sourceFile, alias, moduleSpecifier) {
|
|
37
|
+
const existing = sourceFile.getImportDeclaration((d) => d.getModuleSpecifierValue() === moduleSpecifier);
|
|
38
|
+
if (existing) {
|
|
39
|
+
const defaultImport = existing.getDefaultImport();
|
|
40
|
+
if (defaultImport && defaultImport.getText() === alias) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
existing.setDefaultImport(alias);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
sourceFile.addImportDeclaration({
|
|
47
|
+
defaultImport: alias,
|
|
48
|
+
moduleSpecifier,
|
|
49
|
+
});
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
35
52
|
function findJsxElements(sourceFile, tagName) {
|
|
36
53
|
return sourceFile
|
|
37
54
|
.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxElement)
|
package/dist/utils/ast.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../../src/utils/ast.ts"],"names":[],"mappings":";;AAaA,wCAUC;AAED,8CAoBC;AAED,0CAIC;AAED,gEAOC;AAED,0CAoBC;
|
|
1
|
+
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../../src/utils/ast.ts"],"names":[],"mappings":";;AAaA,wCAUC;AAED,8CAoBC;AAED,kDAqBC;AAED,0CAIC;AAED,gEAOC;AAED,0CAoBC;AAzGD,uCAWkB;AAElB,SAAgB,cAAc,CAAC,QAAgB;IAC7C,MAAM,OAAO,GAAG,IAAI,kBAAO,CAAC;QAC1B,oBAAoB,EAAE;YACpB,SAAS,EAAE,oBAAS,CAAC,MAAM;YAC3B,iBAAiB,EAAE,IAAI;YACvB,eAAe,EAAE,0BAAe,CAAC,SAAS;SAC3C;KACF,CAAC,CAAC;IACH,MAAM,UAAU,GAAG,OAAO,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACzD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;AACjC,CAAC;AAED,SAAgB,iBAAiB,CAC/B,UAAsB,EACtB,QAAgB,EAChB,eAAuB;IAEvB,MAAM,QAAQ,GAAG,UAAU,CAAC,oBAAoB,CAC9C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,uBAAuB,EAAE,KAAK,eAAe,CACvD,CAAC;IACF,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,SAAS,GAAG,QAAQ,CAAC,eAAe,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC;QACnF,IAAI,SAAS;YAAE,OAAO,KAAK,CAAC;QAC5B,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,UAAU,CAAC,oBAAoB,CAAC;QAC9B,YAAY,EAAE,CAAC,QAAQ,CAAC;QACxB,eAAe;KAChB,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,mBAAmB,CACjC,UAAsB,EACtB,KAAa,EACb,eAAuB;IAEvB,MAAM,QAAQ,GAAG,UAAU,CAAC,oBAAoB,CAC9C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,uBAAuB,EAAE,KAAK,eAAe,CACvD,CAAC;IACF,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,aAAa,GAAG,QAAQ,CAAC,gBAAgB,EAAE,CAAC;QAClD,IAAI,aAAa,IAAI,aAAa,CAAC,OAAO,EAAE,KAAK,KAAK,EAAE,CAAC;YACvD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,QAAQ,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,UAAU,CAAC,oBAAoB,CAAC;QAC9B,aAAa,EAAE,KAAK;QACpB,eAAe;KAChB,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,eAAe,CAAC,UAAsB,EAAE,OAAe;IACrE,OAAO,UAAU;SACd,oBAAoB,CAAC,qBAAU,CAAC,UAAU,CAAC;SAC3C,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,cAAc,EAAE,CAAC,OAAO,EAAE,KAAK,OAAO,CAAC,CAAC;AACnF,CAAC;AAED,SAAgB,0BAA0B,CACxC,UAAsB,EACtB,OAAe;IAEf,OAAO,UAAU;SACd,oBAAoB,CAAC,qBAAU,CAAC,qBAAqB,CAAC;SACtD,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,OAAO,EAAE,KAAK,OAAO,CAAC,CAAC;AAC/D,CAAC;AAED,SAAgB,eAAe,CAC7B,OAA2C,EAC3C,aAAqB,EACrB,SAAsC;IAEtC,MAAM,UAAU,GAAuB,eAAI,CAAC,YAAY,CAAC,OAAO,CAAC;QAC/D,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,aAAa,EAAE;QAC7C,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,IAAI,CAAC,eAAI,CAAC,cAAc,CAAC,IAAI,CAAC;YAAE,SAAS;QACzC,MAAM,OAAO,GAAiB,IAAI,CAAC;QACnC,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,CAAC;QACjD,IAAI,QAAQ,KAAK,aAAa;YAAE,SAAS;QACzC,MAAM,IAAI,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;QACtC,IAAI,CAAC,IAAI;YAAE,OAAO,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACnD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import {\n JsxElement,\n JsxSelfClosingElement,\n Project,\n QuoteKind,\n SourceFile,\n SyntaxKind,\n IndentationText,\n Node,\n JsxAttributeLike,\n JsxAttribute,\n} from \"ts-morph\";\n\nexport function loadSourceFile(filePath: string): { project: Project; sourceFile: SourceFile } {\n const project = new Project({\n manipulationSettings: {\n quoteKind: QuoteKind.Double,\n useTrailingCommas: true,\n indentationText: IndentationText.TwoSpaces,\n },\n });\n const sourceFile = project.addSourceFileAtPath(filePath);\n return { project, sourceFile };\n}\n\nexport function ensureNamedImport(\n sourceFile: SourceFile,\n imported: string,\n moduleSpecifier: string,\n): boolean {\n const existing = sourceFile.getImportDeclaration(\n (d) => d.getModuleSpecifierValue() === moduleSpecifier,\n );\n if (existing) {\n const hasImport = existing.getNamedImports().some((i) => i.getName() === imported);\n if (hasImport) return false;\n existing.addNamedImport(imported);\n return true;\n }\n\n sourceFile.addImportDeclaration({\n namedImports: [imported],\n moduleSpecifier,\n });\n return true;\n}\n\nexport function ensureDefaultImport(\n sourceFile: SourceFile,\n alias: string,\n moduleSpecifier: string,\n): boolean {\n const existing = sourceFile.getImportDeclaration(\n (d) => d.getModuleSpecifierValue() === moduleSpecifier,\n );\n if (existing) {\n const defaultImport = existing.getDefaultImport();\n if (defaultImport && defaultImport.getText() === alias) {\n return false;\n }\n existing.setDefaultImport(alias);\n return true;\n }\n sourceFile.addImportDeclaration({\n defaultImport: alias,\n moduleSpecifier,\n });\n return true;\n}\n\nexport function findJsxElements(sourceFile: SourceFile, tagName: string): JsxElement[] {\n return sourceFile\n .getDescendantsOfKind(SyntaxKind.JsxElement)\n .filter((el) => el.getOpeningElement().getTagNameNode().getText() === tagName);\n}\n\nexport function findJsxSelfClosingElements(\n sourceFile: SourceFile,\n tagName: string,\n): JsxSelfClosingElement[] {\n return sourceFile\n .getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement)\n .filter((el) => el.getTagNameNode().getText() === tagName);\n}\n\nexport function hasJsxAttribute(\n element: JsxElement | JsxSelfClosingElement,\n attributeName: string,\n predicate?: (value: string) => boolean,\n): boolean {\n const attributes: JsxAttributeLike[] = Node.isJsxElement(element)\n ? element.getOpeningElement().getAttributes()\n : element.getAttributes();\n\n for (const attr of attributes) {\n if (!Node.isJsxAttribute(attr)) continue;\n const jsxAttr: JsxAttribute = attr;\n const attrName = jsxAttr.getNameNode().getText();\n if (attrName !== attributeName) continue;\n const init = jsxAttr.getInitializer();\n if (!init) return predicate ? predicate(\"\") : true;\n const text = init.getText().replace(/['\"`{}]/g, \"\");\n if (!predicate || predicate(text)) return true;\n }\n return false;\n}\n"]}
|
package/package.json
CHANGED