@noego/app 0.0.3 → 0.0.5
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/.claude/settings.local.json +3 -11
- package/package.json +15 -1
- package/src/args.js +1 -0
- package/src/build/bootstrap.js +114 -8
- package/src/build/context.js +2 -2
- package/src/build/helpers.js +10 -0
- package/src/build/openapi.js +9 -4
- package/src/build/runtime-manifest.js +22 -30
- package/src/build/server.js +34 -4
- package/src/cli.js +10 -5
- package/src/client.js +141 -0
- package/src/commands/build.js +66 -21
- package/src/commands/dev.js +624 -0
- package/src/commands/runtime-entry.ts +16 -0
- package/src/config.js +73 -553
- package/src/index.js +7 -0
- package/src/runtime/config-loader.js +203 -0
- package/src/runtime/html-parser.js +47 -0
- package/src/runtime/index.js +4 -0
- package/src/runtime/runtime.js +749 -0
- package/types/client.d.ts +23 -0
package/src/config.js
CHANGED
|
@@ -1,584 +1,104 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
1
|
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import YAML from 'yaml';
|
|
6
|
-
|
|
7
|
-
const CONFIG_FILENAMES = [
|
|
8
|
-
// Primary App config filenames
|
|
9
|
-
'noego.yaml',
|
|
10
|
-
'noego.yml',
|
|
11
|
-
'noego.config.yaml',
|
|
12
|
-
'noego.config.yml',
|
|
13
|
-
'noego.config.json',
|
|
14
|
-
// Common App config filenames
|
|
15
|
-
'app.yaml',
|
|
16
|
-
'app.yml',
|
|
17
|
-
'app.config.yaml',
|
|
18
|
-
'app.config.yml',
|
|
19
|
-
'app.config.json',
|
|
20
|
-
// Back-compat with legacy Hammer config filenames
|
|
21
|
-
'hammer.yaml',
|
|
22
|
-
'hammer.yml',
|
|
23
|
-
'hammer.config.yaml',
|
|
24
|
-
'hammer.config.yml',
|
|
25
|
-
'hammer.config.json'
|
|
26
|
-
];
|
|
27
|
-
const OVERRIDE_FILE_KEY = '__appFromFile';
|
|
28
|
-
|
|
29
|
-
const DEFAULTS = {
|
|
30
|
-
mode: 'production',
|
|
31
|
-
outDir: 'dist',
|
|
32
|
-
assets: ['ui/resources/**', 'ui/styles/**', 'database/**'],
|
|
33
|
-
server: {
|
|
34
|
-
entry: 'index.ts',
|
|
35
|
-
rootDir: '.',
|
|
36
|
-
controllers: 'server/controller',
|
|
37
|
-
middleware: 'middleware',
|
|
38
|
-
openapi: 'server/stitch.yaml',
|
|
39
|
-
sqlGlobs: []
|
|
40
|
-
},
|
|
41
|
-
ui: {
|
|
42
|
-
page: 'ui/index.html',
|
|
43
|
-
rootDir: null,
|
|
44
|
-
openapi: 'ui/stitch.yaml',
|
|
45
|
-
assets: [],
|
|
46
|
-
clientExclude: ['server/**', 'middleware/**'],
|
|
47
|
-
options: 'ui/options.ts'
|
|
48
|
-
},
|
|
49
|
-
vite: {
|
|
50
|
-
client: {
|
|
51
|
-
configFile: null,
|
|
52
|
-
override: null
|
|
53
|
-
},
|
|
54
|
-
ssr: {
|
|
55
|
-
configFile: null,
|
|
56
|
-
override: null
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
dev: {
|
|
60
|
-
watch: false,
|
|
61
|
-
watchPaths: [],
|
|
62
|
-
splitServe: false,
|
|
63
|
-
frontendCmd: 'vite'
|
|
64
|
-
}
|
|
65
|
-
};
|
|
2
|
+
import { findConfigFile, loadConfig } from './runtime/config-loader.js';
|
|
3
|
+
import { resolvePath } from './build/helpers.js';
|
|
66
4
|
|
|
67
5
|
export async function loadBuildConfig(cliOptions = {}, { cwd = process.cwd() } = {}) {
|
|
68
6
|
const rootDir = resolvePath(cliOptions.root ?? '.', cwd);
|
|
69
7
|
const configFilePath = await findConfigFile(rootDir, cliOptions.configFile);
|
|
70
8
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
9
|
+
// The new config-loader will handle everything: finding, loading, parsing, validating, and resolving paths.
|
|
10
|
+
const { config } = await loadConfig(configFilePath, rootDir);
|
|
11
|
+
|
|
12
|
+
// The build commands expect a slightly different structure, so we adapt it here.
|
|
13
|
+
|
|
14
|
+
const outDir = config.outDir ? path.resolve(config.root, config.outDir) : path.resolve(config.root, 'dist');
|
|
15
|
+
config.outDir = outDir; // Ensure outDir is absolute for later use
|
|
16
|
+
config.outDir_abs = outDir;
|
|
17
|
+
|
|
18
|
+
const rawAssets = Array.isArray(config.assets) ? config.assets : [];
|
|
19
|
+
const rawServerSqlGlobs = Array.isArray(config.server?.sqlGlobs) ? config.server.sqlGlobs : [];
|
|
20
|
+
const rawClientExclude = Array.isArray(config.client?.exclude) ? config.client.exclude : [];
|
|
21
|
+
const rawViteClient = config.vite?.client ?? {};
|
|
22
|
+
const rawViteSsr = config.vite?.ssr ?? {};
|
|
23
|
+
|
|
24
|
+
const toGlobSpec = (pattern) => {
|
|
25
|
+
const isAbsolute = path.isAbsolute(pattern);
|
|
26
|
+
return {
|
|
27
|
+
pattern,
|
|
28
|
+
isAbsolute,
|
|
29
|
+
cwd: isAbsolute ? undefined : config.root,
|
|
30
|
+
absolute: isAbsolute ? path.normalize(pattern) : path.resolve(config.root, pattern)
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const resolveOptionalPath = (value) => {
|
|
35
|
+
if (!value) return null;
|
|
36
|
+
return path.isAbsolute(value) ? value : path.resolve(config.root, value);
|
|
96
37
|
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async function findConfigFile(rootDir, explicit) {
|
|
100
|
-
if (explicit) {
|
|
101
|
-
const candidate = resolvePath(explicit, rootDir);
|
|
102
|
-
await assertFile(candidate, `Config file not found: ${explicit}`);
|
|
103
|
-
return candidate;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
for (const fileName of CONFIG_FILENAMES) {
|
|
107
|
-
const candidate = path.join(rootDir, fileName);
|
|
108
|
-
if (await pathExists(candidate)) {
|
|
109
|
-
return candidate;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async function loadConfigFile(filePath) {
|
|
116
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
117
|
-
if (ext === '.yaml' || ext === '.yml') {
|
|
118
|
-
return loadYamlConfig(filePath);
|
|
119
|
-
}
|
|
120
|
-
if (ext === '.json') {
|
|
121
|
-
return loadJsonConfig(filePath);
|
|
122
|
-
}
|
|
123
|
-
throw new Error(`Unsupported App config extension for ${filePath}`);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async function loadYamlConfig(filePath) {
|
|
127
|
-
const content = await fs.readFile(filePath, 'utf8');
|
|
128
|
-
const parsed = YAML.parse(content);
|
|
129
|
-
if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
130
|
-
throw new Error(`Invalid YAML configuration in ${filePath}: expected an object`);
|
|
131
|
-
}
|
|
132
|
-
return parsed;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async function loadJsonConfig(filePath) {
|
|
136
|
-
const content = await fs.readFile(filePath, 'utf8');
|
|
137
|
-
try {
|
|
138
|
-
const parsed = JSON.parse(content);
|
|
139
|
-
if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
140
|
-
throw new Error('expected an object');
|
|
141
|
-
}
|
|
142
|
-
return parsed;
|
|
143
|
-
} catch (error) {
|
|
144
|
-
throw new Error(`Invalid JSON configuration in ${filePath}: ${error.message}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function normalizeExternalConfig(raw, { baseDir }) {
|
|
149
|
-
const result = {};
|
|
150
|
-
|
|
151
|
-
if (raw.mode != null) {
|
|
152
|
-
result.mode = raw.mode;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (raw.outDir != null) {
|
|
156
|
-
result.outDir = resolveMaybePath(raw.outDir, baseDir);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Top-level page shortcut
|
|
160
|
-
if (raw.page != null) {
|
|
161
|
-
result.ui = result.ui || {};
|
|
162
|
-
result.ui.page = resolveMaybePath(raw.page, baseDir);
|
|
163
|
-
}
|
|
164
|
-
if (raw.options != null) {
|
|
165
|
-
result.ui = result.ui || {};
|
|
166
|
-
result.ui.options = resolveMaybePath(raw.options, baseDir);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (raw.assets != null) {
|
|
170
|
-
result.assets = normalizeList(raw.assets).map((glob) =>
|
|
171
|
-
resolveMaybePath(glob, baseDir)
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (raw.server) {
|
|
176
|
-
result.server = {};
|
|
177
|
-
if (raw.server.entry != null) {
|
|
178
|
-
result.server.entry = resolveMaybePath(raw.server.entry, baseDir);
|
|
179
|
-
}
|
|
180
|
-
if (raw.server.rootDir != null) {
|
|
181
|
-
result.server.rootDir = resolveMaybePath(raw.server.rootDir, baseDir);
|
|
182
|
-
}
|
|
183
|
-
if (raw.server.controllers != null) {
|
|
184
|
-
result.server.controllers = resolveMaybePath(raw.server.controllers, baseDir);
|
|
185
|
-
}
|
|
186
|
-
if (raw.server.middleware != null) {
|
|
187
|
-
result.server.middleware = resolveMaybePath(raw.server.middleware, baseDir);
|
|
188
|
-
}
|
|
189
|
-
if (raw.server.openapi != null) {
|
|
190
|
-
result.server.openapi = resolveMaybePath(raw.server.openapi, baseDir);
|
|
191
|
-
}
|
|
192
|
-
if (raw.server.sqlGlobs != null) {
|
|
193
|
-
result.server.sqlGlobs = normalizeList(raw.server.sqlGlobs).map((glob) =>
|
|
194
|
-
resolveMaybePath(glob, baseDir)
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (raw.ui) {
|
|
200
|
-
result.ui = {};
|
|
201
|
-
if (raw.ui.page != null) {
|
|
202
|
-
result.ui.page = resolveMaybePath(raw.ui.page, baseDir);
|
|
203
|
-
}
|
|
204
|
-
if (raw.ui.options != null) {
|
|
205
|
-
result.ui.options = resolveMaybePath(raw.ui.options, baseDir);
|
|
206
|
-
}
|
|
207
|
-
if (raw.ui.rootDir != null) {
|
|
208
|
-
result.ui.rootDir = resolveMaybePath(raw.ui.rootDir, baseDir);
|
|
209
|
-
}
|
|
210
|
-
if (raw.ui.openapi != null) {
|
|
211
|
-
result.ui.openapi = resolveMaybePath(raw.ui.openapi, baseDir);
|
|
212
|
-
}
|
|
213
|
-
if (raw.ui.assets != null) {
|
|
214
|
-
result.ui.assets = normalizeList(raw.ui.assets).map((glob) =>
|
|
215
|
-
resolveMaybePath(glob, baseDir)
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
if (raw.ui.clientExclude != null) {
|
|
219
|
-
result.ui.clientExclude = normalizeList(raw.ui.clientExclude).map((glob) =>
|
|
220
|
-
resolveMaybePath(glob, baseDir)
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (raw.vite) {
|
|
226
|
-
result.vite = {};
|
|
227
|
-
if (raw.vite.client) {
|
|
228
|
-
result.vite.client = {};
|
|
229
|
-
if (raw.vite.client.configFile != null) {
|
|
230
|
-
result.vite.client.configFile = resolveMaybePath(raw.vite.client.configFile, baseDir);
|
|
231
|
-
}
|
|
232
|
-
if (raw.vite.client.override != null) {
|
|
233
|
-
result.vite.client.override = wrapOverride(raw.vite.client.override, baseDir);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
if (raw.vite.ssr) {
|
|
237
|
-
result.vite.ssr = {};
|
|
238
|
-
if (raw.vite.ssr.configFile != null) {
|
|
239
|
-
result.vite.ssr.configFile = resolveMaybePath(raw.vite.ssr.configFile, baseDir);
|
|
240
|
-
}
|
|
241
|
-
if (raw.vite.ssr.override != null) {
|
|
242
|
-
result.vite.ssr.override = wrapOverride(raw.vite.ssr.override, baseDir);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (raw.dev) {
|
|
248
|
-
result.dev = {};
|
|
249
|
-
if (raw.dev.watch != null) {
|
|
250
|
-
result.dev.watch = Boolean(raw.dev.watch);
|
|
251
|
-
}
|
|
252
|
-
if (raw.dev.watchPaths != null) {
|
|
253
|
-
result.dev.watchPaths = normalizeList(raw.dev.watchPaths).map((glob) =>
|
|
254
|
-
resolveMaybePath(glob, baseDir)
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
if (raw.dev.splitServe != null) {
|
|
258
|
-
result.dev.splitServe = Boolean(raw.dev.splitServe);
|
|
259
|
-
}
|
|
260
|
-
if (raw.dev.frontendCmd != null) {
|
|
261
|
-
result.dev.frontendCmd = raw.dev.frontendCmd;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return result;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function normalizeCliOptions(options) {
|
|
269
|
-
const result = {};
|
|
270
|
-
|
|
271
|
-
if (options.out != null) {
|
|
272
|
-
result.outDir = options.out;
|
|
273
|
-
}
|
|
274
|
-
if (options.mode != null) {
|
|
275
|
-
result.mode = options.mode;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (options.server != null || options.serverRoot != null || options.controllers != null || options.middleware != null || options.openapi != null || options.sqlGlob != null) {
|
|
279
|
-
result.server = {};
|
|
280
|
-
if (options.server != null) result.server.entry = options.server;
|
|
281
|
-
if (options.serverRoot != null) result.server.rootDir = options.serverRoot;
|
|
282
|
-
if (options.controllers != null) result.server.controllers = options.controllers;
|
|
283
|
-
if (options.middleware != null) result.server.middleware = options.middleware;
|
|
284
|
-
if (options.openapi != null) result.server.openapi = options.openapi;
|
|
285
|
-
if (options.sqlGlob != null) result.server.sqlGlobs = normalizeList(options.sqlGlob);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (options.page != null || options.uiRoot != null || options.uiOpenapi != null || options.uiOptions != null || options.clientExclude != null) {
|
|
289
|
-
result.ui = {};
|
|
290
|
-
if (options.page != null) result.ui.page = options.page;
|
|
291
|
-
if (options.uiRoot != null) result.ui.rootDir = options.uiRoot;
|
|
292
|
-
if (options.uiOpenapi != null) result.ui.openapi = options.uiOpenapi;
|
|
293
|
-
if (options.uiOptions != null) result.ui.options = options.uiOptions;
|
|
294
|
-
if (options.clientExclude != null) result.ui.clientExclude = normalizeList(options.clientExclude);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (options.assets != null) {
|
|
298
|
-
result.assets = normalizeList(options.assets);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (options.clientViteConfig != null || options.clientViteOverride != null || options.ssrViteConfig != null || options.ssrViteOverride != null) {
|
|
302
|
-
result.vite = {};
|
|
303
|
-
if (options.clientViteConfig != null || options.clientViteOverride != null) {
|
|
304
|
-
result.vite.client = {};
|
|
305
|
-
if (options.clientViteConfig != null) {
|
|
306
|
-
result.vite.client.configFile = options.clientViteConfig;
|
|
307
|
-
}
|
|
308
|
-
if (options.clientViteOverride != null) {
|
|
309
|
-
result.vite.client.override = parseJsonOption(options.clientViteOverride, '--client-vite-override');
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
if (options.ssrViteConfig != null || options.ssrViteOverride != null) {
|
|
313
|
-
result.vite.ssr = {};
|
|
314
|
-
if (options.ssrViteConfig != null) {
|
|
315
|
-
result.vite.ssr.configFile = options.ssrViteConfig;
|
|
316
|
-
}
|
|
317
|
-
if (options.ssrViteOverride != null) {
|
|
318
|
-
result.vite.ssr.override = parseJsonOption(options.ssrViteOverride, '--ssr-vite-override');
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
if (options.watch != null || options.watchPath != null || options.splitServe != null || options.frontendCmd != null) {
|
|
324
|
-
result.dev = {};
|
|
325
|
-
if (options.watch != null) result.dev.watch = Boolean(options.watch);
|
|
326
|
-
if (options.watchPath != null) result.dev.watchPaths = normalizeList(options.watchPath);
|
|
327
|
-
if (options.splitServe != null) result.dev.splitServe = Boolean(options.splitServe);
|
|
328
|
-
if (options.frontendCmd != null) result.dev.frontendCmd = options.frontendCmd;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
return result;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
async function resolveOverrides(raw) {
|
|
335
|
-
const resolved = structuredClone(raw);
|
|
336
|
-
|
|
337
|
-
const clientOverride = raw?.vite?.client?.override;
|
|
338
|
-
if (clientOverride && typeof clientOverride === 'object' && OVERRIDE_FILE_KEY in clientOverride) {
|
|
339
|
-
const loaded = await readJsonFile(clientOverride[OVERRIDE_FILE_KEY]);
|
|
340
|
-
resolved.vite.client.override = loaded;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
const ssrOverride = raw?.vite?.ssr?.override;
|
|
344
|
-
if (ssrOverride && typeof ssrOverride === 'object' && OVERRIDE_FILE_KEY in ssrOverride) {
|
|
345
|
-
const loaded = await readJsonFile(ssrOverride[OVERRIDE_FILE_KEY]);
|
|
346
|
-
resolved.vite.ssr.override = loaded;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return resolved;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
function finalizeConfig(raw, { rootDir }) {
|
|
353
|
-
const outDir = resolvePath(raw.outDir ?? DEFAULTS.outDir, rootDir);
|
|
354
|
-
|
|
355
|
-
const serverRootDir = resolvePath(raw.server?.rootDir ?? DEFAULTS.server.rootDir ?? '.', rootDir);
|
|
356
|
-
const serverEntryRaw = raw.server?.entry ?? DEFAULTS.server.entry;
|
|
357
|
-
const serverEntryAbs = resolveWithBase(serverEntryRaw, serverRootDir);
|
|
358
38
|
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
const sqlGlobs = normalizeGlobList(raw.server?.sqlGlobs ?? DEFAULTS.server.sqlGlobs, rootDir);
|
|
39
|
+
const serverSqlPatterns = rawServerSqlGlobs.length > 0
|
|
40
|
+
? rawServerSqlGlobs
|
|
41
|
+
: rawAssets.filter((pattern) => pattern.toLowerCase().includes('.sql'));
|
|
363
42
|
|
|
364
|
-
const
|
|
365
|
-
const
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
const
|
|
369
|
-
const
|
|
370
|
-
const assetSources = collectAssetPatterns({
|
|
371
|
-
topLevel: raw.assets,
|
|
372
|
-
topLevelDefault: DEFAULTS.assets,
|
|
373
|
-
uiLevel: raw.ui?.assets,
|
|
374
|
-
uiDefault: DEFAULTS.ui.assets
|
|
375
|
-
});
|
|
376
|
-
const assetGlobs = normalizeGlobList(assetSources, rootDir);
|
|
377
|
-
const clientExclude = normalizeGlobList(raw.ui?.clientExclude ?? DEFAULTS.ui.clientExclude, rootDir);
|
|
43
|
+
const serverSqlGlobs = serverSqlPatterns.map(toGlobSpec);
|
|
44
|
+
const assetSpecs = rawAssets.map(toGlobSpec);
|
|
45
|
+
const clientExcludeSpecs = rawClientExclude.map(toGlobSpec);
|
|
46
|
+
|
|
47
|
+
const uiRootDir = config.client?.main_abs ? path.dirname(config.client.main_abs) : null;
|
|
48
|
+
const uiRelRoot = uiRootDir ? path.relative(config.root, uiRootDir) : null;
|
|
378
49
|
|
|
379
|
-
const uiRelRoot = path.relative(rootDir, uiRootDir);
|
|
380
50
|
const layout = {
|
|
381
51
|
outDir,
|
|
382
52
|
serverOutDir: path.join(outDir, 'server'),
|
|
383
|
-
// Place client bundle under .app
|
|
384
53
|
clientOutDir: path.join(outDir, '.app', 'assets'),
|
|
385
|
-
|
|
386
|
-
uiOutDir: path.join(outDir, uiRelRoot),
|
|
387
|
-
// SSR precompile staging (build-only); will be overlaid into uiOutDir
|
|
54
|
+
uiOutDir: uiRelRoot ? path.join(outDir, uiRelRoot) : null,
|
|
388
55
|
ssrOutDir: path.join(outDir, '.app', 'ssr'),
|
|
389
56
|
middlewareOutDir: path.join(outDir, 'middleware'),
|
|
390
57
|
tempDir: path.join(outDir, '.app'),
|
|
391
58
|
tsOutDir: path.join(outDir, '.app', 'tsc')
|
|
392
59
|
};
|
|
393
60
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
rootDir,
|
|
397
|
-
outDir,
|
|
398
|
-
// publicPaths removed; client base is fixed to '/.app/client'
|
|
61
|
+
const buildConfig = {
|
|
62
|
+
...config,
|
|
63
|
+
rootDir: config.root,
|
|
399
64
|
layout,
|
|
400
|
-
server: {
|
|
401
|
-
rootDir:
|
|
402
|
-
entry: {
|
|
403
|
-
raw:
|
|
404
|
-
absolute:
|
|
405
|
-
relativeToRoot: path.relative(
|
|
406
|
-
relativeToServerRoot: path.relative(
|
|
407
|
-
},
|
|
408
|
-
controllersDir,
|
|
409
|
-
middlewareDir,
|
|
410
|
-
openapiFile,
|
|
411
|
-
sqlGlobs
|
|
412
|
-
},
|
|
413
|
-
ui: {
|
|
65
|
+
server: config.server ? {
|
|
66
|
+
rootDir: config.server.main_abs ? path.dirname(config.server.main_abs) : config.root,
|
|
67
|
+
entry: config.server.main_abs ? {
|
|
68
|
+
raw: config.server.main,
|
|
69
|
+
absolute: config.server.main_abs,
|
|
70
|
+
relativeToRoot: path.relative(config.root, config.server.main_abs),
|
|
71
|
+
relativeToServerRoot: path.relative(path.dirname(config.server.main_abs), config.server.main_abs)
|
|
72
|
+
} : null,
|
|
73
|
+
controllersDir: config.server.controllers_abs,
|
|
74
|
+
middlewareDir: config.server.middleware_abs,
|
|
75
|
+
openapiFile: config.server.openapi_abs,
|
|
76
|
+
sqlGlobs: serverSqlGlobs
|
|
77
|
+
} : null,
|
|
78
|
+
ui: config.client ? {
|
|
414
79
|
rootDir: uiRootDir,
|
|
415
|
-
page: {
|
|
416
|
-
raw:
|
|
417
|
-
absolute:
|
|
418
|
-
relativeToRoot: path.relative(
|
|
419
|
-
},
|
|
420
|
-
options: {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
openapiFile: uiOpenapiFile,
|
|
427
|
-
assets: assetGlobs,
|
|
428
|
-
clientExclude
|
|
429
|
-
},
|
|
430
|
-
assets: assetGlobs,
|
|
80
|
+
page: config.client.shell_abs ? {
|
|
81
|
+
raw: config.client.shell,
|
|
82
|
+
absolute: config.client.shell_abs,
|
|
83
|
+
relativeToRoot: path.relative(config.root, config.client.shell_abs)
|
|
84
|
+
} : null,
|
|
85
|
+
options: {}, // This seems to be deprecated
|
|
86
|
+
openapiFile: config.client.openapi_abs,
|
|
87
|
+
assets: assetSpecs,
|
|
88
|
+
clientExclude: clientExcludeSpecs
|
|
89
|
+
} : null,
|
|
90
|
+
assets: assetSpecs,
|
|
431
91
|
vite: {
|
|
432
92
|
client: {
|
|
433
|
-
configFile:
|
|
434
|
-
|
|
435
|
-
: null,
|
|
436
|
-
override: raw?.vite?.client?.override ?? null
|
|
93
|
+
configFile: resolveOptionalPath(rawViteClient.configFile),
|
|
94
|
+
override: rawViteClient.override ?? null
|
|
437
95
|
},
|
|
438
96
|
ssr: {
|
|
439
|
-
configFile:
|
|
440
|
-
|
|
441
|
-
: null,
|
|
442
|
-
override: raw?.vite?.ssr?.override ?? null
|
|
97
|
+
configFile: resolveOptionalPath(rawViteSsr.configFile),
|
|
98
|
+
override: rawViteSsr.override ?? null
|
|
443
99
|
}
|
|
444
|
-
},
|
|
445
|
-
dev: {
|
|
446
|
-
watch: Boolean(raw?.dev?.watch ?? DEFAULTS.dev.watch),
|
|
447
|
-
watchPaths: normalizeGlobList(raw?.dev?.watchPaths ?? DEFAULTS.dev.watchPaths, rootDir),
|
|
448
|
-
splitServe: Boolean(raw?.dev?.splitServe ?? DEFAULTS.dev.splitServe),
|
|
449
|
-
frontendCmd: raw?.dev?.frontendCmd ?? DEFAULTS.dev.frontendCmd
|
|
450
|
-
}
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
function collectAssetPatterns({ topLevel, topLevelDefault, uiLevel, uiDefault }) {
|
|
455
|
-
const patterns = new Set();
|
|
456
|
-
|
|
457
|
-
const addAll = (value) => {
|
|
458
|
-
if (value == null) return;
|
|
459
|
-
for (const entry of normalizeList(value)) {
|
|
460
|
-
if (entry == null || entry === '') continue;
|
|
461
|
-
patterns.add(entry);
|
|
462
100
|
}
|
|
463
101
|
};
|
|
464
102
|
|
|
465
|
-
|
|
466
|
-
addAll(topLevel);
|
|
467
|
-
} else {
|
|
468
|
-
addAll(topLevelDefault);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
if (uiLevel != null) {
|
|
472
|
-
addAll(uiLevel);
|
|
473
|
-
} else {
|
|
474
|
-
addAll(uiDefault);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
return Array.from(patterns);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
function normalizeGlobList(values, baseDir) {
|
|
481
|
-
return normalizeList(values).map((pattern) => ({
|
|
482
|
-
pattern,
|
|
483
|
-
isAbsolute: path.isAbsolute(pattern),
|
|
484
|
-
cwd: path.isAbsolute(pattern) ? undefined : baseDir,
|
|
485
|
-
absolute: path.isAbsolute(pattern) ? pattern : path.resolve(baseDir, pattern)
|
|
486
|
-
}));
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
function wrapOverride(value, baseDir) {
|
|
490
|
-
if (value == null) return null;
|
|
491
|
-
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
492
|
-
return value;
|
|
493
|
-
}
|
|
494
|
-
if (typeof value === 'string') {
|
|
495
|
-
const trimmed = value.trim();
|
|
496
|
-
try {
|
|
497
|
-
return JSON.parse(trimmed);
|
|
498
|
-
} catch {
|
|
499
|
-
const resolved = resolveMaybePath(trimmed, baseDir);
|
|
500
|
-
return { [OVERRIDE_FILE_KEY]: resolved };
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
throw new Error('Unsupported override configuration value');
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
function normalizeList(value) {
|
|
507
|
-
if (Array.isArray(value)) {
|
|
508
|
-
return value.flatMap((item) => normalizeList(item));
|
|
509
|
-
}
|
|
510
|
-
if (typeof value === 'string') {
|
|
511
|
-
if (value.includes(',')) {
|
|
512
|
-
return value
|
|
513
|
-
.split(',')
|
|
514
|
-
.map((part) => part.trim())
|
|
515
|
-
.filter(Boolean);
|
|
516
|
-
}
|
|
517
|
-
return value.trim() ? [value.trim()] : [];
|
|
518
|
-
}
|
|
519
|
-
if (value == null) {
|
|
520
|
-
return [];
|
|
521
|
-
}
|
|
522
|
-
return [String(value)];
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
function resolveMaybePath(value, baseDir) {
|
|
526
|
-
if (value == null || value === '') {
|
|
527
|
-
return value;
|
|
528
|
-
}
|
|
529
|
-
if (path.isAbsolute(value)) {
|
|
530
|
-
return path.normalize(value);
|
|
531
|
-
}
|
|
532
|
-
return path.resolve(baseDir, value);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
function resolveWithBase(value, baseDir) {
|
|
536
|
-
if (path.isAbsolute(value)) {
|
|
537
|
-
return path.normalize(value);
|
|
538
|
-
}
|
|
539
|
-
return path.resolve(baseDir, value);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
function resolvePath(input, baseDir) {
|
|
543
|
-
if (input == null || input === '') {
|
|
544
|
-
return path.resolve(baseDir);
|
|
545
|
-
}
|
|
546
|
-
if (path.isAbsolute(input)) {
|
|
547
|
-
return path.normalize(input);
|
|
548
|
-
}
|
|
549
|
-
return path.resolve(baseDir, input);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// No public base normalization; client assets served from '/.app/client'
|
|
553
|
-
|
|
554
|
-
async function pathExists(target) {
|
|
555
|
-
try {
|
|
556
|
-
await fs.access(target);
|
|
557
|
-
return true;
|
|
558
|
-
} catch {
|
|
559
|
-
return false;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
async function assertFile(filePath, message) {
|
|
564
|
-
if (!(await pathExists(filePath))) {
|
|
565
|
-
throw new Error(message);
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
async function readJsonFile(filePath) {
|
|
570
|
-
const content = await fs.readFile(filePath, 'utf8');
|
|
571
|
-
try {
|
|
572
|
-
return JSON.parse(content);
|
|
573
|
-
} catch (error) {
|
|
574
|
-
throw new Error(`Failed to parse JSON override at ${filePath}: ${error.message}`);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
function parseJsonOption(value, flagName) {
|
|
579
|
-
try {
|
|
580
|
-
return JSON.parse(value);
|
|
581
|
-
} catch (error) {
|
|
582
|
-
throw new Error(`Invalid JSON passed to ${flagName}: ${error.message}`);
|
|
583
|
-
}
|
|
103
|
+
return buildConfig;
|
|
584
104
|
}
|