vite-plugin-taro 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +44 -0
- package/README.md +22 -497
- package/client.d.ts +9 -0
- package/dist/node/plugins/client/client-taro.d.ts +4 -0
- package/dist/node/plugins/client/client-taro.js +19 -0
- package/dist/node/plugins/client/constant.d.ts +4 -0
- package/dist/node/plugins/client/constant.js +4 -0
- package/dist/node/plugins/conditional/conditional-directives.d.ts +4 -0
- package/dist/node/plugins/{conditional-directives.js → conditional/conditional-directives.js} +27 -11
- package/dist/node/plugins/css/plugins.d.ts +4 -0
- package/dist/node/plugins/css/plugins.js +84 -0
- package/dist/node/plugins/h5/constant.d.ts +2 -0
- package/dist/node/plugins/h5/constant.js +3 -0
- package/dist/node/plugins/h5/plugins.d.ts +4 -0
- package/dist/node/plugins/h5/plugins.js +161 -0
- package/dist/node/plugins/h5/resolver/module-resolver.d.ts +14 -0
- package/dist/node/plugins/h5/resolver/module-resolver.js +19 -0
- package/dist/node/plugins/h5/transform-app.d.ts +10 -0
- package/dist/node/plugins/h5/transform-app.js +48 -0
- package/dist/node/plugins/wx/dev/dev-host.d.ts +15 -0
- package/dist/node/plugins/wx/dev/dev-host.js +352 -0
- package/dist/node/plugins/wx/dev/hmr-files.d.ts +28 -0
- package/dist/node/plugins/wx/dev/hmr-files.js +34 -0
- package/dist/node/plugins/wx/dev/patch-publisher.d.ts +22 -0
- package/dist/node/plugins/wx/dev/patch-publisher.js +40 -0
- package/dist/node/plugins/wx/dev/plugin.d.ts +7 -0
- package/dist/node/plugins/wx/dev/plugin.js +73 -0
- package/dist/node/plugins/wx/dev/react-refresh.d.ts +20 -0
- package/dist/node/plugins/wx/dev/react-refresh.js +198 -0
- package/dist/node/plugins/wx/module.d.ts +45 -0
- package/dist/node/plugins/wx/module.js +66 -0
- package/dist/node/plugins/wx/native/compile-native-component-interface.d.ts +21 -0
- package/dist/node/plugins/wx/native/compile-native-component-interface.js +29 -0
- package/dist/node/plugins/wx/native/create-native-component-output.d.ts +15 -0
- package/dist/node/plugins/wx/native/create-native-component-output.js +39 -0
- package/dist/node/plugins/wx/native/native-component-assets.d.ts +26 -0
- package/dist/node/plugins/wx/native/native-component-assets.js +76 -0
- package/dist/node/plugins/wx/native/native-component-interface.d.ts +12 -0
- package/dist/node/plugins/wx/native/native-component-interface.js +201 -0
- package/dist/node/plugins/wx/output/files.d.ts +12 -0
- package/dist/node/plugins/wx/output/files.js +12 -0
- package/dist/node/plugins/wx/output/json.d.ts +14 -0
- package/dist/node/plugins/wx/output/json.js +52 -0
- package/dist/node/plugins/wx/output/relative-root.d.ts +2 -0
- package/dist/node/plugins/wx/output/relative-root.js +6 -0
- package/dist/node/plugins/wx/output/templates.d.ts +7 -0
- package/dist/node/plugins/wx/output/templates.js +100 -0
- package/dist/node/plugins/wx/placement/placer.d.ts +78 -0
- package/dist/node/plugins/wx/placement/placer.js +145 -0
- package/dist/node/plugins/wx/placement/plan.d.ts +46 -0
- package/dist/node/plugins/wx/placement/plan.js +210 -0
- package/dist/node/plugins/wx/plugins.d.ts +4 -0
- package/dist/node/plugins/wx/plugins.js +137 -0
- package/dist/node/plugins/wx/render/capsule-wrapper.d.ts +3 -0
- package/dist/node/plugins/wx/render/capsule-wrapper.js +64 -0
- package/dist/node/plugins/wx/render/capsule.d.ts +4 -0
- package/dist/node/plugins/wx/render/capsule.js +13 -0
- package/dist/node/plugins/wx/render/native.d.ts +9 -0
- package/dist/node/plugins/wx/render/native.js +45 -0
- package/dist/node/plugins/wx/render/transport.d.ts +17 -0
- package/dist/node/plugins/wx/render/transport.js +94 -0
- package/dist/node/plugins/wx/resolve/resolver.d.ts +7 -0
- package/dist/node/plugins/wx/resolve/resolver.js +86 -0
- package/dist/node/plugins/wx/resolve/specialize-bootstrap.d.ts +9 -0
- package/dist/node/plugins/wx/resolve/specialize-bootstrap.js +9 -0
- package/dist/node/plugins/wx/resolve/specialize-page-capsule.d.ts +9 -0
- package/dist/node/plugins/wx/resolve/specialize-page-capsule.js +11 -0
- package/dist/node/utils/constant.d.ts +1 -0
- package/dist/node/utils/constant.js +1 -0
- package/dist/node/utils/memoize.d.ts +122 -0
- package/dist/node/utils/memoize.js +86 -0
- package/dist/node/utils/modules.d.ts +20 -24
- package/dist/node/utils/modules.js +21 -34
- package/dist/node/utils/once.d.ts +10 -0
- package/dist/node/utils/once.js +20 -0
- package/dist/node/utils/packages.d.ts +1 -0
- package/dist/node/utils/packages.js +1 -1
- package/dist/node/utils/project-config.d.ts +3 -0
- package/dist/node/utils/project-config.js +8 -0
- package/dist/node/utils/serialized-task-queue.d.ts +9 -0
- package/dist/node/utils/serialized-task-queue.js +17 -0
- package/dist/node/utils/transform.d.ts +11 -0
- package/dist/node/utils/transform.js +58 -0
- package/dist/node/vite-plugin.d.ts +1 -1
- package/dist/node/vite-plugin.js +11 -27
- package/dist/options.d.ts +3 -4
- package/dist/runtime/{taro → client/taro}/api.d.ts +0 -1
- package/dist/runtime/client/taro/api.js +9 -0
- package/dist/runtime/client/taro/component.d.ts +1 -0
- package/dist/runtime/client/taro/component.js +1 -0
- package/dist/runtime/client/taro/define-native-component.d.ts +7 -0
- package/dist/runtime/client/taro/define-native-component.js +4 -0
- package/dist/runtime/h5/app.d.ts +2 -0
- package/dist/runtime/h5/app.js +15 -0
- package/dist/runtime/h5/taro-runtime.d.ts +1 -1
- package/dist/runtime/h5/taro-runtime.js +1 -4
- package/dist/runtime/wx/amphibious/bootstrap.d.ts +4 -0
- package/dist/runtime/wx/amphibious/bootstrap.js +16 -0
- package/dist/runtime/wx/amphibious/transport.d.ts +2 -0
- package/dist/runtime/wx/amphibious/transport.js +4 -0
- package/dist/runtime/wx/capsule/app.d.ts +2 -0
- package/dist/runtime/wx/capsule/app.js +7 -0
- package/dist/runtime/wx/capsule/component.d.ts +3 -0
- package/dist/runtime/wx/capsule/component.js +4 -0
- package/dist/runtime/wx/capsule/page.d.ts +3 -0
- package/dist/runtime/wx/capsule/page.js +68 -0
- package/dist/runtime/wx/capsule/taro-runtime.d.ts +6 -0
- package/dist/runtime/wx/capsule/taro-runtime.js +6 -0
- package/dist/runtime/wx/dev/dev-runtime.d.ts +1 -0
- package/dist/runtime/wx/dev/dev-runtime.js +246 -0
- package/dist/runtime/wx/native/app.d.ts +1 -0
- package/dist/runtime/wx/native/app.js +3 -0
- package/dist/runtime/wx/native/component.d.ts +1 -0
- package/dist/runtime/wx/native/component.js +3 -0
- package/dist/runtime/wx/native/page.d.ts +1 -0
- package/dist/runtime/wx/native/page.js +4 -0
- package/dist/runtime/wx/systemjs/system-core.d.ts +4 -0
- package/dist/runtime/wx/systemjs/system-core.js +331 -0
- package/package.json +21 -14
- package/src/node/plugins/client/client-taro.ts +23 -0
- package/src/node/plugins/client/constant.ts +5 -0
- package/src/node/plugins/{conditional-directives.ts → conditional/conditional-directives.ts} +33 -13
- package/src/node/plugins/css/plugins.ts +96 -0
- package/src/node/plugins/h5/constant.ts +4 -0
- package/src/node/plugins/h5/plugins.ts +197 -0
- package/src/node/plugins/h5/resolver/module-resolver.ts +32 -0
- package/src/node/plugins/h5/transform-app.ts +90 -0
- package/src/node/plugins/wx/dev/dev-host.ts +448 -0
- package/src/node/plugins/wx/dev/hmr-files.ts +55 -0
- package/src/node/plugins/wx/dev/patch-publisher.ts +49 -0
- package/src/node/plugins/wx/dev/plugin.ts +82 -0
- package/src/node/plugins/wx/dev/react-refresh.ts +227 -0
- package/src/node/plugins/wx/module.ts +97 -0
- package/src/node/plugins/wx/native/compile-native-component-interface.ts +47 -0
- package/src/node/plugins/wx/native/create-native-component-output.ts +57 -0
- package/src/node/plugins/wx/native/native-component-assets.ts +114 -0
- package/src/node/plugins/wx/native/native-component-interface.ts +265 -0
- package/src/node/plugins/wx/output/files.ts +27 -0
- package/src/node/plugins/wx/output/json.ts +79 -0
- package/src/node/plugins/wx/output/relative-root.ts +7 -0
- package/src/node/plugins/wx/output/templates.ts +141 -0
- package/src/node/plugins/wx/placement/placer.ts +172 -0
- package/src/node/plugins/wx/placement/plan.ts +306 -0
- package/src/node/plugins/wx/plugins.ts +165 -0
- package/src/node/plugins/wx/render/capsule-wrapper.ts +86 -0
- package/src/node/plugins/wx/render/capsule.ts +21 -0
- package/src/node/plugins/wx/render/native.ts +84 -0
- package/src/node/plugins/wx/render/transport.ts +157 -0
- package/src/node/plugins/wx/resolve/resolver.ts +125 -0
- package/src/node/plugins/wx/resolve/specialize-bootstrap.ts +27 -0
- package/src/node/plugins/wx/resolve/specialize-page-capsule.ts +29 -0
- package/src/node/utils/constant.ts +1 -0
- package/src/node/utils/memoize.ts +149 -0
- package/src/node/utils/modules.ts +36 -38
- package/src/node/utils/once.ts +21 -0
- package/src/node/utils/packages.ts +1 -1
- package/src/node/utils/project-config.ts +10 -0
- package/src/node/utils/serialized-task-queue.ts +20 -0
- package/src/node/utils/transform.ts +82 -0
- package/src/node/vite-plugin.ts +12 -32
- package/src/options.ts +3 -4
- package/src/runtime/client/taro/api.ts +11 -0
- package/src/runtime/client/taro/component.ts +1 -0
- package/src/runtime/client/taro/define-native-component.ts +15 -0
- package/src/runtime/h5/app.ts +25 -0
- package/src/runtime/h5/taro-runtime.ts +1 -4
- package/src/runtime/wx/amphibious/bootstrap.ts +22 -0
- package/src/runtime/wx/amphibious/transport.ts +10 -0
- package/src/runtime/wx/capsule/app.ts +9 -0
- package/src/runtime/wx/capsule/component.ts +6 -0
- package/src/runtime/wx/capsule/page.ts +96 -0
- package/src/runtime/wx/capsule/taro-runtime.ts +14 -0
- package/src/runtime/wx/dev/dev-runtime.ts +317 -0
- package/src/runtime/wx/native/app.ts +4 -0
- package/src/runtime/wx/native/component.ts +4 -0
- package/src/runtime/wx/native/page.ts +6 -0
- package/src/runtime/wx/systemjs/LICENSE +10 -0
- package/src/runtime/wx/systemjs/system-core.js +370 -0
- package/src/runtime/wx/systemjs/system.d.ts +55 -0
- package/src/runtime/wx/wechat.d.ts +52 -0
- package/README.zh.md +0 -521
- package/dist/node/build-context.d.ts +0 -25
- package/dist/node/build-context.js +0 -47
- package/dist/node/css/css-pipeline.d.ts +0 -35
- package/dist/node/css/css-pipeline.js +0 -154
- package/dist/node/plugins/conditional-directives.d.ts +0 -3
- package/dist/node/plugins/taro-runtime.d.ts +0 -5
- package/dist/node/plugins/taro-runtime.js +0 -18
- package/dist/node/targets/h5/plugin.d.ts +0 -6
- package/dist/node/targets/h5/plugin.js +0 -117
- package/dist/node/targets/h5/virtual-modules.d.ts +0 -5
- package/dist/node/targets/h5/virtual-modules.js +0 -60
- package/dist/node/targets/wx/companion-assets.d.ts +0 -19
- package/dist/node/targets/wx/companion-assets.js +0 -129
- package/dist/node/targets/wx/dev-server/development-session.d.ts +0 -32
- package/dist/node/targets/wx/dev-server/development-session.js +0 -215
- package/dist/node/targets/wx/dev-server/full-build-scheduler.d.ts +0 -14
- package/dist/node/targets/wx/dev-server/full-build-scheduler.js +0 -60
- package/dist/node/targets/wx/dev-server/js-utils.d.ts +0 -5
- package/dist/node/targets/wx/dev-server/js-utils.js +0 -28
- package/dist/node/targets/wx/dev-server/output.d.ts +0 -19
- package/dist/node/targets/wx/dev-server/output.js +0 -44
- package/dist/node/targets/wx/dev-server/rolldown-runtime-source.d.ts +0 -8
- package/dist/node/targets/wx/dev-server/rolldown-runtime-source.js +0 -95
- package/dist/node/targets/wx/dev-server/update-server-state.d.ts +0 -65
- package/dist/node/targets/wx/dev-server/update-server-state.js +0 -116
- package/dist/node/targets/wx/dev-server/update-transport.d.ts +0 -28
- package/dist/node/targets/wx/dev-server/update-transport.js +0 -212
- package/dist/node/targets/wx/dev-server/vite-bundled-dev-adapter.d.ts +0 -44
- package/dist/node/targets/wx/dev-server/vite-bundled-dev-adapter.js +0 -145
- package/dist/node/targets/wx/development-files.d.ts +0 -8
- package/dist/node/targets/wx/development-files.js +0 -8
- package/dist/node/targets/wx/plugin.d.ts +0 -6
- package/dist/node/targets/wx/plugin.js +0 -144
- package/dist/node/targets/wx/react-refresh.d.ts +0 -4
- package/dist/node/targets/wx/react-refresh.js +0 -38
- package/dist/node/targets/wx/virtual-modules.d.ts +0 -16
- package/dist/node/targets/wx/virtual-modules.js +0 -120
- package/dist/node/utils/async.d.ts +0 -8
- package/dist/node/utils/async.js +0 -16
- package/dist/node/utils/filesystem.d.ts +0 -13
- package/dist/node/utils/filesystem.js +0 -43
- package/dist/runtime/taro/api.js +0 -11
- package/dist/runtime/taro/components.d.ts +0 -2
- package/dist/runtime/taro/components.js +0 -2
- package/dist/runtime/wx/page-update.d.ts +0 -6
- package/dist/runtime/wx/page-update.js +0 -157
- package/dist/runtime/wx/taro-runtime.d.ts +0 -5
- package/dist/runtime/wx/taro-runtime.js +0 -6
- package/dist/runtime/wx/update-client-state.d.ts +0 -68
- package/dist/runtime/wx/update-client-state.js +0 -84
- package/dist/runtime/wx/update-client.d.ts +0 -1
- package/dist/runtime/wx/update-client.js +0 -171
- package/src/node/build-context.ts +0 -62
- package/src/node/css/css-pipeline.ts +0 -171
- package/src/node/plugins/taro-runtime.ts +0 -22
- package/src/node/targets/h5/plugin.ts +0 -135
- package/src/node/targets/h5/virtual-modules.ts +0 -71
- package/src/node/targets/wx/companion-assets.ts +0 -174
- package/src/node/targets/wx/dev-server/development-session.ts +0 -271
- package/src/node/targets/wx/dev-server/full-build-scheduler.ts +0 -62
- package/src/node/targets/wx/dev-server/js-utils.ts +0 -33
- package/src/node/targets/wx/dev-server/output.ts +0 -56
- package/src/node/targets/wx/dev-server/rolldown-runtime-source.ts +0 -95
- package/src/node/targets/wx/dev-server/update-server-state.ts +0 -183
- package/src/node/targets/wx/dev-server/update-transport.ts +0 -261
- package/src/node/targets/wx/dev-server/vite-bundled-dev-adapter.ts +0 -203
- package/src/node/targets/wx/development-files.ts +0 -11
- package/src/node/targets/wx/plugin.ts +0 -164
- package/src/node/targets/wx/react-refresh.ts +0 -51
- package/src/node/targets/wx/virtual-modules.ts +0 -152
- package/src/node/utils/async.ts +0 -19
- package/src/node/utils/filesystem.ts +0 -45
- package/src/runtime/taro/api.ts +0 -13
- package/src/runtime/taro/components.ts +0 -2
- package/src/runtime/wx/page-update.ts +0 -230
- package/src/runtime/wx/react-refresh.d.ts +0 -20
- package/src/runtime/wx/taro-runtime.ts +0 -7
- package/src/runtime/wx/update-client-state.ts +0 -138
- package/src/runtime/wx/update-client.ts +0 -223
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { type PluginObject, types } from '@babel/core'
|
|
2
|
+
import { resolveChunkReference } from '../../../utils/modules.ts'
|
|
3
|
+
|
|
4
|
+
/** Wraps System.register as an inert CommonJS capsule tuple with canonical final dependency IDs. */
|
|
5
|
+
export function wrapCapsulePlugin(fileName: string): PluginObject {
|
|
6
|
+
return {
|
|
7
|
+
name: 'vpt:wrap-capsule',
|
|
8
|
+
visitor: {},
|
|
9
|
+
|
|
10
|
+
post(file) {
|
|
11
|
+
const program = file.ast.program
|
|
12
|
+
const [statement] = program.body
|
|
13
|
+
const registration = statement && types.isExpressionStatement(statement) ? statement.expression : undefined
|
|
14
|
+
|
|
15
|
+
if (
|
|
16
|
+
program.body.length !== 1 ||
|
|
17
|
+
!types.isCallExpression(registration) ||
|
|
18
|
+
!types.isMemberExpression(registration.callee) ||
|
|
19
|
+
registration.callee.computed ||
|
|
20
|
+
!types.isIdentifier(registration.callee.object, { name: 'System' }) ||
|
|
21
|
+
!types.isIdentifier(registration.callee.property, { name: 'register' })
|
|
22
|
+
) {
|
|
23
|
+
throw new Error(`Expected one anonymous System.register call in ${fileName}`)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const [dependencies, declaration] = registration.arguments
|
|
27
|
+
if (
|
|
28
|
+
registration.arguments.length !== 2 ||
|
|
29
|
+
!types.isArrayExpression(dependencies) ||
|
|
30
|
+
!types.isFunctionExpression(declaration)
|
|
31
|
+
) {
|
|
32
|
+
throw new Error(`Expected System.register(dependencies, declaration) in ${fileName}`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
dependencies.elements.forEach((dependency) => {
|
|
36
|
+
canonicalizeStaticReference(dependency, fileName)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const context = declaration.params[1]
|
|
40
|
+
if (types.isIdentifier(context)) {
|
|
41
|
+
file.path.traverse({
|
|
42
|
+
CallExpression(callPath) {
|
|
43
|
+
const callee = callPath.node.callee
|
|
44
|
+
if (
|
|
45
|
+
!types.isMemberExpression(callee) ||
|
|
46
|
+
callee.computed ||
|
|
47
|
+
!types.isIdentifier(callee.object, { name: context.name }) ||
|
|
48
|
+
!types.isIdentifier(callee.property, { name: 'import' })
|
|
49
|
+
) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const [dependency] = callPath.node.arguments
|
|
54
|
+
canonicalizeDynamicReference(dependency, fileName)
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
program.directives = []
|
|
60
|
+
program.body = [
|
|
61
|
+
types.expressionStatement(
|
|
62
|
+
types.assignmentExpression(
|
|
63
|
+
'=',
|
|
64
|
+
types.memberExpression(types.identifier('module'), types.identifier('exports')),
|
|
65
|
+
types.arrayExpression([dependencies, declaration])
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Resolves one generated static reference while the final importing chunk filename is known. */
|
|
74
|
+
function canonicalizeStaticReference(reference: types.Node | null | undefined, fileName: string): void {
|
|
75
|
+
if (!types.isStringLiteral(reference)) {
|
|
76
|
+
throw new Error(`Expected a literal System.register dependency in ${fileName}`)
|
|
77
|
+
}
|
|
78
|
+
reference.value = resolveChunkReference(fileName, reference.value)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Resolves application literals while preserving runtime-computed IDs injected by the development runtime. */
|
|
82
|
+
function canonicalizeDynamicReference(reference: types.Node | null | undefined, fileName: string): void {
|
|
83
|
+
if (types.isStringLiteral(reference) && (reference.value.startsWith('./') || reference.value.startsWith('../'))) {
|
|
84
|
+
reference.value = resolveChunkReference(fileName, reference.value)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PluginTarget } from '@babel/core'
|
|
2
|
+
import transformDynamicImport from '@babel/plugin-transform-dynamic-import'
|
|
3
|
+
import transformModulesSystemjs from '@babel/plugin-transform-modules-systemjs'
|
|
4
|
+
import type { Rolldown } from 'vite'
|
|
5
|
+
import { type AstTransformResult, transformWithBabel } from '../../../utils/transform.ts'
|
|
6
|
+
import { wrapCapsulePlugin } from './capsule-wrapper.ts'
|
|
7
|
+
|
|
8
|
+
/** Renders one ESM chunk as an inert SystemJS capsule. */
|
|
9
|
+
export function renderCapsule(code: string, chunk: Rolldown.RenderedChunk, sourcemap = true): AstTransformResult {
|
|
10
|
+
return transformWithBabel(
|
|
11
|
+
code,
|
|
12
|
+
chunk.fileName,
|
|
13
|
+
[
|
|
14
|
+
transformDynamicImport,
|
|
15
|
+
// Erase Babel's internal plugin pass type.
|
|
16
|
+
transformModulesSystemjs as PluginTarget,
|
|
17
|
+
wrapCapsulePlugin(chunk.fileName) as PluginTarget
|
|
18
|
+
],
|
|
19
|
+
sourcemap
|
|
20
|
+
)
|
|
21
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type PluginObject, type PluginTarget, types } from '@babel/core'
|
|
2
|
+
import transformModulesCommonjs from '@babel/plugin-transform-modules-commonjs'
|
|
3
|
+
import type { Rolldown } from 'vite'
|
|
4
|
+
import { resolveChunkReference } from '../../../utils/modules.ts'
|
|
5
|
+
import { type AstTransformResult, transformWithBabel } from '../../../utils/transform.ts'
|
|
6
|
+
import { getWxEntryRole } from '../module.ts'
|
|
7
|
+
|
|
8
|
+
/** Renders a native module while activating its statically imported capsules through SystemJS. */
|
|
9
|
+
export function renderNative({
|
|
10
|
+
code,
|
|
11
|
+
chunk,
|
|
12
|
+
chunks,
|
|
13
|
+
sourcemap
|
|
14
|
+
}: {
|
|
15
|
+
code: string
|
|
16
|
+
chunk: Rolldown.RenderedChunk
|
|
17
|
+
chunks: Readonly<Record<string, Rolldown.RenderedChunk>>
|
|
18
|
+
sourcemap: boolean
|
|
19
|
+
}): AstTransformResult {
|
|
20
|
+
return transformWithBabel(
|
|
21
|
+
code,
|
|
22
|
+
chunk.fileName,
|
|
23
|
+
[connectNativeCapsulesPlugin(chunk.fileName, chunks) as PluginTarget, transformModulesCommonjs as PluginTarget],
|
|
24
|
+
sourcemap
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Converts only cross-runtime static imports; ordinary native dependencies remain CommonJS imports. */
|
|
29
|
+
function connectNativeCapsulesPlugin(
|
|
30
|
+
fileName: string,
|
|
31
|
+
chunks: Readonly<Record<string, Rolldown.RenderedChunk>>
|
|
32
|
+
): PluginObject {
|
|
33
|
+
return {
|
|
34
|
+
name: 'vpt:connect-native-capsules',
|
|
35
|
+
visitor: {
|
|
36
|
+
ImportDeclaration(importPath) {
|
|
37
|
+
const reference = importPath.node.source.value
|
|
38
|
+
if (!reference.startsWith('./') && !reference.startsWith('../')) {
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const chunkId = resolveChunkReference(fileName, reference)
|
|
43
|
+
const importedChunk = chunks[chunkId]
|
|
44
|
+
if (!importedChunk || getWxEntryRole(importedChunk) !== 'capsule') {
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const [specifier] = importPath.node.specifiers
|
|
49
|
+
if (
|
|
50
|
+
importPath.node.specifiers.length !== 1 ||
|
|
51
|
+
!specifier ||
|
|
52
|
+
types.isImportNamespaceSpecifier(specifier)
|
|
53
|
+
) {
|
|
54
|
+
throw new Error(`Expected one capsule value import from ${chunkId} in ${fileName}`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const imported = types.isImportDefaultSpecifier(specifier)
|
|
58
|
+
? types.identifier('default')
|
|
59
|
+
: specifier.imported
|
|
60
|
+
const importedConfig = types.memberExpression(
|
|
61
|
+
createSyncImport(chunkId),
|
|
62
|
+
types.cloneNode(imported),
|
|
63
|
+
types.isStringLiteral(imported)
|
|
64
|
+
)
|
|
65
|
+
importPath.replaceWith(
|
|
66
|
+
types.variableDeclaration('const', [
|
|
67
|
+
types.variableDeclarator(types.cloneNode(specifier.local), importedConfig)
|
|
68
|
+
])
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Creates one synchronous lookup through the canonical output chunk ID. */
|
|
76
|
+
function createSyncImport(chunkId: string): types.CallExpression {
|
|
77
|
+
return types.callExpression(
|
|
78
|
+
types.memberExpression(
|
|
79
|
+
types.memberExpression(types.identifier('global'), types.identifier('System')),
|
|
80
|
+
types.identifier('importSync')
|
|
81
|
+
),
|
|
82
|
+
[types.stringLiteral(chunkId)]
|
|
83
|
+
)
|
|
84
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { types } from '@babel/core'
|
|
3
|
+
import type { Rolldown } from 'vite'
|
|
4
|
+
import { type AstTransformResult, replaceWithAst } from '../../../utils/transform.ts'
|
|
5
|
+
import { getWxExecutionKind } from '../module.ts'
|
|
6
|
+
|
|
7
|
+
const transportPlaceholder = '__VITE_PLUGIN_TARO_TRANSPORT__'
|
|
8
|
+
const moduleIdParameter = 'moduleId'
|
|
9
|
+
const exportBindingParameter = 'exportBinding'
|
|
10
|
+
|
|
11
|
+
type TransportedChunk = {
|
|
12
|
+
chunk: Rolldown.RenderedChunk
|
|
13
|
+
kind: 'capsule' | 'amphibious'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Materializes transport while Rolldown's preliminary hash placeholders are still active, so every injected physical
|
|
18
|
+
* reference participates in final hash calculation instead of changing code after its filename has been fixed.
|
|
19
|
+
*
|
|
20
|
+
* This intentionally creates broad hash invalidation: changing one capsule can rename transport, then bootstrap, then
|
|
21
|
+
* chunks that import bootstrap. A Mini Program ships one application package rather than independently cached HTTP
|
|
22
|
+
* chunks, so honest content hashes and automatic graph linking are more valuable than minimizing that hash fan-out.
|
|
23
|
+
*/
|
|
24
|
+
export async function materializeTransport({
|
|
25
|
+
code,
|
|
26
|
+
transportChunk,
|
|
27
|
+
chunks,
|
|
28
|
+
getLoadMode,
|
|
29
|
+
sourcemap = true
|
|
30
|
+
}: {
|
|
31
|
+
code: string
|
|
32
|
+
transportChunk: Rolldown.RenderedChunk
|
|
33
|
+
chunks: Readonly<Record<string, Rolldown.RenderedChunk>>
|
|
34
|
+
getLoadMode(chunk: Rolldown.RenderedChunk): 'sync' | 'async'
|
|
35
|
+
sourcemap?: boolean
|
|
36
|
+
}): Promise<AstTransformResult> {
|
|
37
|
+
// Babel constructs and safely serializes an expression shaped like:
|
|
38
|
+
// (moduleId) => {
|
|
39
|
+
// switch (moduleId) {
|
|
40
|
+
// case 'assets/app.js': return require('./app.js')
|
|
41
|
+
// case 'sub/p_account/page.js': return require.async('../sub/p_account/page.js')
|
|
42
|
+
// case 'assets/bootstrap.js':
|
|
43
|
+
// return [[], (exportBinding) => ({ execute() { exportBinding(require('./bootstrap.js')) } })]
|
|
44
|
+
// default: throw new Error(`Unknown System module: ${moduleId}`)
|
|
45
|
+
// }
|
|
46
|
+
// }
|
|
47
|
+
const cases = getTransportedChunks(chunks)
|
|
48
|
+
.sort((left, right) => left.chunk.fileName.localeCompare(right.chunk.fileName))
|
|
49
|
+
.map(({ chunk, kind }) => {
|
|
50
|
+
const loadMode = getLoadMode(chunk)
|
|
51
|
+
if (kind === 'amphibious' && loadMode !== 'sync') {
|
|
52
|
+
throw new Error(`Amphibious wx module must be in the main package: ${chunk.fileName}`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return createTransportCase({
|
|
56
|
+
chunkId: chunk.fileName,
|
|
57
|
+
transportFileName: transportChunk.fileName,
|
|
58
|
+
loadMode,
|
|
59
|
+
kind
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
return await replaceWithAst(
|
|
64
|
+
code,
|
|
65
|
+
transportChunk.fileName,
|
|
66
|
+
{
|
|
67
|
+
[transportPlaceholder]: types.arrowFunctionExpression(
|
|
68
|
+
[types.identifier(moduleIdParameter)],
|
|
69
|
+
types.blockStatement([
|
|
70
|
+
types.switchStatement(types.identifier(moduleIdParameter), [
|
|
71
|
+
...cases,
|
|
72
|
+
createUnknownModuleCase(moduleIdParameter)
|
|
73
|
+
])
|
|
74
|
+
])
|
|
75
|
+
)
|
|
76
|
+
},
|
|
77
|
+
sourcemap
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Keeps only capsule and amphibious chunks and carries their narrowed kind into source generation. */
|
|
82
|
+
function getTransportedChunks(chunks: Readonly<Record<string, Rolldown.RenderedChunk>>): TransportedChunk[] {
|
|
83
|
+
const transportedChunks: TransportedChunk[] = []
|
|
84
|
+
|
|
85
|
+
for (const chunk of Object.values(chunks)) {
|
|
86
|
+
const kind = getWxExecutionKind(chunk)
|
|
87
|
+
if (kind !== 'native') {
|
|
88
|
+
transportedChunks.push({ chunk, kind })
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return transportedChunks
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Creates one canonical-ID switch case while keeping its native require argument literal. */
|
|
96
|
+
function createTransportCase({
|
|
97
|
+
chunkId,
|
|
98
|
+
transportFileName,
|
|
99
|
+
loadMode,
|
|
100
|
+
kind
|
|
101
|
+
}: {
|
|
102
|
+
chunkId: string
|
|
103
|
+
transportFileName: string
|
|
104
|
+
loadMode: 'sync' | 'async'
|
|
105
|
+
kind: 'capsule' | 'amphibious'
|
|
106
|
+
}): ReturnType<typeof types.switchCase> {
|
|
107
|
+
const requirePath = toNativeRequirePath(transportFileName, chunkId)
|
|
108
|
+
|
|
109
|
+
const requireCallee =
|
|
110
|
+
loadMode === 'sync'
|
|
111
|
+
? types.identifier('require')
|
|
112
|
+
: types.memberExpression(types.identifier('require'), types.identifier('async'))
|
|
113
|
+
|
|
114
|
+
const loadedModule = types.callExpression(requireCallee, [types.stringLiteral(requirePath)])
|
|
115
|
+
const registration = kind === 'capsule' ? loadedModule : createAmphibiousRegistrationExpression(loadedModule)
|
|
116
|
+
|
|
117
|
+
return types.switchCase(types.stringLiteral(chunkId), [types.returnStatement(registration)])
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Creates a registration that loads and publishes an amphibious CommonJS namespace only when executed. */
|
|
121
|
+
function createAmphibiousRegistrationExpression(namespace: types.Expression): ReturnType<typeof types.arrayExpression> {
|
|
122
|
+
return types.arrayExpression([
|
|
123
|
+
types.arrayExpression([]),
|
|
124
|
+
types.arrowFunctionExpression(
|
|
125
|
+
[types.identifier(exportBindingParameter)],
|
|
126
|
+
types.objectExpression([
|
|
127
|
+
types.objectMethod(
|
|
128
|
+
'method',
|
|
129
|
+
types.identifier('execute'),
|
|
130
|
+
[],
|
|
131
|
+
types.blockStatement([
|
|
132
|
+
types.expressionStatement(
|
|
133
|
+
types.callExpression(types.identifier(exportBindingParameter), [namespace])
|
|
134
|
+
)
|
|
135
|
+
])
|
|
136
|
+
)
|
|
137
|
+
])
|
|
138
|
+
)
|
|
139
|
+
])
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Rejects module IDs absent from the closed output graph. */
|
|
143
|
+
function createUnknownModuleCase(moduleId: string): ReturnType<typeof types.switchCase> {
|
|
144
|
+
return types.switchCase(null, [
|
|
145
|
+
types.throwStatement(
|
|
146
|
+
types.newExpression(types.identifier('Error'), [
|
|
147
|
+
types.binaryExpression('+', types.stringLiteral('Unknown System module: '), types.identifier(moduleId))
|
|
148
|
+
])
|
|
149
|
+
)
|
|
150
|
+
])
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Converts one preliminary output path to a literal require path relative to transport. */
|
|
154
|
+
function toNativeRequirePath(fromFileName: string, toFileName: string): string {
|
|
155
|
+
const relativePath = path.posix.relative(path.posix.dirname(fromFileName), toFileName)
|
|
156
|
+
return relativePath.startsWith('.') ? relativePath : `./${relativePath}`
|
|
157
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { VitePluginTaroOptions, VitePluginTaroPageOption } from '../../../../options.ts'
|
|
2
|
+
import { normalizeModuleId, resolveAppComponentPath, resolvePageComponentPath } from '../../../utils/modules.ts'
|
|
3
|
+
import { createAppConfig } from '../../../utils/project-config.ts'
|
|
4
|
+
import { appComponentId } from '../../client/constant.ts'
|
|
5
|
+
import {
|
|
6
|
+
appCapsulePath,
|
|
7
|
+
appShellFileName,
|
|
8
|
+
appShellPath,
|
|
9
|
+
bootstrapPath,
|
|
10
|
+
componentCapsulePath,
|
|
11
|
+
componentShellFileName,
|
|
12
|
+
componentShellPath,
|
|
13
|
+
pageCapsuleId,
|
|
14
|
+
pageCapsulePath,
|
|
15
|
+
pageComponentId,
|
|
16
|
+
pageShellPath,
|
|
17
|
+
transportPath,
|
|
18
|
+
vitePreloadId
|
|
19
|
+
} from '../module.ts'
|
|
20
|
+
import { specializeBootstrap } from './specialize-bootstrap.ts'
|
|
21
|
+
import { specializePageCapsule } from './specialize-page-capsule.ts'
|
|
22
|
+
|
|
23
|
+
/** Resolves one exact plugin-private ID using its importer and configured project root. */
|
|
24
|
+
type PrivateIdResolver = (importer: string | undefined, projectRoot: string) => string
|
|
25
|
+
|
|
26
|
+
/** Creates the resolver and source specializer for the wx module graph. */
|
|
27
|
+
export function createResolver(options: VitePluginTaroOptions) {
|
|
28
|
+
const normalizedBootstrapPath = normalizeModuleId(bootstrapPath)
|
|
29
|
+
const normalizedPageCapsulePath = normalizeModuleId(pageCapsulePath)
|
|
30
|
+
|
|
31
|
+
// Provide constant-time route validation and access to each configured Page JSON object.
|
|
32
|
+
const pageByPath = new Map(options.pages.map((page) => [page.path, page]))
|
|
33
|
+
|
|
34
|
+
const privateIdResolvers = new Map<string, PrivateIdResolver>([
|
|
35
|
+
// Share bootstrap's preload identity through native require and its amphibious SystemJS registration.
|
|
36
|
+
[vitePreloadId, () => bootstrapPath],
|
|
37
|
+
// Keep the configured App component behind one stable private import in the App capsule.
|
|
38
|
+
[appComponentId, (_importer, projectRoot) => resolveAppComponentPath({ appPath: options.app, projectRoot })],
|
|
39
|
+
[
|
|
40
|
+
pageComponentId,
|
|
41
|
+
(importer, projectRoot) => {
|
|
42
|
+
const page = requireConfiguredPage({ moduleId: importer, pageByPath })
|
|
43
|
+
|
|
44
|
+
return resolvePageComponentPath({ pagePath: page.path, projectRoot })
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
pageCapsuleId,
|
|
49
|
+
(importer) => {
|
|
50
|
+
// Query-qualify the capsule source so every Page retains a distinct graph identity.
|
|
51
|
+
const page = requireConfiguredPage({ moduleId: importer, pageByPath })
|
|
52
|
+
|
|
53
|
+
return createRouteModuleId({ moduleId: pageCapsulePath, pagePath: page.path })
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
])
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
input: createInput(options.pages),
|
|
60
|
+
|
|
61
|
+
resolveId(id: string, importer: string | undefined, projectRoot: string): string | undefined {
|
|
62
|
+
// Unknown IDs fall through so Vite and other plugins retain normal resolution.
|
|
63
|
+
return privateIdResolvers.get(id)?.(importer, projectRoot)
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
specialize(code: string, id: string, sourcemap = true) {
|
|
67
|
+
const normalizedId = normalizeModuleId(id)
|
|
68
|
+
|
|
69
|
+
if (normalizedId === normalizedBootstrapPath) {
|
|
70
|
+
return specializeBootstrap({ code, id, appConfig: createAppConfig(options), sourcemap })
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (normalizedId === normalizedPageCapsulePath) {
|
|
74
|
+
return specializePageCapsule({
|
|
75
|
+
code,
|
|
76
|
+
id,
|
|
77
|
+
page: requireConfiguredPage({ moduleId: id, pageByPath }),
|
|
78
|
+
sourcemap
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Declares native shells, infrastructure, and capsules as independent output entries. */
|
|
86
|
+
function createInput(pages: readonly VitePluginTaroPageOption[]): Record<string, string> {
|
|
87
|
+
return Object.fromEntries([
|
|
88
|
+
['bootstrap', bootstrapPath],
|
|
89
|
+
['transport', transportPath],
|
|
90
|
+
[appShellFileName, appShellPath],
|
|
91
|
+
['app-capsule', appCapsulePath],
|
|
92
|
+
[componentShellFileName, componentShellPath],
|
|
93
|
+
['component-capsule', componentCapsulePath],
|
|
94
|
+
...pages.flatMap((page) => {
|
|
95
|
+
return [
|
|
96
|
+
[`${page.path}.js`, createRouteModuleId({ moduleId: pageShellPath, pagePath: page.path })],
|
|
97
|
+
[`${page.path}-capsule`, createRouteModuleId({ moduleId: pageCapsulePath, pagePath: page.path })]
|
|
98
|
+
]
|
|
99
|
+
})
|
|
100
|
+
])
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Creates one route-qualified module ID. */
|
|
104
|
+
function createRouteModuleId({ moduleId, pagePath }: { moduleId: string; pagePath: string }): string {
|
|
105
|
+
return `${moduleId}?route=${encodeURIComponent(pagePath)}`
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Returns the configured Page identified by a route-qualified capsule ID. */
|
|
109
|
+
function requireConfiguredPage({
|
|
110
|
+
moduleId,
|
|
111
|
+
pageByPath
|
|
112
|
+
}: {
|
|
113
|
+
moduleId: string | undefined
|
|
114
|
+
pageByPath: ReadonlyMap<string, VitePluginTaroPageOption>
|
|
115
|
+
}): VitePluginTaroPageOption {
|
|
116
|
+
const queryIndex = moduleId?.indexOf('?') ?? -1
|
|
117
|
+
const pagePath = queryIndex === -1 ? undefined : new URLSearchParams(moduleId?.slice(queryIndex + 1)).get('route')
|
|
118
|
+
const page = pagePath ? pageByPath.get(pagePath) : undefined
|
|
119
|
+
if (!page) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
pagePath ? `Unknown Page capsule: ${pagePath}` : 'Page capsule import must originate from a route module'
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
return page
|
|
125
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { types } from '@babel/core'
|
|
2
|
+
import type { JsonObject } from '../../../../options.ts'
|
|
3
|
+
import { type AstTransformResult, replaceWithAst } from '../../../utils/transform.ts'
|
|
4
|
+
|
|
5
|
+
const appConfigPlaceholder = '__VITE_PLUGIN_TARO_APP_CONFIG__'
|
|
6
|
+
|
|
7
|
+
/** Specializes the amphibious bootstrap with the shared App configuration. */
|
|
8
|
+
export function specializeBootstrap({
|
|
9
|
+
code,
|
|
10
|
+
id,
|
|
11
|
+
appConfig,
|
|
12
|
+
sourcemap = true
|
|
13
|
+
}: {
|
|
14
|
+
code: string
|
|
15
|
+
id: string
|
|
16
|
+
appConfig: JsonObject
|
|
17
|
+
sourcemap?: boolean
|
|
18
|
+
}): Promise<AstTransformResult> {
|
|
19
|
+
return replaceWithAst(
|
|
20
|
+
code,
|
|
21
|
+
id,
|
|
22
|
+
{
|
|
23
|
+
[appConfigPlaceholder]: types.valueToNode(appConfig)
|
|
24
|
+
},
|
|
25
|
+
sourcemap
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { types } from '@babel/core'
|
|
2
|
+
import type { VitePluginTaroPageOption } from '../../../../options.ts'
|
|
3
|
+
import { type AstTransformResult, replaceWithAst } from '../../../utils/transform.ts'
|
|
4
|
+
|
|
5
|
+
const pagePathPlaceholder = '__VITE_PLUGIN_TARO_PAGE_PATH__'
|
|
6
|
+
const pageConfigPlaceholder = '__VITE_PLUGIN_TARO_PAGE_CONFIG__'
|
|
7
|
+
|
|
8
|
+
/** Specializes the Page capsule for one configured route. */
|
|
9
|
+
export function specializePageCapsule({
|
|
10
|
+
code,
|
|
11
|
+
id,
|
|
12
|
+
page,
|
|
13
|
+
sourcemap = true
|
|
14
|
+
}: {
|
|
15
|
+
code: string
|
|
16
|
+
id: string
|
|
17
|
+
page: VitePluginTaroPageOption
|
|
18
|
+
sourcemap?: boolean
|
|
19
|
+
}): Promise<AstTransformResult> {
|
|
20
|
+
return replaceWithAst(
|
|
21
|
+
code,
|
|
22
|
+
id,
|
|
23
|
+
{
|
|
24
|
+
[pagePathPlaceholder]: types.stringLiteral(page.path),
|
|
25
|
+
[pageConfigPlaceholder]: types.valueToNode(page.config)
|
|
26
|
+
},
|
|
27
|
+
sourcemap
|
|
28
|
+
)
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const esTarget = 'es2018'
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a memoized version of the provided function. The memoized function caches
|
|
3
|
+
* results based on the argument it receives, so if the same argument is passed again,
|
|
4
|
+
* it returns the cached result instead of recomputing it.
|
|
5
|
+
*
|
|
6
|
+
* This function works with functions that take zero or just one argument. If your function
|
|
7
|
+
* originally takes multiple arguments, you should refactor it to take a single object or array
|
|
8
|
+
* that combines those arguments.
|
|
9
|
+
*
|
|
10
|
+
* If the argument is not primitive (e.g., arrays or objects), provide a
|
|
11
|
+
* `getCacheKey` function to generate a unique cache key for proper caching.
|
|
12
|
+
*
|
|
13
|
+
* @template F - The type of the function to be memoized.
|
|
14
|
+
* @param fn - The function to be memoized. It should accept a single argument and return a value.
|
|
15
|
+
* @param [options={}] - Optional configuration for the memoization.
|
|
16
|
+
* @param [options.cache] - The cache object used to store results. Defaults to a new `Map`.
|
|
17
|
+
* @param [options.getCacheKey] - An optional function to generate a unique cache key for each argument.
|
|
18
|
+
*
|
|
19
|
+
* @returns The memoized function with an additional `cache` property that exposes the internal cache.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // Example using the default cache
|
|
23
|
+
* const add = (x: number) => x + 10;
|
|
24
|
+
* const memoizedAdd = memoize(add);
|
|
25
|
+
*
|
|
26
|
+
* console.log(memoizedAdd(5)); // 15
|
|
27
|
+
* console.log(memoizedAdd(5)); // 15 (cached result)
|
|
28
|
+
* console.log(memoizedAdd.cache.size); // 1
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* // Example using a custom resolver
|
|
32
|
+
* const sum = (arr: number[]) => arr.reduce((x, y) => x + y, 0);
|
|
33
|
+
* const memoizedSum = memoize(sum, { getCacheKey: (arr: number[]) => arr.join(',') });
|
|
34
|
+
* console.log(memoizedSum([1, 2])); // 3
|
|
35
|
+
* console.log(memoizedSum([1, 2])); // 3 (cached result)
|
|
36
|
+
* console.log(memoizedSum.cache.size); // 1
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* // Example using a custom cache implementation
|
|
40
|
+
* class CustomCache<K, T> implements MemoizeCache<K, T> {
|
|
41
|
+
* private cache = new Map<K, T>();
|
|
42
|
+
*
|
|
43
|
+
* set(key: K, value: T): void {
|
|
44
|
+
* this.cache.set(key, value);
|
|
45
|
+
* }
|
|
46
|
+
*
|
|
47
|
+
* get(key: K): T | undefined {
|
|
48
|
+
* return this.cache.get(key);
|
|
49
|
+
* }
|
|
50
|
+
*
|
|
51
|
+
* has(key: K): boolean {
|
|
52
|
+
* return this.cache.has(key);
|
|
53
|
+
* }
|
|
54
|
+
*
|
|
55
|
+
* delete(key: K): boolean {
|
|
56
|
+
* return this.cache.delete(key);
|
|
57
|
+
* }
|
|
58
|
+
*
|
|
59
|
+
* clear(): void {
|
|
60
|
+
* this.cache.clear();
|
|
61
|
+
* }
|
|
62
|
+
*
|
|
63
|
+
* get size(): number {
|
|
64
|
+
* return this.cache.size;
|
|
65
|
+
* }
|
|
66
|
+
* }
|
|
67
|
+
* const customCache = new CustomCache<string, number>();
|
|
68
|
+
* const memoizedSumWithCustomCache = memoize(sum, { cache: customCache });
|
|
69
|
+
* console.log(memoizedSumWithCustomCache([1, 2])); // 3
|
|
70
|
+
* console.log(memoizedSumWithCustomCache([1, 2])); // 3 (cached result)
|
|
71
|
+
* console.log(memoizedSumWithCustomCache.cache.size); // 1
|
|
72
|
+
*/
|
|
73
|
+
export function memoize<F extends (arg: never) => unknown>(
|
|
74
|
+
fn: F,
|
|
75
|
+
options: {
|
|
76
|
+
cache?: MemoizeCache<unknown, ReturnType<F>>
|
|
77
|
+
getCacheKey?: (arg: Parameters<F>[0]) => unknown
|
|
78
|
+
} = {}
|
|
79
|
+
): F & { cache: MemoizeCache<unknown, ReturnType<F>> } {
|
|
80
|
+
const { cache = new Map<unknown, ReturnType<F>>(), getCacheKey } = options
|
|
81
|
+
|
|
82
|
+
const memoizedFn = function (this: unknown, arg: Parameters<F>[0]): ReturnType<F> {
|
|
83
|
+
const key = getCacheKey ? getCacheKey(arg) : arg
|
|
84
|
+
|
|
85
|
+
if (cache.has(key)) {
|
|
86
|
+
return cache.get(key)!
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const result = fn.call(this, arg) as ReturnType<F>
|
|
90
|
+
|
|
91
|
+
cache.set(key, result)
|
|
92
|
+
|
|
93
|
+
return result
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
memoizedFn.cache = cache
|
|
97
|
+
|
|
98
|
+
return memoizedFn as F & { cache: MemoizeCache<unknown, ReturnType<F>> }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Represents a cache for memoization, allowing storage and retrieval of computed values.
|
|
103
|
+
*
|
|
104
|
+
* @template K - The type of keys used to store values in the cache.
|
|
105
|
+
* @template V - The type of values stored in the cache.
|
|
106
|
+
*/
|
|
107
|
+
export interface MemoizeCache<K, V> {
|
|
108
|
+
/**
|
|
109
|
+
* Stores a value in the cache with the specified key.
|
|
110
|
+
*
|
|
111
|
+
* @param key - The key to associate with the value.
|
|
112
|
+
* @param value - The value to store in the cache.
|
|
113
|
+
*/
|
|
114
|
+
set(key: K, value: V): void
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Retrieves a value from the cache by its key.
|
|
118
|
+
*
|
|
119
|
+
* @param key - The key of the value to retrieve.
|
|
120
|
+
* @returns The value associated with the key, or undefined if the key does not exist.
|
|
121
|
+
*/
|
|
122
|
+
get(key: K): V | undefined
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Checks if a value exists in the cache for the specified key.
|
|
126
|
+
*
|
|
127
|
+
* @param key - The key to check for existence in the cache.
|
|
128
|
+
* @returns True if the cache contains the key, false otherwise.
|
|
129
|
+
*/
|
|
130
|
+
has(key: K): boolean
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Deletes a value from the cache by its key.
|
|
134
|
+
*
|
|
135
|
+
* @param key - The key of the value to delete.
|
|
136
|
+
* @returns True if the value was successfully deleted, false otherwise.
|
|
137
|
+
*/
|
|
138
|
+
delete(key: K): boolean
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Clears all values from the cache.
|
|
142
|
+
*/
|
|
143
|
+
clear(): void
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The number of entries in the cache.
|
|
147
|
+
*/
|
|
148
|
+
size: number
|
|
149
|
+
}
|