vite-plugin-taro 0.5.13 → 0.5.14
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/node/plugins/client/client-taro.d.ts +1 -1
- package/dist/node/plugins/client/client-taro.js +2 -1
- package/dist/node/plugins/h5/create-stencil-client-adapter.d.ts +1 -1
- package/dist/node/plugins/h5/create-stencil-client-adapter.js +2 -1
- package/dist/node/plugins/h5/resolver/module-resolver.js +2 -1
- package/dist/node/plugins/wx/dev/create-hmr-results-stream.d.ts +23 -0
- package/dist/node/plugins/wx/dev/create-hmr-results-stream.js +57 -0
- package/dist/node/plugins/wx/dev/create-style-capture.d.ts +42 -0
- package/dist/node/plugins/wx/dev/create-style-capture.js +125 -0
- package/dist/node/plugins/wx/dev/dev-host.d.ts +1 -1
- package/dist/node/plugins/wx/dev/dev-host.js +151 -93
- package/dist/node/plugins/wx/dev/hmr-files.d.ts +1 -0
- package/dist/node/plugins/wx/dev/hmr-files.js +1 -0
- package/dist/node/plugins/wx/dev/host-actions.d.ts +16 -0
- package/dist/node/plugins/wx/dev/host-actions.js +58 -0
- package/dist/node/plugins/wx/dev/patch-publisher.d.ts +2 -2
- package/dist/node/plugins/wx/dev/patch-publisher.js +17 -1
- package/dist/node/plugins/wx/dev/plugins.d.ts +4 -0
- package/dist/node/plugins/wx/dev/plugins.js +24 -8
- package/dist/node/plugins/wx/dev/react-refresh.js +6 -2
- package/dist/node/plugins/wx/dev/runtime-reports.d.ts +27 -0
- package/dist/node/plugins/wx/dev/runtime-reports.js +45 -0
- package/dist/node/plugins/wx/dev/wx-dev-options.d.ts +7 -3
- package/dist/node/plugins/wx/dev/wx-dev-options.js +46 -28
- package/dist/node/plugins/wx/module.d.ts +1 -1
- package/dist/node/plugins/wx/module.js +2 -1
- package/dist/node/plugins/wx/native/compile-native-component-interface.js +2 -1
- package/dist/node/plugins/wx/native/create-native-component-output.js +2 -0
- package/dist/node/plugins/wx/native/native-component-assets.d.ts +1 -1
- package/dist/node/plugins/wx/native/native-component-assets.js +3 -3
- package/dist/node/plugins/wx/native/native-component-interface.js +2 -1
- package/dist/node/plugins/wx/resolve/resolver.js +3 -2
- package/dist/node/plugins/wx/styles/plugins.js +20 -1
- package/dist/node/utils/modules.d.ts +1 -1
- package/dist/node/utils/modules.js +4 -3
- package/dist/node/utils/packages.js +2 -2
- package/package.json +4 -3
- package/src/node/plugins/client/client-taro.ts +2 -2
- package/src/node/plugins/h5/create-stencil-client-adapter.ts +2 -2
- package/src/node/plugins/h5/resolver/module-resolver.ts +2 -1
- package/src/node/plugins/wx/dev/create-hmr-results-stream.ts +71 -0
- package/src/node/plugins/wx/dev/create-style-capture.ts +179 -0
- package/src/node/plugins/wx/dev/dev-host.ts +177 -124
- package/src/node/plugins/wx/dev/hmr-files.ts +1 -0
- package/src/node/plugins/wx/dev/host-actions.ts +88 -0
- package/src/node/plugins/wx/dev/patch-publisher.ts +19 -2
- package/src/node/plugins/wx/dev/plugins.ts +25 -8
- package/src/node/plugins/wx/dev/react-refresh.ts +6 -2
- package/src/node/plugins/wx/dev/runtime-reports.ts +70 -0
- package/src/node/plugins/wx/dev/wx-dev-options.ts +50 -36
- package/src/node/plugins/wx/module.ts +2 -4
- package/src/node/plugins/wx/native/compile-native-component-interface.ts +2 -1
- package/src/node/plugins/wx/native/create-native-component-output.ts +2 -0
- package/src/node/plugins/wx/native/native-component-assets.ts +3 -4
- package/src/node/plugins/wx/native/native-component-interface.ts +2 -1
- package/src/node/plugins/wx/resolve/resolver.ts +3 -2
- package/src/node/plugins/wx/styles/plugins.ts +21 -1
- package/src/node/utils/modules.ts +4 -3
- package/src/node/utils/packages.ts +2 -2
- package/dist/node/plugins/wx/dev/create-wx-dev-mode.d.ts +0 -6
- package/dist/node/plugins/wx/dev/create-wx-dev-mode.js +0 -9
- package/dist/node/plugins/wx/dev/styles/create-style-capture-plugin.d.ts +0 -12
- package/dist/node/plugins/wx/dev/styles/create-style-capture-plugin.js +0 -21
- package/dist/node/plugins/wx/dev/styles/publish-style-hmr.d.ts +0 -27
- package/dist/node/plugins/wx/dev/styles/publish-style-hmr.js +0 -48
- package/dist/node/utils/serialized-task-queue.d.ts +0 -13
- package/dist/node/utils/serialized-task-queue.js +0 -25
- package/src/node/plugins/wx/dev/create-wx-dev-mode.ts +0 -14
- package/src/node/plugins/wx/dev/styles/create-style-capture-plugin.ts +0 -34
- package/src/node/plugins/wx/dev/styles/publish-style-hmr.ts +0 -71
- package/src/node/utils/serialized-task-queue.ts +0 -32
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { catchError, concatMap, defer, EMPTY, filter, firstValueFrom, lastValueFrom, map, Subject } from 'rxjs'
|
|
2
|
+
|
|
3
|
+
type ActionEnvelope<Action> =
|
|
4
|
+
| Readonly<{ kind: 'action'; action: Action }>
|
|
5
|
+
| Readonly<{ kind: 'barrier'; resolve: () => void }>
|
|
6
|
+
|
|
7
|
+
export type HostActions<Action> = Readonly<{
|
|
8
|
+
complete: () => Promise<void>
|
|
9
|
+
next: (action: Action) => void
|
|
10
|
+
waitForAction: <Selected extends Action>(select: (action: Action) => action is Selected) => Promise<Selected>
|
|
11
|
+
waitForIdle: () => Promise<void>
|
|
12
|
+
}>
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Executes semantic host actions one at a time while keeping source admission synchronous.
|
|
16
|
+
*
|
|
17
|
+
* `concatMap` is the sole effect serializer: each deferred reducer call starts only after the prior Promise settles. Errors are
|
|
18
|
+
* converted to empty inner streams so one failed physical transaction is reported without terminating later host actions.
|
|
19
|
+
* Barrier envelopes participate in the same ordering but run no effect; resolving one proves every action admitted before it
|
|
20
|
+
* has settled. `waitForAction` observes this same admission edge for lifecycle gates without introducing a forwarding Subject.
|
|
21
|
+
* Completing the source drains already-admitted effects and gives shutdown one Promise for final quiescence.
|
|
22
|
+
*/
|
|
23
|
+
export function createHostActions<Action>(
|
|
24
|
+
apply: (action: Action) => void | Promise<void>,
|
|
25
|
+
reportError: (action: Action, error: unknown) => void
|
|
26
|
+
): HostActions<Action> {
|
|
27
|
+
/*
|
|
28
|
+
* This Subject is the host's only mutable admission edge. Independent callback stacks synchronously append semantic actions
|
|
29
|
+
* or barriers; concatMap owns the queued ordering and admits exactly one asynchronous effect at a time. A Promise chain would
|
|
30
|
+
* require mutation at every producer and is permanently rejected after one failure, while mergeMap could overlap physical
|
|
31
|
+
* writes and build rotation. The Subject retains no business frontier itself and is completed only after all external sources
|
|
32
|
+
* are quiescent, allowing lastValueFrom to prove the final queued effect settled.
|
|
33
|
+
*/
|
|
34
|
+
const envelopes = new Subject<ActionEnvelope<Action>>()
|
|
35
|
+
|
|
36
|
+
const completion = lastValueFrom(
|
|
37
|
+
envelopes.pipe(
|
|
38
|
+
concatMap((envelope) => {
|
|
39
|
+
if (envelope.kind === 'barrier') {
|
|
40
|
+
return defer(() => {
|
|
41
|
+
envelope.resolve()
|
|
42
|
+
return EMPTY
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
return defer(() => Promise.resolve(apply(envelope.action))).pipe(
|
|
46
|
+
catchError((error: unknown) => {
|
|
47
|
+
reportError(envelope.action, error)
|
|
48
|
+
return EMPTY
|
|
49
|
+
})
|
|
50
|
+
)
|
|
51
|
+
})
|
|
52
|
+
),
|
|
53
|
+
{ defaultValue: undefined }
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
complete: async () => {
|
|
58
|
+
envelopes.complete()
|
|
59
|
+
await completion
|
|
60
|
+
},
|
|
61
|
+
next(action) {
|
|
62
|
+
envelopes.next({ kind: 'action', action: action })
|
|
63
|
+
},
|
|
64
|
+
waitForAction(select) {
|
|
65
|
+
/*
|
|
66
|
+
* Subscribe to the multicast admission Subject rather than creating another lifecycle source. Barrier envelopes are
|
|
67
|
+
* internal ordering controls, so expose only semantic actions and let the caller's type guard select its milestone.
|
|
68
|
+
* firstValueFrom unsubscribes after the match; concatMap's independent subscription still applies the same action.
|
|
69
|
+
*/
|
|
70
|
+
return firstValueFrom(
|
|
71
|
+
envelopes.pipe(
|
|
72
|
+
filter((envelope) => envelope.kind === 'action'),
|
|
73
|
+
map((envelope) => envelope.action),
|
|
74
|
+
filter(select)
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
},
|
|
78
|
+
waitForIdle() {
|
|
79
|
+
/*
|
|
80
|
+
* The resolver is one transaction-local mutable latch. Enqueuing it behind ordinary actions makes resolution mean
|
|
81
|
+
* every earlier effect settled, without completing the shared source needed by final DevEngine output callbacks.
|
|
82
|
+
*/
|
|
83
|
+
const barrier = Promise.withResolvers<void>()
|
|
84
|
+
envelopes.next({ kind: 'barrier', resolve: barrier.resolve })
|
|
85
|
+
return barrier.promise
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -2,7 +2,7 @@ import { randomUUID } from 'node:crypto'
|
|
|
2
2
|
import { type PatchUpdate, renderHmrPatches } from './hmr-files.ts'
|
|
3
3
|
|
|
4
4
|
/** Abstracts the physical patches write; the engine owns the file destination. */
|
|
5
|
-
|
|
5
|
+
type WritePatches = (content: string) => Promise<void>
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Owns the cumulative sequence range between the host's published frontier and the runtime's applied frontier.
|
|
@@ -19,9 +19,22 @@ export type WritePatches = (content: string) => Promise<void>
|
|
|
19
19
|
*/
|
|
20
20
|
export class PatchPublisher {
|
|
21
21
|
private readonly writePatches: WritePatches
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* This mutable identity is the sole session authority for runtime reports and per-client Rolldown updates. startBuild rotates
|
|
25
|
+
* it only after a successful complete output reaches the serialized host reducer. Delayed reports retain their original ID
|
|
26
|
+
* and therefore fail isCurrentBuild instead of pruning or rebuilding the new session. An immutable constructor value cannot
|
|
27
|
+
* represent full-build rotation, while deriving identity from patches would leave empty builds without a client ID.
|
|
28
|
+
*/
|
|
22
29
|
private buildId: string | undefined
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
/*
|
|
32
|
+
* This mutable ordered suffix bridges two independent frontiers: Rolldown payloads become published immediately after the
|
|
33
|
+
* cumulative file is durable, but entries remain here until the runtime acknowledges applying their sequence. Appending is
|
|
34
|
+
* lossless because later patches are deltas; prefix removal is the only valid mutation because acknowledgements are monotonic
|
|
35
|
+
* frontiers. A Set, latest-only value, or immutable reconstruction would either lose duplicate filenames/order or copy the
|
|
36
|
+
* complete unapplied history for every burst.
|
|
37
|
+
*/
|
|
25
38
|
private readonly pendingPatches: PatchUpdate[] = []
|
|
26
39
|
|
|
27
40
|
// Explicit field assignment: node --test strips types and does not support parameter properties.
|
|
@@ -64,6 +77,10 @@ export class PatchPublisher {
|
|
|
64
77
|
* retaining the gap between published and applied frontiers is what lets DevTools safely miss intermediate file events.
|
|
65
78
|
*/
|
|
66
79
|
acknowledge(seq: number): void {
|
|
80
|
+
/*
|
|
81
|
+
* This local counter scans the monotonic prefix without allocating or invoking a callback for each patch. It exists only
|
|
82
|
+
* long enough to supply splice's prefix length; pendingPatches remains the sole durable frontier and is mutated once.
|
|
83
|
+
*/
|
|
67
84
|
let appliedCount = 0
|
|
68
85
|
|
|
69
86
|
while (appliedCount < this.pendingPatches.length && this.pendingPatches[appliedCount].seq <= seq) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type PluginOption, transformWithOxc } from 'vite'
|
|
1
|
+
import { normalizePath, type PluginOption, transformWithOxc } from 'vite'
|
|
2
2
|
import type { VitePluginTaroOptions } from '../../../../options.ts'
|
|
3
3
|
import { esTarget } from '../../../utils/constant.ts'
|
|
4
4
|
import { memoize } from '../../../utils/memoize.ts'
|
|
@@ -10,6 +10,11 @@ import { createWxReactRefreshTransforms } from './react-refresh.ts'
|
|
|
10
10
|
|
|
11
11
|
const taroRuntimeId = '@tarojs/runtime'
|
|
12
12
|
|
|
13
|
+
/** Selects the sole Vite environment that owns the physical Mini Program development project. */
|
|
14
|
+
export function isWxClientEnvironment(environment: Readonly<{ name: string }>): boolean {
|
|
15
|
+
return environment.name === 'client'
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
/**
|
|
14
19
|
* Adds the serve-only bundled-development plugin set for the wx target: the dev adapter,
|
|
15
20
|
* Page HMR activation, and React Refresh adaptation transforms.
|
|
@@ -21,17 +26,26 @@ export function createWxDevelopmentPlugin(
|
|
|
21
26
|
options: VitePluginTaroOptions,
|
|
22
27
|
applicationEntryIds: readonly string[]
|
|
23
28
|
): PluginOption[] {
|
|
24
|
-
|
|
29
|
+
/*
|
|
30
|
+
* Vite creates this plugin descriptor before a server or DevEngine exists, then invokes configureServer and closeBundle on
|
|
31
|
+
* different lifecycle stacks. This mutable handle transfers the one client-owned host between those hooks: configureServer
|
|
32
|
+
* assigns it after asynchronous construction, and closeBundle reads it to drain the same engine. Environment scoping ensures
|
|
33
|
+
* only the client hook pair participates. Capturing a construction Promise would start too early, while recreating the host
|
|
34
|
+
* in closeBundle would lose every live action, patch, style, and client frontier owned by the running instance.
|
|
35
|
+
*/
|
|
25
36
|
let host: WxDevHost | null = null
|
|
26
37
|
// Portable hook filters stay broad; these exact identities exclude similarly named user modules.
|
|
27
|
-
const normalizedAppCapsulePath =
|
|
28
|
-
const normalizedPageCapsulePath =
|
|
29
|
-
const normalizedTaroRuntimePath =
|
|
38
|
+
const normalizedAppCapsulePath = normalizePath(appCapsulePath)
|
|
39
|
+
const normalizedPageCapsulePath = normalizePath(pageCapsulePath)
|
|
40
|
+
const normalizedTaroRuntimePath = normalizePath(taroRuntimePath)
|
|
30
41
|
|
|
31
42
|
return [
|
|
32
43
|
{
|
|
33
44
|
name: 'vpt:wx-dev',
|
|
34
45
|
apply: 'serve',
|
|
46
|
+
// The physical WX DevEngine is a client build. Native environment scoping gives its shared host exactly one
|
|
47
|
+
// generate/close lifecycle instead of admitting hooks from Vite's unrelated SSR environment.
|
|
48
|
+
applyToEnvironment: isWxClientEnvironment,
|
|
35
49
|
|
|
36
50
|
config() {
|
|
37
51
|
return {
|
|
@@ -205,9 +219,12 @@ function getPageRoute(id: string): string {
|
|
|
205
219
|
return route
|
|
206
220
|
}
|
|
207
221
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
222
|
+
/*
|
|
223
|
+
* memoize owns a mutable one-entry-by-input cache. The assembled runtime source is byte-identical across complete generations
|
|
224
|
+
* because both the Rolldown base and injected implementation are immutable for the server lifetime. Reusing its lowered result
|
|
225
|
+
* avoids repeating the only large Oxc transform on every rebuild; keying by source still invalidates correctly if a future Vite
|
|
226
|
+
* generation changes the runtime, unlike a module-global cached string detached from its actual input.
|
|
227
|
+
*/
|
|
211
228
|
const fixRolldownRuntime = memoize((code: string) => {
|
|
212
229
|
return transformWithOxc(code, rolldownRuntimeId, {
|
|
213
230
|
lang: 'js',
|
|
@@ -214,7 +214,11 @@ export function removeRefreshPreambleGuard({ code, id }: { code: string; id: str
|
|
|
214
214
|
})
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
|
|
218
|
-
|
|
217
|
+
/*
|
|
218
|
+
* Each memoized transform owns a mutable cache keyed only by source bytes. The matching runtime/react-family modules are
|
|
219
|
+
* immutable between complete generations, so reparsing them would repeat O(source bytes) Oxc work without observing new state.
|
|
220
|
+
* Source keys still create a fresh result after a dependency upgrade or real module edit, unlike a once-only cache keyed by ID;
|
|
221
|
+
* keeping separate caches prevents identical text in different transform domains from sharing the wrong adaptation.
|
|
222
|
+
*/
|
|
219
223
|
const fixRefreshRuntime = memoize(transformRefreshRuntime, { getCacheKey: ({ code }) => code })
|
|
220
224
|
const fixReactDevtoolsHook = memoize(transformReactDevtoolsHook, { getCacheKey: ({ code }) => code })
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { buffer, debounceTime, filter, type SchedulerLike, Subject } from 'rxjs'
|
|
2
|
+
|
|
3
|
+
type AppliedReport = Readonly<{
|
|
4
|
+
kind: 'applied'
|
|
5
|
+
buildId: string
|
|
6
|
+
seq: number
|
|
7
|
+
}>
|
|
8
|
+
|
|
9
|
+
/** The runtime hit an unrecoverable state and needs a complete build. */
|
|
10
|
+
type RebuildReport = Readonly<{
|
|
11
|
+
kind: 'rebuild'
|
|
12
|
+
buildId: string
|
|
13
|
+
reason: string
|
|
14
|
+
}>
|
|
15
|
+
|
|
16
|
+
export type RuntimeReport = AppliedReport | RebuildReport
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Conflates runtime receipts without weakening their build identity or recovery semantics.
|
|
20
|
+
*
|
|
21
|
+
* Reports are metadata frontiers rather than executable deltas: for one build, the highest applied sequence subsumes lower and
|
|
22
|
+
* duplicate acknowledgements. A rebuild does not subsume reports from a different build, because delayed Pages may still POST
|
|
23
|
+
* after session rotation, but it dominates every acknowledgement for its own build. The host validates each retained build ID
|
|
24
|
+
* against the physical publisher only when its serialized task executes, preserving rotation order.
|
|
25
|
+
*
|
|
26
|
+
* Completing the Subject flushes the active window synchronously so shutdown can include its host task before closing Rolldown.
|
|
27
|
+
* Reduction is O(reports) time and O(distinct builds) retained space per quiet window.
|
|
28
|
+
*/
|
|
29
|
+
export function createRuntimeReportsStream(
|
|
30
|
+
settleMilliseconds: number,
|
|
31
|
+
scheduler: SchedulerLike,
|
|
32
|
+
publish: (reports: readonly RuntimeReport[]) => void
|
|
33
|
+
): Subject<RuntimeReport> {
|
|
34
|
+
/*
|
|
35
|
+
* This Subject is the sole mutable HTTP admission edge. Requests append reports synchronously and finish their responses;
|
|
36
|
+
* buffer then owns the short-lived window while debounceTime supplies only its quiet-edge signal. Reports are metadata rather
|
|
37
|
+
* than executable deltas, so the window may be reduced before entering hostActions. Directly mutating PatchPublisher from the
|
|
38
|
+
* request stack would race patch writes and build rotation; retaining every raw receipt beyond the window would grow with Page
|
|
39
|
+
* count and duplicate acknowledgements. Completion flushes the final window during phased shutdown.
|
|
40
|
+
*/
|
|
41
|
+
const reports = new Subject<RuntimeReport>()
|
|
42
|
+
|
|
43
|
+
reports
|
|
44
|
+
.pipe(
|
|
45
|
+
buffer(reports.pipe(debounceTime(settleMilliseconds, scheduler))),
|
|
46
|
+
filter((window) => window.length > 0)
|
|
47
|
+
)
|
|
48
|
+
.subscribe((window) => publish(reduceRuntimeReportWindow(window)))
|
|
49
|
+
return reports
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function reduceRuntimeReportWindow(window: readonly RuntimeReport[]): readonly RuntimeReport[] {
|
|
53
|
+
/*
|
|
54
|
+
* This transaction-local mutable Map is bounded by distinct build IDs in one quiet window. Insertion order preserves causal
|
|
55
|
+
* build order, while replacing a value conflates only that build's monotonic acknowledgement frontier; rebuild becomes its
|
|
56
|
+
* terminal value. A single global latest report would let delayed Pages erase another build, and retaining the raw window
|
|
57
|
+
* would make downstream work proportional to duplicate report volume. The Map is discarded immediately after reduction.
|
|
58
|
+
*/
|
|
59
|
+
const reportsByBuild = new Map<string, RuntimeReport>()
|
|
60
|
+
for (const report of window) {
|
|
61
|
+
const previous = reportsByBuild.get(report.buildId)
|
|
62
|
+
if (previous?.kind === 'rebuild') {
|
|
63
|
+
continue
|
|
64
|
+
}
|
|
65
|
+
if (report.kind === 'rebuild' || previous === undefined || report.seq > previous.seq) {
|
|
66
|
+
reportsByBuild.set(report.buildId, report)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return [...reportsByBuild.values()]
|
|
70
|
+
}
|
|
@@ -7,9 +7,8 @@ import type { VitePluginTaroOptions } from '../../../../options.ts'
|
|
|
7
7
|
import { once } from '../../../utils/once.ts'
|
|
8
8
|
import { resolvePackageFile } from '../../../utils/packages.ts'
|
|
9
9
|
import { appShellFileName } from '../module.ts'
|
|
10
|
-
import { createWxDevMode } from './create-wx-dev-mode.ts'
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
type BundledDevRolldownOptions = InputOptions & {
|
|
13
12
|
experimental?: {
|
|
14
13
|
[key: string]: unknown
|
|
15
14
|
devMode?: boolean | Record<string, unknown>
|
|
@@ -24,7 +23,10 @@ export type BundledDev = {
|
|
|
24
23
|
triggerBundleRegenerationIfStale(): Promise<boolean>
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Installs physical WX output and runtime conventions over Vite's browser-oriented bundled-development options.
|
|
28
|
+
* Build completion deliberately remains outside this options adapter: DevEngine onOutput is the single lifecycle authority.
|
|
29
|
+
*/
|
|
28
30
|
export function installWxDevOptions({
|
|
29
31
|
bundledDev,
|
|
30
32
|
server,
|
|
@@ -35,17 +37,13 @@ export function installWxDevOptions({
|
|
|
35
37
|
server: ViteDevServer
|
|
36
38
|
options: VitePluginTaroOptions
|
|
37
39
|
hostPlugins: Plugin[]
|
|
38
|
-
}):
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
} else {
|
|
46
|
-
initialBuild.resolve()
|
|
47
|
-
}
|
|
48
|
-
})
|
|
40
|
+
}): void {
|
|
41
|
+
/*
|
|
42
|
+
* Vite owns this mutable adapter method and calls it later when constructing DevEngine. Replacing that one seam preserves
|
|
43
|
+
* Vite's resolved graph while applying WX physical-output conventions to every options generation. Capturing and mutating one
|
|
44
|
+
* options object here would be stale on later complete builds; creating a second engine would duplicate watchers and graphs.
|
|
45
|
+
* The bound original remains immutable and is invoked exactly once per delegated options request.
|
|
46
|
+
*/
|
|
49
47
|
const original = bundledDev.getRolldownOptions.bind(bundledDev)
|
|
50
48
|
|
|
51
49
|
bundledDev.getRolldownOptions = async () => {
|
|
@@ -59,9 +57,16 @@ export function installWxDevOptions({
|
|
|
59
57
|
const configured = configuredOutput ?? {}
|
|
60
58
|
|
|
61
59
|
// Every page entry must depend on hmr/patches.js: DevTools classifies a changed Page dependency as Page JavaScript
|
|
62
|
-
// hot reload and re-executes live Pages, which is the only physical patch trigger that preserves the App heap.
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
// hot reload and re-executes live Pages, which is the only physical patch trigger that preserves the App heap. Although
|
|
61
|
+
// Set provides O(1) membership, exposing it as ReadonlySet prevents mutation after this options generation is resolved.
|
|
62
|
+
const pageFiles: ReadonlySet<string> = new Set(options.pages.map((page) => `${page.path}.js`))
|
|
63
|
+
|
|
64
|
+
/*
|
|
65
|
+
* Rolldown passes this mutable output object onward by identity, and nested Vite plugins may already retain it. Mutating
|
|
66
|
+
* that single object preserves their references while atomically replacing browser-oriented naming and format fields with
|
|
67
|
+
* stable physical WX conventions. Cloning only our fields would leave retained references on stale hashed filenames;
|
|
68
|
+
* mutating configuredOutput itself would leak development normalization back into the user's resolved Vite config.
|
|
69
|
+
*/
|
|
65
70
|
Object.assign(output, configured, {
|
|
66
71
|
assetFileNames: createStableFileNames(configured.assetFileNames, 'assets/[name][extname]'),
|
|
67
72
|
banner: createEntryBanner(pageFiles),
|
|
@@ -72,31 +77,29 @@ export function installWxDevOptions({
|
|
|
72
77
|
sourcemap: false
|
|
73
78
|
})
|
|
74
79
|
|
|
80
|
+
/*
|
|
81
|
+
* experimental belongs to this transaction-local Rolldown options generation. Materialize it only when absent, then
|
|
82
|
+
* replace devMode with a new object so user fields survive without mutating a possibly shared configured sub-object.
|
|
83
|
+
*/
|
|
75
84
|
rolldownOptions.experimental ??= {}
|
|
76
|
-
rolldownOptions.experimental.devMode
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
name: 'vpt:wx-report-initial-build',
|
|
83
|
-
buildEnd: settleInitialBuild
|
|
85
|
+
const existingDevMode = rolldownOptions.experimental.devMode
|
|
86
|
+
rolldownOptions.experimental.devMode = {
|
|
87
|
+
...(typeof existingDevMode === 'object' ? existingDevMode : {}),
|
|
88
|
+
implement: await bundleRuntimeSource(),
|
|
89
|
+
lazy: false,
|
|
90
|
+
skipCommonRuntimeInjection: false
|
|
84
91
|
}
|
|
85
92
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
createViteReporter(server)
|
|
93
|
-
]
|
|
93
|
+
/*
|
|
94
|
+
* The plugin list is mutable configuration consumed once by this engine generation. Replace the top-level reference with
|
|
95
|
+
* an ordered composite rather than pushing into Vite's potentially shared nested array: existing transforms run first,
|
|
96
|
+
* host capture observes their final values, and the reporter observes final output without mutating either input list.
|
|
97
|
+
*/
|
|
98
|
+
rolldownOptions.plugins = [rolldownOptions.plugins, hostPlugins, createViteReporter(server)]
|
|
94
99
|
disableViteOxcSourcemap(rolldownOptions.plugins)
|
|
95
100
|
|
|
96
101
|
return rolldownOptions
|
|
97
102
|
}
|
|
98
|
-
|
|
99
|
-
return initialBuild.promise
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
/** Returns the configured output after rejecting states unsupported by the physical WX engine. */
|
|
@@ -113,6 +116,7 @@ function ensureSingleOutput(rolldownOptions: BundledDevRolldownOptions): OutputO
|
|
|
113
116
|
if (Array.isArray(rolldownOptions.output)) {
|
|
114
117
|
throw new Error('wx development requires one configured Rolldown output.')
|
|
115
118
|
}
|
|
119
|
+
// Rolldown needs the created object attached to input options by identity; returning a detached fallback would be ignored.
|
|
116
120
|
rolldownOptions.output ??= {}
|
|
117
121
|
return rolldownOptions.output
|
|
118
122
|
}
|
|
@@ -164,6 +168,11 @@ function disableViteOxcSourcemap(pluginOption: unknown): void {
|
|
|
164
168
|
|
|
165
169
|
const plugin = pluginOption as ViteTransformPlugin
|
|
166
170
|
if (plugin.name === 'builtin:vite-transform' && plugin._options?.transformOptions) {
|
|
171
|
+
/*
|
|
172
|
+
* Vite's private builtin retains this mutable transform options object and does not expose a public replacement hook.
|
|
173
|
+
* Updating its one sourcemap flag prevents Oxc from allocating maps that final WX output always discards; cloning the
|
|
174
|
+
* descriptor would not update the builtin closure that reads the original object during transforms.
|
|
175
|
+
*/
|
|
167
176
|
plugin._options.transformOptions.sourcemap = false
|
|
168
177
|
}
|
|
169
178
|
}
|
|
@@ -182,7 +191,12 @@ function createViteReporter(server: ViteDevServer) {
|
|
|
182
191
|
})
|
|
183
192
|
}
|
|
184
193
|
|
|
185
|
-
|
|
194
|
+
/*
|
|
195
|
+
* once owns a mutable cached Promise, justified because getRolldownOptions may run for multiple complete generations while the
|
|
196
|
+
* runtime input and build options remain immutable for the process lifetime. Sharing the in-flight/result Promise prevents
|
|
197
|
+
* concurrent options requests from launching duplicate nested builds; caching source instead of a Rolldown output object avoids
|
|
198
|
+
* leaking mutable bundle metadata between engines.
|
|
199
|
+
*/
|
|
186
200
|
const bundleRuntimeSource = once(async function bundleRuntimeSource(): Promise<string> {
|
|
187
201
|
// write: false keeps this nested helper build from creating a second dist directory in the application project.
|
|
188
202
|
const result = await build({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
-
import type
|
|
2
|
+
import { normalizePath, type Rolldown } from 'vite'
|
|
3
3
|
import { normalizeModuleId } from '../../utils/modules.ts'
|
|
4
4
|
import { packageRequire, resolvePackageFile } from '../../utils/packages.ts'
|
|
5
5
|
|
|
@@ -7,9 +7,7 @@ import { packageRequire, resolvePackageFile } from '../../utils/packages.ts'
|
|
|
7
7
|
export const rolldownRuntimeId = '\0rolldown/runtime.js'
|
|
8
8
|
|
|
9
9
|
/** Resolves the direct React Reconciler dependency without assuming pnpm's layout. */
|
|
10
|
-
export const reactReconcilerRoot =
|
|
11
|
-
path.dirname(packageRequire.resolve('react-reconciler/package.json'))
|
|
12
|
-
)
|
|
10
|
+
export const reactReconcilerRoot = normalizePath(path.dirname(packageRequire.resolve('react-reconciler/package.json')))
|
|
13
11
|
|
|
14
12
|
/** Identifies the amphibious bootstrap that initializes SystemJS and serves every native shell. */
|
|
15
13
|
export const bootstrapPath = resolvePackageFile('dist/runtime/wx/amphibious/bootstrap.js')
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
+
import { normalizePath } from 'vite'
|
|
2
3
|
import { normalizeModuleId } from '../../../utils/modules.ts'
|
|
3
4
|
import { collectNativeComponentAssets, nativeComponentMetaKey } from './native-component-assets.ts'
|
|
4
5
|
import { transformNativeComponentInterfaces } from './native-component-interface.ts'
|
|
@@ -26,7 +27,7 @@ export async function compileNativeComponentInterface({
|
|
|
26
27
|
addWatchFile(source.folder)
|
|
27
28
|
|
|
28
29
|
source.assets.forEach((asset) => {
|
|
29
|
-
addWatchFile(
|
|
30
|
+
addWatchFile(normalizePath(path.join(source.folder, asset.relativePath)))
|
|
30
31
|
})
|
|
31
32
|
})
|
|
32
33
|
|
|
@@ -43,6 +43,8 @@ export async function createNativeComponentOutput({
|
|
|
43
43
|
})
|
|
44
44
|
|
|
45
45
|
for (const asset of source.assets) {
|
|
46
|
+
// Native folders are intentionally opaque to Rolldown, so no bundle asset contains these bytes to reuse.
|
|
47
|
+
// Read each planned source once here to transfer it into the compiler-owned output bundle.
|
|
46
48
|
files.push({
|
|
47
49
|
type: 'asset',
|
|
48
50
|
fileName: path.posix.join(outputFolder, asset.relativePath),
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { readdir, stat } from 'node:fs/promises'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
-
import type
|
|
4
|
-
import { normalizeModuleId } from '../../../utils/modules.ts'
|
|
3
|
+
import { normalizePath, type Rolldown } from 'vite'
|
|
5
4
|
import type { NativeComponentDefinition } from './native-component-interface.ts'
|
|
6
5
|
|
|
7
6
|
type NativeComponentAsset = {
|
|
@@ -46,7 +45,7 @@ export async function collectNativeComponentAssets(
|
|
|
46
45
|
|
|
47
46
|
// This mutable accumulator is local to one traversal and avoids repeatedly copying growing asset arrays.
|
|
48
47
|
const assets: NativeComponentAsset[] = []
|
|
49
|
-
await collectDirectory(definition.folder, [], assets,
|
|
48
|
+
await collectDirectory(definition.folder, [], assets, normalizePath(interfacePath))
|
|
50
49
|
|
|
51
50
|
return { ...definition, assets }
|
|
52
51
|
}
|
|
@@ -103,7 +102,7 @@ async function collectDirectory(
|
|
|
103
102
|
continue
|
|
104
103
|
}
|
|
105
104
|
const sourcePath = path.join(folder, ...entrySegments)
|
|
106
|
-
if (
|
|
105
|
+
if (normalizePath(sourcePath) === interfacePath) {
|
|
107
106
|
continue
|
|
108
107
|
}
|
|
109
108
|
assets.push({
|
|
@@ -3,6 +3,7 @@ import type { ExistingRawSourceMap } from 'rolldown'
|
|
|
3
3
|
import { RolldownMagicString } from 'rolldown'
|
|
4
4
|
import { parseAst } from 'rolldown/parseAst'
|
|
5
5
|
import type { ESTree } from 'rolldown/utils'
|
|
6
|
+
import { normalizePath } from 'vite'
|
|
6
7
|
import { normalizeModuleId } from '../../../utils/modules.ts'
|
|
7
8
|
import { clientTaroNativeId } from '../../client/constant.ts'
|
|
8
9
|
|
|
@@ -138,7 +139,7 @@ function parseDefinition(
|
|
|
138
139
|
throw buildError('Native component entry must use () => import(...) with a static relative .js path')
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
const entryPath =
|
|
142
|
+
const entryPath = normalizePath(path.resolve(path.dirname(moduleId), entryReference))
|
|
142
143
|
return {
|
|
143
144
|
folder: path.posix.dirname(entryPath),
|
|
144
145
|
entry: path.posix.basename(entryPath, '.js'),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizePath } from 'vite'
|
|
1
2
|
import type { VitePluginTaroOptions, VitePluginTaroPageOption } from '../../../../options.ts'
|
|
2
3
|
import { normalizeModuleId, resolveAppComponentPath, resolvePageComponentPath } from '../../../utils/modules.ts'
|
|
3
4
|
import { createAppConfig } from '../../../utils/project-config.ts'
|
|
@@ -25,8 +26,8 @@ type PrivateIdResolver = (importer: string | undefined, projectRoot: string) =>
|
|
|
25
26
|
|
|
26
27
|
/** Creates the resolver and source specializer for the wx module graph. */
|
|
27
28
|
export function createResolver(options: VitePluginTaroOptions) {
|
|
28
|
-
const normalizedBootstrapPath =
|
|
29
|
-
const normalizedPageCapsulePath =
|
|
29
|
+
const normalizedBootstrapPath = normalizePath(bootstrapPath)
|
|
30
|
+
const normalizedPageCapsulePath = normalizePath(pageCapsulePath)
|
|
30
31
|
|
|
31
32
|
// Construct output input and application traversal roots together once so style order cannot drift from route order.
|
|
32
33
|
const entryGraph = createEntryGraph(options.pages)
|
|
@@ -48,6 +48,15 @@ export function createWxStylePlugins(): PluginOption[] {
|
|
|
48
48
|
* remain opaque.
|
|
49
49
|
*/
|
|
50
50
|
function createWxStyleFinalizer(transformStyle: typeof transformWxStyle): Plugin {
|
|
51
|
+
/*
|
|
52
|
+
* DevEngine can omit an unchanged stylesheet from later complete output generations. Absence therefore has two meanings:
|
|
53
|
+
* the first clean output genuinely has no styles, or a later output is reusing the physical stylesheet already on disk.
|
|
54
|
+
* The Vite server creates a fresh plugin instance on a clean start, so this one lifecycle bit distinguishes those cases and
|
|
55
|
+
* resets naturally on restart. Deliberately retain no CSS bytes or graph projection here: memory remains O(1), changed CSS
|
|
56
|
+
* still arrives as a normal asset, and the dev host remains the only owner of incremental style preparation.
|
|
57
|
+
*/
|
|
58
|
+
let hasFinalizedOutput = false
|
|
59
|
+
|
|
51
60
|
return {
|
|
52
61
|
name: 'vpt:wx-style-finalizer',
|
|
53
62
|
generateBundle: {
|
|
@@ -61,7 +70,17 @@ function createWxStyleFinalizer(transformStyle: typeof transformWxStyle): Plugin
|
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
if (styles.length === 0) {
|
|
64
|
-
|
|
73
|
+
/*
|
|
74
|
+
* A clean first output must materialize the stable import target imported by app.wxss. On later complete
|
|
75
|
+
* outputs, DevEngine's physical writer preserves files omitted from the bundle; emitting the same empty
|
|
76
|
+
* placeholder would instead overwrite valid unchanged WXSS. Emit nothing in that later case. A source or
|
|
77
|
+
* configuration change that really removes all styles is already published as empty by ordinary style HMR,
|
|
78
|
+
* while a clean server restart reaches this first-output branch and also clears any stale prior file.
|
|
79
|
+
*/
|
|
80
|
+
if (!hasFinalizedOutput) {
|
|
81
|
+
this.emitFile({ type: 'asset', fileName: 'assets/global.wxss', source: '' })
|
|
82
|
+
}
|
|
83
|
+
hasFinalizedOutput = true
|
|
65
84
|
return
|
|
66
85
|
}
|
|
67
86
|
|
|
@@ -73,6 +92,7 @@ function createWxStyleFinalizer(transformStyle: typeof transformWxStyle): Plugin
|
|
|
73
92
|
// intact. Only its finalized contents and stable WXSS identity change.
|
|
74
93
|
style.source = transformedResult.css
|
|
75
94
|
style.fileName = 'assets/global.wxss'
|
|
95
|
+
hasFinalizedOutput = true
|
|
76
96
|
}
|
|
77
97
|
}
|
|
78
98
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
+
import { normalizePath } from 'vite'
|
|
2
3
|
|
|
3
4
|
type AppComponentPathOptions = {
|
|
4
5
|
appPath: string
|
|
@@ -33,12 +34,12 @@ export function createPageComponentImportPath(options: PageComponentPathOptions)
|
|
|
33
34
|
return toViteFileImportPath(resolvePageComponentPath(options))
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
/**
|
|
37
|
+
/** Converts a file-backed Vite request ID to its normalized physical path for stable comparisons. */
|
|
37
38
|
export function normalizeModuleId(id: string): string {
|
|
38
|
-
return id.
|
|
39
|
+
return normalizePath(id.replace(/\?.*$/, ''))
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
/** Converts a local file path into Vite's portable file-system import form. */
|
|
42
43
|
export function toViteFileImportPath(filePath: string): string {
|
|
43
|
-
return `/@fs/${
|
|
44
|
+
return `/@fs/${normalizePath(path.resolve(filePath))}`
|
|
44
45
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
-
import {
|
|
3
|
+
import { normalizePath } from 'vite'
|
|
4
4
|
|
|
5
5
|
export const packageRequire = createRequire(import.meta.url)
|
|
6
6
|
const packageRoot = path.dirname(packageRequire.resolve('vite-plugin-taro/package.json'))
|
|
7
7
|
|
|
8
8
|
/** Resolves a file shipped by this package as a portable Vite module ID. */
|
|
9
9
|
export function resolvePackageFile(...segments: string[]): string {
|
|
10
|
-
return
|
|
10
|
+
return normalizePath(path.join(packageRoot, ...segments))
|
|
11
11
|
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { InputOptions } from 'rolldown';
|
|
2
|
-
type DevMode = NonNullable<NonNullable<InputOptions['experimental']>['devMode']>;
|
|
3
|
-
type DevModeOptions = Exclude<DevMode, boolean>;
|
|
4
|
-
/** Installs the custom WX runtime while restoring Rolldown's common runtime base. */
|
|
5
|
-
export declare function createWxDevMode(devMode: DevMode | undefined, implement: string): DevModeOptions;
|
|
6
|
-
export {};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/** Installs the custom WX runtime while restoring Rolldown's common runtime base. */
|
|
2
|
-
export function createWxDevMode(devMode, implement) {
|
|
3
|
-
return {
|
|
4
|
-
...(typeof devMode === 'object' ? devMode : {}),
|
|
5
|
-
implement,
|
|
6
|
-
lazy: false,
|
|
7
|
-
skipCommonRuntimeInjection: false
|
|
8
|
-
};
|
|
9
|
-
}
|