@ossy/app 0.15.12 → 1.0.1
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 +6 -20
- package/cli/build.js +419 -352
- package/cli/dev.js +117 -136
- package/cli/server.js +25 -12
- package/package.json +13 -14
- package/scripts/ensure-build-stubs.mjs +1 -2
- package/src/index.js +6 -1
- package/cli/Api.js +0 -3
- package/cli/client.js +0 -8
- package/cli/default-app.jsx +0 -10
package/cli/build.js
CHANGED
|
@@ -5,28 +5,95 @@ import { rollup } from 'rollup';
|
|
|
5
5
|
import babel from '@rollup/plugin-babel';
|
|
6
6
|
import { nodeResolve as resolveDependencies } from '@rollup/plugin-node-resolve'
|
|
7
7
|
import resolveCommonJsDependencies from '@rollup/plugin-commonjs'
|
|
8
|
-
import
|
|
9
|
-
import minifyJS from '@rollup/plugin-terser'
|
|
8
|
+
import { minify as minifyWithTerser } from 'terser'
|
|
10
9
|
// import typescript from '@rollup/plugin-typescript'
|
|
11
|
-
import preserveDirectives from "rollup-plugin-preserve-directives"
|
|
12
10
|
import json from "@rollup/plugin-json"
|
|
11
|
+
import nodeExternals from 'rollup-plugin-node-externals'
|
|
13
12
|
import copy from 'rollup-plugin-copy';
|
|
14
13
|
import replace from '@rollup/plugin-replace';
|
|
15
14
|
import arg from 'arg'
|
|
16
15
|
import { ensureBuildStubs } from '../scripts/ensure-build-stubs.mjs'
|
|
17
|
-
import {
|
|
16
|
+
import { createRequire } from 'node:module'
|
|
18
17
|
// import inject from '@rollup/plugin-inject'
|
|
19
18
|
|
|
20
19
|
const PAGE_FILE_PATTERN = /\.page\.(jsx?|tsx?)$/
|
|
21
20
|
const API_FILE_PATTERN = /\.api\.(mjs|cjs|js)$/
|
|
22
21
|
const TASK_FILE_PATTERN = /\.task\.(mjs|cjs|js)$/
|
|
22
|
+
const RESOURCE_TEMPLATE_FILE_PATTERN = /\.resource\.js$/
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
const NODE_MODULES_SEG = `${path.sep}node_modules${path.sep}`
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Keep third-party code out of the Rollup graph for the server build.
|
|
28
|
+
* `preserveModules` + `@rollup/plugin-commonjs` otherwise emits `_virtual/*` chunks that import
|
|
29
|
+
* `{ __require }` from real `node_modules` paths, which Node ESM cannot execute.
|
|
30
|
+
*
|
|
31
|
+
* Rules:
|
|
32
|
+
* - Resolved absolute path under `node_modules` → external (any npm dep, including transitive).
|
|
33
|
+
* - Unresolved bare specifier → external so Node loads it (covers `react`, `lodash`, customers’
|
|
34
|
+
* `@acme/*`, etc.). Exception: `@ossy/*` is bundled (framework packages; monorepo paths or
|
|
35
|
+
* published tarballs — avoids relying on Node resolving those bare ids from `build/server.js`).
|
|
36
|
+
*
|
|
37
|
+
* React’s main entry is still CJS; there is no separate official ESM prod file for `import 'react'`.
|
|
38
|
+
* Leaving it external is correct: Node applies default export interop when Rollup does not rewrite it.
|
|
39
|
+
*/
|
|
40
|
+
export function ossyServerExternal (id, _importer, _isResolved) {
|
|
41
|
+
if (!id || id[0] === '\0') return false
|
|
42
|
+
if (path.isAbsolute(id)) {
|
|
43
|
+
return id.includes(NODE_MODULES_SEG)
|
|
44
|
+
}
|
|
45
|
+
if (id.startsWith('.')) return false
|
|
46
|
+
if (id.startsWith('@ossy/')) return false
|
|
47
|
+
return true
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Written next to `*.resource.js` under `src/resource-templates/` when that dir exists. */
|
|
51
|
+
export const OSSY_RESOURCE_TEMPLATES_OUT = '.ossy-system-templates.generated.js'
|
|
52
|
+
|
|
53
|
+
/** Rollup output paths for JS served to the browser (see `entryFileNames` / `chunkFileNames`). */
|
|
54
|
+
const BROWSER_STATIC_PREFIX = 'public/static/'
|
|
55
|
+
|
|
56
|
+
function minifyBrowserStaticChunks () {
|
|
57
|
+
return {
|
|
58
|
+
name: 'minify-browser-static-chunks',
|
|
59
|
+
async renderChunk (code, chunk, outputOptions) {
|
|
60
|
+
const fileName = chunk.fileName
|
|
61
|
+
if (!fileName || !fileName.startsWith(BROWSER_STATIC_PREFIX)) {
|
|
62
|
+
return null
|
|
63
|
+
}
|
|
64
|
+
const useSourceMap =
|
|
65
|
+
outputOptions.sourcemap === true || typeof outputOptions.sourcemap === 'string'
|
|
66
|
+
const fmt = outputOptions.format
|
|
67
|
+
const result = await minifyWithTerser(code, {
|
|
68
|
+
sourceMap: useSourceMap,
|
|
69
|
+
module: fmt === 'es' || fmt === 'esm',
|
|
70
|
+
})
|
|
71
|
+
const out = result.code ?? code
|
|
72
|
+
if (useSourceMap && result.map) {
|
|
73
|
+
const map =
|
|
74
|
+
typeof result.map === 'string' ? JSON.parse(result.map) : result.map
|
|
75
|
+
return { code: out, map }
|
|
76
|
+
}
|
|
77
|
+
return out
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Subfolder under `build/` for generated pages/api/task entry stubs. */
|
|
25
83
|
export const OSSY_GEN_DIRNAME = '.ossy'
|
|
26
84
|
export const OSSY_GEN_PAGES_BASENAME = 'pages.generated.jsx'
|
|
27
85
|
export const OSSY_GEN_API_BASENAME = 'api.generated.js'
|
|
28
86
|
export const OSSY_GEN_TASKS_BASENAME = 'tasks.generated.js'
|
|
29
87
|
|
|
88
|
+
/** Per-page client entries: `hydrate-<pageId>.jsx` under `.ossy/` */
|
|
89
|
+
const HYDRATE_STUB_PREFIX = 'hydrate-'
|
|
90
|
+
const HYDRATE_STUB_SUFFIX = '.jsx'
|
|
91
|
+
|
|
92
|
+
/** Rollup input chunk name for a page id (safe identifier; id may contain `-`). */
|
|
93
|
+
export function hydrateEntryName (pageId) {
|
|
94
|
+
return `hydrate__${pageId}`
|
|
95
|
+
}
|
|
96
|
+
|
|
30
97
|
export function ossyGeneratedDir (buildPath) {
|
|
31
98
|
return path.join(buildPath, OSSY_GEN_DIRNAME)
|
|
32
99
|
}
|
|
@@ -41,18 +108,138 @@ function relToGeneratedImport (generatedAbs, targetAbs) {
|
|
|
41
108
|
return path.relative(path.dirname(generatedAbs), targetAbs).replace(/\\/g, '/')
|
|
42
109
|
}
|
|
43
110
|
|
|
44
|
-
/**
|
|
45
|
-
export function
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
111
|
+
/** Deletes the entire build output dir, then recreates `build/.ossy` (generated stubs are written next). */
|
|
112
|
+
export function resetOssyBuildDir (buildPath) {
|
|
113
|
+
fs.rmSync(buildPath, { recursive: true, force: true })
|
|
114
|
+
ensureOssyGeneratedDir(buildPath)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function resourceTemplatesDir (cwd = process.cwd()) {
|
|
118
|
+
return path.join(cwd, 'src', 'resource-templates')
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function discoverResourceTemplateFiles (templatesDir) {
|
|
122
|
+
if (!fs.existsSync(templatesDir) || !fs.statSync(templatesDir).isDirectory()) {
|
|
123
|
+
return []
|
|
55
124
|
}
|
|
125
|
+
return fs
|
|
126
|
+
.readdirSync(templatesDir)
|
|
127
|
+
.filter((n) => RESOURCE_TEMPLATE_FILE_PATTERN.test(n))
|
|
128
|
+
.map((n) => path.join(templatesDir, n))
|
|
129
|
+
.sort((a, b) => path.basename(a).localeCompare(path.basename(b)))
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function generateResourceTemplatesBarrelSource ({ outputAbs, templateFilesAbs }) {
|
|
133
|
+
const lines = [
|
|
134
|
+
'// Generated by @ossy/app — do not edit',
|
|
135
|
+
'',
|
|
136
|
+
]
|
|
137
|
+
templateFilesAbs.forEach((f, i) => {
|
|
138
|
+
const rel = relToGeneratedImport(outputAbs, f)
|
|
139
|
+
lines.push(`import _resource${i} from './${rel}'`)
|
|
140
|
+
})
|
|
141
|
+
lines.push('')
|
|
142
|
+
lines.push(
|
|
143
|
+
'/** Built-in resource templates merged into every workspace (with imported templates) in the API. */'
|
|
144
|
+
)
|
|
145
|
+
lines.push('export const SystemTemplates = [')
|
|
146
|
+
templateFilesAbs.forEach((_, i) => {
|
|
147
|
+
lines.push(` _resource${i},`)
|
|
148
|
+
})
|
|
149
|
+
lines.push(']')
|
|
150
|
+
lines.push('')
|
|
151
|
+
return lines.join('\n')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* If `src/resource-templates/` exists, writes `.ossy-system-templates.generated.js` there.
|
|
156
|
+
* No-op when the directory is missing (e.g. website packages). Runs during `build` / `dev`.
|
|
157
|
+
*/
|
|
158
|
+
export function writeResourceTemplatesBarrelIfPresent ({ cwd = process.cwd(), log = true } = {}) {
|
|
159
|
+
const dir = resourceTemplatesDir(cwd)
|
|
160
|
+
if (!fs.existsSync(dir)) {
|
|
161
|
+
return { wrote: false, count: 0, path: null }
|
|
162
|
+
}
|
|
163
|
+
const files = discoverResourceTemplateFiles(dir)
|
|
164
|
+
const outAbs = path.join(dir, OSSY_RESOURCE_TEMPLATES_OUT)
|
|
165
|
+
fs.writeFileSync(outAbs, generateResourceTemplatesBarrelSource({ outputAbs: outAbs, templateFilesAbs: files }), 'utf8')
|
|
166
|
+
if (log) {
|
|
167
|
+
console.log(
|
|
168
|
+
`[@ossy/app][resource-templates] merged ${files.length} template(s) → ${path.relative(cwd, outAbs)}`
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
return { wrote: true, count: files.length, path: outAbs }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Shared Rollup plugins (virtual path replaces, resolve, JSX).
|
|
176
|
+
* Server builds pass `nodeExternals: true` so `dependencies` stay importable from Node at runtime.
|
|
177
|
+
*/
|
|
178
|
+
export function createOssyRollupPlugins ({
|
|
179
|
+
pagesGeneratedPath,
|
|
180
|
+
apiSourcePath,
|
|
181
|
+
middlewareSourcePath,
|
|
182
|
+
configSourcePath,
|
|
183
|
+
nodeEnv,
|
|
184
|
+
nodeExternals: useNodeExternals = false,
|
|
185
|
+
preferBuiltins = true,
|
|
186
|
+
copyPublicFrom,
|
|
187
|
+
buildPath,
|
|
188
|
+
}) {
|
|
189
|
+
const plugins = [
|
|
190
|
+
replace({
|
|
191
|
+
preventAssignment: true,
|
|
192
|
+
delimiters: ['%%', '%%'],
|
|
193
|
+
'@ossy/pages/source-file': pagesGeneratedPath,
|
|
194
|
+
}),
|
|
195
|
+
replace({
|
|
196
|
+
preventAssignment: true,
|
|
197
|
+
delimiters: ['%%', '%%'],
|
|
198
|
+
'@ossy/api/source-file': apiSourcePath,
|
|
199
|
+
}),
|
|
200
|
+
replace({
|
|
201
|
+
preventAssignment: true,
|
|
202
|
+
delimiters: ['%%', '%%'],
|
|
203
|
+
'@ossy/middleware/source-file': middlewareSourcePath,
|
|
204
|
+
}),
|
|
205
|
+
replace({
|
|
206
|
+
preventAssignment: true,
|
|
207
|
+
delimiters: ['%%', '%%'],
|
|
208
|
+
'@ossy/config/source-file': configSourcePath,
|
|
209
|
+
}),
|
|
210
|
+
replace({
|
|
211
|
+
preventAssignment: true,
|
|
212
|
+
'process.env.NODE_ENV': JSON.stringify(nodeEnv),
|
|
213
|
+
}),
|
|
214
|
+
json(),
|
|
215
|
+
]
|
|
216
|
+
if (useNodeExternals) {
|
|
217
|
+
plugins.push(
|
|
218
|
+
nodeExternals({
|
|
219
|
+
deps: true,
|
|
220
|
+
devDeps: false,
|
|
221
|
+
peerDeps: true,
|
|
222
|
+
packagePath: path.join(process.cwd(), 'package.json'),
|
|
223
|
+
})
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
plugins.push(
|
|
227
|
+
resolveCommonJsDependencies(),
|
|
228
|
+
resolveDependencies({ preferBuiltins }),
|
|
229
|
+
babel({
|
|
230
|
+
babelHelpers: 'bundled',
|
|
231
|
+
extensions: ['.jsx', '.tsx'],
|
|
232
|
+
presets: ['@babel/preset-react'],
|
|
233
|
+
})
|
|
234
|
+
)
|
|
235
|
+
if (copyPublicFrom && fs.existsSync(copyPublicFrom)) {
|
|
236
|
+
plugins.push(
|
|
237
|
+
copy({
|
|
238
|
+
targets: [{ src: `${copyPublicFrom}/**/*`, dest: path.join(buildPath, 'public') }],
|
|
239
|
+
})
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
return plugins
|
|
56
243
|
}
|
|
57
244
|
|
|
58
245
|
export function discoverApiFiles(srcDir) {
|
|
@@ -92,19 +279,22 @@ export function discoverTaskFiles(srcDir) {
|
|
|
92
279
|
}
|
|
93
280
|
|
|
94
281
|
/**
|
|
95
|
-
* Merges
|
|
96
|
-
* for the Ossy API router ({ id, path, handle }).
|
|
282
|
+
* Merges every `*.api.js` (and `.api.mjs` / `.api.cjs`) under `src/` into one default export array
|
|
283
|
+
* for the Ossy API router ({ id, path, handle }). With no API files, emits `export default []`.
|
|
97
284
|
*/
|
|
98
|
-
export function generateApiModule ({ generatedPath, apiFiles
|
|
285
|
+
export function generateApiModule ({ generatedPath, apiFiles }) {
|
|
286
|
+
if (apiFiles.length === 0) {
|
|
287
|
+
return [
|
|
288
|
+
'// Generated by @ossy/app — do not edit',
|
|
289
|
+
'',
|
|
290
|
+
'export default []',
|
|
291
|
+
'',
|
|
292
|
+
].join('\n')
|
|
293
|
+
}
|
|
99
294
|
const lines = [
|
|
100
295
|
'// Generated by @ossy/app — do not edit',
|
|
101
296
|
'',
|
|
102
297
|
]
|
|
103
|
-
const hasLegacy = legacyPath && fs.existsSync(legacyPath)
|
|
104
|
-
if (hasLegacy) {
|
|
105
|
-
const rel = relToGeneratedImport(generatedPath, legacyPath)
|
|
106
|
-
lines.push(`import _legacyApi from './${rel}'`)
|
|
107
|
-
}
|
|
108
298
|
apiFiles.forEach((f, i) => {
|
|
109
299
|
const rel = relToGeneratedImport(generatedPath, f)
|
|
110
300
|
lines.push(`import * as _api${i} from './${rel}'`)
|
|
@@ -119,13 +309,7 @@ export function generateApiModule ({ generatedPath, apiFiles, legacyPath }) {
|
|
|
119
309
|
'',
|
|
120
310
|
'export default [',
|
|
121
311
|
)
|
|
122
|
-
const parts =
|
|
123
|
-
if (hasLegacy) {
|
|
124
|
-
parts.push(' ..._normalizeApiExport({ default: _legacyApi }),')
|
|
125
|
-
}
|
|
126
|
-
apiFiles.forEach((_, i) => {
|
|
127
|
-
parts.push(` ..._normalizeApiExport(_api${i}),`)
|
|
128
|
-
})
|
|
312
|
+
const parts = apiFiles.map((_, i) => ` ..._normalizeApiExport(_api${i}),`)
|
|
129
313
|
lines.push(parts.join('\n'))
|
|
130
314
|
lines.push(']')
|
|
131
315
|
lines.push('')
|
|
@@ -133,60 +317,29 @@ export function generateApiModule ({ generatedPath, apiFiles, legacyPath }) {
|
|
|
133
317
|
}
|
|
134
318
|
|
|
135
319
|
/**
|
|
136
|
-
*
|
|
320
|
+
* Writes `build/.ossy/api.generated.js` and returns its path for `@ossy/api/source-file`.
|
|
321
|
+
* Always uses the generated file so the Rollup replace target stays stable.
|
|
137
322
|
*/
|
|
138
|
-
export function resolveApiSource ({
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
const defaultStub = path.resolve(scriptDir, 'api.js')
|
|
142
|
-
const bp = path.resolve(buildPath ?? path.join(cwd, 'build'))
|
|
143
|
-
ensureOssyGeneratedDir(bp)
|
|
144
|
-
const generatedPath = path.join(ossyGeneratedDir(bp), OSSY_GEN_API_BASENAME)
|
|
145
|
-
|
|
146
|
-
if (explicitApiSource) {
|
|
147
|
-
const p = path.isAbsolute(explicitApiSource)
|
|
148
|
-
? explicitApiSource
|
|
149
|
-
: path.resolve(cwd, explicitApiSource)
|
|
150
|
-
if (fs.existsSync(p)) {
|
|
151
|
-
return { apiSourcePath: p, apiOverviewFiles: [p], usedGenerated: false }
|
|
152
|
-
}
|
|
153
|
-
return { apiSourcePath: defaultStub, apiOverviewFiles: [], usedGenerated: false }
|
|
154
|
-
}
|
|
155
|
-
|
|
323
|
+
export function resolveApiSource ({ srcDir, buildPath }) {
|
|
324
|
+
ensureOssyGeneratedDir(buildPath)
|
|
325
|
+
const generatedPath = path.join(ossyGeneratedDir(buildPath), OSSY_GEN_API_BASENAME)
|
|
156
326
|
const apiFiles = discoverApiFiles(srcDir)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
generatedPath,
|
|
163
|
-
generateApiModule({
|
|
164
|
-
generatedPath,
|
|
165
|
-
apiFiles,
|
|
166
|
-
legacyPath: hasLegacy ? legacyPath : null,
|
|
167
|
-
})
|
|
168
|
-
)
|
|
169
|
-
const overview = [...(hasLegacy ? [legacyPath] : []), ...apiFiles]
|
|
170
|
-
return { apiSourcePath: generatedPath, apiOverviewFiles: overview, usedGenerated: true }
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return { apiSourcePath: defaultStub, apiOverviewFiles: [], usedGenerated: false }
|
|
327
|
+
fs.writeFileSync(
|
|
328
|
+
generatedPath,
|
|
329
|
+
generateApiModule({ generatedPath, apiFiles })
|
|
330
|
+
)
|
|
331
|
+
return { apiSourcePath: generatedPath, apiOverviewFiles: apiFiles }
|
|
174
332
|
}
|
|
175
333
|
|
|
176
334
|
/**
|
|
177
335
|
* Merges `src/tasks.js` (optional) and every `*.task.js` under `src/` into one default export array
|
|
178
336
|
* of job handlers `{ type, handler }` for the Ossy worker.
|
|
179
337
|
*/
|
|
180
|
-
export function generateTaskModule ({ generatedPath, taskFiles
|
|
338
|
+
export function generateTaskModule ({ generatedPath, taskFiles }) {
|
|
181
339
|
const lines = [
|
|
182
340
|
'// Generated by @ossy/app — do not edit',
|
|
183
341
|
'',
|
|
184
342
|
]
|
|
185
|
-
const hasLegacy = legacyPath && fs.existsSync(legacyPath)
|
|
186
|
-
if (hasLegacy) {
|
|
187
|
-
const rel = relToGeneratedImport(generatedPath, legacyPath)
|
|
188
|
-
lines.push(`import _legacyTasks from './${rel}'`)
|
|
189
|
-
}
|
|
190
343
|
taskFiles.forEach((f, i) => {
|
|
191
344
|
const rel = relToGeneratedImport(generatedPath, f)
|
|
192
345
|
lines.push(`import * as _task${i} from './${rel}'`)
|
|
@@ -202,9 +355,6 @@ export function generateTaskModule ({ generatedPath, taskFiles, legacyPath }) {
|
|
|
202
355
|
'export default [',
|
|
203
356
|
)
|
|
204
357
|
const parts = []
|
|
205
|
-
if (hasLegacy) {
|
|
206
|
-
parts.push(' ..._normalizeTaskExport({ default: _legacyTasks }),')
|
|
207
|
-
}
|
|
208
358
|
taskFiles.forEach((_, i) => {
|
|
209
359
|
parts.push(` ..._normalizeTaskExport(_task${i}),`)
|
|
210
360
|
})
|
|
@@ -217,45 +367,28 @@ export function generateTaskModule ({ generatedPath, taskFiles, legacyPath }) {
|
|
|
217
367
|
/**
|
|
218
368
|
* Resolves the Rollup entry for @ossy/tasks/source-file and which files to list in the worker build overview.
|
|
219
369
|
*/
|
|
220
|
-
export function resolveTaskSource ({
|
|
221
|
-
const srcDir = path.resolve(cwd, pagesOpt)
|
|
222
|
-
const legacyPath = path.resolve(cwd, 'src/tasks.js')
|
|
370
|
+
export function resolveTaskSource ({ srcDir, scriptDir, buildPath }) {
|
|
223
371
|
const defaultStub = path.resolve(scriptDir, 'tasks.js')
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const generatedPath = path.join(ossyGeneratedDir(bp), OSSY_GEN_TASKS_BASENAME)
|
|
227
|
-
|
|
228
|
-
if (explicitTaskSource) {
|
|
229
|
-
const p = path.isAbsolute(explicitTaskSource)
|
|
230
|
-
? explicitTaskSource
|
|
231
|
-
: path.resolve(cwd, explicitTaskSource)
|
|
232
|
-
if (fs.existsSync(p)) {
|
|
233
|
-
return { taskSourcePath: p, taskOverviewFiles: [p], usedGenerated: false }
|
|
234
|
-
}
|
|
235
|
-
return { taskSourcePath: defaultStub, taskOverviewFiles: [], usedGenerated: false }
|
|
236
|
-
}
|
|
372
|
+
ensureOssyGeneratedDir(buildPath)
|
|
373
|
+
const generatedPath = path.join(ossyGeneratedDir(buildPath), OSSY_GEN_TASKS_BASENAME)
|
|
237
374
|
|
|
238
375
|
const taskFiles = discoverTaskFiles(srcDir)
|
|
239
|
-
|
|
240
|
-
if (taskFiles.length > 0 || hasLegacy) {
|
|
376
|
+
if (taskFiles.length > 0) {
|
|
241
377
|
fs.writeFileSync(
|
|
242
378
|
generatedPath,
|
|
243
379
|
generateTaskModule({
|
|
244
380
|
generatedPath,
|
|
245
381
|
taskFiles,
|
|
246
|
-
legacyPath: hasLegacy ? legacyPath : null,
|
|
247
382
|
})
|
|
248
383
|
)
|
|
249
|
-
|
|
250
|
-
return { taskSourcePath: generatedPath, taskOverviewFiles: overview, usedGenerated: true }
|
|
384
|
+
return { taskSourcePath: generatedPath, taskOverviewFiles: taskFiles, usedGenerated: true }
|
|
251
385
|
}
|
|
252
386
|
|
|
253
387
|
return { taskSourcePath: defaultStub, taskOverviewFiles: [], usedGenerated: false }
|
|
254
388
|
}
|
|
255
389
|
|
|
256
390
|
export function discoverPageFiles(srcDir) {
|
|
257
|
-
|
|
258
|
-
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
|
391
|
+
if (!fs.existsSync(srcDir) || !fs.statSync(srcDir).isDirectory()) {
|
|
259
392
|
return []
|
|
260
393
|
}
|
|
261
394
|
const files = []
|
|
@@ -267,7 +400,7 @@ export function discoverPageFiles(srcDir) {
|
|
|
267
400
|
else if (PAGE_FILE_PATTERN.test(e.name)) files.push(full)
|
|
268
401
|
}
|
|
269
402
|
}
|
|
270
|
-
walk(
|
|
403
|
+
walk(srcDir)
|
|
271
404
|
return files.sort()
|
|
272
405
|
}
|
|
273
406
|
|
|
@@ -280,8 +413,105 @@ export function filePathToRoute(filePath, srcDir) {
|
|
|
280
413
|
return { id, path: routePath }
|
|
281
414
|
}
|
|
282
415
|
|
|
283
|
-
|
|
284
|
-
|
|
416
|
+
/**
|
|
417
|
+
* Basename for `/static/hydrate-<id>.js` must match `route.id` after `metadata` is merged in `toPage`
|
|
418
|
+
* (`{ ...derived, ...metadata }`). Uses a light `metadata` scan when possible.
|
|
419
|
+
*/
|
|
420
|
+
export function clientHydrateIdForPage (pageAbsPath, srcDir) {
|
|
421
|
+
const derived = filePathToRoute(pageAbsPath, srcDir)
|
|
422
|
+
let src = ''
|
|
423
|
+
try {
|
|
424
|
+
src = fs.readFileSync(pageAbsPath, 'utf8')
|
|
425
|
+
} catch {
|
|
426
|
+
return derived.id
|
|
427
|
+
}
|
|
428
|
+
const metaIdx = src.indexOf('export const metadata')
|
|
429
|
+
if (metaIdx === -1) return derived.id
|
|
430
|
+
const after = src.slice(metaIdx)
|
|
431
|
+
const idMatch = after.match(/\bid\s*:\s*['"]([^'"]+)['"]/)
|
|
432
|
+
return idMatch ? idMatch[1] : derived.id
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* One client entry per page: imports only that page module and hydrates the document.
|
|
437
|
+
* Keeps the same `toPage` shape as `generatePagesModule` so SSR and client trees match.
|
|
438
|
+
*/
|
|
439
|
+
export function generatePageHydrateModule ({ pageAbsPath, stubAbsPath, srcDir }) {
|
|
440
|
+
const rel = relToGeneratedImport(stubAbsPath, pageAbsPath)
|
|
441
|
+
const { id, path: routePath } = filePathToRoute(pageAbsPath, srcDir)
|
|
442
|
+
const pathLiteral = JSON.stringify(routePath)
|
|
443
|
+
const idLiteral = JSON.stringify(id)
|
|
444
|
+
return [
|
|
445
|
+
'// Generated by @ossy/app — do not edit',
|
|
446
|
+
'',
|
|
447
|
+
"import React, { cloneElement } from 'react'",
|
|
448
|
+
"import 'react-dom'",
|
|
449
|
+
"import { hydrateRoot } from 'react-dom/client'",
|
|
450
|
+
`import * as _page from './${rel}'`,
|
|
451
|
+
'',
|
|
452
|
+
'function toPage(mod, derived) {',
|
|
453
|
+
' const meta = mod?.metadata || {}',
|
|
454
|
+
' const def = mod?.default',
|
|
455
|
+
' if (typeof def === \'function\') {',
|
|
456
|
+
' return { ...derived, ...meta, element: React.createElement(def) }',
|
|
457
|
+
' }',
|
|
458
|
+
' return { ...derived, ...meta, ...(def || {}) }',
|
|
459
|
+
'}',
|
|
460
|
+
'',
|
|
461
|
+
`const _route = toPage(_page, { id: ${idLiteral}, path: ${pathLiteral} })`,
|
|
462
|
+
'const initialConfig = window.__INITIAL_APP_CONFIG__ || {}',
|
|
463
|
+
'const rootTree = _route?.element',
|
|
464
|
+
' ? cloneElement(_route.element, initialConfig)',
|
|
465
|
+
" : React.createElement('p', null, 'Not found')",
|
|
466
|
+
'hydrateRoot(document, rootTree)',
|
|
467
|
+
'',
|
|
468
|
+
].join('\n')
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/** Writes `hydrate-<id>.jsx` for each page; removes stale `hydrate-*` outputs in `ossyDir` first. */
|
|
472
|
+
export function writePageHydrateStubs (pageFiles, srcDir, ossyDir) {
|
|
473
|
+
if (!fs.existsSync(ossyDir)) fs.mkdirSync(ossyDir, { recursive: true })
|
|
474
|
+
for (const ent of fs.readdirSync(ossyDir, { withFileTypes: true })) {
|
|
475
|
+
const full = path.join(ossyDir, ent.name)
|
|
476
|
+
if (ent.isDirectory() && ent.name.startsWith(HYDRATE_STUB_PREFIX)) {
|
|
477
|
+
fs.rmSync(full, { recursive: true, force: true })
|
|
478
|
+
} else if (
|
|
479
|
+
ent.isFile() &&
|
|
480
|
+
ent.name.startsWith(HYDRATE_STUB_PREFIX) &&
|
|
481
|
+
ent.name.endsWith(HYDRATE_STUB_SUFFIX)
|
|
482
|
+
) {
|
|
483
|
+
fs.rmSync(full, { force: true })
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const seenIds = new Set()
|
|
487
|
+
for (const f of pageFiles) {
|
|
488
|
+
const hydrateId = clientHydrateIdForPage(f, srcDir)
|
|
489
|
+
if (seenIds.has(hydrateId)) {
|
|
490
|
+
throw new Error(
|
|
491
|
+
`[@ossy/app] Duplicate client hydrate id "${hydrateId}" (${f}). Per-page hydrate bundles need unique ids.`
|
|
492
|
+
)
|
|
493
|
+
}
|
|
494
|
+
seenIds.add(hydrateId)
|
|
495
|
+
const stubPath = path.join(ossyDir, `${HYDRATE_STUB_PREFIX}${hydrateId}${HYDRATE_STUB_SUFFIX}`)
|
|
496
|
+
fs.mkdirSync(path.dirname(stubPath), { recursive: true })
|
|
497
|
+
fs.writeFileSync(
|
|
498
|
+
stubPath,
|
|
499
|
+
generatePageHydrateModule({ pageAbsPath: f, stubAbsPath: stubPath, srcDir })
|
|
500
|
+
)
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
export function buildClientHydrateInput (pageFiles, srcDir, ossyDir) {
|
|
505
|
+
const input = {}
|
|
506
|
+
for (const f of pageFiles) {
|
|
507
|
+
const hydrateId = clientHydrateIdForPage(f, srcDir)
|
|
508
|
+
const stubPath = path.join(ossyDir, `${HYDRATE_STUB_PREFIX}${hydrateId}${HYDRATE_STUB_SUFFIX}`)
|
|
509
|
+
input[hydrateEntryName(hydrateId)] = stubPath
|
|
510
|
+
}
|
|
511
|
+
return input
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export function generatePagesModule (pageFiles, srcDir, generatedPath) {
|
|
285
515
|
const lines = [
|
|
286
516
|
"import React from 'react'",
|
|
287
517
|
...pageFiles.map((f, i) => {
|
|
@@ -300,7 +530,7 @@ export function generatePagesModule (pageFiles, cwd, srcDir, generatedPath) {
|
|
|
300
530
|
'',
|
|
301
531
|
'export default [',
|
|
302
532
|
...pageFiles.map((f, i) => {
|
|
303
|
-
const { id, path: defaultPath } = filePathToRoute(f,
|
|
533
|
+
const { id, path: defaultPath } = filePathToRoute(f, srcDir)
|
|
304
534
|
const pathStr = JSON.stringify(defaultPath)
|
|
305
535
|
return ` toPage(_page${i}, { id: '${id}', path: ${pathStr} }),`
|
|
306
536
|
}),
|
|
@@ -309,7 +539,7 @@ export function generatePagesModule (pageFiles, cwd, srcDir, generatedPath) {
|
|
|
309
539
|
return lines.join('\n')
|
|
310
540
|
}
|
|
311
541
|
|
|
312
|
-
export async function discoverModulePageFiles({
|
|
542
|
+
export async function discoverModulePageFiles({ configPath }) {
|
|
313
543
|
if (!configPath || !fs.existsSync(configPath)) return []
|
|
314
544
|
try {
|
|
315
545
|
// Try a cheap static parse first so we don't depend on the config file being
|
|
@@ -334,7 +564,7 @@ export async function discoverModulePageFiles({ cwd, configPath }) {
|
|
|
334
564
|
}
|
|
335
565
|
|
|
336
566
|
if (modules.length) {
|
|
337
|
-
const req = createRequire(path.resolve(cwd, 'package.json'))
|
|
567
|
+
const req = createRequire(path.resolve(process.cwd(), 'package.json'))
|
|
338
568
|
const files = []
|
|
339
569
|
for (const name of modules) {
|
|
340
570
|
const pkgJsonPath = req.resolve(`${name}/package.json`)
|
|
@@ -353,7 +583,7 @@ export async function discoverModulePageFiles({ cwd, configPath }) {
|
|
|
353
583
|
|
|
354
584
|
if (!modules2.length) return []
|
|
355
585
|
|
|
356
|
-
const req = createRequire(path.resolve(cwd, 'package.json'))
|
|
586
|
+
const req = createRequire(path.resolve(process.cwd(), 'package.json'))
|
|
357
587
|
const files = []
|
|
358
588
|
for (const name of modules2) {
|
|
359
589
|
const pkgJsonPath = req.resolve(`${name}/package.json`)
|
|
@@ -406,22 +636,17 @@ export function printBuildOverview({
|
|
|
406
636
|
apiSourcePath,
|
|
407
637
|
apiOverviewFiles = [],
|
|
408
638
|
configPath,
|
|
409
|
-
isPageFiles,
|
|
410
639
|
pageFiles,
|
|
411
640
|
}) {
|
|
412
|
-
const rel = (p) => path.relative(process.cwd(), p)
|
|
413
|
-
const
|
|
414
|
-
const srcDir = path.resolve(cwd, 'src')
|
|
641
|
+
const rel = (p) => p ? path.relative(process.cwd(), p) : undefined
|
|
642
|
+
const srcDir = path.resolve(process.cwd(), 'src')
|
|
415
643
|
console.log('\n \x1b[1mBuild overview\x1b[0m')
|
|
416
|
-
console.log(' ' + '─'.repeat(50))
|
|
417
|
-
console.log(` \x1b[36mPages:\x1b[0m ${rel(pagesSourcePath)}`)
|
|
418
644
|
if (fs.existsSync(configPath)) {
|
|
419
645
|
console.log(` \x1b[36mConfig:\x1b[0m ${rel(configPath)}`)
|
|
420
646
|
}
|
|
421
|
-
console.log(` \x1b[36mAPI:\x1b[0m ${rel(apiSourcePath)}`)
|
|
422
647
|
console.log(' ' + '─'.repeat(50))
|
|
423
648
|
|
|
424
|
-
const pages =
|
|
649
|
+
const pages = pageFiles?.length
|
|
425
650
|
? pageFiles.map((f) => filePathToRoute(f, srcDir))
|
|
426
651
|
: parsePagesFromSource(pagesSourcePath)
|
|
427
652
|
if (pages.length > 0) {
|
|
@@ -456,192 +681,58 @@ export function printBuildOverview({
|
|
|
456
681
|
console.log(' ' + '─'.repeat(50) + '\n')
|
|
457
682
|
}
|
|
458
683
|
|
|
459
|
-
const WORKER_EXTERNAL_IDS = new Set([
|
|
460
|
-
...builtinModules,
|
|
461
|
-
...builtinModules.map((m) => `node:${m}`),
|
|
462
|
-
'dotenv',
|
|
463
|
-
'dotenv/config',
|
|
464
|
-
'@ossy/sdk',
|
|
465
|
-
'openai',
|
|
466
|
-
'sharp',
|
|
467
|
-
])
|
|
468
|
-
|
|
469
|
-
function isWorkerExternal(id) {
|
|
470
|
-
if (id.startsWith('.') || path.isAbsolute(id)) return false
|
|
471
|
-
if (WORKER_EXTERNAL_IDS.has(id)) return true
|
|
472
|
-
if (id.startsWith('node:')) return true
|
|
473
|
-
if (id === '@ossy/sdk' || id.startsWith('@ossy/sdk/')) return true
|
|
474
|
-
if (id === 'openai' || id.startsWith('openai/')) return true
|
|
475
|
-
if (id === 'sharp' || id.startsWith('sharp/')) return true
|
|
476
|
-
if (id === 'dotenv' || id.startsWith('dotenv/')) return true
|
|
477
|
-
return false
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
/**
|
|
481
|
-
* Worker-only bundle: discovers `*.task.js` (and optional `src/tasks.js`), writes `build/.ossy/tasks.generated.js`
|
|
482
|
-
* when needed, and emits `build/worker.js`. Invoke via `npx @ossy/cli build --worker`.
|
|
483
|
-
*/
|
|
484
|
-
export async function buildWorker(cliArgs) {
|
|
485
|
-
console.log('[@ossy/app][build][worker] Starting...')
|
|
486
|
-
|
|
487
|
-
const workerArgv = cliArgs.filter((a) => a !== '--worker')
|
|
488
|
-
const options = arg(
|
|
489
|
-
{
|
|
490
|
-
'--pages': String,
|
|
491
|
-
'--p': '--pages',
|
|
492
|
-
'--destination': String,
|
|
493
|
-
'--d': '--destination',
|
|
494
|
-
'--task-source': String,
|
|
495
|
-
},
|
|
496
|
-
{ argv: workerArgv }
|
|
497
|
-
)
|
|
498
|
-
|
|
499
|
-
const scriptDir = path.dirname(url.fileURLToPath(import.meta.url))
|
|
500
|
-
const cwd = process.cwd()
|
|
501
|
-
const pagesOpt = options['--pages'] || 'src'
|
|
502
|
-
const buildPath = path.resolve(options['--destination'] || 'build')
|
|
503
|
-
const { taskSourcePath, taskOverviewFiles, usedGenerated } = resolveTaskSource({
|
|
504
|
-
cwd,
|
|
505
|
-
pagesOpt,
|
|
506
|
-
scriptDir,
|
|
507
|
-
explicitTaskSource: options['--task-source'],
|
|
508
|
-
buildPath,
|
|
509
|
-
})
|
|
510
|
-
|
|
511
|
-
const workerEntryPath = path.resolve(scriptDir, 'worker-entry.js')
|
|
512
|
-
if (!fs.existsSync(workerEntryPath)) {
|
|
513
|
-
throw new Error(`[@ossy/app][build][worker] Missing worker entry: ${workerEntryPath}`)
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
console.log('\n \x1b[1mWorker build\x1b[0m')
|
|
517
|
-
console.log(' ' + '─'.repeat(50))
|
|
518
|
-
console.log(` \x1b[36mTasks module:\x1b[0m ${path.relative(cwd, taskSourcePath)}`)
|
|
519
|
-
if (taskOverviewFiles.length > 0) {
|
|
520
|
-
console.log(' \x1b[36mTask files:\x1b[0m')
|
|
521
|
-
taskOverviewFiles.forEach((f) => console.log(` ${path.relative(cwd, f)}`))
|
|
522
|
-
} else if (!usedGenerated) {
|
|
523
|
-
console.log(' \x1b[33m(no *.task.js; using empty task list stub)\x1b[0m')
|
|
524
|
-
}
|
|
525
|
-
console.log(' ' + '─'.repeat(50) + '\n')
|
|
526
|
-
|
|
527
|
-
const bundle = await rollup({
|
|
528
|
-
input: workerEntryPath,
|
|
529
|
-
external: isWorkerExternal,
|
|
530
|
-
plugins: [
|
|
531
|
-
ossyCleanBuildDirPlugin(buildPath),
|
|
532
|
-
replace({
|
|
533
|
-
preventAssignment: true,
|
|
534
|
-
delimiters: ['%%', '%%'],
|
|
535
|
-
'@ossy/tasks/source-file': taskSourcePath,
|
|
536
|
-
}),
|
|
537
|
-
replace({
|
|
538
|
-
preventAssignment: true,
|
|
539
|
-
'process.env.NODE_ENV': JSON.stringify('production'),
|
|
540
|
-
}),
|
|
541
|
-
json(),
|
|
542
|
-
resolveCommonJsDependencies(),
|
|
543
|
-
resolveDependencies({ preferBuiltins: true }),
|
|
544
|
-
babel({
|
|
545
|
-
babelHelpers: 'bundled',
|
|
546
|
-
presets: ['@babel/preset-env', '@babel/preset-react'],
|
|
547
|
-
}),
|
|
548
|
-
],
|
|
549
|
-
})
|
|
550
|
-
|
|
551
|
-
await bundle.write({
|
|
552
|
-
dir: buildPath,
|
|
553
|
-
entryFileNames: 'worker.js',
|
|
554
|
-
chunkFileNames: '[name]-[hash].js',
|
|
555
|
-
format: 'esm',
|
|
556
|
-
})
|
|
557
|
-
|
|
558
|
-
console.log('[@ossy/app][build][worker] Finished')
|
|
559
|
-
}
|
|
560
|
-
|
|
561
684
|
export const build = async (cliArgs) => {
|
|
562
685
|
const options = arg({
|
|
563
|
-
'--pages': String,
|
|
564
|
-
'--p': '--pages',
|
|
565
|
-
|
|
566
|
-
'--destination': String,
|
|
567
|
-
'--d': '--destination',
|
|
568
|
-
|
|
569
686
|
'--config': String,
|
|
570
687
|
'-c': '--config',
|
|
571
|
-
|
|
572
|
-
'--api-source': String,
|
|
573
|
-
'--worker': Boolean,
|
|
574
|
-
'--task-source': String,
|
|
575
688
|
}, { argv: cliArgs })
|
|
576
689
|
|
|
577
|
-
if (options['--worker']) {
|
|
578
|
-
return buildWorker(cliArgs)
|
|
579
|
-
}
|
|
580
|
-
|
|
581
690
|
console.log('[@ossy/app][build] Starting...')
|
|
582
691
|
|
|
583
692
|
|
|
584
693
|
const scriptDir = path.dirname(url.fileURLToPath(import.meta.url))
|
|
585
|
-
const
|
|
586
|
-
const
|
|
587
|
-
const buildPath = path.resolve(options['--destination'] || 'build')
|
|
588
|
-
ensureOssyGeneratedDir(buildPath)
|
|
589
|
-
const srcDir = path.resolve(pagesOpt)
|
|
694
|
+
const buildPath = path.resolve('build')
|
|
695
|
+
const srcDir = path.resolve('src')
|
|
590
696
|
const configPath = path.resolve(options['--config'] || 'src/config.js');
|
|
591
697
|
const pageFiles = discoverPageFiles(srcDir)
|
|
592
|
-
const modulePageFiles = await discoverModulePageFiles({
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
)
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
} else {
|
|
609
|
-
throw new Error(`[@ossy/app][build] No pages found. Create *.page.jsx files in src/, or src/pages.jsx`);
|
|
610
|
-
}
|
|
698
|
+
const modulePageFiles = await discoverModulePageFiles({ configPath })
|
|
699
|
+
|
|
700
|
+
resetOssyBuildDir(buildPath)
|
|
701
|
+
|
|
702
|
+
writeResourceTemplatesBarrelIfPresent({ cwd: process.cwd(), log: true })
|
|
703
|
+
|
|
704
|
+
const pagesGeneratedPath = path.join(ossyGeneratedDir(buildPath), OSSY_GEN_PAGES_BASENAME)
|
|
705
|
+
|
|
706
|
+
const allPageFiles = [...pageFiles, ...modulePageFiles]
|
|
707
|
+
fs.writeFileSync(
|
|
708
|
+
pagesGeneratedPath,
|
|
709
|
+
generatePagesModule(allPageFiles, srcDir, pagesGeneratedPath)
|
|
710
|
+
)
|
|
711
|
+
const ossyDir = ossyGeneratedDir(buildPath)
|
|
712
|
+
writePageHydrateStubs(allPageFiles, srcDir, ossyDir)
|
|
713
|
+
const clientHydrateInput = buildClientHydrateInput(allPageFiles, srcDir, ossyDir)
|
|
611
714
|
|
|
612
715
|
const {
|
|
613
716
|
apiSourcePath: resolvedApi,
|
|
614
717
|
apiOverviewFiles,
|
|
615
718
|
} = resolveApiSource({
|
|
616
|
-
|
|
617
|
-
pagesOpt,
|
|
618
|
-
scriptDir,
|
|
619
|
-
explicitApiSource: options['--api-source'],
|
|
719
|
+
srcDir,
|
|
620
720
|
buildPath,
|
|
621
721
|
})
|
|
622
722
|
let apiSourcePath = resolvedApi
|
|
623
|
-
let middlewareSourcePath = path.resolve(
|
|
723
|
+
let middlewareSourcePath = path.resolve('src/middleware.js');
|
|
624
724
|
const publicDir = path.resolve('public')
|
|
625
725
|
|
|
626
|
-
const inputClient = path.resolve(scriptDir, 'client.js')
|
|
627
726
|
const inputServer = path.resolve(scriptDir, 'server.js')
|
|
628
727
|
|
|
629
|
-
const inputFiles = [inputClient, inputServer]
|
|
630
|
-
|
|
631
|
-
const appEntryPath = path.resolve(scriptDir, 'default-app.jsx')
|
|
632
728
|
printBuildOverview({
|
|
633
|
-
pagesSourcePath:
|
|
729
|
+
pagesSourcePath: pagesGeneratedPath,
|
|
634
730
|
apiSourcePath,
|
|
635
731
|
apiOverviewFiles,
|
|
636
732
|
configPath,
|
|
637
|
-
|
|
638
|
-
pageFiles: isPageFiles ? pageFiles : [],
|
|
733
|
+
pageFiles,
|
|
639
734
|
});
|
|
640
735
|
|
|
641
|
-
if (!fs.existsSync(apiSourcePath)) {
|
|
642
|
-
apiSourcePath = path.resolve(scriptDir, 'api.js')
|
|
643
|
-
}
|
|
644
|
-
|
|
645
736
|
if (!fs.existsSync(middlewareSourcePath)) {
|
|
646
737
|
middlewareSourcePath = path.resolve(scriptDir, 'middleware.js')
|
|
647
738
|
}
|
|
@@ -650,88 +741,64 @@ export const build = async (cliArgs) => {
|
|
|
650
741
|
? configPath
|
|
651
742
|
: path.resolve(scriptDir, 'default-config.js')
|
|
652
743
|
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
// preserveModules: true,
|
|
712
|
-
entryFileNames: ({ name }) => {
|
|
713
|
-
|
|
714
|
-
const serverFileNames = ['server', 'api', 'middleware']
|
|
715
|
-
|
|
716
|
-
if (serverFileNames.includes(name)) {
|
|
717
|
-
return '[name].js'
|
|
718
|
-
} else if (name === 'client') {
|
|
719
|
-
return 'public/static/main.js'
|
|
720
|
-
} else if (name === 'config') {
|
|
721
|
-
return 'public/static/[name].js'
|
|
722
|
-
} else {
|
|
723
|
-
return 'public/static/[name].js'
|
|
724
|
-
}
|
|
725
|
-
},
|
|
726
|
-
chunkFileNames: 'public/static/[name]-[hash].js',
|
|
727
|
-
format: 'esm',
|
|
728
|
-
}
|
|
729
|
-
];
|
|
730
|
-
|
|
731
|
-
const bundle = await rollup(inputOptions);
|
|
732
|
-
|
|
733
|
-
for (const options of outputOptions) {
|
|
734
|
-
await bundle.write(options);
|
|
744
|
+
const sharedPluginOpts = {
|
|
745
|
+
pagesGeneratedPath,
|
|
746
|
+
apiSourcePath,
|
|
747
|
+
middlewareSourcePath,
|
|
748
|
+
configSourcePath,
|
|
749
|
+
nodeEnv: 'production',
|
|
750
|
+
buildPath,
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const serverPlugins = createOssyRollupPlugins({
|
|
754
|
+
...sharedPluginOpts,
|
|
755
|
+
nodeExternals: true,
|
|
756
|
+
preferBuiltins: true,
|
|
757
|
+
copyPublicFrom: publicDir,
|
|
758
|
+
})
|
|
759
|
+
|
|
760
|
+
const serverBundle = await rollup({
|
|
761
|
+
input: { server: inputServer },
|
|
762
|
+
plugins: serverPlugins,
|
|
763
|
+
external: ossyServerExternal,
|
|
764
|
+
})
|
|
765
|
+
await serverBundle.write({
|
|
766
|
+
dir: buildPath,
|
|
767
|
+
format: 'esm',
|
|
768
|
+
preserveModules: true,
|
|
769
|
+
preserveModulesRoot: path.dirname(inputServer),
|
|
770
|
+
entryFileNames ({ name }) {
|
|
771
|
+
return name === 'server' ? 'server.js' : '[name].js'
|
|
772
|
+
},
|
|
773
|
+
assetFileNames: '[name][extname]',
|
|
774
|
+
})
|
|
775
|
+
await serverBundle.close()
|
|
776
|
+
|
|
777
|
+
const clientPlugins = createOssyRollupPlugins({
|
|
778
|
+
...sharedPluginOpts,
|
|
779
|
+
nodeExternals: false,
|
|
780
|
+
preferBuiltins: false,
|
|
781
|
+
})
|
|
782
|
+
|
|
783
|
+
if (Object.keys(clientHydrateInput).length > 0) {
|
|
784
|
+
const clientBundle = await rollup({
|
|
785
|
+
input: clientHydrateInput,
|
|
786
|
+
plugins: clientPlugins,
|
|
787
|
+
})
|
|
788
|
+
await clientBundle.write({
|
|
789
|
+
dir: buildPath,
|
|
790
|
+
format: 'esm',
|
|
791
|
+
entryFileNames ({ name }) {
|
|
792
|
+
if (name.startsWith('hydrate__')) {
|
|
793
|
+
const pageId = name.slice('hydrate__'.length)
|
|
794
|
+
return `public/static/hydrate-${pageId}.js`
|
|
795
|
+
}
|
|
796
|
+
return 'public/static/[name].js'
|
|
797
|
+
},
|
|
798
|
+
chunkFileNames: 'public/static/[name]-[hash].js',
|
|
799
|
+
plugins: [minifyBrowserStaticChunks()],
|
|
800
|
+
})
|
|
801
|
+
await clientBundle.close()
|
|
735
802
|
}
|
|
736
803
|
|
|
737
804
|
ensureBuildStubs(buildPath)
|