one 1.17.3 → 1.17.5
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/metro-config/withOne.test.cjs +8 -0
- package/dist/cjs/metro-config/withOne.test.native.js +8 -0
- package/dist/cjs/metro-config/withOne.test.native.js.map +1 -1
- package/dist/cjs/typed-routes/generateRouteTypes.cjs +12 -2
- package/dist/cjs/typed-routes/generateRouteTypes.native.js +12 -2
- package/dist/cjs/typed-routes/generateRouteTypes.native.js.map +1 -1
- package/dist/cjs/typed-routes/generateRouteTypes.test.cjs +58 -0
- package/dist/cjs/typed-routes/generateRouteTypes.test.native.js +61 -0
- package/dist/cjs/typed-routes/generateRouteTypes.test.native.js.map +1 -0
- package/dist/cjs/typed-routes/getTypedRoutesDeclarationFile.cjs +5 -5
- package/dist/cjs/typed-routes/getTypedRoutesDeclarationFile.native.js +5 -5
- package/dist/cjs/typed-routes/getTypedRoutesDeclarationFile.native.js.map +1 -1
- package/dist/cjs/typed-routes/getTypedRoutesDeclarationFile.test.cjs +18 -0
- package/dist/cjs/typed-routes/getTypedRoutesDeclarationFile.test.native.js +25 -0
- package/dist/cjs/typed-routes/getTypedRoutesDeclarationFile.test.native.js.map +1 -0
- package/dist/cjs/utils/routeFileWatch.cjs +58 -0
- package/dist/cjs/utils/routeFileWatch.native.js +62 -0
- package/dist/cjs/utils/routeFileWatch.native.js.map +1 -0
- package/dist/cjs/utils/routeFileWatch.test.cjs +92 -0
- package/dist/cjs/utils/routeFileWatch.test.native.js +95 -0
- package/dist/cjs/utils/routeFileWatch.test.native.js.map +1 -0
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.cjs +16 -7
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.native.js +16 -7
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.native.js.map +1 -1
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.test.cjs +111 -0
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.test.native.js +116 -0
- package/dist/cjs/vite/plugins/fileSystemRouterPlugin.test.native.js.map +1 -0
- package/dist/cjs/vite/plugins/generateFileSystemRouteTypesPlugin.cjs +9 -9
- package/dist/cjs/vite/plugins/generateFileSystemRouteTypesPlugin.native.js +10 -10
- package/dist/cjs/vite/plugins/generateFileSystemRouteTypesPlugin.native.js.map +1 -1
- package/dist/esm/metro-config/withOne.test.mjs +8 -0
- package/dist/esm/metro-config/withOne.test.mjs.map +1 -1
- package/dist/esm/metro-config/withOne.test.native.js +8 -0
- package/dist/esm/metro-config/withOne.test.native.js.map +1 -1
- package/dist/esm/typed-routes/generateRouteTypes.mjs +13 -3
- package/dist/esm/typed-routes/generateRouteTypes.mjs.map +1 -1
- package/dist/esm/typed-routes/generateRouteTypes.native.js +13 -3
- package/dist/esm/typed-routes/generateRouteTypes.native.js.map +1 -1
- package/dist/esm/typed-routes/generateRouteTypes.test.mjs +35 -0
- package/dist/esm/typed-routes/generateRouteTypes.test.mjs.map +1 -0
- package/dist/esm/typed-routes/generateRouteTypes.test.native.js +35 -0
- package/dist/esm/typed-routes/generateRouteTypes.test.native.js.map +1 -0
- package/dist/esm/typed-routes/getTypedRoutesDeclarationFile.mjs +5 -5
- package/dist/esm/typed-routes/getTypedRoutesDeclarationFile.mjs.map +1 -1
- package/dist/esm/typed-routes/getTypedRoutesDeclarationFile.native.js +5 -5
- package/dist/esm/typed-routes/getTypedRoutesDeclarationFile.native.js.map +1 -1
- package/dist/esm/typed-routes/getTypedRoutesDeclarationFile.test.mjs +19 -0
- package/dist/esm/typed-routes/getTypedRoutesDeclarationFile.test.mjs.map +1 -0
- package/dist/esm/typed-routes/getTypedRoutesDeclarationFile.test.native.js +23 -0
- package/dist/esm/typed-routes/getTypedRoutesDeclarationFile.test.native.js.map +1 -0
- package/dist/esm/utils/routeFileWatch.mjs +20 -0
- package/dist/esm/utils/routeFileWatch.mjs.map +1 -0
- package/dist/esm/utils/routeFileWatch.native.js +21 -0
- package/dist/esm/utils/routeFileWatch.native.js.map +1 -0
- package/dist/esm/utils/routeFileWatch.test.mjs +69 -0
- package/dist/esm/utils/routeFileWatch.test.mjs.map +1 -0
- package/dist/esm/utils/routeFileWatch.test.native.js +69 -0
- package/dist/esm/utils/routeFileWatch.test.native.js.map +1 -0
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.mjs +16 -7
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.mjs.map +1 -1
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.native.js +16 -7
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.native.js.map +1 -1
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.test.mjs +88 -0
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.test.mjs.map +1 -0
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.test.native.js +90 -0
- package/dist/esm/vite/plugins/fileSystemRouterPlugin.test.native.js.map +1 -0
- package/dist/esm/vite/plugins/generateFileSystemRouteTypesPlugin.mjs +9 -9
- package/dist/esm/vite/plugins/generateFileSystemRouteTypesPlugin.mjs.map +1 -1
- package/dist/esm/vite/plugins/generateFileSystemRouteTypesPlugin.native.js +10 -10
- package/dist/esm/vite/plugins/generateFileSystemRouteTypesPlugin.native.js.map +1 -1
- package/package.json +10 -10
- package/src/metro-config/withOne.test.ts +14 -0
- package/src/typed-routes/generateRouteTypes.test.ts +53 -0
- package/src/typed-routes/generateRouteTypes.ts +14 -3
- package/src/typed-routes/getTypedRoutesDeclarationFile.test.ts +22 -0
- package/src/typed-routes/getTypedRoutesDeclarationFile.ts +5 -5
- package/src/utils/routeFileWatch.test.ts +94 -0
- package/src/utils/routeFileWatch.ts +40 -0
- package/src/vite/plugins/fileSystemRouterPlugin.test.ts +106 -0
- package/src/vite/plugins/fileSystemRouterPlugin.tsx +21 -13
- package/src/vite/plugins/generateFileSystemRouteTypesPlugin.tsx +16 -17
- package/types/typed-routes/generateRouteTypes.d.ts.map +1 -1
- package/types/typed-routes/generateRouteTypes.test.d.ts +2 -0
- package/types/typed-routes/generateRouteTypes.test.d.ts.map +1 -0
- package/types/typed-routes/getTypedRoutesDeclarationFile.test.d.ts +2 -0
- package/types/typed-routes/getTypedRoutesDeclarationFile.test.d.ts.map +1 -0
- package/types/utils/routeFileWatch.d.ts +9 -0
- package/types/utils/routeFileWatch.d.ts.map +1 -0
- package/types/utils/routeFileWatch.test.d.ts +2 -0
- package/types/utils/routeFileWatch.test.d.ts.map +1 -0
- package/types/vite/plugins/fileSystemRouterPlugin.d.ts.map +1 -1
- package/types/vite/plugins/fileSystemRouterPlugin.test.d.ts +2 -0
- package/types/vite/plugins/fileSystemRouterPlugin.test.d.ts.map +1 -0
- package/types/vite/plugins/generateFileSystemRouteTypesPlugin.d.ts.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "one",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.5",
|
|
4
4
|
"license": "BSD-3-Clause",
|
|
5
5
|
"sideEffects": [
|
|
6
6
|
"setup.mjs",
|
|
@@ -180,18 +180,18 @@
|
|
|
180
180
|
"@cloudflare/vite-plugin": "^1.33.1",
|
|
181
181
|
"@swc/core": "^1.14.0",
|
|
182
182
|
"@ungap/structured-clone": "^1.2.0",
|
|
183
|
-
"@vxrn/color-scheme": "1.17.
|
|
184
|
-
"@vxrn/compiler": "1.17.
|
|
185
|
-
"@vxrn/resolve": "1.17.
|
|
186
|
-
"@vxrn/tslib-lite": "1.17.
|
|
187
|
-
"@vxrn/use-isomorphic-layout-effect": "1.17.
|
|
188
|
-
"@vxrn/vite-plugin-metro": "1.17.
|
|
183
|
+
"@vxrn/color-scheme": "1.17.5",
|
|
184
|
+
"@vxrn/compiler": "1.17.5",
|
|
185
|
+
"@vxrn/resolve": "1.17.5",
|
|
186
|
+
"@vxrn/tslib-lite": "1.17.5",
|
|
187
|
+
"@vxrn/use-isomorphic-layout-effect": "1.17.5",
|
|
188
|
+
"@vxrn/vite-plugin-metro": "1.17.5",
|
|
189
189
|
"babel-dead-code-elimination": "1.0.10",
|
|
190
190
|
"babel-plugin-module-resolver": "^5.0.2",
|
|
191
191
|
"babel-preset-expo": "*",
|
|
192
192
|
"citty": "^0.1.6",
|
|
193
193
|
"core-js": "^3.38.1",
|
|
194
|
-
"create-vxrn": "1.17.
|
|
194
|
+
"create-vxrn": "1.17.5",
|
|
195
195
|
"escape-string-regexp": "^5.0.0",
|
|
196
196
|
"expo-linking": "~55.0.7",
|
|
197
197
|
"expo-modules-core": "~55.0.16",
|
|
@@ -210,8 +210,8 @@
|
|
|
210
210
|
"ts-pattern": "^5.6.2",
|
|
211
211
|
"tsconfig-paths": "^4",
|
|
212
212
|
"use-latest-callback": "^0.2.3",
|
|
213
|
-
"vite": "^8.0.
|
|
214
|
-
"vxrn": "1.17.
|
|
213
|
+
"vite": "^8.0.13",
|
|
214
|
+
"vxrn": "1.17.5",
|
|
215
215
|
"ws": "^8.18.0",
|
|
216
216
|
"xxhashjs": "^0.2.2"
|
|
217
217
|
},
|
|
@@ -115,4 +115,18 @@ describe('withOne', () => {
|
|
|
115
115
|
expect(config.resolver.extraNodeModules['fixture-singleton']).toBe(fixtureRoot)
|
|
116
116
|
expect(config.watchFolders).toContain(path.join(fixtureRoot, 'shared'))
|
|
117
117
|
})
|
|
118
|
+
|
|
119
|
+
it('rewrites native default index bundle requests to the One entry', async () => {
|
|
120
|
+
const config = (await withOne(projectRoot, { loadViteConfig: false })) as any
|
|
121
|
+
const rewriteRequestUrl = config.server.rewriteRequestUrl
|
|
122
|
+
|
|
123
|
+
expect(
|
|
124
|
+
rewriteRequestUrl('/.expo/.virtual-metro-entry.bundle?platform=ios&dev=true')
|
|
125
|
+
).toContain('/packages/one/metro-entry.bundle?platform=ios&dev=true')
|
|
126
|
+
expect(
|
|
127
|
+
rewriteRequestUrl('/index.bundle?platform=ios&dev=true&hot=true&minify=false')
|
|
128
|
+
).toContain(
|
|
129
|
+
'/packages/one/metro-entry.bundle?platform=ios&dev=true&hot=true&minify=false'
|
|
130
|
+
)
|
|
131
|
+
})
|
|
118
132
|
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mkdirSync,
|
|
3
|
+
mkdtempSync,
|
|
4
|
+
rmSync,
|
|
5
|
+
statSync,
|
|
6
|
+
utimesSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from 'node:fs'
|
|
9
|
+
import { tmpdir } from 'node:os'
|
|
10
|
+
import path from 'node:path'
|
|
11
|
+
import { afterEach, describe, expect, it } from 'vitest'
|
|
12
|
+
import { generateRouteTypes } from './generateRouteTypes'
|
|
13
|
+
|
|
14
|
+
describe(generateRouteTypes, () => {
|
|
15
|
+
const originalCwd = process.cwd()
|
|
16
|
+
let tempRoot: string | undefined
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
process.chdir(originalCwd)
|
|
20
|
+
|
|
21
|
+
if (tempRoot) {
|
|
22
|
+
rmSync(tempRoot, { recursive: true, force: true })
|
|
23
|
+
tempRoot = undefined
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('does not rewrite routes declarations when contents are unchanged', async () => {
|
|
28
|
+
tempRoot = mkdtempSync(path.join(tmpdir(), 'one-routes-types-'))
|
|
29
|
+
const appDir = path.join(tempRoot, 'app')
|
|
30
|
+
mkdirSync(appDir)
|
|
31
|
+
writeFileSync(
|
|
32
|
+
path.join(appDir, 'index+ssg.tsx'),
|
|
33
|
+
'export default function Index() { return null }\n'
|
|
34
|
+
)
|
|
35
|
+
writeFileSync(
|
|
36
|
+
path.join(appDir, '[slug]+ssg.tsx'),
|
|
37
|
+
'export default function Slug() { return null }\n'
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
process.chdir(tempRoot)
|
|
41
|
+
|
|
42
|
+
const outFile = path.join('app', 'routes.d.ts')
|
|
43
|
+
await generateRouteTypes(outFile, 'app')
|
|
44
|
+
|
|
45
|
+
const oldDate = new Date('2001-01-01T00:00:00.000Z')
|
|
46
|
+
utimesSync(outFile, oldDate, oldDate)
|
|
47
|
+
const previousMtimeMs = statSync(outFile).mtimeMs
|
|
48
|
+
|
|
49
|
+
await generateRouteTypes(outFile, 'app')
|
|
50
|
+
|
|
51
|
+
expect(statSync(outFile).mtimeMs).toBe(previousMtimeMs)
|
|
52
|
+
})
|
|
53
|
+
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { writeFile } from 'node:fs/promises'
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises'
|
|
2
2
|
import { dirname, join } from 'node:path'
|
|
3
3
|
import FSExtra from 'fs-extra'
|
|
4
4
|
import micromatch from 'micromatch'
|
|
@@ -33,8 +33,19 @@ export async function generateRouteTypes(
|
|
|
33
33
|
const context = globbedRoutesToRouteContext(routes, routerRoot)
|
|
34
34
|
const declarations = getTypedRoutesDeclarationFile(context)
|
|
35
35
|
const outDir = dirname(outFile)
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
let currentDeclarations: string | undefined
|
|
37
|
+
try {
|
|
38
|
+
currentDeclarations = await readFile(outFile, 'utf8')
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
|
41
|
+
throw error
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (currentDeclarations !== declarations) {
|
|
46
|
+
await FSExtra.ensureDir(outDir)
|
|
47
|
+
await writeFile(outFile, declarations)
|
|
48
|
+
}
|
|
38
49
|
|
|
39
50
|
// If experimental.typedRoutesGeneration is enabled, inject helpers into route files
|
|
40
51
|
if (typedRoutesMode) {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import type { One } from '../vite/types'
|
|
3
|
+
import { getTypedRoutesDeclarationFile } from './getTypedRoutesDeclarationFile'
|
|
4
|
+
|
|
5
|
+
function createRouteContext(paths: string[]) {
|
|
6
|
+
const context = (() => ({ default() {} })) as unknown as One.RouteContext
|
|
7
|
+
Object.defineProperty(context, 'keys', {
|
|
8
|
+
value: () => paths,
|
|
9
|
+
})
|
|
10
|
+
return context
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe(getTypedRoutesDeclarationFile, () => {
|
|
14
|
+
it('does not emit trailing whitespace for multi-line route unions', () => {
|
|
15
|
+
const declaration = getTypedRoutesDeclarationFile(
|
|
16
|
+
createRouteContext(['./index+ssg.tsx', './about+ssg.tsx', './[slug]+ssg.tsx'])
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
expect(declaration).toContain(' StaticRoutes:\n | `/`')
|
|
20
|
+
expect(declaration).not.toMatch(/[ \t]+$/m)
|
|
21
|
+
})
|
|
22
|
+
})
|
|
@@ -36,9 +36,9 @@ import type { OneRouter } from 'one'
|
|
|
36
36
|
declare module 'one' {
|
|
37
37
|
export namespace OneRouter {
|
|
38
38
|
export interface __routes<T extends string = string> extends Record<string, unknown> {
|
|
39
|
-
StaticRoutes
|
|
40
|
-
DynamicRoutes
|
|
41
|
-
DynamicRouteTemplate
|
|
39
|
+
StaticRoutes:${setToUnionType(staticRoutes)}
|
|
40
|
+
DynamicRoutes:${setToUnionType(dynamicRoutes)}
|
|
41
|
+
DynamicRouteTemplate:${setToUnionType(dynamicRouteContextKeys)}
|
|
42
42
|
IsTyped: true
|
|
43
43
|
${hasRoutes ? `RouteTypes: ${generateRouteTypesMap(dynamicRouteContextKeys)}` : ''}
|
|
44
44
|
}
|
|
@@ -189,9 +189,9 @@ function addRouteNode(
|
|
|
189
189
|
* Formats with one route per line for cleaner git diffs
|
|
190
190
|
*/
|
|
191
191
|
const setToUnionType = <T>(set: Set<T>) => {
|
|
192
|
-
if (set.size === 0) return 'never'
|
|
192
|
+
if (set.size === 0) return ' never'
|
|
193
193
|
const sorted = [...set].sort()
|
|
194
|
-
if (sorted.length === 1) return
|
|
194
|
+
if (sorted.length === 1) return ` \`${sorted[0]}\``
|
|
195
195
|
// format as multi-line union for cleaner diffs
|
|
196
196
|
return '\n | ' + sorted.map((s) => `\`${s}\``).join('\n | ')
|
|
197
197
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import {
|
|
4
|
+
isPathInsideDirectory,
|
|
5
|
+
isRouteFilePath,
|
|
6
|
+
isRouteFileWatchEvent,
|
|
7
|
+
} from './routeFileWatch'
|
|
8
|
+
|
|
9
|
+
describe(isRouteFilePath, () => {
|
|
10
|
+
it('matches route source files', () => {
|
|
11
|
+
expect(isRouteFilePath('/project/app/index.ts')).toBe(true)
|
|
12
|
+
expect(isRouteFilePath('/project/app/index.tsx')).toBe(true)
|
|
13
|
+
expect(isRouteFilePath('/project/app/index.js')).toBe(true)
|
|
14
|
+
expect(isRouteFilePath('/project/app/index.jsx')).toBe(true)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('ignores non-route files', () => {
|
|
18
|
+
expect(isRouteFilePath('/project/app/tamagui.generated.css')).toBe(false)
|
|
19
|
+
expect(isRouteFilePath('/project/app/routes.d.ts')).toBe(false)
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
describe(isPathInsideDirectory, () => {
|
|
24
|
+
it('only matches real descendants of the router root', () => {
|
|
25
|
+
const routerRoot = path.resolve('/project/app')
|
|
26
|
+
|
|
27
|
+
expect(isPathInsideDirectory('/project/app/index.tsx', routerRoot)).toBe(true)
|
|
28
|
+
expect(isPathInsideDirectory('/project/app-copy/index.tsx', routerRoot)).toBe(false)
|
|
29
|
+
expect(isPathInsideDirectory('/project/app', routerRoot)).toBe(false)
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
describe(isRouteFileWatchEvent, () => {
|
|
34
|
+
const routerRoot = path.resolve('/project/app')
|
|
35
|
+
|
|
36
|
+
it('matches route file add and delete events', () => {
|
|
37
|
+
expect(
|
|
38
|
+
isRouteFileWatchEvent({
|
|
39
|
+
event: 'add',
|
|
40
|
+
filePath: '/project/app/index.tsx',
|
|
41
|
+
routerRoot,
|
|
42
|
+
})
|
|
43
|
+
).toBe(true)
|
|
44
|
+
expect(
|
|
45
|
+
isRouteFileWatchEvent({
|
|
46
|
+
event: 'delete',
|
|
47
|
+
filePath: '/project/app/nested/page.jsx',
|
|
48
|
+
routerRoot,
|
|
49
|
+
})
|
|
50
|
+
).toBe(true)
|
|
51
|
+
expect(
|
|
52
|
+
isRouteFileWatchEvent({
|
|
53
|
+
event: 'unlink',
|
|
54
|
+
filePath: '/project/app/nested/page.jsx',
|
|
55
|
+
routerRoot,
|
|
56
|
+
})
|
|
57
|
+
).toBe(true)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('ignores non-route file add and delete events', () => {
|
|
61
|
+
expect(
|
|
62
|
+
isRouteFileWatchEvent({
|
|
63
|
+
event: 'add',
|
|
64
|
+
filePath: '/project/app/tamagui.generated.css',
|
|
65
|
+
routerRoot,
|
|
66
|
+
})
|
|
67
|
+
).toBe(false)
|
|
68
|
+
expect(
|
|
69
|
+
isRouteFileWatchEvent({
|
|
70
|
+
event: 'delete',
|
|
71
|
+
filePath: '/project/app/routes.d.ts',
|
|
72
|
+
routerRoot,
|
|
73
|
+
})
|
|
74
|
+
).toBe(false)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('only matches change events when requested', () => {
|
|
78
|
+
expect(
|
|
79
|
+
isRouteFileWatchEvent({
|
|
80
|
+
event: 'change',
|
|
81
|
+
filePath: '/project/app/index.tsx',
|
|
82
|
+
routerRoot,
|
|
83
|
+
})
|
|
84
|
+
).toBe(false)
|
|
85
|
+
expect(
|
|
86
|
+
isRouteFileWatchEvent({
|
|
87
|
+
event: 'change',
|
|
88
|
+
filePath: '/project/app/index.tsx',
|
|
89
|
+
routerRoot,
|
|
90
|
+
includeChangeEvents: true,
|
|
91
|
+
})
|
|
92
|
+
).toBe(true)
|
|
93
|
+
})
|
|
94
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
|
|
3
|
+
const routeFileExtensionRe = /\.[jt]sx?$/
|
|
4
|
+
|
|
5
|
+
export function isRouteFilePath(filePath: string) {
|
|
6
|
+
return routeFileExtensionRe.test(filePath) && !filePath.endsWith('.d.ts')
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isPathInsideDirectory(filePath: string, directory: string) {
|
|
10
|
+
const relativePath = path.relative(path.resolve(directory), path.resolve(filePath))
|
|
11
|
+
return (
|
|
12
|
+
relativePath !== '' &&
|
|
13
|
+
!relativePath.startsWith('..') &&
|
|
14
|
+
!path.isAbsolute(relativePath)
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function isRouteFileWatchEvent({
|
|
19
|
+
event,
|
|
20
|
+
filePath,
|
|
21
|
+
routerRoot,
|
|
22
|
+
includeChangeEvents = false,
|
|
23
|
+
}: {
|
|
24
|
+
event: string
|
|
25
|
+
filePath: string
|
|
26
|
+
routerRoot: string
|
|
27
|
+
includeChangeEvents?: boolean
|
|
28
|
+
}) {
|
|
29
|
+
const isRouteFileEvent =
|
|
30
|
+
event === 'add' ||
|
|
31
|
+
event === 'delete' ||
|
|
32
|
+
event === 'unlink' ||
|
|
33
|
+
(includeChangeEvents && event === 'change')
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
isRouteFileEvent &&
|
|
37
|
+
isPathInsideDirectory(filePath, routerRoot) &&
|
|
38
|
+
isRouteFilePath(filePath)
|
|
39
|
+
)
|
|
40
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { tmpdir } from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
5
|
+
|
|
6
|
+
vi.mock('vite', async () => {
|
|
7
|
+
const actual = await vi.importActual<typeof import('vite')>('vite')
|
|
8
|
+
return {
|
|
9
|
+
...actual,
|
|
10
|
+
createServerModuleRunner: vi.fn(() => ({
|
|
11
|
+
clearCache: vi.fn(),
|
|
12
|
+
import: vi.fn(),
|
|
13
|
+
})),
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
describe('createFileSystemRouterPlugin', () => {
|
|
18
|
+
const previousIsVxrnCli = process.env.IS_VXRN_CLI
|
|
19
|
+
let previousVxrnPluginConfig: unknown
|
|
20
|
+
let tempRoot: string | undefined
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
previousVxrnPluginConfig = (globalThis as any).__vxrnPluginConfig__
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
if (tempRoot) {
|
|
28
|
+
rmSync(tempRoot, { recursive: true, force: true })
|
|
29
|
+
tempRoot = undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (previousIsVxrnCli === undefined) {
|
|
33
|
+
delete process.env.IS_VXRN_CLI
|
|
34
|
+
} else {
|
|
35
|
+
process.env.IS_VXRN_CLI = previousIsVxrnCli
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (previousVxrnPluginConfig === undefined) {
|
|
39
|
+
delete (globalThis as any).__vxrnPluginConfig__
|
|
40
|
+
} else {
|
|
41
|
+
;(globalThis as any).__vxrnPluginConfig__ = previousVxrnPluginConfig
|
|
42
|
+
}
|
|
43
|
+
vi.restoreAllMocks()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('keeps route watcher rebuild errors handled', async () => {
|
|
47
|
+
process.env.IS_VXRN_CLI = '1'
|
|
48
|
+
tempRoot = mkdtempSync(path.join(tmpdir(), 'one-router-watch-'))
|
|
49
|
+
const appDir = path.join(tempRoot, 'app')
|
|
50
|
+
writeFileSync(path.join(tempRoot, 'package.json'), '{}\n')
|
|
51
|
+
mkdirSync(appDir)
|
|
52
|
+
writeFileSync(
|
|
53
|
+
path.join(appDir, 'index.tsx'),
|
|
54
|
+
'export default function Index() { return null }\n'
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
;(globalThis as any).__vxrnPluginConfig__ = {
|
|
58
|
+
web: {
|
|
59
|
+
defaultRenderMode: 'ssg',
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const { createFileSystemRouterPlugin } = await import('./fileSystemRouterPlugin')
|
|
64
|
+
const plugin = createFileSystemRouterPlugin({
|
|
65
|
+
router: {
|
|
66
|
+
root: appDir,
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
let watcherListener:
|
|
71
|
+
| ((event: string, changedPath: string) => void | Promise<void>)
|
|
72
|
+
| undefined
|
|
73
|
+
const server = {
|
|
74
|
+
environments: {
|
|
75
|
+
ssr: {},
|
|
76
|
+
},
|
|
77
|
+
watcher: {
|
|
78
|
+
addListener: vi.fn((event: string, listener: typeof watcherListener) => {
|
|
79
|
+
if (event === 'all') {
|
|
80
|
+
watcherListener = listener
|
|
81
|
+
}
|
|
82
|
+
}),
|
|
83
|
+
on: vi.fn(),
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
;(plugin as any).configureServer(server)
|
|
88
|
+
expect(watcherListener).toBeDefined()
|
|
89
|
+
|
|
90
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
91
|
+
delete (globalThis as any).__vxrnPluginConfig__
|
|
92
|
+
|
|
93
|
+
if (!watcherListener) {
|
|
94
|
+
throw new Error('Expected route watcher listener to be registered')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
await expect(
|
|
98
|
+
Promise.resolve(watcherListener('add', path.join(appDir, 'new-route.tsx')))
|
|
99
|
+
).resolves.toBeUndefined()
|
|
100
|
+
|
|
101
|
+
expect(warn).toHaveBeenCalledWith(
|
|
102
|
+
expect.stringContaining('[one] Failed to rebuild routes'),
|
|
103
|
+
expect.any(Error)
|
|
104
|
+
)
|
|
105
|
+
})
|
|
106
|
+
})
|
|
@@ -14,6 +14,7 @@ import { getRouterRootFromOneOptions } from '../../utils/getRouterRootFromOneOpt
|
|
|
14
14
|
import { isResponse } from '../../utils/isResponse'
|
|
15
15
|
import { isStatusRedirect } from '../../utils/isStatus'
|
|
16
16
|
import { promiseWithResolvers } from '../../utils/promiseWithResolvers'
|
|
17
|
+
import { isRouteFileWatchEvent } from '../../utils/routeFileWatch'
|
|
17
18
|
import { trackLoaderDependencies } from '../../utils/trackLoaderDependencies'
|
|
18
19
|
import { LoaderDataCache } from '../../vite/constants'
|
|
19
20
|
import { replaceLoader } from '../../vite/replaceLoader'
|
|
@@ -524,6 +525,14 @@ export function createFileSystemRouterPlugin(options: One.PluginOptions): Plugin
|
|
|
524
525
|
)
|
|
525
526
|
}
|
|
526
527
|
|
|
528
|
+
function recreateRequestHandler(changedPath: string) {
|
|
529
|
+
try {
|
|
530
|
+
handleRequest = createRequestHandler()
|
|
531
|
+
} catch (error) {
|
|
532
|
+
console.warn(`[one] Failed to rebuild routes after ${changedPath} changed.`, error)
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
527
536
|
return {
|
|
528
537
|
name: `one-router-fs`,
|
|
529
538
|
enforce: 'post',
|
|
@@ -585,21 +594,20 @@ export function createFileSystemRouterPlugin(options: One.PluginOptions): Plugin
|
|
|
585
594
|
USE_SERVER_ENV ? server.environments.server : server.environments.ssr
|
|
586
595
|
)
|
|
587
596
|
|
|
588
|
-
const appDir = path.
|
|
597
|
+
const appDir = path.resolve(process.cwd(), getRouterRootFromOneOptions(options))
|
|
589
598
|
|
|
590
599
|
// on change ./app stuff lets reload this to pick up any route changes
|
|
591
|
-
const fileWatcherChangeListener = debounce(
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
)
|
|
600
|
+
const fileWatcherChangeListener = debounce((type: string, changedPath: string) => {
|
|
601
|
+
if (
|
|
602
|
+
isRouteFileWatchEvent({
|
|
603
|
+
event: type,
|
|
604
|
+
filePath: changedPath,
|
|
605
|
+
routerRoot: appDir,
|
|
606
|
+
})
|
|
607
|
+
) {
|
|
608
|
+
recreateRequestHandler(changedPath)
|
|
609
|
+
}
|
|
610
|
+
}, 100)
|
|
603
611
|
|
|
604
612
|
server.watcher.addListener('all', fileWatcherChangeListener)
|
|
605
613
|
|
|
@@ -3,6 +3,7 @@ import { debounce } from 'perfect-debounce'
|
|
|
3
3
|
import type { Plugin } from 'vite'
|
|
4
4
|
import { generateRouteTypes } from '../../typed-routes/generateRouteTypes'
|
|
5
5
|
import { getRouterRootFromOneOptions } from '../../utils/getRouterRootFromOneOptions'
|
|
6
|
+
import { isRouteFileWatchEvent } from '../../utils/routeFileWatch'
|
|
6
7
|
import type { One } from '../types'
|
|
7
8
|
|
|
8
9
|
export function generateFileSystemRouteTypesPlugin(options: One.PluginOptions): Plugin {
|
|
@@ -12,7 +13,7 @@ export function generateFileSystemRouteTypesPlugin(options: One.PluginOptions):
|
|
|
12
13
|
apply: 'serve',
|
|
13
14
|
|
|
14
15
|
configureServer(server) {
|
|
15
|
-
const appDir =
|
|
16
|
+
const appDir = resolve(process.cwd(), getRouterRootFromOneOptions(options))
|
|
16
17
|
// Generate routes.d.ts inside the app directory to keep it organized
|
|
17
18
|
const outFile = join(appDir, 'routes.d.ts')
|
|
18
19
|
|
|
@@ -22,22 +23,20 @@ export function generateFileSystemRouteTypesPlugin(options: One.PluginOptions):
|
|
|
22
23
|
|
|
23
24
|
// on change ./app stuff lets reload this to pick up any route changes
|
|
24
25
|
const fileWatcherChangeListener = debounce(async (type: string, path: string) => {
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
)
|
|
40
|
-
}
|
|
26
|
+
if (
|
|
27
|
+
isRouteFileWatchEvent({
|
|
28
|
+
event: type,
|
|
29
|
+
filePath: path,
|
|
30
|
+
routerRoot: appDir,
|
|
31
|
+
includeChangeEvents: true,
|
|
32
|
+
})
|
|
33
|
+
) {
|
|
34
|
+
generateRouteTypes(
|
|
35
|
+
outFile,
|
|
36
|
+
routerRoot,
|
|
37
|
+
options.router?.ignoredRouteFiles,
|
|
38
|
+
typedRoutesGeneration
|
|
39
|
+
)
|
|
41
40
|
}
|
|
42
41
|
}, 100)
|
|
43
42
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateRouteTypes.d.ts","sourceRoot":"","sources":["../../src/typed-routes/generateRouteTypes.ts"],"names":[],"mappings":"AAWA,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,iBAAiB,CAAC,EAAE,MAAM,EAAE,EAC5B,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"generateRouteTypes.d.ts","sourceRoot":"","sources":["../../src/typed-routes/generateRouteTypes.ts"],"names":[],"mappings":"AAWA,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,iBAAiB,CAAC,EAAE,MAAM,EAAE,EAC5B,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,iBAyErC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateRouteTypes.test.d.ts","sourceRoot":"","sources":["../../src/typed-routes/generateRouteTypes.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getTypedRoutesDeclarationFile.test.d.ts","sourceRoot":"","sources":["../../src/typed-routes/getTypedRoutesDeclarationFile.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function isRouteFilePath(filePath: string): boolean;
|
|
2
|
+
export declare function isPathInsideDirectory(filePath: string, directory: string): boolean;
|
|
3
|
+
export declare function isRouteFileWatchEvent({ event, filePath, routerRoot, includeChangeEvents, }: {
|
|
4
|
+
event: string;
|
|
5
|
+
filePath: string;
|
|
6
|
+
routerRoot: string;
|
|
7
|
+
includeChangeEvents?: boolean;
|
|
8
|
+
}): boolean;
|
|
9
|
+
//# sourceMappingURL=routeFileWatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routeFileWatch.d.ts","sourceRoot":"","sources":["../../src/utils/routeFileWatch.ts"],"names":[],"mappings":"AAIA,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,WAE/C;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,WAOxE;AAED,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,QAAQ,EACR,UAAU,EACV,mBAA2B,GAC5B,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC9B,WAYA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routeFileWatch.test.d.ts","sourceRoot":"","sources":["../../src/utils/routeFileWatch.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileSystemRouterPlugin.d.ts","sourceRoot":"","sources":["../../../src/vite/plugins/fileSystemRouterPlugin.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAW,MAAM,EAAiB,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"fileSystemRouterPlugin.d.ts","sourceRoot":"","sources":["../../../src/vite/plugins/fileSystemRouterPlugin.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAW,MAAM,EAAiB,MAAM,MAAM,CAAA;AAgB1D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAiB3C,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,GAAG,CAAC,aAAa,GAAG,MAAM,CAquB/E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileSystemRouterPlugin.test.d.ts","sourceRoot":"","sources":["../../../src/vite/plugins/fileSystemRouterPlugin.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateFileSystemRouteTypesPlugin.d.ts","sourceRoot":"","sources":["../../../src/vite/plugins/generateFileSystemRouteTypesPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"generateFileSystemRouteTypesPlugin.d.ts","sourceRoot":"","sources":["../../../src/vite/plugins/generateFileSystemRouteTypesPlugin.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAIlC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAEnC,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,GAAG,CAAC,aAAa,GAAG,MAAM,CAgDrF"}
|