one 1.25.12 → 1.25.13
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/dist/cjs/cli/build.cjs +8 -161
- package/dist/cjs/cli/build.native.js +8 -204
- package/dist/cjs/cli/build.native.js.map +1 -1
- package/dist/cjs/createDevHandleRequest.cjs +151 -0
- package/dist/cjs/createDevHandleRequest.native.js +194 -0
- package/dist/cjs/createDevHandleRequest.native.js.map +1 -0
- package/dist/cjs/createHandleRequest.cjs +1 -123
- package/dist/cjs/createHandleRequest.native.js +1 -163
- package/dist/cjs/createHandleRequest.native.js.map +1 -1
- package/dist/cjs/createHandleRequest.test.cjs +25 -25
- package/dist/cjs/createHandleRequest.test.native.js +25 -25
- package/dist/cjs/createHandleRequest.test.native.js.map +1 -1
- package/dist/cjs/layouts/Drawer.cjs +0 -2
- package/dist/cjs/layouts/Drawer.native.js.map +1 -1
- package/dist/cjs/layouts/Tabs.cjs +0 -2
- package/dist/cjs/layouts/Tabs.native.js.map +1 -1
- package/dist/cjs/router/web/WebStackView.cjs +0 -2
- package/dist/cjs/router/web/WebStackView.native.js +0 -2
- package/dist/cjs/router/web/WebStackView.native.js.map +1 -1
- package/dist/cjs/serve-worker.cjs +3 -2
- package/dist/cjs/serve-worker.native.js +3 -2
- package/dist/cjs/serve-worker.native.js.map +1 -1
- package/dist/cjs/server/workerHandler.cjs +12 -7
- package/dist/cjs/server/workerHandler.native.js +13 -8
- package/dist/cjs/server/workerHandler.native.js.map +1 -1
- package/dist/cjs/server/workerHandler.test.cjs +25 -0
- package/dist/cjs/server/workerHandler.test.native.js +25 -0
- package/dist/cjs/server/workerHandler.test.native.js.map +1 -1
- package/dist/cjs/vite/cloudflareWranglerConfig.cjs +247 -0
- package/dist/cjs/vite/cloudflareWranglerConfig.native.js +392 -0
- package/dist/cjs/vite/cloudflareWranglerConfig.native.js.map +1 -0
- package/dist/cjs/vite/cloudflareWranglerConfig.test.cjs +116 -0
- package/dist/cjs/vite/cloudflareWranglerConfig.test.native.js +120 -0
- package/dist/cjs/vite/cloudflareWranglerConfig.test.native.js.map +1 -0
- package/dist/cjs/vite/one.cjs +4 -2
- package/dist/cjs/vite/one.native.js +8 -6
- package/dist/cjs/vite/one.native.js.map +1 -1
- package/dist/cjs/vite/plugins/SSRCSSPlugin.cjs +10 -1
- package/dist/cjs/vite/plugins/SSRCSSPlugin.native.js +52 -3
- package/dist/cjs/vite/plugins/SSRCSSPlugin.native.js.map +1 -1
- package/dist/cjs/vite/plugins/environmentGuardPlugin.cjs +1 -1
- package/dist/cjs/vite/plugins/environmentGuardPlugin.native.js +1 -1
- package/dist/cjs/vite/plugins/environmentGuardPlugin.native.js.map +1 -1
- package/dist/cjs/vite/plugins/environmentGuardPlugin.test.cjs +4 -0
- package/dist/cjs/vite/plugins/environmentGuardPlugin.test.native.js +4 -0
- package/dist/cjs/vite/plugins/environmentGuardPlugin.test.native.js.map +1 -1
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.cjs +14 -5
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.native.js +15 -5
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.native.js.map +1 -1
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.test.cjs +47 -0
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.test.native.js +47 -0
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.test.native.js.map +1 -1
- package/dist/cjs/vite/plugins/virtualEntryPlugin.cjs +2 -1
- package/dist/cjs/vite/plugins/virtualEntryPlugin.native.js +2 -1
- package/dist/cjs/vite/plugins/virtualEntryPlugin.native.js.map +1 -1
- package/dist/cjs/vite/plugins/virtualEntryPlugin.test.cjs +12 -0
- package/dist/cjs/vite/plugins/virtualEntryPlugin.test.native.js +12 -0
- package/dist/cjs/vite/plugins/virtualEntryPlugin.test.native.js.map +1 -1
- package/dist/cjs/vite/plugins/workerdDevPlugin.cjs +377 -0
- package/dist/cjs/vite/plugins/workerdDevPlugin.native.js +493 -0
- package/dist/cjs/vite/plugins/workerdDevPlugin.native.js.map +1 -0
- package/dist/cjs/vite/plugins/workerdDevPlugin.test.cjs +42 -0
- package/dist/cjs/vite/plugins/workerdDevPlugin.test.native.js +45 -0
- package/dist/cjs/vite/plugins/workerdDevPlugin.test.native.js.map +1 -0
- package/dist/esm/cli/build.mjs +1 -154
- package/dist/esm/cli/build.mjs.map +1 -1
- package/dist/esm/cli/build.native.js +1 -197
- package/dist/esm/cli/build.native.js.map +1 -1
- package/dist/esm/createDevHandleRequest.mjs +126 -0
- package/dist/esm/createDevHandleRequest.mjs.map +1 -0
- package/dist/esm/createDevHandleRequest.native.js +166 -0
- package/dist/esm/createDevHandleRequest.native.js.map +1 -0
- package/dist/esm/createHandleRequest.mjs +1 -123
- package/dist/esm/createHandleRequest.mjs.map +1 -1
- package/dist/esm/createHandleRequest.native.js +1 -163
- package/dist/esm/createHandleRequest.native.js.map +1 -1
- package/dist/esm/createHandleRequest.test.mjs +1 -1
- package/dist/esm/createHandleRequest.test.native.js +1 -1
- package/dist/esm/fork/useLinking.native.test.mjs +1 -1
- package/dist/esm/getURL.native.test.mjs +1 -1
- package/dist/esm/layouts/Drawer.mjs +0 -2
- package/dist/esm/layouts/Drawer.mjs.map +1 -1
- package/dist/esm/layouts/Drawer.native.js.map +1 -1
- package/dist/esm/layouts/Tabs.mjs +0 -2
- package/dist/esm/layouts/Tabs.mjs.map +1 -1
- package/dist/esm/layouts/Tabs.native.js.map +1 -1
- package/dist/esm/router/hmrImport.native.test.mjs +1 -1
- package/dist/esm/router/routeHmr.native.test.mjs +1 -1
- package/dist/esm/router/routeHmr.native.test.mjs.map +1 -1
- package/dist/esm/router/web/WebStackView.mjs +0 -2
- package/dist/esm/router/web/WebStackView.mjs.map +1 -1
- package/dist/esm/router/web/WebStackView.native.js +0 -2
- package/dist/esm/router/web/WebStackView.native.js.map +1 -1
- package/dist/esm/serve-worker.mjs +3 -2
- package/dist/esm/serve-worker.mjs.map +1 -1
- package/dist/esm/serve-worker.native.js +3 -2
- package/dist/esm/serve-worker.native.js.map +1 -1
- package/dist/esm/server/workerHandler.mjs +12 -7
- package/dist/esm/server/workerHandler.mjs.map +1 -1
- package/dist/esm/server/workerHandler.native.js +13 -8
- package/dist/esm/server/workerHandler.native.js.map +1 -1
- package/dist/esm/server/workerHandler.test.mjs +25 -0
- package/dist/esm/server/workerHandler.test.mjs.map +1 -1
- package/dist/esm/server/workerHandler.test.native.js +25 -0
- package/dist/esm/server/workerHandler.test.native.js.map +1 -1
- package/dist/esm/vite/cloudflareWranglerConfig.mjs +202 -0
- package/dist/esm/vite/cloudflareWranglerConfig.mjs.map +1 -0
- package/dist/esm/vite/cloudflareWranglerConfig.native.js +344 -0
- package/dist/esm/vite/cloudflareWranglerConfig.native.js.map +1 -0
- package/dist/esm/vite/cloudflareWranglerConfig.test.mjs +117 -0
- package/dist/esm/vite/cloudflareWranglerConfig.test.mjs.map +1 -0
- package/dist/esm/vite/cloudflareWranglerConfig.test.native.js +118 -0
- package/dist/esm/vite/cloudflareWranglerConfig.test.native.js.map +1 -0
- package/dist/esm/vite/one.mjs +4 -2
- package/dist/esm/vite/one.mjs.map +1 -1
- package/dist/esm/vite/one.native.js +8 -6
- package/dist/esm/vite/one.native.js.map +1 -1
- package/dist/esm/vite/plugins/SSRCSSPlugin.mjs +10 -1
- package/dist/esm/vite/plugins/SSRCSSPlugin.mjs.map +1 -1
- package/dist/esm/vite/plugins/SSRCSSPlugin.native.js +52 -3
- package/dist/esm/vite/plugins/SSRCSSPlugin.native.js.map +1 -1
- package/dist/esm/vite/plugins/environmentGuardPlugin.mjs +1 -1
- package/dist/esm/vite/plugins/environmentGuardPlugin.mjs.map +1 -1
- package/dist/esm/vite/plugins/environmentGuardPlugin.native.js +1 -1
- package/dist/esm/vite/plugins/environmentGuardPlugin.native.js.map +1 -1
- package/dist/esm/vite/plugins/environmentGuardPlugin.test.mjs +4 -0
- package/dist/esm/vite/plugins/environmentGuardPlugin.test.mjs.map +1 -1
- package/dist/esm/vite/plugins/environmentGuardPlugin.test.native.js +4 -0
- package/dist/esm/vite/plugins/environmentGuardPlugin.test.native.js.map +1 -1
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.mjs +13 -4
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.mjs.map +1 -1
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.native.js +14 -4
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.native.js.map +1 -1
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.test.mjs +47 -0
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.test.mjs.map +1 -1
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.test.native.js +47 -0
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.test.native.js.map +1 -1
- package/dist/esm/vite/plugins/virtualEntryPlugin.mjs +2 -1
- package/dist/esm/vite/plugins/virtualEntryPlugin.mjs.map +1 -1
- package/dist/esm/vite/plugins/virtualEntryPlugin.native.js +2 -1
- package/dist/esm/vite/plugins/virtualEntryPlugin.native.js.map +1 -1
- package/dist/esm/vite/plugins/virtualEntryPlugin.test.mjs +12 -0
- package/dist/esm/vite/plugins/virtualEntryPlugin.test.mjs.map +1 -1
- package/dist/esm/vite/plugins/virtualEntryPlugin.test.native.js +12 -0
- package/dist/esm/vite/plugins/virtualEntryPlugin.test.native.js.map +1 -1
- package/dist/esm/vite/plugins/workerdDevPlugin.mjs +349 -0
- package/dist/esm/vite/plugins/workerdDevPlugin.mjs.map +1 -0
- package/dist/esm/vite/plugins/workerdDevPlugin.native.js +462 -0
- package/dist/esm/vite/plugins/workerdDevPlugin.native.js.map +1 -0
- package/dist/esm/vite/plugins/workerdDevPlugin.test.mjs +43 -0
- package/dist/esm/vite/plugins/workerdDevPlugin.test.mjs.map +1 -0
- package/dist/esm/vite/plugins/workerdDevPlugin.test.native.js +43 -0
- package/dist/esm/vite/plugins/workerdDevPlugin.test.native.js.map +1 -0
- package/package.json +9 -9
- package/src/cli/build.ts +7 -192
- package/src/createDevHandleRequest.ts +186 -0
- package/src/createHandleRequest.test.ts +1 -1
- package/src/createHandleRequest.ts +2 -172
- package/src/layouts/Drawer.web.tsx +0 -3
- package/src/layouts/Tabs.web.tsx +0 -2
- package/src/router/web/WebStackView.tsx +0 -3
- package/src/serve-worker.ts +6 -1
- package/src/server/workerHandler.test.ts +23 -0
- package/src/server/workerHandler.ts +15 -7
- package/src/vite/cloudflareWranglerConfig.test.ts +101 -0
- package/src/vite/cloudflareWranglerConfig.ts +248 -0
- package/src/vite/one.ts +11 -1
- package/src/vite/plugins/SSRCSSPlugin.ts +17 -2
- package/src/vite/plugins/environmentGuardPlugin.test.ts +5 -0
- package/src/vite/plugins/environmentGuardPlugin.ts +2 -2
- package/src/vite/plugins/fileSystemRouterPlugin.test.ts +44 -0
- package/src/vite/plugins/fileSystemRouterPlugin.tsx +20 -8
- package/src/vite/plugins/virtualEntryPlugin.test.ts +13 -0
- package/src/vite/plugins/virtualEntryPlugin.ts +3 -1
- package/src/vite/plugins/workerdDevPlugin.test.ts +44 -0
- package/src/vite/plugins/workerdDevPlugin.ts +408 -0
- package/types/cli/build.d.ts.map +1 -1
- package/types/createDevHandleRequest.d.ts +10 -0
- package/types/createDevHandleRequest.d.ts.map +1 -0
- package/types/createHandleRequest.d.ts +2 -9
- package/types/createHandleRequest.d.ts.map +1 -1
- package/types/layouts/Drawer.web.d.ts.map +1 -1
- package/types/layouts/Tabs.web.d.ts.map +1 -1
- package/types/router/web/WebStackView.d.ts.map +1 -1
- package/types/serve-worker.d.ts +3 -1
- package/types/serve-worker.d.ts.map +1 -1
- package/types/server/workerHandler.d.ts +1 -0
- package/types/server/workerHandler.d.ts.map +1 -1
- package/types/vite/cloudflareWranglerConfig.d.ts +21 -0
- package/types/vite/cloudflareWranglerConfig.d.ts.map +1 -0
- package/types/vite/cloudflareWranglerConfig.test.d.ts +2 -0
- package/types/vite/cloudflareWranglerConfig.test.d.ts.map +1 -0
- package/types/vite/one.d.ts.map +1 -1
- package/types/vite/plugins/SSRCSSPlugin.d.ts.map +1 -1
- package/types/vite/plugins/fileSystemRouterPlugin.d.ts.map +1 -1
- package/types/vite/plugins/virtualEntryPlugin.d.ts.map +1 -1
- package/types/vite/plugins/workerdDevPlugin.d.ts +15 -0
- package/types/vite/plugins/workerdDevPlugin.d.ts.map +1 -0
- package/types/vite/plugins/workerdDevPlugin.test.d.ts +2 -0
- package/types/vite/plugins/workerdDevPlugin.test.d.ts.map +1 -0
- package/dist/cjs/headless/devHeadlessNote.cjs +0 -34
- package/dist/cjs/headless/devHeadlessNote.native.js +0 -37
- package/dist/cjs/headless/devHeadlessNote.native.js.map +0 -1
- package/dist/esm/headless/devHeadlessNote.mjs +0 -9
- package/dist/esm/headless/devHeadlessNote.mjs.map +0 -1
- package/dist/esm/headless/devHeadlessNote.native.js +0 -9
- package/dist/esm/headless/devHeadlessNote.native.js.map +0 -1
- package/src/headless/devHeadlessNote.ts +0 -13
- package/types/headless/devHeadlessNote.d.ts +0 -2
- package/types/headless/devHeadlessNote.d.ts.map +0 -1
|
@@ -122,4 +122,27 @@ describe('createWorkerHandler', () => {
|
|
|
122
122
|
expect(response).toBeInstanceOf(Response)
|
|
123
123
|
expect(response?.status).toBe(204)
|
|
124
124
|
})
|
|
125
|
+
|
|
126
|
+
it('disableModuleCache re-imports the page module on each request', async () => {
|
|
127
|
+
vi.stubEnv('ONE_BUFFERED_SSR', '1')
|
|
128
|
+
let imports = 0
|
|
129
|
+
const handleRequest = createWorkerHandler({
|
|
130
|
+
oneOptions: { web: { defaultRenderMode: 'ssr' } },
|
|
131
|
+
buildInfo,
|
|
132
|
+
disableModuleCache: true,
|
|
133
|
+
lazyRoutes: {
|
|
134
|
+
...lazyRoutes,
|
|
135
|
+
pages: {
|
|
136
|
+
[pageRoute.file]: async () => {
|
|
137
|
+
imports += 1
|
|
138
|
+
return {}
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
}).handleRequest
|
|
143
|
+
|
|
144
|
+
await handleRequest(new Request('https://example.com/some-page'))
|
|
145
|
+
await handleRequest(new Request('https://example.com/some-page'))
|
|
146
|
+
expect(imports).toBeGreaterThan(1)
|
|
147
|
+
})
|
|
125
148
|
})
|
|
@@ -38,10 +38,14 @@ type WorkerHandlerOptions = {
|
|
|
38
38
|
oneOptions: One.PluginOptions
|
|
39
39
|
buildInfo: One.BuildInfo
|
|
40
40
|
lazyRoutes: LazyRoutes
|
|
41
|
+
// when true, skip the module/loader caches so vite hmr of a route file is
|
|
42
|
+
// visible on the next request. production omits this.
|
|
43
|
+
disableModuleCache?: boolean
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
export function createWorkerHandler(options: WorkerHandlerOptions) {
|
|
44
47
|
const { oneOptions } = options
|
|
48
|
+
const disableModuleCache = !!options.disableModuleCache
|
|
45
49
|
|
|
46
50
|
// mutable state for route swapping
|
|
47
51
|
let currentLazyRoutes = options.lazyRoutes
|
|
@@ -137,25 +141,29 @@ export function createWorkerHandler(options: WorkerHandlerOptions) {
|
|
|
137
141
|
lazyKey: string | undefined
|
|
138
142
|
): Function | null | Promise<Function | null> {
|
|
139
143
|
const cacheKey = lazyKey || ''
|
|
140
|
-
|
|
141
|
-
|
|
144
|
+
if (!disableModuleCache) {
|
|
145
|
+
const cached = loaderCache.get(cacheKey)
|
|
146
|
+
if (cached !== undefined) return cached
|
|
147
|
+
}
|
|
142
148
|
|
|
143
149
|
return (async () => {
|
|
144
150
|
let routeExported: any
|
|
145
|
-
if (moduleImportCache.has(cacheKey)) {
|
|
151
|
+
if (!disableModuleCache && moduleImportCache.has(cacheKey)) {
|
|
146
152
|
routeExported = moduleImportCache.get(cacheKey)
|
|
147
153
|
} else if (lazyKey && currentLazyRoutes.pages[lazyKey]) {
|
|
148
154
|
routeExported = await currentLazyRoutes.pages[lazyKey]()
|
|
149
|
-
moduleImportCache.set(cacheKey, routeExported)
|
|
155
|
+
if (!disableModuleCache) moduleImportCache.set(cacheKey, routeExported)
|
|
150
156
|
} else {
|
|
151
157
|
console.warn(`[one/worker] no lazy route for ${cacheKey}`)
|
|
152
|
-
loaderCache.set(cacheKey, null)
|
|
158
|
+
if (!disableModuleCache) loaderCache.set(cacheKey, null)
|
|
153
159
|
return null
|
|
154
160
|
}
|
|
155
161
|
|
|
156
162
|
const loader = routeExported?.loader || null
|
|
157
|
-
|
|
158
|
-
|
|
163
|
+
if (!disableModuleCache) {
|
|
164
|
+
loaderCache.set(cacheKey, loader)
|
|
165
|
+
loaderCacheFnMap.set(cacheKey, routeExported?.loaderCache ?? null)
|
|
166
|
+
}
|
|
159
167
|
return loader
|
|
160
168
|
})()
|
|
161
169
|
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { tmpdir } from 'node:os'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest'
|
|
5
|
+
import {
|
|
6
|
+
createCloudflareWranglerConfig,
|
|
7
|
+
isExperimentalWorkerDevEnabled,
|
|
8
|
+
loadUserWranglerConfigSync,
|
|
9
|
+
shouldEnableWorkerdDev,
|
|
10
|
+
} from './cloudflareWranglerConfig'
|
|
11
|
+
|
|
12
|
+
describe('createCloudflareWranglerConfig', () => {
|
|
13
|
+
it('synthesizes a worker config when no user wrangler file exists', () => {
|
|
14
|
+
const config = createCloudflareWranglerConfig('my-app')
|
|
15
|
+
expect(config.name).toBe('my-app')
|
|
16
|
+
expect(config.main).toBe('worker.js')
|
|
17
|
+
expect(config.compatibility_flags).toEqual(['nodejs_compat'])
|
|
18
|
+
expect(config.find_additional_modules).toBe(true)
|
|
19
|
+
expect(config.assets).toEqual({
|
|
20
|
+
directory: 'client',
|
|
21
|
+
binding: 'ASSETS',
|
|
22
|
+
run_worker_first: true,
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('merges user wrangler.jsonc the same way the cloudflare build does', () => {
|
|
27
|
+
const config = createCloudflareWranglerConfig('generated-name', {
|
|
28
|
+
name: 'test-cloudflare-user-config',
|
|
29
|
+
compatibility_date: '2025-01-01',
|
|
30
|
+
compatibility_flags: ['nodejs_als'],
|
|
31
|
+
vars: { CUSTOM_VALUE: 'from-root-config' },
|
|
32
|
+
assets: { html_handling: 'auto-trailing-slash' },
|
|
33
|
+
rules: [{ type: 'ESModule', globs: ['./extra/**/*.js'], fallthrough: true }],
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
expect(config.name).toBe('test-cloudflare-user-config')
|
|
37
|
+
expect(config.main).toBe('worker.js')
|
|
38
|
+
expect(config.compatibility_date).toBe('2025-01-01')
|
|
39
|
+
expect(config.compatibility_flags).toEqual(['nodejs_compat', 'nodejs_als'])
|
|
40
|
+
expect(config.vars).toEqual({ CUSTOM_VALUE: 'from-root-config' })
|
|
41
|
+
expect(config.assets).toEqual({
|
|
42
|
+
html_handling: 'auto-trailing-slash',
|
|
43
|
+
directory: 'client',
|
|
44
|
+
binding: 'ASSETS',
|
|
45
|
+
run_worker_first: true,
|
|
46
|
+
})
|
|
47
|
+
expect(config.rules).toEqual([
|
|
48
|
+
{ type: 'ESModule', globs: ['./server/**/*.js'], fallthrough: true },
|
|
49
|
+
{ type: 'ESModule', globs: ['./api/**/*.js'], fallthrough: true },
|
|
50
|
+
{ type: 'ESModule', globs: ['./middlewares/**/*.js'], fallthrough: true },
|
|
51
|
+
{ type: 'ESModule', globs: ['./assets/**/*.js'], fallthrough: true },
|
|
52
|
+
{ type: 'ESModule', globs: ['./extra/**/*.js'], fallthrough: true },
|
|
53
|
+
])
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
describe('shouldEnableWorkerdDev', () => {
|
|
58
|
+
const previous = process.env.ONE_EXPERIMENTAL_WORKER_DEV
|
|
59
|
+
|
|
60
|
+
afterEach(() => {
|
|
61
|
+
if (previous === undefined) delete process.env.ONE_EXPERIMENTAL_WORKER_DEV
|
|
62
|
+
else process.env.ONE_EXPERIMENTAL_WORKER_DEV = previous
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('is off when the flag is unset', () => {
|
|
66
|
+
delete process.env.ONE_EXPERIMENTAL_WORKER_DEV
|
|
67
|
+
expect(isExperimentalWorkerDevEnabled()).toBe(false)
|
|
68
|
+
expect(shouldEnableWorkerdDev('cloudflare', '/tmp')).toBe(false)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('is off when the flag is on but the app has no cloudflare target or wrangler file', () => {
|
|
72
|
+
process.env.ONE_EXPERIMENTAL_WORKER_DEV = '1'
|
|
73
|
+
const root = mkdtempSync(join(tmpdir(), 'one-workerd-flag-'))
|
|
74
|
+
try {
|
|
75
|
+
expect(shouldEnableWorkerdDev(undefined, root)).toBe(false)
|
|
76
|
+
expect(shouldEnableWorkerdDev('node', root)).toBe(false)
|
|
77
|
+
} finally {
|
|
78
|
+
rmSync(root, { recursive: true, force: true })
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('is on when the flag is set and deploy is cloudflare', () => {
|
|
83
|
+
process.env.ONE_EXPERIMENTAL_WORKER_DEV = '1'
|
|
84
|
+
expect(shouldEnableWorkerdDev('cloudflare', '/tmp-does-not-exist')).toBe(true)
|
|
85
|
+
expect(shouldEnableWorkerdDev({ target: 'cloudflare' }, '/tmp-does-not-exist')).toBe(
|
|
86
|
+
true
|
|
87
|
+
)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('is on when the flag is set and wrangler.jsonc exists', () => {
|
|
91
|
+
process.env.ONE_EXPERIMENTAL_WORKER_DEV = '1'
|
|
92
|
+
const root = mkdtempSync(join(tmpdir(), 'one-workerd-wrangler-'))
|
|
93
|
+
try {
|
|
94
|
+
writeFileSync(join(root, 'wrangler.jsonc'), '{ "name": "from-file" }\n')
|
|
95
|
+
expect(shouldEnableWorkerdDev(undefined, root)).toBe(true)
|
|
96
|
+
expect(loadUserWranglerConfigSync(root)?.config.name).toBe('from-file')
|
|
97
|
+
} finally {
|
|
98
|
+
rmSync(root, { recursive: true, force: true })
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
})
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
+
import { join, relative } from 'node:path'
|
|
3
|
+
import FSExtra from 'fs-extra'
|
|
4
|
+
|
|
5
|
+
export type JsonPrimitive = string | number | boolean | null
|
|
6
|
+
export type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue }
|
|
7
|
+
|
|
8
|
+
export const GENERATED_CLOUDFLARE_WRANGLER_RULES: JsonValue[] = [
|
|
9
|
+
{ type: 'ESModule', globs: ['./server/**/*.js'], fallthrough: true },
|
|
10
|
+
{ type: 'ESModule', globs: ['./api/**/*.js'], fallthrough: true },
|
|
11
|
+
{ type: 'ESModule', globs: ['./middlewares/**/*.js'], fallthrough: true },
|
|
12
|
+
{ type: 'ESModule', globs: ['./assets/**/*.js'], fallthrough: true },
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
export function isPlainObject(value: unknown): value is Record<string, JsonValue> {
|
|
16
|
+
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function mergeJsonObjects(
|
|
20
|
+
base: Record<string, JsonValue>,
|
|
21
|
+
overrides: Record<string, JsonValue>
|
|
22
|
+
): Record<string, JsonValue> {
|
|
23
|
+
const merged: Record<string, JsonValue> = { ...base }
|
|
24
|
+
|
|
25
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
26
|
+
const baseValue = merged[key]
|
|
27
|
+
if (isPlainObject(baseValue) && isPlainObject(value)) {
|
|
28
|
+
merged[key] = mergeJsonObjects(baseValue, value)
|
|
29
|
+
} else {
|
|
30
|
+
merged[key] = value
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return merged
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function dedupeJsonValues<T extends JsonValue>(values: T[]): T[] {
|
|
38
|
+
const seen = new Set<string>()
|
|
39
|
+
return values.filter((value) => {
|
|
40
|
+
const key = JSON.stringify(value)
|
|
41
|
+
if (seen.has(key)) return false
|
|
42
|
+
seen.add(key)
|
|
43
|
+
return true
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function mergeCloudflareCompatibilityFlags(flags: unknown): string[] {
|
|
48
|
+
const userFlags = Array.isArray(flags)
|
|
49
|
+
? flags.filter((flag): flag is string => typeof flag === 'string')
|
|
50
|
+
: []
|
|
51
|
+
|
|
52
|
+
return dedupeJsonValues<string>(['nodejs_compat', ...userFlags])
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function mergeCloudflareRules(rules: unknown): JsonValue[] {
|
|
56
|
+
const userRules = Array.isArray(rules)
|
|
57
|
+
? rules.filter((rule): rule is JsonValue => isPlainObject(rule))
|
|
58
|
+
: []
|
|
59
|
+
|
|
60
|
+
return dedupeJsonValues<JsonValue>([
|
|
61
|
+
...GENERATED_CLOUDFLARE_WRANGLER_RULES,
|
|
62
|
+
...userRules,
|
|
63
|
+
])
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// minimal JSONC parser: strips line/block comments (string-aware) and trailing
|
|
67
|
+
// commas, then runs JSON.parse. sufficient for small hand-written config files.
|
|
68
|
+
function parseJsonc(text: string): unknown {
|
|
69
|
+
let out = ''
|
|
70
|
+
let i = 0
|
|
71
|
+
let inString = false
|
|
72
|
+
let quote = ''
|
|
73
|
+
while (i < text.length) {
|
|
74
|
+
const ch = text[i]
|
|
75
|
+
const next = text[i + 1]
|
|
76
|
+
if (inString) {
|
|
77
|
+
if (ch === '\\') {
|
|
78
|
+
out += ch + (next ?? '')
|
|
79
|
+
i += 2
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
82
|
+
if (ch === quote) inString = false
|
|
83
|
+
out += ch
|
|
84
|
+
i++
|
|
85
|
+
continue
|
|
86
|
+
}
|
|
87
|
+
if (ch === '"' || ch === "'") {
|
|
88
|
+
inString = true
|
|
89
|
+
quote = ch
|
|
90
|
+
out += ch
|
|
91
|
+
i++
|
|
92
|
+
continue
|
|
93
|
+
}
|
|
94
|
+
if (ch === '/' && next === '/') {
|
|
95
|
+
while (i < text.length && text[i] !== '\n') i++
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
if (ch === '/' && next === '*') {
|
|
99
|
+
i += 2
|
|
100
|
+
while (i < text.length - 1 && !(text[i] === '*' && text[i + 1] === '/')) i++
|
|
101
|
+
i += 2
|
|
102
|
+
continue
|
|
103
|
+
}
|
|
104
|
+
out += ch
|
|
105
|
+
i++
|
|
106
|
+
}
|
|
107
|
+
return JSON.parse(out.replace(/,(\s*[}\]])/g, '$1'))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const WRANGLER_FILE_NAMES = ['wrangler.jsonc', 'wrangler.json'] as const
|
|
111
|
+
|
|
112
|
+
function wranglerCandidateRoots(root: string): string[] {
|
|
113
|
+
return [...new Set([root, process.cwd()])]
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function hasUserWranglerConfig(root: string): boolean {
|
|
117
|
+
for (const candidateRoot of wranglerCandidateRoots(root)) {
|
|
118
|
+
for (const fileName of WRANGLER_FILE_NAMES) {
|
|
119
|
+
if (existsSync(join(candidateRoot, fileName))) return true
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return false
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function parseUserWranglerFile(
|
|
126
|
+
configPath: string
|
|
127
|
+
): { path: string; config: Record<string, JsonValue> } {
|
|
128
|
+
const contents = readFileSync(configPath, 'utf-8')
|
|
129
|
+
let parsed: unknown
|
|
130
|
+
try {
|
|
131
|
+
parsed = parseJsonc(contents)
|
|
132
|
+
} catch (err) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`Failed to parse ${relative(process.cwd(), configPath)}: ${(err as Error).message}`
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!isPlainObject(parsed)) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Expected ${relative(process.cwd(), configPath)} to contain a top-level JSON object`
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
path: configPath,
|
|
146
|
+
config: parsed,
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function loadUserWranglerConfigSync(
|
|
151
|
+
root: string
|
|
152
|
+
): { path: string; config: Record<string, JsonValue> } | null {
|
|
153
|
+
for (const candidateRoot of wranglerCandidateRoots(root)) {
|
|
154
|
+
for (const fileName of WRANGLER_FILE_NAMES) {
|
|
155
|
+
const configPath = join(candidateRoot, fileName)
|
|
156
|
+
if (!existsSync(configPath)) continue
|
|
157
|
+
return parseUserWranglerFile(configPath)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return null
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export async function loadUserWranglerConfig(
|
|
164
|
+
root: string
|
|
165
|
+
): Promise<{ path: string; config: Record<string, JsonValue> } | null> {
|
|
166
|
+
const candidateRoots = wranglerCandidateRoots(root)
|
|
167
|
+
|
|
168
|
+
for (const candidateRoot of candidateRoots) {
|
|
169
|
+
for (const fileName of WRANGLER_FILE_NAMES) {
|
|
170
|
+
const configPath = join(candidateRoot, fileName)
|
|
171
|
+
if (!(await FSExtra.pathExists(configPath))) {
|
|
172
|
+
continue
|
|
173
|
+
}
|
|
174
|
+
return parseUserWranglerFile(configPath)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return null
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function createCloudflareWranglerConfig(
|
|
182
|
+
projectName: string,
|
|
183
|
+
userConfig?: Record<string, JsonValue>
|
|
184
|
+
): Record<string, JsonValue> {
|
|
185
|
+
const generatedConfig: Record<string, JsonValue> = {
|
|
186
|
+
name: projectName,
|
|
187
|
+
main: 'worker.js',
|
|
188
|
+
compatibility_date: '2024-12-05',
|
|
189
|
+
compatibility_flags: ['nodejs_compat'],
|
|
190
|
+
find_additional_modules: true,
|
|
191
|
+
rules: GENERATED_CLOUDFLARE_WRANGLER_RULES,
|
|
192
|
+
assets: {
|
|
193
|
+
directory: 'client',
|
|
194
|
+
binding: 'ASSETS',
|
|
195
|
+
run_worker_first: true,
|
|
196
|
+
},
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const mergedConfig = userConfig
|
|
200
|
+
? mergeJsonObjects(generatedConfig, userConfig)
|
|
201
|
+
: generatedConfig
|
|
202
|
+
|
|
203
|
+
mergedConfig.main = 'worker.js'
|
|
204
|
+
mergedConfig.find_additional_modules = true
|
|
205
|
+
mergedConfig.compatibility_flags = mergeCloudflareCompatibilityFlags(
|
|
206
|
+
mergedConfig.compatibility_flags
|
|
207
|
+
)
|
|
208
|
+
mergedConfig.rules = mergeCloudflareRules(mergedConfig.rules)
|
|
209
|
+
mergedConfig.assets = {
|
|
210
|
+
...(isPlainObject(mergedConfig.assets) ? mergedConfig.assets : {}),
|
|
211
|
+
directory: 'client',
|
|
212
|
+
binding: 'ASSETS',
|
|
213
|
+
run_worker_first: true,
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return mergedConfig
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function getCloudflareProjectNameSync(root: string): string {
|
|
220
|
+
try {
|
|
221
|
+
const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf-8'))
|
|
222
|
+
if (pkg.name) {
|
|
223
|
+
return String(pkg.name).replace(/^@[^/]+\//, '')
|
|
224
|
+
}
|
|
225
|
+
} catch {}
|
|
226
|
+
return 'one-app'
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export async function getCloudflareProjectName(root: string): Promise<string> {
|
|
230
|
+
try {
|
|
231
|
+
const pkg = JSON.parse(await FSExtra.readFile(join(root, 'package.json'), 'utf-8'))
|
|
232
|
+
if (pkg.name) {
|
|
233
|
+
return pkg.name.replace(/^@[^/]+\//, '')
|
|
234
|
+
}
|
|
235
|
+
} catch {}
|
|
236
|
+
return 'one-app'
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function isExperimentalWorkerDevEnabled(): boolean {
|
|
240
|
+
return process.env.ONE_EXPERIMENTAL_WORKER_DEV === '1'
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function shouldEnableWorkerdDev(deploy: unknown, root: string): boolean {
|
|
244
|
+
if (!isExperimentalWorkerDevEnabled()) return false
|
|
245
|
+
const target = typeof deploy === 'string' ? deploy : (deploy as { target?: string } | undefined)?.target
|
|
246
|
+
if (target === 'cloudflare') return true
|
|
247
|
+
return hasUserWranglerConfig(root)
|
|
248
|
+
}
|
package/src/vite/one.ts
CHANGED
|
@@ -34,6 +34,7 @@ import { SSRCSSPlugin } from './plugins/SSRCSSPlugin'
|
|
|
34
34
|
import { virtualEntryId } from './plugins/virtualEntryConstants'
|
|
35
35
|
import { createVirtualEntry } from './plugins/virtualEntryPlugin'
|
|
36
36
|
import { environmentGuardPlugin } from './plugins/environmentGuardPlugin'
|
|
37
|
+
import { createWorkerdDevPlugins, shouldEnableWorkerdDev } from './plugins/workerdDevPlugin'
|
|
37
38
|
import type { One } from './types'
|
|
38
39
|
|
|
39
40
|
type MetroOptions = MetroPluginOptions
|
|
@@ -553,7 +554,11 @@ export function one(options: One.PluginOptions = {}): PluginOption {
|
|
|
553
554
|
resolveId: {
|
|
554
555
|
filter: { id: RN_DEEP_IMPORT_RE },
|
|
555
556
|
handler(source) {
|
|
556
|
-
if (
|
|
557
|
+
if (
|
|
558
|
+
this.environment?.name === 'client' ||
|
|
559
|
+
this.environment?.name === 'ssr' ||
|
|
560
|
+
this.environment?.name === 'worker'
|
|
561
|
+
) {
|
|
557
562
|
return '\0rn-empty-module'
|
|
558
563
|
}
|
|
559
564
|
},
|
|
@@ -867,7 +872,12 @@ export function one(options: One.PluginOptions = {}): PluginOption {
|
|
|
867
872
|
return inspector ? sourceInspectorPlugin({ editor }) : []
|
|
868
873
|
})()
|
|
869
874
|
|
|
875
|
+
const workerdDevPlugins = shouldEnableWorkerdDev(options.web?.deploy, root)
|
|
876
|
+
? createWorkerdDevPlugins(options, root, routeIndex)
|
|
877
|
+
: []
|
|
878
|
+
|
|
870
879
|
return [
|
|
880
|
+
...workerdDevPlugins,
|
|
871
881
|
...vxrnPlugins,
|
|
872
882
|
...devAndProdPlugins,
|
|
873
883
|
...inspectorPlugins,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Plugin, ViteDevServer } from 'vite'
|
|
1
|
+
import type { ModuleNode, Plugin, ViteDevServer } from 'vite'
|
|
2
2
|
import { VIRTUAL_SSR_CSS_ENTRY, VIRTUAL_SSR_CSS_HREF } from '../../constants'
|
|
3
3
|
|
|
4
4
|
// thanks to hi-ogawa https://github.com/hi-ogawa/vite-plugins/tree/main/packages/ssr-css
|
|
@@ -152,6 +152,21 @@ async function collectStyleUrls(
|
|
|
152
152
|
): Promise<string[]> {
|
|
153
153
|
const visited = new Set<string>()
|
|
154
154
|
|
|
155
|
+
// every module in importedModules already carries the id vite resolved when
|
|
156
|
+
// it entered the graph, so walk them directly. resolveUrl runs the whole
|
|
157
|
+
// plugin container and was awaited once per import EDGE rather than once per
|
|
158
|
+
// module, which on a large app is the entire cost of serving this stylesheet.
|
|
159
|
+
// the walk is an explicit stack so a deep import chain cannot overflow.
|
|
160
|
+
function walk(root: ModuleNode) {
|
|
161
|
+
const stack = [root]
|
|
162
|
+
while (stack.length) {
|
|
163
|
+
const mod = stack.pop()!
|
|
164
|
+
if (!mod.id || mod.url.includes('.server.') || visited.has(mod.id)) continue
|
|
165
|
+
visited.add(mod.id)
|
|
166
|
+
for (const childMod of mod.importedModules) stack.push(childMod)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
155
170
|
async function traverse(url: string) {
|
|
156
171
|
if (url.includes('.server.')) return
|
|
157
172
|
const [, id] = await server.moduleGraph.resolveUrl(url)
|
|
@@ -163,7 +178,7 @@ async function collectStyleUrls(
|
|
|
163
178
|
if (!mod) {
|
|
164
179
|
return
|
|
165
180
|
}
|
|
166
|
-
|
|
181
|
+
for (const childMod of mod.importedModules) walk(childMod)
|
|
167
182
|
}
|
|
168
183
|
|
|
169
184
|
// ensure vite's import analysis is ready _only_ for top entries to not go too aggresive
|
|
@@ -135,6 +135,11 @@ describe('environmentGuardPlugin', () => {
|
|
|
135
135
|
expect(result).toBe('export {}')
|
|
136
136
|
})
|
|
137
137
|
|
|
138
|
+
it('web-only: allowed in worker', () => {
|
|
139
|
+
const result = loadEnvironmentGuard('\0one-env-guard:web-only:worker:server')
|
|
140
|
+
expect(result).toBe('export {}')
|
|
141
|
+
})
|
|
142
|
+
|
|
138
143
|
it('web-only: forbidden in ios', () => {
|
|
139
144
|
const result = loadEnvironmentGuard('\0one-env-guard:web-only:ios:unknown')
|
|
140
145
|
expect(result).toContain('throw new Error')
|
|
@@ -35,11 +35,11 @@ const GUARD_SPECIFIERS = [
|
|
|
35
35
|
const GUARD_SPECIFIERS_RE = new RegExp(`^(?:${GUARD_SPECIFIERS.join('|')})$`)
|
|
36
36
|
|
|
37
37
|
type GuardSpecifier = (typeof GUARD_SPECIFIERS)[number]
|
|
38
|
-
type ViteEnvironment = 'client' | 'ssr' | 'ios' | 'android'
|
|
38
|
+
type ViteEnvironment = 'client' | 'ssr' | 'ios' | 'android' | 'worker'
|
|
39
39
|
|
|
40
40
|
const ALLOWED_BY_NAME: Partial<Record<GuardSpecifier, readonly ViteEnvironment[]>> = {
|
|
41
41
|
'native-only': ['ios', 'android'],
|
|
42
|
-
'web-only': ['client', 'ssr'],
|
|
42
|
+
'web-only': ['client', 'ssr', 'worker'],
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
const ALLOWED_BY_CONSUMER: Partial<Record<GuardSpecifier, 'server' | 'client'>> = {
|
|
@@ -124,6 +124,50 @@ describe('createFileSystemRouterPlugin', () => {
|
|
|
124
124
|
)
|
|
125
125
|
})
|
|
126
126
|
|
|
127
|
+
it('does not install the node request middleware when a worker environment exists', async () => {
|
|
128
|
+
process.env.IS_VXRN_CLI = '1'
|
|
129
|
+
tempRoot = mkdtempSync(path.join(tmpdir(), 'one-router-worker-'))
|
|
130
|
+
const appDir = path.join(tempRoot, 'app')
|
|
131
|
+
writeFileSync(path.join(tempRoot, 'package.json'), '{}\n')
|
|
132
|
+
mkdirSync(appDir)
|
|
133
|
+
writeFileSync(
|
|
134
|
+
path.join(appDir, 'index.tsx'),
|
|
135
|
+
'export default function Index() { return null }\n'
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
;(globalThis as any).__vxrnPluginConfig__ = {
|
|
139
|
+
web: {
|
|
140
|
+
defaultRenderMode: 'ssg',
|
|
141
|
+
},
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const { createRouteIndex } = await import('../../utils/routeIndex')
|
|
145
|
+
const { createFileSystemRouterPlugin } = await import('./fileSystemRouterPlugin')
|
|
146
|
+
const plugin = createFileSystemRouterPlugin(
|
|
147
|
+
{
|
|
148
|
+
router: { root: appDir },
|
|
149
|
+
},
|
|
150
|
+
createRouteIndex({ routerRoot: appDir })
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
const use = vi.fn()
|
|
154
|
+
const server = {
|
|
155
|
+
environments: {
|
|
156
|
+
ssr: {},
|
|
157
|
+
worker: { moduleGraph: { getModuleById: vi.fn() } },
|
|
158
|
+
},
|
|
159
|
+
middlewares: { use },
|
|
160
|
+
watcher: {
|
|
161
|
+
addListener: vi.fn(),
|
|
162
|
+
on: vi.fn(),
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const installMiddlewares = (plugin as any).configureServer(server)
|
|
167
|
+
installMiddlewares()
|
|
168
|
+
expect(use).not.toHaveBeenCalled()
|
|
169
|
+
})
|
|
170
|
+
|
|
127
171
|
it('caches dev ssg html until the module runner is invalidated', async () => {
|
|
128
172
|
process.env.VITE_ENVIRONMENT = 'ssr'
|
|
129
173
|
tempRoot = mkdtempSync(path.join(tmpdir(), 'one-router-ssg-cache-'))
|
|
@@ -7,7 +7,7 @@ import type { Connect, Plugin, ViteDevServer } from 'vite'
|
|
|
7
7
|
import { createServerModuleRunner, normalizePath } from 'vite'
|
|
8
8
|
import type { ModuleRunner } from 'vite/module-runner'
|
|
9
9
|
import { getSpaHeaderElements } from '../../constants'
|
|
10
|
-
import { createHandleRequest } from '../../
|
|
10
|
+
import { createHandleRequest } from '../../createDevHandleRequest'
|
|
11
11
|
import type { RouteNode } from '../../router/Route' // used for type in runLoaderWithTracking
|
|
12
12
|
import type { RenderAppProps } from '../../types'
|
|
13
13
|
import { getPageExport } from '../../utils/getPageExport'
|
|
@@ -42,9 +42,6 @@ const routeTypeColors: Record<string, (s: string) => string> = {
|
|
|
42
42
|
api: colors.magenta,
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// server needs better dep optimization
|
|
46
|
-
const USE_SERVER_ENV = false //!!process.env.USE_SERVER_ENV
|
|
47
|
-
|
|
48
45
|
export function createFileSystemRouterPlugin(
|
|
49
46
|
options: One.PluginOptions,
|
|
50
47
|
routeIndex: RouteIndex
|
|
@@ -615,6 +612,11 @@ export function createFileSystemRouterPlugin(
|
|
|
615
612
|
handleRequest = createRequestHandler()
|
|
616
613
|
ssgHtmlCache.clear()
|
|
617
614
|
needsRouteRefresh = true
|
|
615
|
+
const workerGraph = server?.environments.worker?.moduleGraph
|
|
616
|
+
const workerEntry = workerGraph?.getModuleById('\0virtual:one-workerd-dev-entry')
|
|
617
|
+
if (workerGraph && workerEntry) {
|
|
618
|
+
workerGraph.invalidateModule(workerEntry)
|
|
619
|
+
}
|
|
618
620
|
} catch (error) {
|
|
619
621
|
console.warn(`[one] Failed to rebuild routes after ${changedPath} changed.`, error)
|
|
620
622
|
}
|
|
@@ -708,10 +710,14 @@ export function createFileSystemRouterPlugin(
|
|
|
708
710
|
preloads = ['/bundledDevClient.mjs', '/assets/_virtual_one-entry.js']
|
|
709
711
|
}
|
|
710
712
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
)
|
|
713
|
+
const workerDev = !!server.environments.worker
|
|
714
|
+
|
|
715
|
+
// workerd-backed worker env handles web requests via @cloudflare/vite-plugin
|
|
716
|
+
// middleware (node req/res <-> fetch). the node module runner is only for
|
|
717
|
+
// the default (flag off) path.
|
|
718
|
+
if (!workerDev) {
|
|
719
|
+
runner = createServerModuleRunner(server.environments.ssr)
|
|
720
|
+
}
|
|
715
721
|
|
|
716
722
|
const appDir = path.resolve(process.cwd(), getRouterRootFromOneOptions(options))
|
|
717
723
|
|
|
@@ -762,6 +768,12 @@ export function createFileSystemRouterPlugin(
|
|
|
762
768
|
// Vite's transform middleware so that we can focus on handling the requests
|
|
763
769
|
// we're interested in.
|
|
764
770
|
return () => {
|
|
771
|
+
if (workerDev) {
|
|
772
|
+
// @cloudflare/vite-plugin already dispatches the request into workerd.
|
|
773
|
+
// do not run the node handler alongside it.
|
|
774
|
+
return
|
|
775
|
+
}
|
|
776
|
+
|
|
765
777
|
// packager status fallback — runs after one's routing so user-defined
|
|
766
778
|
// /status routes, +api handlers, and custom middleware take priority.
|
|
767
779
|
// only fires if nothing else handled the request.
|
|
@@ -194,6 +194,19 @@ export const registerPreloadedRoute = () => {}`
|
|
|
194
194
|
expect(code).not.toContain('__oneGetSetupPromise')
|
|
195
195
|
})
|
|
196
196
|
|
|
197
|
+
it('worker environment uses the server setupFile like ssr', () => {
|
|
198
|
+
const plugin = createVirtualEntry({
|
|
199
|
+
...base,
|
|
200
|
+
setupFile: {
|
|
201
|
+
client: './src/setupClient.ts',
|
|
202
|
+
server: './src/setupServer.ts',
|
|
203
|
+
},
|
|
204
|
+
})
|
|
205
|
+
const code = loadEntry(plugin, 'worker', 'serve')
|
|
206
|
+
expect(code).toContain('import "./src/setupServer.ts"')
|
|
207
|
+
expect(code).not.toContain('__oneGetSetupPromise')
|
|
208
|
+
})
|
|
209
|
+
|
|
197
210
|
it('no setupFile produces no import', () => {
|
|
198
211
|
const plugin = createVirtualEntry(base)
|
|
199
212
|
const code = loadEntry(plugin, 'ssr')
|
|
@@ -88,6 +88,7 @@ function getSetupFileImport(
|
|
|
88
88
|
const envMap: Record<string, keyof NormalizedSetupFiles> = {
|
|
89
89
|
client: 'client',
|
|
90
90
|
ssr: 'server',
|
|
91
|
+
worker: 'server',
|
|
91
92
|
ios: 'ios',
|
|
92
93
|
android: 'android',
|
|
93
94
|
}
|
|
@@ -170,7 +171,8 @@ export function createVirtualEntry(options: {
|
|
|
170
171
|
handler(id) {
|
|
171
172
|
if (id === resolvedVirtualEntryId) {
|
|
172
173
|
const isNative = isNativeEnvironment(this.environment)
|
|
173
|
-
const isSSR =
|
|
174
|
+
const isSSR =
|
|
175
|
+
this.environment.name === 'ssr' || this.environment.name === 'worker'
|
|
174
176
|
const serverSpaRouteFiles = isSSR
|
|
175
177
|
? options.routeIndex
|
|
176
178
|
.getPaths()
|