@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/dev.js
CHANGED
|
@@ -3,13 +3,15 @@ import url from 'url';
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import {
|
|
5
5
|
printBuildOverview,
|
|
6
|
+
getBuildOverviewSnapshot,
|
|
7
|
+
filePathToRoute,
|
|
6
8
|
discoverFilesByPattern,
|
|
7
9
|
PAGE_FILE_PATTERN,
|
|
8
|
-
|
|
10
|
+
writePagesManifest,
|
|
9
11
|
resolveApiSource,
|
|
10
12
|
resolveTaskSource,
|
|
11
13
|
resetOssyBuildDir,
|
|
12
|
-
|
|
14
|
+
compileOssyNodeArtifacts,
|
|
13
15
|
copyOssyAppRuntime,
|
|
14
16
|
writeAppRuntimeShims,
|
|
15
17
|
createOssyAppBundlePlugins,
|
|
@@ -19,26 +21,19 @@ import {
|
|
|
19
21
|
clientHydrateIdForPage,
|
|
20
22
|
ossyGeneratedDir,
|
|
21
23
|
OSSY_GEN_PAGES_BASENAME,
|
|
22
|
-
|
|
23
|
-
OSSY_GEN_TASKS_BASENAME,
|
|
24
|
-
OSSY_PAGES_PRERENDER_BUNDLE,
|
|
25
|
-
OSSY_API_SERVER_BUNDLE,
|
|
26
|
-
OSSY_TASKS_SERVER_BUNDLE,
|
|
24
|
+
OSSY_PAGES_RUNTIME_BASENAME,
|
|
27
25
|
writeResourceTemplatesBarrelIfPresent,
|
|
28
26
|
resourceTemplatesDir,
|
|
29
27
|
OSSY_RESOURCE_TEMPLATES_OUT,
|
|
30
28
|
} from './build.js';
|
|
31
29
|
import prerenderReactTask from './prerender-react.task.js'
|
|
32
|
-
import {
|
|
30
|
+
import { createBuildDashboard } from './build-terminal.js'
|
|
33
31
|
import { watch } from 'rollup';
|
|
34
32
|
import arg from 'arg'
|
|
35
33
|
import { spawn } from 'node:child_process'
|
|
36
34
|
// import inject from '@rollup/plugin-inject'
|
|
37
35
|
|
|
38
36
|
export const dev = async (cliArgs) => {
|
|
39
|
-
console.log('\n \x1b[1m@ossy/app\x1b[0m \x1b[2mdev\x1b[0m')
|
|
40
|
-
console.log(' \x1b[2m────────────────────────────────────────\x1b[0m')
|
|
41
|
-
|
|
42
37
|
const options = arg({
|
|
43
38
|
'--config': String,
|
|
44
39
|
'-c': '--config',
|
|
@@ -49,40 +44,50 @@ export const dev = async (cliArgs) => {
|
|
|
49
44
|
const buildPath = path.resolve('build')
|
|
50
45
|
const srcDir = path.resolve('src')
|
|
51
46
|
const configPath = path.resolve(options['--config'] || 'src/config.js');
|
|
52
|
-
|
|
47
|
+
let currentPageFiles = discoverFilesByPattern(srcDir, PAGE_FILE_PATTERN)
|
|
53
48
|
|
|
54
49
|
resetOssyBuildDir(buildPath)
|
|
55
50
|
|
|
56
|
-
writeResourceTemplatesBarrelIfPresent({
|
|
51
|
+
const resourceTemplatesResult = writeResourceTemplatesBarrelIfPresent({
|
|
52
|
+
cwd: process.cwd(),
|
|
53
|
+
log: false,
|
|
54
|
+
})
|
|
55
|
+
let resourceTemplatesDevLogged = false
|
|
57
56
|
|
|
58
|
-
const pagesGeneratedPath = path.join(ossyGeneratedDir(buildPath), OSSY_GEN_PAGES_BASENAME)
|
|
59
|
-
fs.writeFileSync(
|
|
60
|
-
pagesGeneratedPath,
|
|
61
|
-
generatePagesModule(pageFiles, srcDir, pagesGeneratedPath)
|
|
62
|
-
)
|
|
63
57
|
const ossyDir = ossyGeneratedDir(buildPath)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const {
|
|
68
|
-
apiSourcePath: resolvedApi,
|
|
69
|
-
apiOverviewFiles,
|
|
70
|
-
} = resolveApiSource({
|
|
58
|
+
const pagesGeneratedPath = path.join(ossyDir, OSSY_GEN_PAGES_BASENAME)
|
|
59
|
+
writePagesManifest({
|
|
60
|
+
pageFiles: currentPageFiles,
|
|
71
61
|
srcDir,
|
|
72
|
-
|
|
62
|
+
pagesGeneratedPath,
|
|
73
63
|
})
|
|
74
|
-
|
|
75
|
-
|
|
64
|
+
writePageHydrateStubs(currentPageFiles, srcDir, ossyDir)
|
|
65
|
+
const clientHydrateInput = buildClientHydrateInput(currentPageFiles, srcDir, ossyDir)
|
|
66
|
+
|
|
67
|
+
let apiOverviewFiles = []
|
|
68
|
+
let taskOverviewFiles = []
|
|
69
|
+
const refreshApiTaskManifests = () => {
|
|
70
|
+
apiOverviewFiles = resolveApiSource({ srcDir, buildPath }).apiOverviewFiles
|
|
71
|
+
taskOverviewFiles = resolveTaskSource({ srcDir, buildPath }).taskOverviewFiles
|
|
72
|
+
}
|
|
73
|
+
refreshApiTaskManifests()
|
|
76
74
|
let middlewareSourcePath = path.resolve(options['--middleware-source'] || 'src/middleware.js');
|
|
77
75
|
const publicDir = path.resolve('public')
|
|
78
76
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
77
|
+
if (currentPageFiles.length === 0) {
|
|
78
|
+
console.log('\n \x1b[1m@ossy/app\x1b[0m \x1b[2mdev\x1b[0m')
|
|
79
|
+
printBuildOverview({
|
|
80
|
+
pagesSourcePath: pagesGeneratedPath,
|
|
81
|
+
apiOverviewFiles,
|
|
82
|
+
configPath,
|
|
83
|
+
pageFiles: currentPageFiles,
|
|
84
|
+
})
|
|
85
|
+
if (resourceTemplatesResult.wrote && resourceTemplatesResult.path) {
|
|
86
|
+
console.log(
|
|
87
|
+
`[@ossy/app][resource-templates] merged ${resourceTemplatesResult.count} template(s) → ${path.relative(process.cwd(), resourceTemplatesResult.path)}`
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
86
91
|
|
|
87
92
|
if (!fs.existsSync(middlewareSourcePath)) {
|
|
88
93
|
middlewareSourcePath = path.resolve(scriptDir, 'middleware.js')
|
|
@@ -92,29 +97,21 @@ export const dev = async (cliArgs) => {
|
|
|
92
97
|
? configPath
|
|
93
98
|
: path.resolve(scriptDir, 'default-config.js')
|
|
94
99
|
|
|
95
|
-
const
|
|
96
|
-
const apiBundlePath = path.join(ossyDir, OSSY_API_SERVER_BUNDLE)
|
|
97
|
-
const tasksBundlePath = path.join(ossyDir, OSSY_TASKS_SERVER_BUNDLE)
|
|
98
|
-
const tasksGeneratedPath = path.join(ossyDir, OSSY_GEN_TASKS_BASENAME)
|
|
100
|
+
const pagesEntryPath = path.join(ossyDir, OSSY_PAGES_RUNTIME_BASENAME)
|
|
99
101
|
|
|
100
102
|
const runNodeBundles = async () => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
outputFile: apiBundlePath,
|
|
109
|
-
nodeEnv: 'development',
|
|
110
|
-
})
|
|
111
|
-
await bundleOssyNodeEntry({
|
|
112
|
-
inputPath: tasksGeneratedPath,
|
|
113
|
-
outputFile: tasksBundlePath,
|
|
103
|
+
refreshApiTaskManifests()
|
|
104
|
+
await compileOssyNodeArtifacts({
|
|
105
|
+
pageFiles: currentPageFiles,
|
|
106
|
+
srcDir,
|
|
107
|
+
ossyDir,
|
|
108
|
+
apiFiles: apiOverviewFiles,
|
|
109
|
+
taskFiles: taskOverviewFiles,
|
|
114
110
|
nodeEnv: 'development',
|
|
115
111
|
})
|
|
116
112
|
writeAppRuntimeShims({
|
|
117
113
|
middlewareSourcePath,
|
|
114
|
+
configSourcePath,
|
|
118
115
|
ossyDir,
|
|
119
116
|
})
|
|
120
117
|
copyOssyAppRuntime({ scriptDir, buildPath })
|
|
@@ -183,38 +180,7 @@ export const dev = async (cliArgs) => {
|
|
|
183
180
|
}
|
|
184
181
|
|
|
185
182
|
const nodeWatchOpts = { watch: { clearScreen: false } }
|
|
186
|
-
const watchConfigs = [
|
|
187
|
-
{
|
|
188
|
-
input: pagesGeneratedPath,
|
|
189
|
-
output: {
|
|
190
|
-
file: pagesBundlePath,
|
|
191
|
-
format: 'esm',
|
|
192
|
-
inlineDynamicImports: true,
|
|
193
|
-
},
|
|
194
|
-
plugins: createOssyAppBundlePlugins({ nodeEnv: 'development' }),
|
|
195
|
-
...nodeWatchOpts,
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
input: apiSourcePath,
|
|
199
|
-
output: {
|
|
200
|
-
file: apiBundlePath,
|
|
201
|
-
format: 'esm',
|
|
202
|
-
inlineDynamicImports: true,
|
|
203
|
-
},
|
|
204
|
-
plugins: createOssyAppBundlePlugins({ nodeEnv: 'development' }),
|
|
205
|
-
...nodeWatchOpts,
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
input: tasksGeneratedPath,
|
|
209
|
-
output: {
|
|
210
|
-
file: tasksBundlePath,
|
|
211
|
-
format: 'esm',
|
|
212
|
-
inlineDynamicImports: true,
|
|
213
|
-
},
|
|
214
|
-
plugins: createOssyAppBundlePlugins({ nodeEnv: 'development' }),
|
|
215
|
-
...nodeWatchOpts,
|
|
216
|
-
},
|
|
217
|
-
]
|
|
183
|
+
const watchConfigs = []
|
|
218
184
|
if (Object.keys(clientHydrateInput).length > 0) {
|
|
219
185
|
watchConfigs.push({
|
|
220
186
|
input: clientHydrateInput,
|
|
@@ -222,6 +188,30 @@ export const dev = async (cliArgs) => {
|
|
|
222
188
|
plugins: clientPlugins,
|
|
223
189
|
watch: { clearScreen: false },
|
|
224
190
|
})
|
|
191
|
+
} else {
|
|
192
|
+
watchConfigs.push({
|
|
193
|
+
input: '\0ossy-dev-noop',
|
|
194
|
+
output: {
|
|
195
|
+
file: path.join(ossyDir, '.dev-noop-out.mjs'),
|
|
196
|
+
format: 'esm',
|
|
197
|
+
inlineDynamicImports: true,
|
|
198
|
+
},
|
|
199
|
+
plugins: [
|
|
200
|
+
{
|
|
201
|
+
name: 'ossy-dev-noop',
|
|
202
|
+
resolveId (id) {
|
|
203
|
+
if (id === '\0ossy-dev-noop') return id
|
|
204
|
+
return null
|
|
205
|
+
},
|
|
206
|
+
load (id) {
|
|
207
|
+
if (id === '\0ossy-dev-noop') return 'export default 0\n'
|
|
208
|
+
return null
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
...createOssyAppBundlePlugins({ nodeEnv: 'development' }),
|
|
212
|
+
],
|
|
213
|
+
...nodeWatchOpts,
|
|
214
|
+
})
|
|
225
215
|
}
|
|
226
216
|
const watcher = watch(watchConfigs)
|
|
227
217
|
|
|
@@ -240,21 +230,54 @@ export const dev = async (cliArgs) => {
|
|
|
240
230
|
if (event.code === 'END') {
|
|
241
231
|
writeAppRuntimeShims({
|
|
242
232
|
middlewareSourcePath,
|
|
233
|
+
configSourcePath,
|
|
243
234
|
ossyDir,
|
|
244
235
|
})
|
|
245
236
|
copyOssyAppRuntime({ scriptDir, buildPath })
|
|
246
|
-
if (
|
|
237
|
+
if (currentPageFiles.length > 0) {
|
|
247
238
|
const pageIds = [
|
|
248
|
-
...new Set(
|
|
239
|
+
...new Set(currentPageFiles.map((f) => clientHydrateIdForPage(f, srcDir))),
|
|
249
240
|
].sort()
|
|
241
|
+
const overviewSnap = getBuildOverviewSnapshot({
|
|
242
|
+
pagesSourcePath: pagesGeneratedPath,
|
|
243
|
+
apiOverviewFiles,
|
|
244
|
+
configPath,
|
|
245
|
+
pageFiles: currentPageFiles,
|
|
246
|
+
})
|
|
247
|
+
const idToPath = Object.fromEntries(
|
|
248
|
+
currentPageFiles.map((f) => [
|
|
249
|
+
clientHydrateIdForPage(f, srcDir),
|
|
250
|
+
filePathToRoute(f, srcDir).path,
|
|
251
|
+
])
|
|
252
|
+
)
|
|
250
253
|
const reporter = pageIds.length
|
|
251
|
-
?
|
|
254
|
+
? createBuildDashboard({
|
|
255
|
+
mode: 'prerender-only',
|
|
256
|
+
pageIds,
|
|
257
|
+
idToPath,
|
|
258
|
+
overview: {
|
|
259
|
+
title: '@ossy/app dev',
|
|
260
|
+
configRel: overviewSnap.configRel,
|
|
261
|
+
apiRoutes: overviewSnap.apiRoutes,
|
|
262
|
+
},
|
|
263
|
+
})
|
|
252
264
|
: null
|
|
253
|
-
reporter?.
|
|
265
|
+
reporter?.start()
|
|
266
|
+
if (
|
|
267
|
+
!resourceTemplatesDevLogged &&
|
|
268
|
+
resourceTemplatesResult.wrote &&
|
|
269
|
+
resourceTemplatesResult.path &&
|
|
270
|
+
reporter
|
|
271
|
+
) {
|
|
272
|
+
reporter.pushLog(
|
|
273
|
+
`[resource-templates] merged ${resourceTemplatesResult.count} → ${path.relative(process.cwd(), resourceTemplatesResult.path)}`
|
|
274
|
+
)
|
|
275
|
+
resourceTemplatesDevLogged = true
|
|
276
|
+
}
|
|
254
277
|
try {
|
|
255
278
|
await prerenderReactTask.handler({
|
|
256
279
|
op: 'prerenderPagesParallel',
|
|
257
|
-
|
|
280
|
+
pagesEntryPath,
|
|
258
281
|
configSourcePath,
|
|
259
282
|
publicDir: path.join(buildPath, 'public'),
|
|
260
283
|
reporter,
|
|
@@ -271,37 +294,34 @@ export const dev = async (cliArgs) => {
|
|
|
271
294
|
|
|
272
295
|
const regenApiGenerated = () => {
|
|
273
296
|
if (options['--api-source']) return
|
|
274
|
-
|
|
275
|
-
srcDir,
|
|
276
|
-
buildPath,
|
|
277
|
-
})
|
|
278
|
-
const gen = path.join(ossyGeneratedDir(buildPath), OSSY_GEN_API_BASENAME)
|
|
279
|
-
if (fs.existsSync(gen) && typeof watcher?.invalidate === 'function') {
|
|
280
|
-
watcher.invalidate(gen)
|
|
281
|
-
}
|
|
297
|
+
void runNodeBundles().then(() => scheduleRestart())
|
|
282
298
|
}
|
|
283
299
|
|
|
284
300
|
const regenTasksGenerated = () => {
|
|
285
|
-
|
|
286
|
-
if (fs.existsSync(tasksGeneratedPath) && typeof watcher?.invalidate === 'function') {
|
|
287
|
-
watcher.invalidate(tasksGeneratedPath)
|
|
288
|
-
}
|
|
301
|
+
void runNodeBundles().then(() => scheduleRestart())
|
|
289
302
|
}
|
|
290
303
|
|
|
291
304
|
fs.watch(srcDir, { recursive: true }, (eventType, filename) => {
|
|
292
305
|
if (!filename) return
|
|
293
306
|
if (/\.page\.(jsx?|tsx?)$/.test(filename)) {
|
|
294
307
|
const refreshedPageFiles = discoverFilesByPattern(srcDir, PAGE_FILE_PATTERN)
|
|
308
|
+
currentPageFiles = refreshedPageFiles
|
|
295
309
|
const regenPath = path.join(ossyGeneratedDir(buildPath), OSSY_GEN_PAGES_BASENAME)
|
|
296
|
-
|
|
310
|
+
writePagesManifest({
|
|
311
|
+
pageFiles: refreshedPageFiles,
|
|
312
|
+
srcDir,
|
|
313
|
+
pagesGeneratedPath: regenPath,
|
|
314
|
+
})
|
|
297
315
|
writePageHydrateStubs(refreshedPageFiles, srcDir, ossyDir)
|
|
298
|
-
|
|
299
|
-
watcher
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
316
|
+
void runNodeBundles().then(() => {
|
|
317
|
+
if (typeof watcher?.invalidate === 'function') {
|
|
318
|
+
for (const f of refreshedPageFiles) {
|
|
319
|
+
const hid = clientHydrateIdForPage(f, srcDir)
|
|
320
|
+
watcher.invalidate(path.join(ossyDir, `hydrate-${hid}.jsx`))
|
|
321
|
+
}
|
|
303
322
|
}
|
|
304
|
-
|
|
323
|
+
scheduleRestart()
|
|
324
|
+
})
|
|
305
325
|
}
|
|
306
326
|
if (/\.api\.(mjs|cjs|js)$/.test(filename)) {
|
|
307
327
|
regenApiGenerated()
|
|
@@ -311,7 +331,7 @@ export const dev = async (cliArgs) => {
|
|
|
311
331
|
}
|
|
312
332
|
const norm = filename.replace(/\\/g, '/')
|
|
313
333
|
if (/\.resource\.js$/.test(norm) && norm.includes('resource-templates/')) {
|
|
314
|
-
writeResourceTemplatesBarrelIfPresent({ cwd: process.cwd(), log:
|
|
334
|
+
writeResourceTemplatesBarrelIfPresent({ cwd: process.cwd(), log: false })
|
|
315
335
|
const rtOut = path.join(resourceTemplatesDir(process.cwd()), OSSY_RESOURCE_TEMPLATES_OUT)
|
|
316
336
|
if (fs.existsSync(rtOut) && typeof watcher?.invalidate === 'function') {
|
|
317
337
|
watcher.invalidate(rtOut)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
|
|
6
|
+
const __ossyDir = path.dirname(fileURLToPath(import.meta.url))
|
|
7
|
+
|
|
8
|
+
function readJson (name) {
|
|
9
|
+
return JSON.parse(fs.readFileSync(path.join(__ossyDir, name), 'utf8'))
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function toPage (mod, derived) {
|
|
13
|
+
const meta = mod?.metadata || {}
|
|
14
|
+
const def = mod?.default
|
|
15
|
+
if (typeof def === 'function') {
|
|
16
|
+
return { ...derived, ...meta, element: React.createElement(def) }
|
|
17
|
+
}
|
|
18
|
+
return { ...derived, ...meta, ...(def || {}) }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const { pages: metaPages } = readJson('pages.generated.json')
|
|
22
|
+
const { pages: bundlePages } = readJson('pages.bundle.json')
|
|
23
|
+
|
|
24
|
+
if (metaPages.length !== bundlePages.length) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
'[@ossy/app][pages.runtime] pages.generated.json and pages.bundle.json must list the same number of pages'
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const out = []
|
|
31
|
+
for (let i = 0; i < metaPages.length; i++) {
|
|
32
|
+
const derived = { id: metaPages[i].id, path: metaPages[i].path }
|
|
33
|
+
const rel = bundlePages[i].module
|
|
34
|
+
const abs = path.resolve(__ossyDir, rel)
|
|
35
|
+
const mod = await import(pathToFileURL(abs).href)
|
|
36
|
+
out.push(toPage(mod, derived))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default out
|
|
@@ -2,7 +2,7 @@ import path from 'path'
|
|
|
2
2
|
import url from 'url'
|
|
3
3
|
import fs from 'fs'
|
|
4
4
|
import { rollup } from 'rollup'
|
|
5
|
-
import { BuildPage } from './render-page.task.js'
|
|
5
|
+
import { BuildPage, buildPrerenderAppConfig } from './render-page.task.js'
|
|
6
6
|
import { pageIdFromHydrateEntryName } from './build-terminal.js'
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -24,35 +24,6 @@ function pathIsPrerenderable (routePath) {
|
|
|
24
24
|
return true
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/** Mirrors server-era app shell config with static defaults (no cookies / session). */
|
|
28
|
-
export function buildPrerenderAppConfig ({
|
|
29
|
-
buildTimeConfig,
|
|
30
|
-
pageList,
|
|
31
|
-
activeRouteId,
|
|
32
|
-
urlPath,
|
|
33
|
-
}) {
|
|
34
|
-
const pages = pageList.map((page) => {
|
|
35
|
-
const entry = {
|
|
36
|
-
id: page?.id,
|
|
37
|
-
path: page?.path,
|
|
38
|
-
}
|
|
39
|
-
if (activeRouteId != null && page?.id === activeRouteId) {
|
|
40
|
-
entry.element = page?.element
|
|
41
|
-
}
|
|
42
|
-
return entry
|
|
43
|
-
})
|
|
44
|
-
return {
|
|
45
|
-
...buildTimeConfig,
|
|
46
|
-
url: urlPath,
|
|
47
|
-
theme: buildTimeConfig.theme || 'light',
|
|
48
|
-
isAuthenticated: false,
|
|
49
|
-
workspaceId: buildTimeConfig.workspaceId,
|
|
50
|
-
apiUrl: buildTimeConfig.apiUrl,
|
|
51
|
-
pages,
|
|
52
|
-
sidebarPrimaryCollapsed: false,
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
27
|
function copyPublicToBuild ({ copyPublicFrom, buildPath }) {
|
|
57
28
|
if (!copyPublicFrom || !fs.existsSync(copyPublicFrom)) return
|
|
58
29
|
const dest = path.join(buildPath, 'public')
|
|
@@ -170,13 +141,13 @@ async function prerenderOnePage ({
|
|
|
170
141
|
}
|
|
171
142
|
|
|
172
143
|
async function prerenderPagesParallel ({
|
|
173
|
-
|
|
144
|
+
pagesEntryPath,
|
|
174
145
|
configSourcePath,
|
|
175
146
|
publicDir,
|
|
176
147
|
reporter,
|
|
177
148
|
}) {
|
|
178
149
|
const cfgHref = url.pathToFileURL(path.resolve(configSourcePath)).href
|
|
179
|
-
const pagesHref = url.pathToFileURL(path.resolve(
|
|
150
|
+
const pagesHref = url.pathToFileURL(path.resolve(pagesEntryPath)).href
|
|
180
151
|
|
|
181
152
|
const configModule = await import(cfgHref)
|
|
182
153
|
const pagesModule = await import(pagesHref)
|
|
@@ -237,7 +208,7 @@ async function runProduction ({
|
|
|
237
208
|
copyPublicFrom,
|
|
238
209
|
buildPath,
|
|
239
210
|
nodeEnv,
|
|
240
|
-
|
|
211
|
+
pagesEntryPath,
|
|
241
212
|
configSourcePath,
|
|
242
213
|
createClientRollupPlugins,
|
|
243
214
|
minifyBrowserStaticChunks,
|
|
@@ -259,7 +230,7 @@ async function runProduction ({
|
|
|
259
230
|
})
|
|
260
231
|
|
|
261
232
|
const { failures: prerenderFailures } = await prerenderPagesParallel({
|
|
262
|
-
|
|
233
|
+
pagesEntryPath,
|
|
263
234
|
configSourcePath,
|
|
264
235
|
publicDir: path.join(buildPath, 'public'),
|
|
265
236
|
reporter,
|
|
@@ -284,7 +255,7 @@ export default {
|
|
|
284
255
|
}
|
|
285
256
|
if (op === 'prerenderPagesParallel') {
|
|
286
257
|
return prerenderPagesParallel({
|
|
287
|
-
|
|
258
|
+
pagesEntryPath: input.pagesEntryPath,
|
|
288
259
|
configSourcePath: input.configSourcePath,
|
|
289
260
|
publicDir: input.publicDir,
|
|
290
261
|
reporter: input.reporter,
|
package/cli/render-page.task.js
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
import React, { cloneElement } from 'react'
|
|
2
2
|
import { prerenderToNodeStream } from 'react-dom/static'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* App shell config for SSR / prerender (mirrors client: theme, pages metadata, active route element).
|
|
6
|
+
*/
|
|
7
|
+
export function buildPrerenderAppConfig ({
|
|
8
|
+
buildTimeConfig,
|
|
9
|
+
pageList,
|
|
10
|
+
activeRouteId,
|
|
11
|
+
urlPath,
|
|
12
|
+
isAuthenticated = false,
|
|
13
|
+
}) {
|
|
14
|
+
const pages = pageList.map((page) => {
|
|
15
|
+
const entry = {
|
|
16
|
+
id: page?.id,
|
|
17
|
+
path: page?.path,
|
|
18
|
+
}
|
|
19
|
+
if (activeRouteId != null && page?.id === activeRouteId) {
|
|
20
|
+
entry.element = page?.element
|
|
21
|
+
}
|
|
22
|
+
return entry
|
|
23
|
+
})
|
|
24
|
+
return {
|
|
25
|
+
...buildTimeConfig,
|
|
26
|
+
url: urlPath,
|
|
27
|
+
theme: buildTimeConfig.theme || 'light',
|
|
28
|
+
isAuthenticated,
|
|
29
|
+
workspaceId: buildTimeConfig.workspaceId,
|
|
30
|
+
apiUrl: buildTimeConfig.apiUrl,
|
|
31
|
+
pages,
|
|
32
|
+
sidebarPrimaryCollapsed: false,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
4
36
|
/** Strips non-JSON content (e.g. React elements on `pages`) for the bootstrap script. */
|
|
5
37
|
export function appConfigForBootstrap (appConfig) {
|
|
6
38
|
if (!appConfig || typeof appConfig !== 'object') return appConfig
|
package/cli/server.js
CHANGED
|
@@ -6,12 +6,17 @@ import { Router as OssyRouter } from '@ossy/router'
|
|
|
6
6
|
import { ProxyInternal } from './proxy-internal.js'
|
|
7
7
|
import cookieParser from 'cookie-parser'
|
|
8
8
|
|
|
9
|
-
import ApiRoutes from './.ossy/api.
|
|
9
|
+
import ApiRoutes from './.ossy/api.runtime.mjs'
|
|
10
|
+
import pageRoutes from './.ossy/pages.runtime.mjs'
|
|
11
|
+
import buildTimeConfig from './.ossy/server-config.runtime.mjs'
|
|
12
|
+
import { BuildPage, buildPrerenderAppConfig } from './.ossy/render-page.task.js'
|
|
10
13
|
import Middleware from './.ossy/middleware.runtime.js'
|
|
11
14
|
|
|
12
15
|
/** API bundle default may be an empty array. */
|
|
13
16
|
const apiRouteList = ApiRoutes ?? []
|
|
14
17
|
|
|
18
|
+
const sitePageList = Array.isArray(pageRoutes) ? pageRoutes : []
|
|
19
|
+
|
|
15
20
|
const app = express();
|
|
16
21
|
|
|
17
22
|
const currentDir = path.dirname(url.fileURLToPath(import.meta.url))
|
|
@@ -101,22 +106,57 @@ const middleware = [
|
|
|
101
106
|
|
|
102
107
|
app.use(middleware)
|
|
103
108
|
|
|
104
|
-
const
|
|
109
|
+
const apiRouter = OssyRouter.of({
|
|
105
110
|
pages: apiRouteList,
|
|
106
111
|
})
|
|
107
112
|
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
const pageRouter = OssyRouter.of({
|
|
114
|
+
pages: sitePageList,
|
|
115
|
+
defaultLanguage: buildTimeConfig.defaultLanguage,
|
|
116
|
+
supportedLanguages: buildTimeConfig.supportedLanguages || [],
|
|
117
|
+
})
|
|
110
118
|
|
|
111
|
-
|
|
119
|
+
app.all('*all', async (req, res) => {
|
|
120
|
+
const requestUrl = req.originalUrl || '/'
|
|
112
121
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
122
|
+
try {
|
|
123
|
+
const apiRoute = apiRouter.getPageByUrl(requestUrl)
|
|
124
|
+
if (apiRoute && typeof apiRoute.handle === 'function') {
|
|
125
|
+
console.log(`[@ossy/app][server] Handling API route: ${requestUrl}`)
|
|
126
|
+
apiRoute.handle(req, res)
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
131
|
+
res.status(404).send('Not found')
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const pageRoute = pageRouter.getPageByUrl(requestUrl)
|
|
136
|
+
if (pageRoute?.element) {
|
|
137
|
+
const appConfig = buildPrerenderAppConfig({
|
|
138
|
+
buildTimeConfig,
|
|
139
|
+
pageList: sitePageList,
|
|
140
|
+
activeRouteId: pageRoute.id,
|
|
141
|
+
urlPath: requestUrl,
|
|
142
|
+
isAuthenticated: !!req.isAuthenticated,
|
|
143
|
+
})
|
|
144
|
+
const html = await BuildPage.handle({
|
|
145
|
+
route: pageRoute,
|
|
146
|
+
appConfig,
|
|
147
|
+
isDevReloadEnabled,
|
|
148
|
+
})
|
|
149
|
+
res.status(200).type('html').send(html)
|
|
150
|
+
return
|
|
151
|
+
}
|
|
118
152
|
|
|
119
|
-
|
|
153
|
+
res.status(404).send('Not found')
|
|
154
|
+
} catch (err) {
|
|
155
|
+
console.error('[@ossy/app][server] Request handling failed:', err)
|
|
156
|
+
if (!res.headersSent) {
|
|
157
|
+
res.status(500).type('text').send('Internal Server Error')
|
|
158
|
+
}
|
|
159
|
+
}
|
|
120
160
|
})
|
|
121
161
|
|
|
122
162
|
app.listen(port, () => {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
4
|
+
|
|
5
|
+
const __ossyDir = path.dirname(fileURLToPath(import.meta.url))
|
|
6
|
+
|
|
7
|
+
function normalizeTaskExport (mod) {
|
|
8
|
+
const d = mod?.default
|
|
9
|
+
if (d == null) return []
|
|
10
|
+
return Array.isArray(d) ? d : [d]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { modules } = JSON.parse(fs.readFileSync(path.join(__ossyDir, 'tasks.bundle.json'), 'utf8'))
|
|
14
|
+
|
|
15
|
+
const out = []
|
|
16
|
+
for (const rel of modules) {
|
|
17
|
+
const abs = path.resolve(__ossyDir, rel)
|
|
18
|
+
const mod = await import(pathToFileURL(abs).href)
|
|
19
|
+
out.push(...normalizeTaskExport(mod))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default out
|
package/cli/worker-entry.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossy/app",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"source": "./src/index.js",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -27,14 +27,14 @@
|
|
|
27
27
|
"@babel/eslint-parser": "^7.15.8",
|
|
28
28
|
"@babel/preset-react": "^7.26.3",
|
|
29
29
|
"@babel/register": "^7.25.9",
|
|
30
|
-
"@ossy/connected-components": "^1.11.
|
|
31
|
-
"@ossy/design-system": "^1.11.
|
|
32
|
-
"@ossy/pages": "^1.11.
|
|
33
|
-
"@ossy/router": "^1.11.
|
|
34
|
-
"@ossy/router-react": "^1.11.
|
|
35
|
-
"@ossy/sdk": "^1.11.
|
|
36
|
-
"@ossy/sdk-react": "^1.11.
|
|
37
|
-
"@ossy/themes": "^1.11.
|
|
30
|
+
"@ossy/connected-components": "^1.11.2",
|
|
31
|
+
"@ossy/design-system": "^1.11.2",
|
|
32
|
+
"@ossy/pages": "^1.11.2",
|
|
33
|
+
"@ossy/router": "^1.11.2",
|
|
34
|
+
"@ossy/router-react": "^1.11.2",
|
|
35
|
+
"@ossy/sdk": "^1.11.2",
|
|
36
|
+
"@ossy/sdk-react": "^1.11.2",
|
|
37
|
+
"@ossy/themes": "^1.11.2",
|
|
38
38
|
"@rollup/plugin-alias": "^6.0.0",
|
|
39
39
|
"@rollup/plugin-babel": "6.1.0",
|
|
40
40
|
"@rollup/plugin-commonjs": "^29.0.0",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"README.md",
|
|
68
68
|
"tsconfig.json"
|
|
69
69
|
],
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "3998c8b461e8b7317462c3d1d65b25b9454ba18a"
|
|
71
71
|
}
|