vite-plugin-taro 0.5.12 → 0.5.13
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/h5/plugins.js +22 -1
- package/dist/node/plugins/tailwind/tailwind-css.d.ts +1 -0
- package/dist/node/plugins/tailwind/tailwind-css.js +3 -0
- package/dist/node/plugins/wx/dev/dev-host.d.ts +6 -2
- package/dist/node/plugins/wx/dev/dev-host.js +70 -5
- package/dist/node/plugins/wx/dev/hmr-files.d.ts +9 -0
- package/dist/node/plugins/wx/dev/hmr-files.js +11 -0
- package/dist/node/plugins/wx/dev/plugins.d.ts +21 -2
- package/dist/node/plugins/wx/dev/plugins.js +38 -2
- package/dist/node/plugins/wx/dev/styles/create-style-capture-plugin.d.ts +12 -0
- package/dist/node/plugins/wx/dev/styles/create-style-capture-plugin.js +21 -0
- package/dist/node/plugins/wx/dev/styles/publish-style-hmr.d.ts +27 -0
- package/dist/node/plugins/wx/dev/styles/publish-style-hmr.js +48 -0
- package/dist/node/plugins/wx/dev/wx-dev-options.d.ts +3 -2
- package/dist/node/plugins/wx/dev/wx-dev-options.js +7 -2
- package/dist/node/plugins/wx/output/files.d.ts +1 -1
- package/dist/node/plugins/wx/output/files.js +6 -1
- package/dist/node/plugins/wx/plugins.js +13 -6
- package/dist/node/plugins/wx/resolve/resolver.d.ts +2 -1
- package/dist/node/plugins/wx/resolve/resolver.js +31 -17
- package/dist/node/plugins/wx/styles/plugins.d.ts +3 -0
- package/dist/node/plugins/wx/styles/plugins.js +92 -0
- package/dist/node/plugins/wx/styles/transform-wx-style.d.ts +8 -0
- package/dist/node/plugins/wx/styles/transform-wx-style.js +9 -0
- package/dist/node/plugins/wx/styles/utils.d.ts +39 -0
- package/dist/node/plugins/wx/styles/utils.js +95 -0
- package/dist/node/utils/vite.d.ts +17 -0
- package/dist/node/utils/vite.js +43 -0
- package/dist/node/vite-plugin.js +0 -2
- package/package.json +3 -3
- package/src/node/plugins/h5/plugins.ts +22 -1
- package/src/node/plugins/tailwind/tailwind-css.ts +4 -0
- package/src/node/plugins/wx/dev/dev-host.ts +87 -5
- package/src/node/plugins/wx/dev/hmr-files.ts +12 -0
- package/src/node/plugins/wx/dev/plugins.ts +44 -4
- package/src/node/plugins/wx/dev/styles/create-style-capture-plugin.ts +34 -0
- package/src/node/plugins/wx/dev/styles/publish-style-hmr.ts +71 -0
- package/src/node/plugins/wx/dev/wx-dev-options.ts +9 -2
- package/src/node/plugins/wx/output/files.ts +6 -1
- package/src/node/plugins/wx/plugins.ts +16 -6
- package/src/node/plugins/wx/resolve/resolver.ts +33 -17
- package/src/node/plugins/wx/styles/plugins.ts +105 -0
- package/src/node/plugins/wx/styles/transform-wx-style.ts +11 -0
- package/src/node/plugins/wx/styles/utils.ts +119 -0
- package/src/node/utils/vite.ts +67 -0
- package/src/node/vite-plugin.ts +0 -2
- package/dist/node/plugins/css/plugins.d.ts +0 -4
- package/dist/node/plugins/css/plugins.js +0 -119
- package/src/node/plugins/css/plugins.ts +0 -135
|
@@ -3,6 +3,7 @@ import fs from 'node:fs/promises'
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import type { BindingClientHmrUpdate } from 'rolldown/experimental'
|
|
5
5
|
|
|
6
|
+
export const developmentAppWxssFileName = 'app.wxss'
|
|
6
7
|
export const hmrInfoFileName = 'hmr/info.js'
|
|
7
8
|
export const hmrPatchesFileName = 'hmr/patches.js'
|
|
8
9
|
|
|
@@ -28,6 +29,17 @@ export function renderInitialHmrPatches(): string {
|
|
|
28
29
|
return 'module.exports = undefined;\n'
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Renders the native App style entry for one complete development build.
|
|
34
|
+
*
|
|
35
|
+
* Imported global CSS can change while the entry remains byte-identical. DevTools may then reuse an older persistent compile
|
|
36
|
+
* cache after it restarts. Tying this inert comment to the build identity invalidates that cache once per complete build,
|
|
37
|
+
* while incremental style updates continue changing only the imported file and therefore preserve the App heap.
|
|
38
|
+
*/
|
|
39
|
+
export function renderDevelopmentAppWxss(buildId: string): string {
|
|
40
|
+
return `@import "./assets/global.wxss";\n/* vpt-build:${buildId} */\n`
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
/**
|
|
32
44
|
* Renders the cumulative patch suffix as inert CommonJS data.
|
|
33
45
|
*
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { transformWithOxc } from 'vite'
|
|
1
|
+
import { type PluginOption, transformWithOxc } from 'vite'
|
|
3
2
|
import type { VitePluginTaroOptions } from '../../../../options.ts'
|
|
4
3
|
import { esTarget } from '../../../utils/constant.ts'
|
|
5
4
|
import { memoize } from '../../../utils/memoize.ts'
|
|
6
5
|
import { normalizeModuleId } from '../../../utils/modules.ts'
|
|
7
6
|
import { appCapsulePath, pageCapsulePath, rolldownRuntimeId, taroRuntimePath } from '../module.ts'
|
|
8
7
|
import { createWxDevHost, type WxDevHost } from './dev-host.ts'
|
|
8
|
+
import { developmentAppWxssFileName } from './hmr-files.ts'
|
|
9
9
|
import { createWxReactRefreshTransforms } from './react-refresh.ts'
|
|
10
10
|
|
|
11
11
|
const taroRuntimeId = '@tarojs/runtime'
|
|
@@ -13,8 +13,15 @@ const taroRuntimeId = '@tarojs/runtime'
|
|
|
13
13
|
/**
|
|
14
14
|
* Adds the serve-only bundled-development plugin set for the wx target: the dev adapter,
|
|
15
15
|
* Page HMR activation, and React Refresh adaptation transforms.
|
|
16
|
+
*
|
|
17
|
+
* The ordered application entries cross this configuration boundary unchanged so the host can compose global CSS without
|
|
18
|
+
* reconstructing the resolver's App/Page ownership policy from unrelated Rolldown shell and transport entries.
|
|
16
19
|
*/
|
|
17
|
-
export function createWxDevelopmentPlugin(
|
|
20
|
+
export function createWxDevelopmentPlugin(
|
|
21
|
+
options: VitePluginTaroOptions,
|
|
22
|
+
applicationEntryIds: readonly string[]
|
|
23
|
+
): PluginOption[] {
|
|
24
|
+
// This mutable handle transfers ownership from configureServer to closeBundle; at most one host exists per plugin instance.
|
|
18
25
|
let host: WxDevHost | null = null
|
|
19
26
|
// Portable hook filters stay broad; these exact identities exclude similarly named user modules.
|
|
20
27
|
const normalizedAppCapsulePath = normalizeModuleId(appCapsulePath)
|
|
@@ -54,7 +61,21 @@ export function createWxDevelopmentPlugin(options: VitePluginTaroOptions): Plugi
|
|
|
54
61
|
// asks bundledDev to create its hard-coded skip-write DevEngine.
|
|
55
62
|
order: 'post',
|
|
56
63
|
async handler(server) {
|
|
57
|
-
host = await createWxDevHost({
|
|
64
|
+
host = await createWxDevHost({
|
|
65
|
+
server: server,
|
|
66
|
+
options: options,
|
|
67
|
+
applicationEntryIds: applicationEntryIds
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
generateBundle: {
|
|
73
|
+
order: 'post',
|
|
74
|
+
handler(_, bundle) {
|
|
75
|
+
// `vpt:wx` emits the production App wrapper during every complete build: initial startup and each
|
|
76
|
+
// recovery build, not just once when this plugin instance is created. Development transfers that file
|
|
77
|
+
// to the host so its only physical write happens after the matching build identity is durable.
|
|
78
|
+
removeDevelopmentAppWxss(bundle)
|
|
58
79
|
}
|
|
59
80
|
},
|
|
60
81
|
|
|
@@ -123,6 +144,25 @@ export function createWxDevelopmentPlugin(options: VitePluginTaroOptions): Plugi
|
|
|
123
144
|
]
|
|
124
145
|
}
|
|
125
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Transfers development ownership of `app.wxss` from complete output to the dev host.
|
|
149
|
+
*
|
|
150
|
+
* The ordinary WX output hook emits the static production wrapper during every complete build. In development, the existing
|
|
151
|
+
* physical wrapper instead contains the previous build ID. If the static asset remained in this bundle, the DevEngine would:
|
|
152
|
+
*
|
|
153
|
+
* 1. replace the versioned wrapper with static production bytes while JavaScript and HMR metadata are still being finalized;
|
|
154
|
+
* 2. let DevTools observe that root-style change and reload the App against the previous build identity;
|
|
155
|
+
* 3. let the host replace the wrapper again with the new build ID, causing a second App reload.
|
|
156
|
+
*
|
|
157
|
+
* Deleting only the in-memory bundle entry avoids both premature writes. Development sets `emptyOutDir: false`, so the prior
|
|
158
|
+
* physical `app.wxss` remains available while the complete output is written. The host replaces it exactly once after
|
|
159
|
+
* `hmr/patches.js` and `hmr/info.js` are durable. Incremental HMR never enters this complete-output hook and continues changing
|
|
160
|
+
* only `assets/global.wxss`, which preserves the App heap. The serve-only plugin leaves production output unchanged.
|
|
161
|
+
*/
|
|
162
|
+
export function removeDevelopmentAppWxss(bundle: Record<string, unknown>): void {
|
|
163
|
+
delete bundle[developmentAppWxssFileName]
|
|
164
|
+
}
|
|
165
|
+
|
|
126
166
|
/** Ensures the Refresh hook exists before React's renderer evaluates and injects itself. */
|
|
127
167
|
export function injectReactRefreshBootstrap(code: string): { code: string; map: null } {
|
|
128
168
|
return {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { GetModuleInfo, Plugin } from 'rolldown'
|
|
2
|
+
import { normalizeModuleId } from '../../../../utils/modules.ts'
|
|
3
|
+
import { extractViteCss, isGlobalStyleRequest } from '../../styles/utils.ts'
|
|
4
|
+
|
|
5
|
+
export type ProcessedStyle = Readonly<{
|
|
6
|
+
css: string
|
|
7
|
+
isTailwindRoot: boolean
|
|
8
|
+
}>
|
|
9
|
+
|
|
10
|
+
type StyleCaptureOptions = Readonly<{
|
|
11
|
+
captureGraph: (getModuleInfo: GetModuleInfo) => void
|
|
12
|
+
captureStyle: (id: string, style: ProcessedStyle) => void
|
|
13
|
+
}>
|
|
14
|
+
|
|
15
|
+
/** Captures Vite's final development CSS and Rolldown's live graph without running the style pipeline twice. */
|
|
16
|
+
export function createStyleCapturePlugin({ captureGraph, captureStyle }: StyleCaptureOptions): Plugin {
|
|
17
|
+
return {
|
|
18
|
+
name: 'vpt:wx-dev-style-capture',
|
|
19
|
+
buildStart() {
|
|
20
|
+
captureGraph((moduleId) => this.getModuleInfo(moduleId))
|
|
21
|
+
},
|
|
22
|
+
transform(code, id) {
|
|
23
|
+
if (!isGlobalStyleRequest(id)) {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const css = extractViteCss(code, id)
|
|
28
|
+
captureStyle(normalizeModuleId(id), {
|
|
29
|
+
css: css,
|
|
30
|
+
isTailwindRoot: css.includes('weapp-tailwindcss vite-generated-css:')
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { transformWxStyle } from '../../styles/transform-wx-style.ts'
|
|
2
|
+
import { composeGraphStyleCss, createTailwindSidecarId, extractViteCss } from '../../styles/utils.ts'
|
|
3
|
+
import { writeHmrFile } from '../hmr-files.ts'
|
|
4
|
+
import type { ProcessedStyle } from './create-style-capture-plugin.ts'
|
|
5
|
+
|
|
6
|
+
export const globalWxssFileName = 'assets/global.wxss'
|
|
7
|
+
|
|
8
|
+
type StyleHmrOptions = Readonly<{
|
|
9
|
+
styleIds: readonly string[]
|
|
10
|
+
outDir: string
|
|
11
|
+
processedStyles: ReadonlyMap<string, ProcessedStyle>
|
|
12
|
+
publishedWxss: string | undefined
|
|
13
|
+
}>
|
|
14
|
+
|
|
15
|
+
/** Regenerates reachable Tailwind roots concurrently from upstream's current candidate state. */
|
|
16
|
+
export async function refreshTailwindStyles(
|
|
17
|
+
styleIds: readonly string[],
|
|
18
|
+
processedStyles: Map<string, ProcessedStyle>,
|
|
19
|
+
transformRoot: (rootId: string, requestId: string) => Promise<Readonly<{ code: string }> | null>
|
|
20
|
+
): Promise<void> {
|
|
21
|
+
const roots = styleIds.filter((styleId) => requireProcessedStyle(processedStyles, styleId).isTailwindRoot)
|
|
22
|
+
const refreshedStyles = await Promise.all(
|
|
23
|
+
roots.map(async (rootId) => {
|
|
24
|
+
const requestId = createTailwindSidecarId(rootId)
|
|
25
|
+
const result = await transformRoot(rootId, requestId)
|
|
26
|
+
if (!result) {
|
|
27
|
+
throw new Error(`Tailwind sidecar transform produced no result: ${requestId}`)
|
|
28
|
+
}
|
|
29
|
+
return [rootId, { css: extractViteCss(result.code, requestId), isTailwindRoot: true }] as const
|
|
30
|
+
})
|
|
31
|
+
)
|
|
32
|
+
// Commit the cache generation only after every independent root succeeds; a failed sibling cannot leave a mixed snapshot.
|
|
33
|
+
refreshedStyles.forEach(([rootId, style]) => {
|
|
34
|
+
processedStyles.set(rootId, style)
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Materializes one host-owned style snapshot for a completed HMR transaction.
|
|
40
|
+
*
|
|
41
|
+
* Rolldown owns topology but does not retain Vite's final CSS module payload, while `processedStyles` owns those payloads but
|
|
42
|
+
* deliberately retains no topology. The caller supplies the transaction's one ordered graph plan; both Tailwind refresh and
|
|
43
|
+
* this renderer consume that same snapshot, so sidecar generation cannot cause a second topology traversal.
|
|
44
|
+
*
|
|
45
|
+
* Ordinary CSS is never transformed again here: capture already observed it after Vite/PostCSS. The composed stylesheet alone
|
|
46
|
+
* receives the same final WX conversion as complete builds. Byte equality with the host's physical publication frontier skips
|
|
47
|
+
* an atomic rename that would otherwise notify DevTools despite no style change. The returned bytes advance that frontier only
|
|
48
|
+
* after any required write succeeds. The caller awaits this before exposing the corresponding JavaScript patch.
|
|
49
|
+
* Complexity is O(styles + CSS bytes), after the caller's O(V + E) plan traversal.
|
|
50
|
+
*/
|
|
51
|
+
export async function publishStyleHmr({
|
|
52
|
+
styleIds,
|
|
53
|
+
outDir,
|
|
54
|
+
processedStyles,
|
|
55
|
+
publishedWxss
|
|
56
|
+
}: StyleHmrOptions): Promise<string> {
|
|
57
|
+
const css = composeGraphStyleCss(styleIds, (styleId) => requireProcessedStyle(processedStyles, styleId).css)
|
|
58
|
+
const wxss = (await transformWxStyle(css)).css
|
|
59
|
+
if (wxss !== publishedWxss) {
|
|
60
|
+
await writeHmrFile(outDir, globalWxssFileName, wxss)
|
|
61
|
+
}
|
|
62
|
+
return wxss
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function requireProcessedStyle(processedStyles: ReadonlyMap<string, ProcessedStyle>, styleId: string): ProcessedStyle {
|
|
66
|
+
const style = processedStyles.get(styleId)
|
|
67
|
+
if (!style) {
|
|
68
|
+
throw new Error(`WX style plan references uncaptured CSS: ${styleId}`)
|
|
69
|
+
}
|
|
70
|
+
return style
|
|
71
|
+
}
|
|
@@ -28,11 +28,13 @@ export type BundledDev = {
|
|
|
28
28
|
export function installWxDevOptions({
|
|
29
29
|
bundledDev,
|
|
30
30
|
server,
|
|
31
|
-
options
|
|
31
|
+
options,
|
|
32
|
+
hostPlugins
|
|
32
33
|
}: {
|
|
33
34
|
bundledDev: BundledDev
|
|
34
35
|
server: ViteDevServer
|
|
35
36
|
options: VitePluginTaroOptions
|
|
37
|
+
hostPlugins: Plugin[]
|
|
36
38
|
}): Promise<void> {
|
|
37
39
|
// The buildEnd hook and its one-shot result belong to the same abstraction. Consumers can await startup without receiving
|
|
38
40
|
// a resolver capable of settling it externally.
|
|
@@ -83,7 +85,12 @@ export function installWxDevOptions({
|
|
|
83
85
|
|
|
84
86
|
// Preserve the physical paths watched by WeChat DevTools across host restarts; the complete build overwrites every
|
|
85
87
|
// active output without disrupting the hot-reload watcher.
|
|
86
|
-
rolldownOptions.plugins = [
|
|
88
|
+
rolldownOptions.plugins = [
|
|
89
|
+
rolldownOptions.plugins,
|
|
90
|
+
hostPlugins,
|
|
91
|
+
reportInitialBuildPlugin,
|
|
92
|
+
createViteReporter(server)
|
|
93
|
+
]
|
|
87
94
|
disableViteOxcSourcemap(rolldownOptions.plugins)
|
|
88
95
|
|
|
89
96
|
return rolldownOptions
|
|
@@ -5,7 +5,7 @@ import type { GeneratedSubpackage } from '../placement/placer.ts'
|
|
|
5
5
|
import { createJsonAssets } from './json.ts'
|
|
6
6
|
import { createTemplateAssets } from './templates.ts'
|
|
7
7
|
|
|
8
|
-
/** Creates every
|
|
8
|
+
/** Creates every compiler-owned WX file derived from the final Rolldown bundle. */
|
|
9
9
|
export async function createOutputFiles({
|
|
10
10
|
bundle,
|
|
11
11
|
options,
|
|
@@ -20,6 +20,11 @@ export async function createOutputFiles({
|
|
|
20
20
|
const nativeOutput = await createNativeComponentOutput({ bundle, getModuleInfo })
|
|
21
21
|
|
|
22
22
|
return [
|
|
23
|
+
{
|
|
24
|
+
type: 'asset',
|
|
25
|
+
fileName: 'app.wxss',
|
|
26
|
+
source: '@import "./assets/global.wxss";\n'
|
|
27
|
+
},
|
|
23
28
|
...createJsonAssets({ options, subpackages, nativeComponents: nativeOutput.registrations }),
|
|
24
29
|
...createTemplateAssets(bundle, options, nativeOutput.registrations),
|
|
25
30
|
...nativeOutput.files
|
|
@@ -13,15 +13,25 @@ import { renderCapsule } from './render/capsule.ts'
|
|
|
13
13
|
import { renderNative } from './render/native.ts'
|
|
14
14
|
import { materializeTransport } from './render/transport.ts'
|
|
15
15
|
import { createResolver } from './resolve/resolver.ts'
|
|
16
|
+
import { createWxStylePlugins } from './styles/plugins.ts'
|
|
17
|
+
|
|
18
|
+
type WxResolver = ReturnType<typeof createResolver>
|
|
16
19
|
|
|
17
20
|
/** Creates the complete plugin set for the wx target. */
|
|
18
21
|
export function createWxTargetPlugins(options: VitePluginTaroOptions): PluginOption[] {
|
|
19
|
-
|
|
22
|
+
const resolver = createResolver(options)
|
|
23
|
+
|
|
24
|
+
// Reuse the resolver instance's ordered application subset. Rolldown's complete input also contains bootstrap, transport,
|
|
25
|
+
// shell, and component entries; entry membership alone cannot recover which roots define the App/Page CSS cascade.
|
|
26
|
+
return [
|
|
27
|
+
createWxStylePlugins(),
|
|
28
|
+
createWxPlugin(options, resolver),
|
|
29
|
+
createWxDevelopmentPlugin(options, resolver.applicationEntryIds)
|
|
30
|
+
]
|
|
20
31
|
}
|
|
21
32
|
|
|
22
33
|
/** Configures the complete wx target build pipeline. */
|
|
23
|
-
function createWxPlugin(options: VitePluginTaroOptions): Plugin {
|
|
24
|
-
const resolver = createResolver(options)
|
|
34
|
+
function createWxPlugin(options: VitePluginTaroOptions, resolver: WxResolver): Plugin {
|
|
25
35
|
const placer = createPlacer()
|
|
26
36
|
|
|
27
37
|
return {
|
|
@@ -127,10 +137,10 @@ function createWxPlugin(options: VitePluginTaroOptions): Plugin {
|
|
|
127
137
|
|
|
128
138
|
generateBundle: {
|
|
129
139
|
/*
|
|
130
|
-
* This hook is registered after
|
|
140
|
+
* This hook is registered after createWxStylePlugins() and shares hook-level `order: 'post'` with the adapted
|
|
131
141
|
* upstream hooks and VPT style finalizer. Registration order therefore guarantees that the imported global
|
|
132
|
-
* stylesheet is complete before native Page/component companions are emitted. Without this order, the finalizer
|
|
133
|
-
* incomplete Tailwind output or mistake native WXSS companions for additional compiler styles.
|
|
142
|
+
* stylesheet is complete before native Page/component companions are emitted. Without this order, the finalizer
|
|
143
|
+
* could consume incomplete Tailwind output or mistake native WXSS companions for additional compiler styles.
|
|
134
144
|
*/
|
|
135
145
|
order: 'post',
|
|
136
146
|
async handler(_, bundle) {
|
|
@@ -28,6 +28,9 @@ export function createResolver(options: VitePluginTaroOptions) {
|
|
|
28
28
|
const normalizedBootstrapPath = normalizeModuleId(bootstrapPath)
|
|
29
29
|
const normalizedPageCapsulePath = normalizeModuleId(pageCapsulePath)
|
|
30
30
|
|
|
31
|
+
// Construct output input and application traversal roots together once so style order cannot drift from route order.
|
|
32
|
+
const entryGraph = createEntryGraph(options.pages)
|
|
33
|
+
|
|
31
34
|
// Provide constant-time route validation and access to each configured Page JSON object.
|
|
32
35
|
const pageByPath = new Map(options.pages.map((page) => [page.path, page]))
|
|
33
36
|
|
|
@@ -56,7 +59,7 @@ export function createResolver(options: VitePluginTaroOptions) {
|
|
|
56
59
|
])
|
|
57
60
|
|
|
58
61
|
return {
|
|
59
|
-
|
|
62
|
+
...entryGraph,
|
|
60
63
|
|
|
61
64
|
resolveId(id: string, importer: string | undefined, projectRoot: string): string | undefined {
|
|
62
65
|
// Unknown IDs fall through so Vite and other plugins retain normal resolution.
|
|
@@ -82,22 +85,35 @@ export function createResolver(options: VitePluginTaroOptions) {
|
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
87
|
|
|
85
|
-
/** Declares
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
88
|
+
/** Declares output entries and the ordered application subset that can own user styles. */
|
|
89
|
+
function createEntryGraph(pages: readonly VitePluginTaroPageOption[]) {
|
|
90
|
+
const pageEntries = pages.map((page) => {
|
|
91
|
+
return {
|
|
92
|
+
capsuleId: createRouteModuleId({ moduleId: pageCapsulePath, pagePath: page.path }),
|
|
93
|
+
capsuleName: `${page.path}-capsule`,
|
|
94
|
+
shellId: createRouteModuleId({ moduleId: pageShellPath, pagePath: page.path }),
|
|
95
|
+
shellName: `${page.path}.js`
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
// The App owns the first global cascade layer; configured Pages follow in their declared route order.
|
|
101
|
+
applicationEntryIds: [appCapsulePath, ...pageEntries.map((entry) => entry.capsuleId)],
|
|
102
|
+
input: Object.fromEntries([
|
|
103
|
+
['bootstrap', bootstrapPath],
|
|
104
|
+
['transport', transportPath],
|
|
105
|
+
[appShellFileName, appShellPath],
|
|
106
|
+
['app-capsule', appCapsulePath],
|
|
107
|
+
[componentShellFileName, componentShellPath],
|
|
108
|
+
['component-capsule', componentCapsulePath],
|
|
109
|
+
...pageEntries.flatMap((entry) => {
|
|
110
|
+
return [
|
|
111
|
+
[entry.shellName, entry.shellId],
|
|
112
|
+
[entry.capsuleName, entry.capsuleId]
|
|
113
|
+
]
|
|
114
|
+
})
|
|
115
|
+
])
|
|
116
|
+
}
|
|
101
117
|
}
|
|
102
118
|
|
|
103
119
|
/** Creates one route-qualified module ID. */
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { Plugin, PluginOption, Rolldown } from 'vite'
|
|
2
|
+
import { WeappTailwindcss } from 'weapp-tailwindcss/vite'
|
|
3
|
+
import { transformVitePlugin } from '../../../utils/vite.ts'
|
|
4
|
+
import { tailwindcssBasedir } from '../../tailwind/tailwind-css.ts'
|
|
5
|
+
import { transformWxStyle, wxStyleOptions } from './transform-wx-style.ts'
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* WX style output order:
|
|
9
|
+
*
|
|
10
|
+
* weapp-tailwindcss output hooks
|
|
11
|
+
* → vpt:wx-style-finalizer
|
|
12
|
+
* → vpt:wx native companion emission
|
|
13
|
+
*
|
|
14
|
+
* All three generateBundle hooks retain hook-level `order: 'post'` and therefore execute in registration order. The
|
|
15
|
+
* upstream plugin normally also uses plugin-level `enforce: 'post'`, which would move it behind both VPT plugins and
|
|
16
|
+
* break this sequence. `alignGenerateBundleOrder` removes only that broader phase from upstream output hooks.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** Creates the complete WX Tailwind and global-style pipeline. */
|
|
20
|
+
export function createWxStylePlugins(): PluginOption[] {
|
|
21
|
+
const tailwindPlugins =
|
|
22
|
+
WeappTailwindcss({
|
|
23
|
+
// VPT is a custom Vite compiler.
|
|
24
|
+
// Using Taro's adapter would import Taro-specific CSS ownership rules which we don't need.
|
|
25
|
+
appType: 'weapp-vite',
|
|
26
|
+
// WX generation rewrites Tailwind's split package imports before Vite tries to resolve them in the app.
|
|
27
|
+
// Without this, strict workspaces fail on imports such as `tailwindcss/theme.css`.
|
|
28
|
+
rewriteCssImports: true,
|
|
29
|
+
platform: 'weapp',
|
|
30
|
+
tailwindcssBasedir,
|
|
31
|
+
generator: {
|
|
32
|
+
target: 'weapp'
|
|
33
|
+
},
|
|
34
|
+
cssOptions: wxStyleOptions,
|
|
35
|
+
logLevel: 'warn'
|
|
36
|
+
}) ?? []
|
|
37
|
+
|
|
38
|
+
return [transformVitePlugin(tailwindPlugins, alignGenerateBundleOrder), createWxStyleFinalizer(transformWxStyle)]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Finalizes the one global stylesheet after upstream Tailwind generation.
|
|
43
|
+
*
|
|
44
|
+
* `cssCodeSplit: false` makes the compiler style global, but upstream can name it `.css` or `.wxss` depending on build
|
|
45
|
+
* mode. This hook converts its complete final contents once and renames that compiler asset to `assets/global.wxss`. An
|
|
46
|
+
* application without styles receives an empty global asset at the same stable path. Running earlier loses CSS from
|
|
47
|
+
* dynamic chunks; running after native companion emission would also see Page and native-component WXSS files that must
|
|
48
|
+
* remain opaque.
|
|
49
|
+
*/
|
|
50
|
+
function createWxStyleFinalizer(transformStyle: typeof transformWxStyle): Plugin {
|
|
51
|
+
return {
|
|
52
|
+
name: 'vpt:wx-style-finalizer',
|
|
53
|
+
generateBundle: {
|
|
54
|
+
order: 'post',
|
|
55
|
+
async handler(_, bundle) {
|
|
56
|
+
const styles = Object.values(bundle).filter(isStyleAsset)
|
|
57
|
+
|
|
58
|
+
// Multiple compiler styles mean cssCodeSplit was re-enabled. Choosing one would silently lose CSS.
|
|
59
|
+
if (styles.length > 1) {
|
|
60
|
+
throw new Error('WX builds support at most one compiler-emitted stylesheet')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (styles.length === 0) {
|
|
64
|
+
this.emitFile({ type: 'asset', fileName: 'assets/global.wxss', source: '' })
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const [style] = styles
|
|
69
|
+
const source = typeof style.source === 'string' ? style.source : new TextDecoder().decode(style.source)
|
|
70
|
+
const transformedResult = await transformStyle(source)
|
|
71
|
+
|
|
72
|
+
// Preserve the compiler stylesheet as the real global asset so its bundle metadata and ownership remain
|
|
73
|
+
// intact. Only its finalized contents and stable WXSS identity change.
|
|
74
|
+
style.source = transformedResult.css
|
|
75
|
+
style.fileName = 'assets/global.wxss'
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Adapts upstream plugin descriptors without mutating `weapp-tailwindcss` or patching node_modules.
|
|
83
|
+
*
|
|
84
|
+
* Vite first groups whole plugins by `enforce`, then orders individual hooks. Upstream's output plugins specify both
|
|
85
|
+
* `enforce: 'post'` and `generateBundle.order: 'post'`. The plugin-level phase overrides their earlier registration and
|
|
86
|
+
* places them after VPT's normal plugins, so VPT observes incomplete CSS. Making all of VPT post-enforced would fix that
|
|
87
|
+
* one hook while unnecessarily reordering resolution and transforms.
|
|
88
|
+
*
|
|
89
|
+
* For upstream plugins that actually own generateBundle, clone the descriptor without plugin-level enforcement. Keep
|
|
90
|
+
* hook-level `order: 'post'`: it still waits for ordinary bundle generation, while registration order becomes the sole
|
|
91
|
+
* tie-breaker between upstream generation, VPT finalization and native output.
|
|
92
|
+
*/
|
|
93
|
+
function alignGenerateBundleOrder(plugin: Plugin): Plugin {
|
|
94
|
+
if (plugin.enforce !== 'post' || plugin.generateBundle === undefined) {
|
|
95
|
+
return plugin
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Clone rather than mutate: upstream may retain or reuse the descriptor returned by its factory.
|
|
99
|
+
return { ...plugin, enforce: undefined }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Selects only the compiler stylesheet; native WXSS assets are emitted by the later WX hook. */
|
|
103
|
+
function isStyleAsset(output: Rolldown.OutputBundle[string]): output is Rolldown.OutputAsset {
|
|
104
|
+
return output.type === 'asset' && /\.(?:css|wxss)$/.test(output.fileName)
|
|
105
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createStyleHandler } from '@weapp-tailwindcss/postcss'
|
|
2
|
+
|
|
3
|
+
export const wxStyleOptions = {
|
|
4
|
+
cssCalc: false,
|
|
5
|
+
autoprefixer: false,
|
|
6
|
+
rem2rpx: true,
|
|
7
|
+
px2rpx: true
|
|
8
|
+
} as const
|
|
9
|
+
|
|
10
|
+
/** Shared finalization policy for complete builds and host-owned style HMR. */
|
|
11
|
+
export const transformWxStyle = createStyleHandler(wxStyleOptions)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { isCSSRequest } from 'vite'
|
|
2
|
+
import { normalizeModuleId } from '../../../utils/modules.ts'
|
|
3
|
+
|
|
4
|
+
const nonRuntimeStyleQueries = ['direct', 'inline', 'inline-css', 'raw', 'style-attr', 'transform-only', 'url'] as const
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates an auxiliary style request without replacing the physical Rolldown graph module.
|
|
8
|
+
*
|
|
9
|
+
* `weapp-vite-sidecar` is an upstream protocol marker, not a cache-busting nonce. `weapp-tailwindcss` detects the query
|
|
10
|
+
* key, strips the complete query when resolving the physical CSS pipeline file, and excludes this synthetic request from
|
|
11
|
+
* transformed-source candidate collection. That lets the request use the latest candidate state without feeding generated
|
|
12
|
+
* CSS back into Tailwind's source memory. The descriptive `style` value is stable; upstream treats the presence of the key
|
|
13
|
+
* as the protocol contract.
|
|
14
|
+
*/
|
|
15
|
+
export function createTailwindSidecarId(rootId: string): string {
|
|
16
|
+
return `${rootId}?weapp-vite-sidecar=style`
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Selects imported style modules whose Vite development output owns a runtime CSS payload. */
|
|
20
|
+
export function isGlobalStyleRequest(id: string): boolean {
|
|
21
|
+
if (!isCSSRequest(id)) {
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const queryStart = id.indexOf('?')
|
|
26
|
+
if (queryStart < 0) {
|
|
27
|
+
return true
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const fragmentStart = id.indexOf('#', queryStart)
|
|
31
|
+
const query = id.slice(queryStart + 1, fragmentStart < 0 ? undefined : fragmentStart)
|
|
32
|
+
const parameters = new URLSearchParams(query)
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
!parameters.has('weapp-vite-sidecar') && nonRuntimeStyleQueries.every((parameter) => !parameters.has(parameter))
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Creates the ordered style plan for one completed HMR transaction.
|
|
41
|
+
*
|
|
42
|
+
* `entryIds` carries semantic cascade ownership from the WX resolver: the App capsule first, followed by Page capsules in
|
|
43
|
+
* configured route order. Each entry is traversed depth-first. Static imports retain source order and precede dynamic imports;
|
|
44
|
+
* dynamic branches are included because WX cannot inject lazy CSS at browser runtime. A style follows its dependencies,
|
|
45
|
+
* matching evaluation order.
|
|
46
|
+
*
|
|
47
|
+
* Graph IDs may contain queries while `hasStyle` reads physical IDs. Normalization lets aliases share one captured style.
|
|
48
|
+
* JavaScript and non-runtime CSS requests remain traversal nodes but do not enter the plan. All mutable traversal state is
|
|
49
|
+
* transaction-local, so import additions and removals need no invalidation bookkeeping. Complexity is O(modules + edges),
|
|
50
|
+
* with O(modules + styles) temporary memory.
|
|
51
|
+
*/
|
|
52
|
+
export function createGraphStylePlan(
|
|
53
|
+
entryIds: readonly string[],
|
|
54
|
+
getModuleInfo: (
|
|
55
|
+
moduleId: string
|
|
56
|
+
) => Readonly<{ importedIds: readonly string[]; dynamicallyImportedIds: readonly string[] }> | null,
|
|
57
|
+
hasStyle: (styleId: string) => boolean
|
|
58
|
+
): readonly string[] {
|
|
59
|
+
// These local collections collapse cycles, shared modules, and aliases without retaining derived topology between batches.
|
|
60
|
+
const visitedModuleIds = new Set<string>()
|
|
61
|
+
const visitedStyleIds = new Set<string>()
|
|
62
|
+
const styleIds: string[] = []
|
|
63
|
+
|
|
64
|
+
const visit = (moduleId: string): void => {
|
|
65
|
+
if (visitedModuleIds.has(moduleId)) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
visitedModuleIds.add(moduleId)
|
|
69
|
+
|
|
70
|
+
const moduleInfo = getModuleInfo(moduleId)
|
|
71
|
+
if (!moduleInfo) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
moduleInfo.importedIds.forEach(visit)
|
|
76
|
+
moduleInfo.dynamicallyImportedIds.forEach(visit)
|
|
77
|
+
|
|
78
|
+
const styleId = normalizeModuleId(moduleId)
|
|
79
|
+
if (hasStyle(styleId) && !visitedStyleIds.has(styleId)) {
|
|
80
|
+
visitedStyleIds.add(styleId)
|
|
81
|
+
styleIds.push(styleId)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
entryIds.forEach(visit)
|
|
86
|
+
return styleIds
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Renders one immutable graph plan after any Tailwind roots in that same plan have been refreshed. */
|
|
90
|
+
export function composeGraphStyleCss(styleIds: readonly string[], getStyleCss: (styleId: string) => string): string {
|
|
91
|
+
return styleIds.map(getStyleCss).join('\n')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Extracts Vite's final CSS payload from the development module without evaluating its browser HMR code.
|
|
96
|
+
*
|
|
97
|
+
* Remove this parser when either Vite exposes a supported plugin-container API that returns final CSS directly, or
|
|
98
|
+
* `weapp-tailwindcss` exposes generated root CSS after candidate updates. Until then the sidecar receives Vite's JavaScript
|
|
99
|
+
* style module, so this function isolates the version-specific `__vite__css` serialization contract.
|
|
100
|
+
*/
|
|
101
|
+
export function extractViteCss(moduleCode: string, rootId: string): string {
|
|
102
|
+
const assignmentPrefix = 'const __vite__css = '
|
|
103
|
+
const assignmentStart = moduleCode.indexOf(assignmentPrefix)
|
|
104
|
+
if (assignmentStart < 0) {
|
|
105
|
+
throw new Error(`Vite CSS transform for ${rootId} did not expose __vite__css`)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Vite serializes the payload with JSON.stringify on one assignment line. Parse that literal so quotes, escapes, and
|
|
109
|
+
// embedded CSS newlines are decoded by JSON rather than by a second, subtly different unescaping implementation.
|
|
110
|
+
const valueStart = assignmentStart + assignmentPrefix.length
|
|
111
|
+
const lineEnd = moduleCode.indexOf('\n', valueStart)
|
|
112
|
+
const serializedCss = moduleCode.slice(valueStart, lineEnd < 0 ? moduleCode.length : lineEnd)
|
|
113
|
+
const css: unknown = JSON.parse(serializedCss)
|
|
114
|
+
if (typeof css !== 'string') {
|
|
115
|
+
throw new Error(`Vite CSS transform for ${rootId} exposed a non-string __vite__css value`)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return css
|
|
119
|
+
}
|