@kinetixui/cli 0.4.3 → 0.5.1
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/dist/index.js +43 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -58,7 +58,11 @@ async function writeItems(cwd, config, items, overwrite) {
|
|
|
58
58
|
for (const item of items) {
|
|
59
59
|
for (const dep of item.dependencies ?? []) deps.add(dep);
|
|
60
60
|
for (const file of item.files) {
|
|
61
|
-
const dest = targetPath(cwd, config, file);
|
|
61
|
+
const dest = path2.resolve(targetPath(cwd, config, file));
|
|
62
|
+
const root = path2.resolve(cwd);
|
|
63
|
+
if (dest !== root && !dest.startsWith(root + path2.sep)) {
|
|
64
|
+
throw new Error(`Refusing to write outside the project: ${path2.relative(root, dest)}`);
|
|
65
|
+
}
|
|
62
66
|
const rel = path2.relative(cwd, dest);
|
|
63
67
|
if (existsSync2(dest) && !overwrite) {
|
|
64
68
|
skipped.push(rel);
|
|
@@ -76,6 +80,37 @@ async function writeItems(cwd, config, items, overwrite) {
|
|
|
76
80
|
import { existsSync as existsSync3 } from "fs";
|
|
77
81
|
import { spawn } from "child_process";
|
|
78
82
|
import path3 from "path";
|
|
83
|
+
|
|
84
|
+
// src/lib/validate.ts
|
|
85
|
+
function assertComponentName(name) {
|
|
86
|
+
if (typeof name !== "string" || !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(name)) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Invalid component name ${JSON.stringify(name)} \u2014 expected letters, digits and dashes.`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return name;
|
|
92
|
+
}
|
|
93
|
+
function assertRegistryUrl(url) {
|
|
94
|
+
let parsed;
|
|
95
|
+
try {
|
|
96
|
+
parsed = new URL(url);
|
|
97
|
+
} catch {
|
|
98
|
+
throw new Error(`Invalid registry URL: ${JSON.stringify(url)}`);
|
|
99
|
+
}
|
|
100
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
101
|
+
throw new Error(`Registry URL must be http(s), got "${parsed.protocol}".`);
|
|
102
|
+
}
|
|
103
|
+
return parsed.toString();
|
|
104
|
+
}
|
|
105
|
+
var PKG_SPEC = /^(?:@[a-z0-9-~][a-z0-9._-]*\/)?[a-z0-9-~][a-z0-9._-]*(?:@[a-z0-9.^~*x><=|.-]+)?$/i;
|
|
106
|
+
function assertPackageSpec(spec) {
|
|
107
|
+
if (typeof spec !== "string" || spec.startsWith("-") || !PKG_SPEC.test(spec)) {
|
|
108
|
+
throw new Error(`Refusing to install suspicious dependency: ${JSON.stringify(spec)}`);
|
|
109
|
+
}
|
|
110
|
+
return spec;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/lib/pm.ts
|
|
79
114
|
function detectPackageManager(cwd) {
|
|
80
115
|
if (existsSync3(path3.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
81
116
|
if (existsSync3(path3.join(cwd, "yarn.lock"))) return "yarn";
|
|
@@ -87,8 +122,9 @@ function installArgs(pm, packages) {
|
|
|
87
122
|
}
|
|
88
123
|
function runInstall(cwd, pm, packages) {
|
|
89
124
|
if (packages.length === 0) return Promise.resolve();
|
|
125
|
+
const safe = packages.map(assertPackageSpec);
|
|
90
126
|
return new Promise((resolve, reject) => {
|
|
91
|
-
const child = spawn(pm, installArgs(pm,
|
|
127
|
+
const child = spawn(pm, installArgs(pm, safe), {
|
|
92
128
|
cwd,
|
|
93
129
|
stdio: "inherit",
|
|
94
130
|
shell: process.platform === "win32"
|
|
@@ -103,7 +139,7 @@ function runInstall(cwd, pm, packages) {
|
|
|
103
139
|
|
|
104
140
|
// src/lib/registry.ts
|
|
105
141
|
async function fetchRegistryIndex(registryUrl) {
|
|
106
|
-
const url = `${registryUrl.replace(/\/$/, "")}/registry.json`;
|
|
142
|
+
const url = `${assertRegistryUrl(registryUrl).replace(/\/$/, "")}/registry.json`;
|
|
107
143
|
const res = await fetch(url);
|
|
108
144
|
if (!res.ok) {
|
|
109
145
|
throw new Error(`Could not load the registry index (${res.status} at ${url}).`);
|
|
@@ -116,7 +152,8 @@ async function fetchComponentNames(registryUrl) {
|
|
|
116
152
|
return items.filter((item) => item.type === "registry:ui").map((item) => item.name);
|
|
117
153
|
}
|
|
118
154
|
async function fetchRegistryItem(registryUrl, name) {
|
|
119
|
-
const
|
|
155
|
+
const base = assertRegistryUrl(registryUrl).replace(/\/$/, "");
|
|
156
|
+
const url = `${base}/${assertComponentName(name)}.json`;
|
|
120
157
|
const res = await fetch(url);
|
|
121
158
|
if (!res.ok) {
|
|
122
159
|
throw new Error(`Could not find "${name}" in the registry (${res.status} at ${url}).`);
|
|
@@ -129,10 +166,10 @@ async function resolveTree(registryUrl, names) {
|
|
|
129
166
|
while (queue.length) {
|
|
130
167
|
const name = queue.shift();
|
|
131
168
|
if (!name || resolved.has(name)) continue;
|
|
132
|
-
const item = await fetchRegistryItem(registryUrl, name);
|
|
169
|
+
const item = await fetchRegistryItem(registryUrl, assertComponentName(name));
|
|
133
170
|
resolved.set(name, item);
|
|
134
171
|
for (const dep of item.registryDependencies ?? []) {
|
|
135
|
-
if (!resolved.has(dep)) queue.push(dep);
|
|
172
|
+
if (!resolved.has(dep)) queue.push(assertComponentName(dep));
|
|
136
173
|
}
|
|
137
174
|
}
|
|
138
175
|
return resolved;
|