@weapp-vite/tailwindcss 0.2.0 → 1.0.1
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.md +1 -1
- package/dist/index.d.mts +8 -2
- package/dist/index.mjs +173 -10
- package/package.json +22 -6
package/README.md
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -22,6 +22,10 @@ export declare function createTailwindController(options?: TailwindControllerOpt
|
|
|
22
22
|
/** 同一份 core 快照用于所有转换;宿主提供最终资产,公共层不推断框架输出归属。 */
|
|
23
23
|
export declare function createTailwindPreparation(compiler: Compiler$1, snapshot: CompilerSnapshot$1, assets?: NonNullable<HmrCompilerPreparation['assets']>): HmrCompilerPreparation;
|
|
24
24
|
//#endregion
|
|
25
|
+
//#region src/generationValue.d.ts
|
|
26
|
+
/** 只复制可比较的生成配置;保留函数身份、属性描述与原型,拒绝不可安全封存的输入。 */
|
|
27
|
+
export declare function copyTailwindGenerationValue<T>(value: T, parents?: Set<object>): T;
|
|
28
|
+
//#endregion
|
|
25
29
|
//#region src/output.d.ts
|
|
26
30
|
export interface TailwindOutputInput {
|
|
27
31
|
id: string;
|
|
@@ -45,14 +49,16 @@ export interface TailwindRootState {
|
|
|
45
49
|
result: CompilerGenerateResult$1;
|
|
46
50
|
rawSources: ReadonlyMap<string, string | null | undefined>;
|
|
47
51
|
}
|
|
48
|
-
export interface TailwindRootHost {
|
|
52
|
+
export interface TailwindRootHost<K = unknown> {
|
|
49
53
|
cwd: string;
|
|
50
54
|
sourceId: (file: string) => string;
|
|
51
55
|
isOutput?: (file: string) => boolean;
|
|
52
56
|
reusePreprocessedCss?: (file: string, previous: string | null | undefined, current: string | null | undefined) => boolean;
|
|
57
|
+
/** 捕获实际内存请求的独立只读副本;生成和缓存命中均调用,失败保留原始错误。 */
|
|
58
|
+
captureRequest?: (key: K, request: CompilerGenerateRequest$1) => void | Promise<void>;
|
|
53
59
|
}
|
|
54
60
|
/** 使用封存源码驱动上游完整候选生成;框架预处理与输出排除由宿主提供。 */
|
|
55
|
-
export declare function prepareTailwindRoots<K>(compiler: Compiler$1, request: HmrCompilerRequest, roots: ReadonlyMap<K, TailwindRootState>, options: TailwindRootHost): Promise<Map<K, CompilerGenerateResult$1>>;
|
|
61
|
+
export declare function prepareTailwindRoots<K>(compiler: Compiler$1, request: HmrCompilerRequest, roots: ReadonlyMap<K, TailwindRootState>, options: TailwindRootHost<K>): Promise<Map<K, CompilerGenerateResult$1>>;
|
|
56
62
|
//#endregion
|
|
57
63
|
//#region src/sources.d.ts
|
|
58
64
|
/** 返回上游实际扫描边界中的文件,宿主将它们登记到现有 watcher。 */
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { CompilerHmrResyncError, normalizeHmrSourceMap } from "@weapp-vite/hmr";
|
|
2
|
+
import { types } from "node:util";
|
|
2
3
|
import { createHash } from "node:crypto";
|
|
3
4
|
import { readFileSync, statSync } from "node:fs";
|
|
4
|
-
import { createTailwindV4CompiledSourceEntries, createTailwindV4SourceEntryMatcher, resolveProjectSourceFiles } from "@weapp-tailwindcss/engine";
|
|
5
5
|
import path from "pathe";
|
|
6
|
+
import path$1 from "node:path";
|
|
7
|
+
import { resolveSourceScanPathWithApi, sourcePathApi, toPosixPath } from "@weapp-tailwindcss/source-scan";
|
|
8
|
+
import micromatch from "micromatch";
|
|
6
9
|
//#region src/controller.ts
|
|
7
10
|
/** 编译器按宿主生命周期惰性创建;不创建 Vite、DevEngine、watcher 或输出写入者。 */
|
|
8
11
|
var TailwindController = class {
|
|
@@ -63,6 +66,34 @@ function createTailwindPreparation(compiler, snapshot, assets = []) {
|
|
|
63
66
|
};
|
|
64
67
|
}
|
|
65
68
|
//#endregion
|
|
69
|
+
//#region src/generationValue.ts
|
|
70
|
+
/** 只复制可比较的生成配置;保留函数身份、属性描述与原型,拒绝不可安全封存的输入。 */
|
|
71
|
+
function copyTailwindGenerationValue(value, parents = /* @__PURE__ */ new Set()) {
|
|
72
|
+
if (value === null || typeof value !== "object") return value;
|
|
73
|
+
if (types.isProxy(value)) throw new TypeError("Tailwind generation configuration must not contain proxies");
|
|
74
|
+
const array = Array.isArray(value);
|
|
75
|
+
if (parents.has(value) || (array ? Object.getPrototypeOf(value) !== Array.prototype : Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null)) throw new TypeError("Unsupported Tailwind generation configuration");
|
|
76
|
+
parents.add(value);
|
|
77
|
+
try {
|
|
78
|
+
const keys = Reflect.ownKeys(value);
|
|
79
|
+
if (array && keys.some((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9]\d*)$/.test(key) || Number(key) >= value.length))) throw new TypeError("Tailwind generation configuration must not contain custom array properties");
|
|
80
|
+
const entries = keys.filter((key) => !array || key !== "length").map((key) => {
|
|
81
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
82
|
+
if (!("value" in descriptor)) throw new TypeError("Tailwind generation configuration must not contain accessors");
|
|
83
|
+
return [key, copyTailwindGenerationValue(descriptor.value, parents)];
|
|
84
|
+
});
|
|
85
|
+
const copy = array ? [] : Object.create(Object.getPrototypeOf(value));
|
|
86
|
+
if (array) copy.length = value.length;
|
|
87
|
+
for (const [key, item] of entries) Object.defineProperty(copy, key, {
|
|
88
|
+
value: item,
|
|
89
|
+
enumerable: Object.getOwnPropertyDescriptor(value, key).enumerable
|
|
90
|
+
});
|
|
91
|
+
return Object.freeze(copy);
|
|
92
|
+
} finally {
|
|
93
|
+
parents.delete(value);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
66
97
|
//#region src/output.ts
|
|
67
98
|
/** 在宿主回调内封存投影,再异步转换;Vite 最终 CSS 和所有补丁共用同一身份集合。 */
|
|
68
99
|
function prepareTailwindOutput(compiler, input) {
|
|
@@ -84,6 +115,127 @@ function prepareTailwindOutput(compiler, input) {
|
|
|
84
115
|
});
|
|
85
116
|
}
|
|
86
117
|
//#endregion
|
|
118
|
+
//#region src/sources/compact.ts
|
|
119
|
+
const MAX_COMPACTED_PATTERN_LENGTH = 8192;
|
|
120
|
+
function isLiteral(value) {
|
|
121
|
+
return /^[\w.-]+$/.test(value) && value !== "." && !value.includes("..");
|
|
122
|
+
}
|
|
123
|
+
function classify(entry) {
|
|
124
|
+
if (entry.negated !== true || typeof entry.base !== "string" || typeof entry.pattern !== "string" || !entry.pattern.startsWith("**/")) return;
|
|
125
|
+
const rest = entry.pattern.slice(3);
|
|
126
|
+
if (rest.endsWith("/**")) {
|
|
127
|
+
const token = rest.slice(0, -3);
|
|
128
|
+
return isLiteral(token) ? {
|
|
129
|
+
shape: "directory",
|
|
130
|
+
token
|
|
131
|
+
} : void 0;
|
|
132
|
+
}
|
|
133
|
+
const extension = /^\*\.([\w-]+)$/.exec(rest)?.[1];
|
|
134
|
+
if (extension) return {
|
|
135
|
+
shape: "extension",
|
|
136
|
+
token: extension
|
|
137
|
+
};
|
|
138
|
+
return isLiteral(rest) ? {
|
|
139
|
+
shape: "file",
|
|
140
|
+
token: rest
|
|
141
|
+
} : void 0;
|
|
142
|
+
}
|
|
143
|
+
function combinedPattern(shape, tokens) {
|
|
144
|
+
const prefix = shape === "extension" ? "**/*." : "**/";
|
|
145
|
+
const suffix = shape === "directory" ? "/**" : "";
|
|
146
|
+
return `${prefix}{${[...tokens].join(",")}}${suffix}`;
|
|
147
|
+
}
|
|
148
|
+
/** 仅合并连续的固定否定形态;保留上游 globstar、隐藏文件与异常求值顺序。 */
|
|
149
|
+
function compactTailwindSourceEntries(entries) {
|
|
150
|
+
const result = [];
|
|
151
|
+
for (let index = 0; index < entries.length;) {
|
|
152
|
+
const first = entries[index];
|
|
153
|
+
const shape = classify(first);
|
|
154
|
+
if (!shape) {
|
|
155
|
+
result.push(first);
|
|
156
|
+
index += 1;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const run = [first];
|
|
160
|
+
const tokens = /* @__PURE__ */ new Set([shape.token]);
|
|
161
|
+
let next = index + 1;
|
|
162
|
+
while (next < entries.length) {
|
|
163
|
+
const current = entries[next];
|
|
164
|
+
const member = classify(current);
|
|
165
|
+
if (current.base !== first.base || member?.shape !== shape.shape) break;
|
|
166
|
+
run.push(current);
|
|
167
|
+
tokens.add(member.token);
|
|
168
|
+
next += 1;
|
|
169
|
+
}
|
|
170
|
+
const pattern = combinedPattern(shape.shape, tokens);
|
|
171
|
+
result.push(...tokens.size > 1 && pattern.length <= MAX_COMPACTED_PATTERN_LENGTH ? [{
|
|
172
|
+
...first,
|
|
173
|
+
pattern
|
|
174
|
+
}] : run);
|
|
175
|
+
index = next;
|
|
176
|
+
}
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/sources/matcher.ts
|
|
181
|
+
/** 检查不执行访问器;不明对象仍由上游读取并保持原异常顺序。 */
|
|
182
|
+
function hasPlainEntries(entries) {
|
|
183
|
+
if (types.isProxy(entries) || !Array.isArray(entries) || Object.getPrototypeOf(entries) !== Array.prototype || Reflect.ownKeys(entries).some((key) => typeof key !== "string" || key !== "length" && !/^(?:0|[1-9]\d*)$/.test(key))) return false;
|
|
184
|
+
for (let index = 0; index < entries.length; index++) {
|
|
185
|
+
const item = Object.getOwnPropertyDescriptor(entries, String(index));
|
|
186
|
+
if (!item || !("value" in item)) return false;
|
|
187
|
+
const entry = item.value;
|
|
188
|
+
if (!entry || typeof entry !== "object" || types.isProxy(entry)) return false;
|
|
189
|
+
const prototype = Object.getPrototypeOf(entry);
|
|
190
|
+
if (prototype !== null && prototype !== Object.prototype) return false;
|
|
191
|
+
const descriptors = Object.getOwnPropertyDescriptors(entry);
|
|
192
|
+
if (Reflect.ownKeys(entry).some((key) => !("value" in Object.getOwnPropertyDescriptor(entry, key)))) return false;
|
|
193
|
+
if (typeof descriptors.base?.value !== "string" || typeof descriptors.pattern?.value !== "string" || typeof descriptors.negated?.value !== "boolean") return false;
|
|
194
|
+
}
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
/** 绝对模式沿上游词法 base 归一化;不能替换成真实路径 base。 */
|
|
198
|
+
function normalizePattern(entry) {
|
|
199
|
+
const windows = /^[a-z]:[\\/]/i.test(entry.base) || entry.base.includes("\\") || /^[a-z]:[\\/]/i.test(entry.pattern);
|
|
200
|
+
const pathApi = windows ? path$1.win32 : sourcePathApi(entry.base);
|
|
201
|
+
return pathApi.isAbsolute(entry.pattern) ? toPosixPath(pathApi.relative(pathApi.resolve(entry.base), entry.pattern)) : windows ? toPosixPath(entry.pattern) : entry.pattern;
|
|
202
|
+
}
|
|
203
|
+
/** 每文件刷新真实路径,仅复用本批次与文件系统无关的延迟 glob 编译。 */
|
|
204
|
+
function createTailwindSourceMatcher(original) {
|
|
205
|
+
const patterns = /* @__PURE__ */ new Map();
|
|
206
|
+
return (file, entries) => {
|
|
207
|
+
if (typeof file !== "string" || !hasPlainEntries(entries) || !entries.length) return original(file, entries);
|
|
208
|
+
const sources = compactTailwindSourceEntries(entries);
|
|
209
|
+
const resolvedFile = resolveSourceScanPathWithApi(file, sourcePathApi(file, ...sources.map((entry) => entry.base)));
|
|
210
|
+
const bases = /* @__PURE__ */ new Map();
|
|
211
|
+
const matches = (entry) => {
|
|
212
|
+
const pathApi = sourcePathApi(entry.base, resolvedFile);
|
|
213
|
+
let paths = bases.get(pathApi);
|
|
214
|
+
if (!paths) {
|
|
215
|
+
paths = /* @__PURE__ */ new Map();
|
|
216
|
+
bases.set(pathApi, paths);
|
|
217
|
+
}
|
|
218
|
+
let base = paths.get(entry.base);
|
|
219
|
+
if (base === void 0) {
|
|
220
|
+
base = resolveSourceScanPathWithApi(entry.base, pathApi);
|
|
221
|
+
paths.set(entry.base, base);
|
|
222
|
+
}
|
|
223
|
+
const relative = toPosixPath(pathApi.relative(base, resolvedFile));
|
|
224
|
+
if (!relative || relative.startsWith("../") || pathApi.isAbsolute(relative)) return false;
|
|
225
|
+
const pattern = normalizePattern(entry);
|
|
226
|
+
let match = patterns.get(pattern);
|
|
227
|
+
if (!match) {
|
|
228
|
+
match = micromatch.matcher(pattern);
|
|
229
|
+
patterns.set(pattern, match);
|
|
230
|
+
}
|
|
231
|
+
return match(relative);
|
|
232
|
+
};
|
|
233
|
+
const positive = sources.filter((entry) => !entry.negated);
|
|
234
|
+
const negative = sources.filter((entry) => entry.negated);
|
|
235
|
+
return positive.some(matches) && !negative.some(matches);
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
//#endregion
|
|
87
239
|
//#region src/roots.ts
|
|
88
240
|
const caches = /* @__PURE__ */ new WeakMap();
|
|
89
241
|
/** 使用封存源码驱动上游完整候选生成;框架预处理与输出排除由宿主提供。 */
|
|
@@ -97,14 +249,16 @@ async function prepareTailwindRoots(compiler, request, roots, options) {
|
|
|
97
249
|
sources: new Map(request.sources)
|
|
98
250
|
};
|
|
99
251
|
const entries = /* @__PURE__ */ new Map();
|
|
252
|
+
if (!capturedRoots.size) return entries;
|
|
253
|
+
const { isFileMatchedByTailwindV4SourceEntries } = await import("@weapp-tailwindcss/engine");
|
|
254
|
+
const matches = createTailwindSourceMatcher(isFileMatchedByTailwindV4SourceEntries);
|
|
100
255
|
for (const [index, root] of capturedRoots) {
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const sources = [...request.sources].flatMap(([file, content]) => content !== null && matches
|
|
256
|
+
const sourceOptions = root.request.sourceOptions;
|
|
257
|
+
const sourceEntries = [...root.result.sources];
|
|
258
|
+
const sources = [...request.sources].flatMap(([file, content]) => content !== null && sourceEntries.length > 0 && matches(file, sourceEntries) && !options.isOutput?.(file) ? [{
|
|
104
259
|
content,
|
|
105
260
|
extension: path.extname(file).slice(1)
|
|
106
261
|
}] : []);
|
|
107
|
-
const sourceOptions = root.request.sourceOptions;
|
|
108
262
|
if (!sourceOptions) throw new Error("Tailwind HMR requires a captured source entry");
|
|
109
263
|
const cssSources = sourceOptions.cssSources?.map((source) => {
|
|
110
264
|
if (!source.file) return source;
|
|
@@ -149,7 +303,7 @@ async function prepareTailwindRoots(compiler, request, roots, options) {
|
|
|
149
303
|
[...dependencyVersions]
|
|
150
304
|
])).digest("hex");
|
|
151
305
|
const cached = cache.get(root);
|
|
152
|
-
|
|
306
|
+
let effectiveRequest = {
|
|
153
307
|
...root.request,
|
|
154
308
|
sourceOptions: {
|
|
155
309
|
...sourceOptions,
|
|
@@ -157,8 +311,14 @@ async function prepareTailwindRoots(compiler, request, roots, options) {
|
|
|
157
311
|
cssSources: [...cssSources ?? [], ...entrySources]
|
|
158
312
|
},
|
|
159
313
|
sources,
|
|
314
|
+
scanMode: "compiled",
|
|
160
315
|
scanSources: false
|
|
161
|
-
}
|
|
316
|
+
};
|
|
317
|
+
if (options.captureRequest) {
|
|
318
|
+
effectiveRequest = copyTailwindGenerationValue(effectiveRequest);
|
|
319
|
+
await options.captureRequest(index, effectiveRequest);
|
|
320
|
+
}
|
|
321
|
+
const generated = cached?.signature === signature ? cached.result : await compiler.generate(effectiveRequest);
|
|
162
322
|
entries.set(index, generated);
|
|
163
323
|
for (const file of generated.dependencies) checkDependency(file);
|
|
164
324
|
cache.set(root, {
|
|
@@ -171,8 +331,11 @@ async function prepareTailwindRoots(compiler, request, roots, options) {
|
|
|
171
331
|
//#endregion
|
|
172
332
|
//#region src/sources.ts
|
|
173
333
|
/** 返回上游实际扫描边界中的文件,宿主将它们登记到现有 watcher。 */
|
|
174
|
-
function listTailwindSourceFiles(result, cwd) {
|
|
175
|
-
const
|
|
334
|
+
async function listTailwindSourceFiles(result, cwd) {
|
|
335
|
+
const root = result.root;
|
|
336
|
+
const scannedSources = [...result.sources];
|
|
337
|
+
const { createTailwindV4CompiledSourceEntries, createTailwindV4SourceEntryMatcher, resolveProjectSourceFiles } = await import("@weapp-tailwindcss/engine");
|
|
338
|
+
const sources = createTailwindV4CompiledSourceEntries(root, scannedSources, cwd);
|
|
176
339
|
return resolveProjectSourceFiles({
|
|
177
340
|
cwd,
|
|
178
341
|
sources,
|
|
@@ -180,4 +343,4 @@ function listTailwindSourceFiles(result, cwd) {
|
|
|
180
343
|
});
|
|
181
344
|
}
|
|
182
345
|
//#endregion
|
|
183
|
-
export { TailwindController, createTailwindController, createTailwindPreparation, listTailwindSourceFiles, prepareTailwindOutput, prepareTailwindRoots };
|
|
346
|
+
export { TailwindController, copyTailwindGenerationValue, createTailwindController, createTailwindPreparation, listTailwindSourceFiles, prepareTailwindOutput, prepareTailwindRoots };
|
package/package.json
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weapp-vite/tailwindcss",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "1.0.1",
|
|
5
5
|
"description": "基于 weapp-tailwindcss/core 的宿主无关编译控制器",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/weapp-vite/weapp-vite#readme",
|
|
7
8
|
"repository": {
|
|
8
9
|
"type": "git",
|
|
9
10
|
"url": "git+https://github.com/weapp-vite/weapp-vite.git",
|
|
10
11
|
"directory": "packages/tailwindcss"
|
|
11
12
|
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/weapp-vite/weapp-vite/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"weapp-vite",
|
|
18
|
+
"weapp",
|
|
19
|
+
"mini-program",
|
|
20
|
+
"tailwindcss"
|
|
21
|
+
],
|
|
12
22
|
"sideEffects": false,
|
|
13
23
|
"exports": {
|
|
14
24
|
".": {
|
|
@@ -24,13 +34,18 @@
|
|
|
24
34
|
"dist"
|
|
25
35
|
],
|
|
26
36
|
"engines": {
|
|
27
|
-
"node": "^22.18.0 ||
|
|
37
|
+
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
|
|
28
38
|
},
|
|
29
39
|
"dependencies": {
|
|
30
|
-
"@weapp-tailwindcss/engine": "0.1.
|
|
31
|
-
"@weapp-
|
|
40
|
+
"@weapp-tailwindcss/engine": "0.1.6",
|
|
41
|
+
"@weapp-tailwindcss/source-scan": "0.1.2",
|
|
42
|
+
"@weapp-vite/hmr": "0.2.2",
|
|
43
|
+
"micromatch": "4.0.8",
|
|
32
44
|
"pathe": "^2.0.3",
|
|
33
|
-
"weapp-tailwindcss": "^5.
|
|
45
|
+
"weapp-tailwindcss": "^5.6.3"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/micromatch": "4.0.10"
|
|
34
49
|
},
|
|
35
50
|
"publishConfig": {
|
|
36
51
|
"access": "public",
|
|
@@ -40,7 +55,8 @@
|
|
|
40
55
|
"build": "tsdown",
|
|
41
56
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
42
57
|
"test": "vitest run -c vitest.config.ts",
|
|
43
|
-
"test:types": "
|
|
58
|
+
"test:types": "pnpm run test:types:check",
|
|
59
|
+
"test:types:check": "tsd --files \"test-d/**/*.test-d.ts\"",
|
|
44
60
|
"lint": "eslint ."
|
|
45
61
|
}
|
|
46
62
|
}
|