@wolfstar/http-framework 5.0.0-next-20260919162943 → 5.1.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/README.md +18 -3
- package/bin/stars.mjs +2 -0
- package/config.d.ts +1 -0
- package/config.js +1 -0
- package/dist/esm/auto-imports.d.ts +1 -1
- package/dist/esm/auto-imports.js +1 -1
- package/dist/esm/auto-imports.js.map +1 -1
- package/dist/esm/config.d.ts +1 -2
- package/dist/esm/config.js +2 -871
- package/dist/esm/index.js +10 -10
- package/package.json +22 -7
- package/schema.d.ts +1 -0
- package/schema.js +1 -0
- package/dist/esm/config.js.map +0 -1
- package/dist/esm/resolve-B0J5V4xC.d.ts +0 -829
- package/dist/esm/resolve-B0J5V4xC.d.ts.map +0 -1
package/dist/esm/config.js
CHANGED
|
@@ -1,872 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3
|
-
import { defineDiagnostics } from "nostics";
|
|
1
|
+
export * from "@wolfstar/schema"
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Structured, stable diagnostic codes for every way a `stars.config.*` can fail to load or validate.
|
|
8
|
-
*
|
|
9
|
-
* This is a plain data error (no exit code or terminal formatting), so nothing here reports anywhere on its own —
|
|
10
|
-
* `reporters` stays empty and each call only builds and returns a `Diagnostic`. That keeps it meaningful outside a
|
|
11
|
-
* CLI, e.g. for a dashboard or test that calls {@link loadStarsConfig} directly; `@wolfstar/cli` is what renders it
|
|
12
|
-
* and picks an exit code (`exitCodeOf`).
|
|
13
|
-
*
|
|
14
|
-
* The `sources` field (populated with the configuration file, when there is one) carries the "which file" grounding
|
|
15
|
-
* `ConfigError` used to expose as `.file`; the "which option" grounding it exposed as `.path` is folded directly into
|
|
16
|
-
* every `why`/`fix` message instead, the way every other diagnostic code already reads.
|
|
17
|
-
*
|
|
18
|
-
* `why` and `fix` are always given the same, fully-typed params object (even when one of them ignores part of it):
|
|
19
|
-
* a bare `() => value` loses nostics' param-type inference (a zero-arg function widens to `unknown` params), so every
|
|
20
|
-
* entry here spells out its shape on both sides instead.
|
|
21
|
-
*/
|
|
22
|
-
const configDiagnostics = defineDiagnostics({
|
|
23
|
-
docsBase: (code) => `https://stars-components.js.org/docs/config/errors#${code.toLowerCase()}`,
|
|
24
|
-
reporters: [],
|
|
25
|
-
codes: {
|
|
26
|
-
ROOT_NOT_FOUND: {
|
|
27
|
-
why: (p) => `The project root does not exist: ${p.root}`,
|
|
28
|
-
fix: (_p) => "Point `root` to an existing directory, relative to the configuration file."
|
|
29
|
-
},
|
|
30
|
-
PACKAGE_JSON_INVALID: {
|
|
31
|
-
why: (p) => `Failed to parse ${p.file}: ${p.message}`,
|
|
32
|
-
fix: (_p) => "Fix the JSON syntax of the package.json file."
|
|
33
|
-
},
|
|
34
|
-
ENTRY_NOT_FOUND: {
|
|
35
|
-
why: (p) => `The entry file does not exist: ${p.entry}`,
|
|
36
|
-
fix: (_p) => "Point `entry` to the file that starts the bot, relative to the project root."
|
|
37
|
-
},
|
|
38
|
-
ENTRY_DEFAULT_NOT_FOUND: {
|
|
39
|
-
why: (p) => `Could not find the entry file in ${p.root}`,
|
|
40
|
-
fix: (p) => `Set \`entry\` in the configuration, or create one of ${p.defaults}.`
|
|
41
|
-
},
|
|
42
|
-
INVALID_BUILD_TOOL: {
|
|
43
|
-
why: (p) => `Unknown build tool "${p.tool}"`,
|
|
44
|
-
fix: (_p) => "Use one of 'tsdown', 'tsc', 'vite', 'none' or 'auto'."
|
|
45
|
-
},
|
|
46
|
-
EXPERIMENTAL_BUILD_TOOL: {
|
|
47
|
-
why: (p) => `The '${p.tool}' build tool is experimental`,
|
|
48
|
-
fix: (p) => `Set \`${p.flag}\` to true to use it.`
|
|
49
|
-
},
|
|
50
|
-
BUILD_TOOL_REQUIRED: {
|
|
51
|
-
why: (p) => `The entry ${p.entry} is TypeScript but the build tool is 'none'`,
|
|
52
|
-
fix: (_p) => "Set `build.tool` to 'tsdown' or 'tsc', or point `entry` to a JavaScript file."
|
|
53
|
-
},
|
|
54
|
-
TSCONFIG_EXPLICIT_NOT_FOUND: {
|
|
55
|
-
why: (p) => `The tsconfig file does not exist: ${p.tsconfig}`,
|
|
56
|
-
fix: (p) => `Point \`${p.path}\` to an existing tsconfig.json, relative to the project root.`
|
|
57
|
-
},
|
|
58
|
-
TSCONFIG_NOT_FOUND: {
|
|
59
|
-
why: (p) => `Could not find a tsconfig.json in ${p.root}`,
|
|
60
|
-
fix: (p) => `Create src/tsconfig.json or tsconfig.json, or set \`${p.suggestion}\`.`
|
|
61
|
-
},
|
|
62
|
-
TSDOWN_OPTIONS_REQUIRE_TSDOWN: {
|
|
63
|
-
why: (_p) => "`tsdown` options need the `tsdown` build tool",
|
|
64
|
-
fix: (p) => `Set \`build.tool\` to 'tsdown', or remove \`tsdown\` (the build tool is '${p.tool}').`
|
|
65
|
-
},
|
|
66
|
-
VITE_OPTIONS_REQUIRE_VITE: {
|
|
67
|
-
why: (_p) => "`vite` options need the `vite` build tool",
|
|
68
|
-
fix: (p) => `Set \`build.tool\` to 'vite' with \`experimental.enableVite\`, or remove \`vite\` (the build tool is '${p.tool}').`
|
|
69
|
-
},
|
|
70
|
-
TSDOWN_CONFIG_FILE_UNSUPPORTED: {
|
|
71
|
-
why: (p) => `\`${p.file}\` is not used with compatibility version ${p.version}`,
|
|
72
|
-
fix: (p) => `Move its options into \`tsdown\` here, drop the ${p.file} configuration, or set \`future.compatibilityVersion\` to ${p.legacyVersion}.`
|
|
73
|
-
},
|
|
74
|
-
INVALID_TYPE: {
|
|
75
|
-
why: (p) => `\`${p.path}\` must be ${p.expected}, received ${describeValue(p.value)}`,
|
|
76
|
-
fix: (p) => p.fix
|
|
77
|
-
},
|
|
78
|
-
INVALID_COMPATIBILITY_VERSION: {
|
|
79
|
-
why: (p) => `Unknown compatibility version ${describeValue(p.value)}`,
|
|
80
|
-
fix: (p) => `Use ${p.legacyVersion} for the legacy build pipeline or ${p.latestVersion} for today's defaults.`
|
|
81
|
-
},
|
|
82
|
-
UNKNOWN_OPTION: {
|
|
83
|
-
why: (p) => `Unknown option \`${p.path}\``,
|
|
84
|
-
fix: (p) => `Known options${p.parent ? ` of \`${p.parent}\`` : ""}: ${p.known}.`
|
|
85
|
-
},
|
|
86
|
-
EXPERIMENT_REQUIRED: {
|
|
87
|
-
why: (p) => `\`${p.path}\` needs \`${p.requires}\``,
|
|
88
|
-
fix: (p) => `Set \`${p.requires}\` to true as well, or drop \`${p.drop}\`.`
|
|
89
|
-
},
|
|
90
|
-
IMPORTS_REQUIRE_TSDOWN: {
|
|
91
|
-
why: (_p) => "`imports` requires the `tsdown` build tool",
|
|
92
|
-
fix: (_p) => "Set `build.tool` to 'tsdown', or remove `imports`/set it to `false`."
|
|
93
|
-
},
|
|
94
|
-
LOCALES_NOT_FOUND: {
|
|
95
|
-
why: (p) => `The locales directory does not exist: ${p.locales}`,
|
|
96
|
-
fix: (_p) => "Point `codegen.i18n.locales` to the base locale directory, relative to the project root."
|
|
97
|
-
},
|
|
98
|
-
INVALID_URL: {
|
|
99
|
-
why: (p) => `Invalid URL "${p.url}"`,
|
|
100
|
-
fix: (p) => p.fix
|
|
101
|
-
},
|
|
102
|
-
TUNNEL_URL_NOT_HTTPS: {
|
|
103
|
-
why: (p) => `The tunnel URL must be https, received "${p.url}"`,
|
|
104
|
-
fix: (_p) => "Discord only accepts an https interactions endpoint."
|
|
105
|
-
},
|
|
106
|
-
INVALID_TYPECHECKER: {
|
|
107
|
-
why: (p) => `Unknown type checker "${p.checker}"`,
|
|
108
|
-
fix: (_p) => "Use one of 'tsc', 'golar', 'tsz' or 'auto'."
|
|
109
|
-
},
|
|
110
|
-
CONFIG_NOT_FOUND: {
|
|
111
|
-
why: (p) => `Configuration file not found: ${p.file}`,
|
|
112
|
-
fix: (p) => `Pass an existing file to --config, or create one of ${p.names} in ${p.cwd}.`
|
|
113
|
-
},
|
|
114
|
-
CONFIG_LOAD_FAILED: {
|
|
115
|
-
why: (p) => `Failed to load the configuration: ${p.message}`,
|
|
116
|
-
fix: (_p) => "The file must be valid TypeScript/JavaScript and export the configuration as its default export."
|
|
117
|
-
},
|
|
118
|
-
CONFIG_NOT_OBJECT: {
|
|
119
|
-
why: (_p) => "The configuration file must export an object as its default export.",
|
|
120
|
-
fix: (_p) => "Use `export default defineConfig({ ... })` from '@wolfstar/http-framework/config'."
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
function describeValue(value) {
|
|
125
|
-
if (value === null) return "null";
|
|
126
|
-
if (Array.isArray(value)) return "an array";
|
|
127
|
-
if (typeof value === "string") return `"${value}"`;
|
|
128
|
-
return typeof value === "object" ? "an object" : `${typeof value} ${String(value)}`;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
//#endregion
|
|
132
|
-
//#region src/lib/config/load.ts
|
|
133
|
-
const CONFIG_EXTENSIONS = [
|
|
134
|
-
"ts",
|
|
135
|
-
"mts",
|
|
136
|
-
"cts",
|
|
137
|
-
"js",
|
|
138
|
-
"mjs",
|
|
139
|
-
"cjs"
|
|
140
|
-
];
|
|
141
|
-
const CONFIG_FILE_NAMES = CONFIG_EXTENSIONS.map((extension) => `stars.config.${extension}`);
|
|
142
|
-
/**
|
|
143
|
-
* Finds the first `stars.config.*` file in `cwd`, in {@link CONFIG_FILE_NAMES} order.
|
|
144
|
-
*/
|
|
145
|
-
function discoverConfigFile(cwd) {
|
|
146
|
-
for (const name of CONFIG_FILE_NAMES) {
|
|
147
|
-
const candidate = join(cwd, name);
|
|
148
|
-
if (isFile$1(candidate)) return candidate;
|
|
149
|
-
}
|
|
150
|
-
return null;
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Loads the raw configuration object. The loader (`c12`) is imported lazily so
|
|
154
|
-
* commands that never touch the configuration stay fast.
|
|
155
|
-
*/
|
|
156
|
-
async function loadConfigFile(options) {
|
|
157
|
-
const cwd = resolve(options.cwd);
|
|
158
|
-
let file;
|
|
159
|
-
if (options.configFile) {
|
|
160
|
-
file = resolve(cwd, options.configFile);
|
|
161
|
-
if (!isFile$1(file)) throw configDiagnostics.CONFIG_NOT_FOUND({
|
|
162
|
-
file,
|
|
163
|
-
cwd,
|
|
164
|
-
names: CONFIG_FILE_NAMES.join(", ")
|
|
165
|
-
});
|
|
166
|
-
} else {
|
|
167
|
-
file = discoverConfigFile(cwd);
|
|
168
|
-
if (!file) return {
|
|
169
|
-
configFile: null,
|
|
170
|
-
config: {}
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
const { loadConfig } = await import("c12");
|
|
174
|
-
let loaded;
|
|
175
|
-
try {
|
|
176
|
-
const result = await loadConfig({
|
|
177
|
-
name: "stars",
|
|
178
|
-
cwd: dirname(file),
|
|
179
|
-
configFile: basename(file),
|
|
180
|
-
rcFile: false,
|
|
181
|
-
globalRc: false,
|
|
182
|
-
dotenv: false,
|
|
183
|
-
packageJson: false,
|
|
184
|
-
defaults: {}
|
|
185
|
-
});
|
|
186
|
-
const layer = result.layers?.find((candidate) => candidate.configFile && resolve(candidate.cwd ?? dirname(file), candidate.configFile) === file);
|
|
187
|
-
loaded = layer ? layer.config : result.config;
|
|
188
|
-
} catch (error) {
|
|
189
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
190
|
-
throw configDiagnostics.CONFIG_LOAD_FAILED({
|
|
191
|
-
message,
|
|
192
|
-
cause: error,
|
|
193
|
-
sources: [file]
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
if (loaded === null || typeof loaded !== "object" || Array.isArray(loaded)) throw configDiagnostics.CONFIG_NOT_OBJECT({ sources: [file] });
|
|
197
|
-
return {
|
|
198
|
-
configFile: file,
|
|
199
|
-
config: loaded
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
function isFile$1(path) {
|
|
203
|
-
try {
|
|
204
|
-
return existsSync(path) && statSync(path).isFile();
|
|
205
|
-
} catch {
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
//#endregion
|
|
211
|
-
//#region src/lib/config/resolve.ts
|
|
212
|
-
const DEFAULT_ENTRIES = [
|
|
213
|
-
"src/main.ts",
|
|
214
|
-
"src/main.js",
|
|
215
|
-
"src/index.ts",
|
|
216
|
-
"src/index.js"
|
|
217
|
-
];
|
|
218
|
-
const DEFAULT_IGNORE = [
|
|
219
|
-
"**/node_modules/**",
|
|
220
|
-
"**/dist/**",
|
|
221
|
-
"**/.git/**"
|
|
222
|
-
];
|
|
223
|
-
const DEFAULT_DEBOUNCE = 150;
|
|
224
|
-
const DEFAULT_KILL_TIMEOUT = 5e3;
|
|
225
|
-
const DEFAULT_NODE_ARGS = ["--enable-source-maps"];
|
|
226
|
-
const DEFAULT_DEV_PORT = 3e3;
|
|
227
|
-
const DEFAULT_I18N_LOCALES = "src/locales/en-US";
|
|
228
|
-
const DEFAULT_I18N_OUTPUT = "src/@types/i18next.d.ts";
|
|
229
|
-
const DEFAULT_IMPORTS_DIRS = ["src/lib/**", "src/utils/**"];
|
|
230
|
-
const DEFAULT_IMPORTS_PRESETS = ["@wolfstar/http-framework", "@wolfstar/env-utilities"];
|
|
231
|
-
const DEFAULT_IMPORTS_DTS = ".stars/imports.d.ts";
|
|
232
|
-
const DEFAULT_DEV_LOG_FILE = ".stars/dev.log";
|
|
233
|
-
const DEFAULT_TUNNEL_PATH = "/";
|
|
234
|
-
const DEFAULT_COMPATIBILITY_VERSION = 4;
|
|
235
|
-
const LEGACY_COMPATIBILITY_VERSION = 3;
|
|
236
|
-
const LATEST_COMPATIBILITY_VERSION = 4;
|
|
237
|
-
const COMPATIBILITY_VERSIONS = /* @__PURE__ */ new Set([3, 4]);
|
|
238
|
-
const BUILD_TOOLS = /* @__PURE__ */ new Set([
|
|
239
|
-
"tsdown",
|
|
240
|
-
"tsc",
|
|
241
|
-
"none",
|
|
242
|
-
"vite",
|
|
243
|
-
"auto"
|
|
244
|
-
]);
|
|
245
|
-
const TYPECHECKERS = /* @__PURE__ */ new Set([
|
|
246
|
-
"tsc",
|
|
247
|
-
"golar",
|
|
248
|
-
"tsz",
|
|
249
|
-
"auto"
|
|
250
|
-
]);
|
|
251
|
-
const VITE_CONFIG_FILES = [
|
|
252
|
-
"vite.config.ts",
|
|
253
|
-
"vite.config.mts",
|
|
254
|
-
"vite.config.cts",
|
|
255
|
-
"vite.config.js",
|
|
256
|
-
"vite.config.mjs",
|
|
257
|
-
"vite.config.cjs"
|
|
258
|
-
];
|
|
259
|
-
const TSDOWN_CONFIG_FILES = [
|
|
260
|
-
"tsdown.config.ts",
|
|
261
|
-
"tsdown.config.mts",
|
|
262
|
-
"tsdown.config.cts",
|
|
263
|
-
"tsdown.config.js",
|
|
264
|
-
"tsdown.config.mjs",
|
|
265
|
-
"tsdown.config.cjs",
|
|
266
|
-
"tsdown.config.json"
|
|
267
|
-
];
|
|
268
|
-
const TYPESCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
269
|
-
".ts",
|
|
270
|
-
".mts",
|
|
271
|
-
".cts"
|
|
272
|
-
]);
|
|
273
|
-
/**
|
|
274
|
-
* Applies defaults, validates every option and resolves all paths to absolute ones.
|
|
275
|
-
*
|
|
276
|
-
* @throws {Diagnostic} (from `nostics`, via {@link configDiagnostics}) with an actionable `fix` on the first invalid option.
|
|
277
|
-
*/
|
|
278
|
-
function resolveStarsConfig(options) {
|
|
279
|
-
const cwd = resolve(options.cwd);
|
|
280
|
-
const env = options.env ?? process.env;
|
|
281
|
-
const file = options.configFile;
|
|
282
|
-
const config = options.config;
|
|
283
|
-
const validator = new Validator(file);
|
|
284
|
-
validator.knownKeys(config, "", [
|
|
285
|
-
"root",
|
|
286
|
-
"entry",
|
|
287
|
-
"build",
|
|
288
|
-
"dev",
|
|
289
|
-
"codegen",
|
|
290
|
-
"imports",
|
|
291
|
-
"experimental",
|
|
292
|
-
"future",
|
|
293
|
-
"vite",
|
|
294
|
-
"tsdown"
|
|
295
|
-
]);
|
|
296
|
-
const baseDirectory = file ? dirname(file) : cwd;
|
|
297
|
-
const root = resolve(baseDirectory, validator.string(config.root, "root") ?? ".");
|
|
298
|
-
if (!isDirectory(root)) throw validator.error(configDiagnostics.ROOT_NOT_FOUND, { root });
|
|
299
|
-
const packageJson = readPackageJson(root, validator);
|
|
300
|
-
const experimental = resolveExperimental(config.experimental ?? {}, validator);
|
|
301
|
-
const future = resolveFuture(config.future ?? {}, validator);
|
|
302
|
-
const entry = resolveEntry(root, validator.string(config.entry, "entry"), validator);
|
|
303
|
-
const vite = validator.plainObject(config.vite, "vite") ?? {};
|
|
304
|
-
const tsdown = validator.plainObject(config.tsdown, "tsdown") ?? {};
|
|
305
|
-
const build = resolveBuild(root, entry, packageJson, config.build ?? {}, experimental, future, Object.keys(tsdown).length > 0, validator);
|
|
306
|
-
const dev = resolveDev(root, entry, packageJson, config.dev ?? {}, env, validator);
|
|
307
|
-
const codegen = resolveCodegen(root, config.codegen ?? {}, validator);
|
|
308
|
-
const imports = resolveImports(root, build.tool, future, config.imports, validator);
|
|
309
|
-
if (Object.keys(tsdown).length > 0 && build.tool !== "tsdown") throw validator.error(configDiagnostics.TSDOWN_OPTIONS_REQUIRE_TSDOWN, { tool: build.tool });
|
|
310
|
-
if (Object.keys(vite).length > 0 && build.tool !== "vite") throw validator.error(configDiagnostics.VITE_OPTIONS_REQUIRE_VITE, { tool: build.tool });
|
|
311
|
-
return {
|
|
312
|
-
configFile: file,
|
|
313
|
-
cwd,
|
|
314
|
-
root,
|
|
315
|
-
packageJson,
|
|
316
|
-
entry,
|
|
317
|
-
build,
|
|
318
|
-
dev,
|
|
319
|
-
codegen,
|
|
320
|
-
imports,
|
|
321
|
-
experimental,
|
|
322
|
-
future,
|
|
323
|
-
vite,
|
|
324
|
-
tsdown
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
/**
|
|
328
|
-
* Presents an absolute path relative to `root` when possible, for display purposes.
|
|
329
|
-
*/
|
|
330
|
-
function displayPath(root, path) {
|
|
331
|
-
const rel = relative(root, path);
|
|
332
|
-
if (!rel) return ".";
|
|
333
|
-
return rel.startsWith("..") || isAbsolute(rel) ? path : rel;
|
|
334
|
-
}
|
|
335
|
-
function resolveEntry(root, configured, validator) {
|
|
336
|
-
if (configured !== void 0) {
|
|
337
|
-
const entry = resolve(root, configured);
|
|
338
|
-
if (!isFile(entry)) throw validator.error(configDiagnostics.ENTRY_NOT_FOUND, { entry });
|
|
339
|
-
return entry;
|
|
340
|
-
}
|
|
341
|
-
for (const candidate of DEFAULT_ENTRIES) {
|
|
342
|
-
const entry = join(root, candidate);
|
|
343
|
-
if (isFile(entry)) return entry;
|
|
344
|
-
}
|
|
345
|
-
throw validator.error(configDiagnostics.ENTRY_DEFAULT_NOT_FOUND, {
|
|
346
|
-
root,
|
|
347
|
-
defaults: DEFAULT_ENTRIES.join(", ")
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
function resolveBuild(root, entry, packageJson, config, experimental, future, hasTsdownOptions, validator) {
|
|
351
|
-
validator.knownKeys(config, "build", [
|
|
352
|
-
"tool",
|
|
353
|
-
"outDir",
|
|
354
|
-
"tsconfig"
|
|
355
|
-
]);
|
|
356
|
-
const requested = validator.string(config.tool, "build.tool") ?? "auto";
|
|
357
|
-
if (!BUILD_TOOLS.has(requested)) throw validator.error(configDiagnostics.INVALID_BUILD_TOOL, { tool: requested });
|
|
358
|
-
if (requested === "vite" && !experimental.enableVite) throw validator.error(configDiagnostics.EXPERIMENTAL_BUILD_TOOL, {
|
|
359
|
-
tool: "vite",
|
|
360
|
-
flag: "experimental.enableVite"
|
|
361
|
-
});
|
|
362
|
-
const isTypeScriptEntry = TYPESCRIPT_EXTENSIONS.has(extname(entry));
|
|
363
|
-
const tool = requested === "auto" ? detectBuildTool(root, packageJson, isTypeScriptEntry, experimental, future, hasTsdownOptions) : requested;
|
|
364
|
-
if (tool === "none" && isTypeScriptEntry) throw validator.error(configDiagnostics.BUILD_TOOL_REQUIRED, { entry: displayPath(root, entry) });
|
|
365
|
-
const defaultOutDir = experimental.enableNitro ? ".output" : "dist";
|
|
366
|
-
const outDir = resolve(root, validator.string(config.outDir, "build.outDir") ?? defaultOutDir);
|
|
367
|
-
let tsconfig = null;
|
|
368
|
-
const configuredTsconfig = validator.string(config.tsconfig, "build.tsconfig");
|
|
369
|
-
if (configuredTsconfig !== void 0) {
|
|
370
|
-
tsconfig = resolve(root, configuredTsconfig);
|
|
371
|
-
if (!isFile(tsconfig)) throw validator.error(configDiagnostics.TSCONFIG_EXPLICIT_NOT_FOUND, {
|
|
372
|
-
tsconfig,
|
|
373
|
-
path: "build.tsconfig"
|
|
374
|
-
});
|
|
375
|
-
} else if (tool === "tsc" || tool === "tsdown") {
|
|
376
|
-
tsconfig = [join(root, "src", "tsconfig.json"), join(root, "tsconfig.json")].find((candidate) => isFile(candidate)) ?? null;
|
|
377
|
-
if (!tsconfig && tool === "tsc") throw validator.error(configDiagnostics.TSCONFIG_NOT_FOUND, {
|
|
378
|
-
root,
|
|
379
|
-
suggestion: "build.tsconfig"
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
const output = experimental.enableNitro ? join(outDir, "server", "index.mjs") : tool === "none" ? entry : resolveBuildOutput(root, entry, outDir, packageJson);
|
|
383
|
-
let configFile = findConfigFile(root, tool === "tsdown" ? TSDOWN_CONFIG_FILES : tool === "vite" ? VITE_CONFIG_FILES : []);
|
|
384
|
-
if (tool === "tsdown" && configFile === null && packageJson?.tsdown !== void 0) configFile = join(root, "package.json");
|
|
385
|
-
if (tool === "tsdown" && configFile !== null && future.compatibilityVersion >= 4) throw validator.error(configDiagnostics.TSDOWN_CONFIG_FILE_UNSUPPORTED, {
|
|
386
|
-
file: displayPath(root, configFile),
|
|
387
|
-
version: future.compatibilityVersion,
|
|
388
|
-
legacyVersion: 3
|
|
389
|
-
});
|
|
390
|
-
return {
|
|
391
|
-
tool,
|
|
392
|
-
outDir,
|
|
393
|
-
tsconfig,
|
|
394
|
-
output,
|
|
395
|
-
configFile
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
function findConfigFile(root, names) {
|
|
399
|
-
for (const name of names) {
|
|
400
|
-
const candidate = join(root, name);
|
|
401
|
-
if (isFile(candidate)) return candidate;
|
|
402
|
-
}
|
|
403
|
-
return null;
|
|
404
|
-
}
|
|
405
|
-
function detectBuildTool(root, packageJson, isTypeScriptEntry, experimental, future, hasTsdownOptions) {
|
|
406
|
-
if (experimental.enableVite) {
|
|
407
|
-
if (VITE_CONFIG_FILES.some((name) => isFile(join(root, name))) || hasDependency(packageJson, "vite")) return "vite";
|
|
408
|
-
}
|
|
409
|
-
if (hasTsdownOptions) return "tsdown";
|
|
410
|
-
if (future.compatibilityVersion >= 4) return isTypeScriptEntry ? "tsdown" : "none";
|
|
411
|
-
if (TSDOWN_CONFIG_FILES.some((name) => isFile(join(root, name))) || hasDependency(packageJson, "tsdown")) return "tsdown";
|
|
412
|
-
if (isTypeScriptEntry) return "tsc";
|
|
413
|
-
return "none";
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* Resolves the Nuxt-style compatibility block. Version 4 is the default; version 3 remains an explicit legacy mode.
|
|
417
|
-
*/
|
|
418
|
-
function resolveFuture(config, validator) {
|
|
419
|
-
if (config === null || typeof config !== "object" || Array.isArray(config)) throw validator.typeError("future", "an object", config, "Use `{ compatibilityVersion }`.");
|
|
420
|
-
validator.knownKeys(config, "future", ["compatibilityVersion"]);
|
|
421
|
-
const version = config.compatibilityVersion;
|
|
422
|
-
if (version === void 0) return { compatibilityVersion: 4 };
|
|
423
|
-
if (typeof version !== "number" || !COMPATIBILITY_VERSIONS.has(version)) throw validator.error(configDiagnostics.INVALID_COMPATIBILITY_VERSION, {
|
|
424
|
-
value: version,
|
|
425
|
-
legacyVersion: 3,
|
|
426
|
-
latestVersion: 4
|
|
427
|
-
});
|
|
428
|
-
return { compatibilityVersion: version };
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* Resolves the `experimental` block. Every flag is a boolean defaulting to `false`, the way Nuxt's own experimental
|
|
432
|
-
* flags are declared, and the ones that build on each other are checked here rather than surfacing later as a
|
|
433
|
-
* confusing runtime failure.
|
|
434
|
-
*/
|
|
435
|
-
function resolveExperimental(config, validator) {
|
|
436
|
-
if (config === null || typeof config !== "object" || Array.isArray(config)) throw validator.typeError("experimental", "an object", config, "Use `{ enableVite, enableExternalVite, enableNitro, nitro }`.");
|
|
437
|
-
validator.knownKeys(config, "experimental", [
|
|
438
|
-
"enableVite",
|
|
439
|
-
"enableExternalVite",
|
|
440
|
-
"enableNitro",
|
|
441
|
-
"nitro"
|
|
442
|
-
]);
|
|
443
|
-
const enableVite = validator.boolean(config.enableVite, "experimental.enableVite") ?? false;
|
|
444
|
-
const enableExternalVite = validator.boolean(config.enableExternalVite, "experimental.enableExternalVite") ?? false;
|
|
445
|
-
const enableNitro = validator.boolean(config.enableNitro, "experimental.enableNitro") ?? false;
|
|
446
|
-
if (enableExternalVite && !enableVite) throw validator.error(configDiagnostics.EXPERIMENT_REQUIRED, {
|
|
447
|
-
path: "experimental.enableExternalVite",
|
|
448
|
-
requires: "experimental.enableVite",
|
|
449
|
-
drop: "enableExternalVite"
|
|
450
|
-
});
|
|
451
|
-
if (enableNitro && !enableVite) throw validator.error(configDiagnostics.EXPERIMENT_REQUIRED, {
|
|
452
|
-
path: "experimental.enableNitro",
|
|
453
|
-
requires: "experimental.enableVite",
|
|
454
|
-
drop: "enableNitro"
|
|
455
|
-
});
|
|
456
|
-
const rawNitro = "nitro" in config ? config.nitro : void 0;
|
|
457
|
-
if (rawNitro !== void 0 && !enableNitro) throw validator.error(configDiagnostics.EXPERIMENT_REQUIRED, {
|
|
458
|
-
path: "experimental.nitro",
|
|
459
|
-
requires: "experimental.enableNitro",
|
|
460
|
-
drop: "nitro"
|
|
461
|
-
});
|
|
462
|
-
if (rawNitro !== void 0 && (rawNitro === null || typeof rawNitro !== "object" || Array.isArray(rawNitro))) throw validator.typeError("experimental.nitro", "an object", rawNitro, "Use `{ preset }`.");
|
|
463
|
-
if (rawNitro) validator.knownKeys(rawNitro, "experimental.nitro", ["preset"]);
|
|
464
|
-
return {
|
|
465
|
-
enableVite,
|
|
466
|
-
enableExternalVite,
|
|
467
|
-
enableNitro,
|
|
468
|
-
nitro: { preset: validator.string(rawNitro?.preset, "experimental.nitro.preset") ?? "node-server" }
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
function resolveBuildOutput(root, entry, outDir, packageJson) {
|
|
472
|
-
if (packageJson?.main) return resolve(root, packageJson.main);
|
|
473
|
-
const extension = extname(entry);
|
|
474
|
-
const outputExtension = extension === ".mts" ? ".mjs" : extension === ".cts" ? ".cjs" : ".js";
|
|
475
|
-
return join(outDir, `${basename(entry, extension)}${outputExtension}`);
|
|
476
|
-
}
|
|
477
|
-
const ENV_PORT_KEYS = ["HTTP_PORT", "PORT"];
|
|
478
|
-
/**
|
|
479
|
-
* Reads the project's environment layers from `src/.env*` and `.env*` into a plain object, the way `stars dev` and
|
|
480
|
-
* `stars commands` need it:
|
|
481
|
-
* these files are only loaded into `process.env` by the bot itself once it starts (see `@wolfstar/env-utilities`),
|
|
482
|
-
* so by the time the CLI runs they are not there yet. This is a minimal line reader, not a full dotenv
|
|
483
|
-
* implementation — quoting is stripped, but expansion (`dotenv-expand`) is not. Earlier files win, matching
|
|
484
|
-
* dotenv's own precedence.
|
|
485
|
-
*/
|
|
486
|
-
function readProjectEnvFiles(root, environment = "development") {
|
|
487
|
-
const result = {};
|
|
488
|
-
const files = [
|
|
489
|
-
`.${environment}.local`,
|
|
490
|
-
...environment === "test" ? [] : [".local"],
|
|
491
|
-
`.${environment}`,
|
|
492
|
-
""
|
|
493
|
-
].flatMap((suffix) => [join("src", `.env${suffix}`), `.env${suffix}`]);
|
|
494
|
-
for (const file of files) {
|
|
495
|
-
const path = join(root, file);
|
|
496
|
-
if (!isFile(path)) continue;
|
|
497
|
-
let contents;
|
|
498
|
-
try {
|
|
499
|
-
contents = readFileSync(path, "utf-8");
|
|
500
|
-
} catch {
|
|
501
|
-
continue;
|
|
502
|
-
}
|
|
503
|
-
for (const line of contents.split(/\r?\n/)) {
|
|
504
|
-
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(line);
|
|
505
|
-
if (!match) continue;
|
|
506
|
-
const key = match[1];
|
|
507
|
-
if (key in result) continue;
|
|
508
|
-
result[key] = match[2].trim().replace(/^['"]|['"]$/g, "");
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
return result;
|
|
512
|
-
}
|
|
513
|
-
function readDevPortFromEnvFile(root, environment) {
|
|
514
|
-
const values = readProjectEnvFiles(root, environment);
|
|
515
|
-
for (const key of ENV_PORT_KEYS) if (values[key]) return values[key];
|
|
516
|
-
return null;
|
|
517
|
-
}
|
|
518
|
-
function resolveDev(root, entry, packageJson, config, env, validator) {
|
|
519
|
-
validator.knownKeys(config, "dev", [
|
|
520
|
-
"banner",
|
|
521
|
-
"watch",
|
|
522
|
-
"ignore",
|
|
523
|
-
"debounce",
|
|
524
|
-
"env",
|
|
525
|
-
"nodeArgs",
|
|
526
|
-
"args",
|
|
527
|
-
"url",
|
|
528
|
-
"health",
|
|
529
|
-
"killTimeout",
|
|
530
|
-
"typecheck",
|
|
531
|
-
"tunnel",
|
|
532
|
-
"logFile"
|
|
533
|
-
]);
|
|
534
|
-
const watch = (validator.stringArray(config.watch, "dev.watch") ?? [displayPath(root, dirname(entry))]).map((path) => resolve(root, path));
|
|
535
|
-
const ignore = validator.stringArray(config.ignore, "dev.ignore") ?? [...DEFAULT_IGNORE];
|
|
536
|
-
const debounce = validator.nonNegativeNumber(config.debounce, "dev.debounce") ?? 150;
|
|
537
|
-
const devEnv = validator.stringRecord(config.env, "dev.env") ?? {};
|
|
538
|
-
const nodeArgs = validator.stringArray(config.nodeArgs, "dev.nodeArgs") ?? [...DEFAULT_NODE_ARGS];
|
|
539
|
-
const args = validator.stringArray(config.args, "dev.args") ?? [];
|
|
540
|
-
const killTimeout = validator.nonNegativeNumber(config.killTimeout, "dev.killTimeout") ?? 5e3;
|
|
541
|
-
const health = validator.string(config.health, "dev.health") ?? null;
|
|
542
|
-
let url = validator.string(config.url, "dev.url") ?? null;
|
|
543
|
-
if (url !== null) try {
|
|
544
|
-
new URL(url);
|
|
545
|
-
} catch {
|
|
546
|
-
throw validator.error(configDiagnostics.INVALID_URL, {
|
|
547
|
-
url,
|
|
548
|
-
fix: "Use an absolute URL such as http://localhost:3000."
|
|
549
|
-
});
|
|
550
|
-
}
|
|
551
|
-
else {
|
|
552
|
-
const port = devEnv.HTTP_PORT ?? env.HTTP_PORT ?? readDevPortFromEnvFile(root, env.NODE_ENV ?? "development") ?? String(3e3);
|
|
553
|
-
url = /^\d+$/.test(port) ? `http://localhost:${port}` : `http://localhost:${DEFAULT_DEV_PORT}`;
|
|
554
|
-
}
|
|
555
|
-
const typecheck = resolveTypecheck(root, packageJson, config.typecheck, validator);
|
|
556
|
-
const tunnel = resolveTunnel(config.tunnel, validator);
|
|
557
|
-
const logFile = config.logFile === false ? null : resolve(root, validator.string(config.logFile, "dev.logFile") ?? ".stars/dev.log");
|
|
558
|
-
const banner = config.banner === false ? false : typeof config.banner === "string" ? config.banner.split("\n") : validator.stringArray(config.banner, "dev.banner") ?? null;
|
|
559
|
-
return {
|
|
560
|
-
watch,
|
|
561
|
-
ignore,
|
|
562
|
-
debounce,
|
|
563
|
-
env: devEnv,
|
|
564
|
-
nodeArgs,
|
|
565
|
-
args,
|
|
566
|
-
url,
|
|
567
|
-
health,
|
|
568
|
-
killTimeout,
|
|
569
|
-
typecheck,
|
|
570
|
-
tunnel,
|
|
571
|
-
logFile,
|
|
572
|
-
banner
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
/**
|
|
576
|
-
* Resolves `dev.typecheck`. The tsconfig is looked up the same way the `tsc` build tool looks up its own, so a
|
|
577
|
-
* project building with `tsdown` still gets `tsc --watch --noEmit` on the right project file.
|
|
578
|
-
*/
|
|
579
|
-
function resolveTypecheck(root, packageJson, config, validator) {
|
|
580
|
-
if (config === void 0 || config === false) return {
|
|
581
|
-
enabled: false,
|
|
582
|
-
tsconfig: null,
|
|
583
|
-
checker: detectTypechecker(packageJson)
|
|
584
|
-
};
|
|
585
|
-
let configured;
|
|
586
|
-
let requestedChecker = "auto";
|
|
587
|
-
if (config !== true) {
|
|
588
|
-
if (config === null || typeof config !== "object" || Array.isArray(config)) throw validator.typeError("dev.typecheck", "a boolean or an object", config, "Use `true` to type-check with the project tsconfig, `{ tsconfig }` to pick one, or `false` to disable it.");
|
|
589
|
-
validator.knownKeys(config, "dev.typecheck", ["tsconfig", "checker"]);
|
|
590
|
-
configured = validator.string(config.tsconfig, "dev.typecheck.tsconfig");
|
|
591
|
-
requestedChecker = validator.string(config.checker, "dev.typecheck.checker") ?? "auto";
|
|
592
|
-
if (!TYPECHECKERS.has(requestedChecker)) throw validator.error(configDiagnostics.INVALID_TYPECHECKER, { checker: requestedChecker });
|
|
593
|
-
}
|
|
594
|
-
const checker = requestedChecker === "auto" ? detectTypechecker(packageJson) : requestedChecker;
|
|
595
|
-
if (configured !== void 0) {
|
|
596
|
-
const tsconfig = resolve(root, configured);
|
|
597
|
-
if (!isFile(tsconfig)) throw validator.error(configDiagnostics.TSCONFIG_EXPLICIT_NOT_FOUND, {
|
|
598
|
-
tsconfig,
|
|
599
|
-
path: "dev.typecheck.tsconfig"
|
|
600
|
-
});
|
|
601
|
-
return {
|
|
602
|
-
enabled: true,
|
|
603
|
-
tsconfig,
|
|
604
|
-
checker
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
const found = [join(root, "src", "tsconfig.json"), join(root, "tsconfig.json")].find((candidate) => isFile(candidate)) ?? null;
|
|
608
|
-
if (!found) throw validator.error(configDiagnostics.TSCONFIG_NOT_FOUND, {
|
|
609
|
-
root,
|
|
610
|
-
suggestion: "dev.typecheck.tsconfig"
|
|
611
|
-
});
|
|
612
|
-
return {
|
|
613
|
-
enabled: true,
|
|
614
|
-
tsconfig: found,
|
|
615
|
-
checker
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
/**
|
|
619
|
-
* Picks the type checker when `dev.typecheck.checker` is `auto`: `golar` when the project already depends on it
|
|
620
|
-
* (it wraps TypeScript and is what this repository's own `typecheck` scripts run), `tsc` otherwise. `tsz` is never
|
|
621
|
-
* picked automatically — it is an early, tsc-compatible alternative a project opts into.
|
|
622
|
-
*/
|
|
623
|
-
function detectTypechecker(packageJson) {
|
|
624
|
-
return hasDependency(packageJson, "golar") ? "golar" : "tsc";
|
|
625
|
-
}
|
|
626
|
-
/**
|
|
627
|
-
* Resolves `dev.tunnel`: `true` (or `{}`) opens a `cloudflared` quick tunnel, a string (or `{ url }`) is an https
|
|
628
|
-
* URL the user already serves and the CLI only checks.
|
|
629
|
-
*/
|
|
630
|
-
function resolveTunnel(config, validator) {
|
|
631
|
-
if (config === void 0 || config === false) return { mode: "off" };
|
|
632
|
-
let url;
|
|
633
|
-
let updateEndpoint = false;
|
|
634
|
-
let path = "/";
|
|
635
|
-
if (typeof config === "string") url = config;
|
|
636
|
-
else if (config !== true) {
|
|
637
|
-
if (config === null || typeof config !== "object" || Array.isArray(config)) throw validator.typeError("dev.tunnel", "a boolean, an https URL or an object", config, "Use `true` for a cloudflared quick tunnel, an https URL you already serve, or `false` to disable it.");
|
|
638
|
-
validator.knownKeys(config, "dev.tunnel", [
|
|
639
|
-
"url",
|
|
640
|
-
"updateEndpoint",
|
|
641
|
-
"path"
|
|
642
|
-
]);
|
|
643
|
-
url = validator.string(config.url, "dev.tunnel.url");
|
|
644
|
-
updateEndpoint = validator.boolean(config.updateEndpoint, "dev.tunnel.updateEndpoint") ?? false;
|
|
645
|
-
path = validator.string(config.path, "dev.tunnel.path") ?? "/";
|
|
646
|
-
}
|
|
647
|
-
if (url === void 0) return {
|
|
648
|
-
mode: "quick",
|
|
649
|
-
path,
|
|
650
|
-
updateEndpoint
|
|
651
|
-
};
|
|
652
|
-
let parsed;
|
|
653
|
-
try {
|
|
654
|
-
parsed = new URL(url);
|
|
655
|
-
} catch {
|
|
656
|
-
throw validator.error(configDiagnostics.INVALID_URL, {
|
|
657
|
-
url,
|
|
658
|
-
fix: "Use an absolute https URL such as https://bot.example.com."
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
if (parsed.protocol !== "https:") throw validator.error(configDiagnostics.TUNNEL_URL_NOT_HTTPS, { url });
|
|
662
|
-
return {
|
|
663
|
-
mode: "url",
|
|
664
|
-
url,
|
|
665
|
-
path,
|
|
666
|
-
updateEndpoint
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
function resolveCodegen(root, config, validator) {
|
|
670
|
-
validator.knownKeys(config, "codegen", ["i18n"]);
|
|
671
|
-
if (config.i18n === false) return { i18n: null };
|
|
672
|
-
if (config.i18n === void 0) {
|
|
673
|
-
const locales = join(root, DEFAULT_I18N_LOCALES);
|
|
674
|
-
return { i18n: isDirectory(locales) ? {
|
|
675
|
-
locales,
|
|
676
|
-
output: join(root, DEFAULT_I18N_OUTPUT)
|
|
677
|
-
} : null };
|
|
678
|
-
}
|
|
679
|
-
if (config.i18n === null || typeof config.i18n !== "object") throw validator.typeError("codegen.i18n", "an object or `false`", config.i18n, "Use `{ locales, output }` to configure it or `false` to disable it.");
|
|
680
|
-
validator.knownKeys(config.i18n, "codegen.i18n", ["locales", "output"]);
|
|
681
|
-
const locales = resolve(root, validator.string(config.i18n.locales, "codegen.i18n.locales") ?? "src/locales/en-US");
|
|
682
|
-
if (!isDirectory(locales)) throw validator.error(configDiagnostics.LOCALES_NOT_FOUND, { locales });
|
|
683
|
-
return { i18n: {
|
|
684
|
-
locales,
|
|
685
|
-
output: resolve(root, validator.string(config.i18n.output, "codegen.i18n.output") ?? "src/@types/i18next.d.ts")
|
|
686
|
-
} };
|
|
687
|
-
}
|
|
688
|
-
/**
|
|
689
|
-
* The transform that injects auto imports (`@wolfstar/http-framework/auto-imports`) only runs through `tsdown`'s
|
|
690
|
-
* rolldown pipeline, the same way Nuxt's own auto imports only run through its Vite/webpack build: `tsc` and `none`
|
|
691
|
-
* have no transform step to hook into. `tsdown` is picked as `build.tool` first (see {@link detectBuildTool}) for the
|
|
692
|
-
* same reason — it is the only tool this feature, and this build config in general, treats as the default choice.
|
|
693
|
-
*
|
|
694
|
-
* They are on by default from compatibility version 4 on, where `stars` wires the plugin into the build itself. At 3
|
|
695
|
-
* the plugin is the project's to add, so defaulting them on would promise imports that never get injected.
|
|
696
|
-
*/
|
|
697
|
-
function resolveImports(root, buildTool, future, config, validator) {
|
|
698
|
-
const defaultDirs = DEFAULT_IMPORTS_DIRS.map((dir) => resolve(root, dir));
|
|
699
|
-
const defaultPresets = [...DEFAULT_IMPORTS_PRESETS];
|
|
700
|
-
const defaultDts = resolve(root, DEFAULT_IMPORTS_DTS);
|
|
701
|
-
if (config === false) return {
|
|
702
|
-
enabled: false,
|
|
703
|
-
dirs: defaultDirs,
|
|
704
|
-
presets: defaultPresets,
|
|
705
|
-
exclude: [],
|
|
706
|
-
dts: defaultDts
|
|
707
|
-
};
|
|
708
|
-
const forcedOn = config === true;
|
|
709
|
-
const options = forcedOn || config === void 0 ? {} : config;
|
|
710
|
-
if (typeof options !== "object" || options === null || Array.isArray(options)) throw validator.typeError("imports", "an object, `true` or `false`", options, "Use `{ dirs, presets, exclude, dts }`, `true` to enable with defaults, or `false` to disable.");
|
|
711
|
-
validator.knownKeys(options, "imports", [
|
|
712
|
-
"enabled",
|
|
713
|
-
"dirs",
|
|
714
|
-
"presets",
|
|
715
|
-
"exclude",
|
|
716
|
-
"dts"
|
|
717
|
-
]);
|
|
718
|
-
const requestedOn = forcedOn || validator.boolean(options.enabled, "imports.enabled");
|
|
719
|
-
if (requestedOn && buildTool !== "tsdown") throw validator.error(configDiagnostics.IMPORTS_REQUIRE_TSDOWN, {});
|
|
720
|
-
const dirs = (validator.stringArray(options.dirs, "imports.dirs") ?? [...DEFAULT_IMPORTS_DIRS]).map((dir) => resolve(root, dir));
|
|
721
|
-
const presets = validator.stringArray(options.presets, "imports.presets") ?? defaultPresets;
|
|
722
|
-
const exclude = validator.stringArray(options.exclude, "imports.exclude") ?? [];
|
|
723
|
-
const dts = resolve(root, validator.string(options.dts, "imports.dts") ?? ".stars/imports.d.ts");
|
|
724
|
-
const enabledByDefault = buildTool === "tsdown" && future.compatibilityVersion >= 4;
|
|
725
|
-
return {
|
|
726
|
-
enabled: requestedOn ?? enabledByDefault,
|
|
727
|
-
dirs,
|
|
728
|
-
presets,
|
|
729
|
-
exclude,
|
|
730
|
-
dts
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
var Validator = class {
|
|
734
|
-
constructor(file) {
|
|
735
|
-
this.file = file;
|
|
736
|
-
}
|
|
737
|
-
get sources() {
|
|
738
|
-
return this.file ? [this.file] : void 0;
|
|
739
|
-
}
|
|
740
|
-
error(handle, params) {
|
|
741
|
-
return handle({
|
|
742
|
-
...params,
|
|
743
|
-
sources: this.sources
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
knownKeys(value, path, keys) {
|
|
747
|
-
for (const key of Object.keys(value)) {
|
|
748
|
-
if (keys.includes(key)) continue;
|
|
749
|
-
const fullPath = path ? `${path}.${key}` : key;
|
|
750
|
-
throw this.error(configDiagnostics.UNKNOWN_OPTION, {
|
|
751
|
-
path: fullPath,
|
|
752
|
-
parent: path,
|
|
753
|
-
known: keys.join(", ")
|
|
754
|
-
});
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
string(value, path) {
|
|
758
|
-
if (value === void 0) return void 0;
|
|
759
|
-
if (typeof value !== "string" || value.length === 0) throw this.typeError(path, "a non-empty string", value);
|
|
760
|
-
return value;
|
|
761
|
-
}
|
|
762
|
-
boolean(value, path) {
|
|
763
|
-
if (value === void 0) return void 0;
|
|
764
|
-
if (typeof value !== "boolean") throw this.typeError(path, "a boolean", value);
|
|
765
|
-
return value;
|
|
766
|
-
}
|
|
767
|
-
/** A plain object passed through as-is (e.g. raw `vite`/`tsdown` config merged into the project's own). */
|
|
768
|
-
plainObject(value, path) {
|
|
769
|
-
if (value === void 0) return void 0;
|
|
770
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) throw this.typeError(path, "an object", value);
|
|
771
|
-
return value;
|
|
772
|
-
}
|
|
773
|
-
stringArray(value, path) {
|
|
774
|
-
if (value === void 0) return void 0;
|
|
775
|
-
if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) throw this.typeError(path, "an array of strings", value);
|
|
776
|
-
return value;
|
|
777
|
-
}
|
|
778
|
-
nonNegativeNumber(value, path) {
|
|
779
|
-
if (value === void 0) return void 0;
|
|
780
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) throw this.typeError(path, "a non-negative number", value);
|
|
781
|
-
return value;
|
|
782
|
-
}
|
|
783
|
-
stringRecord(value, path) {
|
|
784
|
-
if (value === void 0) return void 0;
|
|
785
|
-
if (value === null || typeof value !== "object" || Array.isArray(value) || !Object.values(value).every((item) => typeof item === "string")) throw this.typeError(path, "an object of string values", value);
|
|
786
|
-
return value;
|
|
787
|
-
}
|
|
788
|
-
/** A generic "wrong type" diagnostic. `fix` defaults to the standard "set it or remove it" wording. */
|
|
789
|
-
typeError(path, expected, value, fix) {
|
|
790
|
-
return this.error(configDiagnostics.INVALID_TYPE, {
|
|
791
|
-
path,
|
|
792
|
-
expected,
|
|
793
|
-
value,
|
|
794
|
-
fix: fix ?? `Set \`${path}\` to ${expected} or remove it to use the default.`
|
|
795
|
-
});
|
|
796
|
-
}
|
|
797
|
-
};
|
|
798
|
-
function hasDependency(packageJson, name) {
|
|
799
|
-
return Boolean(packageJson?.dependencies?.[name] ?? packageJson?.devDependencies?.[name]);
|
|
800
|
-
}
|
|
801
|
-
function readPackageJson(root, validator) {
|
|
802
|
-
const file = join(root, "package.json");
|
|
803
|
-
if (!isFile(file)) return null;
|
|
804
|
-
try {
|
|
805
|
-
const parsed = JSON.parse(readFileSync(file, "utf-8"));
|
|
806
|
-
return parsed !== null && typeof parsed === "object" ? parsed : null;
|
|
807
|
-
} catch (error) {
|
|
808
|
-
throw validator.error(configDiagnostics.PACKAGE_JSON_INVALID, {
|
|
809
|
-
file,
|
|
810
|
-
message: error instanceof Error ? error.message : String(error),
|
|
811
|
-
cause: error
|
|
812
|
-
});
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
function isFile(path) {
|
|
816
|
-
try {
|
|
817
|
-
return existsSync(path) && statSync(path).isFile();
|
|
818
|
-
} catch {
|
|
819
|
-
return false;
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
function isDirectory(path) {
|
|
823
|
-
try {
|
|
824
|
-
return existsSync(path) && statSync(path).isDirectory();
|
|
825
|
-
} catch {
|
|
826
|
-
return false;
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
//#endregion
|
|
831
|
-
//#region src/lib/config/index.ts
|
|
832
|
-
/**
|
|
833
|
-
* Loads, validates and resolves a project's `stars.config.*`.
|
|
834
|
-
*
|
|
835
|
-
* @throws {Diagnostic} (from `nostics`, via {@link configDiagnostics}) when the configuration file cannot be loaded or contains an invalid option.
|
|
836
|
-
*/
|
|
837
|
-
async function loadStarsConfig(options = {}) {
|
|
838
|
-
const cwd = options.cwd ?? process.cwd();
|
|
839
|
-
const loaded = await loadConfigFile({
|
|
840
|
-
cwd,
|
|
841
|
-
configFile: options.configFile
|
|
842
|
-
});
|
|
843
|
-
return resolveStarsConfig({
|
|
844
|
-
cwd,
|
|
845
|
-
configFile: loaded.configFile,
|
|
846
|
-
config: loaded.config,
|
|
847
|
-
env: options.env
|
|
848
|
-
});
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
//#endregion
|
|
852
|
-
//#region src/config.ts
|
|
853
|
-
/**
|
|
854
|
-
* Typed helper for `stars.config.{ts,mts,cts,js,mjs,cjs}` files.
|
|
855
|
-
*
|
|
856
|
-
* @example
|
|
857
|
-
* ```ts
|
|
858
|
-
* import { defineConfig } from '@wolfstar/http-framework/config';
|
|
859
|
-
*
|
|
860
|
-
* export default defineConfig({
|
|
861
|
-
* entry: 'src/main.ts',
|
|
862
|
-
* build: { tool: 'tsdown' }
|
|
863
|
-
* });
|
|
864
|
-
* ```
|
|
865
|
-
*/
|
|
866
|
-
function defineConfig(config) {
|
|
867
|
-
return config;
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
//#endregion
|
|
871
|
-
export { CONFIG_EXTENSIONS, CONFIG_FILE_NAMES, configDiagnostics, defineConfig, discoverConfigFile, displayPath, loadConfigFile, loadStarsConfig, readProjectEnvFiles, resolveStarsConfig };
|
|
872
|
-
//# sourceMappingURL=config.js.map
|
|
3
|
+
export { };
|