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
|
@@ -8,6 +8,8 @@ import { specializePageCapsule } from './specialize-page-capsule.js';
|
|
|
8
8
|
export function createResolver(options) {
|
|
9
9
|
const normalizedBootstrapPath = normalizeModuleId(bootstrapPath);
|
|
10
10
|
const normalizedPageCapsulePath = normalizeModuleId(pageCapsulePath);
|
|
11
|
+
// Construct output input and application traversal roots together once so style order cannot drift from route order.
|
|
12
|
+
const entryGraph = createEntryGraph(options.pages);
|
|
11
13
|
// Provide constant-time route validation and access to each configured Page JSON object.
|
|
12
14
|
const pageByPath = new Map(options.pages.map((page) => [page.path, page]));
|
|
13
15
|
const privateIdResolvers = new Map([
|
|
@@ -32,7 +34,7 @@ export function createResolver(options) {
|
|
|
32
34
|
]
|
|
33
35
|
]);
|
|
34
36
|
return {
|
|
35
|
-
|
|
37
|
+
...entryGraph,
|
|
36
38
|
resolveId(id, importer, projectRoot) {
|
|
37
39
|
// Unknown IDs fall through so Vite and other plugins retain normal resolution.
|
|
38
40
|
return privateIdResolvers.get(id)?.(importer, projectRoot);
|
|
@@ -53,22 +55,34 @@ export function createResolver(options) {
|
|
|
53
55
|
}
|
|
54
56
|
};
|
|
55
57
|
}
|
|
56
|
-
/** Declares
|
|
57
|
-
function
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
58
|
+
/** Declares output entries and the ordered application subset that can own user styles. */
|
|
59
|
+
function createEntryGraph(pages) {
|
|
60
|
+
const pageEntries = pages.map((page) => {
|
|
61
|
+
return {
|
|
62
|
+
capsuleId: createRouteModuleId({ moduleId: pageCapsulePath, pagePath: page.path }),
|
|
63
|
+
capsuleName: `${page.path}-capsule`,
|
|
64
|
+
shellId: createRouteModuleId({ moduleId: pageShellPath, pagePath: page.path }),
|
|
65
|
+
shellName: `${page.path}.js`
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
// The App owns the first global cascade layer; configured Pages follow in their declared route order.
|
|
70
|
+
applicationEntryIds: [appCapsulePath, ...pageEntries.map((entry) => entry.capsuleId)],
|
|
71
|
+
input: Object.fromEntries([
|
|
72
|
+
['bootstrap', bootstrapPath],
|
|
73
|
+
['transport', transportPath],
|
|
74
|
+
[appShellFileName, appShellPath],
|
|
75
|
+
['app-capsule', appCapsulePath],
|
|
76
|
+
[componentShellFileName, componentShellPath],
|
|
77
|
+
['component-capsule', componentCapsulePath],
|
|
78
|
+
...pageEntries.flatMap((entry) => {
|
|
79
|
+
return [
|
|
80
|
+
[entry.shellName, entry.shellId],
|
|
81
|
+
[entry.capsuleName, entry.capsuleId]
|
|
82
|
+
];
|
|
83
|
+
})
|
|
84
|
+
])
|
|
85
|
+
};
|
|
72
86
|
}
|
|
73
87
|
/** Creates one route-qualified module ID. */
|
|
74
88
|
function createRouteModuleId({ moduleId, pagePath }) {
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { WeappTailwindcss } from 'weapp-tailwindcss/vite';
|
|
2
|
+
import { transformVitePlugin } from '../../../utils/vite.js';
|
|
3
|
+
import { tailwindcssBasedir } from '../../tailwind/tailwind-css.js';
|
|
4
|
+
import { transformWxStyle, wxStyleOptions } from './transform-wx-style.js';
|
|
5
|
+
/*
|
|
6
|
+
* WX style output order:
|
|
7
|
+
*
|
|
8
|
+
* weapp-tailwindcss output hooks
|
|
9
|
+
* → vpt:wx-style-finalizer
|
|
10
|
+
* → vpt:wx native companion emission
|
|
11
|
+
*
|
|
12
|
+
* All three generateBundle hooks retain hook-level `order: 'post'` and therefore execute in registration order. The
|
|
13
|
+
* upstream plugin normally also uses plugin-level `enforce: 'post'`, which would move it behind both VPT plugins and
|
|
14
|
+
* break this sequence. `alignGenerateBundleOrder` removes only that broader phase from upstream output hooks.
|
|
15
|
+
*/
|
|
16
|
+
/** Creates the complete WX Tailwind and global-style pipeline. */
|
|
17
|
+
export function createWxStylePlugins() {
|
|
18
|
+
const tailwindPlugins = WeappTailwindcss({
|
|
19
|
+
// VPT is a custom Vite compiler.
|
|
20
|
+
// Using Taro's adapter would import Taro-specific CSS ownership rules which we don't need.
|
|
21
|
+
appType: 'weapp-vite',
|
|
22
|
+
// WX generation rewrites Tailwind's split package imports before Vite tries to resolve them in the app.
|
|
23
|
+
// Without this, strict workspaces fail on imports such as `tailwindcss/theme.css`.
|
|
24
|
+
rewriteCssImports: true,
|
|
25
|
+
platform: 'weapp',
|
|
26
|
+
tailwindcssBasedir,
|
|
27
|
+
generator: {
|
|
28
|
+
target: 'weapp'
|
|
29
|
+
},
|
|
30
|
+
cssOptions: wxStyleOptions,
|
|
31
|
+
logLevel: 'warn'
|
|
32
|
+
}) ?? [];
|
|
33
|
+
return [transformVitePlugin(tailwindPlugins, alignGenerateBundleOrder), createWxStyleFinalizer(transformWxStyle)];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Finalizes the one global stylesheet after upstream Tailwind generation.
|
|
37
|
+
*
|
|
38
|
+
* `cssCodeSplit: false` makes the compiler style global, but upstream can name it `.css` or `.wxss` depending on build
|
|
39
|
+
* mode. This hook converts its complete final contents once and renames that compiler asset to `assets/global.wxss`. An
|
|
40
|
+
* application without styles receives an empty global asset at the same stable path. Running earlier loses CSS from
|
|
41
|
+
* dynamic chunks; running after native companion emission would also see Page and native-component WXSS files that must
|
|
42
|
+
* remain opaque.
|
|
43
|
+
*/
|
|
44
|
+
function createWxStyleFinalizer(transformStyle) {
|
|
45
|
+
return {
|
|
46
|
+
name: 'vpt:wx-style-finalizer',
|
|
47
|
+
generateBundle: {
|
|
48
|
+
order: 'post',
|
|
49
|
+
async handler(_, bundle) {
|
|
50
|
+
const styles = Object.values(bundle).filter(isStyleAsset);
|
|
51
|
+
// Multiple compiler styles mean cssCodeSplit was re-enabled. Choosing one would silently lose CSS.
|
|
52
|
+
if (styles.length > 1) {
|
|
53
|
+
throw new Error('WX builds support at most one compiler-emitted stylesheet');
|
|
54
|
+
}
|
|
55
|
+
if (styles.length === 0) {
|
|
56
|
+
this.emitFile({ type: 'asset', fileName: 'assets/global.wxss', source: '' });
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const [style] = styles;
|
|
60
|
+
const source = typeof style.source === 'string' ? style.source : new TextDecoder().decode(style.source);
|
|
61
|
+
const transformedResult = await transformStyle(source);
|
|
62
|
+
// Preserve the compiler stylesheet as the real global asset so its bundle metadata and ownership remain
|
|
63
|
+
// intact. Only its finalized contents and stable WXSS identity change.
|
|
64
|
+
style.source = transformedResult.css;
|
|
65
|
+
style.fileName = 'assets/global.wxss';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Adapts upstream plugin descriptors without mutating `weapp-tailwindcss` or patching node_modules.
|
|
72
|
+
*
|
|
73
|
+
* Vite first groups whole plugins by `enforce`, then orders individual hooks. Upstream's output plugins specify both
|
|
74
|
+
* `enforce: 'post'` and `generateBundle.order: 'post'`. The plugin-level phase overrides their earlier registration and
|
|
75
|
+
* places them after VPT's normal plugins, so VPT observes incomplete CSS. Making all of VPT post-enforced would fix that
|
|
76
|
+
* one hook while unnecessarily reordering resolution and transforms.
|
|
77
|
+
*
|
|
78
|
+
* For upstream plugins that actually own generateBundle, clone the descriptor without plugin-level enforcement. Keep
|
|
79
|
+
* hook-level `order: 'post'`: it still waits for ordinary bundle generation, while registration order becomes the sole
|
|
80
|
+
* tie-breaker between upstream generation, VPT finalization and native output.
|
|
81
|
+
*/
|
|
82
|
+
function alignGenerateBundleOrder(plugin) {
|
|
83
|
+
if (plugin.enforce !== 'post' || plugin.generateBundle === undefined) {
|
|
84
|
+
return plugin;
|
|
85
|
+
}
|
|
86
|
+
// Clone rather than mutate: upstream may retain or reuse the descriptor returned by its factory.
|
|
87
|
+
return { ...plugin, enforce: undefined };
|
|
88
|
+
}
|
|
89
|
+
/** Selects only the compiler stylesheet; native WXSS assets are emitted by the later WX hook. */
|
|
90
|
+
function isStyleAsset(output) {
|
|
91
|
+
return output.type === 'asset' && /\.(?:css|wxss)$/.test(output.fileName);
|
|
92
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
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;
|
|
@@ -0,0 +1,9 @@
|
|
|
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);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates an auxiliary style request without replacing the physical Rolldown graph module.
|
|
3
|
+
*
|
|
4
|
+
* `weapp-vite-sidecar` is an upstream protocol marker, not a cache-busting nonce. `weapp-tailwindcss` detects the query
|
|
5
|
+
* key, strips the complete query when resolving the physical CSS pipeline file, and excludes this synthetic request from
|
|
6
|
+
* transformed-source candidate collection. That lets the request use the latest candidate state without feeding generated
|
|
7
|
+
* CSS back into Tailwind's source memory. The descriptive `style` value is stable; upstream treats the presence of the key
|
|
8
|
+
* as the protocol contract.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createTailwindSidecarId(rootId: string): string;
|
|
11
|
+
/** Selects imported style modules whose Vite development output owns a runtime CSS payload. */
|
|
12
|
+
export declare function isGlobalStyleRequest(id: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Creates the ordered style plan for one completed HMR transaction.
|
|
15
|
+
*
|
|
16
|
+
* `entryIds` carries semantic cascade ownership from the WX resolver: the App capsule first, followed by Page capsules in
|
|
17
|
+
* configured route order. Each entry is traversed depth-first. Static imports retain source order and precede dynamic imports;
|
|
18
|
+
* dynamic branches are included because WX cannot inject lazy CSS at browser runtime. A style follows its dependencies,
|
|
19
|
+
* matching evaluation order.
|
|
20
|
+
*
|
|
21
|
+
* Graph IDs may contain queries while `hasStyle` reads physical IDs. Normalization lets aliases share one captured style.
|
|
22
|
+
* JavaScript and non-runtime CSS requests remain traversal nodes but do not enter the plan. All mutable traversal state is
|
|
23
|
+
* transaction-local, so import additions and removals need no invalidation bookkeeping. Complexity is O(modules + edges),
|
|
24
|
+
* with O(modules + styles) temporary memory.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createGraphStylePlan(entryIds: readonly string[], getModuleInfo: (moduleId: string) => Readonly<{
|
|
27
|
+
importedIds: readonly string[];
|
|
28
|
+
dynamicallyImportedIds: readonly string[];
|
|
29
|
+
}> | null, hasStyle: (styleId: string) => boolean): readonly string[];
|
|
30
|
+
/** Renders one immutable graph plan after any Tailwind roots in that same plan have been refreshed. */
|
|
31
|
+
export declare function composeGraphStyleCss(styleIds: readonly string[], getStyleCss: (styleId: string) => string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Extracts Vite's final CSS payload from the development module without evaluating its browser HMR code.
|
|
34
|
+
*
|
|
35
|
+
* Remove this parser when either Vite exposes a supported plugin-container API that returns final CSS directly, or
|
|
36
|
+
* `weapp-tailwindcss` exposes generated root CSS after candidate updates. Until then the sidecar receives Vite's JavaScript
|
|
37
|
+
* style module, so this function isolates the version-specific `__vite__css` serialization contract.
|
|
38
|
+
*/
|
|
39
|
+
export declare function extractViteCss(moduleCode: string, rootId: string): string;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { isCSSRequest } from 'vite';
|
|
2
|
+
import { normalizeModuleId } from '../../../utils/modules.js';
|
|
3
|
+
const nonRuntimeStyleQueries = ['direct', 'inline', 'inline-css', 'raw', 'style-attr', 'transform-only', 'url'];
|
|
4
|
+
/**
|
|
5
|
+
* Creates an auxiliary style request without replacing the physical Rolldown graph module.
|
|
6
|
+
*
|
|
7
|
+
* `weapp-vite-sidecar` is an upstream protocol marker, not a cache-busting nonce. `weapp-tailwindcss` detects the query
|
|
8
|
+
* key, strips the complete query when resolving the physical CSS pipeline file, and excludes this synthetic request from
|
|
9
|
+
* transformed-source candidate collection. That lets the request use the latest candidate state without feeding generated
|
|
10
|
+
* CSS back into Tailwind's source memory. The descriptive `style` value is stable; upstream treats the presence of the key
|
|
11
|
+
* as the protocol contract.
|
|
12
|
+
*/
|
|
13
|
+
export function createTailwindSidecarId(rootId) {
|
|
14
|
+
return `${rootId}?weapp-vite-sidecar=style`;
|
|
15
|
+
}
|
|
16
|
+
/** Selects imported style modules whose Vite development output owns a runtime CSS payload. */
|
|
17
|
+
export function isGlobalStyleRequest(id) {
|
|
18
|
+
if (!isCSSRequest(id)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const queryStart = id.indexOf('?');
|
|
22
|
+
if (queryStart < 0) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
const fragmentStart = id.indexOf('#', queryStart);
|
|
26
|
+
const query = id.slice(queryStart + 1, fragmentStart < 0 ? undefined : fragmentStart);
|
|
27
|
+
const parameters = new URLSearchParams(query);
|
|
28
|
+
return (!parameters.has('weapp-vite-sidecar') && nonRuntimeStyleQueries.every((parameter) => !parameters.has(parameter)));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates the ordered style plan for one completed HMR transaction.
|
|
32
|
+
*
|
|
33
|
+
* `entryIds` carries semantic cascade ownership from the WX resolver: the App capsule first, followed by Page capsules in
|
|
34
|
+
* configured route order. Each entry is traversed depth-first. Static imports retain source order and precede dynamic imports;
|
|
35
|
+
* dynamic branches are included because WX cannot inject lazy CSS at browser runtime. A style follows its dependencies,
|
|
36
|
+
* matching evaluation order.
|
|
37
|
+
*
|
|
38
|
+
* Graph IDs may contain queries while `hasStyle` reads physical IDs. Normalization lets aliases share one captured style.
|
|
39
|
+
* JavaScript and non-runtime CSS requests remain traversal nodes but do not enter the plan. All mutable traversal state is
|
|
40
|
+
* transaction-local, so import additions and removals need no invalidation bookkeeping. Complexity is O(modules + edges),
|
|
41
|
+
* with O(modules + styles) temporary memory.
|
|
42
|
+
*/
|
|
43
|
+
export function createGraphStylePlan(entryIds, getModuleInfo, hasStyle) {
|
|
44
|
+
// These local collections collapse cycles, shared modules, and aliases without retaining derived topology between batches.
|
|
45
|
+
const visitedModuleIds = new Set();
|
|
46
|
+
const visitedStyleIds = new Set();
|
|
47
|
+
const styleIds = [];
|
|
48
|
+
const visit = (moduleId) => {
|
|
49
|
+
if (visitedModuleIds.has(moduleId)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
visitedModuleIds.add(moduleId);
|
|
53
|
+
const moduleInfo = getModuleInfo(moduleId);
|
|
54
|
+
if (!moduleInfo) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
moduleInfo.importedIds.forEach(visit);
|
|
58
|
+
moduleInfo.dynamicallyImportedIds.forEach(visit);
|
|
59
|
+
const styleId = normalizeModuleId(moduleId);
|
|
60
|
+
if (hasStyle(styleId) && !visitedStyleIds.has(styleId)) {
|
|
61
|
+
visitedStyleIds.add(styleId);
|
|
62
|
+
styleIds.push(styleId);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
entryIds.forEach(visit);
|
|
66
|
+
return styleIds;
|
|
67
|
+
}
|
|
68
|
+
/** Renders one immutable graph plan after any Tailwind roots in that same plan have been refreshed. */
|
|
69
|
+
export function composeGraphStyleCss(styleIds, getStyleCss) {
|
|
70
|
+
return styleIds.map(getStyleCss).join('\n');
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Extracts Vite's final CSS payload from the development module without evaluating its browser HMR code.
|
|
74
|
+
*
|
|
75
|
+
* Remove this parser when either Vite exposes a supported plugin-container API that returns final CSS directly, or
|
|
76
|
+
* `weapp-tailwindcss` exposes generated root CSS after candidate updates. Until then the sidecar receives Vite's JavaScript
|
|
77
|
+
* style module, so this function isolates the version-specific `__vite__css` serialization contract.
|
|
78
|
+
*/
|
|
79
|
+
export function extractViteCss(moduleCode, rootId) {
|
|
80
|
+
const assignmentPrefix = 'const __vite__css = ';
|
|
81
|
+
const assignmentStart = moduleCode.indexOf(assignmentPrefix);
|
|
82
|
+
if (assignmentStart < 0) {
|
|
83
|
+
throw new Error(`Vite CSS transform for ${rootId} did not expose __vite__css`);
|
|
84
|
+
}
|
|
85
|
+
// Vite serializes the payload with JSON.stringify on one assignment line. Parse that literal so quotes, escapes, and
|
|
86
|
+
// embedded CSS newlines are decoded by JSON rather than by a second, subtly different unescaping implementation.
|
|
87
|
+
const valueStart = assignmentStart + assignmentPrefix.length;
|
|
88
|
+
const lineEnd = moduleCode.indexOf('\n', valueStart);
|
|
89
|
+
const serializedCss = moduleCode.slice(valueStart, lineEnd < 0 ? moduleCode.length : lineEnd);
|
|
90
|
+
const css = JSON.parse(serializedCss);
|
|
91
|
+
if (typeof css !== 'string') {
|
|
92
|
+
throw new Error(`Vite CSS transform for ${rootId} exposed a non-string __vite__css value`);
|
|
93
|
+
}
|
|
94
|
+
return css;
|
|
95
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HookHandler, Plugin, PluginOption } from 'vite';
|
|
2
|
+
type TransformHook = HookHandler<NonNullable<Plugin['transform']>>;
|
|
3
|
+
export type TransformHookResult = Awaited<ReturnType<TransformHook>>;
|
|
4
|
+
export type AsyncTransformHook = (this: ThisParameterType<TransformHook>, ...args: Parameters<TransformHook>) => Promise<TransformHookResult>;
|
|
5
|
+
export type TransformHookWrapper = (transform: AsyncTransformHook) => AsyncTransformHook;
|
|
6
|
+
export type PluginMapper = (plugin: Plugin) => Plugin;
|
|
7
|
+
/** Transforms every concrete plugin while preserving nested arrays, falsy options, and promised options. */
|
|
8
|
+
export declare function transformVitePlugin(pluginOptions: PluginOption[], mapPlugin: PluginMapper): PluginOption[];
|
|
9
|
+
/**
|
|
10
|
+
* Clones a Vite transform hook with middleware that controls execution of the original handler.
|
|
11
|
+
*
|
|
12
|
+
* Function and object hook forms retain their original plugin context. Object metadata such as `order` and `filter` is copied
|
|
13
|
+
* unchanged, and the input descriptor is never mutated. The wrapper receives the normalized asynchronous transform with its
|
|
14
|
+
* complete plugin context, code, ID, and metadata signature, and returns the handler that continues Vite's plugin pipeline.
|
|
15
|
+
*/
|
|
16
|
+
export declare function wrapPluginTransform(plugin: Plugin, wrapper: TransformHookWrapper): Plugin;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Transforms every concrete plugin while preserving nested arrays, falsy options, and promised options. */
|
|
2
|
+
export function transformVitePlugin(pluginOptions, mapPlugin) {
|
|
3
|
+
return pluginOptions.map((option) => transformPluginOption(option, mapPlugin));
|
|
4
|
+
}
|
|
5
|
+
function transformPluginOption(option, mapPlugin) {
|
|
6
|
+
if (option instanceof Promise) {
|
|
7
|
+
return option.then((resolvedOption) => transformPluginOption(resolvedOption, mapPlugin));
|
|
8
|
+
}
|
|
9
|
+
if (Array.isArray(option)) {
|
|
10
|
+
return transformVitePlugin(option, mapPlugin);
|
|
11
|
+
}
|
|
12
|
+
return isPlugin(option) ? mapPlugin(option) : option;
|
|
13
|
+
}
|
|
14
|
+
function isPlugin(option) {
|
|
15
|
+
return (option !== null &&
|
|
16
|
+
option !== false &&
|
|
17
|
+
option !== undefined &&
|
|
18
|
+
typeof option === 'object' &&
|
|
19
|
+
!Array.isArray(option) &&
|
|
20
|
+
'name' in option);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Clones a Vite transform hook with middleware that controls execution of the original handler.
|
|
24
|
+
*
|
|
25
|
+
* Function and object hook forms retain their original plugin context. Object metadata such as `order` and `filter` is copied
|
|
26
|
+
* unchanged, and the input descriptor is never mutated. The wrapper receives the normalized asynchronous transform with its
|
|
27
|
+
* complete plugin context, code, ID, and metadata signature, and returns the handler that continues Vite's plugin pipeline.
|
|
28
|
+
*/
|
|
29
|
+
export function wrapPluginTransform(plugin, wrapper) {
|
|
30
|
+
const { transform } = plugin;
|
|
31
|
+
if (!transform) {
|
|
32
|
+
throw new Error(`${plugin.name} must expose a transform hook`);
|
|
33
|
+
}
|
|
34
|
+
const isTransformFunction = typeof transform === 'function';
|
|
35
|
+
const handler = isTransformFunction ? transform : transform.handler;
|
|
36
|
+
const wrappedHandler = wrapper(async function (code, id, meta) {
|
|
37
|
+
return handler.call(this, code, id, meta);
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
...plugin,
|
|
41
|
+
transform: isTransformFunction ? wrappedHandler : { ...transform, handler: wrappedHandler }
|
|
42
|
+
};
|
|
43
|
+
}
|
package/dist/node/vite-plugin.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import react from '@vitejs/plugin-react';
|
|
2
2
|
import { createClientTaroPlugin } from './plugins/client/client-taro.js';
|
|
3
3
|
import { createConditionalDirectivePlugin } from './plugins/conditional/conditional-directives.js';
|
|
4
|
-
import { createCssPlugins } from './plugins/css/plugins.js';
|
|
5
4
|
import { createH5TargetPlugins } from './plugins/h5/plugins.js';
|
|
6
5
|
import { createWxTargetPlugins } from './plugins/wx/plugins.js';
|
|
7
6
|
/** Creates the Vite plugins for one Taro target. */
|
|
@@ -9,7 +8,6 @@ export default function vitePluginTaro(options) {
|
|
|
9
8
|
return [
|
|
10
9
|
createConditionalDirectivePlugin(options.target),
|
|
11
10
|
createClientTaroPlugin(options.target),
|
|
12
|
-
...createCssPlugins(options.target),
|
|
13
11
|
...react(),
|
|
14
12
|
...(options.target === 'wx' ? createWxTargetPlugins(options) : []),
|
|
15
13
|
...(options.target === 'h5' ? createH5TargetPlugins(options) : [])
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-taro",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.13",
|
|
4
4
|
"author": "sep2",
|
|
5
5
|
"description": "Vite 8 plugin for building one React/Taro codebase for WeChat Mini Program and H5 targets.",
|
|
6
6
|
"type": "module",
|
|
@@ -75,8 +75,8 @@
|
|
|
75
75
|
"rolldown": "1.2.3",
|
|
76
76
|
"tailwindcss": "^4.3.3",
|
|
77
77
|
"weapp-tailwindcss": "^5.2.11",
|
|
78
|
-
"@tarojs/plugin-framework-react": "npm:vite-plugin-taro-plugin-framework-react@0.5.
|
|
79
|
-
"@tarojs/react": "npm:vite-plugin-taro-react@0.5.
|
|
78
|
+
"@tarojs/plugin-framework-react": "npm:vite-plugin-taro-plugin-framework-react@0.5.13",
|
|
79
|
+
"@tarojs/react": "npm:vite-plugin-taro-react@0.5.13"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
82
|
"react": "^19.0.0",
|
|
@@ -1,17 +1,38 @@
|
|
|
1
1
|
import babel, { defineRolldownBabelPreset } from '@rolldown/plugin-babel'
|
|
2
2
|
import type { HtmlTagDescriptor, Plugin, PluginOption } from 'vite'
|
|
3
|
+
import { WeappTailwindcss } from 'weapp-tailwindcss/vite'
|
|
3
4
|
import type { VitePluginTaroOptions } from '../../../options.ts'
|
|
4
5
|
import { esTarget } from '../../utils/constant.ts'
|
|
5
6
|
import { toViteFileImportPath } from '../../utils/modules.ts'
|
|
6
7
|
import { packageRequire } from '../../utils/packages.ts'
|
|
7
8
|
import { clientTaroApiId } from '../client/client-taro.ts'
|
|
9
|
+
import { tailwindcssBasedir } from '../tailwind/tailwind-css.ts'
|
|
8
10
|
import { h5AppPath } from './constant.ts'
|
|
9
11
|
import { createStencilClientAdapter } from './create-stencil-client-adapter.ts'
|
|
10
12
|
import { createModuleResolver } from './resolver/module-resolver.ts'
|
|
11
13
|
|
|
12
14
|
/** Creates the plugins that own the H5 target. */
|
|
13
15
|
export function createH5TargetPlugins(options: VitePluginTaroOptions): PluginOption[] {
|
|
14
|
-
return [
|
|
16
|
+
return [
|
|
17
|
+
WeappTailwindcss({
|
|
18
|
+
appType: 'weapp-vite',
|
|
19
|
+
rewriteCssImports: false,
|
|
20
|
+
platform: 'web',
|
|
21
|
+
tailwindcssBasedir,
|
|
22
|
+
generator: {
|
|
23
|
+
target: 'web'
|
|
24
|
+
},
|
|
25
|
+
cssOptions: {
|
|
26
|
+
cssCalc: false,
|
|
27
|
+
autoprefixer: true,
|
|
28
|
+
rem2rpx: true,
|
|
29
|
+
px2rpx: true
|
|
30
|
+
},
|
|
31
|
+
logLevel: 'warn'
|
|
32
|
+
}),
|
|
33
|
+
...createH5SupportPlugins(),
|
|
34
|
+
createH5TargetPlugin(options)
|
|
35
|
+
]
|
|
15
36
|
}
|
|
16
37
|
|
|
17
38
|
/** Configures H5 resolution and supplies the specialized physical application entry. */
|
|
@@ -1,21 +1,28 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises'
|
|
1
2
|
import type { ServerResponse } from 'node:http'
|
|
2
3
|
import path from 'node:path'
|
|
3
4
|
import colors from 'picocolors'
|
|
5
|
+
import type { GetModuleInfo } from 'rolldown'
|
|
4
6
|
import { type DevEngine, type DevOptions, dev } from 'rolldown/experimental'
|
|
5
|
-
import type
|
|
7
|
+
import { type Connect, isCSSRequest, type ViteDevServer } from 'vite'
|
|
6
8
|
import type { VitePluginTaroOptions } from '../../../../options.ts'
|
|
7
9
|
import { SerializedTaskQueue } from '../../../utils/serialized-task-queue.ts'
|
|
10
|
+
import { createGraphStylePlan, isGlobalStyleRequest } from '../styles/utils.ts'
|
|
8
11
|
import {
|
|
12
|
+
developmentAppWxssFileName,
|
|
9
13
|
type HmrInfo,
|
|
10
14
|
hmrControlPath,
|
|
11
15
|
hmrInfoFileName,
|
|
12
16
|
hmrPatchesFileName,
|
|
13
17
|
type PatchUpdate,
|
|
18
|
+
renderDevelopmentAppWxss,
|
|
14
19
|
renderHmrInfo,
|
|
15
20
|
renderInitialHmrPatches,
|
|
16
21
|
writeHmrFile
|
|
17
22
|
} from './hmr-files.ts'
|
|
18
23
|
import { PatchPublisher } from './patch-publisher.ts'
|
|
24
|
+
import { createStyleCapturePlugin, type ProcessedStyle } from './styles/create-style-capture-plugin.ts'
|
|
25
|
+
import { globalWxssFileName, publishStyleHmr, refreshTailwindStyles } from './styles/publish-style-hmr.ts'
|
|
19
26
|
import { type BundledDev, installWxDevOptions, requireSingleOutput } from './wx-dev-options.ts'
|
|
20
27
|
|
|
21
28
|
export type WxDevHost = Readonly<{
|
|
@@ -45,15 +52,41 @@ type DevOutputResult = Parameters<NonNullable<DevOptions['onOutput']>>[0]
|
|
|
45
52
|
* with dev(...)) and the patch publisher, and replaces Vite's bundledDev.listen so the
|
|
46
53
|
* engine writes directly to the Mini Program output directory instead of serving browser
|
|
47
54
|
* HMR over HTTP.
|
|
55
|
+
*
|
|
56
|
+
* `applicationEntryIds` is the resolver's immutable cascade policy, not a second graph: it selects the App capsule followed
|
|
57
|
+
* by configured Page capsules from Rolldown's larger entry set. Rolldown remains the authority for every live import edge.
|
|
48
58
|
*/
|
|
49
59
|
export async function createWxDevHost({
|
|
50
60
|
server,
|
|
51
|
-
options
|
|
61
|
+
options,
|
|
62
|
+
applicationEntryIds
|
|
52
63
|
}: {
|
|
53
64
|
server: ViteDevServer
|
|
54
65
|
options: VitePluginTaroOptions
|
|
66
|
+
applicationEntryIds: readonly string[]
|
|
55
67
|
}): Promise<WxDevHost> {
|
|
56
68
|
const bundledDev = getBundledDev(server)
|
|
69
|
+
// This is the host's mutable style projection: CSS absent from Rolldown, the live graph capability rebound by each
|
|
70
|
+
// complete build, and the last durable WXSS bytes used only to suppress identical filesystem publications. Style capture
|
|
71
|
+
// remains O(1); derived order and reachability stay local to each HMR transaction.
|
|
72
|
+
const styleState: {
|
|
73
|
+
getModuleInfo: GetModuleInfo | undefined
|
|
74
|
+
processedStyles: Map<string, ProcessedStyle>
|
|
75
|
+
publishedWxss: string | undefined
|
|
76
|
+
} = {
|
|
77
|
+
getModuleInfo: undefined,
|
|
78
|
+
processedStyles: new Map(),
|
|
79
|
+
publishedWxss: undefined
|
|
80
|
+
}
|
|
81
|
+
const styleCapturePlugin = createStyleCapturePlugin({
|
|
82
|
+
captureGraph(reader) {
|
|
83
|
+
styleState.getModuleInfo = reader
|
|
84
|
+
},
|
|
85
|
+
captureStyle(id, style) {
|
|
86
|
+
styleState.processedStyles.set(id, style)
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
|
|
57
90
|
// Rolldown invokes output callbacks without awaiting their promises. This queue is the single owner of mutable HMR host
|
|
58
91
|
// state and physical metadata writes, preventing a later patch or build identity from being overwritten by older work.
|
|
59
92
|
const hostTasks = new SerializedTaskQueue((operation, error) => logWxError(server.config.logger, operation, error))
|
|
@@ -64,7 +97,7 @@ export async function createWxDevHost({
|
|
|
64
97
|
|
|
65
98
|
// DevEngine does not reject run() after an initial plugin failure. The options layer owns a first-build buildEnd barrier
|
|
66
99
|
// and exposes only its result; later build errors continue independently through onOutput and onHmrUpdates.
|
|
67
|
-
const initialBuild = installWxDevOptions({ bundledDev, server, options })
|
|
100
|
+
const initialBuild = installWxDevOptions({ bundledDev, server, options, hostPlugins: [styleCapturePlugin] })
|
|
68
101
|
const engine: DevEngine = await createEngine()
|
|
69
102
|
|
|
70
103
|
// The wx dev host owns the only DevEngine. Vite's default listen() would create a second
|
|
@@ -198,6 +231,12 @@ export async function createWxDevHost({
|
|
|
198
231
|
return
|
|
199
232
|
}
|
|
200
233
|
|
|
234
|
+
// `onHmrUpdates` is the transaction boundary after every affected transform has updated graph and candidate state.
|
|
235
|
+
// Every non-CSS edit may alter imports or Tailwind classes; rendering broadly and comparing finalized bytes avoids
|
|
236
|
+
// source scanning while preventing unrelated JavaScript edits from notifying DevTools through an identical rename.
|
|
237
|
+
await publishChangedStyles(batch)
|
|
238
|
+
|
|
239
|
+
// Publish global.wxss before the matching JavaScript patch so DevTools observes a coherent HMR transaction.
|
|
201
240
|
// The physical file must exist before Rolldown advances: once committed, later patches may be generated relative to
|
|
202
241
|
// this batch even if DevTools has not observed its file event yet. PatchPublisher keeps the unapplied range cumulative,
|
|
203
242
|
// so any later file generation still carries every factory needed to bridge the runtime's older application frontier.
|
|
@@ -206,6 +245,37 @@ export async function createWxDevHost({
|
|
|
206
245
|
await commitPublishedBatch(batch)
|
|
207
246
|
}
|
|
208
247
|
|
|
248
|
+
/** Publishes the style projection for one completed patch transaction when its source or topology may have changed. */
|
|
249
|
+
async function publishChangedStyles(batch: readonly PatchUpdate[]): Promise<void> {
|
|
250
|
+
const changedIds = batch.flatMap((patch) => patch.changedIds)
|
|
251
|
+
const styleChanged = changedIds.some(isGlobalStyleRequest)
|
|
252
|
+
const candidatesChanged = changedIds.some((id) => !isCSSRequest(id))
|
|
253
|
+
if (!styleChanged && !candidatesChanged) {
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// buildStart installs this reader before Rolldown can produce either a complete output or an incremental batch.
|
|
258
|
+
const getModuleInfo = styleState.getModuleInfo
|
|
259
|
+
if (!getModuleInfo) {
|
|
260
|
+
throw new Error('WX style graph is unavailable before HMR publication')
|
|
261
|
+
}
|
|
262
|
+
// Traverse topology exactly once; root refresh and final rendering consume this immutable transaction plan.
|
|
263
|
+
const styleIds = createGraphStylePlan(applicationEntryIds, getModuleInfo, (styleId) =>
|
|
264
|
+
styleState.processedStyles.has(styleId)
|
|
265
|
+
)
|
|
266
|
+
if (candidatesChanged) {
|
|
267
|
+
await refreshTailwindStyles(styleIds, styleState.processedStyles, async (rootId, requestId) =>
|
|
268
|
+
server.environments.client.pluginContainer.transform(await readFile(rootId, 'utf8'), requestId)
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
styleState.publishedWxss = await publishStyleHmr({
|
|
272
|
+
styleIds: styleIds,
|
|
273
|
+
outDir: server.config.build.outDir,
|
|
274
|
+
processedStyles: styleState.processedStyles,
|
|
275
|
+
publishedWxss: styleState.publishedWxss
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
|
|
209
279
|
/**
|
|
210
280
|
* Advances Rolldown's published frontier in the same sequence order materialized in the cumulative physical file.
|
|
211
281
|
*
|
|
@@ -228,7 +298,13 @@ export async function createWxDevHost({
|
|
|
228
298
|
logWxError(server.config.logger, 'wx dev build failed', result)
|
|
229
299
|
return
|
|
230
300
|
}
|
|
231
|
-
hostTasks.enqueue('wx dev build finalization failed',
|
|
301
|
+
hostTasks.enqueue('wx dev build finalization failed', finalizeDevOutput)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** Rebinds the byte frontier after a complete build replaces the physical stylesheet outside HMR publication. */
|
|
305
|
+
async function finalizeDevOutput(): Promise<void> {
|
|
306
|
+
styleState.publishedWxss = await readFile(path.join(server.config.build.outDir, globalWxssFileName), 'utf8')
|
|
307
|
+
await rotateBuildSession()
|
|
232
308
|
}
|
|
233
309
|
}
|
|
234
310
|
|
|
@@ -245,7 +321,7 @@ function boundPort(server: ViteDevServer): number | undefined {
|
|
|
245
321
|
return address.port
|
|
246
322
|
}
|
|
247
323
|
|
|
248
|
-
/** Resets physical patches
|
|
324
|
+
/** Resets physical patches and then exposes one coherent build identity to a freshly compiled App heap. */
|
|
249
325
|
async function publishBuildMetadata(server: ViteDevServer, buildId: string, port: number): Promise<void> {
|
|
250
326
|
const info: HmrInfo = {
|
|
251
327
|
buildId,
|
|
@@ -254,6 +330,12 @@ async function publishBuildMetadata(server: ViteDevServer, buildId: string, port
|
|
|
254
330
|
|
|
255
331
|
await writeHmrFile(server.config.build.outDir, hmrPatchesFileName, renderInitialHmrPatches())
|
|
256
332
|
await writeHmrFile(server.config.build.outDir, hmrInfoFileName, renderHmrInfo(info))
|
|
333
|
+
// `removeDevelopmentAppWxss` kept the previous physical wrapper in place while the complete output was written. Replace
|
|
334
|
+
// it only now, after the empty patch frontier and matching identity are durable. DevTools treats `app.wxss` as an App root,
|
|
335
|
+
// so this write intentionally causes the one full refresh allowed at a complete-build boundary; the refreshed App reads
|
|
336
|
+
// the new info above. Incremental updates must never write this file because an App refresh could destroy the heap while
|
|
337
|
+
// its JavaScript patch is being acknowledged. They publish only the imported `assets/global.wxss` stylesheet instead.
|
|
338
|
+
await writeHmrFile(server.config.build.outDir, developmentAppWxssFileName, renderDevelopmentAppWxss(buildId))
|
|
257
339
|
}
|
|
258
340
|
|
|
259
341
|
/** Replaces browser server URLs with the physical project directory consumed by WeChat DevTools. */
|