@tanstack/router-plugin 1.20.3-alpha.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/LICENSE +21 -0
- package/README.md +13 -0
- package/dist/cjs/core/code-splitter/compilers.cjs +755 -0
- package/dist/cjs/core/code-splitter/compilers.cjs.map +1 -0
- package/dist/cjs/core/code-splitter/compilers.d.cts +22 -0
- package/dist/cjs/core/code-splitter/framework-options.cjs +34 -0
- package/dist/cjs/core/code-splitter/framework-options.cjs.map +1 -0
- package/dist/cjs/core/code-splitter/framework-options.d.cts +10 -0
- package/dist/cjs/core/code-splitter/path-ids.cjs +37 -0
- package/dist/cjs/core/code-splitter/path-ids.cjs.map +1 -0
- package/dist/cjs/core/code-splitter/path-ids.d.cts +2 -0
- package/dist/cjs/core/config.cjs +46 -0
- package/dist/cjs/core/config.cjs.map +1 -0
- package/dist/cjs/core/config.d.cts +160 -0
- package/dist/cjs/core/constants.cjs +19 -0
- package/dist/cjs/core/constants.cjs.map +1 -0
- package/dist/cjs/core/constants.d.cts +5 -0
- package/dist/cjs/core/route-autoimport-plugin.cjs +98 -0
- package/dist/cjs/core/route-autoimport-plugin.cjs.map +1 -0
- package/dist/cjs/core/route-autoimport-plugin.d.cts +6 -0
- package/dist/cjs/core/route-hmr-statement.cjs +33 -0
- package/dist/cjs/core/route-hmr-statement.cjs.map +1 -0
- package/dist/cjs/core/route-hmr-statement.d.cts +1 -0
- package/dist/cjs/core/router-code-splitter-plugin.cjs +173 -0
- package/dist/cjs/core/router-code-splitter-plugin.cjs.map +1 -0
- package/dist/cjs/core/router-code-splitter-plugin.d.cts +3 -0
- package/dist/cjs/core/router-composed-plugin.cjs +27 -0
- package/dist/cjs/core/router-composed-plugin.cjs.map +1 -0
- package/dist/cjs/core/router-composed-plugin.d.cts +3 -0
- package/dist/cjs/core/router-generator-plugin.cjs +145 -0
- package/dist/cjs/core/router-generator-plugin.cjs.map +1 -0
- package/dist/cjs/core/router-generator-plugin.d.cts +3 -0
- package/dist/cjs/core/router-hmr-plugin.cjs +51 -0
- package/dist/cjs/core/router-hmr-plugin.cjs.map +1 -0
- package/dist/cjs/core/router-hmr-plugin.d.cts +8 -0
- package/dist/cjs/core/utils.cjs +12 -0
- package/dist/cjs/core/utils.cjs.map +1 -0
- package/dist/cjs/core/utils.d.cts +2 -0
- package/dist/cjs/esbuild.cjs +20 -0
- package/dist/cjs/esbuild.cjs.map +1 -0
- package/dist/cjs/esbuild.d.cts +127 -0
- package/dist/cjs/index.cjs +9 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/index.d.cts +4 -0
- package/dist/cjs/rspack.cjs +22 -0
- package/dist/cjs/rspack.cjs.map +1 -0
- package/dist/cjs/rspack.d.cts +139 -0
- package/dist/cjs/vite.cjs +22 -0
- package/dist/cjs/vite.cjs.map +1 -0
- package/dist/cjs/vite.d.cts +159 -0
- package/dist/cjs/webpack.cjs +22 -0
- package/dist/cjs/webpack.cjs.map +1 -0
- package/dist/cjs/webpack.d.cts +127 -0
- package/dist/esm/core/code-splitter/compilers.d.ts +22 -0
- package/dist/esm/core/code-splitter/compilers.js +737 -0
- package/dist/esm/core/code-splitter/compilers.js.map +1 -0
- package/dist/esm/core/code-splitter/framework-options.d.ts +10 -0
- package/dist/esm/core/code-splitter/framework-options.js +34 -0
- package/dist/esm/core/code-splitter/framework-options.js.map +1 -0
- package/dist/esm/core/code-splitter/path-ids.d.ts +2 -0
- package/dist/esm/core/code-splitter/path-ids.js +37 -0
- package/dist/esm/core/code-splitter/path-ids.js.map +1 -0
- package/dist/esm/core/config.d.ts +160 -0
- package/dist/esm/core/config.js +46 -0
- package/dist/esm/core/config.js.map +1 -0
- package/dist/esm/core/constants.d.ts +5 -0
- package/dist/esm/core/constants.js +19 -0
- package/dist/esm/core/constants.js.map +1 -0
- package/dist/esm/core/route-autoimport-plugin.d.ts +6 -0
- package/dist/esm/core/route-autoimport-plugin.js +81 -0
- package/dist/esm/core/route-autoimport-plugin.js.map +1 -0
- package/dist/esm/core/route-hmr-statement.d.ts +1 -0
- package/dist/esm/core/route-hmr-statement.js +16 -0
- package/dist/esm/core/route-hmr-statement.js.map +1 -0
- package/dist/esm/core/router-code-splitter-plugin.d.ts +3 -0
- package/dist/esm/core/router-code-splitter-plugin.js +173 -0
- package/dist/esm/core/router-code-splitter-plugin.js.map +1 -0
- package/dist/esm/core/router-composed-plugin.d.ts +3 -0
- package/dist/esm/core/router-composed-plugin.js +27 -0
- package/dist/esm/core/router-composed-plugin.js.map +1 -0
- package/dist/esm/core/router-generator-plugin.d.ts +3 -0
- package/dist/esm/core/router-generator-plugin.js +123 -0
- package/dist/esm/core/router-generator-plugin.js.map +1 -0
- package/dist/esm/core/router-hmr-plugin.d.ts +8 -0
- package/dist/esm/core/router-hmr-plugin.js +51 -0
- package/dist/esm/core/router-hmr-plugin.js.map +1 -0
- package/dist/esm/core/utils.d.ts +2 -0
- package/dist/esm/core/utils.js +12 -0
- package/dist/esm/core/utils.js.map +1 -0
- package/dist/esm/esbuild.d.ts +127 -0
- package/dist/esm/esbuild.js +20 -0
- package/dist/esm/esbuild.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +9 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/rspack.d.ts +139 -0
- package/dist/esm/rspack.js +22 -0
- package/dist/esm/rspack.js.map +1 -0
- package/dist/esm/vite.d.ts +159 -0
- package/dist/esm/vite.js +22 -0
- package/dist/esm/vite.js.map +1 -0
- package/dist/esm/webpack.d.ts +127 -0
- package/dist/esm/webpack.js +22 -0
- package/dist/esm/webpack.js.map +1 -0
- package/package.json +133 -0
- package/src/core/code-splitter/compilers.ts +1005 -0
- package/src/core/code-splitter/framework-options.ts +41 -0
- package/src/core/code-splitter/path-ids.ts +39 -0
- package/src/core/config.ts +80 -0
- package/src/core/constants.ts +17 -0
- package/src/core/route-autoimport-plugin.ts +102 -0
- package/src/core/route-hmr-statement.ts +13 -0
- package/src/core/router-code-splitter-plugin.ts +253 -0
- package/src/core/router-composed-plugin.ts +32 -0
- package/src/core/router-generator-plugin.ts +172 -0
- package/src/core/router-hmr-plugin.ts +65 -0
- package/src/core/utils.ts +18 -0
- package/src/esbuild.ts +56 -0
- package/src/index.ts +4 -0
- package/src/rspack.ts +67 -0
- package/src/vite.ts +57 -0
- package/src/webpack.ts +55 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
type FrameworkOptions = {
|
|
2
|
+
package: string
|
|
3
|
+
idents: {
|
|
4
|
+
createFileRoute: string
|
|
5
|
+
lazyFn: string
|
|
6
|
+
lazyRouteComponent: string
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function getFrameworkOptions(framework: string): FrameworkOptions {
|
|
11
|
+
let frameworkOptions: FrameworkOptions
|
|
12
|
+
|
|
13
|
+
switch (framework) {
|
|
14
|
+
case 'react':
|
|
15
|
+
frameworkOptions = {
|
|
16
|
+
package: '@tanstack/react-router',
|
|
17
|
+
idents: {
|
|
18
|
+
createFileRoute: 'createFileRoute',
|
|
19
|
+
lazyFn: 'lazyFn',
|
|
20
|
+
lazyRouteComponent: 'lazyRouteComponent',
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
break
|
|
24
|
+
case 'solid':
|
|
25
|
+
frameworkOptions = {
|
|
26
|
+
package: '@tanstack/solid-router',
|
|
27
|
+
idents: {
|
|
28
|
+
createFileRoute: 'createFileRoute',
|
|
29
|
+
lazyFn: 'lazyFn',
|
|
30
|
+
lazyRouteComponent: 'lazyRouteComponent',
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
break
|
|
34
|
+
default:
|
|
35
|
+
throw new Error(
|
|
36
|
+
`[getFrameworkOptions] - Unsupported framework: ${framework}`,
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return frameworkOptions
|
|
41
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function createIdentifier(strings: Array<string>): string {
|
|
2
|
+
if (strings.length === 0) {
|
|
3
|
+
throw new Error('Cannot create an identifier from an empty array')
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const sortedStrings = [...strings].sort()
|
|
7
|
+
const combinedString = sortedStrings.join('---') // Delimiter
|
|
8
|
+
|
|
9
|
+
// Replace unsafe characters
|
|
10
|
+
let safeString = combinedString.replace(/\//g, '--slash--')
|
|
11
|
+
safeString = safeString.replace(/\\/g, '--backslash--')
|
|
12
|
+
safeString = safeString.replace(/\?/g, '--question--')
|
|
13
|
+
safeString = safeString.replace(/%/g, '--percent--')
|
|
14
|
+
safeString = safeString.replace(/#/g, '--hash--')
|
|
15
|
+
safeString = safeString.replace(/\+/g, '--plus--')
|
|
16
|
+
safeString = safeString.replace(/=/g, '--equals--')
|
|
17
|
+
safeString = safeString.replace(/&/g, '--ampersand--')
|
|
18
|
+
safeString = safeString.replace(/\s/g, '_') // Replace spaces with underscores
|
|
19
|
+
|
|
20
|
+
return safeString
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function decodeIdentifier(identifier: string): Array<string> {
|
|
24
|
+
if (!identifier) {
|
|
25
|
+
return []
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let combinedString = identifier.replace(/--slash--/g, '/')
|
|
29
|
+
combinedString = combinedString.replace(/--backslash--/g, '\\')
|
|
30
|
+
combinedString = combinedString.replace(/--question--/g, '?')
|
|
31
|
+
combinedString = combinedString.replace(/--percent--/g, '%')
|
|
32
|
+
combinedString = combinedString.replace(/--hash--/g, '#')
|
|
33
|
+
combinedString = combinedString.replace(/--plus--/g, '+')
|
|
34
|
+
combinedString = combinedString.replace(/--equals--/g, '=')
|
|
35
|
+
combinedString = combinedString.replace(/--ampersand--/g, '&')
|
|
36
|
+
combinedString = combinedString.replace(/_/g, ' ') // Restore spaces
|
|
37
|
+
|
|
38
|
+
return combinedString.split('---')
|
|
39
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import {
|
|
3
|
+
configSchema as generatorConfigSchema,
|
|
4
|
+
getConfig as getGeneratorConfig,
|
|
5
|
+
} from '@tanstack/router-generator'
|
|
6
|
+
import type { RegisteredRouter, RouteIds } from '@tanstack/router-core'
|
|
7
|
+
import type { CodeSplitGroupings } from './constants'
|
|
8
|
+
|
|
9
|
+
export const splitGroupingsSchema = z
|
|
10
|
+
.array(
|
|
11
|
+
z.array(
|
|
12
|
+
z.union([
|
|
13
|
+
z.literal('loader'),
|
|
14
|
+
z.literal('component'),
|
|
15
|
+
z.literal('pendingComponent'),
|
|
16
|
+
z.literal('errorComponent'),
|
|
17
|
+
z.literal('notFoundComponent'),
|
|
18
|
+
]),
|
|
19
|
+
),
|
|
20
|
+
{
|
|
21
|
+
message:
|
|
22
|
+
" Must be an Array of Arrays containing the split groupings. i.e. [['component'], ['pendingComponent'], ['errorComponent', 'notFoundComponent']]",
|
|
23
|
+
},
|
|
24
|
+
)
|
|
25
|
+
.superRefine((val, ctx) => {
|
|
26
|
+
const flattened = val.flat()
|
|
27
|
+
const unique = [...new Set(flattened)]
|
|
28
|
+
|
|
29
|
+
// Elements must be unique,
|
|
30
|
+
// ie. this shouldn't be allows [['component'], ['component', 'loader']]
|
|
31
|
+
if (unique.length !== flattened.length) {
|
|
32
|
+
ctx.addIssue({
|
|
33
|
+
code: 'custom',
|
|
34
|
+
message:
|
|
35
|
+
" Split groupings must be unique and not repeated. i.e. i.e. [['component'], ['pendingComponent'], ['errorComponent', 'notFoundComponent']]." +
|
|
36
|
+
`\n You input was: ${JSON.stringify(val)}.`,
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
export type CodeSplittingOptions = {
|
|
42
|
+
/**
|
|
43
|
+
* Use this function to programmatically control the code splitting behavior
|
|
44
|
+
* based on the `routeId` for each route.
|
|
45
|
+
*
|
|
46
|
+
* If you just need to change the default behavior, you can use the `defaultBehavior` option.
|
|
47
|
+
* @param params
|
|
48
|
+
*/
|
|
49
|
+
splitBehavior?: (params: {
|
|
50
|
+
routeId: RouteIds<RegisteredRouter['routeTree']>
|
|
51
|
+
}) => CodeSplitGroupings | undefined | void
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The default/global configuration to control your code splitting behavior per route.
|
|
55
|
+
* @default [['component'],['pendingComponent'],['errorComponent'],['notFoundComponent']]
|
|
56
|
+
*/
|
|
57
|
+
defaultBehavior?: CodeSplitGroupings
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const codeSplittingOptionsSchema = z.object({
|
|
61
|
+
splitBehavior: z.function().optional(),
|
|
62
|
+
defaultBehavior: splitGroupingsSchema.optional(),
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
export const configSchema = generatorConfigSchema.extend({
|
|
66
|
+
enableRouteGeneration: z.boolean().optional(),
|
|
67
|
+
codeSplittingOptions: z
|
|
68
|
+
.custom<CodeSplittingOptions>((v) => {
|
|
69
|
+
return codeSplittingOptionsSchema.parse(v)
|
|
70
|
+
})
|
|
71
|
+
.optional(),
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
export const getConfig = (inlineConfig: Partial<Config>, root: string) => {
|
|
75
|
+
const config = getGeneratorConfig(inlineConfig, root)
|
|
76
|
+
|
|
77
|
+
return configSchema.parse({ ...config, ...inlineConfig })
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type Config = z.infer<typeof configSchema>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const tsrSplit = 'tsr-split'
|
|
2
|
+
|
|
3
|
+
export const splitRouteIdentNodes = [
|
|
4
|
+
'loader',
|
|
5
|
+
'component',
|
|
6
|
+
'pendingComponent',
|
|
7
|
+
'errorComponent',
|
|
8
|
+
'notFoundComponent',
|
|
9
|
+
] as const
|
|
10
|
+
export type SplitRouteIdentNodes = (typeof splitRouteIdentNodes)[number]
|
|
11
|
+
export type CodeSplitGroupings = Array<Array<SplitRouteIdentNodes>>
|
|
12
|
+
|
|
13
|
+
export const defaultCodeSplitGroupings: CodeSplitGroupings = [
|
|
14
|
+
['component'],
|
|
15
|
+
['errorComponent'],
|
|
16
|
+
['notFoundComponent'],
|
|
17
|
+
]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { generateFromAst, logDiff, parseAst } from '@tanstack/router-utils'
|
|
2
|
+
import babel from '@babel/core'
|
|
3
|
+
import * as template from '@babel/template'
|
|
4
|
+
import { getConfig } from './config'
|
|
5
|
+
import { debug, fileIsInRoutesDirectory } from './utils'
|
|
6
|
+
import type { Config } from './config'
|
|
7
|
+
import type { UnpluginFactory } from 'unplugin'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* This plugin adds imports for createFileRoute and createLazyFileRoute to the file route.
|
|
11
|
+
*/
|
|
12
|
+
export const unpluginRouteAutoimportFactory: UnpluginFactory<
|
|
13
|
+
Partial<Config> | undefined
|
|
14
|
+
> = (options = {}) => {
|
|
15
|
+
let ROOT: string = process.cwd()
|
|
16
|
+
let userConfig = options as Config
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
name: 'router-autoimport-plugin',
|
|
20
|
+
enforce: 'pre',
|
|
21
|
+
|
|
22
|
+
transform(code, id) {
|
|
23
|
+
let routeType: 'createFileRoute' | 'createLazyFileRoute'
|
|
24
|
+
if (code.includes('export const Route = createFileRoute(')) {
|
|
25
|
+
routeType = 'createFileRoute'
|
|
26
|
+
} else if (code.includes('export const Route = createLazyFileRoute(')) {
|
|
27
|
+
routeType = 'createLazyFileRoute'
|
|
28
|
+
} else {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const routerImportPath = `@tanstack/${userConfig.target}-router`
|
|
33
|
+
|
|
34
|
+
const ast = parseAst({ code })
|
|
35
|
+
|
|
36
|
+
let isCreateRouteFunctionImported = false as boolean
|
|
37
|
+
|
|
38
|
+
babel.traverse(ast, {
|
|
39
|
+
Program: {
|
|
40
|
+
enter(programPath) {
|
|
41
|
+
programPath.traverse({
|
|
42
|
+
ImportDeclaration(path) {
|
|
43
|
+
const importedSpecifiers = path.node.specifiers.map(
|
|
44
|
+
(specifier) => specifier.local.name,
|
|
45
|
+
)
|
|
46
|
+
if (
|
|
47
|
+
importedSpecifiers.includes(routeType) &&
|
|
48
|
+
path.node.source.value === routerImportPath
|
|
49
|
+
) {
|
|
50
|
+
isCreateRouteFunctionImported = true
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
if (!isCreateRouteFunctionImported) {
|
|
59
|
+
if (debug) console.info('Adding autoimports to route ', id)
|
|
60
|
+
|
|
61
|
+
const autoImportStatement = template.statement(
|
|
62
|
+
`import { ${routeType} } from '${routerImportPath}'`,
|
|
63
|
+
)()
|
|
64
|
+
ast.program.body.unshift(autoImportStatement)
|
|
65
|
+
|
|
66
|
+
const result = generateFromAst(ast, {
|
|
67
|
+
sourceMaps: true,
|
|
68
|
+
filename: id,
|
|
69
|
+
sourceFileName: id,
|
|
70
|
+
})
|
|
71
|
+
if (debug) {
|
|
72
|
+
logDiff(code, result.code)
|
|
73
|
+
console.log('Output:\n', result.code + '\n\n')
|
|
74
|
+
}
|
|
75
|
+
return result
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return null
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
transformInclude(id) {
|
|
82
|
+
return fileIsInRoutesDirectory(id, userConfig.routesDirectory)
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
vite: {
|
|
86
|
+
configResolved(config) {
|
|
87
|
+
ROOT = config.root
|
|
88
|
+
userConfig = getConfig(options, ROOT)
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
rspack() {
|
|
93
|
+
ROOT = process.cwd()
|
|
94
|
+
userConfig = getConfig(options, ROOT)
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
webpack() {
|
|
98
|
+
ROOT = process.cwd()
|
|
99
|
+
userConfig = getConfig(options, ROOT)
|
|
100
|
+
},
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as template from '@babel/template'
|
|
2
|
+
|
|
3
|
+
export const routeHmrStatement = template.statement(
|
|
4
|
+
`
|
|
5
|
+
if (import.meta.hot) {
|
|
6
|
+
import.meta.hot.accept((newModule) => {
|
|
7
|
+
if (newModule.Route && typeof newModule.Route.clone === 'function') {
|
|
8
|
+
newModule.Route.clone(Route)
|
|
9
|
+
}
|
|
10
|
+
})
|
|
11
|
+
}
|
|
12
|
+
`,
|
|
13
|
+
)()
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* It is important to familiarize yourself with how the code-splitting works in this plugin.
|
|
3
|
+
* https://github.com/TanStack/router/pull/3355
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
7
|
+
import { logDiff } from '@tanstack/router-utils'
|
|
8
|
+
import { getConfig, splitGroupingsSchema } from './config'
|
|
9
|
+
import {
|
|
10
|
+
compileCodeSplitReferenceRoute,
|
|
11
|
+
compileCodeSplitVirtualRoute,
|
|
12
|
+
detectCodeSplitGroupingsFromRoute,
|
|
13
|
+
} from './code-splitter/compilers'
|
|
14
|
+
import {
|
|
15
|
+
defaultCodeSplitGroupings,
|
|
16
|
+
splitRouteIdentNodes,
|
|
17
|
+
tsrSplit,
|
|
18
|
+
} from './constants'
|
|
19
|
+
import { decodeIdentifier } from './code-splitter/path-ids'
|
|
20
|
+
import { debug, fileIsInRoutesDirectory } from './utils'
|
|
21
|
+
import type { CodeSplitGroupings, SplitRouteIdentNodes } from './constants'
|
|
22
|
+
|
|
23
|
+
import type { Config } from './config'
|
|
24
|
+
import type {
|
|
25
|
+
UnpluginContextMeta,
|
|
26
|
+
UnpluginFactory,
|
|
27
|
+
TransformResult as UnpluginTransformResult,
|
|
28
|
+
} from 'unplugin'
|
|
29
|
+
|
|
30
|
+
function capitalizeFirst(str: string): string {
|
|
31
|
+
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type BannedBeforeExternalPlugin = {
|
|
35
|
+
identifier: string
|
|
36
|
+
pkg: string
|
|
37
|
+
usage: string
|
|
38
|
+
frameworks: Array<UnpluginContextMeta['framework']>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const bannedBeforeExternalPlugins: Array<BannedBeforeExternalPlugin> = [
|
|
42
|
+
{
|
|
43
|
+
identifier: '@react-refresh',
|
|
44
|
+
pkg: '@vitejs/plugin-react',
|
|
45
|
+
usage: 'viteReact()',
|
|
46
|
+
frameworks: ['vite'],
|
|
47
|
+
},
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
class FoundPluginInBeforeCode extends Error {
|
|
51
|
+
constructor(externalPlugin: BannedBeforeExternalPlugin, framework: string) {
|
|
52
|
+
super(`We detected that the '${externalPlugin.pkg}' was passed before '@tanstack/router-plugin'. Please make sure that '@tanstack/router-plugin' is passed before '${externalPlugin.pkg}' and try again:
|
|
53
|
+
e.g.
|
|
54
|
+
plugins: [
|
|
55
|
+
TanStackRouter${capitalizeFirst(framework)}(), // Place this before ${externalPlugin.usage}
|
|
56
|
+
${externalPlugin.usage},
|
|
57
|
+
]
|
|
58
|
+
`)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const PLUGIN_NAME = 'unplugin:router-code-splitter'
|
|
63
|
+
|
|
64
|
+
export const unpluginRouterCodeSplitterFactory: UnpluginFactory<
|
|
65
|
+
Partial<Config> | undefined
|
|
66
|
+
> = (options = {}, { framework }) => {
|
|
67
|
+
let ROOT: string = process.cwd()
|
|
68
|
+
let userConfig = options as Config
|
|
69
|
+
|
|
70
|
+
const isProduction = process.env.NODE_ENV === 'production'
|
|
71
|
+
|
|
72
|
+
const getGlobalCodeSplitGroupings = () => {
|
|
73
|
+
return (
|
|
74
|
+
userConfig.codeSplittingOptions?.defaultBehavior ||
|
|
75
|
+
defaultCodeSplitGroupings
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
const getShouldSplitFn = () => {
|
|
79
|
+
return userConfig.codeSplittingOptions?.splitBehavior
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const handleCompilingReferenceFile = (
|
|
83
|
+
code: string,
|
|
84
|
+
id: string,
|
|
85
|
+
): UnpluginTransformResult => {
|
|
86
|
+
if (debug) console.info('Compiling Route: ', id)
|
|
87
|
+
|
|
88
|
+
const fromCode = detectCodeSplitGroupingsFromRoute({
|
|
89
|
+
code,
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
if (fromCode.groupings) {
|
|
93
|
+
const res = splitGroupingsSchema.safeParse(fromCode.groupings)
|
|
94
|
+
if (!res.success) {
|
|
95
|
+
const message = res.error.errors.map((e) => e.message).join('. ')
|
|
96
|
+
throw new Error(
|
|
97
|
+
`The groupings for the route "${id}" are invalid.\n${message}`,
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const userShouldSplitFn = getShouldSplitFn()
|
|
103
|
+
|
|
104
|
+
const pluginSplitBehavior = userShouldSplitFn?.({
|
|
105
|
+
routeId: fromCode.routeId,
|
|
106
|
+
}) as CodeSplitGroupings | undefined
|
|
107
|
+
|
|
108
|
+
if (pluginSplitBehavior) {
|
|
109
|
+
const res = splitGroupingsSchema.safeParse(pluginSplitBehavior)
|
|
110
|
+
if (!res.success) {
|
|
111
|
+
const message = res.error.errors.map((e) => e.message).join('. ')
|
|
112
|
+
throw new Error(
|
|
113
|
+
`The groupings returned when using \`splitBehavior\` for the route "${id}" are invalid.\n${message}`,
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const splitGroupings: CodeSplitGroupings =
|
|
119
|
+
fromCode.groupings || pluginSplitBehavior || getGlobalCodeSplitGroupings()
|
|
120
|
+
|
|
121
|
+
const compiledReferenceRoute = compileCodeSplitReferenceRoute({
|
|
122
|
+
code,
|
|
123
|
+
runtimeEnv: isProduction ? 'prod' : 'dev',
|
|
124
|
+
codeSplitGroupings: splitGroupings,
|
|
125
|
+
targetFramework: userConfig.target,
|
|
126
|
+
filename: id,
|
|
127
|
+
id,
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
if (debug) {
|
|
131
|
+
logDiff(code, compiledReferenceRoute.code)
|
|
132
|
+
console.log('Output:\n', compiledReferenceRoute.code + '\n\n')
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return compiledReferenceRoute
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const handleCompilingVirtualFile = (
|
|
139
|
+
code: string,
|
|
140
|
+
id: string,
|
|
141
|
+
): UnpluginTransformResult => {
|
|
142
|
+
if (debug) console.info('Splitting Route: ', id)
|
|
143
|
+
|
|
144
|
+
const [_, ...pathnameParts] = id.split('?')
|
|
145
|
+
|
|
146
|
+
const searchParams = new URLSearchParams(pathnameParts.join('?'))
|
|
147
|
+
const splitValue = searchParams.get(tsrSplit)
|
|
148
|
+
|
|
149
|
+
if (!splitValue) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`The split value for the virtual route "${id}" was not found.`,
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const rawGrouping = decodeIdentifier(splitValue)
|
|
156
|
+
const grouping = [...new Set(rawGrouping)].filter((p) =>
|
|
157
|
+
splitRouteIdentNodes.includes(p as any),
|
|
158
|
+
) as Array<SplitRouteIdentNodes>
|
|
159
|
+
|
|
160
|
+
const result = compileCodeSplitVirtualRoute({
|
|
161
|
+
code,
|
|
162
|
+
filename: id,
|
|
163
|
+
splitTargets: grouping,
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
if (debug) {
|
|
167
|
+
logDiff(code, result.code)
|
|
168
|
+
console.log('Output:\n', result.code + '\n\n')
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return result
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
name: 'router-code-splitter-plugin',
|
|
176
|
+
enforce: 'pre',
|
|
177
|
+
|
|
178
|
+
transform(code, id) {
|
|
179
|
+
if (!userConfig.autoCodeSplitting) {
|
|
180
|
+
return null
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const url = pathToFileURL(id)
|
|
184
|
+
url.searchParams.delete('v')
|
|
185
|
+
id = fileURLToPath(url).replace(/\\/g, '/')
|
|
186
|
+
|
|
187
|
+
if (id.includes(tsrSplit)) {
|
|
188
|
+
return handleCompilingVirtualFile(code, id)
|
|
189
|
+
} else if (
|
|
190
|
+
fileIsInRoutesDirectory(id, userConfig.routesDirectory) &&
|
|
191
|
+
(code.includes('createRoute(') || code.includes('createFileRoute('))
|
|
192
|
+
) {
|
|
193
|
+
for (const externalPlugin of bannedBeforeExternalPlugins) {
|
|
194
|
+
if (!externalPlugin.frameworks.includes(framework)) {
|
|
195
|
+
continue
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (code.includes(externalPlugin.identifier)) {
|
|
199
|
+
throw new FoundPluginInBeforeCode(externalPlugin, framework)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return handleCompilingReferenceFile(code, id)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return null
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
transformInclude(id) {
|
|
210
|
+
if (!userConfig.autoCodeSplitting) {
|
|
211
|
+
return undefined
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (
|
|
215
|
+
fileIsInRoutesDirectory(id, userConfig.routesDirectory) ||
|
|
216
|
+
id.includes(tsrSplit)
|
|
217
|
+
) {
|
|
218
|
+
return true
|
|
219
|
+
}
|
|
220
|
+
return false
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
vite: {
|
|
224
|
+
configResolved(config) {
|
|
225
|
+
ROOT = config.root
|
|
226
|
+
|
|
227
|
+
userConfig = getConfig(options, ROOT)
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
rspack(_compiler) {
|
|
232
|
+
ROOT = process.cwd()
|
|
233
|
+
userConfig = getConfig(options, ROOT)
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
webpack(compiler) {
|
|
237
|
+
ROOT = process.cwd()
|
|
238
|
+
userConfig = getConfig(options, ROOT)
|
|
239
|
+
|
|
240
|
+
if (
|
|
241
|
+
userConfig.autoCodeSplitting &&
|
|
242
|
+
compiler.options.mode === 'production'
|
|
243
|
+
) {
|
|
244
|
+
compiler.hooks.done.tap(PLUGIN_NAME, () => {
|
|
245
|
+
console.info('✅ ' + PLUGIN_NAME + ': code-splitting done!')
|
|
246
|
+
setTimeout(() => {
|
|
247
|
+
process.exit(0)
|
|
248
|
+
})
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
}
|
|
253
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { unpluginRouterGeneratorFactory } from './router-generator-plugin'
|
|
2
|
+
import { unpluginRouterCodeSplitterFactory } from './router-code-splitter-plugin'
|
|
3
|
+
import { unpluginRouterHmrFactory } from './router-hmr-plugin'
|
|
4
|
+
import { unpluginRouteAutoimportFactory } from './route-autoimport-plugin'
|
|
5
|
+
import type { Config } from './config'
|
|
6
|
+
import type { UnpluginFactory } from 'unplugin'
|
|
7
|
+
|
|
8
|
+
export const unpluginRouterComposedFactory: UnpluginFactory<
|
|
9
|
+
Partial<Config> | undefined
|
|
10
|
+
> = (options = {}, meta) => {
|
|
11
|
+
const getPlugin = (pluginFactory: UnpluginFactory<Partial<Config>>) => {
|
|
12
|
+
const plugin = pluginFactory(options, meta)
|
|
13
|
+
if (!Array.isArray(plugin)) {
|
|
14
|
+
return [plugin]
|
|
15
|
+
}
|
|
16
|
+
return plugin
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const routerGenerator = getPlugin(unpluginRouterGeneratorFactory)
|
|
20
|
+
const routerCodeSplitter = getPlugin(unpluginRouterCodeSplitterFactory)
|
|
21
|
+
const routeAutoImport = getPlugin(unpluginRouteAutoimportFactory)
|
|
22
|
+
|
|
23
|
+
const result = [...routerGenerator, ...routerCodeSplitter, ...routeAutoImport]
|
|
24
|
+
|
|
25
|
+
const isProduction = process.env.NODE_ENV === 'production'
|
|
26
|
+
|
|
27
|
+
if (!isProduction && !options.autoCodeSplitting) {
|
|
28
|
+
const routerHmr = getPlugin(unpluginRouterHmrFactory)
|
|
29
|
+
result.push(...routerHmr)
|
|
30
|
+
}
|
|
31
|
+
return result
|
|
32
|
+
}
|