@weapp-vite/tailwindcss 1.0.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/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,7 +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
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";
5
9
  //#region src/controller.ts
6
10
  /** 编译器按宿主生命周期惰性创建;不创建 Vite、DevEngine、watcher 或输出写入者。 */
7
11
  var TailwindController = class {
@@ -62,6 +66,34 @@ function createTailwindPreparation(compiler, snapshot, assets = []) {
62
66
  };
63
67
  }
64
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
65
97
  //#region src/output.ts
66
98
  /** 在宿主回调内封存投影,再异步转换;Vite 最终 CSS 和所有补丁共用同一身份集合。 */
67
99
  function prepareTailwindOutput(compiler, input) {
@@ -83,6 +115,127 @@ function prepareTailwindOutput(compiler, input) {
83
115
  });
84
116
  }
85
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
86
239
  //#region src/roots.ts
87
240
  const caches = /* @__PURE__ */ new WeakMap();
88
241
  /** 使用封存源码驱动上游完整候选生成;框架预处理与输出排除由宿主提供。 */
@@ -97,15 +250,15 @@ async function prepareTailwindRoots(compiler, request, roots, options) {
97
250
  };
98
251
  const entries = /* @__PURE__ */ new Map();
99
252
  if (!capturedRoots.size) return entries;
100
- const { createTailwindV4CompiledSourceEntries, createTailwindV4SourceEntryMatcher } = await import("@weapp-tailwindcss/engine");
253
+ const { isFileMatchedByTailwindV4SourceEntries } = await import("@weapp-tailwindcss/engine");
254
+ const matches = createTailwindSourceMatcher(isFileMatchedByTailwindV4SourceEntries);
101
255
  for (const [index, root] of capturedRoots) {
102
- const base = root.request.sourceOptions?.projectRoot ?? root.request.source?.base ?? options.cwd;
103
- const matches = createTailwindV4SourceEntryMatcher(createTailwindV4CompiledSourceEntries(root.result.root, [...root.result.sources], base));
104
- const sources = [...request.sources].flatMap(([file, content]) => content !== null && matches?.(file) && !options.isOutput?.(file) ? [{
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) ? [{
105
259
  content,
106
260
  extension: path.extname(file).slice(1)
107
261
  }] : []);
108
- const sourceOptions = root.request.sourceOptions;
109
262
  if (!sourceOptions) throw new Error("Tailwind HMR requires a captured source entry");
110
263
  const cssSources = sourceOptions.cssSources?.map((source) => {
111
264
  if (!source.file) return source;
@@ -150,7 +303,7 @@ async function prepareTailwindRoots(compiler, request, roots, options) {
150
303
  [...dependencyVersions]
151
304
  ])).digest("hex");
152
305
  const cached = cache.get(root);
153
- const generated = cached?.signature === signature ? cached.result : await compiler.generate({
306
+ let effectiveRequest = {
154
307
  ...root.request,
155
308
  sourceOptions: {
156
309
  ...sourceOptions,
@@ -158,8 +311,14 @@ async function prepareTailwindRoots(compiler, request, roots, options) {
158
311
  cssSources: [...cssSources ?? [], ...entrySources]
159
312
  },
160
313
  sources,
314
+ scanMode: "compiled",
161
315
  scanSources: false
162
- });
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);
163
322
  entries.set(index, generated);
164
323
  for (const file of generated.dependencies) checkDependency(file);
165
324
  cache.set(root, {
@@ -184,4 +343,4 @@ async function listTailwindSourceFiles(result, cwd) {
184
343
  });
185
344
  }
186
345
  //#endregion
187
- export { TailwindController, createTailwindController, createTailwindPreparation, listTailwindSourceFiles, prepareTailwindOutput, prepareTailwindRoots };
346
+ export { TailwindController, copyTailwindGenerationValue, createTailwindController, createTailwindPreparation, listTailwindSourceFiles, prepareTailwindOutput, prepareTailwindRoots };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@weapp-vite/tailwindcss",
3
3
  "type": "module",
4
- "version": "1.0.0",
4
+ "version": "1.0.1",
5
5
  "description": "基于 weapp-tailwindcss/core 的宿主无关编译控制器",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/weapp-vite/weapp-vite#readme",
@@ -37,10 +37,15 @@
37
37
  "node": "^22.18.0 || ^24.11.0 || >=26.0.0"
38
38
  },
39
39
  "dependencies": {
40
- "@weapp-tailwindcss/engine": "0.1.5",
41
- "@weapp-vite/hmr": "0.2.1",
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",
42
44
  "pathe": "^2.0.3",
43
- "weapp-tailwindcss": "^5.6.0"
45
+ "weapp-tailwindcss": "^5.6.3"
46
+ },
47
+ "devDependencies": {
48
+ "@types/micromatch": "4.0.10"
44
49
  },
45
50
  "publishConfig": {
46
51
  "access": "public",