@ossy/app 1.11.0 → 1.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/cli/api.runtime.mjs +22 -0
- package/cli/build-terminal.js +170 -37
- package/cli/build.js +335 -193
- package/cli/dev.js +129 -109
- package/cli/pages.runtime.mjs +39 -0
- package/cli/prerender-react.task.js +6 -35
- package/cli/render-page.task.js +32 -0
- package/cli/server.js +51 -11
- package/cli/tasks.runtime.mjs +22 -0
- package/cli/worker-entry.js +1 -1
- package/package.json +10 -10
package/cli/build.js
CHANGED
|
@@ -12,7 +12,7 @@ import copy from 'rollup-plugin-copy';
|
|
|
12
12
|
import replace from '@rollup/plugin-replace';
|
|
13
13
|
import arg from 'arg'
|
|
14
14
|
import prerenderReactTask from './prerender-react.task.js'
|
|
15
|
-
import {
|
|
15
|
+
import { createBuildDashboard } from './build-terminal.js'
|
|
16
16
|
|
|
17
17
|
export const PAGE_FILE_PATTERN = /\.page\.(jsx?|tsx?)$/
|
|
18
18
|
export const API_FILE_PATTERN = /\.api\.(mjs|cjs|js)$/
|
|
@@ -53,15 +53,35 @@ export function minifyBrowserStaticChunks () {
|
|
|
53
53
|
|
|
54
54
|
/** Subfolder under `build/` for generated pages/api/task entry stubs. */
|
|
55
55
|
export const OSSY_GEN_DIRNAME = '.ossy'
|
|
56
|
-
|
|
57
|
-
export const
|
|
58
|
-
export const
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
export const
|
|
62
|
-
export const
|
|
63
|
-
export const
|
|
56
|
+
/** JSON-only manifests (sources + route metadata). */
|
|
57
|
+
export const OSSY_GEN_PAGES_BASENAME = 'pages.generated.json'
|
|
58
|
+
export const OSSY_GEN_API_BASENAME = 'api.generated.json'
|
|
59
|
+
export const OSSY_GEN_TASKS_BASENAME = 'tasks.generated.json'
|
|
60
|
+
/** JSON-only build artifact index (compiled module paths, aligned to generated manifests). */
|
|
61
|
+
export const OSSY_PAGES_BUNDLE_BASENAME = 'pages.bundle.json'
|
|
62
|
+
export const OSSY_API_BUNDLE_BASENAME = 'api.bundle.json'
|
|
63
|
+
export const OSSY_TASKS_BUNDLE_BASENAME = 'tasks.bundle.json'
|
|
64
|
+
/** Small Node loaders (not JSON) that `import()` compiled modules from the bundle manifests. */
|
|
65
|
+
export const OSSY_PAGES_RUNTIME_BASENAME = 'pages.runtime.mjs'
|
|
66
|
+
export const OSSY_API_RUNTIME_BASENAME = 'api.runtime.mjs'
|
|
67
|
+
export const OSSY_TASKS_RUNTIME_BASENAME = 'tasks.runtime.mjs'
|
|
68
|
+
|
|
69
|
+
export const OSSY_PAGE_MODULES_DIRNAME = 'page-modules'
|
|
70
|
+
export const OSSY_API_MODULES_DIRNAME = 'api-modules'
|
|
71
|
+
export const OSSY_TASK_MODULES_DIRNAME = 'task-modules'
|
|
72
|
+
|
|
64
73
|
export const OSSY_MIDDLEWARE_RUNTIME_BASENAME = 'middleware.runtime.js'
|
|
74
|
+
export const OSSY_SERVER_CONFIG_RUNTIME_BASENAME = 'server-config.runtime.mjs'
|
|
75
|
+
export const OSSY_RENDER_PAGE_RUNTIME_BASENAME = 'render-page.task.js'
|
|
76
|
+
|
|
77
|
+
/** Keep React external across per-page server chunks so `pages.runtime.mjs` shares one React. */
|
|
78
|
+
export const OSSY_PAGE_SERVER_EXTERNAL = [
|
|
79
|
+
'react',
|
|
80
|
+
'react-dom',
|
|
81
|
+
'react-dom/static',
|
|
82
|
+
'react-dom/client',
|
|
83
|
+
'react/jsx-runtime',
|
|
84
|
+
]
|
|
65
85
|
|
|
66
86
|
/** Per-page client entries: `hydrate-<pageId>.jsx` under `.ossy/` */
|
|
67
87
|
const HYDRATE_STUB_PREFIX = 'hydrate-'
|
|
@@ -210,10 +230,18 @@ export function createOssyClientRollupPlugins ({ nodeEnv, copyPublicFrom, buildP
|
|
|
210
230
|
}
|
|
211
231
|
|
|
212
232
|
/** Bundles a single Node ESM file (inline dynamic imports) for SSR / API / tasks. */
|
|
213
|
-
export async function bundleOssyNodeEntry ({ inputPath, outputFile, nodeEnv }) {
|
|
233
|
+
export async function bundleOssyNodeEntry ({ inputPath, outputFile, nodeEnv, onWarn, external }) {
|
|
214
234
|
const bundle = await rollup({
|
|
215
235
|
input: inputPath,
|
|
236
|
+
...(external && external.length ? { external } : {}),
|
|
216
237
|
plugins: createOssyAppBundlePlugins({ nodeEnv }),
|
|
238
|
+
onwarn (warning, defaultHandler) {
|
|
239
|
+
if (onWarn) {
|
|
240
|
+
onWarn(warning)
|
|
241
|
+
return
|
|
242
|
+
}
|
|
243
|
+
defaultHandler(warning)
|
|
244
|
+
},
|
|
217
245
|
})
|
|
218
246
|
await bundle.write({
|
|
219
247
|
file: outputFile,
|
|
@@ -224,14 +252,19 @@ export async function bundleOssyNodeEntry ({ inputPath, outputFile, nodeEnv }) {
|
|
|
224
252
|
}
|
|
225
253
|
|
|
226
254
|
/**
|
|
227
|
-
* Re-exports middleware via `file:`
|
|
255
|
+
* Re-exports middleware and app config via `file:` URLs so `src/*.js` can keep relative imports.
|
|
228
256
|
*/
|
|
229
|
-
export function writeAppRuntimeShims ({ middlewareSourcePath, ossyDir }) {
|
|
257
|
+
export function writeAppRuntimeShims ({ middlewareSourcePath, configSourcePath, ossyDir }) {
|
|
230
258
|
const mwHref = url.pathToFileURL(path.resolve(middlewareSourcePath)).href
|
|
231
259
|
fs.writeFileSync(
|
|
232
260
|
path.join(ossyDir, OSSY_MIDDLEWARE_RUNTIME_BASENAME),
|
|
233
261
|
`// Generated by @ossy/app — do not edit\nexport { default } from '${mwHref}'\n`
|
|
234
262
|
)
|
|
263
|
+
const cfgHref = url.pathToFileURL(path.resolve(configSourcePath)).href
|
|
264
|
+
fs.writeFileSync(
|
|
265
|
+
path.join(ossyDir, OSSY_SERVER_CONFIG_RUNTIME_BASENAME),
|
|
266
|
+
`// Generated by @ossy/app — do not edit\nexport { default } from '${cfgHref}'\n`
|
|
267
|
+
)
|
|
235
268
|
}
|
|
236
269
|
|
|
237
270
|
/**
|
|
@@ -251,6 +284,18 @@ export function copyOssyAppRuntime ({ scriptDir, buildPath }) {
|
|
|
251
284
|
}
|
|
252
285
|
fs.copyFileSync(path.join(scriptDir, 'worker-entry.js'), path.join(buildPath, 'worker.js'))
|
|
253
286
|
fs.copyFileSync(path.join(scriptDir, 'worker-runtime.js'), path.join(buildPath, 'worker-runtime.js'))
|
|
287
|
+
const ossyOut = ossyGeneratedDir(buildPath)
|
|
288
|
+
for (const name of [
|
|
289
|
+
OSSY_PAGES_RUNTIME_BASENAME,
|
|
290
|
+
OSSY_API_RUNTIME_BASENAME,
|
|
291
|
+
OSSY_TASKS_RUNTIME_BASENAME,
|
|
292
|
+
]) {
|
|
293
|
+
fs.copyFileSync(path.join(scriptDir, name), path.join(ossyOut, name))
|
|
294
|
+
}
|
|
295
|
+
fs.copyFileSync(
|
|
296
|
+
path.join(scriptDir, OSSY_RENDER_PAGE_RUNTIME_BASENAME),
|
|
297
|
+
path.join(ossyOut, OSSY_RENDER_PAGE_RUNTIME_BASENAME)
|
|
298
|
+
)
|
|
254
299
|
}
|
|
255
300
|
|
|
256
301
|
/**
|
|
@@ -274,107 +319,157 @@ export function discoverFilesByPattern (srcDir, filePattern) {
|
|
|
274
319
|
return files.sort()
|
|
275
320
|
}
|
|
276
321
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
322
|
+
export function writeOssyJson (filePath, data) {
|
|
323
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
324
|
+
fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf8')
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** JSON manifest: discovered API source paths (posix, relative to `cwd`). */
|
|
328
|
+
export function buildApiManifestPayload (apiFiles, cwd = process.cwd()) {
|
|
329
|
+
return {
|
|
330
|
+
version: 1,
|
|
331
|
+
files: apiFiles.map((f) => path.relative(cwd, f).replace(/\\/g, '/')),
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** JSON manifest: discovered task source paths (posix, relative to `cwd`). */
|
|
336
|
+
export function buildTasksManifestPayload (taskFiles, cwd = process.cwd()) {
|
|
337
|
+
return {
|
|
338
|
+
version: 1,
|
|
339
|
+
files: taskFiles.map((f) => path.relative(cwd, f).replace(/\\/g, '/')),
|
|
289
340
|
}
|
|
290
|
-
const lines = [
|
|
291
|
-
'// Generated by @ossy/app — do not edit',
|
|
292
|
-
'',
|
|
293
|
-
]
|
|
294
|
-
apiFiles.forEach((f, i) => {
|
|
295
|
-
const rel = relToGeneratedImport(generatedPath, f)
|
|
296
|
-
lines.push(`import * as _api${i} from './${rel}'`)
|
|
297
|
-
})
|
|
298
|
-
lines.push(
|
|
299
|
-
'',
|
|
300
|
-
'function _normalizeApiExport(mod) {',
|
|
301
|
-
' const d = mod?.default',
|
|
302
|
-
' if (d == null) return []',
|
|
303
|
-
' return Array.isArray(d) ? d : [d]',
|
|
304
|
-
'}',
|
|
305
|
-
'',
|
|
306
|
-
'export default [',
|
|
307
|
-
)
|
|
308
|
-
const parts = apiFiles.map((_, i) => ` ..._normalizeApiExport(_api${i}),`)
|
|
309
|
-
lines.push(parts.join('\n'))
|
|
310
|
-
lines.push(']')
|
|
311
|
-
lines.push('')
|
|
312
|
-
return lines.join('\n')
|
|
313
341
|
}
|
|
314
342
|
|
|
315
343
|
/**
|
|
316
|
-
* Writes `build/.ossy/api.generated.
|
|
317
|
-
*
|
|
344
|
+
* Writes `build/.ossy/api.generated.json` (sources only).
|
|
345
|
+
* Compiled modules + `api.bundle.json` are produced by {@link compileOssyNodeArtifacts}.
|
|
318
346
|
*/
|
|
319
|
-
export function resolveApiSource ({ srcDir, buildPath }) {
|
|
347
|
+
export function resolveApiSource ({ srcDir, buildPath, cwd = process.cwd() }) {
|
|
320
348
|
ensureOssyGeneratedDir(buildPath)
|
|
321
349
|
const generatedPath = path.join(ossyGeneratedDir(buildPath), OSSY_GEN_API_BASENAME)
|
|
322
350
|
const apiFiles = discoverFilesByPattern(srcDir, API_FILE_PATTERN)
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
generateApiModule({ generatedPath, apiFiles })
|
|
326
|
-
)
|
|
327
|
-
return { apiSourcePath: generatedPath, apiOverviewFiles: apiFiles }
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Merges `src/tasks.js` (optional) and every `*.task.js` under `src/` into one default export array
|
|
332
|
-
* of job handlers `{ type, handler }` for the Ossy worker.
|
|
333
|
-
*/
|
|
334
|
-
export function generateTaskModule ({ generatedPath, taskFiles }) {
|
|
335
|
-
const lines = [
|
|
336
|
-
'// Generated by @ossy/app — do not edit',
|
|
337
|
-
'',
|
|
338
|
-
]
|
|
339
|
-
taskFiles.forEach((f, i) => {
|
|
340
|
-
const rel = relToGeneratedImport(generatedPath, f)
|
|
341
|
-
lines.push(`import * as _task${i} from './${rel}'`)
|
|
342
|
-
})
|
|
343
|
-
lines.push(
|
|
344
|
-
'',
|
|
345
|
-
'function _normalizeTaskExport(mod) {',
|
|
346
|
-
' const d = mod?.default',
|
|
347
|
-
' if (d == null) return []',
|
|
348
|
-
' return Array.isArray(d) ? d : [d]',
|
|
349
|
-
'}',
|
|
350
|
-
'',
|
|
351
|
-
'export default [',
|
|
352
|
-
)
|
|
353
|
-
const parts = []
|
|
354
|
-
taskFiles.forEach((_, i) => {
|
|
355
|
-
parts.push(` ..._normalizeTaskExport(_task${i}),`)
|
|
356
|
-
})
|
|
357
|
-
lines.push(parts.join('\n'))
|
|
358
|
-
lines.push(']')
|
|
359
|
-
lines.push('')
|
|
360
|
-
return lines.join('\n')
|
|
351
|
+
writeOssyJson(generatedPath, buildApiManifestPayload(apiFiles, cwd))
|
|
352
|
+
return { apiGeneratedPath: generatedPath, apiOverviewFiles: apiFiles }
|
|
361
353
|
}
|
|
362
354
|
|
|
363
355
|
/**
|
|
364
|
-
* Writes `build/.ossy/tasks.generated.
|
|
356
|
+
* Writes `build/.ossy/tasks.generated.json` (sources only).
|
|
365
357
|
*/
|
|
366
|
-
export function resolveTaskSource ({ srcDir, buildPath }) {
|
|
358
|
+
export function resolveTaskSource ({ srcDir, buildPath, cwd = process.cwd() }) {
|
|
367
359
|
ensureOssyGeneratedDir(buildPath)
|
|
368
360
|
const generatedPath = path.join(ossyGeneratedDir(buildPath), OSSY_GEN_TASKS_BASENAME)
|
|
369
361
|
const taskFiles = discoverFilesByPattern(srcDir, TASK_FILE_PATTERN)
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
362
|
+
writeOssyJson(generatedPath, buildTasksManifestPayload(taskFiles, cwd))
|
|
363
|
+
return { tasksGeneratedPath: generatedPath, taskOverviewFiles: taskFiles }
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export async function compilePageServerModules ({
|
|
367
|
+
pageFiles,
|
|
368
|
+
srcDir,
|
|
369
|
+
ossyDir,
|
|
370
|
+
nodeEnv,
|
|
371
|
+
onWarn,
|
|
372
|
+
}) {
|
|
373
|
+
const modsDir = path.join(ossyDir, OSSY_PAGE_MODULES_DIRNAME)
|
|
374
|
+
fs.rmSync(modsDir, { recursive: true, force: true })
|
|
375
|
+
if (pageFiles.length === 0) {
|
|
376
|
+
return []
|
|
377
|
+
}
|
|
378
|
+
fs.mkdirSync(modsDir, { recursive: true })
|
|
379
|
+
const bundlePages = []
|
|
380
|
+
for (const f of pageFiles) {
|
|
381
|
+
const pageId = clientHydrateIdForPage(f, srcDir)
|
|
382
|
+
const safeId = String(pageId).replace(/[^a-zA-Z0-9_-]+/g, '-') || 'page'
|
|
383
|
+
const outName = `${safeId}.mjs`
|
|
384
|
+
const outFile = path.join(modsDir, outName)
|
|
385
|
+
await bundleOssyNodeEntry({
|
|
386
|
+
inputPath: f,
|
|
387
|
+
outputFile: outFile,
|
|
388
|
+
nodeEnv,
|
|
389
|
+
onWarn,
|
|
390
|
+
external: OSSY_PAGE_SERVER_EXTERNAL,
|
|
375
391
|
})
|
|
376
|
-
|
|
377
|
-
|
|
392
|
+
bundlePages.push({
|
|
393
|
+
id: pageId,
|
|
394
|
+
module: `${OSSY_PAGE_MODULES_DIRNAME}/${outName}`,
|
|
395
|
+
})
|
|
396
|
+
}
|
|
397
|
+
return bundlePages
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export async function compileApiServerModules ({ apiFiles, ossyDir, nodeEnv, onWarn }) {
|
|
401
|
+
const modsDir = path.join(ossyDir, OSSY_API_MODULES_DIRNAME)
|
|
402
|
+
fs.rmSync(modsDir, { recursive: true, force: true })
|
|
403
|
+
if (apiFiles.length === 0) {
|
|
404
|
+
return []
|
|
405
|
+
}
|
|
406
|
+
fs.mkdirSync(modsDir, { recursive: true })
|
|
407
|
+
const modules = []
|
|
408
|
+
for (let i = 0; i < apiFiles.length; i++) {
|
|
409
|
+
const outName = `api-${i}.mjs`
|
|
410
|
+
const outFile = path.join(modsDir, outName)
|
|
411
|
+
await bundleOssyNodeEntry({
|
|
412
|
+
inputPath: apiFiles[i],
|
|
413
|
+
outputFile: outFile,
|
|
414
|
+
nodeEnv,
|
|
415
|
+
onWarn,
|
|
416
|
+
})
|
|
417
|
+
modules.push(`${OSSY_API_MODULES_DIRNAME}/${outName}`)
|
|
418
|
+
}
|
|
419
|
+
return modules
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
export async function compileTaskServerModules ({ taskFiles, ossyDir, nodeEnv, onWarn }) {
|
|
423
|
+
const modsDir = path.join(ossyDir, OSSY_TASK_MODULES_DIRNAME)
|
|
424
|
+
fs.rmSync(modsDir, { recursive: true, force: true })
|
|
425
|
+
if (taskFiles.length === 0) {
|
|
426
|
+
return []
|
|
427
|
+
}
|
|
428
|
+
fs.mkdirSync(modsDir, { recursive: true })
|
|
429
|
+
const modules = []
|
|
430
|
+
for (let i = 0; i < taskFiles.length; i++) {
|
|
431
|
+
const outName = `task-${i}.mjs`
|
|
432
|
+
const outFile = path.join(modsDir, outName)
|
|
433
|
+
await bundleOssyNodeEntry({
|
|
434
|
+
inputPath: taskFiles[i],
|
|
435
|
+
outputFile: outFile,
|
|
436
|
+
nodeEnv,
|
|
437
|
+
onWarn,
|
|
438
|
+
})
|
|
439
|
+
modules.push(`${OSSY_TASK_MODULES_DIRNAME}/${outName}`)
|
|
440
|
+
}
|
|
441
|
+
return modules
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Writes `pages.bundle.json`, `api.bundle.json`, `tasks.bundle.json` by Rollup-compiling each source module.
|
|
446
|
+
*/
|
|
447
|
+
export async function compileOssyNodeArtifacts ({
|
|
448
|
+
pageFiles,
|
|
449
|
+
srcDir,
|
|
450
|
+
ossyDir,
|
|
451
|
+
apiFiles,
|
|
452
|
+
taskFiles,
|
|
453
|
+
nodeEnv,
|
|
454
|
+
onWarn,
|
|
455
|
+
}) {
|
|
456
|
+
const [pageBundleList, apiModuleList, taskModuleList] = await Promise.all([
|
|
457
|
+
compilePageServerModules({ pageFiles, srcDir, ossyDir, nodeEnv, onWarn }),
|
|
458
|
+
compileApiServerModules({ apiFiles, ossyDir, nodeEnv, onWarn }),
|
|
459
|
+
compileTaskServerModules({ taskFiles, ossyDir, nodeEnv, onWarn }),
|
|
460
|
+
])
|
|
461
|
+
writeOssyJson(path.join(ossyDir, OSSY_PAGES_BUNDLE_BASENAME), {
|
|
462
|
+
version: 1,
|
|
463
|
+
pages: pageBundleList,
|
|
464
|
+
})
|
|
465
|
+
writeOssyJson(path.join(ossyDir, OSSY_API_BUNDLE_BASENAME), {
|
|
466
|
+
version: 1,
|
|
467
|
+
modules: apiModuleList,
|
|
468
|
+
})
|
|
469
|
+
writeOssyJson(path.join(ossyDir, OSSY_TASKS_BUNDLE_BASENAME), {
|
|
470
|
+
version: 1,
|
|
471
|
+
modules: taskModuleList,
|
|
472
|
+
})
|
|
378
473
|
}
|
|
379
474
|
|
|
380
475
|
export function filePathToRoute(filePath, srcDir) {
|
|
@@ -407,7 +502,7 @@ export function clientHydrateIdForPage (pageAbsPath, srcDir) {
|
|
|
407
502
|
|
|
408
503
|
/**
|
|
409
504
|
* One client entry per page: imports only that page module and hydrates the document.
|
|
410
|
-
* Keeps the same `toPage` shape as `
|
|
505
|
+
* Keeps the same `toPage` shape as `pages.runtime.mjs` + manifests so SSR and client trees match.
|
|
411
506
|
*/
|
|
412
507
|
export function generatePageHydrateModule ({ pageAbsPath, stubAbsPath, srcDir }) {
|
|
413
508
|
const rel = relToGeneratedImport(stubAbsPath, pageAbsPath)
|
|
@@ -484,32 +579,39 @@ export function buildClientHydrateInput (pageFiles, srcDir, ossyDir) {
|
|
|
484
579
|
return input
|
|
485
580
|
}
|
|
486
581
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
582
|
+
/** JSON manifest: route ids, default paths, and page source paths (posix, relative to `cwd`). */
|
|
583
|
+
export function buildPagesGeneratedPayload (pageFiles, srcDir, cwd = process.cwd()) {
|
|
584
|
+
const pages = pageFiles.map((f) => {
|
|
585
|
+
const { path: routePath } = filePathToRoute(f, srcDir)
|
|
586
|
+
const pageId = clientHydrateIdForPage(f, srcDir)
|
|
587
|
+
return {
|
|
588
|
+
id: pageId,
|
|
589
|
+
path: routePath,
|
|
590
|
+
sourceFile: path.relative(cwd, f).replace(/\\/g, '/'),
|
|
591
|
+
}
|
|
592
|
+
})
|
|
593
|
+
return { version: 1, pages }
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
export function writePagesManifest ({
|
|
597
|
+
pageFiles,
|
|
598
|
+
srcDir,
|
|
599
|
+
pagesGeneratedPath,
|
|
600
|
+
cwd = process.cwd(),
|
|
601
|
+
}) {
|
|
602
|
+
writeOssyJson(pagesGeneratedPath, buildPagesGeneratedPayload(pageFiles, srcDir, cwd))
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export function parsePagesFromManifestJson (manifestPath) {
|
|
606
|
+
try {
|
|
607
|
+
const raw = fs.readFileSync(manifestPath, 'utf8')
|
|
608
|
+
const data = JSON.parse(raw)
|
|
609
|
+
const pages = data?.pages
|
|
610
|
+
if (!Array.isArray(pages)) return []
|
|
611
|
+
return pages.map((p) => ({ id: p.id, path: p.path }))
|
|
612
|
+
} catch {
|
|
613
|
+
return []
|
|
614
|
+
}
|
|
513
615
|
}
|
|
514
616
|
|
|
515
617
|
export function parsePagesFromSource(filePath) {
|
|
@@ -545,24 +647,42 @@ export function parsePagesFromSource(filePath) {
|
|
|
545
647
|
}
|
|
546
648
|
}
|
|
547
649
|
|
|
548
|
-
|
|
650
|
+
/**
|
|
651
|
+
* Same facts as the old build overview printout, for dashboards / plain logging.
|
|
652
|
+
*/
|
|
653
|
+
export function getBuildOverviewSnapshot ({
|
|
549
654
|
pagesSourcePath,
|
|
550
|
-
apiSourcePath,
|
|
551
655
|
apiOverviewFiles = [],
|
|
552
656
|
configPath,
|
|
553
657
|
pageFiles,
|
|
554
658
|
}) {
|
|
555
|
-
const rel = (p) => p ? path.relative(process.cwd(), p) : undefined
|
|
659
|
+
const rel = (p) => (p ? path.relative(process.cwd(), p) : undefined)
|
|
556
660
|
const srcDir = path.resolve(process.cwd(), 'src')
|
|
661
|
+
const configRel = fs.existsSync(configPath) ? rel(configPath) : null
|
|
662
|
+
|
|
663
|
+
const pages = pageFiles?.length
|
|
664
|
+
? pageFiles.map((f) => {
|
|
665
|
+
const { id, path: routePath } = filePathToRoute(f, srcDir)
|
|
666
|
+
return { id: clientHydrateIdForPage(f, srcDir), path: routePath }
|
|
667
|
+
})
|
|
668
|
+
: parsePagesFromManifestJson(pagesSourcePath)
|
|
669
|
+
|
|
670
|
+
const apiRoutes = []
|
|
671
|
+
for (const f of apiOverviewFiles) {
|
|
672
|
+
if (fs.existsSync(f)) apiRoutes.push(...parsePagesFromSource(f))
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return { configRel, pages, apiRoutes }
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
export function printBuildOverview (opts) {
|
|
679
|
+
const { configRel, pages, apiRoutes } = getBuildOverviewSnapshot(opts)
|
|
557
680
|
console.log('\n \x1b[1mBuild overview\x1b[0m')
|
|
558
|
-
if (
|
|
559
|
-
console.log(` \x1b[36mConfig:\x1b[0m ${
|
|
681
|
+
if (configRel) {
|
|
682
|
+
console.log(` \x1b[36mConfig:\x1b[0m ${configRel}`)
|
|
560
683
|
}
|
|
561
684
|
console.log(' ' + '─'.repeat(50))
|
|
562
685
|
|
|
563
|
-
const pages = pageFiles?.length
|
|
564
|
-
? pageFiles.map((f) => filePathToRoute(f, srcDir))
|
|
565
|
-
: parsePagesFromSource(pagesSourcePath)
|
|
566
686
|
if (pages.length > 0) {
|
|
567
687
|
console.log(' \x1b[36mRoutes:\x1b[0m')
|
|
568
688
|
const maxId = Math.max(6, ...pages.map((p) => String(p.id).length))
|
|
@@ -576,16 +696,6 @@ export function printBuildOverview({
|
|
|
576
696
|
console.log(' \x1b[33mRoutes:\x1b[0m (could not parse or empty)')
|
|
577
697
|
}
|
|
578
698
|
|
|
579
|
-
const apiFilesToScan =
|
|
580
|
-
apiOverviewFiles?.length > 0
|
|
581
|
-
? apiOverviewFiles
|
|
582
|
-
: fs.existsSync(apiSourcePath)
|
|
583
|
-
? [apiSourcePath]
|
|
584
|
-
: []
|
|
585
|
-
const apiRoutes = []
|
|
586
|
-
for (const f of apiFilesToScan) {
|
|
587
|
-
if (fs.existsSync(f)) apiRoutes.push(...parsePagesFromSource(f))
|
|
588
|
-
}
|
|
589
699
|
if (apiRoutes.length > 0) {
|
|
590
700
|
console.log(' \x1b[36mAPI routes:\x1b[0m')
|
|
591
701
|
apiRoutes.forEach((r) => {
|
|
@@ -601,10 +711,6 @@ export const build = async (cliArgs) => {
|
|
|
601
711
|
'-c': '--config',
|
|
602
712
|
}, { argv: cliArgs })
|
|
603
713
|
|
|
604
|
-
console.log('\n \x1b[1m@ossy/app\x1b[0m \x1b[2mbuild\x1b[0m')
|
|
605
|
-
console.log(' \x1b[2m────────────────────────────────────────\x1b[0m')
|
|
606
|
-
|
|
607
|
-
|
|
608
714
|
const scriptDir = path.dirname(url.fileURLToPath(import.meta.url))
|
|
609
715
|
const buildPath = path.resolve('build')
|
|
610
716
|
const srcDir = path.resolve('src')
|
|
@@ -613,37 +719,29 @@ export const build = async (cliArgs) => {
|
|
|
613
719
|
|
|
614
720
|
resetOssyBuildDir(buildPath)
|
|
615
721
|
|
|
616
|
-
writeResourceTemplatesBarrelIfPresent({
|
|
722
|
+
const resourceTemplatesResult = writeResourceTemplatesBarrelIfPresent({
|
|
723
|
+
cwd: process.cwd(),
|
|
724
|
+
log: false,
|
|
725
|
+
})
|
|
617
726
|
|
|
618
|
-
const
|
|
727
|
+
const ossyDir = ossyGeneratedDir(buildPath)
|
|
728
|
+
const pagesGeneratedPath = path.join(ossyDir, OSSY_GEN_PAGES_BASENAME)
|
|
619
729
|
|
|
620
|
-
|
|
730
|
+
writePagesManifest({
|
|
731
|
+
pageFiles,
|
|
732
|
+
srcDir,
|
|
621
733
|
pagesGeneratedPath,
|
|
622
|
-
|
|
623
|
-
)
|
|
624
|
-
const ossyDir = ossyGeneratedDir(buildPath)
|
|
734
|
+
})
|
|
625
735
|
writePageHydrateStubs(pageFiles, srcDir, ossyDir)
|
|
626
736
|
const clientHydrateInput = buildClientHydrateInput(pageFiles, srcDir, ossyDir)
|
|
627
737
|
|
|
628
|
-
const {
|
|
629
|
-
apiSourcePath: resolvedApi,
|
|
630
|
-
apiOverviewFiles,
|
|
631
|
-
} = resolveApiSource({
|
|
738
|
+
const { apiOverviewFiles } = resolveApiSource({
|
|
632
739
|
srcDir,
|
|
633
740
|
buildPath,
|
|
634
741
|
})
|
|
635
|
-
let apiSourcePath = resolvedApi
|
|
636
742
|
let middlewareSourcePath = path.resolve('src/middleware.js');
|
|
637
743
|
const publicDir = path.resolve('public')
|
|
638
744
|
|
|
639
|
-
printBuildOverview({
|
|
640
|
-
pagesSourcePath: pagesGeneratedPath,
|
|
641
|
-
apiSourcePath,
|
|
642
|
-
apiOverviewFiles,
|
|
643
|
-
configPath,
|
|
644
|
-
pageFiles,
|
|
645
|
-
});
|
|
646
|
-
|
|
647
745
|
if (!fs.existsSync(middlewareSourcePath)) {
|
|
648
746
|
middlewareSourcePath = path.resolve(scriptDir, 'middleware.js')
|
|
649
747
|
}
|
|
@@ -652,41 +750,83 @@ export const build = async (cliArgs) => {
|
|
|
652
750
|
? configPath
|
|
653
751
|
: path.resolve(scriptDir, 'default-config.js')
|
|
654
752
|
|
|
655
|
-
const
|
|
656
|
-
const apiBundlePath = path.join(ossyDir, OSSY_API_SERVER_BUNDLE)
|
|
657
|
-
const tasksBundlePath = path.join(ossyDir, OSSY_TASKS_SERVER_BUNDLE)
|
|
753
|
+
const pagesEntryPath = path.join(ossyDir, OSSY_PAGES_RUNTIME_BASENAME)
|
|
658
754
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
inputPath: apiSourcePath,
|
|
666
|
-
outputFile: apiBundlePath,
|
|
667
|
-
nodeEnv: 'production',
|
|
755
|
+
const useDashboard = Object.keys(clientHydrateInput).length > 0
|
|
756
|
+
const overviewSnap = getBuildOverviewSnapshot({
|
|
757
|
+
pagesSourcePath: pagesGeneratedPath,
|
|
758
|
+
apiOverviewFiles,
|
|
759
|
+
configPath,
|
|
760
|
+
pageFiles,
|
|
668
761
|
})
|
|
669
|
-
const
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
762
|
+
const idToPath = Object.fromEntries(
|
|
763
|
+
pageFiles.map((f) => [
|
|
764
|
+
clientHydrateIdForPage(f, srcDir),
|
|
765
|
+
filePathToRoute(f, srcDir).path,
|
|
766
|
+
])
|
|
767
|
+
)
|
|
768
|
+
const pageIds = useDashboard
|
|
769
|
+
? [...new Set(pageFiles.map((f) => clientHydrateIdForPage(f, srcDir)))].sort()
|
|
770
|
+
: []
|
|
771
|
+
|
|
772
|
+
let dashboard = null
|
|
773
|
+
if (useDashboard && pageIds.length > 0) {
|
|
774
|
+
dashboard = createBuildDashboard({
|
|
775
|
+
mode: 'full',
|
|
776
|
+
pageIds,
|
|
777
|
+
idToPath,
|
|
778
|
+
overview: {
|
|
779
|
+
title: '@ossy/app build',
|
|
780
|
+
configRel: overviewSnap.configRel,
|
|
781
|
+
apiRoutes: overviewSnap.apiRoutes,
|
|
782
|
+
},
|
|
783
|
+
})
|
|
784
|
+
dashboard.start()
|
|
785
|
+
if (resourceTemplatesResult.wrote && resourceTemplatesResult.path) {
|
|
786
|
+
dashboard.pushLog(
|
|
787
|
+
`[resource-templates] merged ${resourceTemplatesResult.count} → ${path.relative(process.cwd(), resourceTemplatesResult.path)}`
|
|
788
|
+
)
|
|
789
|
+
}
|
|
790
|
+
} else {
|
|
791
|
+
console.log('\n \x1b[1m@ossy/app\x1b[0m \x1b[2mbuild\x1b[0m')
|
|
792
|
+
printBuildOverview({
|
|
793
|
+
pagesSourcePath: pagesGeneratedPath,
|
|
794
|
+
apiOverviewFiles,
|
|
795
|
+
configPath,
|
|
796
|
+
pageFiles,
|
|
797
|
+
})
|
|
798
|
+
if (resourceTemplatesResult.wrote && resourceTemplatesResult.path) {
|
|
799
|
+
console.log(
|
|
800
|
+
`[@ossy/app][resource-templates] merged ${resourceTemplatesResult.count} template(s) → ${path.relative(process.cwd(), resourceTemplatesResult.path)}`
|
|
801
|
+
)
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
const warnSink = dashboard
|
|
806
|
+
? (w) => {
|
|
807
|
+
dashboard.pushLog(`rollup: ${w.message}`)
|
|
808
|
+
}
|
|
809
|
+
: undefined
|
|
810
|
+
|
|
811
|
+
const { taskOverviewFiles } = resolveTaskSource({ srcDir, buildPath })
|
|
812
|
+
await compileOssyNodeArtifacts({
|
|
813
|
+
pageFiles,
|
|
814
|
+
srcDir,
|
|
815
|
+
ossyDir,
|
|
816
|
+
apiFiles: apiOverviewFiles,
|
|
817
|
+
taskFiles: taskOverviewFiles,
|
|
673
818
|
nodeEnv: 'production',
|
|
819
|
+
onWarn: warnSink,
|
|
674
820
|
})
|
|
675
821
|
|
|
676
822
|
writeAppRuntimeShims({
|
|
677
823
|
middlewareSourcePath,
|
|
824
|
+
configSourcePath,
|
|
678
825
|
ossyDir,
|
|
679
826
|
})
|
|
680
827
|
copyOssyAppRuntime({ scriptDir, buildPath })
|
|
681
828
|
|
|
682
|
-
if (
|
|
683
|
-
const pageIds = [
|
|
684
|
-
...new Set(pageFiles.map((f) => clientHydrateIdForPage(f, srcDir))),
|
|
685
|
-
].sort()
|
|
686
|
-
const reporter = pageIds.length
|
|
687
|
-
? createParallelPagesReporter({ pageIds })
|
|
688
|
-
: null
|
|
689
|
-
reporter?.printSectionTitle('client hydrate + prerender (parallel)')
|
|
829
|
+
if (useDashboard && dashboard) {
|
|
690
830
|
try {
|
|
691
831
|
await prerenderReactTask.handler({
|
|
692
832
|
op: 'runProduction',
|
|
@@ -695,17 +835,19 @@ export const build = async (cliArgs) => {
|
|
|
695
835
|
copyPublicFrom: publicDir,
|
|
696
836
|
buildPath,
|
|
697
837
|
nodeEnv: 'production',
|
|
698
|
-
|
|
838
|
+
pagesEntryPath,
|
|
699
839
|
configSourcePath,
|
|
700
840
|
createClientRollupPlugins: createOssyClientRollupPlugins,
|
|
701
841
|
minifyBrowserStaticChunks,
|
|
702
|
-
reporter,
|
|
842
|
+
reporter: dashboard,
|
|
703
843
|
})
|
|
704
844
|
} finally {
|
|
705
|
-
|
|
845
|
+
dashboard.dispose()
|
|
706
846
|
}
|
|
707
847
|
}
|
|
708
848
|
|
|
709
|
-
|
|
849
|
+
if (useDashboard && dashboard) {
|
|
850
|
+
console.log(' \x1b[2m────────────────────────────────────────\x1b[0m')
|
|
851
|
+
}
|
|
710
852
|
console.log(' \x1b[32m✔\x1b[0m \x1b[1m@ossy/app\x1b[0m \x1b[2mbuild finished\x1b[0m\n')
|
|
711
853
|
};
|