@weapp-tailwindcss/engine 0.1.0 → 0.1.2

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.
@@ -25,16 +25,189 @@ let node_path = require("node:path");
25
25
  node_path = __toESM(node_path, 1);
26
26
  let node_process = require("node:process");
27
27
  node_process = __toESM(node_process, 1);
28
+ let node_crypto = require("node:crypto");
28
29
  let node_module = require("node:module");
29
30
  let node_url = require("node:url");
30
- let micromatch = require("micromatch");
31
- micromatch = __toESM(micromatch, 1);
32
- let node_fs_promises = require("node:fs/promises");
31
+ let node_async_hooks = require("node:async_hooks");
32
+ let _tailwindcss_node_require_cache = require("@tailwindcss/node/require-cache");
33
+ let _weapp_tailwindcss_source_scan = require("@weapp-tailwindcss/source-scan");
33
34
  let postcss = require("postcss");
34
35
  postcss = __toESM(postcss, 1);
36
+ let node_fs_promises = require("node:fs/promises");
37
+ //#region src/v4/module-cache.ts
38
+ const context = new node_async_hooks.AsyncLocalStorage();
39
+ const caches = /* @__PURE__ */ new WeakMap();
40
+ let revision = 0;
41
+ const hooks = globalThis;
42
+ let installedHook;
43
+ let activeContexts = 0;
44
+ const MODULE_CACHE_LIMIT = 128;
45
+ const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
46
+ function canUseCommonJsGraph(file) {
47
+ const extension = node_path.default.extname(file);
48
+ if (extension === ".cjs" || extension === ".cts") return true;
49
+ if (extension !== ".js") return false;
50
+ const manifest = (0, node_module.findPackageJSON)((0, node_url.pathToFileURL)(file).href);
51
+ return !manifest || node_path.default.basename(manifest) !== "package.json" || JSON.parse((0, node_fs.readFileSync)(manifest, "utf8")).type !== "module";
52
+ }
53
+ function collectCommonJsDependencies(file, dependencies) {
54
+ const module = require$1.cache[file];
55
+ if (!module?.loaded) return false;
56
+ const visited = /* @__PURE__ */ new Set();
57
+ const packageRoot = node_path.default.dirname((0, node_module.findPackageJSON)((0, node_url.pathToFileURL)(file).href) ?? file);
58
+ function visit(current) {
59
+ if (visited.has(current) || !current.filename) return;
60
+ visited.add(current);
61
+ let directory = node_path.default.dirname(current.filename);
62
+ const relative = node_path.default.relative(packageRoot, current.filename);
63
+ while (node_path.default.dirname(directory) !== directory) {
64
+ if (node_path.default.basename(directory) === "node_modules" && (relative.startsWith(`..${node_path.default.sep}`) || node_path.default.isAbsolute(relative))) return;
65
+ directory = node_path.default.dirname(directory);
66
+ }
67
+ dependencies.add(current.filename);
68
+ for (const child of current.children) visit(child);
69
+ }
70
+ visit(module);
71
+ return true;
72
+ }
73
+ function fingerprint(files) {
74
+ const hash = (0, node_crypto.createHash)("sha256");
75
+ for (const file of files) {
76
+ hash.update(file);
77
+ hash.update((0, node_fs.readFileSync)(file));
78
+ }
79
+ return hash.digest("hex");
80
+ }
81
+ async function loadCachedModule(loader, file) {
82
+ let state = caches.get(loader);
83
+ if (!state || state.revision !== revision) {
84
+ state = {
85
+ revision,
86
+ entries: /* @__PURE__ */ new Map()
87
+ };
88
+ caches.set(loader, state);
89
+ }
90
+ const cache = state.entries;
91
+ const previous = cache.get(file);
92
+ if (previous) {
93
+ const entry = await previous;
94
+ try {
95
+ if (fingerprint(entry.dependencies) === entry.fingerprint) return entry;
96
+ } catch {}
97
+ if (cache.get(file) !== previous) return loadCachedModule(loader, file);
98
+ (0, _tailwindcss_node_require_cache.clearRequireCache)(entry.dependencies);
99
+ }
100
+ const pending = context.run(void 0, async () => {
101
+ const dependencies = /* @__PURE__ */ new Set([file]);
102
+ const base = node_path.default.dirname(file);
103
+ const onDependency = (dependency) => {
104
+ dependencies.add(dependency);
105
+ };
106
+ let loaded = previous || !canUseCommonJsGraph(file) ? void 0 : await loader.loadModule(file, base, onDependency);
107
+ if (!loaded || !collectCommonJsDependencies(file, dependencies)) loaded = await loader.loadModule(`./${node_path.default.basename(file)}`, base, onDependency);
108
+ const files = [...dependencies].sort();
109
+ return {
110
+ dependencies: files,
111
+ fingerprint: fingerprint(files),
112
+ value: { default: loaded.module }
113
+ };
114
+ });
115
+ cache.set(file, pending);
116
+ while (cache.size > MODULE_CACHE_LIMIT) cache.delete(cache.keys().next().value);
117
+ try {
118
+ return await pending;
119
+ } catch (error) {
120
+ if (cache.get(file) === pending) cache.delete(file);
121
+ throw error;
122
+ }
123
+ }
124
+ function installHook() {
125
+ if (installedHook && hooks.__tw_load === installedHook) return;
126
+ const previous = hooks.__tw_load;
127
+ installedHook = async (url) => {
128
+ const overridden = await previous?.(url);
129
+ if (overridden) return overridden;
130
+ const active = context.getStore();
131
+ if (!active?.loader.loadModule || !url.startsWith("file:")) return;
132
+ const parsed = new URL(url);
133
+ const file = (0, _weapp_tailwindcss_source_scan.resolveSourceScanPath)((0, node_url.fileURLToPath)(parsed));
134
+ if (!parsed.searchParams.has("id") && !active.files.has(file)) return;
135
+ const entry = await loadCachedModule(active.loader, file);
136
+ for (const dependency of entry.dependencies) active.onDependency?.(dependency);
137
+ return entry.value;
138
+ };
139
+ hooks.__tw_load = installedHook;
140
+ }
141
+ function invalidateGenerationModuleCache() {
142
+ revision += 1;
143
+ }
144
+ /** 仅在本引擎调用内复用本地模块;Tailwind 仍负责解析与依赖跟踪。 */
145
+ async function withGenerationModuleCache(loader, run, files = [], onDependency) {
146
+ installHook();
147
+ const resolved = files.map((file) => {
148
+ for (const candidate of [
149
+ file,
150
+ `${file}.ts`,
151
+ node_path.default.join(file, "index.ts")
152
+ ]) try {
153
+ return (0, _weapp_tailwindcss_source_scan.resolveSourceScanPath)(require$1.resolve(candidate));
154
+ } catch {}
155
+ return (0, _weapp_tailwindcss_source_scan.resolveSourceScanPath)(file);
156
+ });
157
+ activeContexts += 1;
158
+ try {
159
+ return await context.run({
160
+ loader,
161
+ files: new Set(resolved),
162
+ onDependency
163
+ }, run);
164
+ } finally {
165
+ activeContexts -= 1;
166
+ if (activeContexts === 0) context.disable();
167
+ }
168
+ }
169
+ //#endregion
170
+ //#region src/v4/module-requests.ts
171
+ /** 固定本地模块身份,依赖跟踪与刷新由生成模块缓存负责。 */
172
+ function prepareGenerationModuleRequests(css, base) {
173
+ const files = [];
174
+ if (!css.includes("@config") && !css.includes("@plugin")) return {
175
+ css,
176
+ files
177
+ };
178
+ const root = postcss.default.parse(css);
179
+ root.walkAtRules((rule) => {
180
+ if (rule.name !== "config" && rule.name !== "plugin") return;
181
+ const match = /^(["'])(.*)\1$/.exec(rule.params.trim());
182
+ if (!match) return;
183
+ const request = match[2];
184
+ const pathApi = /^[a-z]:[\\/]/i.test(base) || base.includes("\\") ? node_path.default.win32 : node_path.default;
185
+ if (!pathApi.isAbsolute(request) && !request.startsWith(".")) return;
186
+ const file = pathApi.resolve(base, request);
187
+ files.push(file);
188
+ rule.params = JSON.stringify(file.replaceAll("\\", "/"));
189
+ });
190
+ return {
191
+ css: root.toString(),
192
+ files
193
+ };
194
+ }
195
+ //#endregion
35
196
  //#region src/v4/node-adapter.ts
36
197
  const nodeModulePromiseCache = /* @__PURE__ */ new Map();
37
198
  const designSystemPromiseCache = /* @__PURE__ */ new Map();
199
+ function fingerprintDesignDependencies(files) {
200
+ const hash = (0, node_crypto.createHash)("sha256");
201
+ for (const file of [...files].sort()) {
202
+ hash.update(file);
203
+ try {
204
+ hash.update((0, node_fs.readFileSync)(file));
205
+ } catch {
206
+ hash.update("missing");
207
+ }
208
+ }
209
+ return hash.digest("hex");
210
+ }
38
211
  function unique(values) {
39
212
  return Array.from(new Set(Array.from(values).filter(Boolean).map((value) => node_path.default.resolve(value))));
40
213
  }
@@ -106,13 +279,14 @@ function createDesignSystemCacheKey(css, bases) {
106
279
  function getTailwindV4DesignSystemCacheKey(source) {
107
280
  return createDesignSystemCacheKey(source.css, [source.base, ...source.baseFallbacks]);
108
281
  }
109
- async function createTailwindV4DesignSystem(source) {
282
+ async function createTailwindV4DesignSystem(source, onDependency) {
110
283
  const bases = unique([source.base, ...source.baseFallbacks]);
111
284
  if (bases.length === 0) throw new Error("No base directories provided for Tailwind CSS v4 design system.");
112
285
  const node = await loadTailwindV4NodeModule([source.projectRoot, ...bases]);
113
286
  let lastError;
114
287
  for (const base of bases) try {
115
- return await node.__unstable__loadDesignSystem(source.css, { base });
288
+ const prepared = prepareGenerationModuleRequests(source.css, base);
289
+ return await withGenerationModuleCache(node, () => node.__unstable__loadDesignSystem(prepared.css, { base }), prepared.files, onDependency);
116
290
  } catch (error) {
117
291
  lastError = error;
118
292
  }
@@ -121,13 +295,20 @@ async function createTailwindV4DesignSystem(source) {
121
295
  }
122
296
  async function loadTailwindV4DesignSystem(source, options) {
123
297
  if (options?.cache === false) return createTailwindV4DesignSystem(source);
124
- const cacheKey = getTailwindV4DesignSystemCacheKey(source);
298
+ const cacheKey = JSON.stringify([source.projectRoot, getTailwindV4DesignSystemCacheKey(source)]);
125
299
  const cached = designSystemPromiseCache.get(cacheKey);
126
- if (cached) return cached;
127
- const promise = createTailwindV4DesignSystem(source);
128
- designSystemPromiseCache.set(cacheKey, promise);
129
- promise.catch(() => {
130
- if (designSystemPromiseCache.get(cacheKey) === promise) designSystemPromiseCache.delete(cacheKey);
300
+ if (cached && (cached.fingerprint === void 0 || cached.fingerprint === fingerprintDesignDependencies(cached.dependencies))) return cached.promise;
301
+ const dependencies = new Set(source.dependencies);
302
+ const promise = createTailwindV4DesignSystem(source, (file) => dependencies.add(file));
303
+ const entry = {
304
+ promise,
305
+ dependencies
306
+ };
307
+ designSystemPromiseCache.set(cacheKey, entry);
308
+ promise.then(() => {
309
+ entry.fingerprint = fingerprintDesignDependencies(dependencies);
310
+ }, () => {
311
+ if (designSystemPromiseCache.get(cacheKey) === entry) designSystemPromiseCache.delete(cacheKey);
131
312
  });
132
313
  return promise;
133
314
  }
@@ -139,14 +320,14 @@ async function compileTailwindV4Source(source) {
139
320
  for (const base of bases) {
140
321
  const dependencies = new Set(source.dependencies);
141
322
  try {
323
+ const prepared = prepareGenerationModuleRequests(source.css, base);
324
+ const onDependency = (dependency) => dependencies.add(node_path.default.resolve(dependency));
142
325
  return {
143
- compiled: await node.compile(source.css, {
326
+ compiled: await withGenerationModuleCache(node, () => node.compile(prepared.css, {
144
327
  base,
145
328
  customCssResolver: createFallbackCssResolver([source.projectRoot, ...bases]),
146
- onDependency(dependency) {
147
- dependencies.add(node_path.default.resolve(dependency));
148
- }
149
- }),
329
+ onDependency
330
+ }), prepared.files, onDependency),
150
331
  dependencies
151
332
  };
152
333
  } catch (error) {
@@ -157,6 +338,17 @@ async function compileTailwindV4Source(source) {
157
338
  throw new Error("Failed to compile Tailwind CSS v4 source.");
158
339
  }
159
340
  //#endregion
341
+ //#region src/v4/source-scan/matcher.ts
342
+ function isFileMatchedByTailwindV4SourceEntries(file, entries) {
343
+ return (0, _weapp_tailwindcss_source_scan.isFileMatchedByTailwindSourceEntries)(file, entries, { requirePositive: true });
344
+ }
345
+ function createTailwindV4SourceEntryMatcher(entries) {
346
+ return entries?.length ? (file) => isFileMatchedByTailwindV4SourceEntries(file, entries) : void 0;
347
+ }
348
+ function createTailwindV4SourceExclusionMatcher(entries) {
349
+ return entries?.length ? (file) => (0, _weapp_tailwindcss_source_scan.isFileExcludedByTailwindSourceEntries)(file, entries) : void 0;
350
+ }
351
+ //#endregion
160
352
  //#region src/v4/source-scan/sources.ts
161
353
  const TAILWIND_V4_IGNORED_CONTENT_DIRS = [
162
354
  ".git",
@@ -201,48 +393,6 @@ function uniqueResolvedPaths(values) {
201
393
  }
202
394
  return result;
203
395
  }
204
- function toPosixPath(value) {
205
- return value.replaceAll(node_path.default.sep, "/");
206
- }
207
- function resolveSourceScanPath(value) {
208
- const resolved = node_path.default.resolve(value);
209
- try {
210
- return node_fs.realpathSync.native(resolved);
211
- } catch {
212
- return resolved;
213
- }
214
- }
215
- function normalizeGlobPattern(pattern) {
216
- return pattern.startsWith("./") ? pattern.slice(2) : pattern;
217
- }
218
- function hasGlobMagic(value) {
219
- return /[*?[\]{}()!+@]/.test(value);
220
- }
221
- function splitStaticGlobPrefix(pattern) {
222
- const segments = normalizeGlobPattern(pattern).split(/[\\/]+/);
223
- const prefix = [];
224
- const rest = [];
225
- let reachedGlob = false;
226
- for (const segment of segments) {
227
- if (!reachedGlob && segment && !hasGlobMagic(segment)) {
228
- prefix.push(segment);
229
- continue;
230
- }
231
- reachedGlob = true;
232
- rest.push(segment);
233
- }
234
- return {
235
- prefix,
236
- rest
237
- };
238
- }
239
- async function pathExistsAsDirectory(file) {
240
- try {
241
- return (await (0, node_fs_promises.stat)(file)).isDirectory();
242
- } catch {
243
- return false;
244
- }
245
- }
246
396
  function createTailwindV4DefaultIgnoreSources(base) {
247
397
  return [
248
398
  ...TAILWIND_V4_IGNORED_CONTENT_DIRS.map((pattern) => ({
@@ -263,117 +413,32 @@ function createTailwindV4DefaultIgnoreSources(base) {
263
413
  ];
264
414
  }
265
415
  function createTailwindV4RootSources(root, fallbackBase) {
266
- if (root === "none") return [];
267
- if (root === null) return [{
416
+ return (0, _weapp_tailwindcss_source_scan.createSourceScanPlan)({
268
417
  base: fallbackBase,
269
- pattern: TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN,
270
- negated: false
271
- }];
272
- return [{
273
- ...root,
274
- negated: false
275
- }];
418
+ mode: root === "none" ? "disabled" : root === null ? "auto" : "explicit",
419
+ entries: root && root !== "none" ? [{
420
+ ...root,
421
+ negated: false
422
+ }] : []
423
+ });
276
424
  }
277
425
  function createTailwindV4CompiledSourceEntries(root, sources, fallbackBase) {
278
426
  return [...createTailwindV4RootSources(root, fallbackBase), ...sources];
279
427
  }
280
- async function resolveTailwindV4SourceEntry(sourcePath, base, negated, defaultPattern = TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN) {
281
- const absoluteSource = node_path.default.isAbsolute(sourcePath) ? node_path.default.resolve(sourcePath) : node_path.default.resolve(base, sourcePath);
282
- if (await pathExistsAsDirectory(absoluteSource)) return {
283
- base: absoluteSource,
284
- negated,
285
- pattern: normalizeGlobPattern(defaultPattern)
286
- };
287
- if (node_path.default.isAbsolute(sourcePath)) return {
288
- base: node_path.default.dirname(absoluteSource),
289
- negated,
290
- pattern: normalizeGlobPattern(node_path.default.basename(absoluteSource))
291
- };
292
- const { prefix, rest } = splitStaticGlobPrefix(sourcePath);
293
- if (prefix.length > 0 && rest.length > 0) return {
294
- base: node_path.default.resolve(base, ...prefix),
295
- negated,
296
- pattern: normalizeGlobPattern(rest.join("/"))
297
- };
298
- return {
299
- base,
300
- negated,
301
- pattern: normalizeGlobPattern(sourcePath)
302
- };
428
+ function resolveTailwindV4SourceEntry(sourcePath, base, negated, defaultPattern = TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN) {
429
+ return (0, _weapp_tailwindcss_source_scan.resolveTailwindSourceEntry)(sourcePath, base, negated, defaultPattern);
303
430
  }
304
431
  async function normalizeTailwindV4SourceEntries(sources, options = {}) {
305
432
  const cwd = options.cwd ? node_path.default.resolve(options.cwd) : node_process.default.cwd();
306
433
  return Promise.all(sources.map((source) => resolveTailwindV4SourceEntry(source.pattern, source.base ? node_path.default.resolve(source.base) : cwd, source.negated, options.defaultPattern)));
307
434
  }
308
- function expandBracePattern(pattern) {
309
- const index = pattern.indexOf("{");
310
- if (index === -1) return [pattern];
311
- const rest = pattern.slice(index);
312
- let depth = 0;
313
- let endIndex = -1;
314
- for (let i = 0; i < rest.length; i++) {
315
- const char = rest[i];
316
- if (char === "\\") {
317
- i += 1;
318
- continue;
319
- }
320
- if (char === "{") {
321
- depth += 1;
322
- continue;
323
- }
324
- if (char === "}") {
325
- depth -= 1;
326
- if (depth === 0) {
327
- endIndex = i;
328
- break;
329
- }
330
- }
331
- }
332
- if (endIndex === -1) return [pattern];
333
- const prefix = pattern.slice(0, index);
334
- const inner = rest.slice(1, endIndex);
335
- const suffix = rest.slice(endIndex + 1);
336
- const parts = [];
337
- const stack = [];
338
- let lastPos = 0;
339
- for (let i = 0; i < inner.length; i++) {
340
- const char = inner[i];
341
- if (char === "\\") {
342
- i += 1;
343
- continue;
344
- }
345
- if (char === "{") {
346
- stack.push("}");
347
- continue;
348
- }
349
- if (char === "}" && stack[stack.length - 1] === "}") {
350
- stack.pop();
351
- continue;
352
- }
353
- if (char === "," && stack.length === 0) {
354
- parts.push(inner.slice(lastPos, i));
355
- lastPos = i + 1;
356
- }
357
- }
358
- parts.push(inner.slice(lastPos));
359
- return parts.flatMap((part) => expandBracePattern(`${prefix}${part}${suffix}`));
360
- }
361
- function expandTailwindV4SourceEntryBraces(sources) {
362
- return sources.flatMap((source) => {
363
- const base = node_path.default.resolve(source.base);
364
- return expandBracePattern(source.pattern).map((pattern) => ({
365
- base,
366
- pattern,
367
- negated: source.negated
368
- }));
369
- });
370
- }
371
435
  function normalizeTailwindV4ScannerSources(sources, cwd, ignoredSources = []) {
372
- return expandTailwindV4SourceEntryBraces([...sources?.length ? sources : [{
436
+ const baseSources = sources?.length ? sources : [{
373
437
  base: cwd,
374
438
  pattern: TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN,
375
439
  negated: false
376
- }], ...ignoredSources]);
440
+ }];
441
+ return (0, _weapp_tailwindcss_source_scan.expandSourceEntryBraces)([...baseSources, ...ignoredSources]);
377
442
  }
378
443
  function resolveTailwindV4SourceBaseCandidates(projectRoot, base, baseFallbacks) {
379
444
  return uniqueResolvedPaths([
@@ -383,79 +448,6 @@ function resolveTailwindV4SourceBaseCandidates(projectRoot, base, baseFallbacks)
383
448
  ]);
384
449
  }
385
450
  //#endregion
386
- //#region src/v4/source-scan/matcher.ts
387
- function normalizeEntryPattern(entry) {
388
- return node_path.default.isAbsolute(entry.pattern) ? toPosixPath(node_path.default.relative(resolveSourceScanPath(entry.base), entry.pattern)) : normalizeGlobPattern(entry.pattern);
389
- }
390
- function isFileMatchedByTailwindV4SourceEntry(file, entry) {
391
- const relative = toPosixPath(node_path.default.relative(resolveSourceScanPath(entry.base), file));
392
- return Boolean(relative) && !relative.startsWith("../") && !node_path.default.isAbsolute(relative) && micromatch.default.isMatch(relative, normalizeEntryPattern(entry));
393
- }
394
- function isFileExcludedByTailwindV4SourceEntries(file, entries) {
395
- if (!entries?.length) return false;
396
- const resolvedFile = resolveSourceScanPath(file);
397
- return entries.some((entry) => entry.negated && isFileMatchedByTailwindV4SourceEntry(resolvedFile, entry));
398
- }
399
- function isFileMatchedByTailwindV4SourceEntries(file, entries) {
400
- if (!entries?.length) return true;
401
- const positiveEntries = entries.filter((entry) => !entry.negated);
402
- const negativeEntries = entries.filter((entry) => entry.negated);
403
- if (positiveEntries.length === 0) return false;
404
- const resolvedFile = resolveSourceScanPath(file);
405
- if (!positiveEntries.some((entry) => isFileMatchedByTailwindV4SourceEntry(resolvedFile, entry))) return false;
406
- return !negativeEntries.some((entry) => isFileMatchedByTailwindV4SourceEntry(resolvedFile, entry));
407
- }
408
- function createTailwindV4SourceEntryMatcher(entries) {
409
- if (!entries?.length) return;
410
- return (file) => isFileMatchedByTailwindV4SourceEntries(file, entries);
411
- }
412
- function createTailwindV4SourceExclusionMatcher(entries) {
413
- if (!entries?.length) return;
414
- return (file) => isFileExcludedByTailwindV4SourceEntries(file, entries);
415
- }
416
- function groupTailwindV4SourceEntriesByBase(entries) {
417
- const entriesByBase = /* @__PURE__ */ new Map();
418
- for (const entry of entries) {
419
- const base = node_path.default.resolve(entry.base);
420
- const group = entriesByBase.get(base) ?? [];
421
- group.push({
422
- ...entry,
423
- base,
424
- pattern: normalizeGlobPattern(entry.pattern)
425
- });
426
- entriesByBase.set(base, group);
427
- }
428
- return entriesByBase;
429
- }
430
- async function expandTailwindV4SourceEntries(entries, resolveFiles) {
431
- if (entries.length === 0) return [];
432
- const files = /* @__PURE__ */ new Set();
433
- await Promise.all([...groupTailwindV4SourceEntriesByBase(entries).entries()].map(async ([base, group]) => {
434
- const matched = await resolveFiles({
435
- cwd: base,
436
- sources: group
437
- });
438
- for (const file of matched) files.add(node_path.default.resolve(file));
439
- }));
440
- return [...files].filter((file) => !isFileExcludedByTailwindV4SourceEntries(file, entries));
441
- }
442
- function mergeTailwindV4SourceEntries(...entries) {
443
- const result = [];
444
- const seen = /* @__PURE__ */ new Set();
445
- for (const group of entries) for (const entry of group ?? []) {
446
- const normalized = {
447
- base: node_path.default.resolve(entry.base),
448
- pattern: normalizeGlobPattern(entry.pattern),
449
- negated: entry.negated
450
- };
451
- const key = JSON.stringify(normalized);
452
- if (seen.has(key)) continue;
453
- seen.add(key);
454
- result.push(normalized);
455
- }
456
- return result;
457
- }
458
- //#endregion
459
451
  //#region src/extraction/oxide.ts
460
452
  let oxideImportPromise;
461
453
  function createOxideRuntimeDependencyError(cause) {
@@ -543,6 +535,94 @@ function createTokenLocation(input) {
543
535
  };
544
536
  }
545
537
  //#endregion
538
+ //#region src/extraction/candidate-extractor/project.ts
539
+ async function resolveScannerSources(options) {
540
+ const cwd = options?.cwd ? node_path.default.resolve(options.cwd) : node_process.default.cwd();
541
+ if (options?.sources?.length || options?.css === void 0) return {
542
+ cwd,
543
+ sources: normalizeTailwindV4ScannerSources(options?.sources, cwd, options?.ignoredSources)
544
+ };
545
+ const base = options.base ? node_path.default.resolve(options.base) : cwd;
546
+ const { compiled } = await compileTailwindV4Source({
547
+ projectRoot: cwd,
548
+ base,
549
+ baseFallbacks: options.baseFallbacks?.map((baseFallback) => node_path.default.resolve(baseFallback)) ?? [],
550
+ css: options.css,
551
+ dependencies: []
552
+ });
553
+ return {
554
+ cwd,
555
+ sources: normalizeTailwindV4ScannerSources(createTailwindV4CompiledSourceEntries(compiled.root, compiled.sources, base), cwd, options.ignoredSources)
556
+ };
557
+ }
558
+ async function resolveProjectSourceFiles(options) {
559
+ const { sources } = await resolveScannerSources(options);
560
+ const { Scanner } = await getOxideModule();
561
+ const files = new Scanner({ sources }).files ?? [];
562
+ return options?.filter ? files.filter(options.filter) : files;
563
+ }
564
+ async function extractProjectCandidatesWithPositions(options) {
565
+ const { cwd, sources } = await resolveScannerSources(options);
566
+ const { Scanner } = await getOxideModule();
567
+ const scanner = new Scanner({ sources });
568
+ const files = scanner.files ?? [];
569
+ const entries = [];
570
+ const skipped = [];
571
+ const batchSize = 32;
572
+ for (let start = 0; start < files.length; start += batchSize) {
573
+ const batch = files.slice(start, start + batchSize);
574
+ const contents = await Promise.allSettled(batch.map((file) => node_fs.promises.readFile(file, "utf8")));
575
+ for (const [index, file] of batch.entries()) {
576
+ const result = contents[index];
577
+ if (result.status === "rejected") {
578
+ skipped.push({
579
+ file,
580
+ reason: result.reason instanceof Error ? result.reason.message : "Unknown error"
581
+ });
582
+ continue;
583
+ }
584
+ const content = result.value;
585
+ const extension = toExtension(file);
586
+ const matches = scanner.getCandidatesWithPositions({
587
+ file,
588
+ content,
589
+ extension
590
+ });
591
+ if (!matches.length) continue;
592
+ const offsets = buildLineOffsets(content);
593
+ for (const match of matches) entries.push(createTokenLocation({
594
+ cwd,
595
+ file,
596
+ content,
597
+ extension,
598
+ candidate: match.candidate,
599
+ position: match.position,
600
+ offsets
601
+ }));
602
+ }
603
+ }
604
+ return {
605
+ entries,
606
+ filesScanned: files.length,
607
+ skippedFiles: skipped,
608
+ sources
609
+ };
610
+ }
611
+ function groupTokensByFile(report, options) {
612
+ const key = options?.key ?? "relative";
613
+ const stripAbsolute = options?.stripAbsolutePaths ?? key !== "absolute";
614
+ return report.entries.reduce((acc, entry) => {
615
+ const bucketKey = key === "absolute" ? entry.file : entry.relativeFile;
616
+ const bucket = acc[bucketKey] ?? (acc[bucketKey] = []);
617
+ const value = stripAbsolute ? {
618
+ ...entry,
619
+ file: entry.relativeFile
620
+ } : entry;
621
+ bucket.push(value);
622
+ return acc;
623
+ }, {});
624
+ }
625
+ //#endregion
546
626
  //#region src/v4/bare-arbitrary-values/escape.ts
547
627
  function escapeCssClassName(value) {
548
628
  let result = "";
@@ -3559,6 +3639,7 @@ function createTailwindGenerationArtifact(css, source, request, classSet, rawCan
3559
3639
  //#region src/v4/generation-request.ts
3560
3640
  function toGenerationRequest(options) {
3561
3641
  return {
3642
+ ...options?.excludeFiles === void 0 ? {} : { excludeFiles: options.excludeFiles },
3562
3643
  ...options?.candidates === void 0 ? {} : { candidates: options.candidates },
3563
3644
  ...options?.sources === void 0 ? {} : { sourceEntries: options.sources.map((sourceEntry, index) => ({
3564
3645
  id: `source:${index}`,
@@ -3570,6 +3651,7 @@ function toGenerationRequest(options) {
3570
3651
  }
3571
3652
  function toGenerateOptions(request) {
3572
3653
  return {
3654
+ ...request.excludeFiles === void 0 ? {} : { excludeFiles: request.excludeFiles },
3573
3655
  ...request.candidates === void 0 ? {} : { candidates: request.candidates },
3574
3656
  ...request.sourceEntries === void 0 ? {} : { sources: request.sourceEntries },
3575
3657
  ...request.bareArbitraryValues === void 0 ? {} : { bareArbitraryValues: request.bareArbitraryValues },
@@ -3608,7 +3690,7 @@ function stripCompiledSourceEntries(source) {
3608
3690
  return source;
3609
3691
  }
3610
3692
  }
3611
- async function collectRawCandidates(source, options, compiledRoot, compiledSources = []) {
3693
+ async function collectRawCandidates(source, options, compiledRoot, compiledSources = [], onFiles) {
3612
3694
  const rawCandidates = /* @__PURE__ */ new Set();
3613
3695
  const extractOptions = options?.bareArbitraryValues === void 0 ? void 0 : { bareArbitraryValues: options.bareArbitraryValues };
3614
3696
  for (const candidate of options?.candidates ?? []) rawCandidates.add(candidate);
@@ -3617,7 +3699,25 @@ async function collectRawCandidates(source, options, compiledRoot, compiledSourc
3617
3699
  for (const candidate of candidates) rawCandidates.add(candidate.rawCandidate);
3618
3700
  }
3619
3701
  const filesystemSources = resolveScanSources(options, source, compiledRoot, compiledSources);
3620
- if (filesystemSources.length > 0) for (const candidate of await extractRawCandidates(filesystemSources, extractOptions)) rawCandidates.add(candidate);
3702
+ if (filesystemSources.some((entry) => !entry.negated)) {
3703
+ const excluded = new Set((options?.excludeFiles ?? []).map(_weapp_tailwindcss_source_scan.resolveSourceScanPath));
3704
+ const files = await resolveProjectSourceFiles({
3705
+ sources: filesystemSources,
3706
+ filter: (file) => !excluded.has((0, _weapp_tailwindcss_source_scan.resolveSourceScanPath)(file))
3707
+ });
3708
+ onFiles?.(files);
3709
+ for (const file of files) {
3710
+ let content;
3711
+ try {
3712
+ content = await (0, node_fs_promises.readFile)(file, "utf8");
3713
+ } catch (error) {
3714
+ const code = error?.code;
3715
+ if (code === "ENOENT" || code === "ENOTDIR") continue;
3716
+ throw error;
3717
+ }
3718
+ for (const candidate of await extractRawCandidatesWithPositions(content, node_path.default.extname(file).slice(1), extractOptions)) rawCandidates.add(candidate.rawCandidate);
3719
+ }
3720
+ }
3621
3721
  const inlineSources = extractTailwindV4InlineSourceCandidates(source.css);
3622
3722
  for (const candidate of inlineSources.included) rawCandidates.add(candidate);
3623
3723
  for (const candidate of inlineSources.excluded) rawCandidates.delete(candidate);
@@ -3630,6 +3730,7 @@ var TailwindGenerationSessionImpl = class {
3630
3730
  runtimes = /* @__PURE__ */ new Map();
3631
3731
  designSystemPromise;
3632
3732
  disposed = false;
3733
+ moduleDependencies = /* @__PURE__ */ new Set();
3633
3734
  constructor(source) {
3634
3735
  this.currentSource = source;
3635
3736
  }
@@ -3662,10 +3763,15 @@ var TailwindGenerationSessionImpl = class {
3662
3763
  invalidate(change) {
3663
3764
  if (this.disposed) return;
3664
3765
  if (change.type === "source") this.currentSource = change.source;
3766
+ (0, _tailwindcss_node_require_cache.clearRequireCache)([...this.moduleDependencies]);
3767
+ invalidateGenerationModuleCache();
3768
+ this.moduleDependencies.clear();
3665
3769
  this.runtimes.clear();
3666
3770
  this.designSystemPromise = void 0;
3667
3771
  }
3668
3772
  dispose() {
3773
+ (0, _tailwindcss_node_require_cache.clearRequireCache)([...this.moduleDependencies]);
3774
+ this.moduleDependencies.clear();
3669
3775
  this.runtimes.clear();
3670
3776
  this.designSystemPromise = void 0;
3671
3777
  this.disposed = true;
@@ -3673,12 +3779,12 @@ var TailwindGenerationSessionImpl = class {
3673
3779
  assertActive() {
3674
3780
  if (this.disposed) throw new Error("Tailwind generation session has been disposed.");
3675
3781
  }
3676
- getRuntime(compileSourceEntries) {
3782
+ getRuntime(compileSourceEntries, designSystem) {
3677
3783
  this.assertActive();
3678
3784
  const cached = this.runtimes.get(compileSourceEntries);
3679
3785
  if (cached) return cached;
3680
3786
  const source = compileSourceEntries ? this.currentSource : stripCompiledSourceEntries(this.currentSource);
3681
- const promise = Promise.all([compileTailwindV4Source(source), loadTailwindV4DesignSystem(source, { cache: false })]).then(([compiledSource, designSystem]) => ({
3787
+ const promise = Promise.all([compileTailwindV4Source(source), designSystem ?? loadTailwindV4DesignSystem(source, { cache: false })]).then(([compiledSource, designSystem]) => ({
3682
3788
  compiled: compiledSource.compiled,
3683
3789
  dependencies: compiledSource.dependencies,
3684
3790
  designSystem,
@@ -3690,28 +3796,32 @@ var TailwindGenerationSessionImpl = class {
3690
3796
  });
3691
3797
  return promise;
3692
3798
  }
3693
- async resetRuntime(compileSourceEntries) {
3799
+ async resetRuntime(compileSourceEntries, designSystem) {
3694
3800
  this.runtimes.delete(compileSourceEntries);
3695
- return this.getRuntime(compileSourceEntries);
3801
+ return this.getRuntime(compileSourceEntries, designSystem);
3696
3802
  }
3697
3803
  async generateInternal(request) {
3698
3804
  this.assertActive();
3699
3805
  const options = toGenerateOptions(request);
3700
3806
  const compileSourceEntries = shouldCompileSourceEntries(options);
3701
3807
  let runtime = await this.getRuntime(compileSourceEntries);
3702
- const rawCandidates = await collectRawCandidates(this.currentSource, options, runtime.compiled.root, runtime.compiled.sources);
3808
+ const sourceFiles = [];
3809
+ let rawCandidates = await collectRawCandidates(this.currentSource, options, runtime.compiled.root, runtime.compiled.sources, (files) => sourceFiles.push(...files));
3810
+ if (request.prepareCandidates) rawCandidates = new Set(request.prepareCandidates(rawCandidates));
3703
3811
  const classSet = resolveValidTailwindV4Candidates(runtime.designSystem, rawCandidates, { ...options.bareArbitraryValues === void 0 ? {} : { bareArbitraryValues: options.bareArbitraryValues } });
3704
3812
  const inlineSources = extractTailwindV4InlineSourceCandidates(this.currentSource.css);
3705
3813
  for (const candidate of inlineSources.excluded) classSet.delete(candidate);
3706
3814
  const buildCandidates = new Set(canonicalizeBareArbitraryValueCandidates(classSet, options.bareArbitraryValues));
3707
- if ([...runtime.builtCandidates].some((candidate) => !buildCandidates.has(candidate))) runtime = await this.resetRuntime(compileSourceEntries);
3815
+ if ([...runtime.builtCandidates].some((candidate) => !buildCandidates.has(candidate))) runtime = await this.resetRuntime(compileSourceEntries, runtime.designSystem);
3708
3816
  const css = replaceBareArbitraryValueSelectors(runtime.compiled.build([...buildCandidates]), classSet, options.bareArbitraryValues);
3709
3817
  runtime.builtCandidates = new Set(buildCandidates);
3818
+ for (const dependency of runtime.dependencies) this.moduleDependencies.add(dependency);
3819
+ const dependencies = [.../* @__PURE__ */ new Set([...runtime.dependencies, ...sourceFiles])];
3710
3820
  return {
3711
3821
  css,
3712
3822
  classSet,
3713
3823
  rawCandidates,
3714
- dependencies: Array.from(runtime.dependencies),
3824
+ dependencies,
3715
3825
  sources: runtime.compiled.sources,
3716
3826
  root: runtime.compiled.root
3717
3827
  };
@@ -3729,6 +3839,9 @@ function createTailwindV4Engine(source) {
3729
3839
  const session = createTailwindV4EngineGenerationSession(source);
3730
3840
  return {
3731
3841
  source,
3842
+ dispose() {
3843
+ session.dispose();
3844
+ },
3732
3845
  loadDesignSystem() {
3733
3846
  return session.loadDesignSystem();
3734
3847
  },
@@ -3885,16 +3998,21 @@ async function collectTailwindV4StyleCandidates(options) {
3885
3998
  async function generateTailwindV4Style(options = {}) {
3886
3999
  const source = options.source ?? await resolveTailwindV4Source(createSourceOptions(options));
3887
4000
  const candidates = await collectTailwindV4StyleCandidates(options);
3888
- const result = await createTailwindV4Engine(source).generate({
3889
- candidates,
3890
- ...options.bareArbitraryValues === void 0 ? {} : { bareArbitraryValues: options.bareArbitraryValues },
3891
- ...options.scanSources === void 0 ? {} : { scanSources: options.scanSources }
3892
- });
3893
- return {
3894
- ...result,
3895
- tokens: result.rawCandidates,
3896
- source
3897
- };
4001
+ const engine = createTailwindV4Engine(source);
4002
+ try {
4003
+ const result = await engine.generate({
4004
+ candidates,
4005
+ ...options.bareArbitraryValues === void 0 ? {} : { bareArbitraryValues: options.bareArbitraryValues },
4006
+ ...options.scanSources === void 0 ? {} : { scanSources: options.scanSources }
4007
+ });
4008
+ return {
4009
+ ...result,
4010
+ tokens: result.rawCandidates,
4011
+ source
4012
+ };
4013
+ } finally {
4014
+ engine.dispose?.();
4015
+ }
3898
4016
  }
3899
4017
  //#endregion
3900
4018
  Object.defineProperty(exports, "TAILWIND_V4_AUTO_SOURCE_SCAN_PATTERN", {
@@ -3927,12 +4045,6 @@ Object.defineProperty(exports, "__toESM", {
3927
4045
  return __toESM;
3928
4046
  }
3929
4047
  });
3930
- Object.defineProperty(exports, "buildLineOffsets", {
3931
- enumerable: true,
3932
- get: function() {
3933
- return buildLineOffsets;
3934
- }
3935
- });
3936
4048
  Object.defineProperty(exports, "canonicalizeBareArbitraryValueCandidates", {
3937
4049
  enumerable: true,
3938
4050
  get: function() {
@@ -3999,30 +4111,12 @@ Object.defineProperty(exports, "createTailwindV4SourceExclusionMatcher", {
3999
4111
  return createTailwindV4SourceExclusionMatcher;
4000
4112
  }
4001
4113
  });
4002
- Object.defineProperty(exports, "createTokenLocation", {
4003
- enumerable: true,
4004
- get: function() {
4005
- return createTokenLocation;
4006
- }
4007
- });
4008
4114
  Object.defineProperty(exports, "escapeCssClassName", {
4009
4115
  enumerable: true,
4010
4116
  get: function() {
4011
4117
  return escapeCssClassName;
4012
4118
  }
4013
4119
  });
4014
- Object.defineProperty(exports, "expandTailwindV4SourceEntries", {
4015
- enumerable: true,
4016
- get: function() {
4017
- return expandTailwindV4SourceEntries;
4018
- }
4019
- });
4020
- Object.defineProperty(exports, "expandTailwindV4SourceEntryBraces", {
4021
- enumerable: true,
4022
- get: function() {
4023
- return expandTailwindV4SourceEntryBraces;
4024
- }
4025
- });
4026
4120
  Object.defineProperty(exports, "extractBareArbitraryValueSourceCandidates", {
4027
4121
  enumerable: true,
4028
4122
  get: function() {
@@ -4035,6 +4129,12 @@ Object.defineProperty(exports, "extractBareArbitraryValueSourceCandidatesWithPos
4035
4129
  return extractBareArbitraryValueSourceCandidatesWithPositions;
4036
4130
  }
4037
4131
  });
4132
+ Object.defineProperty(exports, "extractProjectCandidatesWithPositions", {
4133
+ enumerable: true,
4134
+ get: function() {
4135
+ return extractProjectCandidatesWithPositions;
4136
+ }
4137
+ });
4038
4138
  Object.defineProperty(exports, "extractRawCandidates", {
4039
4139
  enumerable: true,
4040
4140
  get: function() {
@@ -4071,22 +4171,16 @@ Object.defineProperty(exports, "generateTailwindV4Style", {
4071
4171
  return generateTailwindV4Style;
4072
4172
  }
4073
4173
  });
4074
- Object.defineProperty(exports, "getOxideModule", {
4075
- enumerable: true,
4076
- get: function() {
4077
- return getOxideModule;
4078
- }
4079
- });
4080
4174
  Object.defineProperty(exports, "getTailwindV4DesignSystemCacheKey", {
4081
4175
  enumerable: true,
4082
4176
  get: function() {
4083
4177
  return getTailwindV4DesignSystemCacheKey;
4084
4178
  }
4085
4179
  });
4086
- Object.defineProperty(exports, "groupTailwindV4SourceEntriesByBase", {
4180
+ Object.defineProperty(exports, "groupTokensByFile", {
4087
4181
  enumerable: true,
4088
4182
  get: function() {
4089
- return groupTailwindV4SourceEntriesByBase;
4183
+ return groupTokensByFile;
4090
4184
  }
4091
4185
  });
4092
4186
  Object.defineProperty(exports, "isBareArbitraryValuesEnabled", {
@@ -4095,12 +4189,6 @@ Object.defineProperty(exports, "isBareArbitraryValuesEnabled", {
4095
4189
  return isBareArbitraryValuesEnabled;
4096
4190
  }
4097
4191
  });
4098
- Object.defineProperty(exports, "isFileExcludedByTailwindV4SourceEntries", {
4099
- enumerable: true,
4100
- get: function() {
4101
- return isFileExcludedByTailwindV4SourceEntries;
4102
- }
4103
- });
4104
4192
  Object.defineProperty(exports, "isFileMatchedByTailwindV4SourceEntries", {
4105
4193
  enumerable: true,
4106
4194
  get: function() {
@@ -4119,18 +4207,6 @@ Object.defineProperty(exports, "loadTailwindV4NodeModule", {
4119
4207
  return loadTailwindV4NodeModule;
4120
4208
  }
4121
4209
  });
4122
- Object.defineProperty(exports, "mergeTailwindV4SourceEntries", {
4123
- enumerable: true,
4124
- get: function() {
4125
- return mergeTailwindV4SourceEntries;
4126
- }
4127
- });
4128
- Object.defineProperty(exports, "normalizeGlobPattern", {
4129
- enumerable: true,
4130
- get: function() {
4131
- return normalizeGlobPattern;
4132
- }
4133
- });
4134
4210
  Object.defineProperty(exports, "normalizeTailwindV4ScannerSources", {
4135
4211
  enumerable: true,
4136
4212
  get: function() {
@@ -4155,10 +4231,10 @@ Object.defineProperty(exports, "resolveBareArbitraryValueCandidate", {
4155
4231
  return resolveBareArbitraryValueCandidate;
4156
4232
  }
4157
4233
  });
4158
- Object.defineProperty(exports, "resolveSourceScanPath", {
4234
+ Object.defineProperty(exports, "resolveProjectSourceFiles", {
4159
4235
  enumerable: true,
4160
4236
  get: function() {
4161
- return resolveSourceScanPath;
4237
+ return resolveProjectSourceFiles;
4162
4238
  }
4163
4239
  });
4164
4240
  Object.defineProperty(exports, "resolveTailwindV4Source", {
@@ -4185,15 +4261,3 @@ Object.defineProperty(exports, "resolveValidTailwindV4Candidates", {
4185
4261
  return resolveValidTailwindV4Candidates;
4186
4262
  }
4187
4263
  });
4188
- Object.defineProperty(exports, "toExtension", {
4189
- enumerable: true,
4190
- get: function() {
4191
- return toExtension;
4192
- }
4193
- });
4194
- Object.defineProperty(exports, "toPosixPath", {
4195
- enumerable: true,
4196
- get: function() {
4197
- return toPosixPath;
4198
- }
4199
- });