@ossy/app 1.11.7 → 1.11.9
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/cli/build-terminal.js +16 -117
- package/cli/build.js +155 -67
- package/cli/index.js +1 -6
- package/cli/prerender-react.task.js +20 -164
- package/cli/render-page.task.js +18 -61
- package/cli/server.js +1 -35
- package/package.json +10 -10
- package/cli/dev.js +0 -342
package/cli/build-terminal.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import util from 'node:util'
|
|
2
|
-
|
|
3
1
|
const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
|
4
2
|
|
|
5
3
|
const dim = (s) => `\x1b[2m${s}\x1b[0m`
|
|
@@ -69,15 +67,10 @@ function useTty (stream) {
|
|
|
69
67
|
)
|
|
70
68
|
}
|
|
71
69
|
|
|
72
|
-
/**
|
|
73
|
-
* Merged overview + per-page progress, split layout: left = status table, right = log tail.
|
|
74
|
-
* TTY: single redraw block + optional console capture into the right column.
|
|
75
|
-
*/
|
|
76
70
|
export function createBuildDashboard ({
|
|
77
71
|
scope = '@ossy/app',
|
|
78
72
|
mode = 'full',
|
|
79
73
|
pageIds,
|
|
80
|
-
/** @type {Record<string, string>} route path per page id */
|
|
81
74
|
idToPath = {},
|
|
82
75
|
overview = { title: '@ossy/app', configRel: null, apiRoutes: [] },
|
|
83
76
|
stream = process.stdout,
|
|
@@ -86,11 +79,7 @@ export function createBuildDashboard ({
|
|
|
86
79
|
const maxId = Math.max(6, ...ids.map((id) => String(id).length))
|
|
87
80
|
const tty = useTty(stream)
|
|
88
81
|
const termW = Math.max(60, stream.columns || 100)
|
|
89
|
-
const LEFT_W = Math.min(56, Math.max(38, Math.floor(termW * 0.42)))
|
|
90
|
-
const GAP = 2
|
|
91
|
-
const RIGHT_W = Math.max(16, termW - LEFT_W - GAP)
|
|
92
82
|
|
|
93
|
-
/** @type {Map<string, { bundle: object, prerender: object }>} */
|
|
94
83
|
const rows = new Map()
|
|
95
84
|
for (const id of ids) {
|
|
96
85
|
rows.set(id, {
|
|
@@ -99,13 +88,9 @@ export function createBuildDashboard ({
|
|
|
99
88
|
})
|
|
100
89
|
}
|
|
101
90
|
|
|
102
|
-
const logBuffer = []
|
|
103
|
-
const MAX_LOG = 120
|
|
104
91
|
let frame = 0
|
|
105
92
|
let spinTimer = null
|
|
106
93
|
let blockLines = 0
|
|
107
|
-
let captureActive = false
|
|
108
|
-
let savedConsole = null
|
|
109
94
|
|
|
110
95
|
const anyRunning = () => {
|
|
111
96
|
for (const r of rows.values()) {
|
|
@@ -114,36 +99,23 @@ export function createBuildDashboard ({
|
|
|
114
99
|
return false
|
|
115
100
|
}
|
|
116
101
|
|
|
117
|
-
function
|
|
118
|
-
const s = String(line).trim() ? String(line) : ''
|
|
119
|
-
if (!s) return
|
|
120
|
-
if (!tty) {
|
|
121
|
-
stream.write(`${s}\n`)
|
|
122
|
-
return
|
|
123
|
-
}
|
|
124
|
-
logBuffer.push(dim(s))
|
|
125
|
-
if (logBuffer.length > MAX_LOG) logBuffer.splice(0, logBuffer.length - MAX_LOG)
|
|
126
|
-
redraw()
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function leftHeaderLines () {
|
|
102
|
+
function headerLines () {
|
|
130
103
|
const out = []
|
|
131
104
|
out.push(bold(overview.title || `${scope} build`))
|
|
132
105
|
if (overview.configRel) {
|
|
133
|
-
out.push(`${dim('config')} ${truncateVisible(overview.configRel,
|
|
106
|
+
out.push(`${dim('config')} ${truncateVisible(overview.configRel, termW - 10)}`)
|
|
134
107
|
}
|
|
135
108
|
const api = overview.apiRoutes || []
|
|
136
|
-
if (api.length
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
109
|
+
if (api.length > 0) {
|
|
110
|
+
if (api.length <= 2) {
|
|
111
|
+
for (const r of api) {
|
|
112
|
+
out.push(`${dim('api')} ${truncateVisible(`${r.id} ${r.path}`, termW - 6)}`)
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
out.push(`${dim('api')} ${api.length} routes`)
|
|
142
116
|
}
|
|
143
|
-
} else {
|
|
144
|
-
out.push(`${dim('api')} ${api.length} routes`)
|
|
145
117
|
}
|
|
146
|
-
out.push(dim('─'.repeat(Math.min(
|
|
118
|
+
out.push(dim('─'.repeat(Math.min(termW - 2, 44))))
|
|
147
119
|
return out
|
|
148
120
|
}
|
|
149
121
|
|
|
@@ -161,46 +133,20 @@ export function createBuildDashboard ({
|
|
|
161
133
|
return ` ${lead} ${dim(scope)} ${idCol} ${pathPad} ${padVisible(b, 20)} ${p}`
|
|
162
134
|
}
|
|
163
135
|
|
|
164
|
-
function
|
|
165
|
-
|
|
166
|
-
const body = ids.map((id) => lineForPageRow(id))
|
|
167
|
-
return [...head, ...body]
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function buildHeaderOnlyLines () {
|
|
171
|
-
return leftHeaderLines()
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/** Right column row i: logs bottom-aligned to the block height */
|
|
175
|
-
function rightColumnLine (i, totalRows) {
|
|
176
|
-
const start = logBuffer.length - totalRows + i
|
|
177
|
-
if (start < 0 || start >= logBuffer.length) return dim('·')
|
|
178
|
-
return truncateVisible(logBuffer[start], RIGHT_W)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function paintRow (leftLine, rightLine) {
|
|
182
|
-
const L = padVisible(truncateVisible(leftLine, LEFT_W), LEFT_W)
|
|
183
|
-
const R = truncateVisible(rightLine, RIGHT_W)
|
|
184
|
-
return `${L}${' '.repeat(GAP)}${R}\n`
|
|
136
|
+
function buildLines () {
|
|
137
|
+
return [...headerLines(), ...ids.map((id) => lineForPageRow(id))]
|
|
185
138
|
}
|
|
186
139
|
|
|
187
140
|
function redraw () {
|
|
188
141
|
if (!tty) return
|
|
189
|
-
const
|
|
190
|
-
const n = leftCol.length
|
|
191
|
-
const lines = []
|
|
192
|
-
for (let i = 0; i < n; i++) {
|
|
193
|
-
lines.push(paintRow(leftCol[i], rightColumnLine(i, n)))
|
|
194
|
-
}
|
|
142
|
+
const lines = buildLines()
|
|
195
143
|
if (blockLines === 0) {
|
|
196
|
-
for (const ln of lines) stream.write(ln)
|
|
144
|
+
for (const ln of lines) stream.write(ln + '\n')
|
|
197
145
|
blockLines = lines.length
|
|
198
146
|
return
|
|
199
147
|
}
|
|
200
148
|
stream.write(`\x1b[${blockLines}A`)
|
|
201
|
-
for (const ln of lines) {
|
|
202
|
-
stream.write(`\x1b[2K\r${ln}`)
|
|
203
|
-
}
|
|
149
|
+
for (const ln of lines) stream.write(`\x1b[2K\r${ln}\n`)
|
|
204
150
|
}
|
|
205
151
|
|
|
206
152
|
function ensureSpin () {
|
|
@@ -226,55 +172,13 @@ export function createBuildDashboard ({
|
|
|
226
172
|
if (err && !ok) stream.write(` ${red(String(err.message || err))}\n`)
|
|
227
173
|
}
|
|
228
174
|
|
|
229
|
-
function beginCapture () {
|
|
230
|
-
if (!tty || captureActive) return
|
|
231
|
-
captureActive = true
|
|
232
|
-
savedConsole = {
|
|
233
|
-
log: console.log,
|
|
234
|
-
warn: console.warn,
|
|
235
|
-
info: console.info,
|
|
236
|
-
error: console.error,
|
|
237
|
-
}
|
|
238
|
-
const fmt = (...a) => util.format(...a)
|
|
239
|
-
console.log = (...a) => {
|
|
240
|
-
pushLog(`[log] ${fmt(...a)}`)
|
|
241
|
-
}
|
|
242
|
-
console.warn = (...a) => {
|
|
243
|
-
pushLog(`[warn] ${fmt(...a)}`)
|
|
244
|
-
}
|
|
245
|
-
console.info = (...a) => {
|
|
246
|
-
pushLog(`[info] ${fmt(...a)}`)
|
|
247
|
-
}
|
|
248
|
-
console.error = (...a) => {
|
|
249
|
-
const msg = fmt(...a)
|
|
250
|
-
pushLog(`[error] ${msg}`)
|
|
251
|
-
savedConsole.error(`[@ossy/app] ${msg}`)
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function endCapture () {
|
|
256
|
-
if (!captureActive || !savedConsole) return
|
|
257
|
-
console.log = savedConsole.log
|
|
258
|
-
console.warn = savedConsole.warn
|
|
259
|
-
console.info = savedConsole.info
|
|
260
|
-
console.error = savedConsole.error
|
|
261
|
-
savedConsole = null
|
|
262
|
-
captureActive = false
|
|
263
|
-
}
|
|
264
|
-
|
|
265
175
|
return {
|
|
266
|
-
pushLog,
|
|
267
|
-
|
|
268
|
-
/** Start optional console hijack + first paint (TTY). Plain header only when not a TTY. */
|
|
269
176
|
start () {
|
|
270
177
|
if (!tty) {
|
|
271
|
-
for (const ln of
|
|
272
|
-
stream.write(`${ln}\n`)
|
|
273
|
-
}
|
|
178
|
+
for (const ln of headerLines()) stream.write(`${ln}\n`)
|
|
274
179
|
stream.write('\n')
|
|
275
180
|
return
|
|
276
181
|
}
|
|
277
|
-
beginCapture()
|
|
278
182
|
redraw()
|
|
279
183
|
},
|
|
280
184
|
|
|
@@ -326,13 +230,8 @@ export function createBuildDashboard ({
|
|
|
326
230
|
redraw()
|
|
327
231
|
},
|
|
328
232
|
|
|
329
|
-
printSectionTitle () {
|
|
330
|
-
/* merged into overview.title — no-op for API compat */
|
|
331
|
-
},
|
|
332
|
-
|
|
333
233
|
dispose () {
|
|
334
234
|
stopSpin()
|
|
335
|
-
endCapture()
|
|
336
235
|
if (tty && blockLines > 0) {
|
|
337
236
|
redraw()
|
|
338
237
|
stream.write('\n')
|
package/cli/build.js
CHANGED
|
@@ -87,6 +87,11 @@ export const OSSY_PAGE_SERVER_EXTERNAL = [
|
|
|
87
87
|
'react/jsx-runtime',
|
|
88
88
|
]
|
|
89
89
|
|
|
90
|
+
/** Output directory (relative to buildPath) for per-page SSR bundles. */
|
|
91
|
+
export const OSSY_SSR_DIRNAME = 'ssr'
|
|
92
|
+
/** Temp stub entries for SSR bundles (inside .ossy/). */
|
|
93
|
+
const OSSY_SSR_ENTRIES_DIRNAME = 'ssr-entries'
|
|
94
|
+
|
|
90
95
|
/** Per-page client entries: `hydrate-<pageId>.jsx` under `.ossy/` */
|
|
91
96
|
const HYDRATE_STUB_PREFIX = 'hydrate-'
|
|
92
97
|
const HYDRATE_STUB_SUFFIX = '.jsx'
|
|
@@ -186,7 +191,7 @@ export function createOssyAppBundlePlugins ({ nodeEnv }) {
|
|
|
186
191
|
nodeExternals({
|
|
187
192
|
deps: false,
|
|
188
193
|
devDeps: true,
|
|
189
|
-
peerDeps:
|
|
194
|
+
peerDeps: false,
|
|
190
195
|
packagePath: path.join(process.cwd(), 'package.json'),
|
|
191
196
|
}),
|
|
192
197
|
resolveCommonJsDependencies(),
|
|
@@ -200,7 +205,8 @@ export function createOssyAppBundlePlugins ({ nodeEnv }) {
|
|
|
200
205
|
}
|
|
201
206
|
|
|
202
207
|
/**
|
|
203
|
-
* Rollup plugins for browser hydrate bundles.
|
|
208
|
+
* Rollup plugins for browser hydrate bundles. React and all deps are bundled in so the output
|
|
209
|
+
* is self-contained and requires no import maps or CDN.
|
|
204
210
|
*/
|
|
205
211
|
export function createOssyClientRollupPlugins ({ nodeEnv, copyPublicFrom, buildPath }) {
|
|
206
212
|
const plugins = [
|
|
@@ -212,7 +218,7 @@ export function createOssyClientRollupPlugins ({ nodeEnv, copyPublicFrom, buildP
|
|
|
212
218
|
nodeExternals({
|
|
213
219
|
deps: false,
|
|
214
220
|
devDeps: true,
|
|
215
|
-
peerDeps:
|
|
221
|
+
peerDeps: false,
|
|
216
222
|
packagePath: path.join(process.cwd(), 'package.json'),
|
|
217
223
|
}),
|
|
218
224
|
resolveCommonJsDependencies(),
|
|
@@ -233,6 +239,106 @@ export function createOssyClientRollupPlugins ({ nodeEnv, copyPublicFrom, buildP
|
|
|
233
239
|
return plugins
|
|
234
240
|
}
|
|
235
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Rollup plugins for per-page SSR bundles: server-side resolution, React and all deps bundled in
|
|
244
|
+
* so the output is self-contained (no node_modules needed at runtime).
|
|
245
|
+
*/
|
|
246
|
+
export function createOssySsrBundlePlugins ({ nodeEnv }) {
|
|
247
|
+
return [
|
|
248
|
+
replace({
|
|
249
|
+
preventAssignment: true,
|
|
250
|
+
'process.env.NODE_ENV': JSON.stringify(nodeEnv),
|
|
251
|
+
}),
|
|
252
|
+
json(),
|
|
253
|
+
nodeExternals({
|
|
254
|
+
deps: false,
|
|
255
|
+
devDeps: true,
|
|
256
|
+
peerDeps: false,
|
|
257
|
+
packagePath: path.join(process.cwd(), 'package.json'),
|
|
258
|
+
}),
|
|
259
|
+
resolveCommonJsDependencies(),
|
|
260
|
+
resolveDependencies({ preferBuiltins: true }),
|
|
261
|
+
babel({
|
|
262
|
+
babelHelpers: 'bundled',
|
|
263
|
+
extensions: ['.jsx', '.tsx'],
|
|
264
|
+
presets: ['@babel/preset-react'],
|
|
265
|
+
}),
|
|
266
|
+
]
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Generates the SSR entry stub for a page: exports renderPage(props, options) and metadata. */
|
|
270
|
+
export function generatePageSsrModule ({ pageAbsPath, stubAbsPath }) {
|
|
271
|
+
const rel = relToGeneratedImport(stubAbsPath, pageAbsPath)
|
|
272
|
+
return [
|
|
273
|
+
'// Generated by @ossy/app — do not edit',
|
|
274
|
+
'',
|
|
275
|
+
"import { createElement } from 'react'",
|
|
276
|
+
"import { renderToPipeableStream } from 'react-dom/server'",
|
|
277
|
+
"import { Writable } from 'node:stream'",
|
|
278
|
+
`import * as _page from './${rel}'`,
|
|
279
|
+
'',
|
|
280
|
+
'export const metadata = _page.metadata',
|
|
281
|
+
'',
|
|
282
|
+
'export function renderPage (props, options = {}) {',
|
|
283
|
+
' return new Promise((resolve, reject) => {',
|
|
284
|
+
" let html = ''",
|
|
285
|
+
' const writable = new Writable({',
|
|
286
|
+
' write (chunk, _enc, cb) { html += chunk.toString(); cb() },',
|
|
287
|
+
' })',
|
|
288
|
+
' const { pipe } = renderToPipeableStream(createElement(_page.default, props), {',
|
|
289
|
+
' ...options,',
|
|
290
|
+
' onAllReady () { pipe(writable) },',
|
|
291
|
+
' onError (err) { reject(err) },',
|
|
292
|
+
' })',
|
|
293
|
+
" writable.on('finish', () => resolve(html))",
|
|
294
|
+
' })',
|
|
295
|
+
'}',
|
|
296
|
+
'',
|
|
297
|
+
].join('\n')
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** Writes `ssr-entries/<id>.mjs` stubs for each page into ossyDir. */
|
|
301
|
+
export function writePageSsrStubs (pageFiles, srcDir, ossyDir) {
|
|
302
|
+
const entriesDir = path.join(ossyDir, OSSY_SSR_ENTRIES_DIRNAME)
|
|
303
|
+
fs.rmSync(entriesDir, { recursive: true, force: true })
|
|
304
|
+
if (pageFiles.length === 0) return
|
|
305
|
+
fs.mkdirSync(entriesDir, { recursive: true })
|
|
306
|
+
for (const f of pageFiles) {
|
|
307
|
+
const pageId = clientHydrateIdForPage(f, srcDir)
|
|
308
|
+
const stubPath = path.join(entriesDir, `${pageId}.mjs`)
|
|
309
|
+
fs.writeFileSync(stubPath, generatePageSsrModule({ pageAbsPath: f, stubAbsPath: stubPath }))
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** Compiles per-page SSR bundles to `build/ssr/<id>.mjs`. Each is self-contained with React bundled in. */
|
|
314
|
+
export async function compilePageSsrModules ({ pageFiles, srcDir, ossyDir, buildPath, nodeEnv, onWarn }) {
|
|
315
|
+
const ssrDir = path.join(buildPath, OSSY_SSR_DIRNAME)
|
|
316
|
+
const entriesDir = path.join(ossyDir, OSSY_SSR_ENTRIES_DIRNAME)
|
|
317
|
+
fs.rmSync(ssrDir, { recursive: true, force: true })
|
|
318
|
+
if (pageFiles.length === 0) return []
|
|
319
|
+
fs.mkdirSync(ssrDir, { recursive: true })
|
|
320
|
+
const plugins = createOssySsrBundlePlugins({ nodeEnv })
|
|
321
|
+
const results = await Promise.all(
|
|
322
|
+
pageFiles.map(async (f) => {
|
|
323
|
+
const pageId = clientHydrateIdForPage(f, srcDir)
|
|
324
|
+
const stubPath = path.join(entriesDir, `${pageId}.mjs`)
|
|
325
|
+
const outFile = path.join(ssrDir, `${pageId}.mjs`)
|
|
326
|
+
const bundle = await rollup({
|
|
327
|
+
input: stubPath,
|
|
328
|
+
plugins,
|
|
329
|
+
onwarn (warning, defaultHandler) {
|
|
330
|
+
if (onWarn) { onWarn(warning); return }
|
|
331
|
+
defaultHandler(warning)
|
|
332
|
+
},
|
|
333
|
+
})
|
|
334
|
+
await bundle.write({ file: outFile, format: 'esm', inlineDynamicImports: true })
|
|
335
|
+
await bundle.close()
|
|
336
|
+
return { id: pageId }
|
|
337
|
+
})
|
|
338
|
+
)
|
|
339
|
+
return results
|
|
340
|
+
}
|
|
341
|
+
|
|
236
342
|
/** Bundles a single Node ESM file (inline dynamic imports) for SSR / API / tasks. */
|
|
237
343
|
export async function bundleOssyNodeEntry ({ inputPath, outputFile, nodeEnv, onWarn, external }) {
|
|
238
344
|
const bundle = await rollup({
|
|
@@ -455,39 +561,40 @@ export async function compileTaskServerModules ({ taskFiles, ossyDir, nodeEnv, o
|
|
|
455
561
|
* Merges compiled `metadata` + `module` into `pages.generated.json` (same order as `pageBundleList`).
|
|
456
562
|
* Writes `module` on each route for Node SSR (`import()` of `page-modules/*.mjs`); hydrate uses a separate Rollup client entry.
|
|
457
563
|
*/
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}) {
|
|
463
|
-
if (!
|
|
564
|
+
/**
|
|
565
|
+
* Enriches `pages.generated.json` with `module` paths pointing to SSR bundles and merges
|
|
566
|
+
* any `metadata` exported by each page. Runs after SSR bundles are compiled.
|
|
567
|
+
*/
|
|
568
|
+
export async function enrichPagesGeneratedManifest ({ ossyDir, pagesGeneratedPath }) {
|
|
569
|
+
if (!fs.existsSync(pagesGeneratedPath)) return
|
|
464
570
|
const raw = JSON.parse(fs.readFileSync(pagesGeneratedPath, 'utf8'))
|
|
465
571
|
const basePages = raw?.pages
|
|
466
|
-
if (!Array.isArray(basePages) || basePages.length
|
|
467
|
-
|
|
468
|
-
'[@ossy/app] pages.generated.json page count must match compiled page modules (re-run build).'
|
|
469
|
-
)
|
|
470
|
-
}
|
|
572
|
+
if (!Array.isArray(basePages) || basePages.length === 0) return
|
|
573
|
+
|
|
471
574
|
const pages = []
|
|
472
|
-
for (
|
|
473
|
-
const
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
575
|
+
for (const basePage of basePages) {
|
|
576
|
+
const moduleRelPath = `../ssr/${basePage.id}.mjs`
|
|
577
|
+
const bundleAbs = path.join(ossyDir, moduleRelPath)
|
|
578
|
+
let meta = {}
|
|
579
|
+
if (fs.existsSync(bundleAbs)) {
|
|
580
|
+
try {
|
|
581
|
+
const mod = await import(pathToFileURL(bundleAbs).href)
|
|
582
|
+
meta = mod?.metadata && typeof mod.metadata === 'object' ? mod.metadata : {}
|
|
583
|
+
} catch {
|
|
584
|
+
// metadata unreadable — continue with defaults
|
|
585
|
+
}
|
|
586
|
+
}
|
|
477
587
|
const merged = {
|
|
478
|
-
|
|
588
|
+
id: basePage.id,
|
|
589
|
+
path: basePage.path,
|
|
479
590
|
...meta,
|
|
480
|
-
sourceFile:
|
|
481
|
-
module:
|
|
591
|
+
sourceFile: basePage.sourceFile,
|
|
592
|
+
module: moduleRelPath,
|
|
482
593
|
}
|
|
483
594
|
try {
|
|
484
595
|
JSON.stringify(merged)
|
|
485
596
|
} catch {
|
|
486
|
-
pages.push({
|
|
487
|
-
...derived,
|
|
488
|
-
sourceFile: basePages[i].sourceFile,
|
|
489
|
-
module: pageBundleList[i].module,
|
|
490
|
-
})
|
|
597
|
+
pages.push({ id: basePage.id, path: basePage.path, sourceFile: basePage.sourceFile, module: moduleRelPath })
|
|
491
598
|
continue
|
|
492
599
|
}
|
|
493
600
|
pages.push(merged)
|
|
@@ -496,26 +603,23 @@ export async function enrichPagesGeneratedManifest ({
|
|
|
496
603
|
}
|
|
497
604
|
|
|
498
605
|
/**
|
|
499
|
-
*
|
|
606
|
+
* Compiles server-side artifacts: per-page SSR bundles, API modules, and task modules.
|
|
500
607
|
*/
|
|
501
608
|
export async function compileOssyNodeArtifacts ({
|
|
502
609
|
pageFiles,
|
|
503
610
|
srcDir,
|
|
504
611
|
ossyDir,
|
|
612
|
+
buildPath,
|
|
505
613
|
apiFiles,
|
|
506
614
|
taskFiles,
|
|
507
615
|
nodeEnv,
|
|
508
616
|
onWarn,
|
|
509
617
|
}) {
|
|
510
|
-
const [
|
|
511
|
-
|
|
618
|
+
const [, apiModuleList, taskModuleList] = await Promise.all([
|
|
619
|
+
compilePageSsrModules({ pageFiles, srcDir, ossyDir, buildPath, nodeEnv, onWarn }),
|
|
512
620
|
compileApiServerModules({ apiFiles, ossyDir, nodeEnv, onWarn }),
|
|
513
621
|
compileTaskServerModules({ taskFiles, ossyDir, nodeEnv, onWarn }),
|
|
514
622
|
])
|
|
515
|
-
writeOssyJson(path.join(ossyDir, OSSY_PAGES_BUNDLE_BASENAME), {
|
|
516
|
-
version: 1,
|
|
517
|
-
pages: pageBundleList,
|
|
518
|
-
})
|
|
519
623
|
writeOssyJson(path.join(ossyDir, OSSY_API_BUNDLE_BASENAME), {
|
|
520
624
|
version: 1,
|
|
521
625
|
modules: apiModuleList,
|
|
@@ -527,7 +631,6 @@ export async function compileOssyNodeArtifacts ({
|
|
|
527
631
|
await enrichPagesGeneratedManifest({
|
|
528
632
|
ossyDir,
|
|
529
633
|
pagesGeneratedPath: path.join(ossyDir, OSSY_GEN_PAGES_BASENAME),
|
|
530
|
-
pageBundleList,
|
|
531
634
|
})
|
|
532
635
|
}
|
|
533
636
|
|
|
@@ -598,8 +701,9 @@ export function writePageServerRollupEntry ({ pageAbsPath, stubPath }) {
|
|
|
598
701
|
}
|
|
599
702
|
|
|
600
703
|
/**
|
|
601
|
-
* One
|
|
602
|
-
*
|
|
704
|
+
* One bundle per page: exports the component (for server SSR import) and auto-hydrates in the browser.
|
|
705
|
+
* React is kept as a bare external import — resolved from node_modules on the server and via
|
|
706
|
+
* import map on the client.
|
|
603
707
|
*/
|
|
604
708
|
export function generatePageHydrateModule ({ pageAbsPath, stubAbsPath, srcDir }) {
|
|
605
709
|
const rel = relToGeneratedImport(stubAbsPath, pageAbsPath)
|
|
@@ -607,17 +711,17 @@ export function generatePageHydrateModule ({ pageAbsPath, stubAbsPath, srcDir })
|
|
|
607
711
|
'// Generated by @ossy/app — do not edit',
|
|
608
712
|
'',
|
|
609
713
|
"import React, { createElement } from 'react'",
|
|
610
|
-
"import 'react-dom'",
|
|
611
714
|
"import { hydrateRoot } from 'react-dom/client'",
|
|
612
715
|
`import * as _page from './${rel}'`,
|
|
613
716
|
'',
|
|
614
|
-
'
|
|
615
|
-
'const
|
|
616
|
-
'
|
|
617
|
-
|
|
717
|
+
'export default _page.default',
|
|
718
|
+
'export const metadata = _page.metadata',
|
|
719
|
+
'',
|
|
720
|
+
"if (typeof window !== 'undefined') {",
|
|
721
|
+
" const Page = _page.default",
|
|
722
|
+
" const config = window.__INITIAL_APP_CONFIG__ || {}",
|
|
723
|
+
" hydrateRoot(document, createElement(Page, config))",
|
|
618
724
|
'}',
|
|
619
|
-
'const rootTree = createElement(Page, initialConfig)',
|
|
620
|
-
'hydrateRoot(document, rootTree)',
|
|
621
725
|
'',
|
|
622
726
|
].join('\n')
|
|
623
727
|
}
|
|
@@ -816,6 +920,7 @@ export const build = async (cliArgs) => {
|
|
|
816
920
|
pagesGeneratedPath,
|
|
817
921
|
})
|
|
818
922
|
writePageHydrateStubs(pageFiles, srcDir, ossyDir)
|
|
923
|
+
writePageSsrStubs(pageFiles, srcDir, ossyDir)
|
|
819
924
|
const clientHydrateInput = buildClientHydrateInput(pageFiles, srcDir, ossyDir)
|
|
820
925
|
|
|
821
926
|
const { apiOverviewFiles } = resolveApiSource({
|
|
@@ -833,8 +938,6 @@ export const build = async (cliArgs) => {
|
|
|
833
938
|
? configPath
|
|
834
939
|
: path.resolve(scriptDir, 'default-config.js')
|
|
835
940
|
|
|
836
|
-
const pagesEntryPath = path.join(ossyDir, OSSY_PAGES_RUNTIME_BASENAME)
|
|
837
|
-
|
|
838
941
|
const useDashboard = Object.keys(clientHydrateInput).length > 0
|
|
839
942
|
const overviewSnap = getBuildOverviewSnapshot({
|
|
840
943
|
pagesSourcePath: pagesGeneratedPath,
|
|
@@ -865,11 +968,6 @@ export const build = async (cliArgs) => {
|
|
|
865
968
|
},
|
|
866
969
|
})
|
|
867
970
|
dashboard.start()
|
|
868
|
-
if (resourceTemplatesResult.wrote && resourceTemplatesResult.path) {
|
|
869
|
-
dashboard.pushLog(
|
|
870
|
-
`[resource-templates] merged ${resourceTemplatesResult.count} → ${path.relative(process.cwd(), resourceTemplatesResult.path)}`
|
|
871
|
-
)
|
|
872
|
-
}
|
|
873
971
|
} else {
|
|
874
972
|
console.log('\n \x1b[1m@ossy/app\x1b[0m \x1b[2mbuild\x1b[0m')
|
|
875
973
|
printBuildOverview({
|
|
@@ -878,28 +976,23 @@ export const build = async (cliArgs) => {
|
|
|
878
976
|
configPath,
|
|
879
977
|
pageFiles,
|
|
880
978
|
})
|
|
881
|
-
if (resourceTemplatesResult.wrote && resourceTemplatesResult.path) {
|
|
882
|
-
console.log(
|
|
883
|
-
`[@ossy/app][resource-templates] merged ${resourceTemplatesResult.count} template(s) → ${path.relative(process.cwd(), resourceTemplatesResult.path)}`
|
|
884
|
-
)
|
|
885
|
-
}
|
|
886
979
|
}
|
|
887
980
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
981
|
+
if (resourceTemplatesResult.wrote && resourceTemplatesResult.path) {
|
|
982
|
+
console.log(
|
|
983
|
+
`[@ossy/app][resource-templates] merged ${resourceTemplatesResult.count} template(s) → ${path.relative(process.cwd(), resourceTemplatesResult.path)}`
|
|
984
|
+
)
|
|
985
|
+
}
|
|
893
986
|
|
|
894
987
|
const { taskOverviewFiles } = resolveTaskSource({ srcDir, buildPath })
|
|
895
988
|
await compileOssyNodeArtifacts({
|
|
896
989
|
pageFiles,
|
|
897
990
|
srcDir,
|
|
898
991
|
ossyDir,
|
|
992
|
+
buildPath,
|
|
899
993
|
apiFiles: apiOverviewFiles,
|
|
900
994
|
taskFiles: taskOverviewFiles,
|
|
901
995
|
nodeEnv: 'production',
|
|
902
|
-
onWarn: warnSink,
|
|
903
996
|
})
|
|
904
997
|
|
|
905
998
|
writeAppRuntimeShims({
|
|
@@ -918,8 +1011,6 @@ export const build = async (cliArgs) => {
|
|
|
918
1011
|
copyPublicFrom: publicDir,
|
|
919
1012
|
buildPath,
|
|
920
1013
|
nodeEnv: 'production',
|
|
921
|
-
pagesEntryPath,
|
|
922
|
-
configSourcePath,
|
|
923
1014
|
createClientRollupPlugins: createOssyClientRollupPlugins,
|
|
924
1015
|
minifyBrowserStaticChunks,
|
|
925
1016
|
reporter: dashboard,
|
|
@@ -929,8 +1020,5 @@ export const build = async (cliArgs) => {
|
|
|
929
1020
|
}
|
|
930
1021
|
}
|
|
931
1022
|
|
|
932
|
-
if (useDashboard && dashboard) {
|
|
933
|
-
console.log(' \x1b[2m────────────────────────────────────────\x1b[0m')
|
|
934
|
-
}
|
|
935
1023
|
console.log(' \x1b[32m✔\x1b[0m \x1b[1m@ossy/app\x1b[0m \x1b[2mbuild finished\x1b[0m\n')
|
|
936
1024
|
};
|
package/cli/index.js
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { build } from './build.js'
|
|
3
|
-
import { dev } from './dev.js'
|
|
4
3
|
|
|
5
4
|
const [,, command, ...restArgs] = process.argv
|
|
6
5
|
|
|
7
6
|
if (!command) {
|
|
8
7
|
console.error(
|
|
9
|
-
'[@ossy/app] No command provided. Usage: app
|
|
8
|
+
'[@ossy/app] No command provided. Usage: app build'
|
|
10
9
|
)
|
|
11
10
|
process.exit(1)
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
const run = async () => {
|
|
15
|
-
if (command === 'dev') {
|
|
16
|
-
await dev(restArgs)
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
14
|
if (command === 'build') {
|
|
20
15
|
await build(restArgs)
|
|
21
16
|
return
|