@expressots/cli 3.0.0 → 4.0.0-preview.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/README.md +41 -95
- package/bin/cicd/cli.d.ts +6 -0
- package/bin/cicd/cli.js +126 -0
- package/bin/cicd/form.d.ts +29 -0
- package/bin/cicd/form.js +345 -0
- package/bin/cicd/generators/azure-devops.d.ts +2 -0
- package/bin/cicd/generators/azure-devops.js +370 -0
- package/bin/cicd/generators/bitbucket.d.ts +2 -0
- package/bin/cicd/generators/bitbucket.js +217 -0
- package/bin/cicd/generators/circleci.d.ts +2 -0
- package/bin/cicd/generators/circleci.js +274 -0
- package/bin/cicd/generators/github-actions.d.ts +14 -0
- package/bin/cicd/generators/github-actions.js +426 -0
- package/bin/cicd/generators/gitlab-ci.d.ts +2 -0
- package/bin/cicd/generators/gitlab-ci.js +237 -0
- package/bin/cicd/generators/index.d.ts +6 -0
- package/bin/cicd/generators/index.js +15 -0
- package/bin/cicd/generators/jenkins.d.ts +2 -0
- package/bin/cicd/generators/jenkins.js +248 -0
- package/bin/cicd/generators/template-loader.d.ts +17 -0
- package/bin/cicd/generators/template-loader.js +128 -0
- package/bin/cicd/index.d.ts +1 -0
- package/bin/cicd/index.js +5 -0
- package/bin/cli.d.ts +1 -1
- package/bin/cli.js +18 -3
- package/bin/commands/project.commands.d.ts +19 -6
- package/bin/commands/project.commands.js +390 -61
- package/bin/config/index.d.ts +5 -0
- package/bin/config/index.js +10 -0
- package/bin/config/manager.d.ts +98 -0
- package/bin/config/manager.js +222 -0
- package/bin/containerize/analyzers/bootstrap-analyzer.d.ts +46 -0
- package/bin/containerize/analyzers/bootstrap-analyzer.js +187 -0
- package/bin/containerize/analyzers/project-analyzer.d.ts +20 -0
- package/bin/containerize/analyzers/project-analyzer.js +150 -0
- package/bin/containerize/cli.d.ts +4 -0
- package/bin/containerize/cli.js +113 -0
- package/bin/containerize/form.d.ts +15 -0
- package/bin/containerize/form.js +154 -0
- package/bin/containerize/generators/ci-generator.d.ts +31 -0
- package/bin/containerize/generators/ci-generator.js +936 -0
- package/bin/containerize/generators/docker-compose-generator.d.ts +8 -0
- package/bin/containerize/generators/docker-compose-generator.js +186 -0
- package/bin/containerize/generators/dockerfile-generator.d.ts +8 -0
- package/bin/containerize/generators/dockerfile-generator.js +635 -0
- package/bin/containerize/generators/kubernetes-generator.d.ts +8 -0
- package/bin/containerize/generators/kubernetes-generator.js +133 -0
- package/bin/containerize/generators/template-loader.d.ts +36 -0
- package/bin/containerize/generators/template-loader.js +129 -0
- package/bin/containerize/index.d.ts +4 -0
- package/bin/containerize/index.js +13 -0
- package/bin/containerize/presets/preset-registry.d.ts +20 -0
- package/bin/containerize/presets/preset-registry.js +102 -0
- package/bin/costs/cli.d.ts +5 -0
- package/bin/costs/cli.js +183 -0
- package/bin/costs/form.d.ts +44 -0
- package/bin/costs/form.js +412 -0
- package/bin/costs/index.d.ts +4 -0
- package/bin/costs/index.js +25 -0
- package/bin/costs/pricing-manager.d.ts +84 -0
- package/bin/costs/pricing-manager.js +342 -0
- package/bin/costs/providers/index.d.ts +32 -0
- package/bin/costs/providers/index.js +153 -0
- package/bin/costs/sources/api-source.d.ts +10 -0
- package/bin/costs/sources/api-source.js +32 -0
- package/bin/costs/sources/index.d.ts +6 -0
- package/bin/costs/sources/index.js +15 -0
- package/bin/costs/sources/local-json-source.d.ts +23 -0
- package/bin/costs/sources/local-json-source.js +59 -0
- package/bin/costs/sources/remote-json-source.d.ts +11 -0
- package/bin/costs/sources/remote-json-source.js +53 -0
- package/bin/costs/types.d.ts +53 -0
- package/bin/costs/types.js +5 -0
- package/bin/dev/cli.d.ts +4 -0
- package/bin/dev/cli.js +134 -0
- package/bin/dev/form.d.ts +36 -0
- package/bin/dev/form.js +254 -0
- package/bin/dev/index.d.ts +1 -0
- package/bin/dev/index.js +5 -0
- package/bin/generate/cli.js +29 -2
- package/bin/generate/form.d.ts +5 -1
- package/bin/generate/form.js +3 -3
- package/bin/generate/templates/nonopinionated/config.tpl +12 -0
- package/bin/generate/templates/nonopinionated/event.tpl +10 -0
- package/bin/generate/templates/nonopinionated/guard.tpl +18 -0
- package/bin/generate/templates/nonopinionated/handler.tpl +12 -0
- package/bin/generate/templates/nonopinionated/interceptor.tpl +27 -0
- package/bin/generate/templates/opinionated/config.tpl +47 -0
- package/bin/generate/templates/opinionated/entity.tpl +1 -8
- package/bin/generate/templates/opinionated/event.tpl +15 -0
- package/bin/generate/templates/opinionated/guard.tpl +41 -0
- package/bin/generate/templates/opinionated/handler.tpl +23 -0
- package/bin/generate/templates/opinionated/interceptor.tpl +50 -0
- package/bin/generate/utils/command-utils.d.ts +7 -3
- package/bin/generate/utils/command-utils.js +95 -31
- package/bin/generate/utils/nonopininated-cmd.d.ts +10 -1
- package/bin/generate/utils/nonopininated-cmd.js +100 -1
- package/bin/generate/utils/opinionated-cmd.d.ts +10 -1
- package/bin/generate/utils/opinionated-cmd.js +112 -7
- package/bin/generate/utils/string-utils.d.ts +6 -0
- package/bin/generate/utils/string-utils.js +13 -1
- package/bin/help/form.js +11 -3
- package/bin/migrate/analyzers/platform-detector.d.ts +14 -0
- package/bin/migrate/analyzers/platform-detector.js +116 -0
- package/bin/migrate/cli.d.ts +6 -0
- package/bin/migrate/cli.js +96 -0
- package/bin/migrate/form.d.ts +25 -0
- package/bin/migrate/form.js +347 -0
- package/bin/migrate/generators/compose-to-k8s.d.ts +2 -0
- package/bin/migrate/generators/compose-to-k8s.js +324 -0
- package/bin/migrate/generators/compose-to-railway.d.ts +2 -0
- package/bin/migrate/generators/compose-to-railway.js +138 -0
- package/bin/migrate/generators/compose-to-render.d.ts +2 -0
- package/bin/migrate/generators/compose-to-render.js +148 -0
- package/bin/migrate/generators/generic-migration.d.ts +9 -0
- package/bin/migrate/generators/generic-migration.js +221 -0
- package/bin/migrate/generators/heroku-to-fly.d.ts +2 -0
- package/bin/migrate/generators/heroku-to-fly.js +291 -0
- package/bin/migrate/generators/heroku-to-railway.d.ts +2 -0
- package/bin/migrate/generators/heroku-to-railway.js +283 -0
- package/bin/migrate/generators/heroku-to-render.d.ts +2 -0
- package/bin/migrate/generators/heroku-to-render.js +148 -0
- package/bin/migrate/generators/index.d.ts +7 -0
- package/bin/migrate/generators/index.js +17 -0
- package/bin/migrate/generators/template-loader.d.ts +21 -0
- package/bin/migrate/generators/template-loader.js +59 -0
- package/bin/migrate/index.d.ts +1 -0
- package/bin/migrate/index.js +5 -0
- package/bin/new/cli.js +21 -6
- package/bin/new/form.d.ts +25 -4
- package/bin/new/form.js +285 -70
- package/bin/profile/analyzers/dockerfile-analyzer.d.ts +27 -0
- package/bin/profile/analyzers/dockerfile-analyzer.js +122 -0
- package/bin/profile/analyzers/image-analyzer.d.ts +19 -0
- package/bin/profile/analyzers/image-analyzer.js +85 -0
- package/bin/profile/cli.d.ts +4 -0
- package/bin/profile/cli.js +92 -0
- package/bin/profile/form.d.ts +56 -0
- package/bin/profile/form.js +400 -0
- package/bin/profile/index.d.ts +1 -0
- package/bin/profile/index.js +5 -0
- package/bin/profile/optimizers/index.d.ts +19 -0
- package/bin/profile/optimizers/index.js +137 -0
- package/bin/providers/add/form.d.ts +1 -1
- package/bin/providers/add/form.js +27 -6
- package/bin/providers/create/form.js +2 -1
- package/bin/scripts/form.js +27 -5
- package/bin/studio/cli.d.ts +15 -0
- package/bin/studio/cli.js +166 -0
- package/bin/studio/index.d.ts +5 -0
- package/bin/studio/index.js +9 -0
- package/bin/templates/cache.d.ts +54 -0
- package/bin/templates/cache.js +180 -0
- package/bin/templates/cli.d.ts +8 -0
- package/bin/templates/cli.js +292 -0
- package/bin/templates/fetcher.d.ts +49 -0
- package/bin/templates/fetcher.js +208 -0
- package/bin/templates/index.d.ts +11 -0
- package/bin/templates/index.js +37 -0
- package/bin/templates/manager.d.ts +116 -0
- package/bin/templates/manager.js +323 -0
- package/bin/templates/renderer.d.ts +49 -0
- package/bin/templates/renderer.js +204 -0
- package/bin/templates/types.d.ts +51 -0
- package/bin/templates/types.js +5 -0
- package/bin/utils/add-module-to-container.d.ts +2 -2
- package/bin/utils/add-module-to-container.js +15 -5
- package/bin/utils/cli-ui.d.ts +30 -3
- package/bin/utils/cli-ui.js +95 -13
- package/bin/utils/index.d.ts +4 -0
- package/bin/utils/index.js +4 -0
- package/bin/utils/input-validation.d.ts +50 -0
- package/bin/utils/input-validation.js +143 -0
- package/bin/utils/package-manager-commands.d.ts +24 -0
- package/bin/utils/package-manager-commands.js +50 -0
- package/bin/utils/safe-spawn.d.ts +35 -0
- package/bin/utils/safe-spawn.js +51 -0
- package/bin/utils/update-tsconfig-paths.d.ts +35 -0
- package/bin/utils/update-tsconfig-paths.js +286 -0
- package/package.json +154 -154
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Input validation utilities used across the CLI to defend against
|
|
4
|
+
* command injection and path traversal when user-supplied values flow
|
|
5
|
+
* into child_process spawn/exec calls or filesystem writes.
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.assertValidScriptName = exports.assertValidVersion = exports.assertValidPackageName = exports.safeResolveWithin = exports.isValidPackageManager = exports.isValidScriptName = exports.isValidVersion = exports.isValidPackageName = exports.containsShellMetachars = void 0;
|
|
12
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
13
|
+
/**
|
|
14
|
+
* Characters that have shell-special meaning across POSIX shells and
|
|
15
|
+
* Windows cmd. If any appear in a value that will be interpolated into
|
|
16
|
+
* a shell-evaluated command (`shell: true` or `execSync(string)`), the
|
|
17
|
+
* value must be rejected.
|
|
18
|
+
*/
|
|
19
|
+
const SHELL_METACHARACTERS = /[;&|`$()<>\n\r\\"'*?{}[\]!#~]/;
|
|
20
|
+
/**
|
|
21
|
+
* Conservative regex for npm package names. Allows scoped packages,
|
|
22
|
+
* dotted segments and dashes, mirroring https://docs.npmjs.com/cli/v10/configuring-npm/package-json#name
|
|
23
|
+
* but stricter than npm itself to remove ambiguity (no leading dots,
|
|
24
|
+
* no uppercase to keep it portable across registries).
|
|
25
|
+
*/
|
|
26
|
+
const PACKAGE_NAME_RE = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
27
|
+
/**
|
|
28
|
+
* Semver-ish range validator. Accepts the common syntactic shapes
|
|
29
|
+
* (e.g. 1.2.3, ^1.2, ~1, 1.x, latest, next, >=1.2.3 <2.0.0) without
|
|
30
|
+
* pulling in the full semver parser for this guard.
|
|
31
|
+
*/
|
|
32
|
+
const VERSION_RE = /^[A-Za-z0-9.\-+~^>=<* |]+$/;
|
|
33
|
+
/**
|
|
34
|
+
* Script name validator for npm/yarn/pnpm `run` targets. Matches what
|
|
35
|
+
* those package managers actually accept (alphanumerics plus
|
|
36
|
+
* `:_-./`), explicitly rejecting whitespace and shell metacharacters.
|
|
37
|
+
*/
|
|
38
|
+
const SCRIPT_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9:_./-]*$/;
|
|
39
|
+
/**
|
|
40
|
+
* Guard against shell metacharacters in any value that will be
|
|
41
|
+
* interpolated into a shell-evaluated command line.
|
|
42
|
+
*/
|
|
43
|
+
function containsShellMetachars(value) {
|
|
44
|
+
return SHELL_METACHARACTERS.test(value);
|
|
45
|
+
}
|
|
46
|
+
exports.containsShellMetachars = containsShellMetachars;
|
|
47
|
+
/**
|
|
48
|
+
* Validate an npm package name (with optional scope).
|
|
49
|
+
*/
|
|
50
|
+
function isValidPackageName(name) {
|
|
51
|
+
if (typeof name !== "string" || name.length === 0 || name.length > 214) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (containsShellMetachars(name))
|
|
55
|
+
return false;
|
|
56
|
+
return PACKAGE_NAME_RE.test(name);
|
|
57
|
+
}
|
|
58
|
+
exports.isValidPackageName = isValidPackageName;
|
|
59
|
+
/**
|
|
60
|
+
* Validate a version specifier passed to a package manager. Accepts
|
|
61
|
+
* `latest`, `next`, exact versions and common range syntaxes
|
|
62
|
+
* (`>=1.2.3 <2.0.0`, `*`, `1.x`). Returns false for the boolean
|
|
63
|
+
* fallback yargs sometimes assigns when the flag is absent.
|
|
64
|
+
*
|
|
65
|
+
* Versions are forwarded via argv (`shell: false`), so we whitelist
|
|
66
|
+
* the characters npm itself accepts in semver ranges and reject the
|
|
67
|
+
* rest. We do NOT layer the broader `containsShellMetachars` check
|
|
68
|
+
* here because legitimate ranges include `<`, `>`, `|`, and `*`.
|
|
69
|
+
*/
|
|
70
|
+
function isValidVersion(version) {
|
|
71
|
+
if (typeof version !== "string" || version.length === 0)
|
|
72
|
+
return false;
|
|
73
|
+
if (version.length > 64)
|
|
74
|
+
return false;
|
|
75
|
+
return VERSION_RE.test(version);
|
|
76
|
+
}
|
|
77
|
+
exports.isValidVersion = isValidVersion;
|
|
78
|
+
/**
|
|
79
|
+
* Validate an npm/yarn/pnpm script name.
|
|
80
|
+
*/
|
|
81
|
+
function isValidScriptName(name) {
|
|
82
|
+
if (typeof name !== "string" || name.length === 0 || name.length > 214) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
if (containsShellMetachars(name))
|
|
86
|
+
return false;
|
|
87
|
+
return SCRIPT_NAME_RE.test(name);
|
|
88
|
+
}
|
|
89
|
+
exports.isValidScriptName = isValidScriptName;
|
|
90
|
+
/**
|
|
91
|
+
* Validate a package manager identifier.
|
|
92
|
+
*/
|
|
93
|
+
const ALLOWED_PACKAGE_MANAGERS = new Set(["npm", "yarn", "pnpm", "bun"]);
|
|
94
|
+
function isValidPackageManager(pm) {
|
|
95
|
+
return typeof pm === "string" && ALLOWED_PACKAGE_MANAGERS.has(pm);
|
|
96
|
+
}
|
|
97
|
+
exports.isValidPackageManager = isValidPackageManager;
|
|
98
|
+
/**
|
|
99
|
+
* Resolve `target` against `base` and verify the result is contained
|
|
100
|
+
* within `base`. Returns the resolved absolute path on success, or
|
|
101
|
+
* `null` when the resolved path escapes the base directory (path
|
|
102
|
+
* traversal attempt).
|
|
103
|
+
*/
|
|
104
|
+
function safeResolveWithin(base, target) {
|
|
105
|
+
const absoluteBase = node_path_1.default.resolve(base);
|
|
106
|
+
const absoluteTarget = node_path_1.default.resolve(absoluteBase, target);
|
|
107
|
+
const baseWithSep = absoluteBase.endsWith(node_path_1.default.sep)
|
|
108
|
+
? absoluteBase
|
|
109
|
+
: absoluteBase + node_path_1.default.sep;
|
|
110
|
+
if (absoluteTarget !== absoluteBase &&
|
|
111
|
+
!absoluteTarget.startsWith(baseWithSep)) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
return absoluteTarget;
|
|
115
|
+
}
|
|
116
|
+
exports.safeResolveWithin = safeResolveWithin;
|
|
117
|
+
/**
|
|
118
|
+
* Throws a generic `Error` if the value is not a safe package name.
|
|
119
|
+
*/
|
|
120
|
+
function assertValidPackageName(name) {
|
|
121
|
+
if (!isValidPackageName(name)) {
|
|
122
|
+
throw new Error(`Invalid package name: ${JSON.stringify(name)}. Names must match npm package name rules and contain no shell metacharacters.`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.assertValidPackageName = assertValidPackageName;
|
|
126
|
+
/**
|
|
127
|
+
* Throws a generic `Error` if the value is not a safe version range.
|
|
128
|
+
*/
|
|
129
|
+
function assertValidVersion(version) {
|
|
130
|
+
if (!isValidVersion(version)) {
|
|
131
|
+
throw new Error(`Invalid version specifier: ${JSON.stringify(version)}.`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.assertValidVersion = assertValidVersion;
|
|
135
|
+
/**
|
|
136
|
+
* Throws a generic `Error` if the value is not a safe script name.
|
|
137
|
+
*/
|
|
138
|
+
function assertValidScriptName(name) {
|
|
139
|
+
if (!isValidScriptName(name)) {
|
|
140
|
+
throw new Error(`Invalid script name: ${JSON.stringify(name)}. Script names must match ^[a-zA-Z0-9][a-zA-Z0-9:_./-]*$.`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.assertValidScriptName = assertValidScriptName;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared package-manager command helpers for code generators
|
|
3
|
+
* (Dockerfiles, CI/CD pipelines). These centralize the mapping
|
|
4
|
+
* between an analyzer-detected package manager and the literal
|
|
5
|
+
* shell strings emitted into generated files.
|
|
6
|
+
*
|
|
7
|
+
* Two flavors exist:
|
|
8
|
+
* - `RUN`/`CMD`-style strings used INSIDE Dockerfiles.
|
|
9
|
+
* - Plain shell invocations used in CI scripts and informational
|
|
10
|
+
* comments (no `RUN ` prefix).
|
|
11
|
+
*/
|
|
12
|
+
export type SupportedPackageManager = "npm" | "yarn" | "pnpm" | "bun";
|
|
13
|
+
/**
|
|
14
|
+
* Shell invocation that installs project dependencies, suitable for
|
|
15
|
+
* a CI step (no `RUN ` prefix). Uses the strict, lockfile-respecting
|
|
16
|
+
* variant for each package manager because CI runs should be
|
|
17
|
+
* reproducible.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getCiInstallCommand(packageManager: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Shell invocation that runs an npm-style script (e.g. `lint`,
|
|
22
|
+
* `test`, `build`). Used in CI scripts and informational comments.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getRunScriptCommand(packageManager: string, scriptName: string): string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared package-manager command helpers for code generators
|
|
4
|
+
* (Dockerfiles, CI/CD pipelines). These centralize the mapping
|
|
5
|
+
* between an analyzer-detected package manager and the literal
|
|
6
|
+
* shell strings emitted into generated files.
|
|
7
|
+
*
|
|
8
|
+
* Two flavors exist:
|
|
9
|
+
* - `RUN`/`CMD`-style strings used INSIDE Dockerfiles.
|
|
10
|
+
* - Plain shell invocations used in CI scripts and informational
|
|
11
|
+
* comments (no `RUN ` prefix).
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.getRunScriptCommand = exports.getCiInstallCommand = void 0;
|
|
15
|
+
/**
|
|
16
|
+
* Shell invocation that installs project dependencies, suitable for
|
|
17
|
+
* a CI step (no `RUN ` prefix). Uses the strict, lockfile-respecting
|
|
18
|
+
* variant for each package manager because CI runs should be
|
|
19
|
+
* reproducible.
|
|
20
|
+
*/
|
|
21
|
+
function getCiInstallCommand(packageManager) {
|
|
22
|
+
switch (packageManager) {
|
|
23
|
+
case "pnpm":
|
|
24
|
+
return "pnpm install --frozen-lockfile";
|
|
25
|
+
case "yarn":
|
|
26
|
+
return "yarn install --frozen-lockfile";
|
|
27
|
+
case "bun":
|
|
28
|
+
return "bun install --frozen-lockfile";
|
|
29
|
+
default:
|
|
30
|
+
return "npm ci";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.getCiInstallCommand = getCiInstallCommand;
|
|
34
|
+
/**
|
|
35
|
+
* Shell invocation that runs an npm-style script (e.g. `lint`,
|
|
36
|
+
* `test`, `build`). Used in CI scripts and informational comments.
|
|
37
|
+
*/
|
|
38
|
+
function getRunScriptCommand(packageManager, scriptName) {
|
|
39
|
+
switch (packageManager) {
|
|
40
|
+
case "pnpm":
|
|
41
|
+
return `pnpm run ${scriptName}`;
|
|
42
|
+
case "yarn":
|
|
43
|
+
return `yarn ${scriptName}`;
|
|
44
|
+
case "bun":
|
|
45
|
+
return `bun run ${scriptName}`;
|
|
46
|
+
default:
|
|
47
|
+
return `npm run ${scriptName}`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.getRunScriptCommand = getRunScriptCommand;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform wrappers around `child_process.spawn` / `spawnSync` for
|
|
3
|
+
* launching package-manager binaries (npm, yarn, pnpm, npx, tsx, tsc,
|
|
4
|
+
* docker, etc.) reliably on every supported platform.
|
|
5
|
+
*
|
|
6
|
+
* Why this exists
|
|
7
|
+
* ---------------
|
|
8
|
+
* Starting with Node.js 18.20.2 / 20.12.2 / 21.7.3 (and every v22+), the
|
|
9
|
+
* runtime refuses to spawn `.bat` / `.cmd` files unless `shell: true` is
|
|
10
|
+
* passed (see CVE-2024-27980). On Windows, `npm`, `yarn`, `pnpm`, `npx`,
|
|
11
|
+
* `tsx`, `tsc`, and the `node_modules/.bin/*` shims are all `.cmd` files,
|
|
12
|
+
* so a direct `spawn("npm", [...], { shell: false })` call now fails with
|
|
13
|
+
* `EINVAL` ("Package manager not found"). At the same time, just flipping
|
|
14
|
+
* `shell: true` reintroduces the original CVE: arguments containing shell
|
|
15
|
+
* metacharacters (`|`, `>`, `<`, `^`, `&`, `(`, `)`, ...) get interpreted
|
|
16
|
+
* by `cmd.exe` instead of being passed verbatim, which is a real concern
|
|
17
|
+
* for inputs like a semver range (`>=1.0.0 <2.0.0`) or a user-supplied
|
|
18
|
+
* `--src` flag.
|
|
19
|
+
*
|
|
20
|
+
* `cross-spawn` solves both problems:
|
|
21
|
+
* - On Windows it parses the command, resolves `.cmd` shims via PATHEXT,
|
|
22
|
+
* and invokes `cmd.exe /d /s /c "command args"` with `shell: false` and
|
|
23
|
+
* `windowsVerbatimArguments: true`. Each argv entry is passed through a
|
|
24
|
+
* cmd.exe-aware escaper, so metacharacters stay literal.
|
|
25
|
+
* - On Unix it falls through to plain `spawn` with `shell: false`.
|
|
26
|
+
*
|
|
27
|
+
* The helpers here are thin wrappers that default `windowsHide: true` so
|
|
28
|
+
* the Windows console doesn't flash, and re-export the same options shape
|
|
29
|
+
* as `child_process` for drop-in usage.
|
|
30
|
+
*/
|
|
31
|
+
/// <reference types="node" />
|
|
32
|
+
/// <reference types="node" />
|
|
33
|
+
import type { ChildProcess, SpawnOptions, SpawnSyncOptions, SpawnSyncReturns } from "node:child_process";
|
|
34
|
+
export declare function safeSpawn(command: string, args?: ReadonlyArray<string>, options?: SpawnOptions): ChildProcess;
|
|
35
|
+
export declare function safeSpawnSync(command: string, args?: ReadonlyArray<string>, options?: SpawnSyncOptions): SpawnSyncReturns<Buffer>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cross-platform wrappers around `child_process.spawn` / `spawnSync` for
|
|
4
|
+
* launching package-manager binaries (npm, yarn, pnpm, npx, tsx, tsc,
|
|
5
|
+
* docker, etc.) reliably on every supported platform.
|
|
6
|
+
*
|
|
7
|
+
* Why this exists
|
|
8
|
+
* ---------------
|
|
9
|
+
* Starting with Node.js 18.20.2 / 20.12.2 / 21.7.3 (and every v22+), the
|
|
10
|
+
* runtime refuses to spawn `.bat` / `.cmd` files unless `shell: true` is
|
|
11
|
+
* passed (see CVE-2024-27980). On Windows, `npm`, `yarn`, `pnpm`, `npx`,
|
|
12
|
+
* `tsx`, `tsc`, and the `node_modules/.bin/*` shims are all `.cmd` files,
|
|
13
|
+
* so a direct `spawn("npm", [...], { shell: false })` call now fails with
|
|
14
|
+
* `EINVAL` ("Package manager not found"). At the same time, just flipping
|
|
15
|
+
* `shell: true` reintroduces the original CVE: arguments containing shell
|
|
16
|
+
* metacharacters (`|`, `>`, `<`, `^`, `&`, `(`, `)`, ...) get interpreted
|
|
17
|
+
* by `cmd.exe` instead of being passed verbatim, which is a real concern
|
|
18
|
+
* for inputs like a semver range (`>=1.0.0 <2.0.0`) or a user-supplied
|
|
19
|
+
* `--src` flag.
|
|
20
|
+
*
|
|
21
|
+
* `cross-spawn` solves both problems:
|
|
22
|
+
* - On Windows it parses the command, resolves `.cmd` shims via PATHEXT,
|
|
23
|
+
* and invokes `cmd.exe /d /s /c "command args"` with `shell: false` and
|
|
24
|
+
* `windowsVerbatimArguments: true`. Each argv entry is passed through a
|
|
25
|
+
* cmd.exe-aware escaper, so metacharacters stay literal.
|
|
26
|
+
* - On Unix it falls through to plain `spawn` with `shell: false`.
|
|
27
|
+
*
|
|
28
|
+
* The helpers here are thin wrappers that default `windowsHide: true` so
|
|
29
|
+
* the Windows console doesn't flash, and re-export the same options shape
|
|
30
|
+
* as `child_process` for drop-in usage.
|
|
31
|
+
*/
|
|
32
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
33
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
34
|
+
};
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.safeSpawnSync = exports.safeSpawn = void 0;
|
|
37
|
+
const cross_spawn_1 = __importDefault(require("cross-spawn"));
|
|
38
|
+
function safeSpawn(command, args = [], options = {}) {
|
|
39
|
+
return (0, cross_spawn_1.default)(command, args, {
|
|
40
|
+
windowsHide: true,
|
|
41
|
+
...options,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
exports.safeSpawn = safeSpawn;
|
|
45
|
+
function safeSpawnSync(command, args = [], options = {}) {
|
|
46
|
+
return cross_spawn_1.default.sync(command, args, {
|
|
47
|
+
windowsHide: true,
|
|
48
|
+
...options,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
exports.safeSpawnSync = safeSpawnSync;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate path alias from folder name
|
|
3
|
+
* Handles both default mappings and custom folder names
|
|
4
|
+
*
|
|
5
|
+
* @param folderName - The folder name (e.g., "useCases", "my-custom-folder")
|
|
6
|
+
* @returns The path alias (e.g., "@useCases", "@myCustomFolder")
|
|
7
|
+
*/
|
|
8
|
+
export declare function generatePathAlias(folderName: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Update tsconfig.json paths to include missing aliases for opinionated scaffolding
|
|
11
|
+
* Handles both default folders and custom scaffoldSchematics overrides
|
|
12
|
+
*
|
|
13
|
+
* @param folderName - The folder name where the schematic is being created
|
|
14
|
+
* @param sourceRoot - The source root directory (default: "src")
|
|
15
|
+
*/
|
|
16
|
+
export declare function updateTsconfigPaths(folderName: string, sourceRoot?: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Get the path alias for a given folder name
|
|
19
|
+
* Used by other utilities to determine the correct import path
|
|
20
|
+
*
|
|
21
|
+
* @param folderName - The folder name
|
|
22
|
+
* @returns The path alias (e.g., "@useCases")
|
|
23
|
+
*/
|
|
24
|
+
export declare function getPathAliasForFolder(folderName: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Check if tsconfig already has all required path aliases for opinionated mode
|
|
27
|
+
* This can be used to validate project setup
|
|
28
|
+
*
|
|
29
|
+
* @param requiredFolders - List of folder names that need path aliases
|
|
30
|
+
* @returns Object with missing aliases and whether all are present
|
|
31
|
+
*/
|
|
32
|
+
export declare function validateTsconfigPaths(requiredFolders: string[]): {
|
|
33
|
+
valid: boolean;
|
|
34
|
+
missingAliases: string[];
|
|
35
|
+
};
|
|
@@ -0,0 +1,286 @@
|
|
|
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.validateTsconfigPaths = exports.getPathAliasForFolder = exports.updateTsconfigPaths = exports.generatePathAlias = void 0;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const cli_ui_1 = require("./cli-ui");
|
|
10
|
+
/**
|
|
11
|
+
* Default path alias mappings for opinionated scaffolding
|
|
12
|
+
* Maps folder names to their corresponding path aliases
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_PATH_ALIASES = {
|
|
15
|
+
controllers: "@controllers",
|
|
16
|
+
useCases: "@useCases",
|
|
17
|
+
providers: "@providers",
|
|
18
|
+
entities: "@entities",
|
|
19
|
+
middleware: "@middleware",
|
|
20
|
+
interceptors: "@interceptors",
|
|
21
|
+
events: "@events",
|
|
22
|
+
guards: "@guards",
|
|
23
|
+
config: "@config",
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Generate path alias from folder name
|
|
27
|
+
* Handles both default mappings and custom folder names
|
|
28
|
+
*
|
|
29
|
+
* @param folderName - The folder name (e.g., "useCases", "my-custom-folder")
|
|
30
|
+
* @returns The path alias (e.g., "@useCases", "@myCustomFolder")
|
|
31
|
+
*/
|
|
32
|
+
function generatePathAlias(folderName) {
|
|
33
|
+
// Check if we have a default mapping
|
|
34
|
+
if (DEFAULT_PATH_ALIASES[folderName]) {
|
|
35
|
+
return DEFAULT_PATH_ALIASES[folderName];
|
|
36
|
+
}
|
|
37
|
+
// For custom folder names, convert to camelCase and add @ prefix
|
|
38
|
+
// Handles: kebab-case, snake_case, PascalCase, camelCase
|
|
39
|
+
const camelCase = folderName
|
|
40
|
+
.split(/[-_]/)
|
|
41
|
+
.map((word, index) => {
|
|
42
|
+
if (index === 0) {
|
|
43
|
+
// First word: keep original case for already camelCase names
|
|
44
|
+
return word.charAt(0).toLowerCase() + word.slice(1);
|
|
45
|
+
}
|
|
46
|
+
// Subsequent words: capitalize first letter
|
|
47
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
48
|
+
})
|
|
49
|
+
.join("");
|
|
50
|
+
return `@${camelCase}`;
|
|
51
|
+
}
|
|
52
|
+
exports.generatePathAlias = generatePathAlias;
|
|
53
|
+
/**
|
|
54
|
+
* Parse JSONC (JSON with Comments) by stripping comments and trailing commas
|
|
55
|
+
* Handles:
|
|
56
|
+
* - Single-line comments (//)
|
|
57
|
+
* - Multi-line comments
|
|
58
|
+
* - Trailing commas (common in tsconfig.json)
|
|
59
|
+
*
|
|
60
|
+
* @param content - The JSONC content
|
|
61
|
+
* @returns Cleaned JSON string that can be parsed by JSON.parse
|
|
62
|
+
*/
|
|
63
|
+
function stripJsonComments(content) {
|
|
64
|
+
let result = content;
|
|
65
|
+
// Remove multi-line comments first (they can span multiple lines)
|
|
66
|
+
result = result.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
67
|
+
// Remove single-line comments (but not inside strings)
|
|
68
|
+
// This regex looks for // that are not inside strings
|
|
69
|
+
result = result.replace(/^(\s*)\/\/.*$/gm, "$1");
|
|
70
|
+
// Also handle inline comments after values
|
|
71
|
+
// Match: value, // comment or value // comment
|
|
72
|
+
result = result.replace(/,\s*\/\/.*$/gm, ",");
|
|
73
|
+
result = result.replace(/(["\d\w\]}\s])\s*\/\/.*$/gm, "$1");
|
|
74
|
+
// Remove trailing commas before } or ]
|
|
75
|
+
// This handles cases like: { "key": "value", }
|
|
76
|
+
result = result.replace(/,(\s*[}\]])/g, "$1");
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Try to parse JSONC content, first stripping comments then parsing
|
|
81
|
+
* @param content - Raw file content
|
|
82
|
+
* @returns Parsed config object or null if parsing fails
|
|
83
|
+
*/
|
|
84
|
+
function parseJsonc(content) {
|
|
85
|
+
// Always try to strip comments first for consistency
|
|
86
|
+
const stripped = stripJsonComments(content);
|
|
87
|
+
try {
|
|
88
|
+
return JSON.parse(stripped);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// If stripping didn't help, try the original (unlikely but safe)
|
|
92
|
+
try {
|
|
93
|
+
return JSON.parse(content);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Text-based fallback to add path alias when JSON parsing fails
|
|
102
|
+
* This preserves the original file format (comments, etc.)
|
|
103
|
+
*
|
|
104
|
+
* @param content - Original file content
|
|
105
|
+
* @param aliasKey - The path alias key (e.g., "@config/*")
|
|
106
|
+
* @param aliasValue - The path alias value (e.g., ["./src/config/*"])
|
|
107
|
+
* @returns Modified content or null if update failed
|
|
108
|
+
*/
|
|
109
|
+
function addPathAliasTextBased(content, aliasKey, aliasValue) {
|
|
110
|
+
// Check if the alias already exists
|
|
111
|
+
if (content.includes(`"${aliasKey}"`)) {
|
|
112
|
+
return null; // Already exists, no update needed
|
|
113
|
+
}
|
|
114
|
+
// Find the "paths" object
|
|
115
|
+
const pathsMatch = content.match(/"paths"\s*:\s*\{/);
|
|
116
|
+
if (pathsMatch && pathsMatch.index !== undefined) {
|
|
117
|
+
// Insert new path alias after "paths": {
|
|
118
|
+
const insertPos = pathsMatch.index + pathsMatch[0].length;
|
|
119
|
+
const aliasEntry = `\n\t\t\t"${aliasKey}": ${JSON.stringify(aliasValue)},`;
|
|
120
|
+
return (content.slice(0, insertPos) + aliasEntry + content.slice(insertPos));
|
|
121
|
+
}
|
|
122
|
+
// Find compilerOptions to add paths object
|
|
123
|
+
const compilerOptionsMatch = content.match(/"compilerOptions"\s*:\s*\{/);
|
|
124
|
+
if (compilerOptionsMatch && compilerOptionsMatch.index !== undefined) {
|
|
125
|
+
// Check if baseUrl exists
|
|
126
|
+
const hasBaseUrl = /"baseUrl"\s*:/.test(content);
|
|
127
|
+
// Find the end of compilerOptions opening brace
|
|
128
|
+
const insertPos = compilerOptionsMatch.index + compilerOptionsMatch[0].length;
|
|
129
|
+
let insertion = "\n";
|
|
130
|
+
if (!hasBaseUrl) {
|
|
131
|
+
insertion += '\t\t"baseUrl": ".",\n';
|
|
132
|
+
}
|
|
133
|
+
insertion += `\t\t"paths": {\n\t\t\t"${aliasKey}": ${JSON.stringify(aliasValue)}\n\t\t},`;
|
|
134
|
+
return (content.slice(0, insertPos) + insertion + content.slice(insertPos));
|
|
135
|
+
}
|
|
136
|
+
// Can't find compilerOptions, give up
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Update tsconfig.json paths to include missing aliases for opinionated scaffolding
|
|
141
|
+
* Handles both default folders and custom scaffoldSchematics overrides
|
|
142
|
+
*
|
|
143
|
+
* @param folderName - The folder name where the schematic is being created
|
|
144
|
+
* @param sourceRoot - The source root directory (default: "src")
|
|
145
|
+
*/
|
|
146
|
+
async function updateTsconfigPaths(folderName, sourceRoot = "src") {
|
|
147
|
+
if (!folderName) {
|
|
148
|
+
return; // No folder specified
|
|
149
|
+
}
|
|
150
|
+
const tsconfigPath = node_path_1.default.join(process.cwd(), "tsconfig.json");
|
|
151
|
+
const tsconfigBuildPath = node_path_1.default.join(process.cwd(), "tsconfig.build.json");
|
|
152
|
+
// Generate alias from folder name (handles custom names)
|
|
153
|
+
const alias = generatePathAlias(folderName);
|
|
154
|
+
const aliasKey = `${alias}/*`;
|
|
155
|
+
// Track if we updated any config
|
|
156
|
+
let updated = false;
|
|
157
|
+
// Update both tsconfig files if they exist
|
|
158
|
+
for (const configPath of [tsconfigPath, tsconfigBuildPath]) {
|
|
159
|
+
if (!node_fs_1.default.existsSync(configPath)) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
const configContent = node_fs_1.default.readFileSync(configPath, "utf-8");
|
|
164
|
+
// Try JSON parsing first
|
|
165
|
+
const config = parseJsonc(configContent);
|
|
166
|
+
if (config) {
|
|
167
|
+
// JSON parsing succeeded - use structured approach
|
|
168
|
+
// Ensure compilerOptions exists
|
|
169
|
+
if (!config.compilerOptions) {
|
|
170
|
+
config.compilerOptions = {};
|
|
171
|
+
}
|
|
172
|
+
const compilerOptions = config.compilerOptions;
|
|
173
|
+
// Ensure baseUrl is set (required for paths to work)
|
|
174
|
+
if (!compilerOptions.baseUrl) {
|
|
175
|
+
compilerOptions.baseUrl = ".";
|
|
176
|
+
}
|
|
177
|
+
// Determine the correct path value based on baseUrl
|
|
178
|
+
// If baseUrl is "./src" or "src", paths should be relative to src
|
|
179
|
+
// If baseUrl is ".", paths should include the full path from root
|
|
180
|
+
const baseUrl = compilerOptions.baseUrl || ".";
|
|
181
|
+
const isBaseUrlSrc = baseUrl === `./${sourceRoot}` || baseUrl === sourceRoot;
|
|
182
|
+
const aliasValue = isBaseUrlSrc
|
|
183
|
+
? [`./${folderName}/*`]
|
|
184
|
+
: [`./${sourceRoot}/${folderName}/*`];
|
|
185
|
+
// Ensure paths object exists
|
|
186
|
+
if (!compilerOptions.paths) {
|
|
187
|
+
compilerOptions.paths = {};
|
|
188
|
+
}
|
|
189
|
+
const paths = compilerOptions.paths;
|
|
190
|
+
// Only add if it doesn't exist
|
|
191
|
+
if (!paths[aliasKey]) {
|
|
192
|
+
paths[aliasKey] = aliasValue;
|
|
193
|
+
// Write back to file with proper formatting (tab indent)
|
|
194
|
+
node_fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, "\t") + "\n", "utf-8");
|
|
195
|
+
updated = true;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
// JSON parsing failed - use text-based fallback
|
|
200
|
+
// This preserves comments and formatting
|
|
201
|
+
// Try to detect baseUrl from content
|
|
202
|
+
const baseUrlMatch = configContent.match(/"baseUrl"\s*:\s*"([^"]+)"/);
|
|
203
|
+
const baseUrl = baseUrlMatch ? baseUrlMatch[1] : ".";
|
|
204
|
+
const isBaseUrlSrc = baseUrl === `./${sourceRoot}` || baseUrl === sourceRoot;
|
|
205
|
+
const aliasValue = isBaseUrlSrc
|
|
206
|
+
? [`./${folderName}/*`]
|
|
207
|
+
: [`./${sourceRoot}/${folderName}/*`];
|
|
208
|
+
const modifiedContent = addPathAliasTextBased(configContent, aliasKey, aliasValue);
|
|
209
|
+
if (modifiedContent) {
|
|
210
|
+
node_fs_1.default.writeFileSync(configPath, modifiedContent, "utf-8");
|
|
211
|
+
updated = true;
|
|
212
|
+
}
|
|
213
|
+
else if (!configContent.includes(`"${aliasKey}"`)) {
|
|
214
|
+
// Couldn't update and alias doesn't exist
|
|
215
|
+
(0, cli_ui_1.printWarning)(`Could not update ${node_path_1.default.basename(configPath)}. Please add "${aliasKey}" path alias manually`, "tsconfig");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
// Log warning but don't fail the scaffolding process
|
|
221
|
+
(0, cli_ui_1.printWarning)(`Could not update ${node_path_1.default.basename(configPath)}: ${error instanceof Error ? error.message : "Unknown error"}`, "tsconfig");
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Print success message only if we updated something
|
|
225
|
+
if (updated) {
|
|
226
|
+
(0, cli_ui_1.printInfo)(`Path alias ${aliasKey} added`, "tsconfig");
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
exports.updateTsconfigPaths = updateTsconfigPaths;
|
|
230
|
+
/**
|
|
231
|
+
* Get the path alias for a given folder name
|
|
232
|
+
* Used by other utilities to determine the correct import path
|
|
233
|
+
*
|
|
234
|
+
* @param folderName - The folder name
|
|
235
|
+
* @returns The path alias (e.g., "@useCases")
|
|
236
|
+
*/
|
|
237
|
+
function getPathAliasForFolder(folderName) {
|
|
238
|
+
return generatePathAlias(folderName);
|
|
239
|
+
}
|
|
240
|
+
exports.getPathAliasForFolder = getPathAliasForFolder;
|
|
241
|
+
/**
|
|
242
|
+
* Check if tsconfig already has all required path aliases for opinionated mode
|
|
243
|
+
* This can be used to validate project setup
|
|
244
|
+
*
|
|
245
|
+
* @param requiredFolders - List of folder names that need path aliases
|
|
246
|
+
* @returns Object with missing aliases and whether all are present
|
|
247
|
+
*/
|
|
248
|
+
function validateTsconfigPaths(requiredFolders) {
|
|
249
|
+
const tsconfigPath = node_path_1.default.join(process.cwd(), "tsconfig.json");
|
|
250
|
+
if (!node_fs_1.default.existsSync(tsconfigPath)) {
|
|
251
|
+
return {
|
|
252
|
+
valid: false,
|
|
253
|
+
missingAliases: requiredFolders.map((f) => generatePathAlias(f)),
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
try {
|
|
257
|
+
const configContent = node_fs_1.default.readFileSync(tsconfigPath, "utf-8");
|
|
258
|
+
let config;
|
|
259
|
+
try {
|
|
260
|
+
config = JSON.parse(configContent);
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
config = JSON.parse(stripJsonComments(configContent));
|
|
264
|
+
}
|
|
265
|
+
const paths = config.compilerOptions
|
|
266
|
+
?.paths || {};
|
|
267
|
+
const missingAliases = [];
|
|
268
|
+
for (const folder of requiredFolders) {
|
|
269
|
+
const aliasKey = `${generatePathAlias(folder)}/*`;
|
|
270
|
+
if (!paths[aliasKey]) {
|
|
271
|
+
missingAliases.push(aliasKey);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
valid: missingAliases.length === 0,
|
|
276
|
+
missingAliases,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return {
|
|
281
|
+
valid: false,
|
|
282
|
+
missingAliases: requiredFolders.map((f) => generatePathAlias(f)),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
exports.validateTsconfigPaths = validateTsconfigPaths;
|