streetui 1.0.0
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/LICENSE +21 -0
- package/README.md +127 -0
- package/dist/bin.cjs +894 -0
- package/dist/bin.cjs.map +1 -0
- package/dist/bin.d.cts +1 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +892 -0
- package/dist/bin.js.map +1 -0
- package/dist/compile-B0q07Hzq.d.cts +656 -0
- package/dist/compile-B0q07Hzq.d.ts +656 -0
- package/dist/create-bin.cjs +896 -0
- package/dist/create-bin.cjs.map +1 -0
- package/dist/create-bin.d.cts +1 -0
- package/dist/create-bin.d.ts +1 -0
- package/dist/create-bin.js +894 -0
- package/dist/create-bin.js.map +1 -0
- package/dist/hydration-diagnostics-BE6xVWD1.d.cts +89 -0
- package/dist/hydration-diagnostics-Bck5dMbz.d.ts +89 -0
- package/dist/index.cjs +4284 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1759 -0
- package/dist/index.d.ts +1759 -0
- package/dist/index.js +4114 -0
- package/dist/index.js.map +1 -0
- package/dist/server-84Rz4g8W.d.cts +165 -0
- package/dist/server-D9GPmB49.d.ts +165 -0
- package/dist/server.cjs +972 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +2 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +940 -0
- package/dist/server.js.map +1 -0
- package/dist/testing.cjs +1754 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +113 -0
- package/dist/testing.d.ts +113 -0
- package/dist/testing.js +1719 -0
- package/dist/testing.js.map +1 -0
- package/package.json +113 -0
- package/templates/basic/README.md +39 -0
- package/templates/basic/_gitignore +15 -0
- package/templates/basic/_package.json +21 -0
- package/templates/basic/public/styles.css +40 -0
- package/templates/basic/src/app.ts +62 -0
- package/templates/basic/src/main.ts +39 -0
- package/templates/basic/src/server.ts +40 -0
- package/templates/basic/streetui.config.ts +6 -0
- package/templates/basic/tsconfig.json +16 -0
- package/templates/ssr/README.md +46 -0
- package/templates/ssr/_gitignore +15 -0
- package/templates/ssr/_package.json +21 -0
- package/templates/ssr/public/favicon.svg +4 -0
- package/templates/ssr/public/styles.css +61 -0
- package/templates/ssr/src/app.ts +135 -0
- package/templates/ssr/src/main.ts +53 -0
- package/templates/ssr/src/server.ts +47 -0
- package/templates/ssr/streetui.config.ts +14 -0
- package/templates/ssr/tsconfig.json +16 -0
|
@@ -0,0 +1,896 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// ../cli/src/args.ts
|
|
5
|
+
var VALUE_FLAGS = /* @__PURE__ */ new Set(["port", "host", "template", "dir"]);
|
|
6
|
+
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["help", "version"]);
|
|
7
|
+
var SHORT = { h: "help", v: "version", p: "port" };
|
|
8
|
+
function parseArgs(argv2) {
|
|
9
|
+
let command;
|
|
10
|
+
const positionals = [];
|
|
11
|
+
const unknown = [];
|
|
12
|
+
let help = false;
|
|
13
|
+
let version = false;
|
|
14
|
+
let port;
|
|
15
|
+
let host;
|
|
16
|
+
let template;
|
|
17
|
+
let dir;
|
|
18
|
+
for (let i = 0; i < argv2.length; i++) {
|
|
19
|
+
const token = argv2[i];
|
|
20
|
+
if (token === void 0) continue;
|
|
21
|
+
if (token.startsWith("--") || token.startsWith("-") && token.length > 1 && !/^-\d/.test(token)) {
|
|
22
|
+
const isLong = token.startsWith("--");
|
|
23
|
+
const raw = isLong ? token.slice(2) : token.slice(1);
|
|
24
|
+
const eq = raw.indexOf("=");
|
|
25
|
+
let name = eq >= 0 ? raw.slice(0, eq) : raw;
|
|
26
|
+
let inlineValue = eq >= 0 ? raw.slice(eq + 1) : void 0;
|
|
27
|
+
if (!isLong) name = SHORT[name] ?? name;
|
|
28
|
+
if (BOOLEAN_FLAGS.has(name)) {
|
|
29
|
+
if (name === "help") help = true;
|
|
30
|
+
else if (name === "version") version = true;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (VALUE_FLAGS.has(name)) {
|
|
34
|
+
const value = inlineValue ?? argv2[++i];
|
|
35
|
+
if (value === void 0) {
|
|
36
|
+
unknown.push(`${name} (missing value)`);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (name === "port") {
|
|
40
|
+
const n = Number.parseInt(value, 10);
|
|
41
|
+
port = Number.isFinite(n) && n > 0 ? n : void 0;
|
|
42
|
+
if (port === void 0) unknown.push(`port (invalid: ${value})`);
|
|
43
|
+
} else if (name === "host") host = value;
|
|
44
|
+
else if (name === "template") template = value;
|
|
45
|
+
else if (name === "dir") dir = value;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
unknown.push(name);
|
|
49
|
+
inlineValue = void 0;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (command === void 0) command = token;
|
|
53
|
+
else positionals.push(token);
|
|
54
|
+
}
|
|
55
|
+
return { command, positionals, help, version, port, host, template, dir, unknown };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ../cli/src/logger.ts
|
|
59
|
+
var useColor = process.env["NO_COLOR"] === void 0 && process.env["FORCE_COLOR"] !== "0" && (process.stdout.isTTY === true || process.env["FORCE_COLOR"] !== void 0);
|
|
60
|
+
function paint(code, text) {
|
|
61
|
+
return useColor ? `\x1B[${code}m${text}\x1B[0m` : text;
|
|
62
|
+
}
|
|
63
|
+
var style = {
|
|
64
|
+
bold: (t) => paint(1, t),
|
|
65
|
+
dim: (t) => paint(2, t),
|
|
66
|
+
red: (t) => paint(31, t),
|
|
67
|
+
green: (t) => paint(32, t),
|
|
68
|
+
yellow: (t) => paint(33, t),
|
|
69
|
+
blue: (t) => paint(34, t),
|
|
70
|
+
cyan: (t) => paint(36, t)
|
|
71
|
+
};
|
|
72
|
+
var BRAND = style.bold(style.cyan("streetui"));
|
|
73
|
+
function createLogger(prefix = BRAND) {
|
|
74
|
+
return {
|
|
75
|
+
info: (m) => console.log(`${prefix} ${m}`),
|
|
76
|
+
success: (m) => console.log(`${prefix} ${style.green(m)}`),
|
|
77
|
+
warn: (m) => console.warn(`${prefix} ${style.yellow(m)}`),
|
|
78
|
+
error: (m) => console.error(`${prefix} ${style.red(m)}`),
|
|
79
|
+
plain: (m) => console.log(m)
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ../cli/src/diagnostics.ts
|
|
84
|
+
var CliError = class extends Error {
|
|
85
|
+
suggestion;
|
|
86
|
+
/** Process exit code to use when this error reaches the top level. */
|
|
87
|
+
exitCode;
|
|
88
|
+
constructor(message, options) {
|
|
89
|
+
super(message);
|
|
90
|
+
this.name = "CliError";
|
|
91
|
+
this.suggestion = options?.suggestion;
|
|
92
|
+
this.exitCode = options?.exitCode ?? 1;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var KNOWN_DSL_METHODS = [
|
|
96
|
+
"app",
|
|
97
|
+
"page",
|
|
98
|
+
"section",
|
|
99
|
+
"container",
|
|
100
|
+
"heading",
|
|
101
|
+
"text",
|
|
102
|
+
"button",
|
|
103
|
+
"link",
|
|
104
|
+
"input",
|
|
105
|
+
"form",
|
|
106
|
+
"list",
|
|
107
|
+
"listOf",
|
|
108
|
+
"when",
|
|
109
|
+
"errorBoundary"
|
|
110
|
+
];
|
|
111
|
+
function fromEsbuildMessage(msg) {
|
|
112
|
+
const problem = { message: msg.text };
|
|
113
|
+
const loc = msg.location;
|
|
114
|
+
if (loc === null) return withSuggestion(problem);
|
|
115
|
+
return withSuggestion({
|
|
116
|
+
message: msg.text,
|
|
117
|
+
file: loc.file,
|
|
118
|
+
line: loc.line,
|
|
119
|
+
column: loc.column + 1,
|
|
120
|
+
// esbuild columns are 0-based; humans count from 1.
|
|
121
|
+
lineText: loc.lineText
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function withSuggestion(problem) {
|
|
125
|
+
const unknownApi = /Property '(\w+)' does not exist|'(\w+)' is not a function/.exec(problem.message);
|
|
126
|
+
const missingModule = /Could not resolve ["']([^"']+)["']/.exec(problem.message);
|
|
127
|
+
if (missingModule) {
|
|
128
|
+
const spec = missingModule[1] ?? "";
|
|
129
|
+
if (spec.startsWith("@streetui/")) {
|
|
130
|
+
return {
|
|
131
|
+
...problem,
|
|
132
|
+
suggestion: `Install the StreetUI packages (run "npm install") \u2014 "${spec}" is not resolvable yet.`
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return { ...problem, suggestion: `Check the import path "${spec}" \u2014 the file or package could not be found.` };
|
|
136
|
+
}
|
|
137
|
+
if (unknownApi) {
|
|
138
|
+
const name = unknownApi[1] ?? unknownApi[2] ?? "";
|
|
139
|
+
const near = KNOWN_DSL_METHODS.find((m) => m.toLowerCase() === name.toLowerCase() && m !== name) ?? KNOWN_DSL_METHODS.find((m) => m.startsWith(name.slice(0, 3)));
|
|
140
|
+
if (near !== void 0 && name.length > 0) {
|
|
141
|
+
return { ...problem, suggestion: `Did you mean "${near}"? Check the StreetUI DSL API.` };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return problem;
|
|
145
|
+
}
|
|
146
|
+
function formatProblem(problem) {
|
|
147
|
+
const lines = [];
|
|
148
|
+
if (problem.file !== void 0) {
|
|
149
|
+
const pos = problem.line !== void 0 ? `:${problem.line}${problem.column !== void 0 ? `:${problem.column}` : ""}` : "";
|
|
150
|
+
lines.push(style.cyan(`${problem.file}${pos}`));
|
|
151
|
+
}
|
|
152
|
+
lines.push(problem.message);
|
|
153
|
+
if (problem.lineText !== void 0 && problem.lineText.trim().length > 0) {
|
|
154
|
+
lines.push(style.dim(` | ${problem.lineText.trim()}`));
|
|
155
|
+
}
|
|
156
|
+
if (problem.suggestion !== void 0) {
|
|
157
|
+
lines.push("");
|
|
158
|
+
lines.push(`${style.yellow("Suggestion:")} ${problem.suggestion}`);
|
|
159
|
+
}
|
|
160
|
+
return lines.join("\n");
|
|
161
|
+
}
|
|
162
|
+
function formatBuildFailure(problems) {
|
|
163
|
+
const header = style.red(style.bold("StreetUI build error"));
|
|
164
|
+
const count = problems.length === 1 ? "1 error" : `${problems.length} errors`;
|
|
165
|
+
const blocks = problems.map((p) => formatProblem(p)).join("\n\n");
|
|
166
|
+
return `${header} (${count})
|
|
167
|
+
|
|
168
|
+
${blocks}`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ../cli/src/project.ts
|
|
172
|
+
var import_node_fs2 = require("fs");
|
|
173
|
+
var import_node_path2 = require("path");
|
|
174
|
+
|
|
175
|
+
// ../cli/src/config.ts
|
|
176
|
+
var import_esbuild = require("esbuild");
|
|
177
|
+
var import_promises = require("fs/promises");
|
|
178
|
+
var import_node_fs = require("fs");
|
|
179
|
+
var import_node_path = require("path");
|
|
180
|
+
var import_node_url = require("url");
|
|
181
|
+
var DEFAULTS = {
|
|
182
|
+
port: 3e3,
|
|
183
|
+
host: "localhost",
|
|
184
|
+
clientEntry: "src/main.ts",
|
|
185
|
+
serverEntry: "src/server.ts",
|
|
186
|
+
outDir: "dist",
|
|
187
|
+
publicDir: "public"
|
|
188
|
+
};
|
|
189
|
+
var CONFIG_FILENAMES = ["streetui.config.ts", "streetui.config.mjs", "streetui.config.js"];
|
|
190
|
+
function findConfigFile(root) {
|
|
191
|
+
for (const name of CONFIG_FILENAMES) {
|
|
192
|
+
const candidate = (0, import_node_path.join)(root, name);
|
|
193
|
+
if ((0, import_node_fs.existsSync)(candidate)) return candidate;
|
|
194
|
+
}
|
|
195
|
+
return void 0;
|
|
196
|
+
}
|
|
197
|
+
async function importConfigFile(file) {
|
|
198
|
+
if (!file.endsWith(".ts")) {
|
|
199
|
+
const mod = await import((0, import_node_url.pathToFileURL)(file).href);
|
|
200
|
+
return mod.default ?? {};
|
|
201
|
+
}
|
|
202
|
+
const result = await (0, import_esbuild.build)({
|
|
203
|
+
entryPoints: [file],
|
|
204
|
+
bundle: true,
|
|
205
|
+
write: false,
|
|
206
|
+
format: "esm",
|
|
207
|
+
platform: "node",
|
|
208
|
+
// Keep node builtins and any deps external — we only want the config value.
|
|
209
|
+
packages: "external",
|
|
210
|
+
logLevel: "silent"
|
|
211
|
+
});
|
|
212
|
+
const code = result.outputFiles[0]?.text ?? "";
|
|
213
|
+
const outFile = (0, import_node_path.join)((0, import_node_path.dirname)(file), `.streetui.config.${Date.now()}.mjs`);
|
|
214
|
+
try {
|
|
215
|
+
await (0, import_promises.writeFile)(outFile, code, "utf8");
|
|
216
|
+
const mod = await import((0, import_node_url.pathToFileURL)(outFile).href);
|
|
217
|
+
return mod.default ?? {};
|
|
218
|
+
} finally {
|
|
219
|
+
await (0, import_promises.rm)(outFile, { force: true });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function toAbsolute(root, p) {
|
|
223
|
+
return (0, import_node_path.isAbsolute)(p) ? p : (0, import_node_path.resolve)(root, p);
|
|
224
|
+
}
|
|
225
|
+
async function loadConfig(root) {
|
|
226
|
+
const absRoot = (0, import_node_path.resolve)(root);
|
|
227
|
+
const file = findConfigFile(absRoot);
|
|
228
|
+
const user = file ? await importConfigFile(file) : {};
|
|
229
|
+
return {
|
|
230
|
+
root: absRoot,
|
|
231
|
+
port: user.port ?? DEFAULTS.port,
|
|
232
|
+
host: user.host ?? DEFAULTS.host,
|
|
233
|
+
clientEntry: toAbsolute(absRoot, user.clientEntry ?? DEFAULTS.clientEntry),
|
|
234
|
+
serverEntry: toAbsolute(absRoot, user.serverEntry ?? DEFAULTS.serverEntry),
|
|
235
|
+
outDir: toAbsolute(absRoot, user.outDir ?? DEFAULTS.outDir),
|
|
236
|
+
publicDir: toAbsolute(absRoot, user.publicDir ?? DEFAULTS.publicDir)
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ../cli/src/project.ts
|
|
241
|
+
function readPackageJson(root) {
|
|
242
|
+
const pkgPath = (0, import_node_path2.join)(root, "package.json");
|
|
243
|
+
if (!(0, import_node_fs2.existsSync)(pkgPath)) {
|
|
244
|
+
throw new CliError(`No package.json found in ${root}.`, {
|
|
245
|
+
suggestion: 'Run this command from the root of a StreetUI project, or create one with "npm create streetui@latest".'
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
let raw;
|
|
249
|
+
try {
|
|
250
|
+
raw = (0, import_node_fs2.readFileSync)(pkgPath, "utf8");
|
|
251
|
+
} catch (err) {
|
|
252
|
+
throw new CliError(`Could not read ${pkgPath}: ${err.message}`);
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
return JSON.parse(raw);
|
|
256
|
+
} catch (err) {
|
|
257
|
+
throw new CliError(`package.json is not valid JSON: ${err.message}`, {
|
|
258
|
+
suggestion: "Fix the syntax error in package.json and try again."
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function dependsOnStreetUI(pkg) {
|
|
263
|
+
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
264
|
+
return Object.keys(deps).some((name) => name === "streetui" || name.startsWith("@streetui/"));
|
|
265
|
+
}
|
|
266
|
+
async function resolveProject(cwd, options) {
|
|
267
|
+
const root = (0, import_node_path2.resolve)(cwd);
|
|
268
|
+
const packageJson = readPackageJson(root);
|
|
269
|
+
if (!dependsOnStreetUI(packageJson)) {
|
|
270
|
+
throw new CliError(`${root} does not look like a StreetUI project.`, {
|
|
271
|
+
suggestion: 'Its package.json declares no "@streetui/*" dependency. Create a project with "npm create streetui@latest".'
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
let config;
|
|
275
|
+
try {
|
|
276
|
+
config = await loadConfig(root);
|
|
277
|
+
} catch (err) {
|
|
278
|
+
if (err instanceof CliError) throw err;
|
|
279
|
+
throw new CliError(`Failed to load streetui.config: ${err.message}`, {
|
|
280
|
+
suggestion: "Check streetui.config.ts for syntax or import errors."
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
if (options?.requireEntry === true && !(0, import_node_fs2.existsSync)(config.clientEntry)) {
|
|
284
|
+
throw new CliError(`Client entry not found: ${config.clientEntry}`, {
|
|
285
|
+
suggestion: 'Create the entry file, or set "clientEntry" in streetui.config.ts to point at your app entry.'
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
return { root, packageJson, config };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// ../cli/src/build.ts
|
|
292
|
+
var import_esbuild2 = require("esbuild");
|
|
293
|
+
var import_promises2 = require("fs/promises");
|
|
294
|
+
var import_node_fs3 = require("fs");
|
|
295
|
+
var import_node_path3 = require("path");
|
|
296
|
+
|
|
297
|
+
// ../cli/src/env.ts
|
|
298
|
+
var PUBLIC_ENV_PREFIX = "STREETUI_PUBLIC_";
|
|
299
|
+
function clientEnvDefine(mode, env = process.env) {
|
|
300
|
+
const define = {
|
|
301
|
+
"process.env.NODE_ENV": JSON.stringify(mode)
|
|
302
|
+
};
|
|
303
|
+
for (const [key, value] of Object.entries(env)) {
|
|
304
|
+
if (key.startsWith(PUBLIC_ENV_PREFIX) && value !== void 0) {
|
|
305
|
+
define[`process.env.${key}`] = JSON.stringify(value);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return define;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ../cli/src/build.ts
|
|
312
|
+
function toProblems(messages) {
|
|
313
|
+
return messages.map((m) => fromEsbuildMessage({ text: m.text, location: m.location }));
|
|
314
|
+
}
|
|
315
|
+
function baseOptions(mode) {
|
|
316
|
+
return {
|
|
317
|
+
bundle: true,
|
|
318
|
+
format: "esm",
|
|
319
|
+
sourcemap: true,
|
|
320
|
+
logLevel: "silent",
|
|
321
|
+
define: {
|
|
322
|
+
// Public build-time constants. Server secrets are never injected here.
|
|
323
|
+
"process.env.NODE_ENV": JSON.stringify(mode)
|
|
324
|
+
},
|
|
325
|
+
minify: mode === "production"
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
async function buildProject(project, mode = "production") {
|
|
329
|
+
const { config } = project;
|
|
330
|
+
const clientDir = (0, import_node_path3.join)(config.outDir, "client");
|
|
331
|
+
const serverDir = (0, import_node_path3.join)(config.outDir, "server");
|
|
332
|
+
await (0, import_promises2.rm)(config.outDir, { recursive: true, force: true });
|
|
333
|
+
await (0, import_promises2.mkdir)(clientDir, { recursive: true });
|
|
334
|
+
const errors = [];
|
|
335
|
+
await (0, import_esbuild2.build)({
|
|
336
|
+
...baseOptions(mode),
|
|
337
|
+
entryPoints: [config.clientEntry],
|
|
338
|
+
outfile: (0, import_node_path3.join)(clientDir, "main.js"),
|
|
339
|
+
platform: "browser",
|
|
340
|
+
target: ["es2022"],
|
|
341
|
+
// Only STREETUI_PUBLIC_* env vars reach the browser (plus NODE_ENV).
|
|
342
|
+
define: clientEnvDefine(mode)
|
|
343
|
+
}).catch((err) => {
|
|
344
|
+
errors.push(...toProblems(err.errors ?? []));
|
|
345
|
+
return void 0;
|
|
346
|
+
});
|
|
347
|
+
const hasServerEntry = (0, import_node_fs3.existsSync)(config.serverEntry);
|
|
348
|
+
if (hasServerEntry) {
|
|
349
|
+
await (0, import_promises2.mkdir)(serverDir, { recursive: true });
|
|
350
|
+
await (0, import_esbuild2.build)({
|
|
351
|
+
...baseOptions(mode),
|
|
352
|
+
entryPoints: [config.serverEntry],
|
|
353
|
+
outfile: (0, import_node_path3.join)(serverDir, "server.js"),
|
|
354
|
+
platform: "node",
|
|
355
|
+
target: ["node18"],
|
|
356
|
+
packages: "external"
|
|
357
|
+
}).catch((err) => {
|
|
358
|
+
errors.push(...toProblems(err.errors ?? []));
|
|
359
|
+
return void 0;
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
if (errors.length > 0) {
|
|
363
|
+
throw new CliError(formatBuildFailure(errors), { exitCode: 1 });
|
|
364
|
+
}
|
|
365
|
+
if ((0, import_node_fs3.existsSync)(config.publicDir)) {
|
|
366
|
+
await (0, import_promises2.cp)(config.publicDir, clientDir, { recursive: true });
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
clientDir,
|
|
370
|
+
serverDir,
|
|
371
|
+
clientBundle: (0, import_node_path3.join)(clientDir, "main.js"),
|
|
372
|
+
serverBundle: (0, import_node_path3.join)(serverDir, "server.js")
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ../cli/src/dev.ts
|
|
377
|
+
var import_esbuild3 = require("esbuild");
|
|
378
|
+
var import_promises4 = require("fs/promises");
|
|
379
|
+
var import_node_fs4 = require("fs");
|
|
380
|
+
var import_node_path5 = require("path");
|
|
381
|
+
|
|
382
|
+
// ../cli/src/serve.ts
|
|
383
|
+
var import_node_http = require("http");
|
|
384
|
+
var import_promises3 = require("fs/promises");
|
|
385
|
+
var import_node_path4 = require("path");
|
|
386
|
+
var import_node_url2 = require("url");
|
|
387
|
+
var MIME = {
|
|
388
|
+
".js": "text/javascript; charset=utf-8",
|
|
389
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
390
|
+
".css": "text/css; charset=utf-8",
|
|
391
|
+
".html": "text/html; charset=utf-8",
|
|
392
|
+
".json": "application/json; charset=utf-8",
|
|
393
|
+
".svg": "image/svg+xml",
|
|
394
|
+
".png": "image/png",
|
|
395
|
+
".jpg": "image/jpeg",
|
|
396
|
+
".jpeg": "image/jpeg",
|
|
397
|
+
".gif": "image/gif",
|
|
398
|
+
".ico": "image/x-icon",
|
|
399
|
+
".woff": "font/woff",
|
|
400
|
+
".woff2": "font/woff2",
|
|
401
|
+
".map": "application/json; charset=utf-8"
|
|
402
|
+
};
|
|
403
|
+
var ReloadHub = class _ReloadHub {
|
|
404
|
+
clients = /* @__PURE__ */ new Set();
|
|
405
|
+
static PATH = "/__streetui_reload";
|
|
406
|
+
/** The snippet injected before `</body>` so the page listens for reloads. */
|
|
407
|
+
static snippet = `<script>(function(){try{new EventSource("${_ReloadHub.PATH}").onmessage=function(e){if(e.data==="reload")location.reload()}}catch(_){}})();</script>`;
|
|
408
|
+
handle(_req, res) {
|
|
409
|
+
res.writeHead(200, {
|
|
410
|
+
"Content-Type": "text/event-stream",
|
|
411
|
+
"Cache-Control": "no-cache",
|
|
412
|
+
Connection: "keep-alive"
|
|
413
|
+
});
|
|
414
|
+
res.write(": connected\n\n");
|
|
415
|
+
this.clients.add(res);
|
|
416
|
+
res.on("close", () => this.clients.delete(res));
|
|
417
|
+
}
|
|
418
|
+
/** Tell every connected browser to reload. */
|
|
419
|
+
triggerReload() {
|
|
420
|
+
for (const res of this.clients) res.write("data: reload\n\n");
|
|
421
|
+
}
|
|
422
|
+
closeAll() {
|
|
423
|
+
for (const res of this.clients) res.end();
|
|
424
|
+
this.clients.clear();
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
function resolveStatic(clientDir, urlPath) {
|
|
428
|
+
let decoded;
|
|
429
|
+
try {
|
|
430
|
+
decoded = decodeURIComponent(urlPath.split("?")[0] ?? "");
|
|
431
|
+
} catch {
|
|
432
|
+
return void 0;
|
|
433
|
+
}
|
|
434
|
+
if (decoded.includes("\0")) return void 0;
|
|
435
|
+
const clean = (0, import_node_path4.normalize)(decoded).replace(/^(\.\.[/\\])+/, "");
|
|
436
|
+
const full = (0, import_node_path4.join)(clientDir, clean);
|
|
437
|
+
const rel = (0, import_node_path4.relative)(clientDir, full);
|
|
438
|
+
if (rel === "" || !rel.startsWith("..") && !(0, import_node_path4.isAbsolute)(rel)) return full;
|
|
439
|
+
return void 0;
|
|
440
|
+
}
|
|
441
|
+
async function tryServeStatic(clientDir, urlPath, res, devMode) {
|
|
442
|
+
const full = resolveStatic(clientDir, urlPath);
|
|
443
|
+
if (full === void 0) return false;
|
|
444
|
+
try {
|
|
445
|
+
const info = await (0, import_promises3.stat)(full);
|
|
446
|
+
if (!info.isFile()) return false;
|
|
447
|
+
const body = await (0, import_promises3.readFile)(full);
|
|
448
|
+
res.writeHead(200, {
|
|
449
|
+
"Content-Type": MIME[(0, import_node_path4.extname)(full)] ?? "application/octet-stream",
|
|
450
|
+
// Never let a browser MIME-sniff a served asset into something executable.
|
|
451
|
+
"X-Content-Type-Options": "nosniff",
|
|
452
|
+
// Dev must always re-fetch; production may cache immutable build output.
|
|
453
|
+
"Cache-Control": devMode ? "no-cache" : "public, max-age=3600"
|
|
454
|
+
});
|
|
455
|
+
res.end(body);
|
|
456
|
+
return true;
|
|
457
|
+
} catch {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
async function loadRender(serverBundle) {
|
|
462
|
+
const mod = await import(`${(0, import_node_url2.pathToFileURL)(serverBundle).href}?t=${Date.now()}`);
|
|
463
|
+
const candidate = mod.render ?? (typeof mod.default === "function" ? mod.default : mod.default?.render);
|
|
464
|
+
if (typeof candidate !== "function") {
|
|
465
|
+
throw new Error(`Server entry ${serverBundle} must export a "render(request)" function.`);
|
|
466
|
+
}
|
|
467
|
+
return candidate;
|
|
468
|
+
}
|
|
469
|
+
function injectReload(html) {
|
|
470
|
+
if (html.includes("</body>")) return html.replace("</body>", `${ReloadHub.snippet}</body>`);
|
|
471
|
+
return html + ReloadHub.snippet;
|
|
472
|
+
}
|
|
473
|
+
async function startServer(options) {
|
|
474
|
+
let cachedRender;
|
|
475
|
+
const getRender = async () => {
|
|
476
|
+
if (options.devMode === true) return loadRender(options.serverBundle);
|
|
477
|
+
if (cachedRender === void 0) cachedRender = await loadRender(options.serverBundle);
|
|
478
|
+
return cachedRender;
|
|
479
|
+
};
|
|
480
|
+
await getRender();
|
|
481
|
+
const server = (0, import_node_http.createServer)((req, res) => {
|
|
482
|
+
void handleRequest(req, res, getRender, options);
|
|
483
|
+
});
|
|
484
|
+
await new Promise((resolvePromise, reject) => {
|
|
485
|
+
server.once("error", reject);
|
|
486
|
+
server.listen(options.port, options.host, () => {
|
|
487
|
+
server.off("error", reject);
|
|
488
|
+
resolvePromise();
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
const url = `http://${options.host}:${options.port}`;
|
|
492
|
+
return {
|
|
493
|
+
server,
|
|
494
|
+
url,
|
|
495
|
+
close: () => new Promise((resolveClose) => {
|
|
496
|
+
options.reload?.closeAll();
|
|
497
|
+
server.close(() => resolveClose());
|
|
498
|
+
})
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
async function handleRequest(req, res, getRender, options) {
|
|
502
|
+
const url = req.url ?? "/";
|
|
503
|
+
if (options.reload && url === ReloadHub.PATH) {
|
|
504
|
+
options.reload.handle(req, res);
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
if ((0, import_node_path4.extname)(url.split("?")[0] ?? "") !== "") {
|
|
508
|
+
const served = await tryServeStatic(options.clientDir, url, res, options.devMode === true);
|
|
509
|
+
if (served) return;
|
|
510
|
+
}
|
|
511
|
+
try {
|
|
512
|
+
const render = await getRender();
|
|
513
|
+
const result = await render({
|
|
514
|
+
url,
|
|
515
|
+
method: req.method ?? "GET",
|
|
516
|
+
headers: req.headers
|
|
517
|
+
});
|
|
518
|
+
const status = result.status ?? 200;
|
|
519
|
+
const html = options.reload ? injectReload(result.html) : result.html;
|
|
520
|
+
res.writeHead(status, { "Content-Type": "text/html; charset=utf-8", ...result.headers });
|
|
521
|
+
res.end(html);
|
|
522
|
+
} catch (err) {
|
|
523
|
+
console.error(`[StreetUI] render error for ${url}:`, err);
|
|
524
|
+
res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
|
|
525
|
+
if (options.devMode === true) {
|
|
526
|
+
const message = err instanceof Error ? err.stack ?? err.message : String(err);
|
|
527
|
+
res.end(`StreetUI server error while rendering ${url}:
|
|
528
|
+
|
|
529
|
+
${message}`);
|
|
530
|
+
} else {
|
|
531
|
+
res.end("Internal Server Error");
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// ../cli/src/dev.ts
|
|
537
|
+
function reportResult(label, errors, logger, reload) {
|
|
538
|
+
if (errors.length > 0) {
|
|
539
|
+
const problems = errors.map((m) => fromEsbuildMessage({ text: m.text, location: m.location }));
|
|
540
|
+
logger.error(`${label} rebuild failed:`);
|
|
541
|
+
logger.plain(formatBuildFailure(problems));
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
reload.triggerReload();
|
|
545
|
+
}
|
|
546
|
+
async function runDev(options) {
|
|
547
|
+
const { project, logger } = options;
|
|
548
|
+
const { config } = project;
|
|
549
|
+
const clientDir = (0, import_node_path5.join)(config.outDir, "client");
|
|
550
|
+
const serverDir = (0, import_node_path5.join)(config.outDir, "server");
|
|
551
|
+
const serverBundle = (0, import_node_path5.join)(serverDir, "server.js");
|
|
552
|
+
const reload = new ReloadHub();
|
|
553
|
+
await (0, import_promises4.rm)(config.outDir, { recursive: true, force: true });
|
|
554
|
+
await (0, import_promises4.mkdir)(clientDir, { recursive: true });
|
|
555
|
+
await (0, import_promises4.mkdir)(serverDir, { recursive: true });
|
|
556
|
+
const shared = {
|
|
557
|
+
bundle: true,
|
|
558
|
+
format: "esm",
|
|
559
|
+
sourcemap: true,
|
|
560
|
+
logLevel: "silent",
|
|
561
|
+
define: { "process.env.NODE_ENV": JSON.stringify("development") }
|
|
562
|
+
};
|
|
563
|
+
const contexts = [];
|
|
564
|
+
const clientCtx = await (0, import_esbuild3.context)({
|
|
565
|
+
...shared,
|
|
566
|
+
entryPoints: [config.clientEntry],
|
|
567
|
+
outfile: (0, import_node_path5.join)(clientDir, "main.js"),
|
|
568
|
+
platform: "browser",
|
|
569
|
+
target: ["es2022"],
|
|
570
|
+
define: clientEnvDefine("development"),
|
|
571
|
+
plugins: [
|
|
572
|
+
{
|
|
573
|
+
name: "streetui-client-reload",
|
|
574
|
+
setup(builder) {
|
|
575
|
+
builder.onEnd((result) => reportResult("Client", result.errors, logger, reload));
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
]
|
|
579
|
+
});
|
|
580
|
+
contexts.push(clientCtx);
|
|
581
|
+
const hasServerEntry = (0, import_node_fs4.existsSync)(config.serverEntry);
|
|
582
|
+
if (hasServerEntry) {
|
|
583
|
+
const serverCtx = await (0, import_esbuild3.context)({
|
|
584
|
+
...shared,
|
|
585
|
+
entryPoints: [config.serverEntry],
|
|
586
|
+
outfile: serverBundle,
|
|
587
|
+
platform: "node",
|
|
588
|
+
target: ["node18"],
|
|
589
|
+
packages: "external",
|
|
590
|
+
plugins: [
|
|
591
|
+
{
|
|
592
|
+
name: "streetui-server-reload",
|
|
593
|
+
setup(builder) {
|
|
594
|
+
builder.onEnd((result) => {
|
|
595
|
+
if (result.errors.length > 0) {
|
|
596
|
+
reportResult("Server", result.errors, logger, reload);
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
]
|
|
602
|
+
});
|
|
603
|
+
contexts.push(serverCtx);
|
|
604
|
+
}
|
|
605
|
+
await Promise.all(contexts.map((c) => c.rebuild().catch(() => void 0)));
|
|
606
|
+
await Promise.all(contexts.map((c) => c.watch()));
|
|
607
|
+
if ((0, import_node_fs4.existsSync)(config.publicDir)) {
|
|
608
|
+
await (0, import_promises4.cp)(config.publicDir, clientDir, { recursive: true });
|
|
609
|
+
}
|
|
610
|
+
const host = options.host ?? config.host;
|
|
611
|
+
const port = options.port ?? config.port;
|
|
612
|
+
let running;
|
|
613
|
+
if (hasServerEntry) {
|
|
614
|
+
running = await startServer({ clientDir, serverBundle, host, port, reload, devMode: true });
|
|
615
|
+
logger.success(`Dev server running at ${running.url}`);
|
|
616
|
+
logger.info("Watching for changes\u2026 (press Ctrl+C to stop)");
|
|
617
|
+
} else {
|
|
618
|
+
logger.warn("No server entry found \u2014 client bundle is being watched, but no dev server was started.");
|
|
619
|
+
}
|
|
620
|
+
const url = running?.url ?? `http://${host}:${port}`;
|
|
621
|
+
return {
|
|
622
|
+
url,
|
|
623
|
+
stop: async () => {
|
|
624
|
+
await Promise.all(contexts.map((c) => c.dispose()));
|
|
625
|
+
await running?.close();
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// ../cli/src/start.ts
|
|
631
|
+
var import_node_fs5 = require("fs");
|
|
632
|
+
var import_node_path6 = require("path");
|
|
633
|
+
async function runStart(options) {
|
|
634
|
+
const { project, logger } = options;
|
|
635
|
+
const { config } = project;
|
|
636
|
+
const clientDir = (0, import_node_path6.join)(config.outDir, "client");
|
|
637
|
+
const serverBundle = (0, import_node_path6.join)(config.outDir, "server", "server.js");
|
|
638
|
+
if (!(0, import_node_fs5.existsSync)(serverBundle)) {
|
|
639
|
+
logger.info("No production build found \u2014 building first\u2026");
|
|
640
|
+
await buildProject(project, "production");
|
|
641
|
+
}
|
|
642
|
+
if (!(0, import_node_fs5.existsSync)(serverBundle)) {
|
|
643
|
+
throw new CliError("Production build did not produce a server bundle.", {
|
|
644
|
+
suggestion: "Ensure your project has a server entry (default src/server.ts) that exports render()."
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
const host = options.host ?? config.host;
|
|
648
|
+
const port = options.port ?? config.port;
|
|
649
|
+
const running = await startServer({ clientDir, serverBundle, host, port });
|
|
650
|
+
logger.success(`Production server running at ${running.url}`);
|
|
651
|
+
return running;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// ../cli/src/create.ts
|
|
655
|
+
var import_promises5 = require("fs/promises");
|
|
656
|
+
var import_node_fs7 = require("fs");
|
|
657
|
+
var import_node_path8 = require("path");
|
|
658
|
+
|
|
659
|
+
// ../cli/src/templates.ts
|
|
660
|
+
var import_node_fs6 = require("fs");
|
|
661
|
+
var import_node_path7 = require("path");
|
|
662
|
+
var import_node_url3 = require("url");
|
|
663
|
+
var import_meta = {};
|
|
664
|
+
var TEMPLATES = ["basic", "ssr"];
|
|
665
|
+
var DEFAULT_TEMPLATE = "ssr";
|
|
666
|
+
var NAME_MAP = {
|
|
667
|
+
"_gitignore": ".gitignore",
|
|
668
|
+
"_npmrc": ".npmrc",
|
|
669
|
+
"_package.json": "package.json"
|
|
670
|
+
};
|
|
671
|
+
function templatesRoot() {
|
|
672
|
+
const here = (0, import_node_path7.dirname)((0, import_node_url3.fileURLToPath)(import_meta.url));
|
|
673
|
+
const candidates = [(0, import_node_path7.resolve)(here, "..", "templates"), (0, import_node_path7.resolve)(here, "..", "..", "templates")];
|
|
674
|
+
for (const c of candidates) {
|
|
675
|
+
if ((0, import_node_fs6.existsSync)(c)) return c;
|
|
676
|
+
}
|
|
677
|
+
return candidates[0] ?? (0, import_node_path7.resolve)(here, "..", "templates");
|
|
678
|
+
}
|
|
679
|
+
function templateDir(name) {
|
|
680
|
+
return (0, import_node_path7.join)(templatesRoot(), name);
|
|
681
|
+
}
|
|
682
|
+
function resolveTemplateName(name) {
|
|
683
|
+
if (name === void 0) return DEFAULT_TEMPLATE;
|
|
684
|
+
if (TEMPLATES.includes(name)) return name;
|
|
685
|
+
throw new Error(`Unknown template "${name}". Available: ${TEMPLATES.join(", ")}.`);
|
|
686
|
+
}
|
|
687
|
+
function materialisedName(fileName) {
|
|
688
|
+
return NAME_MAP[fileName] ?? fileName;
|
|
689
|
+
}
|
|
690
|
+
function applyTokens(contents, tokens) {
|
|
691
|
+
return contents.replaceAll("__PROJECT_NAME__", tokens.projectName).replaceAll("__FRAMEWORK_VERSION__", tokens.frameworkVersion);
|
|
692
|
+
}
|
|
693
|
+
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
694
|
+
".ts",
|
|
695
|
+
".tsx",
|
|
696
|
+
".js",
|
|
697
|
+
".mjs",
|
|
698
|
+
".cjs",
|
|
699
|
+
".json",
|
|
700
|
+
".css",
|
|
701
|
+
".html",
|
|
702
|
+
".md",
|
|
703
|
+
".txt",
|
|
704
|
+
".npmrc",
|
|
705
|
+
""
|
|
706
|
+
]);
|
|
707
|
+
function isTextFile(fileName) {
|
|
708
|
+
const dot = fileName.lastIndexOf(".");
|
|
709
|
+
const ext = dot >= 0 ? fileName.slice(dot) : "";
|
|
710
|
+
return TEXT_EXTENSIONS.has(ext);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// ../cli/src/create.ts
|
|
714
|
+
async function isEmptyDir(dir) {
|
|
715
|
+
if (!(0, import_node_fs7.existsSync)(dir)) return true;
|
|
716
|
+
const entries = await (0, import_promises5.readdir)(dir);
|
|
717
|
+
return entries.filter((e) => e !== ".git").length === 0;
|
|
718
|
+
}
|
|
719
|
+
async function copyTree(srcDir, destDir, tokens, written) {
|
|
720
|
+
await (0, import_promises5.mkdir)(destDir, { recursive: true });
|
|
721
|
+
const entries = await (0, import_promises5.readdir)(srcDir);
|
|
722
|
+
for (const entry of entries) {
|
|
723
|
+
const srcPath = (0, import_node_path8.join)(srcDir, entry);
|
|
724
|
+
const info = await (0, import_promises5.stat)(srcPath);
|
|
725
|
+
const destName = materialisedName(entry);
|
|
726
|
+
const destPath = (0, import_node_path8.join)(destDir, destName);
|
|
727
|
+
if (info.isDirectory()) {
|
|
728
|
+
await copyTree(srcPath, destPath, tokens, written);
|
|
729
|
+
} else if (isTextFile(entry)) {
|
|
730
|
+
const raw = await (0, import_promises5.readFile)(srcPath, "utf8");
|
|
731
|
+
await (0, import_promises5.writeFile)(destPath, applyTokens(raw, tokens), "utf8");
|
|
732
|
+
written.push(destPath);
|
|
733
|
+
} else {
|
|
734
|
+
const raw = await (0, import_promises5.readFile)(srcPath);
|
|
735
|
+
await (0, import_promises5.writeFile)(destPath, raw);
|
|
736
|
+
written.push(destPath);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
async function createProject(options) {
|
|
741
|
+
const { logger } = options;
|
|
742
|
+
let template;
|
|
743
|
+
try {
|
|
744
|
+
template = resolveTemplateName(options.template);
|
|
745
|
+
} catch (err) {
|
|
746
|
+
throw new CliError(err.message, { suggestion: "Pass a valid --template value." });
|
|
747
|
+
}
|
|
748
|
+
const root = (0, import_node_path8.resolve)(options.targetDir);
|
|
749
|
+
const projectName = (0, import_node_path8.basename)(root);
|
|
750
|
+
if (!await isEmptyDir(root)) {
|
|
751
|
+
throw new CliError(`Target directory ${root} already exists and is not empty.`, {
|
|
752
|
+
suggestion: "Choose a new directory name or empty the existing one."
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
const src = templateDir(template);
|
|
756
|
+
if (!(0, import_node_fs7.existsSync)(src)) {
|
|
757
|
+
throw new CliError(`Template "${template}" is missing from the CLI installation (${src}).`, {
|
|
758
|
+
suggestion: "Reinstall @streetui/cli \u2014 the shipped templates appear to be absent."
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
const tokens = { projectName, frameworkVersion: options.frameworkVersion };
|
|
762
|
+
const files = [];
|
|
763
|
+
await copyTree(src, root, tokens, files);
|
|
764
|
+
logger.success(`Created ${projectName} (${template} template) with ${files.length} files.`);
|
|
765
|
+
logger.plain("");
|
|
766
|
+
logger.info("Next steps:");
|
|
767
|
+
logger.plain(` cd ${options.targetDir}`);
|
|
768
|
+
logger.plain(" npm install");
|
|
769
|
+
logger.plain(" npm run dev");
|
|
770
|
+
return { root, template, files };
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// ../cli/src/index.ts
|
|
774
|
+
var CLI_VERSION = "1.0.0";
|
|
775
|
+
var HELP = `streetui \u2014 the StreetUI application CLI
|
|
776
|
+
|
|
777
|
+
Usage:
|
|
778
|
+
streetui <command> [options]
|
|
779
|
+
|
|
780
|
+
Commands:
|
|
781
|
+
create <dir> Scaffold a new StreetUI project
|
|
782
|
+
dev Start the development server with live reload
|
|
783
|
+
build Produce a production build (dist/client, dist/server)
|
|
784
|
+
start Serve the production build
|
|
785
|
+
|
|
786
|
+
Options:
|
|
787
|
+
-h, --help Show this help
|
|
788
|
+
-v, --version Show the CLI version
|
|
789
|
+
-p, --port <n> Port for dev/start (default 3000)
|
|
790
|
+
--host <host> Host for dev/start (default localhost)
|
|
791
|
+
--template <t> Template for create (basic | ssr)
|
|
792
|
+
--dir <path> Project directory (default current directory)
|
|
793
|
+
|
|
794
|
+
Examples:
|
|
795
|
+
npm create streetui@latest my-app
|
|
796
|
+
streetui dev --port 4000
|
|
797
|
+
streetui build
|
|
798
|
+
streetui start`;
|
|
799
|
+
async function runCli(argv2, options = {}) {
|
|
800
|
+
const logger = options.logger ?? createLogger();
|
|
801
|
+
const cwd = options.cwd ?? process.cwd();
|
|
802
|
+
const args = parseArgs(argv2);
|
|
803
|
+
if (args.unknown.length > 0) {
|
|
804
|
+
logger.error(`Unknown or invalid option(s): ${args.unknown.join(", ")}`);
|
|
805
|
+
logger.plain(HELP);
|
|
806
|
+
return { exitCode: 1 };
|
|
807
|
+
}
|
|
808
|
+
if (args.version && args.command === void 0) {
|
|
809
|
+
logger.plain(CLI_VERSION);
|
|
810
|
+
return { exitCode: 0 };
|
|
811
|
+
}
|
|
812
|
+
if (args.help || args.command === void 0) {
|
|
813
|
+
logger.plain(HELP);
|
|
814
|
+
return { exitCode: args.command === void 0 && !args.help ? 1 : 0 };
|
|
815
|
+
}
|
|
816
|
+
try {
|
|
817
|
+
return await dispatch(args, cwd, logger, options.returnServer === true);
|
|
818
|
+
} catch (err) {
|
|
819
|
+
if (err instanceof CliError) {
|
|
820
|
+
logger.error(err.message);
|
|
821
|
+
if (err.suggestion !== void 0) logger.plain(err.suggestion);
|
|
822
|
+
return { exitCode: err.exitCode };
|
|
823
|
+
}
|
|
824
|
+
logger.error(`Unexpected error: ${err.message}`);
|
|
825
|
+
return { exitCode: 1 };
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
async function dispatch(args, cwd, logger, returnServer) {
|
|
829
|
+
const projectCwd = args.dir ?? cwd;
|
|
830
|
+
switch (args.command) {
|
|
831
|
+
case "create": {
|
|
832
|
+
const targetDir = args.positionals[0] ?? args.dir;
|
|
833
|
+
if (targetDir === void 0) {
|
|
834
|
+
throw new CliError("create requires a target directory.", {
|
|
835
|
+
suggestion: "Usage: streetui create <dir> [--template basic|ssr]"
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
await createProject({
|
|
839
|
+
targetDir,
|
|
840
|
+
...args.template !== void 0 ? { template: args.template } : {},
|
|
841
|
+
frameworkVersion: CLI_VERSION,
|
|
842
|
+
logger
|
|
843
|
+
});
|
|
844
|
+
return { exitCode: 0 };
|
|
845
|
+
}
|
|
846
|
+
case "build": {
|
|
847
|
+
const project = await resolveProject(projectCwd, { requireEntry: true });
|
|
848
|
+
const out = await buildProject(project, "production");
|
|
849
|
+
logger.success(`Build complete \u2192 ${out.clientDir}`);
|
|
850
|
+
return { exitCode: 0 };
|
|
851
|
+
}
|
|
852
|
+
case "dev": {
|
|
853
|
+
const project = await resolveProject(projectCwd, { requireEntry: true });
|
|
854
|
+
const server = await runDev({
|
|
855
|
+
project,
|
|
856
|
+
logger,
|
|
857
|
+
...args.host !== void 0 ? { host: args.host } : {},
|
|
858
|
+
...args.port !== void 0 ? { port: args.port } : {}
|
|
859
|
+
});
|
|
860
|
+
if (returnServer) return { exitCode: 0, server };
|
|
861
|
+
await blockForever();
|
|
862
|
+
return { exitCode: 0 };
|
|
863
|
+
}
|
|
864
|
+
case "start": {
|
|
865
|
+
const project = await resolveProject(projectCwd);
|
|
866
|
+
const running = await runStart({
|
|
867
|
+
project,
|
|
868
|
+
logger,
|
|
869
|
+
...args.host !== void 0 ? { host: args.host } : {},
|
|
870
|
+
...args.port !== void 0 ? { port: args.port } : {}
|
|
871
|
+
});
|
|
872
|
+
if (returnServer) return { exitCode: 0, server: { url: running.url, stop: running.close } };
|
|
873
|
+
await blockForever();
|
|
874
|
+
return { exitCode: 0 };
|
|
875
|
+
}
|
|
876
|
+
default:
|
|
877
|
+
throw new CliError(`Unknown command "${args.command}".`, {
|
|
878
|
+
suggestion: 'Run "streetui --help" to see available commands.'
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
function blockForever() {
|
|
883
|
+
return new Promise(() => {
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// src/create-bin.ts
|
|
888
|
+
var argv = process.argv.slice(2);
|
|
889
|
+
var withCommand = argv[0] === "create" ? argv : ["create", ...argv];
|
|
890
|
+
runCli(withCommand).then((result) => {
|
|
891
|
+
if (result.exitCode !== 0) process.exitCode = result.exitCode;
|
|
892
|
+
}).catch((err) => {
|
|
893
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
894
|
+
process.exitCode = 1;
|
|
895
|
+
});
|
|
896
|
+
//# sourceMappingURL=create-bin.cjs.map
|