@dx-labs/template-designer-setup 0.1.2
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 +20 -0
- package/README.md +44 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +136 -0
- package/dist/index.js.map +1 -0
- package/dist/steps/ensureNpmrc.d.ts +2 -0
- package/dist/steps/ensureNpmrc.js +127 -0
- package/dist/steps/ensureNpmrc.js.map +1 -0
- package/dist/steps/installPackages.d.ts +2 -0
- package/dist/steps/installPackages.js +23 -0
- package/dist/steps/installPackages.js.map +1 -0
- package/dist/steps/patchAppConfig.d.ts +2 -0
- package/dist/steps/patchAppConfig.js +42 -0
- package/dist/steps/patchAppConfig.js.map +1 -0
- package/dist/steps/patchAppTsx.d.ts +2 -0
- package/dist/steps/patchAppTsx.js +63 -0
- package/dist/steps/patchAppTsx.js.map +1 -0
- package/dist/steps/patchBackendIndex.d.ts +2 -0
- package/dist/steps/patchBackendIndex.js +89 -0
- package/dist/steps/patchBackendIndex.js.map +1 -0
- package/dist/steps/patchRootTsx.d.ts +2 -0
- package/dist/steps/patchRootTsx.js +72 -0
- package/dist/steps/patchRootTsx.js.map +1 -0
- package/dist/types.d.ts +10 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/ast.d.ts +9 -0
- package/dist/utils/ast.js +65 -0
- package/dist/utils/ast.js.map +1 -0
- package/dist/utils/exec.d.ts +7 -0
- package/dist/utils/exec.js +22 -0
- package/dist/utils/exec.js.map +1 -0
- package/dist/utils/fs.d.ts +8 -0
- package/dist/utils/fs.js +48 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/log.d.ts +7 -0
- package/dist/utils/log.js +47 -0
- package/dist/utils/log.js.map +1 -0
- package/dist/utils/yaml.d.ts +4 -0
- package/dist/utils/yaml.js +20 -0
- package/dist/utils/yaml.js.map +1 -0
- package/package.json +69 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## [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
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* release package ([53ba47f](https://github.com/dx-labs-com/template-designer-pro-setup/commit/53ba47f204eedbc40cb698860208969592ccd670))
|
|
7
|
+
|
|
8
|
+
## [0.1.1](https://github.com/dx-labs-com/template-designer-pro-setup/compare/template-designer-pro-setup-v0.1.0...template-designer-pro-setup-v0.1.1) (2026-02-03)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* release package ([7a2211f](https://github.com/dx-labs-com/template-designer-pro-setup/commit/7a2211f1074716c59d43a4741fcf5302a85af2d7))
|
|
14
|
+
|
|
15
|
+
# [0.1.0](https://github.com/dx-labs-com/template-designer-pro-setup/compare/template-designer-pro-setup-v0.0.1...template-designer-pro-setup-v0.1.0) (2026-02-03)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* publish template-designer-pro-setup ([f716d0f](https://github.com/dx-labs-com/template-designer-pro-setup/commit/f716d0f6972873ebfac3a68ee47d76abf175ccab))
|
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Template Designer Setup CLI
|
|
2
|
+
|
|
3
|
+
CLI that installs and wires the Template Designer frontend and License Box backend plugins into a Backstage instance. It can be published and used via `npx @dx-labs/template-designer-setup`.
|
|
4
|
+
|
|
5
|
+
## Build
|
|
6
|
+
|
|
7
|
+
- From repo root: `yarn install`
|
|
8
|
+
- Build the CLI: `yarn workspace @dx-labs/template-designer-setup build`
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
Run from the Backstage repo root (must contain `packages/app`, `packages/backend`, and `app-config.yaml`).
|
|
13
|
+
|
|
14
|
+
- Dry run (no writes): `node npx/dist/index.js --dryRun --licenseId LIC-12345`
|
|
15
|
+
- Full run with prompts for missing values: `npx @dx-labs/template-designer-setup --licenseId LIC-12345`
|
|
16
|
+
- Run against a different Backstage path: `npx @dx-labs/template-designer-setup --backstageDir ../my-backstage --licenseId LIC-12345`
|
|
17
|
+
- Provide registry details explicitly: `npx @dx-labs/template-designer-setup --licenseId LIC-12345 --npmToken TOKEN --registryUrl https://registry.npmjs.org --scopes @tduniec,@dx-labs`
|
|
18
|
+
- Skip final install: `npx @dx-labs/template-designer-setup --licenseId LIC-12345 --skipInstall`
|
|
19
|
+
|
|
20
|
+
Flags:
|
|
21
|
+
|
|
22
|
+
- `--licenseId <value>` (required, prompts if missing)
|
|
23
|
+
- `--npmToken <value>` (optional, prompts if missing)
|
|
24
|
+
- `--registryUrl <url>` defaults to `https://registry.npmjs.org`
|
|
25
|
+
- `--scopes <@scope1,@scope2>` defaults to `@tduniec,@dx-labs`
|
|
26
|
+
- `--backstageDir <path>` to target a Backstage repo (default: current working directory)
|
|
27
|
+
- `--dryRun` shows planned actions only
|
|
28
|
+
- `--skipInstall` skips `yarn install` after patching
|
|
29
|
+
|
|
30
|
+
## What it does
|
|
31
|
+
|
|
32
|
+
1. Installs `@dx-labs/plugin-template-designer-pro` in `packages/app`
|
|
33
|
+
2. Installs `@dx-labs/plugin-license-box` in `packages/backend`
|
|
34
|
+
3. Adds Template Designer route to `packages/app/src/App.tsx` using ts-morph (idempotent)
|
|
35
|
+
4. Adds sidebar entry with `TemplateDesignerIcon` to `packages/app/src/components/Root/Root.tsx` (idempotent)
|
|
36
|
+
5. Writes `dxLicenseBox.license.key` to `app-config.local.yaml` when present, otherwise `app-config.yaml`
|
|
37
|
+
6. Configures `.npmrc` with the provided scopes, registry, and auth token (token is masked in logs)
|
|
38
|
+
7. Runs `yarn install` unless `--skipInstall` is passed
|
|
39
|
+
|
|
40
|
+
## Publish (optional)
|
|
41
|
+
|
|
42
|
+
- Bump the version in `npx/package.json` if needed
|
|
43
|
+
- Build: `yarn workspace @dx-labs/template-designer-setup build`
|
|
44
|
+
- Publish from `npx/`: `cd npx && npm publish --access public`
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
10
|
+
const ensureNpmrc_1 = require("./steps/ensureNpmrc");
|
|
11
|
+
const installPackages_1 = require("./steps/installPackages");
|
|
12
|
+
const patchAppConfig_1 = require("./steps/patchAppConfig");
|
|
13
|
+
const patchAppTsx_1 = require("./steps/patchAppTsx");
|
|
14
|
+
const patchRootTsx_1 = require("./steps/patchRootTsx");
|
|
15
|
+
const patchBackendIndex_1 = require("./steps/patchBackendIndex");
|
|
16
|
+
const exec_1 = require("./utils/exec");
|
|
17
|
+
const fs_1 = require("./utils/fs");
|
|
18
|
+
const log_1 = require("./utils/log");
|
|
19
|
+
const DEFAULT_SCOPES = ["@tduniec", "@dx-labs"];
|
|
20
|
+
const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
21
|
+
async function main() {
|
|
22
|
+
const program = new commander_1.Command();
|
|
23
|
+
program
|
|
24
|
+
.name("template-designer-setup")
|
|
25
|
+
.description("Install and configure Template Designer and License Box plugins for Backstage")
|
|
26
|
+
.option("--backstageDir <path>", "Path to Backstage repo root (defaults to current working directory)")
|
|
27
|
+
.option("--licenseId <id>", "License Box license identifier")
|
|
28
|
+
.option("--npmToken <token>", "Token for accessing the private npm registry")
|
|
29
|
+
.option("--registryUrl <url>", "Registry URL", DEFAULT_REGISTRY)
|
|
30
|
+
.option("--scopes <scopes>", "Comma-separated scopes to configure", (value) => value, DEFAULT_SCOPES.join(","))
|
|
31
|
+
.option("--dryRun", "Show actions without writing files or running commands", false)
|
|
32
|
+
.option("--skipInstall", "Skip running yarn install at the end", false)
|
|
33
|
+
.parse(process.argv);
|
|
34
|
+
const options = program.opts();
|
|
35
|
+
try {
|
|
36
|
+
const root = options.backstageDir
|
|
37
|
+
? path_1.default.resolve(process.cwd(), options.backstageDir)
|
|
38
|
+
: process.cwd();
|
|
39
|
+
const scopes = parseScopes(options.scopes);
|
|
40
|
+
const dryRun = Boolean(options.dryRun);
|
|
41
|
+
const skipInstall = Boolean(options.skipInstall);
|
|
42
|
+
await validateBackstageRoot(root);
|
|
43
|
+
const configPath = (await (0, fs_1.fileExists)(path_1.default.join(root, "app-config.local.yaml")))
|
|
44
|
+
? path_1.default.join(root, "app-config.local.yaml")
|
|
45
|
+
: path_1.default.join(root, "app-config.yaml");
|
|
46
|
+
const licenseId = await resolveLicenseId(options.licenseId);
|
|
47
|
+
const npmToken = await resolveNpmToken(options.npmToken);
|
|
48
|
+
const context = {
|
|
49
|
+
root,
|
|
50
|
+
dryRun,
|
|
51
|
+
skipInstall,
|
|
52
|
+
licenseId,
|
|
53
|
+
npmToken,
|
|
54
|
+
registryUrl: options.registryUrl ?? DEFAULT_REGISTRY,
|
|
55
|
+
scopes,
|
|
56
|
+
configPath,
|
|
57
|
+
};
|
|
58
|
+
await (0, ensureNpmrc_1.ensureNpmrc)(context);
|
|
59
|
+
await (0, installPackages_1.installPackages)(context);
|
|
60
|
+
await (0, patchAppTsx_1.patchAppTsx)(context);
|
|
61
|
+
await (0, patchRootTsx_1.patchRootTsx)(context);
|
|
62
|
+
await (0, patchBackendIndex_1.patchBackendIndex)(context);
|
|
63
|
+
await (0, patchAppConfig_1.patchAppConfig)(context);
|
|
64
|
+
if (!skipInstall) {
|
|
65
|
+
await runYarnInstall(context);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
(0, log_1.info)("Skipping yarn install (--skipInstall provided)");
|
|
69
|
+
}
|
|
70
|
+
(0, log_1.success)("Template Designer setup completed");
|
|
71
|
+
(0, log_1.info)("Next: start Backstage to verify → yarn start");
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
75
|
+
(0, log_1.error)(message);
|
|
76
|
+
process.exitCode = 1;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function validateBackstageRoot(root) {
|
|
80
|
+
const requiredPaths = [
|
|
81
|
+
path_1.default.join(root, "packages", "app"),
|
|
82
|
+
path_1.default.join(root, "packages", "backend"),
|
|
83
|
+
path_1.default.join(root, "app-config.yaml"),
|
|
84
|
+
];
|
|
85
|
+
for (const required of requiredPaths) {
|
|
86
|
+
if (!(await (0, fs_1.fileExists)(required))) {
|
|
87
|
+
throw new Error(`This tool must be run from a Backstage repository root. Missing: ${path_1.default.relative(root, required)}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function parseScopes(scopes) {
|
|
92
|
+
if (!scopes)
|
|
93
|
+
return DEFAULT_SCOPES;
|
|
94
|
+
return scopes
|
|
95
|
+
.split(",")
|
|
96
|
+
.map((scope) => scope.trim())
|
|
97
|
+
.filter((scope) => scope.length > 0);
|
|
98
|
+
}
|
|
99
|
+
async function resolveLicenseId(provided) {
|
|
100
|
+
if (provided)
|
|
101
|
+
return provided;
|
|
102
|
+
const validateLicenseId = (value) => value.trim().length > 0 ? true : "licenseId is required";
|
|
103
|
+
const response = (await (0, prompts_1.default)({
|
|
104
|
+
type: "text",
|
|
105
|
+
name: "licenseId",
|
|
106
|
+
message: "Enter the License Box licenseId",
|
|
107
|
+
validate: validateLicenseId,
|
|
108
|
+
}));
|
|
109
|
+
const licenseIdRaw = response.licenseId;
|
|
110
|
+
const licenseId = typeof licenseIdRaw === "string" ? licenseIdRaw.trim() : "";
|
|
111
|
+
if (!licenseId) {
|
|
112
|
+
throw new Error("licenseId is required");
|
|
113
|
+
}
|
|
114
|
+
return licenseId;
|
|
115
|
+
}
|
|
116
|
+
async function resolveNpmToken(provided) {
|
|
117
|
+
if (provided)
|
|
118
|
+
return provided;
|
|
119
|
+
const response = (await (0, prompts_1.default)({
|
|
120
|
+
type: "password",
|
|
121
|
+
name: "npmToken",
|
|
122
|
+
message: "Enter npm token for the private registry (optional)",
|
|
123
|
+
}));
|
|
124
|
+
const tokenRaw = response.npmToken;
|
|
125
|
+
const token = typeof tokenRaw === "string" ? tokenRaw.trim() : "";
|
|
126
|
+
return token.length > 0 ? token : undefined;
|
|
127
|
+
}
|
|
128
|
+
async function runYarnInstall(context) {
|
|
129
|
+
await (0, exec_1.runCommand)("yarn", ["install"], { cwd: context.root, dryRun: context.dryRun });
|
|
130
|
+
if (context.dryRun) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
(0, log_1.success)("yarn install completed");
|
|
134
|
+
}
|
|
135
|
+
void main();
|
|
136
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ensureNpmrc = ensureNpmrc;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const fs_1 = require("../utils/fs");
|
|
9
|
+
const log_1 = require("../utils/log");
|
|
10
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
11
|
+
const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
12
|
+
async function ensureNpmrc(context) {
|
|
13
|
+
const npmrcPath = path_1.default.join(context.root, ".npmrc");
|
|
14
|
+
const registryUrl = context.registryUrl || DEFAULT_REGISTRY;
|
|
15
|
+
const scopes = context.scopes;
|
|
16
|
+
const npmToken = context.npmToken;
|
|
17
|
+
(0, log_1.step)("Ensuring .npmrc is configured");
|
|
18
|
+
const existing = (await (0, fs_1.readFileIfExists)(npmrcPath)) ?? "";
|
|
19
|
+
const lines = existing.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
20
|
+
const additions = [];
|
|
21
|
+
scopes.forEach((scope) => {
|
|
22
|
+
const trimmedScope = scope.trim();
|
|
23
|
+
if (!trimmedScope.startsWith("@"))
|
|
24
|
+
return;
|
|
25
|
+
const scopeLine = `${trimmedScope}:registry=${registryUrl}`;
|
|
26
|
+
const alreadyPresent = lines.some((line) => line.startsWith(`${trimmedScope}:registry=`));
|
|
27
|
+
if (!alreadyPresent) {
|
|
28
|
+
additions.push(scopeLine);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
if (npmToken) {
|
|
32
|
+
const registryHost = toRegistryHost(registryUrl);
|
|
33
|
+
const tokenLine = `//${registryHost}:_authToken=${npmToken}`;
|
|
34
|
+
const hasToken = lines.some((line) => line.startsWith(`//${registryHost}:_authToken=`));
|
|
35
|
+
if (!hasToken) {
|
|
36
|
+
additions.push(tokenLine);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (additions.length === 0) {
|
|
40
|
+
if (context.dryRun) {
|
|
41
|
+
(0, log_1.dryRun)(".npmrc already contains required registry settings");
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
(0, log_1.success)(".npmrc already contains required registry settings");
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const nextContent = buildContent(existing, additions);
|
|
49
|
+
if (context.dryRun) {
|
|
50
|
+
(0, log_1.dryRun)(`Would update .npmrc with scopes ${scopes.join(", ")}${npmToken ? " and auth token" : ""}`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
await (0, fs_1.writeFileSafely)(npmrcPath, nextContent);
|
|
54
|
+
(0, log_1.success)(`.npmrc updated (${additions.length} entr${additions.length === 1 ? "y" : "ies"} added${npmToken ? `, token ${(0, log_1.mask)(npmToken)}` : ""})`);
|
|
55
|
+
await ensureYarnrcYml(context, registryUrl, scopes, npmToken);
|
|
56
|
+
}
|
|
57
|
+
function toRegistryHost(registryUrl) {
|
|
58
|
+
try {
|
|
59
|
+
const url = new URL(registryUrl);
|
|
60
|
+
const path = url.pathname.endsWith("/") ? url.pathname.slice(1) : `${url.pathname.slice(1)}/`;
|
|
61
|
+
return `${url.host}/${path}`;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return registryUrl.replace(/^https?:\/\//, "").replace(/\/?$/, "/");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function buildContent(existing, additions) {
|
|
68
|
+
let content = existing.trimEnd();
|
|
69
|
+
if (content.length > 0 && !content.endsWith("\n")) {
|
|
70
|
+
content += "\n";
|
|
71
|
+
}
|
|
72
|
+
else if (content.length > 0) {
|
|
73
|
+
// keep as is
|
|
74
|
+
}
|
|
75
|
+
content += additions.join("\n");
|
|
76
|
+
if (!content.endsWith("\n")) {
|
|
77
|
+
content += "\n";
|
|
78
|
+
}
|
|
79
|
+
return content;
|
|
80
|
+
}
|
|
81
|
+
async function ensureYarnrcYml(context, registryUrl, scopes, npmToken) {
|
|
82
|
+
const yarnrcPath = path_1.default.join(context.root, ".yarnrc.yml");
|
|
83
|
+
const existingContent = (await (0, fs_1.readFileIfExists)(yarnrcPath)) ?? "";
|
|
84
|
+
let doc = existingContent
|
|
85
|
+
? yaml_1.default.parse(existingContent)
|
|
86
|
+
: {};
|
|
87
|
+
if (!doc || typeof doc !== "object")
|
|
88
|
+
doc = {};
|
|
89
|
+
const npmScopes = doc.npmScopes ?? {};
|
|
90
|
+
doc.npmScopes = npmScopes;
|
|
91
|
+
let changed = false;
|
|
92
|
+
scopes
|
|
93
|
+
.map((s) => s.replace(/^@/, ""))
|
|
94
|
+
.filter((s) => s.length > 0)
|
|
95
|
+
.forEach((scope) => {
|
|
96
|
+
const current = npmScopes[scope] || {};
|
|
97
|
+
const desired = {
|
|
98
|
+
npmRegistryServer: registryUrl,
|
|
99
|
+
};
|
|
100
|
+
if (npmToken) {
|
|
101
|
+
desired.npmAuthToken = npmToken;
|
|
102
|
+
desired.npmAlwaysAuth = true;
|
|
103
|
+
}
|
|
104
|
+
const next = { ...current, ...desired };
|
|
105
|
+
if (JSON.stringify(current) !== JSON.stringify(next)) {
|
|
106
|
+
npmScopes[scope] = next;
|
|
107
|
+
changed = true;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
if (!changed) {
|
|
111
|
+
if (context.dryRun) {
|
|
112
|
+
(0, log_1.dryRun)(".yarnrc.yml already contains required scope registry settings");
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
(0, log_1.success)(".yarnrc.yml already contains required scope registry settings");
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const yamlOut = yaml_1.default.stringify(doc).trimEnd() + "\n";
|
|
120
|
+
if (context.dryRun) {
|
|
121
|
+
(0, log_1.dryRun)("Would update .yarnrc.yml with scope registry/token configuration");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
await (0, fs_1.writeFileSafely)(yarnrcPath, yamlOut);
|
|
125
|
+
(0, log_1.success)(`.yarnrc.yml updated for scopes ${scopes.join(", ")}${npmToken ? ` (token ${(0, log_1.mask)(npmToken)})` : ""}`);
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=ensureNpmrc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ensureNpmrc.js","sourceRoot":"","sources":["../../src/steps/ensureNpmrc.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"path\";\nimport { CliContext } from \"../types\";\nimport { readFileIfExists, writeFileSafely } from \"../utils/fs\";\nimport { dryRun, mask, success, step } from \"../utils/log\";\nimport YAML from \"yaml\";\n\nconst DEFAULT_REGISTRY = \"https://registry.npmjs.org\";\n\nexport async function ensureNpmrc(context: CliContext) {\n const npmrcPath = path.join(context.root, \".npmrc\");\n const registryUrl = context.registryUrl || DEFAULT_REGISTRY;\n const scopes = context.scopes;\n const npmToken = context.npmToken;\n\n step(\"Ensuring .npmrc is configured\");\n const existing = (await readFileIfExists(npmrcPath)) ?? \"\";\n const lines = existing.split(/\\r?\\n/).filter((line) => line.trim().length > 0);\n\n const additions: string[] = [];\n\n scopes.forEach((scope) => {\n const trimmedScope = scope.trim();\n if (!trimmedScope.startsWith(\"@\")) return;\n const scopeLine = `${trimmedScope}:registry=${registryUrl}`;\n const alreadyPresent = lines.some((line) => line.startsWith(`${trimmedScope}:registry=`));\n if (!alreadyPresent) {\n additions.push(scopeLine);\n }\n });\n\n if (npmToken) {\n const registryHost = toRegistryHost(registryUrl);\n const tokenLine = `//${registryHost}:_authToken=${npmToken}`;\n const hasToken = lines.some((line) => line.startsWith(`//${registryHost}:_authToken=`));\n if (!hasToken) {\n additions.push(tokenLine);\n }\n }\n\n if (additions.length === 0) {\n if (context.dryRun) {\n dryRun(\".npmrc already contains required registry settings\");\n } else {\n success(\".npmrc already contains required registry settings\");\n }\n return;\n }\n\n const nextContent = buildContent(existing, additions);\n\n if (context.dryRun) {\n dryRun(\n `Would update .npmrc with scopes ${scopes.join(\", \")}${npmToken ? \" and auth token\" : \"\"}`,\n );\n return;\n }\n\n await writeFileSafely(npmrcPath, nextContent);\n success(\n `.npmrc updated (${additions.length} entr${additions.length === 1 ? \"y\" : \"ies\"} added${\n npmToken ? `, token ${mask(npmToken)}` : \"\"\n })`,\n );\n\n await ensureYarnrcYml(context, registryUrl, scopes, npmToken);\n}\n\nfunction toRegistryHost(registryUrl: string): string {\n try {\n const url = new URL(registryUrl);\n const path = url.pathname.endsWith(\"/\") ? url.pathname.slice(1) : `${url.pathname.slice(1)}/`;\n return `${url.host}/${path}`;\n } catch {\n return registryUrl.replace(/^https?:\\/\\//, \"\").replace(/\\/?$/, \"/\");\n }\n}\n\nfunction buildContent(existing: string, additions: string[]): string {\n let content = existing.trimEnd();\n if (content.length > 0 && !content.endsWith(\"\\n\")) {\n content += \"\\n\";\n } else if (content.length > 0) {\n // keep as is\n }\n content += additions.join(\"\\n\");\n if (!content.endsWith(\"\\n\")) {\n content += \"\\n\";\n }\n return content;\n}\n\ntype YarnScopeConfig = {\n npmRegistryServer: string;\n npmAuthToken?: string;\n npmAlwaysAuth?: boolean;\n};\n\nasync function ensureYarnrcYml(\n context: CliContext,\n registryUrl: string,\n scopes: string[],\n npmToken?: string,\n) {\n const yarnrcPath = path.join(context.root, \".yarnrc.yml\");\n const existingContent = (await readFileIfExists(yarnrcPath)) ?? \"\";\n let doc: Record<string, unknown> = existingContent\n ? (YAML.parse(existingContent) as Record<string, unknown>)\n : {};\n if (!doc || typeof doc !== \"object\") doc = {};\n\n const npmScopes = (doc.npmScopes as Record<string, YarnScopeConfig>) ?? {};\n doc.npmScopes = npmScopes;\n\n let changed = false;\n\n scopes\n .map((s) => s.replace(/^@/, \"\"))\n .filter((s) => s.length > 0)\n .forEach((scope) => {\n const current = npmScopes[scope] || {};\n const desired: YarnScopeConfig = {\n npmRegistryServer: registryUrl,\n };\n if (npmToken) {\n desired.npmAuthToken = npmToken;\n desired.npmAlwaysAuth = true;\n }\n\n const next = { ...current, ...desired };\n if (JSON.stringify(current) !== JSON.stringify(next)) {\n npmScopes[scope] = next;\n changed = true;\n }\n });\n\n if (!changed) {\n if (context.dryRun) {\n dryRun(\".yarnrc.yml already contains required scope registry settings\");\n } else {\n success(\".yarnrc.yml already contains required scope registry settings\");\n }\n return;\n }\n\n const yamlOut = YAML.stringify(doc).trimEnd() + \"\\n\";\n if (context.dryRun) {\n dryRun(\"Would update .yarnrc.yml with scope registry/token configuration\");\n return;\n }\n\n await writeFileSafely(yarnrcPath, yamlOut);\n success(\n `.yarnrc.yml updated for scopes ${scopes.join(\", \")}${npmToken ? ` (token ${mask(npmToken)})` : \"\"}`,\n );\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.installPackages = installPackages;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const exec_1 = require("../utils/exec");
|
|
9
|
+
const log_1 = require("../utils/log");
|
|
10
|
+
async function installPackages(context) {
|
|
11
|
+
const appDir = path_1.default.join(context.root, "packages", "app");
|
|
12
|
+
const backendDir = path_1.default.join(context.root, "packages", "backend");
|
|
13
|
+
(0, log_1.step)("Installing frontend plugin @dx-labs/plugin-template-designer-pro");
|
|
14
|
+
await (0, exec_1.runCommand)("yarn", ["--cwd", appDir, "add", "@dx-labs/plugin-template-designer-pro"], {
|
|
15
|
+
dryRun: context.dryRun,
|
|
16
|
+
});
|
|
17
|
+
(0, log_1.step)("Installing backend plugin @dx-labs/plugin-license-box");
|
|
18
|
+
await (0, exec_1.runCommand)("yarn", ["--cwd", backendDir, "add", "@dx-labs/plugin-license-box"], {
|
|
19
|
+
dryRun: context.dryRun,
|
|
20
|
+
});
|
|
21
|
+
(0, log_1.success)("Dependencies ensured");
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=installPackages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installPackages.js","sourceRoot":"","sources":["../../src/steps/installPackages.ts"],"names":[],"mappings":";;;;;AAKA,0CAeC;AApBD,gDAAwB;AAExB,wCAA2C;AAC3C,sCAA6C;AAEtC,KAAK,UAAU,eAAe,CAAC,OAAmB;IACvD,MAAM,MAAM,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;IAElE,IAAA,UAAI,EAAC,kEAAkE,CAAC,CAAC;IACzE,MAAM,IAAA,iBAAU,EAAC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,uCAAuC,CAAC,EAAE;QAC1F,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IAEH,IAAA,UAAI,EAAC,uDAAuD,CAAC,CAAC;IAC9D,MAAM,IAAA,iBAAU,EAAC,MAAM,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,6BAA6B,CAAC,EAAE;QACpF,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IAEH,IAAA,aAAO,EAAC,sBAAsB,CAAC,CAAC;AAClC,CAAC","sourcesContent":["import path from \"path\";\nimport { CliContext } from \"../types\";\nimport { runCommand } from \"../utils/exec\";\nimport { success, step } from \"../utils/log\";\n\nexport async function installPackages(context: CliContext) {\n const appDir = path.join(context.root, \"packages\", \"app\");\n const backendDir = path.join(context.root, \"packages\", \"backend\");\n\n step(\"Installing frontend plugin @dx-labs/plugin-template-designer-pro\");\n await runCommand(\"yarn\", [\"--cwd\", appDir, \"add\", \"@dx-labs/plugin-template-designer-pro\"], {\n dryRun: context.dryRun,\n });\n\n step(\"Installing backend plugin @dx-labs/plugin-license-box\");\n await runCommand(\"yarn\", [\"--cwd\", backendDir, \"add\", \"@dx-labs/plugin-license-box\"], {\n dryRun: context.dryRun,\n });\n\n success(\"Dependencies ensured\");\n}\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.patchAppConfig = patchAppConfig;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
9
|
+
const fs_1 = require("../utils/fs");
|
|
10
|
+
const log_1 = require("../utils/log");
|
|
11
|
+
const yaml_2 = require("../utils/yaml");
|
|
12
|
+
async function patchAppConfig(context) {
|
|
13
|
+
const targetConfig = context.configPath;
|
|
14
|
+
if (!(await (0, fs_1.fileExists)(targetConfig))) {
|
|
15
|
+
throw new Error(`Config file not found at ${targetConfig}`);
|
|
16
|
+
}
|
|
17
|
+
(0, log_1.step)(`Updating ${path_1.default.relative(context.root, targetConfig)} with license key`);
|
|
18
|
+
const existing = (await (0, yaml_2.readYamlFile)(targetConfig)) ?? {};
|
|
19
|
+
const before = yaml_1.default.stringify(existing);
|
|
20
|
+
const updated = JSON.parse(JSON.stringify(existing));
|
|
21
|
+
const dxLicenseBox = updated.dxLicenseBox ?? {};
|
|
22
|
+
const license = dxLicenseBox.license ?? {};
|
|
23
|
+
license.key = context.licenseId;
|
|
24
|
+
dxLicenseBox.license = license;
|
|
25
|
+
updated.dxLicenseBox = dxLicenseBox;
|
|
26
|
+
const after = yaml_1.default.stringify(updated);
|
|
27
|
+
const changed = before !== after;
|
|
28
|
+
if (context.dryRun) {
|
|
29
|
+
(0, log_1.dryRun)(changed
|
|
30
|
+
? `Would write license key to ${path_1.default.relative(context.root, targetConfig)}`
|
|
31
|
+
: `No changes needed in ${path_1.default.relative(context.root, targetConfig)}`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (changed) {
|
|
35
|
+
await (0, yaml_2.writeYamlFile)(targetConfig, updated);
|
|
36
|
+
(0, log_1.success)(`Wrote license key to ${path_1.default.relative(context.root, targetConfig)}`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
(0, log_1.success)(`License key already present in ${path_1.default.relative(context.root, targetConfig)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=patchAppConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patchAppConfig.js","sourceRoot":"","sources":["../../src/steps/patchAppConfig.ts"],"names":[],"mappings":";;;;;AAOA,wCAqCC;AA5CD,gDAAwB;AACxB,gDAAwB;AAExB,oCAAyC;AACzC,sCAAqD;AACrD,wCAA4D;AAErD,KAAK,UAAU,cAAc,CAAC,OAAmB;IACtD,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IAExC,IAAI,CAAC,CAAC,MAAM,IAAA,eAAU,EAAC,YAAY,CAAC,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,4BAA4B,YAAY,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,IAAA,UAAI,EAAC,YAAY,cAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC,mBAAmB,CAAC,CAAC;IAE/E,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAA,mBAAY,EAA0B,YAAY,CAAC,CAAC,IAAI,EAAE,CAAC;IACnF,MAAM,MAAM,GAAG,cAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAA4B,CAAC;IAEhF,MAAM,YAAY,GAAI,OAAO,CAAC,YAAwC,IAAI,EAAE,CAAC;IAC7E,MAAM,OAAO,GAAI,YAAY,CAAC,OAAmC,IAAI,EAAE,CAAC;IACxE,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC;IAChC,YAAY,CAAC,OAAO,GAAG,OAAO,CAAC;IAC/B,OAAO,CAAC,YAAY,GAAG,YAAY,CAAC;IAEpC,MAAM,KAAK,GAAG,cAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,MAAM,KAAK,KAAK,CAAC;IAEjC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,IAAA,YAAM,EACJ,OAAO;YACL,CAAC,CAAC,8BAA8B,cAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE;YAC3E,CAAC,CAAC,wBAAwB,cAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,CACxE,CAAC;QACF,OAAO;IACT,CAAC;IAED,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,IAAA,oBAAa,EAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAC3C,IAAA,aAAO,EAAC,wBAAwB,cAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC;IAC/E,CAAC;SAAM,CAAC;QACN,IAAA,aAAO,EAAC,kCAAkC,cAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC;IACzF,CAAC;AACH,CAAC","sourcesContent":["import path from \"path\";\nimport YAML from \"yaml\";\nimport { CliContext } from \"../types\";\nimport { fileExists } from \"../utils/fs\";\nimport { dryRun, success, step } from \"../utils/log\";\nimport { readYamlFile, writeYamlFile } from \"../utils/yaml\";\n\nexport async function patchAppConfig(context: CliContext) {\n const targetConfig = context.configPath;\n\n if (!(await fileExists(targetConfig))) {\n throw new Error(`Config file not found at ${targetConfig}`);\n }\n\n step(`Updating ${path.relative(context.root, targetConfig)} with license key`);\n\n const existing = (await readYamlFile<Record<string, unknown>>(targetConfig)) ?? {};\n const before = YAML.stringify(existing);\n const updated = JSON.parse(JSON.stringify(existing)) as Record<string, unknown>;\n\n const dxLicenseBox = (updated.dxLicenseBox as Record<string, unknown>) ?? {};\n const license = (dxLicenseBox.license as Record<string, unknown>) ?? {};\n license.key = context.licenseId;\n dxLicenseBox.license = license;\n updated.dxLicenseBox = dxLicenseBox;\n\n const after = YAML.stringify(updated);\n const changed = before !== after;\n\n if (context.dryRun) {\n dryRun(\n changed\n ? `Would write license key to ${path.relative(context.root, targetConfig)}`\n : `No changes needed in ${path.relative(context.root, targetConfig)}`,\n );\n return;\n }\n\n if (changed) {\n await writeYamlFile(targetConfig, updated);\n success(`Wrote license key to ${path.relative(context.root, targetConfig)}`);\n } else {\n success(`License key already present in ${path.relative(context.root, targetConfig)}`);\n }\n}\n"]}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.patchAppTsx = patchAppTsx;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const fs_1 = require("../utils/fs");
|
|
9
|
+
const ast_1 = require("../utils/ast");
|
|
10
|
+
const log_1 = require("../utils/log");
|
|
11
|
+
const ts_morph_1 = require("ts-morph");
|
|
12
|
+
const ROUTE_PATH = "/template-designer";
|
|
13
|
+
const ROUTE_SNIPPET = `<Route path="${ROUTE_PATH}" element={<TemplateDesignerProPage />} />`;
|
|
14
|
+
const IMPORT_NAME = "TemplateDesignerProPage";
|
|
15
|
+
const IMPORT_SOURCE = "@dx-labs/plugin-template-designer-pro";
|
|
16
|
+
async function patchAppTsx(context) {
|
|
17
|
+
const appTsxPath = path_1.default.join(context.root, "packages", "app", "src", "App.tsx");
|
|
18
|
+
if (!(await (0, fs_1.fileExists)(appTsxPath))) {
|
|
19
|
+
throw new Error(`Could not find App.tsx at ${appTsxPath}`);
|
|
20
|
+
}
|
|
21
|
+
(0, log_1.step)("Patching packages/app/src/App.tsx");
|
|
22
|
+
const { sourceFile } = (0, ast_1.loadSourceFile)(appTsxPath);
|
|
23
|
+
const importsChanged = (0, ast_1.ensureNamedImport)(sourceFile, IMPORT_NAME, IMPORT_SOURCE);
|
|
24
|
+
const flatRoutes = (0, ast_1.findJsxElements)(sourceFile, "FlatRoutes");
|
|
25
|
+
const targetFlatRoutes = flatRoutes.find((el) => el.getDescendants().some((child) => child.getKindName().includes("Route"))) || flatRoutes[0];
|
|
26
|
+
if (!targetFlatRoutes) {
|
|
27
|
+
throw new Error("Could not locate <FlatRoutes> in App.tsx. Please add the route manually and rerun.");
|
|
28
|
+
}
|
|
29
|
+
const descendantRoutes = [
|
|
30
|
+
...targetFlatRoutes.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxElement),
|
|
31
|
+
...targetFlatRoutes.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxSelfClosingElement),
|
|
32
|
+
].filter((node) => {
|
|
33
|
+
const tagName = node.getKind() === ts_morph_1.SyntaxKind.JsxElement
|
|
34
|
+
? node.getOpeningElement().getTagNameNode().getText()
|
|
35
|
+
: node.getTagNameNode().getText();
|
|
36
|
+
return tagName === "Route";
|
|
37
|
+
});
|
|
38
|
+
const existingRoute = descendantRoutes.some((route) => (0, ast_1.hasJsxAttribute)(route, "path", (value) => value === ROUTE_PATH));
|
|
39
|
+
let routeAdded = false;
|
|
40
|
+
if (!existingRoute) {
|
|
41
|
+
const closing = targetFlatRoutes.getClosingElement();
|
|
42
|
+
if (!closing) {
|
|
43
|
+
throw new Error("FlatRoutes element is not well-formed (missing closing tag).");
|
|
44
|
+
}
|
|
45
|
+
const insertion = `\n ${ROUTE_SNIPPET}\n `;
|
|
46
|
+
sourceFile.insertText(closing.getStart(), insertion);
|
|
47
|
+
routeAdded = true;
|
|
48
|
+
}
|
|
49
|
+
if (context.dryRun) {
|
|
50
|
+
(0, log_1.dryRun)(importsChanged || routeAdded
|
|
51
|
+
? "Would update App.tsx with Template Designer import and route"
|
|
52
|
+
: "No changes needed in App.tsx");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (importsChanged || routeAdded) {
|
|
56
|
+
await sourceFile.save();
|
|
57
|
+
(0, log_1.success)("Updated App.tsx with Template Designer route");
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
(0, log_1.success)("App.tsx already has Template Designer route");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=patchAppTsx.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.patchBackendIndex = patchBackendIndex;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const ts_morph_1 = require("ts-morph");
|
|
9
|
+
const fs_1 = require("../utils/fs");
|
|
10
|
+
const ast_1 = require("../utils/ast");
|
|
11
|
+
const log_1 = require("../utils/log");
|
|
12
|
+
const DYNAMIC_IMPORT = "@dx-labs/plugin-license-box";
|
|
13
|
+
async function patchBackendIndex(context) {
|
|
14
|
+
const backendIndexPath = path_1.default.join(context.root, "packages", "backend", "src", "index.ts");
|
|
15
|
+
if (!(await (0, fs_1.fileExists)(backendIndexPath))) {
|
|
16
|
+
throw new Error(`Could not find backend index at ${backendIndexPath}`);
|
|
17
|
+
}
|
|
18
|
+
(0, log_1.step)("Patching packages/backend/src/index.ts");
|
|
19
|
+
const { sourceFile } = (0, ast_1.loadSourceFile)(backendIndexPath);
|
|
20
|
+
const backendAdds = sourceFile.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression).filter((call) => {
|
|
21
|
+
const expr = call.getExpression();
|
|
22
|
+
return expr.getText() === "backend.add";
|
|
23
|
+
});
|
|
24
|
+
const alreadyPresent = backendAdds.some((call) => call.getText().includes(DYNAMIC_IMPORT));
|
|
25
|
+
if (alreadyPresent) {
|
|
26
|
+
if (context.dryRun) {
|
|
27
|
+
(0, log_1.dryRun)("Backend already adds plugin-license-box");
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
(0, log_1.success)("Backend already adds plugin-license-box");
|
|
31
|
+
}
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const insertionTarget = backendAdds[backendAdds.length - 1];
|
|
35
|
+
const insertionText = `backend.add(import('${DYNAMIC_IMPORT}'));`;
|
|
36
|
+
const startCall = sourceFile
|
|
37
|
+
.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression)
|
|
38
|
+
.find((call) => call.getExpression().getText() === "backend.start");
|
|
39
|
+
const targetStatement = startCall?.getFirstAncestorByKind(ts_morph_1.SyntaxKind.ExpressionStatement);
|
|
40
|
+
if (!targetStatement) {
|
|
41
|
+
// Fallback: use previous logic
|
|
42
|
+
if (!insertionTarget) {
|
|
43
|
+
const appendText = `\nbackend.add(import('${DYNAMIC_IMPORT}'));\n`;
|
|
44
|
+
if (context.dryRun) {
|
|
45
|
+
(0, log_1.dryRun)("Would append backend.add(import('@dx-labs/plugin-license-box')) to backend index");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
sourceFile.addStatements(appendText);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
const stmt = insertionTarget.getFirstAncestorByKind(ts_morph_1.SyntaxKind.ExpressionStatement);
|
|
52
|
+
const block = stmt?.getParentIfKind(ts_morph_1.SyntaxKind.Block);
|
|
53
|
+
if (context.dryRun) {
|
|
54
|
+
(0, log_1.dryRun)("Would insert backend.add(import('@dx-labs/plugin-license-box'))");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (stmt && block) {
|
|
58
|
+
const statements = block.getStatements();
|
|
59
|
+
const idx = statements.indexOf(stmt);
|
|
60
|
+
block.insertStatements(idx + 1, insertionText);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
sourceFile.addStatements(`\n${insertionText}\n`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// Insert before backend.start();
|
|
69
|
+
if (context.dryRun) {
|
|
70
|
+
(0, log_1.dryRun)("Would insert backend.add(import('@dx-labs/plugin-license-box')) before backend.start()");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const block = targetStatement.getParentIfKind(ts_morph_1.SyntaxKind.Block);
|
|
74
|
+
if (block) {
|
|
75
|
+
const statements = block.getStatements();
|
|
76
|
+
const idx = statements.indexOf(targetStatement);
|
|
77
|
+
block.insertStatements(idx, `${insertionText}\n`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// As a fallback, insert text directly before the start call statement
|
|
81
|
+
sourceFile.insertText(targetStatement.getStart(), `${insertionText}\n`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (context.dryRun)
|
|
85
|
+
return;
|
|
86
|
+
await sourceFile.save();
|
|
87
|
+
(0, log_1.success)("Added backend.add(import('@dx-labs/plugin-license-box')) to backend");
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=patchBackendIndex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patchBackendIndex.js","sourceRoot":"","sources":["../../src/steps/patchBackendIndex.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"path\";\nimport { SyntaxKind } from \"ts-morph\";\nimport { CliContext } from \"../types\";\nimport { fileExists } from \"../utils/fs\";\nimport { loadSourceFile } from \"../utils/ast\";\nimport { dryRun, success, step } from \"../utils/log\";\n\nconst DYNAMIC_IMPORT = \"@dx-labs/plugin-license-box\";\n\nexport async function patchBackendIndex(context: CliContext) {\n const backendIndexPath = path.join(context.root, \"packages\", \"backend\", \"src\", \"index.ts\");\n if (!(await fileExists(backendIndexPath))) {\n throw new Error(`Could not find backend index at ${backendIndexPath}`);\n }\n\n step(\"Patching packages/backend/src/index.ts\");\n const { sourceFile } = loadSourceFile(backendIndexPath);\n\n const backendAdds = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).filter((call) => {\n const expr = call.getExpression();\n return expr.getText() === \"backend.add\";\n });\n\n const alreadyPresent = backendAdds.some((call) => call.getText().includes(DYNAMIC_IMPORT));\n\n if (alreadyPresent) {\n if (context.dryRun) {\n dryRun(\"Backend already adds plugin-license-box\");\n } else {\n success(\"Backend already adds plugin-license-box\");\n }\n return;\n }\n\n const insertionTarget = backendAdds[backendAdds.length - 1];\n const insertionText = `backend.add(import('${DYNAMIC_IMPORT}'));`;\n\n const startCall = sourceFile\n .getDescendantsOfKind(SyntaxKind.CallExpression)\n .find((call) => call.getExpression().getText() === \"backend.start\");\n\n const targetStatement = startCall?.getFirstAncestorByKind(SyntaxKind.ExpressionStatement);\n\n if (!targetStatement) {\n // Fallback: use previous logic\n if (!insertionTarget) {\n const appendText = `\\nbackend.add(import('${DYNAMIC_IMPORT}'));\\n`;\n if (context.dryRun) {\n dryRun(\"Would append backend.add(import('@dx-labs/plugin-license-box')) to backend index\");\n return;\n }\n sourceFile.addStatements(appendText);\n } else {\n const stmt = insertionTarget.getFirstAncestorByKind(SyntaxKind.ExpressionStatement);\n const block = stmt?.getParentIfKind(SyntaxKind.Block);\n\n if (context.dryRun) {\n dryRun(\"Would insert backend.add(import('@dx-labs/plugin-license-box'))\");\n return;\n }\n\n if (stmt && block) {\n const statements = block.getStatements();\n const idx = statements.indexOf(stmt);\n block.insertStatements(idx + 1, insertionText);\n } else {\n sourceFile.addStatements(`\\n${insertionText}\\n`);\n }\n }\n } else {\n // Insert before backend.start();\n if (context.dryRun) {\n dryRun(\n \"Would insert backend.add(import('@dx-labs/plugin-license-box')) before backend.start()\",\n );\n return;\n }\n const block = targetStatement.getParentIfKind(SyntaxKind.Block);\n if (block) {\n const statements = block.getStatements();\n const idx = statements.indexOf(targetStatement);\n block.insertStatements(idx, `${insertionText}\\n`);\n } else {\n // As a fallback, insert text directly before the start call statement\n sourceFile.insertText(targetStatement.getStart(), `${insertionText}\\n`);\n }\n }\n\n if (context.dryRun) return;\n\n await sourceFile.save();\n success(\"Added backend.add(import('@dx-labs/plugin-license-box')) to backend\");\n}\n"]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.patchRootTsx = patchRootTsx;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const ts_morph_1 = require("ts-morph");
|
|
9
|
+
const fs_1 = require("../utils/fs");
|
|
10
|
+
const ast_1 = require("../utils/ast");
|
|
11
|
+
const log_1 = require("../utils/log");
|
|
12
|
+
const ITEM_SNIPPET = '<SidebarItem icon={TemplateDesignerIcon} to="template-designer" text="Template Designer" />';
|
|
13
|
+
const ICON_IMPORT = "TemplateDesignerIcon";
|
|
14
|
+
const ICON_SOURCE = "@dx-labs/plugin-template-designer-pro";
|
|
15
|
+
const SIDEBAR_ITEM_IMPORT = "SidebarItem";
|
|
16
|
+
const SIDEBAR_ITEM_SOURCE = "@backstage/core-components";
|
|
17
|
+
async function patchRootTsx(context) {
|
|
18
|
+
const rootTsxPath = path_1.default.join(context.root, "packages", "app", "src", "components", "Root", "Root.tsx");
|
|
19
|
+
if (!(await (0, fs_1.fileExists)(rootTsxPath))) {
|
|
20
|
+
throw new Error(`Could not find Root.tsx at ${rootTsxPath}`);
|
|
21
|
+
}
|
|
22
|
+
(0, log_1.step)("Patching packages/app/src/components/Root/Root.tsx");
|
|
23
|
+
const { sourceFile } = (0, ast_1.loadSourceFile)(rootTsxPath);
|
|
24
|
+
const sidebarItemImportChanged = (0, ast_1.ensureNamedImport)(sourceFile, SIDEBAR_ITEM_IMPORT, SIDEBAR_ITEM_SOURCE);
|
|
25
|
+
const iconImportChanged = (0, ast_1.ensureNamedImport)(sourceFile, ICON_IMPORT, ICON_SOURCE);
|
|
26
|
+
const importsChanged = sidebarItemImportChanged || iconImportChanged;
|
|
27
|
+
const sidebarItems = [
|
|
28
|
+
...sourceFile.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxElement),
|
|
29
|
+
...sourceFile.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxSelfClosingElement),
|
|
30
|
+
].filter((node) => {
|
|
31
|
+
const tagName = node.getKind() === ts_morph_1.SyntaxKind.JsxElement
|
|
32
|
+
? node.getOpeningElement().getTagNameNode().getText()
|
|
33
|
+
: node.getTagNameNode().getText();
|
|
34
|
+
return tagName === "SidebarItem";
|
|
35
|
+
});
|
|
36
|
+
const existingItem = sidebarItems.some((item) => (0, ast_1.hasJsxAttribute)(item, "to", (value) => value.includes("template-designer")) ||
|
|
37
|
+
(0, ast_1.hasJsxAttribute)(item, "text", (value) => value.toLowerCase().includes("template designer")));
|
|
38
|
+
let itemAdded = false;
|
|
39
|
+
if (!existingItem) {
|
|
40
|
+
const sidebarGroups = (0, ast_1.findJsxElements)(sourceFile, "SidebarGroup");
|
|
41
|
+
const menuGroup = sidebarGroups.find((group) => (0, ast_1.hasJsxAttribute)(group, "label", (value) => value.toLowerCase().includes("menu"))) ||
|
|
42
|
+
sidebarGroups.find((group) => group.getJsxChildren().some((child) => child.getText().includes("SidebarItem"))) ||
|
|
43
|
+
sidebarGroups[0];
|
|
44
|
+
const sidebar = (0, ast_1.findJsxElements)(sourceFile, "Sidebar")[0];
|
|
45
|
+
const targetContainer = menuGroup || sidebar;
|
|
46
|
+
if (!targetContainer) {
|
|
47
|
+
throw new Error("Could not find a Sidebar or SidebarGroup in Root.tsx. Add the sidebar item manually and rerun.");
|
|
48
|
+
}
|
|
49
|
+
const closing = targetContainer.getClosingElement();
|
|
50
|
+
if (!closing) {
|
|
51
|
+
throw new Error("Sidebar container is not well-formed (missing closing tag).");
|
|
52
|
+
}
|
|
53
|
+
const baseIndent = sourceFile.getIndentationText() ?? " ";
|
|
54
|
+
const insertion = `\n${baseIndent}${ITEM_SNIPPET}\n`;
|
|
55
|
+
sourceFile.insertText(closing.getStart(), insertion);
|
|
56
|
+
itemAdded = true;
|
|
57
|
+
}
|
|
58
|
+
if (context.dryRun) {
|
|
59
|
+
(0, log_1.dryRun)(importsChanged || itemAdded
|
|
60
|
+
? "Would update Root.tsx with Template Designer sidebar link"
|
|
61
|
+
: "No changes needed in Root.tsx");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (importsChanged || itemAdded) {
|
|
65
|
+
await sourceFile.save();
|
|
66
|
+
(0, log_1.success)("Updated Root.tsx with Template Designer sidebar item");
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
(0, log_1.success)("Root.tsx already has Template Designer sidebar item");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=patchRootTsx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patchRootTsx.js","sourceRoot":"","sources":["../../src/steps/patchRootTsx.ts"],"names":[],"mappings":";;;;;AAcA,oCAyFC;AAvGD,gDAAwB;AACxB,uCAAyE;AAEzE,oCAAyC;AACzC,sCAAmG;AACnG,sCAAqD;AAErD,MAAM,YAAY,GAChB,6FAA6F,CAAC;AAChG,MAAM,WAAW,GAAG,sBAAsB,CAAC;AAC3C,MAAM,WAAW,GAAG,uCAAuC,CAAC;AAC5D,MAAM,mBAAmB,GAAG,aAAa,CAAC;AAC1C,MAAM,mBAAmB,GAAG,4BAA4B,CAAC;AAElD,KAAK,UAAU,YAAY,CAAC,OAAmB;IACpD,MAAM,WAAW,GAAG,cAAI,CAAC,IAAI,CAC3B,OAAO,CAAC,IAAI,EACZ,UAAU,EACV,KAAK,EACL,KAAK,EACL,YAAY,EACZ,MAAM,EACN,UAAU,CACX,CAAC;IACF,IAAI,CAAC,CAAC,MAAM,IAAA,eAAU,EAAC,WAAW,CAAC,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,8BAA8B,WAAW,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,IAAA,UAAI,EAAC,oDAAoD,CAAC,CAAC;IAC3D,MAAM,EAAE,UAAU,EAAE,GAAG,IAAA,oBAAc,EAAC,WAAW,CAAC,CAAC;IAEnD,MAAM,wBAAwB,GAAG,IAAA,uBAAiB,EAChD,UAAU,EACV,mBAAmB,EACnB,mBAAmB,CACpB,CAAC;IACF,MAAM,iBAAiB,GAAG,IAAA,uBAAiB,EAAC,UAAU,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;IAClF,MAAM,cAAc,GAAG,wBAAwB,IAAI,iBAAiB,CAAC;IAErE,MAAM,YAAY,GAA8C;QAC9D,GAAG,UAAU,CAAC,oBAAoB,CAAC,qBAAU,CAAC,UAAU,CAAC;QACzD,GAAG,UAAU,CAAC,oBAAoB,CAAC,qBAAU,CAAC,qBAAqB,CAAC;KACrE,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;QAChB,MAAM,OAAO,GACX,IAAI,CAAC,OAAO,EAAE,KAAK,qBAAU,CAAC,UAAU;YACtC,CAAC,CAAE,IAAmB,CAAC,iBAAiB,EAAE,CAAC,cAAc,EAAE,CAAC,OAAO,EAAE;YACrE,CAAC,CAAE,IAA8B,CAAC,cAAc,EAAE,CAAC,OAAO,EAAE,CAAC;QACjE,OAAO,OAAO,KAAK,aAAa,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,CACpC,CAAC,IAAI,EAAE,EAAE,CACP,IAAA,qBAAe,EAAC,IAAI,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;QAC3E,IAAA,qBAAe,EAAC,IAAI,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,CAC9F,CAAC;IAEF,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,aAAa,GAAG,IAAA,qBAAe,EAAC,UAAU,EAAE,cAAc,CAAC,CAAC;QAClE,MAAM,SAAS,GACb,aAAa,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAC3B,IAAA,qBAAe,EAAC,KAAK,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CACjF;YACD,aAAa,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAC3B,KAAK,CAAC,cAAc,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAChF;YACD,aAAa,CAAC,CAAC,CAAC,CAAC;QAEnB,MAAM,OAAO,GAAG,IAAA,qBAAe,EAAC,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QAE1D,MAAM,eAAe,GAAG,SAAS,IAAI,OAAO,CAAC;QAE7C,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CACb,gGAAgG,CACjG,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,eAAe,CAAC,iBAAiB,EAAE,CAAC;QACpD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;QACjF,CAAC;QACD,MAAM,UAAU,GAAG,UAAU,CAAC,kBAAkB,EAAE,IAAI,IAAI,CAAC;QAC3D,MAAM,SAAS,GAAG,KAAK,UAAU,GAAG,YAAY,IAAI,CAAC;QACrD,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,SAAS,CAAC,CAAC;QACrD,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,IAAA,YAAM,EACJ,cAAc,IAAI,SAAS;YACzB,CAAC,CAAC,2DAA2D;YAC7D,CAAC,CAAC,+BAA+B,CACpC,CAAC;QACF,OAAO;IACT,CAAC;IAED,IAAI,cAAc,IAAI,SAAS,EAAE,CAAC;QAChC,MAAM,UAAU,CAAC,IAAI,EAAE,CAAC;QACxB,IAAA,aAAO,EAAC,sDAAsD,CAAC,CAAC;IAClE,CAAC;SAAM,CAAC;QACN,IAAA,aAAO,EAAC,qDAAqD,CAAC,CAAC;IACjE,CAAC;AACH,CAAC","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, 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 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
ADDED
package/dist/types.js
ADDED
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { JsxElement, JsxSelfClosingElement, Project, SourceFile } from "ts-morph";
|
|
2
|
+
export declare function loadSourceFile(filePath: string): {
|
|
3
|
+
project: Project;
|
|
4
|
+
sourceFile: SourceFile;
|
|
5
|
+
};
|
|
6
|
+
export declare function ensureNamedImport(sourceFile: SourceFile, imported: string, moduleSpecifier: string): boolean;
|
|
7
|
+
export declare function findJsxElements(sourceFile: SourceFile, tagName: string): JsxElement[];
|
|
8
|
+
export declare function findJsxSelfClosingElements(sourceFile: SourceFile, tagName: string): JsxSelfClosingElement[];
|
|
9
|
+
export declare function hasJsxAttribute(element: JsxElement | JsxSelfClosingElement, attributeName: string, predicate?: (value: string) => boolean): boolean;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadSourceFile = loadSourceFile;
|
|
4
|
+
exports.ensureNamedImport = ensureNamedImport;
|
|
5
|
+
exports.findJsxElements = findJsxElements;
|
|
6
|
+
exports.findJsxSelfClosingElements = findJsxSelfClosingElements;
|
|
7
|
+
exports.hasJsxAttribute = hasJsxAttribute;
|
|
8
|
+
const ts_morph_1 = require("ts-morph");
|
|
9
|
+
function loadSourceFile(filePath) {
|
|
10
|
+
const project = new ts_morph_1.Project({
|
|
11
|
+
manipulationSettings: {
|
|
12
|
+
quoteKind: ts_morph_1.QuoteKind.Double,
|
|
13
|
+
useTrailingCommas: true,
|
|
14
|
+
indentationText: ts_morph_1.IndentationText.TwoSpaces,
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
const sourceFile = project.addSourceFileAtPath(filePath);
|
|
18
|
+
return { project, sourceFile };
|
|
19
|
+
}
|
|
20
|
+
function ensureNamedImport(sourceFile, imported, moduleSpecifier) {
|
|
21
|
+
const existing = sourceFile.getImportDeclaration((d) => d.getModuleSpecifierValue() === moduleSpecifier);
|
|
22
|
+
if (existing) {
|
|
23
|
+
const hasImport = existing.getNamedImports().some((i) => i.getName() === imported);
|
|
24
|
+
if (hasImport)
|
|
25
|
+
return false;
|
|
26
|
+
existing.addNamedImport(imported);
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
sourceFile.addImportDeclaration({
|
|
30
|
+
namedImports: [imported],
|
|
31
|
+
moduleSpecifier,
|
|
32
|
+
});
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
function findJsxElements(sourceFile, tagName) {
|
|
36
|
+
return sourceFile
|
|
37
|
+
.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxElement)
|
|
38
|
+
.filter((el) => el.getOpeningElement().getTagNameNode().getText() === tagName);
|
|
39
|
+
}
|
|
40
|
+
function findJsxSelfClosingElements(sourceFile, tagName) {
|
|
41
|
+
return sourceFile
|
|
42
|
+
.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxSelfClosingElement)
|
|
43
|
+
.filter((el) => el.getTagNameNode().getText() === tagName);
|
|
44
|
+
}
|
|
45
|
+
function hasJsxAttribute(element, attributeName, predicate) {
|
|
46
|
+
const attributes = ts_morph_1.Node.isJsxElement(element)
|
|
47
|
+
? element.getOpeningElement().getAttributes()
|
|
48
|
+
: element.getAttributes();
|
|
49
|
+
for (const attr of attributes) {
|
|
50
|
+
if (!ts_morph_1.Node.isJsxAttribute(attr))
|
|
51
|
+
continue;
|
|
52
|
+
const jsxAttr = attr;
|
|
53
|
+
const attrName = jsxAttr.getNameNode().getText();
|
|
54
|
+
if (attrName !== attributeName)
|
|
55
|
+
continue;
|
|
56
|
+
const init = jsxAttr.getInitializer();
|
|
57
|
+
if (!init)
|
|
58
|
+
return predicate ? predicate("") : true;
|
|
59
|
+
const text = init.getText().replace(/['"`{}]/g, "");
|
|
60
|
+
if (!predicate || predicate(text))
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=ast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../../src/utils/ast.ts"],"names":[],"mappings":"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","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 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"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runCommand = runCommand;
|
|
7
|
+
const execa_1 = __importDefault(require("execa"));
|
|
8
|
+
const log_1 = require("./log");
|
|
9
|
+
async function runCommand(command, args, options = {}) {
|
|
10
|
+
const display = `${command} ${args.join(" ")}`.trim();
|
|
11
|
+
if (options.dryRun) {
|
|
12
|
+
(0, log_1.dryRun)(`Would run: ${display}`);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
(0, log_1.info)(`Running: ${display}`);
|
|
16
|
+
await (0, execa_1.default)(command, args, {
|
|
17
|
+
cwd: options.cwd,
|
|
18
|
+
env: options.env,
|
|
19
|
+
stdio: "inherit",
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":";;;;;AASA,gCAiBC;AA1BD,kDAA0B;AAC1B,+BAAqC;AAQ9B,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,IAAc,EACd,UAAuB,EAAE;IAEzB,MAAM,OAAO,GAAG,GAAG,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;IACtD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,IAAA,YAAM,EAAC,cAAc,OAAO,EAAE,CAAC,CAAC;QAChC,OAAO;IACT,CAAC;IAED,IAAA,UAAI,EAAC,YAAY,OAAO,EAAE,CAAC,CAAC;IAC5B,MAAM,IAAA,eAAK,EAAC,OAAO,EAAE,IAAI,EAAE;QACzB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,KAAK,EAAE,SAAS;KACjB,CAAC,CAAC;AACL,CAAC","sourcesContent":["import execa from \"execa\";\nimport { dryRun, info } from \"./log\";\n\ntype ExecOptions = {\n cwd?: string;\n dryRun?: boolean;\n env?: Record<string, string | undefined>;\n};\n\nexport async function runCommand(\n command: string,\n args: string[],\n options: ExecOptions = {},\n): Promise<void> {\n const display = `${command} ${args.join(\" \")}`.trim();\n if (options.dryRun) {\n dryRun(`Would run: ${display}`);\n return;\n }\n\n info(`Running: ${display}`);\n await execa(command, args, {\n cwd: options.cwd,\n env: options.env,\n stdio: \"inherit\",\n });\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function fileExists(filePath: string): Promise<boolean>;
|
|
2
|
+
export declare function readFileIfExists(filePath: string): Promise<string | undefined>;
|
|
3
|
+
export declare function writeFileSafely(filePath: string, content: string, options?: {
|
|
4
|
+
dryRun?: boolean;
|
|
5
|
+
}): Promise<boolean>;
|
|
6
|
+
export declare function ensureFile(filePath: string, content: string, options?: {
|
|
7
|
+
dryRun?: boolean;
|
|
8
|
+
}): Promise<boolean>;
|
package/dist/utils/fs.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.fileExists = fileExists;
|
|
7
|
+
exports.readFileIfExists = readFileIfExists;
|
|
8
|
+
exports.writeFileSafely = writeFileSafely;
|
|
9
|
+
exports.ensureFile = ensureFile;
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
async function fileExists(filePath) {
|
|
13
|
+
return fs_1.default.promises
|
|
14
|
+
.access(filePath, fs_1.default.constants.F_OK)
|
|
15
|
+
.then(() => true)
|
|
16
|
+
.catch(() => false);
|
|
17
|
+
}
|
|
18
|
+
async function readFileIfExists(filePath) {
|
|
19
|
+
if (!(await fileExists(filePath)))
|
|
20
|
+
return undefined;
|
|
21
|
+
return fs_1.default.promises.readFile(filePath, "utf8");
|
|
22
|
+
}
|
|
23
|
+
async function writeFileSafely(filePath, content, options = {}) {
|
|
24
|
+
const dir = path_1.default.dirname(filePath);
|
|
25
|
+
await fs_1.default.promises.mkdir(dir, { recursive: true });
|
|
26
|
+
const existing = await readFileIfExists(filePath);
|
|
27
|
+
if (existing !== undefined && existing === content) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (options.dryRun) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
await fs_1.default.promises.writeFile(filePath, content, "utf8");
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
async function ensureFile(filePath, content, options = {}) {
|
|
37
|
+
if (await fileExists(filePath)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
if (options.dryRun) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
const dir = path_1.default.dirname(filePath);
|
|
44
|
+
await fs_1.default.promises.mkdir(dir, { recursive: true });
|
|
45
|
+
await fs_1.default.promises.writeFile(filePath, content, "utf8");
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=fs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":";;;;;AAGA,gCAKC;AAED,4CAGC;AAED,0CAmBC;AAED,gCAeC;AAnDD,4CAAoB;AACpB,gDAAwB;AAEjB,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC/C,OAAO,YAAE,CAAC,QAAQ;SACf,MAAM,CAAC,QAAQ,EAAE,YAAE,CAAC,SAAS,CAAC,IAAI,CAAC;SACnC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;SAChB,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC;AAEM,KAAK,UAAU,gBAAgB,CAAC,QAAgB;IACrD,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACpD,OAAO,YAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AAChD,CAAC;AAEM,KAAK,UAAU,eAAe,CACnC,QAAgB,EAChB,OAAe,EACf,UAAgC,EAAE;IAElC,MAAM,GAAG,GAAG,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,YAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAElD,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAClD,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACnD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,YAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACvD,OAAO,IAAI,CAAC;AACd,CAAC;AAEM,KAAK,UAAU,UAAU,CAC9B,QAAgB,EAChB,OAAe,EACf,UAAgC,EAAE;IAElC,IAAI,MAAM,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,GAAG,GAAG,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,YAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,YAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACvD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n return fs.promises\n .access(filePath, fs.constants.F_OK)\n .then(() => true)\n .catch(() => false);\n}\n\nexport async function readFileIfExists(filePath: string): Promise<string | undefined> {\n if (!(await fileExists(filePath))) return undefined;\n return fs.promises.readFile(filePath, \"utf8\");\n}\n\nexport async function writeFileSafely(\n filePath: string,\n content: string,\n options: { dryRun?: boolean } = {},\n): Promise<boolean> {\n const dir = path.dirname(filePath);\n await fs.promises.mkdir(dir, { recursive: true });\n\n const existing = await readFileIfExists(filePath);\n if (existing !== undefined && existing === content) {\n return false;\n }\n\n if (options.dryRun) {\n return true;\n }\n\n await fs.promises.writeFile(filePath, content, \"utf8\");\n return true;\n}\n\nexport async function ensureFile(\n filePath: string,\n content: string,\n options: { dryRun?: boolean } = {},\n): Promise<boolean> {\n if (await fileExists(filePath)) {\n return false;\n }\n if (options.dryRun) {\n return true;\n }\n const dir = path.dirname(filePath);\n await fs.promises.mkdir(dir, { recursive: true });\n await fs.promises.writeFile(filePath, content, \"utf8\");\n return true;\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function step(message: string): void;
|
|
2
|
+
export declare function success(message: string): void;
|
|
3
|
+
export declare function warn(message: string): void;
|
|
4
|
+
export declare function info(message: string): void;
|
|
5
|
+
export declare function dryRun(message: string): void;
|
|
6
|
+
export declare function error(message: string): void;
|
|
7
|
+
export declare function mask(value: string | undefined): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.step = step;
|
|
7
|
+
exports.success = success;
|
|
8
|
+
exports.warn = warn;
|
|
9
|
+
exports.info = info;
|
|
10
|
+
exports.dryRun = dryRun;
|
|
11
|
+
exports.error = error;
|
|
12
|
+
exports.mask = mask;
|
|
13
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
14
|
+
const prefixes = {
|
|
15
|
+
step: chalk_1.default.cyan("==>"),
|
|
16
|
+
success: chalk_1.default.green("[ok]"),
|
|
17
|
+
warn: chalk_1.default.yellow("[warn]"),
|
|
18
|
+
error: chalk_1.default.red("[err]"),
|
|
19
|
+
info: chalk_1.default.blue("[info]"),
|
|
20
|
+
dryRun: chalk_1.default.magenta("[dry-run]"),
|
|
21
|
+
};
|
|
22
|
+
function step(message) {
|
|
23
|
+
console.log(`${prefixes.step} ${message}`);
|
|
24
|
+
}
|
|
25
|
+
function success(message) {
|
|
26
|
+
console.log(`${prefixes.success} ${message}`);
|
|
27
|
+
}
|
|
28
|
+
function warn(message) {
|
|
29
|
+
console.log(`${prefixes.warn} ${message}`);
|
|
30
|
+
}
|
|
31
|
+
function info(message) {
|
|
32
|
+
console.log(`${prefixes.info} ${message}`);
|
|
33
|
+
}
|
|
34
|
+
function dryRun(message) {
|
|
35
|
+
console.log(`${prefixes.dryRun} ${message}`);
|
|
36
|
+
}
|
|
37
|
+
function error(message) {
|
|
38
|
+
console.error(`${prefixes.error} ${message}`);
|
|
39
|
+
}
|
|
40
|
+
function mask(value) {
|
|
41
|
+
if (!value)
|
|
42
|
+
return "";
|
|
43
|
+
if (value.length <= 6)
|
|
44
|
+
return "*".repeat(value.length);
|
|
45
|
+
return `${value.slice(0, 2)}***${value.slice(-2)}`;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/utils/log.ts"],"names":[],"mappings":";;;;;AAWA,oBAEC;AAED,0BAEC;AAED,oBAEC;AAED,oBAEC;AAED,wBAEC;AAED,sBAEC;AAED,oBAIC;AAvCD,kDAA0B;AAE1B,MAAM,QAAQ,GAAG;IACf,IAAI,EAAE,eAAK,CAAC,IAAI,CAAC,KAAK,CAAC;IACvB,OAAO,EAAE,eAAK,CAAC,KAAK,CAAC,MAAM,CAAC;IAC5B,IAAI,EAAE,eAAK,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC5B,KAAK,EAAE,eAAK,CAAC,GAAG,CAAC,OAAO,CAAC;IACzB,IAAI,EAAE,eAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;IAC1B,MAAM,EAAE,eAAK,CAAC,OAAO,CAAC,WAAW,CAAC;CACnC,CAAC;AAEF,SAAgB,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,SAAgB,OAAO,CAAC,OAAe;IACrC,OAAO,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,OAAO,IAAI,OAAO,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,SAAgB,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,SAAgB,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,SAAgB,MAAM,CAAC,OAAe;IACpC,OAAO,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,OAAO,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,SAAgB,KAAK,CAAC,OAAe;IACnC,OAAO,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,KAAK,IAAI,OAAO,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,SAAgB,IAAI,CAAC,KAAyB;IAC5C,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACvD,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACrD,CAAC","sourcesContent":["import chalk from \"chalk\";\n\nconst prefixes = {\n step: chalk.cyan(\"==>\"),\n success: chalk.green(\"[ok]\"),\n warn: chalk.yellow(\"[warn]\"),\n error: chalk.red(\"[err]\"),\n info: chalk.blue(\"[info]\"),\n dryRun: chalk.magenta(\"[dry-run]\"),\n};\n\nexport function step(message: string) {\n console.log(`${prefixes.step} ${message}`);\n}\n\nexport function success(message: string) {\n console.log(`${prefixes.success} ${message}`);\n}\n\nexport function warn(message: string) {\n console.log(`${prefixes.warn} ${message}`);\n}\n\nexport function info(message: string) {\n console.log(`${prefixes.info} ${message}`);\n}\n\nexport function dryRun(message: string) {\n console.log(`${prefixes.dryRun} ${message}`);\n}\n\nexport function error(message: string) {\n console.error(`${prefixes.error} ${message}`);\n}\n\nexport function mask(value: string | undefined): string {\n if (!value) return \"\";\n if (value.length <= 6) return \"*\".repeat(value.length);\n return `${value.slice(0, 2)}***${value.slice(-2)}`;\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.readYamlFile = readYamlFile;
|
|
7
|
+
exports.writeYamlFile = writeYamlFile;
|
|
8
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
9
|
+
const fs_1 = require("./fs");
|
|
10
|
+
async function readYamlFile(filePath) {
|
|
11
|
+
const contents = await (0, fs_1.readFileIfExists)(filePath);
|
|
12
|
+
if (!contents)
|
|
13
|
+
return undefined;
|
|
14
|
+
return yaml_1.default.parse(contents);
|
|
15
|
+
}
|
|
16
|
+
async function writeYamlFile(filePath, data, options = {}) {
|
|
17
|
+
const content = `${yaml_1.default.stringify(data, { indent: 2 })}`.trimEnd() + "\n";
|
|
18
|
+
return (0, fs_1.writeFileSafely)(filePath, content, { dryRun: options.dryRun });
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=yaml.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yaml.js","sourceRoot":"","sources":["../../src/utils/yaml.ts"],"names":[],"mappings":";;;;;AAGA,oCAIC;AAED,sCAOC;AAhBD,gDAAwB;AACxB,6BAAyD;AAElD,KAAK,UAAU,YAAY,CAAc,QAAgB;IAC9D,MAAM,QAAQ,GAAG,MAAM,IAAA,qBAAgB,EAAC,QAAQ,CAAC,CAAC;IAClD,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChC,OAAO,cAAI,CAAC,KAAK,CAAC,QAAQ,CAAM,CAAC;AACnC,CAAC;AAEM,KAAK,UAAU,aAAa,CACjC,QAAgB,EAChB,IAAa,EACb,UAAgC,EAAE;IAElC,MAAM,OAAO,GAAG,GAAG,cAAI,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;IAC1E,OAAO,IAAA,oBAAe,EAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AACxE,CAAC","sourcesContent":["import YAML from \"yaml\";\nimport { readFileIfExists, writeFileSafely } from \"./fs\";\n\nexport async function readYamlFile<T = unknown>(filePath: string): Promise<T | undefined> {\n const contents = await readFileIfExists(filePath);\n if (!contents) return undefined;\n return YAML.parse(contents) as T;\n}\n\nexport async function writeYamlFile(\n filePath: string,\n data: unknown,\n options: { dryRun?: boolean } = {},\n): Promise<boolean> {\n const content = `${YAML.stringify(data, { indent: 2 })}`.trimEnd() + \"\\n\";\n return writeFileSafely(filePath, content, { dryRun: options.dryRun });\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dx-labs/template-designer-setup",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "CLI to install and wire Template Designer and License Box plugins into a Backstage instance",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": "dist/index.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc -p tsconfig.json",
|
|
11
|
+
"lint": "eslint .",
|
|
12
|
+
"format": "prettier -w .",
|
|
13
|
+
"format:check": "prettier -c .",
|
|
14
|
+
"clean": "rm -rf dist",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"validate:fix": "yarn tsc && yarn lint && yarn format",
|
|
17
|
+
"set-version": "node scripts/set-version.js",
|
|
18
|
+
"prepare": "husky"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"chalk": "^4.1.2",
|
|
22
|
+
"commander": "^11.1.0",
|
|
23
|
+
"execa": "^5.1.1",
|
|
24
|
+
"prompts": "^2.4.2",
|
|
25
|
+
"ts-morph": "^22.0.0",
|
|
26
|
+
"yaml": "^2.5.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@commitlint/cli": "^20.4.1",
|
|
30
|
+
"@commitlint/config-conventional": "^20.1.0",
|
|
31
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
32
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
33
|
+
"@semantic-release/exec": "^7.1.0",
|
|
34
|
+
"@semantic-release/git": "^10.0.1",
|
|
35
|
+
"@semantic-release/github": "^12.0.1",
|
|
36
|
+
"@semantic-release/npm": "^13.1.1",
|
|
37
|
+
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
38
|
+
"@types/node": "^20.14.11",
|
|
39
|
+
"@types/prompts": "^2.4.9",
|
|
40
|
+
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
41
|
+
"@typescript-eslint/parser": "^7.18.0",
|
|
42
|
+
"eslint": "^8.57.1",
|
|
43
|
+
"eslint-config-prettier": "^9.1.0",
|
|
44
|
+
"husky": "^9.1.7",
|
|
45
|
+
"lint-staged": "^16.2.6",
|
|
46
|
+
"prettier": "^3.3.3",
|
|
47
|
+
"typescript": "~5.8.0"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
"dist"
|
|
54
|
+
],
|
|
55
|
+
"sideEffects": false,
|
|
56
|
+
"exports": {
|
|
57
|
+
".": "./dist/index.js",
|
|
58
|
+
"./package.json": "./package.json"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
},
|
|
63
|
+
"lint-staged": {
|
|
64
|
+
"*.{js,ts,tsx,jsx}": "eslint --fix",
|
|
65
|
+
"*.{json,md,yml,yaml}": "prettier --write"
|
|
66
|
+
},
|
|
67
|
+
"packageManager": "yarn@4.1.1",
|
|
68
|
+
"repository": {}
|
|
69
|
+
}
|