vite-plugin-taro 0.6.2 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +8 -3
- package/README.md +8 -3
- package/dist/node/plugins/wx/dev/dev-host.d.ts +5 -4
- package/dist/node/plugins/wx/dev/dev-host.js +19 -43
- package/dist/node/plugins/wx/dev/plugins.d.ts +6 -15
- package/dist/node/plugins/wx/dev/plugins.js +16 -70
- package/dist/node/plugins/wx/dev/react-refresh.d.ts +9 -3
- package/dist/node/plugins/wx/dev/react-refresh.js +35 -7
- package/dist/node/plugins/wx/dev/wx-dev-options.d.ts +2 -3
- package/dist/node/plugins/wx/dev/wx-dev-options.js +6 -7
- package/dist/node/plugins/wx/plugins.js +5 -11
- package/dist/node/plugins/wx/resolve/resolver.js +4 -4
- package/dist/node/plugins/wx/resolve/{specialize-bootstrap.d.ts → specialize-app-capsule.d.ts} +2 -2
- package/dist/node/plugins/wx/resolve/{specialize-bootstrap.js → specialize-app-capsule.js} +2 -2
- package/dist/node/plugins/wx/styles/plugins.d.ts +141 -3
- package/dist/node/plugins/wx/styles/plugins.js +401 -89
- package/dist/node/utils/vite.d.ts +4 -14
- package/dist/node/utils/vite.js +7 -40
- package/dist/runtime/wx/amphibious/bootstrap.d.ts +0 -2
- package/dist/runtime/wx/amphibious/bootstrap.js +0 -2
- package/dist/runtime/wx/capsule/app.d.ts +2 -2
- package/dist/runtime/wx/capsule/app.js +2 -2
- package/dist/runtime/wx/dev/dev-runtime.js +55 -79
- package/package.json +4 -3
- package/src/node/plugins/wx/dev/dev-host.ts +22 -44
- package/src/node/plugins/wx/dev/plugins.ts +17 -73
- package/src/node/plugins/wx/dev/react-refresh.ts +36 -7
- package/src/node/plugins/wx/dev/wx-dev-options.ts +7 -10
- package/src/node/plugins/wx/plugins.ts +5 -11
- package/src/node/plugins/wx/resolve/resolver.ts +4 -4
- package/src/node/plugins/wx/resolve/{specialize-bootstrap.ts → specialize-app-capsule.ts} +2 -2
- package/src/node/plugins/wx/styles/plugins.ts +518 -93
- package/src/node/utils/vite.ts +13 -58
- package/src/runtime/wx/amphibious/bootstrap.ts +0 -5
- package/src/runtime/wx/capsule/app.ts +5 -2
- package/src/runtime/wx/dev/dev-runtime.ts +71 -120
- package/src/runtime/wx/wechat.d.ts +1 -2
- package/dist/node/plugins/wx/dev/create-style-capture.d.ts +0 -54
- package/dist/node/plugins/wx/dev/create-style-capture.js +0 -173
- package/dist/node/plugins/wx/styles/transform-wx-style.d.ts +0 -8
- package/dist/node/plugins/wx/styles/transform-wx-style.js +0 -9
- package/dist/node/plugins/wx/styles/utils.d.ts +0 -39
- package/dist/node/plugins/wx/styles/utils.js +0 -95
- package/src/node/plugins/wx/dev/create-style-capture.ts +0 -248
- package/src/node/plugins/wx/styles/transform-wx-style.ts +0 -11
- package/src/node/plugins/wx/styles/utils.ts +0 -119
package/src/node/utils/vite.ts
CHANGED
|
@@ -1,67 +1,22 @@
|
|
|
1
|
-
import type { HookHandler, Plugin
|
|
1
|
+
import type { HookHandler, Plugin } from 'vite'
|
|
2
2
|
|
|
3
3
|
type TransformHook = HookHandler<NonNullable<Plugin['transform']>>
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
type AsyncTransformHook = (
|
|
6
6
|
this: ThisParameterType<TransformHook>,
|
|
7
7
|
...args: Parameters<TransformHook>
|
|
8
|
-
) => Promise<
|
|
9
|
-
export type TransformHookWrapper = (transform: AsyncTransformHook) => AsyncTransformHook
|
|
10
|
-
|
|
11
|
-
export type PluginMapper = (plugin: Plugin) => Plugin
|
|
12
|
-
|
|
13
|
-
/** Transforms every concrete plugin while preserving nested arrays, falsy options, and promised options. */
|
|
14
|
-
export function transformVitePlugin(pluginOptions: PluginOption[], mapPlugin: PluginMapper): PluginOption[] {
|
|
15
|
-
return pluginOptions.map((option) => transformPluginOption(option, mapPlugin))
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function transformPluginOption(option: PluginOption, mapPlugin: PluginMapper): PluginOption {
|
|
19
|
-
if (option instanceof Promise) {
|
|
20
|
-
return option.then((resolvedOption) => transformPluginOption(resolvedOption, mapPlugin))
|
|
21
|
-
}
|
|
8
|
+
) => Promise<Awaited<ReturnType<TransformHook>>>
|
|
22
9
|
|
|
23
|
-
|
|
24
|
-
return transformVitePlugin(option, mapPlugin)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return isPlugin(option) ? mapPlugin(option) : option
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function isPlugin(option: PluginOption): option is Plugin {
|
|
31
|
-
return (
|
|
32
|
-
option !== null &&
|
|
33
|
-
option !== false &&
|
|
34
|
-
option !== undefined &&
|
|
35
|
-
typeof option === 'object' &&
|
|
36
|
-
!Array.isArray(option) &&
|
|
37
|
-
'name' in option
|
|
38
|
-
)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Clones a Vite transform hook with middleware that controls execution of the original handler.
|
|
43
|
-
*
|
|
44
|
-
* Function and object hook forms retain their original plugin context. Object metadata such as `order` and `filter` is copied
|
|
45
|
-
* unchanged, and the input descriptor is never mutated. The wrapper receives the normalized asynchronous transform with its
|
|
46
|
-
* complete plugin context, code, ID, and metadata signature, and returns the handler that continues Vite's plugin pipeline.
|
|
47
|
-
*/
|
|
48
|
-
export function wrapPluginTransform(plugin: Plugin, wrapper: TransformHookWrapper): Plugin {
|
|
49
|
-
const { transform } = plugin
|
|
50
|
-
|
|
51
|
-
if (!transform) {
|
|
52
|
-
throw new Error(`${plugin.name} must expose a transform hook`)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const isTransformFunction = typeof transform === 'function'
|
|
56
|
-
|
|
57
|
-
const handler = isTransformFunction ? transform : transform.handler
|
|
10
|
+
export type TransformHookWrapper = (transform: AsyncTransformHook) => AsyncTransformHook
|
|
58
11
|
|
|
59
|
-
|
|
60
|
-
|
|
12
|
+
/** Mutates one concrete plugin to interpose on its transform while preserving hook metadata and plugin context. */
|
|
13
|
+
export function wrapPluginTransform(plugin: Plugin, wrapper: TransformHookWrapper): void {
|
|
14
|
+
const transform = plugin.transform!
|
|
15
|
+
const handler = typeof transform === 'function' ? transform : transform.handler
|
|
16
|
+
const wrapped = wrapper(async function (code, id, options) {
|
|
17
|
+
return handler.call(this, code, id, options)
|
|
61
18
|
})
|
|
62
19
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
transform: isTransformFunction ? wrappedHandler : { ...transform, handler: wrappedHandler }
|
|
66
|
-
}
|
|
20
|
+
// Installation-time mutation keeps Vite's existing plugin identity and resolved hook registration.
|
|
21
|
+
plugin.transform = typeof transform === 'function' ? wrapped : { ...transform, handler: wrapped }
|
|
67
22
|
}
|
|
@@ -2,11 +2,6 @@
|
|
|
2
2
|
import '../systemjs/system-core.js'
|
|
3
3
|
import { transport } from './transport.ts'
|
|
4
4
|
|
|
5
|
-
declare const __VPT_APP_CONFIG__: Record<string, unknown>
|
|
6
|
-
|
|
7
|
-
/** Shares one App configuration object between the specialized bootstrap and App capsule. */
|
|
8
|
-
export const appConfig = __VPT_APP_CONFIG__
|
|
9
|
-
|
|
10
5
|
// WX has no modulepreload transport. Genuine application import() boundaries retain System.import() and may load
|
|
11
6
|
// asynchronous subpackage or top-level-await graphs through this identity wrapper.
|
|
12
7
|
export const __vitePreload = <Value>(load: () => Value): Value => load()
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
// biome-ignore assist/source/organizeImports: Taro must initialize before the App component.
|
|
2
2
|
import { createReactApp, ReactDOM } from './taro-runtime.ts'
|
|
3
3
|
import React from 'react'
|
|
4
|
-
import { appConfig } from '../amphibious/bootstrap.ts'
|
|
5
4
|
|
|
6
5
|
// @ts-expect-error: The wx build resolves this private App component.
|
|
7
6
|
import AppComponent from '\0vpt:app-component'
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
declare const __VPT_APP_CONFIG__: Record<string, unknown>
|
|
9
|
+
|
|
10
|
+
const config = createReactApp(AppComponent, React, ReactDOM, __VPT_APP_CONFIG__)
|
|
11
|
+
|
|
12
|
+
export default config
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// The `DevRuntime` base class is injected into the chunk by Rolldown's dev-mode
|
|
6
6
|
// transform, so the WX host only extends it.
|
|
7
7
|
//
|
|
8
|
-
// Every Page explicitly passes the inert hmr/patches.js export here before
|
|
8
|
+
// Every Page explicitly passes the inert hmr/patches.js export here before native Page registration. The runtime applies that
|
|
9
9
|
// cumulative suffix synchronously, reports its successful application frontier, and ignores sequences replayed by other Pages.
|
|
10
10
|
|
|
11
11
|
import type { DevRuntime as RolldownDevRuntime } from 'rolldown/experimental/runtime-types'
|
|
@@ -54,54 +54,30 @@ type HmrUpdate = Readonly<{
|
|
|
54
54
|
|
|
55
55
|
type AcceptCallback = (moduleExports: unknown) => void
|
|
56
56
|
|
|
57
|
-
type PageSnapshot = Readonly<{
|
|
58
|
-
$taroPath: string
|
|
59
|
-
$taroParams: Record<string, unknown>
|
|
60
|
-
data: Record<string, unknown>
|
|
61
|
-
}>
|
|
62
|
-
|
|
63
57
|
type NativePage = {
|
|
64
|
-
$taroPath: string
|
|
65
|
-
$taroParams: Record<string, unknown>
|
|
66
58
|
data: Record<string, unknown>
|
|
67
|
-
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const pageHmrStateKey: unique symbol = Symbol('vpt.pageHmrState')
|
|
62
|
+
|
|
63
|
+
type PageHmrState = {
|
|
64
|
+
/** True only across the unload/load/show sequence triggered by one native re-registration. */
|
|
65
|
+
isReregistering: boolean
|
|
66
|
+
/** The Page bound to `this` by ordinary onLoad, retained until ordinary onUnload. */
|
|
67
|
+
mountedPage: NativePage | undefined
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
type HmrPageConfig = {
|
|
71
|
+
data: Record<string, unknown>
|
|
71
72
|
onUnload?: unknown
|
|
72
73
|
onLoad?: unknown
|
|
73
74
|
onShow?: unknown
|
|
75
|
+
[pageHmrStateKey]?: PageHmrState
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
/** Immutable bridge references plus replacement transactions owned by one Taro singleton. */
|
|
81
|
-
type TaroState = {
|
|
82
|
-
/** Taro's existing current-page source of truth; replacement onLoad switches its receiver. */
|
|
83
|
-
readonly current: { page: NativePage | null }
|
|
84
|
-
/** Taro's virtual document, used to find the retained root by its preserved unique path. */
|
|
85
|
-
readonly document: { getElementById(path: string): unknown }
|
|
86
|
-
/** Replaces Taro's native lifecycle receiver without remounting the retained React subtree. */
|
|
87
|
-
readonly injectPageInstance: (instance: unknown, path: string) => void
|
|
88
|
-
/**
|
|
89
|
-
* Short-lived route transactions. Absence means ordinary lifecycle; null means armed
|
|
90
|
-
* before onUnload or snapshot-consumed before onShow; a snapshot exists only between
|
|
91
|
-
* replacement onUnload and onLoad. The large data reference is therefore released in
|
|
92
|
-
* onLoad, while the null marker remains just long enough to suppress synthetic onShow.
|
|
93
|
-
*/
|
|
94
|
-
readonly pageReplacements: Map<string, PageSnapshot | null>
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** Forwards a native lifecycle with its original Page receiver. */
|
|
98
|
-
function forward(handler: unknown, receiver: unknown, args: unknown[]): void {
|
|
99
|
-
if (typeof handler === 'function') handler.apply(receiver, args)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** Narrows the untyped element returned across the Taro connection boundary. */
|
|
103
|
-
function isTaroRoot(value: unknown): value is TaroRoot {
|
|
104
|
-
return typeof value === 'object' && value !== null && 'ctx' in value
|
|
78
|
+
/** Calls a native lifecycle with the same Page bound to `this` and the same arguments. */
|
|
79
|
+
function forward(handler: unknown, page: unknown, args: unknown[]): void {
|
|
80
|
+
if (typeof handler === 'function') handler.apply(page, args)
|
|
105
81
|
}
|
|
106
82
|
|
|
107
83
|
/** Shared no-op CSS contract because physical rebuilds replace styles wholesale. */
|
|
@@ -285,109 +261,90 @@ class WxDevRuntime extends DevRuntime {
|
|
|
285
261
|
this.session = { ...info, appliedSeq: 0 }
|
|
286
262
|
}
|
|
287
263
|
|
|
288
|
-
/**
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return
|
|
303
|
-
}
|
|
264
|
+
/** Tracks the mounted native Page and prepares its static config for HMR re-registration. */
|
|
265
|
+
injectPageHmr(config: HmrPageConfig): HmrPageConfig {
|
|
266
|
+
const existingState = config[pageHmrStateKey]
|
|
267
|
+
|
|
268
|
+
if (existingState) {
|
|
269
|
+
const mountedPage = existingState.mountedPage
|
|
270
|
+
/*
|
|
271
|
+
* The static config can outlive a native Page instance. Before its first ordinary onLoad, or after a real onUnload,
|
|
272
|
+
* there is no mounted Page or current view-model to carry into another registration. Leave the lifecycle gate
|
|
273
|
+
* unarmed and preserve the config's existing initial data so a future real onLoad still enters Taro normally.
|
|
274
|
+
*/
|
|
275
|
+
if (!mountedPage) {
|
|
276
|
+
return config
|
|
277
|
+
}
|
|
304
278
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
279
|
+
/*
|
|
280
|
+
* Arm the lifecycle wrappers on this exact static config before it is passed back to `Page(config)`. DevTools then
|
|
281
|
+
* triggers an unload/load/show sequence for that native re-registration: unload and load observe `true` and return
|
|
282
|
+
* before entering Taro, preserving the mounted React tree and its original Page connection; show consumes the
|
|
283
|
+
* one-shot gate by restoring `false`. Ordinary navigation never enters this branch, and every Page config owns an
|
|
284
|
+
* independent state object, so no route map, global phase, or Page identity comparison participates in the decision.
|
|
285
|
+
*/
|
|
286
|
+
existingState.isReregistering = true
|
|
287
|
+
/*
|
|
288
|
+
* `Page(config)` reads `config.data` as the initial native view-model for this registration. Supplying the mounted
|
|
289
|
+
* Page's latest data before that call prevents the temporary Page used for re-registration callbacks from starting
|
|
290
|
+
* empty. This is an O(1) reference assignment in vpt: it does not clone the recursive data tree, call `setData`, move
|
|
291
|
+
* React state, or rebind Taro. The ordinary Taro lifecycle remains suppressed until re-registration onShow, so its
|
|
292
|
+
* React tree and output connection stay attached to `mountedPage`; every later registration reads its latest data.
|
|
293
|
+
*/
|
|
294
|
+
config.data = mountedPage.data
|
|
295
|
+
|
|
296
|
+
return config
|
|
310
297
|
}
|
|
311
|
-
}
|
|
312
298
|
|
|
313
|
-
/** Injects snapshot-preserving behavior into one route-specific Taro Page configuration. */
|
|
314
|
-
injectPageHmr(config: HmrPageConfig, route: string): void {
|
|
315
299
|
const originalOnUnload = config.onUnload
|
|
316
300
|
const originalOnLoad = config.onLoad
|
|
317
301
|
const originalOnShow = config.onShow
|
|
318
|
-
|
|
302
|
+
|
|
303
|
+
const state: PageHmrState = {
|
|
304
|
+
isReregistering: false,
|
|
305
|
+
mountedPage: undefined
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/*
|
|
309
|
+
* Attach the one mutable HMR state object to this exact static config. The lifecycle wrappers below close over the same
|
|
310
|
+
* object, while a later `injectPageHmr(config)` call finds it through the symbol and knows wrapping is already complete.
|
|
311
|
+
* A symbol cannot collide with WeChat or Taro's string-named Page options, and config-local ownership avoids route maps,
|
|
312
|
+
* a runtime-wide WeakMap, or state shared by two Page configs. The state becomes unreachable together with the config.
|
|
313
|
+
*/
|
|
314
|
+
config[pageHmrStateKey] = state
|
|
319
315
|
|
|
320
316
|
config.onUnload = function (this: NativePage, ...args: unknown[]) {
|
|
321
|
-
|
|
322
|
-
if (taro.pageReplacements.has(route)) {
|
|
323
|
-
taro.pageReplacements.set(route, {
|
|
324
|
-
$taroPath: this.$taroPath,
|
|
325
|
-
$taroParams: this.$taroParams,
|
|
326
|
-
// WeChat owns this serializable view-model. Keeping its reference is O(1),
|
|
327
|
-
// unlike cloning the complete recursive projection before every edit.
|
|
328
|
-
data: this.data
|
|
329
|
-
})
|
|
317
|
+
if (state.isReregistering) {
|
|
330
318
|
return
|
|
331
319
|
}
|
|
320
|
+
|
|
332
321
|
forward(originalOnUnload, this, args)
|
|
322
|
+
state.mountedPage = undefined
|
|
333
323
|
}
|
|
334
324
|
|
|
335
325
|
config.onLoad = function (this: NativePage, ...args: unknown[]) {
|
|
336
|
-
|
|
337
|
-
const snapshot = taro.pageReplacements.get(route)
|
|
338
|
-
if (snapshot) {
|
|
339
|
-
// Replace the transaction before native work so exceptions cannot retain the
|
|
340
|
-
// large data snapshot while the route waits for its synthetic onShow.
|
|
341
|
-
taro.pageReplacements.set(route, null)
|
|
342
|
-
|
|
343
|
-
// Snapshot paint is the first bridge operation and removes the empty-page gap.
|
|
344
|
-
this.setData(snapshot.data)
|
|
345
|
-
this.$taroPath = snapshot.$taroPath
|
|
346
|
-
this.$taroParams = snapshot.$taroParams
|
|
347
|
-
runtime.bindPage(this, snapshot.$taroPath)
|
|
326
|
+
if (state.isReregistering) {
|
|
348
327
|
return
|
|
349
328
|
}
|
|
329
|
+
|
|
350
330
|
forward(originalOnLoad, this, args)
|
|
331
|
+
state.mountedPage = this
|
|
351
332
|
}
|
|
352
333
|
|
|
353
334
|
config.onShow = function (this: NativePage, ...args: unknown[]) {
|
|
354
|
-
if (
|
|
355
|
-
|
|
335
|
+
if (state.isReregistering) {
|
|
336
|
+
state.isReregistering = false
|
|
356
337
|
return
|
|
357
338
|
}
|
|
358
339
|
|
|
359
340
|
forward(originalOnShow, this, args)
|
|
360
341
|
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/** Returns the Taro connection or fails at the first incorrectly ordered use. */
|
|
364
|
-
private requireTaro(): TaroState {
|
|
365
|
-
if (!this.taro) throw new Error('[vpt] WX HMR used before the Taro runtime was connected')
|
|
366
|
-
return this.taro
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/** Returns a retained Taro root after normal Page mount has created it. */
|
|
370
|
-
private findRoot(path: string): TaroRoot | undefined {
|
|
371
|
-
const root = this.requireTaro().document.getElementById(path)
|
|
372
|
-
return isTaroRoot(root) ? root : undefined
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/** Rebinds a retained Taro tree to one replacement native Page without repainting. */
|
|
376
|
-
private bindPage(instance: NativePage, path: string): void {
|
|
377
|
-
const taro = this.requireTaro()
|
|
378
|
-
taro.injectPageInstance(instance, path)
|
|
379
|
-
taro.current.page = instance
|
|
380
|
-
|
|
381
|
-
const pageElement = this.findRoot(path)
|
|
382
|
-
if (!pageElement) {
|
|
383
|
-
throw new Error(`[vpt] retained Taro page not found: ${path}`)
|
|
384
|
-
}
|
|
385
342
|
|
|
386
|
-
|
|
343
|
+
return config
|
|
387
344
|
}
|
|
388
345
|
|
|
389
|
-
/** Applies one Page-delivered payload
|
|
390
|
-
applyPatches(payload: PatchPayload | undefined
|
|
346
|
+
/** Applies one Page-delivered payload before its native shell registers the static route configuration. */
|
|
347
|
+
applyPatches(payload: PatchPayload | undefined): void {
|
|
391
348
|
// The initial physical dependency exports undefined until the host has a patch range.
|
|
392
349
|
if (!payload) return
|
|
393
350
|
|
|
@@ -397,12 +354,6 @@ class WxDevRuntime extends DevRuntime {
|
|
|
397
354
|
return
|
|
398
355
|
}
|
|
399
356
|
|
|
400
|
-
// A replayed payload still causes DevTools to replace this physical Page. Arm the
|
|
401
|
-
// route independently of whether this App heap already applied its patch sequence.
|
|
402
|
-
if (route) {
|
|
403
|
-
this.requireTaro().pageReplacements.set(route, null)
|
|
404
|
-
}
|
|
405
|
-
|
|
406
357
|
// Apply synchronously: the page's imports below the require resolve against the
|
|
407
358
|
// freshly registered modules, so the re-executed Page evaluates with the new code.
|
|
408
359
|
if (this.applyPatchBatch(session, payload.patches)) {
|
|
@@ -8,8 +8,7 @@ type WeChatGlobal = object
|
|
|
8
8
|
/** The App-global Rolldown dev runtime; present only in wx development builds. */
|
|
9
9
|
declare const __rolldown_runtime__:
|
|
10
10
|
| {
|
|
11
|
-
|
|
12
|
-
injectPageHmr(config: object, route: string): void
|
|
11
|
+
injectPageHmr(config: object): object
|
|
13
12
|
}
|
|
14
13
|
| undefined
|
|
15
14
|
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import type { GetModuleInfo, Plugin } from 'rolldown';
|
|
2
|
-
/** Final CSS extracted from Vite's transformed style module, before the shared WX compatibility pass. */
|
|
3
|
-
type ProcessedStyle = Readonly<{
|
|
4
|
-
css: string;
|
|
5
|
-
/** Marks roots whose generated utilities must be refreshed when JavaScript changes Tailwind candidates. */
|
|
6
|
-
isTailwindRoot: boolean;
|
|
7
|
-
}>;
|
|
8
|
-
/** Minimal structural view of DevEngine output; style reconciliation does not own chunks or other asset metadata. */
|
|
9
|
-
type CompleteOutputFile = Readonly<{
|
|
10
|
-
type: 'asset';
|
|
11
|
-
fileName: string;
|
|
12
|
-
source: string | Uint8Array;
|
|
13
|
-
}> | Readonly<{
|
|
14
|
-
type: 'chunk';
|
|
15
|
-
fileName: string;
|
|
16
|
-
}>;
|
|
17
|
-
export type StyleCaptureAction = Readonly<{
|
|
18
|
-
kind: 'capture-graph';
|
|
19
|
-
getModuleInfo: GetModuleInfo;
|
|
20
|
-
}> | Readonly<{
|
|
21
|
-
kind: 'capture-style';
|
|
22
|
-
id: string;
|
|
23
|
-
style: ProcessedStyle;
|
|
24
|
-
}>;
|
|
25
|
-
/**
|
|
26
|
-
* Captures final Vite CSS and composes the graph projection with its durable WXSS publisher.
|
|
27
|
-
*
|
|
28
|
-
* Rolldown remains authoritative for topology. Plugin hooks emit typed actions so the host serializes capture mutations with
|
|
29
|
-
* output and HMR publication; the projection and publisher below each own only one mutable concern.
|
|
30
|
-
*
|
|
31
|
-
* Complete-build path:
|
|
32
|
-
* final transform captures → DevEngine output write → graph reconciliation → App build rotation
|
|
33
|
-
*
|
|
34
|
-
* Incremental path:
|
|
35
|
-
* final transform captures → optional Tailwind root refresh → graph rendering → WXSS write → JavaScript patch publication
|
|
36
|
-
*
|
|
37
|
-
* Both paths render the same ordered App/Page graph projection. This prevents complete builds and HMR from implementing two
|
|
38
|
-
* subtly different CSS ownership policies, while keeping physical byte equality and atomic writes out of graph state.
|
|
39
|
-
*/
|
|
40
|
-
export declare function createStyleCapture({ applicationEntryIds, outDir, emit, transformTailwindRoot }: {
|
|
41
|
-
applicationEntryIds: readonly string[];
|
|
42
|
-
outDir: string;
|
|
43
|
-
emit: (action: StyleCaptureAction) => void;
|
|
44
|
-
transformTailwindRoot: (rootId: string, requestId: string) => Promise<Readonly<{
|
|
45
|
-
code: string;
|
|
46
|
-
}> | null>;
|
|
47
|
-
}): Readonly<{
|
|
48
|
-
captureGraph: (reader: GetModuleInfo) => void;
|
|
49
|
-
captureStyle: (id: string, style: ProcessedStyle) => void;
|
|
50
|
-
plugin: Plugin;
|
|
51
|
-
publishChanged: (changedIds: readonly string[]) => Promise<void>;
|
|
52
|
-
reconcileComplete: (output: readonly CompleteOutputFile[]) => Promise<void>;
|
|
53
|
-
}>;
|
|
54
|
-
export {};
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import { isCSSRequest } from 'vite';
|
|
2
|
-
import { normalizeModuleId } from '../../../utils/modules.js';
|
|
3
|
-
import { transformWxStyle } from '../styles/transform-wx-style.js';
|
|
4
|
-
import { composeGraphStyleCss, createGraphStylePlan, createTailwindSidecarId, extractViteCss, isGlobalStyleRequest } from '../styles/utils.js';
|
|
5
|
-
import { globalWxssFileName, writeHmrFile } from './hmr-files.js';
|
|
6
|
-
/**
|
|
7
|
-
* Captures final Vite CSS and composes the graph projection with its durable WXSS publisher.
|
|
8
|
-
*
|
|
9
|
-
* Rolldown remains authoritative for topology. Plugin hooks emit typed actions so the host serializes capture mutations with
|
|
10
|
-
* output and HMR publication; the projection and publisher below each own only one mutable concern.
|
|
11
|
-
*
|
|
12
|
-
* Complete-build path:
|
|
13
|
-
* final transform captures → DevEngine output write → graph reconciliation → App build rotation
|
|
14
|
-
*
|
|
15
|
-
* Incremental path:
|
|
16
|
-
* final transform captures → optional Tailwind root refresh → graph rendering → WXSS write → JavaScript patch publication
|
|
17
|
-
*
|
|
18
|
-
* Both paths render the same ordered App/Page graph projection. This prevents complete builds and HMR from implementing two
|
|
19
|
-
* subtly different CSS ownership policies, while keeping physical byte equality and atomic writes out of graph state.
|
|
20
|
-
*/
|
|
21
|
-
export function createStyleCapture({ applicationEntryIds, outDir, emit, transformTailwindRoot }) {
|
|
22
|
-
const projection = createStyleProjection({
|
|
23
|
-
applicationEntryIds: applicationEntryIds,
|
|
24
|
-
transformTailwindRoot: transformTailwindRoot
|
|
25
|
-
});
|
|
26
|
-
const publication = createStylePublication(outDir);
|
|
27
|
-
const plugin = {
|
|
28
|
-
name: 'vpt:wx-dev-style-capture',
|
|
29
|
-
buildStart() {
|
|
30
|
-
// Capture a live reader rather than a graph snapshot. Rolldown updates the capability as imports change, and a later
|
|
31
|
-
// complete generation replaces it through the host action queue before that generation can publish output.
|
|
32
|
-
emit({ kind: 'capture-graph', getModuleInfo: (moduleId) => this.getModuleInfo(moduleId) });
|
|
33
|
-
},
|
|
34
|
-
transform(code, id) {
|
|
35
|
-
if (!isGlobalStyleRequest(id)) {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
// This host plugin runs after Vite's CSS transform, so `code` is the JavaScript wrapper containing final PostCSS and
|
|
39
|
-
// CSS-Module output. Capturing source CSS instead would lose generated class names and framework transformations.
|
|
40
|
-
const css = extractViteCss(code, id);
|
|
41
|
-
emit({
|
|
42
|
-
kind: 'capture-style',
|
|
43
|
-
id: normalizeModuleId(id),
|
|
44
|
-
style: {
|
|
45
|
-
css: css,
|
|
46
|
-
isTailwindRoot: css.includes('weapp-tailwindcss vite-generated-css:')
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
return {
|
|
52
|
-
captureGraph: projection.captureGraph,
|
|
53
|
-
captureStyle: projection.captureStyle,
|
|
54
|
-
plugin: plugin,
|
|
55
|
-
async publishChanged(changedIds) {
|
|
56
|
-
// A CSS edit already carries updated processed bytes. Any non-CSS edit can alter both imports and Tailwind class
|
|
57
|
-
// candidates, so it requires a fresh graph plan and Tailwind-root generation even when no .css ID changed directly.
|
|
58
|
-
const styleChanged = changedIds.some(isGlobalStyleRequest);
|
|
59
|
-
const candidatesChanged = changedIds.some((id) => !isCSSRequest(id));
|
|
60
|
-
if (!styleChanged && !candidatesChanged) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
await publication.publish(await projection.render(candidatesChanged));
|
|
64
|
-
},
|
|
65
|
-
async reconcileComplete(output) {
|
|
66
|
-
// Bundled development can omit CSS Modules from the compiler asset even with cssCodeSplit disabled. Observe the
|
|
67
|
-
// physical output first, then reconcile the same graph projection used by incremental HMR before App rotation.
|
|
68
|
-
publication.observeOutput(output);
|
|
69
|
-
await publication.publish(await projection.render(false));
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Owns the live graph capability and final transformed bytes for every observed style module.
|
|
75
|
-
*
|
|
76
|
-
* Rendering is O(V + E + C): graph planning visits each reachable module and edge once, then composition and WX conversion
|
|
77
|
-
* process C CSS bytes once. The only persistent memory is one processed byte string per style identity observed by the server.
|
|
78
|
-
*/
|
|
79
|
-
function createStyleProjection({ applicationEntryIds, transformTailwindRoot }) {
|
|
80
|
-
/*
|
|
81
|
-
* This mutable capability is rebound by buildStart for each complete generation. Rolldown keeps the function live across
|
|
82
|
-
* incremental graph edits, so rendering uses authoritative topology without maintaining a shadow graph.
|
|
83
|
-
*/
|
|
84
|
-
let getModuleInfo;
|
|
85
|
-
/*
|
|
86
|
-
* This mutable projection stores final CSS bytes that Rolldown does not retain. Successful transforms replace one entry;
|
|
87
|
-
* unreachable entries can remain because every render filters them through the current graph plan. Its size is bounded by
|
|
88
|
-
* style module identities observed during this server lifecycle.
|
|
89
|
-
*/
|
|
90
|
-
const processedStyles = new Map();
|
|
91
|
-
return {
|
|
92
|
-
captureGraph(reader) {
|
|
93
|
-
getModuleInfo = reader;
|
|
94
|
-
},
|
|
95
|
-
captureStyle(id, style) {
|
|
96
|
-
processedStyles.set(id, style);
|
|
97
|
-
},
|
|
98
|
-
async render(refreshTailwind) {
|
|
99
|
-
if (!getModuleInfo) {
|
|
100
|
-
throw new Error('WX style graph is unavailable before publication');
|
|
101
|
-
}
|
|
102
|
-
// App first and configured Pages afterward define one deterministic global cascade. The plan also removes stale map
|
|
103
|
-
// entries implicitly: styles no longer reachable from these roots never enter composition.
|
|
104
|
-
const styleIds = createGraphStylePlan(applicationEntryIds, getModuleInfo, (styleId) => processedStyles.has(styleId));
|
|
105
|
-
if (refreshTailwind) {
|
|
106
|
-
await refreshTailwindStyles(styleIds, processedStyles, transformTailwindRoot);
|
|
107
|
-
}
|
|
108
|
-
// Compose browser-facing transformed CSS first, then run one whole-file WX pass. Transforming modules independently
|
|
109
|
-
// would change cross-module cascade behavior and duplicate compatibility work.
|
|
110
|
-
const css = composeGraphStyleCss(styleIds, (styleId) => requireProcessedStyle(processedStyles, styleId).css);
|
|
111
|
-
return (await transformWxStyle(css)).css;
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Owns the physical WXSS frontier independently from graph capture and rendering.
|
|
117
|
-
*
|
|
118
|
-
* DevEngine writes complete output itself; incremental HMR does not. Observing complete output before publishing the projection
|
|
119
|
-
* gives both writers one byte frontier, so equality avoids redundant filesystem notifications without pretending the host owns
|
|
120
|
-
* the compiler's write transaction.
|
|
121
|
-
*/
|
|
122
|
-
function createStylePublication(outDir) {
|
|
123
|
-
/*
|
|
124
|
-
* This mutable value mirrors bytes durable on disk. Complete output adopts the compiler's external write before graph
|
|
125
|
-
* reconciliation; host publication advances it only after an atomic write succeeds or byte equality proves none is needed.
|
|
126
|
-
*/
|
|
127
|
-
let publishedWxss;
|
|
128
|
-
return {
|
|
129
|
-
observeOutput(output) {
|
|
130
|
-
// Missing WXSS means DevEngine intentionally reused the existing physical asset. Do not reset the frontier: doing so
|
|
131
|
-
// would force an identical rewrite and a spurious DevTools style event on every omitted complete generation.
|
|
132
|
-
const style = output.find((file) => file.type === 'asset' && file.fileName === globalWxssFileName);
|
|
133
|
-
if (style) {
|
|
134
|
-
publishedWxss = typeof style.source === 'string' ? style.source : new TextDecoder().decode(style.source);
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
async publish(wxss) {
|
|
138
|
-
if (wxss !== publishedWxss) {
|
|
139
|
-
// writeHmrFile uses atomic replacement; advance the frontier only after durability so a failed write leaves the
|
|
140
|
-
// last known physical generation available for the next reconciliation attempt.
|
|
141
|
-
await writeHmrFile(outDir, globalWxssFileName, wxss);
|
|
142
|
-
}
|
|
143
|
-
publishedWxss = wxss;
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Regenerates all reachable Tailwind roots and commits the cache only when every transform succeeds.
|
|
149
|
-
*
|
|
150
|
-
* Roots run concurrently because they are independent derivations of the same candidate generation. Results remain local until
|
|
151
|
-
* Promise.all fulfills; one failed root therefore preserves every prior root together instead of publishing a mixed generation.
|
|
152
|
-
*/
|
|
153
|
-
async function refreshTailwindStyles(styleIds, processedStyles, transformRoot) {
|
|
154
|
-
const roots = styleIds.filter((styleId) => requireProcessedStyle(processedStyles, styleId).isTailwindRoot);
|
|
155
|
-
const refreshedStyles = await Promise.all(roots.map(async (rootId) => {
|
|
156
|
-
const requestId = createTailwindSidecarId(rootId);
|
|
157
|
-
const result = await transformRoot(rootId, requestId);
|
|
158
|
-
if (!result) {
|
|
159
|
-
throw new Error(`Tailwind sidecar transform produced no result: ${requestId}`);
|
|
160
|
-
}
|
|
161
|
-
return [rootId, { css: extractViteCss(result.code, requestId), isTailwindRoot: true }];
|
|
162
|
-
}));
|
|
163
|
-
refreshedStyles.forEach(([rootId, style]) => {
|
|
164
|
-
processedStyles.set(rootId, style);
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
function requireProcessedStyle(processedStyles, styleId) {
|
|
168
|
-
const style = processedStyles.get(styleId);
|
|
169
|
-
if (!style) {
|
|
170
|
-
throw new Error(`WX style plan references uncaptured CSS: ${styleId}`);
|
|
171
|
-
}
|
|
172
|
-
return style;
|
|
173
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export declare const wxStyleOptions: {
|
|
2
|
-
readonly cssCalc: false;
|
|
3
|
-
readonly autoprefixer: false;
|
|
4
|
-
readonly rem2rpx: true;
|
|
5
|
-
readonly px2rpx: true;
|
|
6
|
-
};
|
|
7
|
-
/** Shared finalization policy for complete builds and host-owned style HMR. */
|
|
8
|
-
export declare const transformWxStyle: import("@weapp-tailwindcss/postcss").StyleHandler;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { createStyleHandler } from '@weapp-tailwindcss/postcss';
|
|
2
|
-
export const wxStyleOptions = {
|
|
3
|
-
cssCalc: false,
|
|
4
|
-
autoprefixer: false,
|
|
5
|
-
rem2rpx: true,
|
|
6
|
-
px2rpx: true
|
|
7
|
-
};
|
|
8
|
-
/** Shared finalization policy for complete builds and host-owned style HMR. */
|
|
9
|
-
export const transformWxStyle = createStyleHandler(wxStyleOptions);
|