@wix/origami-site-express 1.0.6 → 1.0.7
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/package.json +3 -3
- package/src/server/build.js +22 -11
- package/src/server/core.js +7 -0
- package/src/server/currentSite.js +5 -1
- package/src/server/dev.js +40 -36
- package/src/server/prod.js +22 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/origami-site-express",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Run an exported Wix origami site on Node with Express and Vite.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@vitejs/plugin-react": "^6.1.1",
|
|
35
35
|
"express": "^5.2.1",
|
|
36
36
|
"vite": "^8.1.0",
|
|
37
|
-
"@wix/origami-bi": "0.
|
|
37
|
+
"@wix/origami-bi": "0.10.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@wix/origami-site": "^1.0.0"
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
]
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
|
-
"falconPackageHash": "
|
|
70
|
+
"falconPackageHash": "7b2399e27e28ffe108d074ccf265385d853659a4d40b03dfef29d720"
|
|
71
71
|
}
|
package/src/server/build.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { trackStep } from '@wix/origami-bi'
|
|
1
2
|
import { rm } from 'node:fs/promises'
|
|
2
3
|
import { join } from 'node:path'
|
|
3
4
|
import { build as viteBuild } from 'vite'
|
|
@@ -11,20 +12,30 @@ export async function build({ root }) {
|
|
|
11
12
|
const { readSiteTree, writeBuiltTable } = await importCoreServer(root)
|
|
12
13
|
const { origamiVitePlugins } = await importCoreBuild(root)
|
|
13
14
|
// One read: the browser's page list and the server's routes come from the same table.
|
|
14
|
-
const table = await
|
|
15
|
+
const table = await trackStep('step', 'read_site_tree', {}, async row => {
|
|
16
|
+
const table = await readSiteTree(root)
|
|
17
|
+
row?.update({ step_details: { pages: table.pages.length, routes: table.pages.length + table.routers.length } })
|
|
18
|
+
return table
|
|
19
|
+
})
|
|
15
20
|
// A build that fails after Vite replaced dist/client must not leave the old table for start to accept.
|
|
16
21
|
await rm(join(root, 'dist/server/origami-routes.json'), { force: true })
|
|
17
22
|
const siteTree = { table: async () => table, onTableChange: () => () => {} }
|
|
18
|
-
await
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
await trackStep('step', 'vite_build', {}, async row => {
|
|
24
|
+
const result = await viteBuild({
|
|
25
|
+
root, configFile: false, mode: 'production',
|
|
26
|
+
plugins: [...origamiVitePlugins({ root, siteTree }), react()],
|
|
27
|
+
// Vite 8 minifies CSS with lightningcss, which rejects rules browsers just ignore, like the Menu CSS of
|
|
28
|
+
// @wix/editor-react-components: `@property --wix-menu-dropdown-anchor` without the required initial-value.
|
|
29
|
+
// errorRecovery drops such rules with a warning. It applies to all CSS: a site's own invalid CSS also builds, with
|
|
30
|
+
// the broken rules dropped and warned about. Remove it once that package ships valid CSS.
|
|
31
|
+
css: { lightningcss: { errorRecovery: true } },
|
|
32
|
+
build: { outDir: 'dist/client', emptyOutDir: true, manifest: true, rolldownOptions: { input: CLIENT_ENTRY } },
|
|
33
|
+
})
|
|
34
|
+
const outputs = Array.isArray(result) ? result : 'output' in result ? [result] : []
|
|
35
|
+
const modules = new Set(outputs.flatMap(output => output.output.flatMap(chunk => chunk.type === 'chunk' ? Object.keys(chunk.modules) : [])))
|
|
36
|
+
row?.update({ step_details: { modules: modules.size } })
|
|
27
37
|
})
|
|
28
38
|
// Last, so a failed client build never leaves a table that start would accept.
|
|
29
|
-
await
|
|
39
|
+
await trackStep('step', 'write_route_table', { step_details: { routes: table.pages.length + table.routers.length } },
|
|
40
|
+
() => writeBuiltTable(join(root, 'dist/server'), table))
|
|
30
41
|
}
|
package/src/server/core.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises'
|
|
2
|
+
import { dirname, resolve } from 'node:path'
|
|
1
3
|
import { createRequire } from 'node:module'
|
|
2
4
|
import { join } from 'node:path'
|
|
3
5
|
import { pathToFileURL } from 'node:url'
|
|
@@ -10,6 +12,11 @@ export function coreEntry(root, entry) {
|
|
|
10
12
|
|
|
11
13
|
/** @param {string} root @returns {Promise<typeof import('@wix/origami-site/server')>} */
|
|
12
14
|
export const importCoreServer = root => import(pathToFileURL(coreEntry(root, 'server')).href)
|
|
15
|
+
|
|
16
|
+
/** @param {string} root */
|
|
17
|
+
export async function coreVersion(root) {
|
|
18
|
+
return JSON.parse(await readFile(resolve(dirname(coreEntry(root, 'server')), '../../package.json'), 'utf8')).version
|
|
19
|
+
}
|
|
13
20
|
/** @param {string} root @returns {Promise<typeof import('@wix/origami-site/node')>} */
|
|
14
21
|
export const importCoreNode = root => import(pathToFileURL(coreEntry(root, 'node')).href)
|
|
15
22
|
/** @param {string} root @returns {Promise<typeof import('@wix/origami-site/build')>} */
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
2
|
+
|
|
1
3
|
/** @typedef {import('@wix/origami-site/types').OrigamiSite} OrigamiSite */
|
|
2
4
|
/** @typedef {Awaited<ReturnType<OrigamiSite['table']>>} SiteRouteTable */
|
|
3
5
|
|
|
@@ -8,6 +10,8 @@
|
|
|
8
10
|
* @returns {{ currentSite: () => Promise<OrigamiSite>, site: () => OrigamiSite | undefined, fileChanged: (path: string) => void,
|
|
9
11
|
* siteTree: { table: () => ReturnType<OrigamiSite['table']>, onTableChange: OrigamiSite['onTableChange'] } }} */
|
|
10
12
|
export function createCurrentSite({ loadCore, create }) {
|
|
13
|
+
// Core reloads create the site lazily from requests outside startup; retain the workspace run for its counters.
|
|
14
|
+
const inContext = AsyncLocalStorage.snapshot()
|
|
11
15
|
/** @type {{ core?: unknown, site?: OrigamiSite }} */
|
|
12
16
|
let current = {}
|
|
13
17
|
/** @type {{ core: unknown, site: Promise<OrigamiSite> } | undefined} */
|
|
@@ -27,7 +31,7 @@ export function createCurrentSite({ loadCore, create }) {
|
|
|
27
31
|
const site = (async () => {
|
|
28
32
|
await current.site?.close()
|
|
29
33
|
current = {}
|
|
30
|
-
const created = await create(core)
|
|
34
|
+
const created = await inContext(() => create(core))
|
|
31
35
|
current = { core, site: created }
|
|
32
36
|
for (const path of changedWhileCreating) created.fileChanged(path)
|
|
33
37
|
changedWhileCreating.clear()
|
package/src/server/dev.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { trackStep } from '@wix/origami-bi'
|
|
1
2
|
import { createServer as createHttpServer } from 'node:http'
|
|
2
3
|
import express from 'express'
|
|
3
4
|
import { createServer as createViteServer, isRunnableDevEnvironment, loadEnv } from 'vite'
|
|
4
5
|
import react from '@vitejs/plugin-react'
|
|
5
|
-
import { coreEntry, importCoreBuild, importCoreNode, importCoreServer } from './core.js'
|
|
6
|
+
import { coreEntry, importCoreBuild, importCoreNode, importCoreServer, coreVersion } from './core.js'
|
|
6
7
|
import { catchAll } from './bridge.js'
|
|
7
8
|
import { pageHtml, plantEmbeds } from './shell.js'
|
|
8
9
|
import { host, port, siteOptions } from './env.js'
|
|
@@ -12,40 +13,43 @@ import { createCurrentSite } from './currentSite.js'
|
|
|
12
13
|
/** `origami-site-express dev`: Vite in middleware mode, Express, and the core in dev mode, all live.
|
|
13
14
|
* @param {{ root: string }} options */
|
|
14
15
|
export async function dev({ root }) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
return trackStep('step', 'dev_start', {}, async row => {
|
|
17
|
+
const env = loadEnv('development', root, ['VITE_', 'ORIGAMI_', 'PORT', 'HOST'])
|
|
18
|
+
const { errorResponse, serializePageData } = await importCoreServer(root)
|
|
19
|
+
const { origamiVitePlugins } = await importCoreBuild(root)
|
|
20
|
+
const node = await importCoreNode(root)
|
|
21
|
+
row?.update({ step_details: { core_version: await coreVersion(root), port_kind: env.PORT ? 'custom' : 'default' } })
|
|
22
|
+
const serverEntry = coreEntry(root, 'server')
|
|
23
|
+
const { currentSite, site, fileChanged, siteTree } = createCurrentSite({
|
|
24
|
+
loadCore: () => isRunnableDevEnvironment(vite.environments.ssr) ? vite.environments.ssr.runner.import(serverEntry) : vite.ssrLoadModule(serverEntry),
|
|
25
|
+
create: core => core.createSite({ root, mode: 'dev', ...siteOptions(env) }),
|
|
26
|
+
})
|
|
27
|
+
const httpServer = createHttpServer()
|
|
28
|
+
const vite = await createViteServer({
|
|
29
|
+
root, configFile: false, appType: 'custom', mode: 'development',
|
|
30
|
+
// HMR on the site's own port: without it Vite opens a second port, and two sites would collide.
|
|
31
|
+
server: { middlewareMode: true, ws: { server: httpServer } },
|
|
32
|
+
// React after ours: origami:jsx-checks is enforce 'pre', so our Babel checks see the source first.
|
|
33
|
+
plugins: [...origamiVitePlugins({ root, siteTree }), react()],
|
|
34
|
+
// No index.html lists the pages, so Vite can't scan them up front. Without this, the first visit to a lazily
|
|
35
|
+
// loaded page finds new dependencies, re-optimizes them and reloads, and the page briefly runs two Reacts.
|
|
36
|
+
optimizeDeps: { entries: ['src/site/_generated/client.jsx', 'src/pages/**/_generated/index.jsx'] },
|
|
37
|
+
// The core's CommonJS dependencies go to Node. Linked (as in this repo), Vite would inline them as ESM,
|
|
38
|
+
// where `exports` is not defined. Add any CommonJS package the core starts importing.
|
|
39
|
+
ssr: { external: ['@wix/origami-types', '@wix/origami-bi'] },
|
|
40
|
+
})
|
|
41
|
+
vite.watcher.on('all', (_event, path) => fileChanged(path))
|
|
42
|
+
const app = express()
|
|
43
|
+
app.disable('x-powered-by')
|
|
44
|
+
app.use(vite.middlewares)
|
|
45
|
+
app.use(catchAll({
|
|
46
|
+
mode: 'dev', errorResponse, node, getSite: currentSite,
|
|
47
|
+
renderPage: async (resolution, request) => plantEmbeds(await vite.transformIndexHtml(new URL(request.url).pathname, pageHtml(resolution, {
|
|
48
|
+
serializePageData,
|
|
49
|
+
body: '<script type="module" src="/src/site/_generated/client.jsx"></script>',
|
|
50
|
+
})), resolution.embeds),
|
|
51
|
+
}))
|
|
52
|
+
httpServer.on('request', app)
|
|
53
|
+
await serve(httpServer, { port: port(env), host: host('dev', env), onClose: async () => { await vite.close(); await site()?.close() } })
|
|
23
54
|
})
|
|
24
|
-
const httpServer = createHttpServer()
|
|
25
|
-
const vite = await createViteServer({
|
|
26
|
-
root, configFile: false, appType: 'custom', mode: 'development',
|
|
27
|
-
// HMR on the site's own port: without it Vite opens a second port, and two sites would collide.
|
|
28
|
-
server: { middlewareMode: true, ws: { server: httpServer } },
|
|
29
|
-
// React after ours: origami:jsx-checks is enforce 'pre', so our Babel checks see the source first.
|
|
30
|
-
plugins: [...origamiVitePlugins({ root, siteTree }), react()],
|
|
31
|
-
// No index.html lists the pages, so Vite can't scan them up front. Without this, the first visit to a lazily
|
|
32
|
-
// loaded page finds new dependencies, re-optimizes them and reloads, and the page briefly runs two Reacts.
|
|
33
|
-
optimizeDeps: { entries: ['src/site/_generated/client.jsx', 'src/pages/**/_generated/index.jsx'] },
|
|
34
|
-
// The core's CommonJS dependencies go to Node. Linked (as in this repo), Vite would inline them as ESM,
|
|
35
|
-
// where `exports` is not defined. Add any CommonJS package the core starts importing.
|
|
36
|
-
ssr: { external: ['@wix/origami-types'] },
|
|
37
|
-
})
|
|
38
|
-
vite.watcher.on('all', (_event, path) => fileChanged(path))
|
|
39
|
-
const app = express()
|
|
40
|
-
app.disable('x-powered-by')
|
|
41
|
-
app.use(vite.middlewares)
|
|
42
|
-
app.use(catchAll({
|
|
43
|
-
mode: 'dev', errorResponse, node, getSite: currentSite,
|
|
44
|
-
renderPage: async (resolution, request) => plantEmbeds(await vite.transformIndexHtml(new URL(request.url).pathname, pageHtml(resolution, {
|
|
45
|
-
serializePageData,
|
|
46
|
-
body: '<script type="module" src="/src/site/_generated/client.jsx"></script>',
|
|
47
|
-
})), resolution.embeds),
|
|
48
|
-
}))
|
|
49
|
-
httpServer.on('request', app)
|
|
50
|
-
await serve(httpServer, { port: port(env), host: host('dev', env), onClose: async () => { await vite.close(); await site()?.close() } })
|
|
51
55
|
}
|
package/src/server/prod.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { trackStep } from '@wix/origami-bi'
|
|
1
2
|
import { existsSync } from 'node:fs'
|
|
2
3
|
import { createServer } from 'node:http'
|
|
3
4
|
import { join } from 'node:path'
|
|
4
5
|
import express from 'express'
|
|
5
|
-
import { importCoreNode, importCoreServer } from './core.js'
|
|
6
|
+
import { importCoreNode, importCoreServer, coreVersion } from './core.js'
|
|
6
7
|
import { catchAll } from './bridge.js'
|
|
7
8
|
import { pageHtml, plantEmbeds } from './shell.js'
|
|
8
9
|
import { readManifest } from './manifest.js'
|
|
@@ -12,21 +13,24 @@ import { serve } from './lifecycle.js'
|
|
|
12
13
|
/** `origami-site-express start`: the build output and the core in prod mode. No Vite at runtime.
|
|
13
14
|
* @param {{ root: string }} options */
|
|
14
15
|
export async function start({ root }) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
16
|
+
return trackStep('step', 'start_start', {}, async row => {
|
|
17
|
+
// Values already in process.env win, so the host's environment overrides .env.
|
|
18
|
+
if (existsSync(join(root, '.env'))) process.loadEnvFile(join(root, '.env'))
|
|
19
|
+
const { readBuiltTable, createSite, errorResponse, serializePageData } = await importCoreServer(root)
|
|
20
|
+
const node = await importCoreNode(root)
|
|
21
|
+
row?.update({ step_details: { core_version: await coreVersion(root), port_kind: process.env.PORT ? 'custom' : 'default' } })
|
|
22
|
+
const table = await readBuiltTable(join(root, 'dist/server'))
|
|
23
|
+
const site = await createSite({ root, mode: 'prod', table, ...siteOptions(process.env) })
|
|
24
|
+
const assetsFor = readManifest(root)
|
|
25
|
+
const app = express()
|
|
26
|
+
app.disable('x-powered-by')
|
|
27
|
+
// index: false keeps / from serving a stray index.html; redirect: false lets /somedir reach the core.
|
|
28
|
+
app.use('/assets', express.static(join(root, 'dist/client/assets'), { index: false, redirect: false, maxAge: '1y', immutable: true }))
|
|
29
|
+
app.use(express.static(join(root, 'dist/client'), { index: false, redirect: false }))
|
|
30
|
+
app.use(catchAll({
|
|
31
|
+
mode: 'prod', errorResponse, node, getSite: async () => site,
|
|
32
|
+
renderPage: async resolution => plantEmbeds(pageHtml(resolution, { serializePageData, ...assetsFor(resolution.page) }), resolution.embeds),
|
|
33
|
+
}))
|
|
34
|
+
await serve(createServer(app), { port: port(), onClose: () => site.close() })
|
|
35
|
+
})
|
|
32
36
|
}
|