tw-migrate 0.5.0 → 0.6.0
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 +2 -0
- package/dist/bin.js +1 -1
- package/dist/index.js +694 -58
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -81,6 +81,8 @@ Everything outside this subset is retained and reported with one of the warning
|
|
|
81
81
|
| `dynamic-html-attribute` | An HTML attribute is not a safely writable quoted literal, so the element cannot be migrated. |
|
|
82
82
|
| `dynamic-template-class` | A dynamic class binding makes a Vue template's class set unprovable, so its scoped rules are retained. |
|
|
83
83
|
| `existing-tailwind-conflict` | A generated utility may conflict with a Tailwind class already on the element. |
|
|
84
|
+
| `font-theme-registration-failed` | A safe Tailwind font theme token could not be registered, so the rule is retained. |
|
|
85
|
+
| `font-theme-registration-required` | The font family requires a Tailwind theme token, but the Tailwind entry cannot be edited, so the rule is retained. |
|
|
84
86
|
| `inferred-preprocessor-source` | A linked CSS file was matched to a uniquely named preprocessor source file. |
|
|
85
87
|
| `media-query-definition-fallback` | The Tailwind entry could not be edited safely, so media behavior was preserved with arbitrary variants. |
|
|
86
88
|
| `module-utilities-conflict` | Utilities generated from different module classes on one element overlap, so their rules are retained. |
|
package/dist/bin.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -64,13 +64,17 @@ function sourceImport(value) {
|
|
|
64
64
|
return object(value) && typeof value.specifier === "string" && typeof value.typeOnly === "boolean" && typeof value.dynamic === "boolean";
|
|
65
65
|
}
|
|
66
66
|
function sourceAnalysisResult(value) {
|
|
67
|
-
return object(value) && Array.isArray(value.imports) && value.imports.every(sourceImport) && Array.isArray(value.staticImports) && value.staticImports.every((item) => typeof item === "string") && Array.isArray(value.defaultImports) && value.defaultImports.every((item) => object(item) && typeof item.source === "string" && typeof item.local === "string") && Array.isArray(value.vueGlobPatterns) && value.vueGlobPatterns.every((item) => typeof item === "string") && Array.isArray(value.useCssModuleLocals) && value.useCssModuleLocals.every((item) => typeof item === "string") && Array.isArray(value.unboundReferences) && value.unboundReferences.every((item) => typeof item === "string") && [
|
|
67
|
+
return object(value) && Array.isArray(value.imports) && value.imports.every(sourceImport) && Array.isArray(value.staticImports) && value.staticImports.every((item) => typeof item === "string") && Array.isArray(value.defaultImports) && value.defaultImports.every((item) => object(item) && typeof item.source === "string" && typeof item.local === "string") && Array.isArray(value.vueGlobPatterns) && value.vueGlobPatterns.every((item) => typeof item === "string") && Array.isArray(value.templatePrefixes) && value.templatePrefixes.every((item) => typeof item === "string") && Array.isArray(value.useCssModuleLocals) && value.useCssModuleLocals.every((item) => typeof item === "string") && Array.isArray(value.unboundReferences) && value.unboundReferences.every((item) => typeof item === "string") && Array.isArray(value.stylesheetLinks) && value.stylesheetLinks.every((item) => typeof item === "string") && Array.isArray(value.customProperties) && value.customProperties.every((item) => typeof item === "string") && [
|
|
68
68
|
value.vueGlobUnverifiable,
|
|
69
69
|
value.hasDynamicImport,
|
|
70
70
|
value.hasVueFallthroughMacro,
|
|
71
71
|
value.usesCssModule,
|
|
72
72
|
value.hasUnboundUseCssModule,
|
|
73
|
-
value.definesRootUseCssModule
|
|
73
|
+
value.definesRootUseCssModule,
|
|
74
|
+
value.stylesheetLinksUnverifiable,
|
|
75
|
+
value.rendersStyleElement,
|
|
76
|
+
value.injectsMarkup,
|
|
77
|
+
value.customPropertiesUnbounded
|
|
74
78
|
].every((item) => typeof item === "boolean");
|
|
75
79
|
}
|
|
76
80
|
const ANALYSIS_CACHE_LIMIT = 4096;
|
|
@@ -84,7 +88,7 @@ function bound(cache) {
|
|
|
84
88
|
}
|
|
85
89
|
}
|
|
86
90
|
function expressionAnalysis(path, source) {
|
|
87
|
-
return decode("expression analysis", binding.expressionAnalysis(path, source), (value) => object(value) && (typeof value.staticString === "string" || value.staticString === null) && (typeof value.vueModuleMember === "string" || value.vueModuleMember === null) && typeof value.usesCssModule === "boolean" && typeof value.referencesUseCssModule === "boolean" && Array.isArray(value.references) && value.references.every((item) => typeof item === "string"));
|
|
91
|
+
return decode("expression analysis", binding.expressionAnalysis(path, source), (value) => object(value) && (typeof value.staticString === "string" || value.staticString === null) && (typeof value.vueModuleMember === "string" || value.vueModuleMember === null) && typeof value.usesCssModule === "boolean" && typeof value.referencesUseCssModule === "boolean" && Array.isArray(value.references) && value.references.every((item) => typeof item === "string") && Array.isArray(value.templatePrefixes) && value.templatePrefixes.every((item) => typeof item === "string"));
|
|
88
92
|
}
|
|
89
93
|
function sourceAnalysis(path, source) {
|
|
90
94
|
const cached = sourceAnalysisCache.get(path);
|
|
@@ -97,6 +101,12 @@ function sourceAnalysis(path, source) {
|
|
|
97
101
|
bound(sourceAnalysisCache);
|
|
98
102
|
return analysis;
|
|
99
103
|
}
|
|
104
|
+
function fontFamilyStack(value) {
|
|
105
|
+
return decode("font family stack", binding.fontFamilyStack(value), (parsed) => parsed === null || object(parsed) && typeof parsed.value === "string" && object(parsed.firstFamily) && typeof parsed.firstFamily.name === "string" && (parsed.firstFamily.kind === "name" || parsed.firstFamily.kind === "generic" || parsed.firstFamily.kind === "css-wide"));
|
|
106
|
+
}
|
|
107
|
+
function compiledShape(css) {
|
|
108
|
+
return decode("compiled shape", binding.compiledShape(css), (value) => object(value) && Array.isArray(value.declarations) && value.declarations.every((item) => object(item) && typeof item.property === "string" && typeof item.important === "boolean" && typeof item.value === "string") && Array.isArray(value.referencedProperties) && value.referencedProperties.every((item) => typeof item === "string"));
|
|
109
|
+
}
|
|
100
110
|
function stylesheetAnalysis(path, source) {
|
|
101
111
|
const cached = stylesheetAnalysisCache.get(path);
|
|
102
112
|
if (cached?.source === source) return cached.analysis;
|
|
@@ -104,7 +114,7 @@ function stylesheetAnalysis(path, source) {
|
|
|
104
114
|
value.unverifiable,
|
|
105
115
|
value.scopeEscapesUnverifiable,
|
|
106
116
|
value.selectorsUnverifiable
|
|
107
|
-
].every((item) => typeof item === "boolean") && Array.isArray(value.scopeEscapes) && value.scopeEscapes.every((item) => typeof item === "string") && Array.isArray(value.scopeShadowCss) && value.scopeShadowCss.every((item) => typeof item === "string") && object(value.themeTokens) && Object.values(value.themeTokens).every((item) => typeof item === "string") && Array.isArray(value.globalAtRuleIdentities) && value.globalAtRuleIdentities.every((item) => typeof item === "string") && typeof value.globalAtRulesUnverifiable === "boolean");
|
|
117
|
+
].every((item) => typeof item === "boolean") && Array.isArray(value.scopeEscapes) && value.scopeEscapes.every((item) => typeof item === "string") && Array.isArray(value.scopeShadowCss) && value.scopeShadowCss.every((item) => typeof item === "string") && object(value.themeTokens) && Object.values(value.themeTokens).every((item) => typeof item === "string") && Array.isArray(value.globalAtRuleIdentities) && value.globalAtRuleIdentities.every((item) => typeof item === "string") && typeof value.globalAtRulesUnverifiable === "boolean" && Array.isArray(value.classNames) && value.classNames.every((item) => typeof item === "string") && typeof value.classReservationsUnbounded === "boolean" && Array.isArray(value.customProperties) && value.customProperties.every((item) => typeof item === "string") && typeof value.customPropertiesUnbounded === "boolean");
|
|
108
118
|
stylesheetAnalysisCache.set(path, {
|
|
109
119
|
source,
|
|
110
120
|
analysis
|
|
@@ -348,6 +358,11 @@ function stylesheetReferenceTargets(importer, reference, styleSources) {
|
|
|
348
358
|
join(target, `index${syntax}`)
|
|
349
359
|
])).filter((path) => styleSources.has(path));
|
|
350
360
|
}
|
|
361
|
+
function localHrefTarget(href) {
|
|
362
|
+
const target = href.split(/[?#]/, 1)[0];
|
|
363
|
+
if (!target || /^[a-z][a-z0-9+.-]*:|^\/\//i.test(target)) return void 0;
|
|
364
|
+
return target;
|
|
365
|
+
}
|
|
351
366
|
function cssImports(path, source) {
|
|
352
367
|
return stylesheetAnalysis(path, source).imports;
|
|
353
368
|
}
|
|
@@ -510,6 +525,9 @@ async function collectFiles(root, include) {
|
|
|
510
525
|
//#endregion
|
|
511
526
|
//#region src/parser/html.ts
|
|
512
527
|
const TEMPLATE_MARKERS = /\{\{|\}\}|\$\{|<%|%>|<#|#>|\[%|%\]/;
|
|
528
|
+
/** Balanced template expressions, for masking a templated attribute value
|
|
529
|
+
* before extracting its static class-prefix tokens. */
|
|
530
|
+
const TEMPLATE_EXPRESSIONS = /\{\{[\s\S]*?\}\}|\$\{[\s\S]*?\}|<%[\s\S]*?%>|<#[\s\S]*?#>|\[%[\s\S]*?%\]/g;
|
|
513
531
|
function parseHtmlSource(path, source) {
|
|
514
532
|
const errors = [];
|
|
515
533
|
const document = parse(source, {
|
|
@@ -524,6 +542,9 @@ function parseHtmlSource(path, source) {
|
|
|
524
542
|
const bases = [];
|
|
525
543
|
const elements = [];
|
|
526
544
|
const dynamicAttributes = [];
|
|
545
|
+
const dynamicClassValues = [];
|
|
546
|
+
const styleAttributeValues = [];
|
|
547
|
+
const handlerTexts = [];
|
|
527
548
|
const scriptTexts = [];
|
|
528
549
|
let hasStyle = false;
|
|
529
550
|
const unquoted = (attribute) => source[attribute.start - 1] !== "\"" && source[attribute.start - 1] !== "'";
|
|
@@ -534,6 +555,9 @@ function parseHtmlSource(path, source) {
|
|
|
534
555
|
}
|
|
535
556
|
if (node.tagName) {
|
|
536
557
|
const attributes = new Map((node.attrs ?? []).map((attribute) => [attribute.name, attribute.value]));
|
|
558
|
+
const style = attributes.get("style");
|
|
559
|
+
if (style !== void 0 && style !== "") styleAttributeValues.push(style);
|
|
560
|
+
for (const [name, value] of attributes) if (name.startsWith("on") && value !== "") handlerTexts.push(value);
|
|
537
561
|
const locations = node.sourceCodeLocation?.attrs;
|
|
538
562
|
if (locations) {
|
|
539
563
|
if (node.tagName === "link" && stylesheetRel(attributes.get("rel")) && !attributes.has("disabled")) {
|
|
@@ -560,15 +584,20 @@ function parseHtmlSource(path, source) {
|
|
|
560
584
|
}
|
|
561
585
|
let classAttribute = locatedAttribute(source, locations.class, attributes.get("class"));
|
|
562
586
|
const idAttribute = locatedAttribute(source, locations.id, attributes.get("id"));
|
|
563
|
-
const
|
|
587
|
+
const unparsedClass = !classAttribute && idAttribute && locations.class ? {
|
|
564
588
|
start: locations.class.startOffset,
|
|
565
589
|
end: locations.class.endOffset
|
|
566
|
-
} : void 0
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
590
|
+
} : void 0;
|
|
591
|
+
const dynamic = unparsedClass ?? [classAttribute, idAttribute].find((attribute) => attribute && (!attribute.writable || TEMPLATE_MARKERS.test(attribute.value))) ?? (classAttribute && unquoted(classAttribute) ? classAttribute : void 0);
|
|
592
|
+
if (dynamic) {
|
|
593
|
+
dynamicAttributes.push({
|
|
594
|
+
start: dynamic.start,
|
|
595
|
+
end: dynamic.end
|
|
596
|
+
});
|
|
597
|
+
const decoded = attributes.get("class");
|
|
598
|
+
const entityBearing = classAttribute !== void 0 && dynamic === classAttribute && classAttribute.writable === false;
|
|
599
|
+
if (dynamic !== idAttribute && decoded !== void 0 && (TEMPLATE_MARKERS.test(decoded) || entityBearing || dynamic === unparsedClass)) dynamicClassValues.push(decoded);
|
|
600
|
+
} else if (classAttribute || idAttribute) {
|
|
572
601
|
if (!classAttribute && idAttribute) {
|
|
573
602
|
const startTag = node.sourceCodeLocation?.startTag;
|
|
574
603
|
if (startTag && source[startTag.endOffset - 1] === ">") {
|
|
@@ -599,6 +628,9 @@ function parseHtmlSource(path, source) {
|
|
|
599
628
|
elements,
|
|
600
629
|
dynamicAttributes
|
|
601
630
|
}),
|
|
631
|
+
dynamicClassValues,
|
|
632
|
+
styleAttributeValues,
|
|
633
|
+
handlerText: handlerTexts.join(";\n"),
|
|
602
634
|
scriptText: scriptTexts.join("\n"),
|
|
603
635
|
hasStyle
|
|
604
636
|
};
|
|
@@ -778,8 +810,10 @@ function scanProof(options) {
|
|
|
778
810
|
} else if (directive.kind === "import" && directive.tailwind) {
|
|
779
811
|
if (directive.sourceUnreadable) return null;
|
|
780
812
|
if (directive.source === "none") automaticDisabled = true;
|
|
781
|
-
else if (directive.source !== null)
|
|
782
|
-
|
|
813
|
+
else if (directive.source !== null) {
|
|
814
|
+
if (coversChild(directive.source)) literal = true;
|
|
815
|
+
else automaticDisabled = true;
|
|
816
|
+
}
|
|
783
817
|
}
|
|
784
818
|
}
|
|
785
819
|
if (literal) return "literal";
|
|
@@ -965,8 +999,8 @@ function htmlLinksEntry(file, entry) {
|
|
|
965
999
|
return parsed.links.some((link) => {
|
|
966
1000
|
const media = link.media.trim().toLowerCase();
|
|
967
1001
|
if (media !== "" && media !== "all") return false;
|
|
968
|
-
const href = link.href
|
|
969
|
-
if (
|
|
1002
|
+
const href = localHrefTarget(link.href);
|
|
1003
|
+
if (href === void 0) return false;
|
|
970
1004
|
return resolve(dirname(file.path), href) === entry;
|
|
971
1005
|
});
|
|
972
1006
|
} catch {
|
|
@@ -1142,8 +1176,8 @@ function addInferredPreprocessorContext(state) {
|
|
|
1142
1176
|
return true;
|
|
1143
1177
|
}
|
|
1144
1178
|
function localHtmlReference(packageRoot, base, reference) {
|
|
1145
|
-
const path = reference
|
|
1146
|
-
if (
|
|
1179
|
+
const path = localHrefTarget(reference);
|
|
1180
|
+
if (path === void 0) return void 0;
|
|
1147
1181
|
let decoded;
|
|
1148
1182
|
try {
|
|
1149
1183
|
decoded = decodeURIComponent(path);
|
|
@@ -1179,7 +1213,7 @@ function htmlWarning(code, file, start, end, message) {
|
|
|
1179
1213
|
//#endregion
|
|
1180
1214
|
//#region src/tailwind.ts
|
|
1181
1215
|
function findTailwindEntries(stylePaths, styleSources) {
|
|
1182
|
-
return stylePaths.filter((path) => extname(path) === ".css" &&
|
|
1216
|
+
return stylePaths.filter((path) => extname(path) === ".css" && importEntries(styleSources.get(path) ?? "").some(({ specifier }) => specifier === "tailwindcss" || specifier.startsWith("tailwindcss/")));
|
|
1183
1217
|
}
|
|
1184
1218
|
function selectTailwindEntry(entries, configuredPath) {
|
|
1185
1219
|
if (configuredPath) return configuredPath;
|
|
@@ -1226,9 +1260,10 @@ async function loadTailwind(packageRoot, tailwindCss, snapshots, workspaceRoot)
|
|
|
1226
1260
|
};
|
|
1227
1261
|
const defaultTheme = await readFile(join(dirname(packagePath), "theme.css"), "utf8");
|
|
1228
1262
|
const graphSources = [];
|
|
1263
|
+
const referenceTokens = /* @__PURE__ */ new Set();
|
|
1229
1264
|
const themeTokens = {
|
|
1230
1265
|
...extractThemeTokens("tailwind-default.theme.css", defaultTheme),
|
|
1231
|
-
...await extractThemeTokensFromGraph(tailwindCss, css, base, loadStylesheet, graphSources)
|
|
1266
|
+
...await extractThemeTokensFromGraph(tailwindCss, css, base, loadStylesheet, graphSources, referenceTokens)
|
|
1232
1267
|
};
|
|
1233
1268
|
graphSources.push({
|
|
1234
1269
|
path: tailwindCss,
|
|
@@ -1249,6 +1284,7 @@ async function loadTailwind(packageRoot, tailwindCss, snapshots, workspaceRoot)
|
|
|
1249
1284
|
css,
|
|
1250
1285
|
path: tailwindCss,
|
|
1251
1286
|
themeTokens,
|
|
1287
|
+
referenceTokens,
|
|
1252
1288
|
graphSources,
|
|
1253
1289
|
loadWith
|
|
1254
1290
|
};
|
|
@@ -1256,12 +1292,15 @@ async function loadTailwind(packageRoot, tailwindCss, snapshots, workspaceRoot)
|
|
|
1256
1292
|
function extractThemeTokens(key, css) {
|
|
1257
1293
|
return stylesheetAnalysis(key, css).themeTokens;
|
|
1258
1294
|
}
|
|
1259
|
-
function
|
|
1260
|
-
return (cssDirectives$1(css) ?? []).flatMap((directive) => directive !== null && typeof directive === "object" && "kind" in directive && directive.kind === "import" && "specifier" in directive && typeof directive.specifier === "string" ? [
|
|
1295
|
+
function importEntries(css) {
|
|
1296
|
+
return (cssDirectives$1(css) ?? []).flatMap((directive) => directive !== null && typeof directive === "object" && "kind" in directive && directive.kind === "import" && "specifier" in directive && typeof directive.specifier === "string" ? [{
|
|
1297
|
+
specifier: directive.specifier,
|
|
1298
|
+
reference: "reference" in directive && directive.reference === true
|
|
1299
|
+
}] : []);
|
|
1261
1300
|
}
|
|
1262
|
-
async function extractThemeTokensFromGraph(identity, css, base, loadStylesheet, graphSources, seen = /* @__PURE__ */ new Set()) {
|
|
1301
|
+
async function extractThemeTokensFromGraph(identity, css, base, loadStylesheet, graphSources, referenceTokens, inReference = false, seen = /* @__PURE__ */ new Set()) {
|
|
1263
1302
|
const tokens = {};
|
|
1264
|
-
for (const spec of
|
|
1303
|
+
for (const { specifier: spec, reference } of importEntries(css)) {
|
|
1265
1304
|
const key = `${base}\0${spec}`;
|
|
1266
1305
|
if (seen.has(key)) continue;
|
|
1267
1306
|
seen.add(key);
|
|
@@ -1270,15 +1309,301 @@ async function extractThemeTokensFromGraph(identity, css, base, loadStylesheet,
|
|
|
1270
1309
|
path: key,
|
|
1271
1310
|
source: loaded.content
|
|
1272
1311
|
});
|
|
1273
|
-
Object.assign(tokens, await extractThemeTokensFromGraph(key, loaded.content, loaded.base, loadStylesheet, graphSources, seen));
|
|
1312
|
+
Object.assign(tokens, await extractThemeTokensFromGraph(key, loaded.content, loaded.base, loadStylesheet, graphSources, referenceTokens, inReference || reference, seen));
|
|
1274
1313
|
}
|
|
1275
|
-
|
|
1314
|
+
const own = extractThemeTokens(`${identity}.theme.css`, css);
|
|
1315
|
+
if (inReference) for (const token of Object.keys(own)) referenceTokens.add(token);
|
|
1316
|
+
return Object.assign(tokens, own);
|
|
1276
1317
|
}
|
|
1277
1318
|
function invalidCandidates(system, candidates) {
|
|
1278
1319
|
const generated = system.candidatesToCss(candidates);
|
|
1279
1320
|
return candidates.filter((_, index) => generated[index] === null);
|
|
1280
1321
|
}
|
|
1281
1322
|
//#endregion
|
|
1323
|
+
//#region src/plan/canonicalize.ts
|
|
1324
|
+
const caches = /* @__PURE__ */ new WeakMap();
|
|
1325
|
+
function canonicalCandidate(system, candidate) {
|
|
1326
|
+
if (typeof system.canonicalizeCandidates !== "function") return null;
|
|
1327
|
+
let cache = caches.get(system);
|
|
1328
|
+
if (cache === void 0) {
|
|
1329
|
+
cache = /* @__PURE__ */ new Map();
|
|
1330
|
+
caches.set(system, cache);
|
|
1331
|
+
}
|
|
1332
|
+
const cached = cache.get(candidate);
|
|
1333
|
+
if (cached !== void 0) return cached;
|
|
1334
|
+
let canonical;
|
|
1335
|
+
try {
|
|
1336
|
+
const results = system.canonicalizeCandidates([candidate]);
|
|
1337
|
+
canonical = results.length === 1 && typeof results[0] === "string" && results[0] !== candidate ? results[0] : null;
|
|
1338
|
+
} catch {
|
|
1339
|
+
canonical = null;
|
|
1340
|
+
}
|
|
1341
|
+
cache.set(candidate, canonical);
|
|
1342
|
+
return canonical;
|
|
1343
|
+
}
|
|
1344
|
+
function spellingReservations(styleSources, resolvedExtras, resolvedImports = /* @__PURE__ */ new Set()) {
|
|
1345
|
+
const reservations = {
|
|
1346
|
+
names: /* @__PURE__ */ new Set(),
|
|
1347
|
+
prefixes: /* @__PURE__ */ new Set(),
|
|
1348
|
+
unbounded: false,
|
|
1349
|
+
properties: /* @__PURE__ */ new Set(),
|
|
1350
|
+
propertiesUnbounded: false
|
|
1351
|
+
};
|
|
1352
|
+
for (const [path, source] of styleSources) scanStylesheetReservations(path, source, styleSources, reservations, resolvedImports);
|
|
1353
|
+
for (const [path, source] of resolvedExtras) try {
|
|
1354
|
+
const analysis = stylesheetAnalysis(path, source);
|
|
1355
|
+
for (const name of analysis.classNames) reservations.names.add(name);
|
|
1356
|
+
if (analysis.classReservationsUnbounded) reservations.unbounded = true;
|
|
1357
|
+
for (const property of analysis.customProperties) reservations.properties.add(property);
|
|
1358
|
+
if (analysis.customPropertiesUnbounded) reservations.propertiesUnbounded = true;
|
|
1359
|
+
if (entryDirectiveHazard(source)) reservations.unbounded = true;
|
|
1360
|
+
} catch {
|
|
1361
|
+
reservations.unbounded = true;
|
|
1362
|
+
}
|
|
1363
|
+
return reservations;
|
|
1364
|
+
}
|
|
1365
|
+
function scanStylesheetReservations(path, source, styleSources, reservations, resolvedImports = /* @__PURE__ */ new Set()) {
|
|
1366
|
+
try {
|
|
1367
|
+
const analysis = stylesheetAnalysis(path, source);
|
|
1368
|
+
for (const name of analysis.classNames) reservations.names.add(name);
|
|
1369
|
+
if (analysis.classReservationsUnbounded) reservations.unbounded = true;
|
|
1370
|
+
for (const property of analysis.customProperties) reservations.properties.add(property);
|
|
1371
|
+
if (analysis.customPropertiesUnbounded) reservations.propertiesUnbounded = true;
|
|
1372
|
+
if (analysis.unverifiable) reservations.unbounded = true;
|
|
1373
|
+
for (const reference of analysis.references) {
|
|
1374
|
+
if (reference === "tailwindcss" || reference.startsWith("tailwindcss/")) continue;
|
|
1375
|
+
if (reference.startsWith("sass:")) continue;
|
|
1376
|
+
if (resolvedImports.has(`${dirname(path)}\0${reference}`) || styleSources.has(reference)) continue;
|
|
1377
|
+
if (stylesheetReferenceTargets(path, reference, styleSources).length === 0) reservations.unbounded = true;
|
|
1378
|
+
}
|
|
1379
|
+
} catch {
|
|
1380
|
+
reservations.unbounded = true;
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
function entryDirectiveHazard(source) {
|
|
1384
|
+
const directives = cssDirectives$1(source);
|
|
1385
|
+
if (directives === null) return true;
|
|
1386
|
+
return directives.some((directive) => {
|
|
1387
|
+
if (directive === null || typeof directive !== "object" || !("kind" in directive)) return false;
|
|
1388
|
+
if (directive.kind === "import") return "source" in directive && directive.source !== null || "sourceUnreadable" in directive && directive.sourceUnreadable === true;
|
|
1389
|
+
if (directive.kind === "source") return "not" in directive && directive.not === true || "unreadable" in directive && directive.unreadable === true;
|
|
1390
|
+
return directive.kind === "other" && "name" in directive && (directive.name === "plugin" || directive.name === "config");
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
function scanInlineStyleReservations(key, value, reservations) {
|
|
1394
|
+
try {
|
|
1395
|
+
const analysis = stylesheetAnalysis(key, `.x{${value}}`);
|
|
1396
|
+
for (const property of analysis.customProperties) reservations.properties.add(property);
|
|
1397
|
+
if (analysis.customPropertiesUnbounded) reservations.propertiesUnbounded = true;
|
|
1398
|
+
} catch {
|
|
1399
|
+
reservations.propertiesUnbounded = true;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
function reservedSpelling(reservations, spelling) {
|
|
1403
|
+
if (reservations.names.has(spelling)) return true;
|
|
1404
|
+
for (const prefix of reservations.prefixes) if (spelling.startsWith(prefix)) return true;
|
|
1405
|
+
return false;
|
|
1406
|
+
}
|
|
1407
|
+
function utilitySegment(candidate) {
|
|
1408
|
+
let depth = 0;
|
|
1409
|
+
let start = 0;
|
|
1410
|
+
for (let index = 0; index < candidate.length; index += 1) {
|
|
1411
|
+
const character = candidate[index];
|
|
1412
|
+
if (character === "[" || character === "(") depth += 1;
|
|
1413
|
+
else if (character === "]" || character === ")") depth -= 1;
|
|
1414
|
+
else if (character === ":" && depth === 0) start = index + 1;
|
|
1415
|
+
}
|
|
1416
|
+
return candidate.slice(start);
|
|
1417
|
+
}
|
|
1418
|
+
function sameShape(left, right) {
|
|
1419
|
+
return left.length === right.length && left.every((declaration, index) => declaration.property === right[index].property && declaration.important === right[index].important);
|
|
1420
|
+
}
|
|
1421
|
+
function acceptedCandidateAliases(system, probes, reservations) {
|
|
1422
|
+
const aliases = {};
|
|
1423
|
+
if (reservations.unbounded) return aliases;
|
|
1424
|
+
const prefix = system.theme.prefix;
|
|
1425
|
+
for (const probe of new Set(probes)) {
|
|
1426
|
+
const canonical = canonicalCandidate(system, probe);
|
|
1427
|
+
if (canonical === null) continue;
|
|
1428
|
+
const unprefixed = prefix !== null && canonical.startsWith(`${prefix}:`) ? canonical.slice(prefix.length + 1) : null;
|
|
1429
|
+
const segment = utilitySegment(canonical).replace(/!$/, "");
|
|
1430
|
+
if (segment.includes("[") || reservedSpelling(reservations, canonical) || unprefixed !== null && reservedSpelling(reservations, unprefixed) || reservedSpelling(reservations, segment)) continue;
|
|
1431
|
+
const [compiledProbe, compiledCanonical] = system.candidatesToCss([probe, canonical]);
|
|
1432
|
+
if (compiledProbe == null || compiledCanonical == null) continue;
|
|
1433
|
+
let probeShape;
|
|
1434
|
+
let canonicalShape;
|
|
1435
|
+
try {
|
|
1436
|
+
probeShape = compiledShape(compiledProbe);
|
|
1437
|
+
canonicalShape = compiledShape(compiledCanonical);
|
|
1438
|
+
} catch {
|
|
1439
|
+
continue;
|
|
1440
|
+
}
|
|
1441
|
+
if (canonicalShape.referencedProperties.length > 0 || probeShape.referencedProperties.length > 0 || !sameShape(probeShape.declarations, canonicalShape.declarations)) continue;
|
|
1442
|
+
aliases[probe] = canonical;
|
|
1443
|
+
}
|
|
1444
|
+
return aliases;
|
|
1445
|
+
}
|
|
1446
|
+
//#endregion
|
|
1447
|
+
//#region src/plan/fonts.ts
|
|
1448
|
+
function fontTokenName(family) {
|
|
1449
|
+
const collapsed = family.normalize("NFC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "-").replace(/^-+|-+$/g, "");
|
|
1450
|
+
if (collapsed === "") return "family";
|
|
1451
|
+
return /^\p{N}/u.test(collapsed) ? `family-${collapsed}` : collapsed;
|
|
1452
|
+
}
|
|
1453
|
+
function aliasedCandidate(candidate, utility) {
|
|
1454
|
+
const segment = utilitySegment(candidate);
|
|
1455
|
+
const important = segment.endsWith("!") ? "!" : "";
|
|
1456
|
+
return candidate.slice(0, candidate.length - segment.length) + utility + important;
|
|
1457
|
+
}
|
|
1458
|
+
function appendFontTheme(css, tokens) {
|
|
1459
|
+
const names = Object.keys(tokens).sort();
|
|
1460
|
+
if (names.length === 0) return css;
|
|
1461
|
+
let output = css;
|
|
1462
|
+
if (!output.endsWith("\n")) output += "\n";
|
|
1463
|
+
if (!output.endsWith("\n\n")) output += "\n";
|
|
1464
|
+
output += `@theme {\n${names.map((name) => ` --font-${name}: ${tokens[name]};\n`).join("")}}\n`;
|
|
1465
|
+
return output;
|
|
1466
|
+
}
|
|
1467
|
+
function fontShapesAccept(system, probeCandidate, aliased, reservations, backingToken) {
|
|
1468
|
+
const [compiledProbe, compiledAliased] = system.candidatesToCss([probeCandidate, aliased]);
|
|
1469
|
+
if (compiledProbe == null || compiledAliased == null) return false;
|
|
1470
|
+
let probeShape;
|
|
1471
|
+
let aliasedShape;
|
|
1472
|
+
try {
|
|
1473
|
+
probeShape = compiledShape(compiledProbe);
|
|
1474
|
+
aliasedShape = compiledShape(compiledAliased);
|
|
1475
|
+
} catch {
|
|
1476
|
+
return false;
|
|
1477
|
+
}
|
|
1478
|
+
if (probeShape.referencedProperties.length > 0) return false;
|
|
1479
|
+
if (aliasedShape.referencedProperties.some((property) => reservations.properties.has(property))) return false;
|
|
1480
|
+
if (backingToken !== void 0) {
|
|
1481
|
+
if (!aliasedShape.declarations.some((declaration) => declaration.property === "font-family" && /^var\(--[\w-]+\)$/.test(declaration.value) && (declaration.value === `var(--${backingToken})` || declaration.value.endsWith(`-${backingToken})`)))) return false;
|
|
1482
|
+
}
|
|
1483
|
+
return sameShape(probeShape.declarations, aliasedShape.declarations);
|
|
1484
|
+
}
|
|
1485
|
+
async function registerFontTokens(options) {
|
|
1486
|
+
const { system, reservations } = options;
|
|
1487
|
+
if (reservations.unbounded || reservations.propertiesUnbounded) return {
|
|
1488
|
+
aliases: {},
|
|
1489
|
+
tokens: {},
|
|
1490
|
+
failures: /* @__PURE__ */ new Set()
|
|
1491
|
+
};
|
|
1492
|
+
const prefix = system.theme.prefix;
|
|
1493
|
+
const aliases = {};
|
|
1494
|
+
const failures = /* @__PURE__ */ new Set();
|
|
1495
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1496
|
+
const allocatedByStack = /* @__PURE__ */ new Map();
|
|
1497
|
+
const proposals = [];
|
|
1498
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
1499
|
+
const tokenStacks = fontTokenStacks(options.themeTokens, options.referenceTokens);
|
|
1500
|
+
for (const probe of options.probes) {
|
|
1501
|
+
if (seen.has(probe.candidate)) continue;
|
|
1502
|
+
seen.add(probe.candidate);
|
|
1503
|
+
if (probe.firstFamily.kind !== "name") continue;
|
|
1504
|
+
if (probe.candidate in options.existingAliases) continue;
|
|
1505
|
+
let reused = false;
|
|
1506
|
+
for (const token of matchingFontTokens(tokenStacks, probe.value)) {
|
|
1507
|
+
const aliased = aliasedCandidate(probe.candidate, token);
|
|
1508
|
+
if (reservedSpelling(reservations, token) || reservedSpelling(reservations, aliased)) continue;
|
|
1509
|
+
const reuseName = token.replace(/^font-/, "");
|
|
1510
|
+
const owner = options.globalAllocations.get(reuseName);
|
|
1511
|
+
if (owner !== void 0 && (owner === null || owner !== probe.value)) continue;
|
|
1512
|
+
if (fontShapesAccept(system, probe.candidate, aliased, reservations, token)) {
|
|
1513
|
+
aliases[probe.candidate] = aliased;
|
|
1514
|
+
options.globalAllocations.set(reuseName, probe.value);
|
|
1515
|
+
reused = true;
|
|
1516
|
+
break;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
if (reused) continue;
|
|
1520
|
+
if (!options.generate) continue;
|
|
1521
|
+
const stackKey = probe.value;
|
|
1522
|
+
const shared = allocatedByStack.get(stackKey);
|
|
1523
|
+
if (shared !== void 0) {
|
|
1524
|
+
proposals.push({
|
|
1525
|
+
probe,
|
|
1526
|
+
name: shared
|
|
1527
|
+
});
|
|
1528
|
+
continue;
|
|
1529
|
+
}
|
|
1530
|
+
const base = fontTokenName(probe.firstFamily.name);
|
|
1531
|
+
let allocated = false;
|
|
1532
|
+
for (let attempt = 1; attempt <= 100; attempt += 1) {
|
|
1533
|
+
const name = attempt === 1 ? base : `${base}-${attempt}`;
|
|
1534
|
+
if (claimed.has(name)) continue;
|
|
1535
|
+
if (reservations.properties.has(`--font-${name}`)) continue;
|
|
1536
|
+
if (prefix !== null && reservations.properties.has(`--${prefix}-font-${name}`)) continue;
|
|
1537
|
+
if (reservedSpelling(reservations, `font-${name}`)) continue;
|
|
1538
|
+
if (options.mentionedSegments.has(`font-${name}`)) continue;
|
|
1539
|
+
const global = options.globalAllocations.get(name);
|
|
1540
|
+
if (global !== void 0 && (global === null || global !== probe.value)) continue;
|
|
1541
|
+
const compiled = prefix !== null ? `${prefix}:font-${name}` : `font-${name}`;
|
|
1542
|
+
if (system.candidatesToCss([compiled])[0] !== null) continue;
|
|
1543
|
+
claimed.add(name);
|
|
1544
|
+
allocatedByStack.set(stackKey, name);
|
|
1545
|
+
proposals.push({
|
|
1546
|
+
probe,
|
|
1547
|
+
name
|
|
1548
|
+
});
|
|
1549
|
+
allocated = true;
|
|
1550
|
+
break;
|
|
1551
|
+
}
|
|
1552
|
+
if (!allocated) failures.add(probe.candidate);
|
|
1553
|
+
}
|
|
1554
|
+
if (proposals.length === 0) return {
|
|
1555
|
+
aliases,
|
|
1556
|
+
tokens: {},
|
|
1557
|
+
failures
|
|
1558
|
+
};
|
|
1559
|
+
const tokens = {};
|
|
1560
|
+
for (const { probe, name } of proposals) tokens[name] = probe.value;
|
|
1561
|
+
let fontSystem;
|
|
1562
|
+
try {
|
|
1563
|
+
fontSystem = await options.loadWith(appendFontTheme(options.entryCss, tokens));
|
|
1564
|
+
} catch (error) {
|
|
1565
|
+
if (isIntegrityError(error)) throw error;
|
|
1566
|
+
for (const { probe } of proposals) failures.add(probe.candidate);
|
|
1567
|
+
return {
|
|
1568
|
+
aliases,
|
|
1569
|
+
tokens: {},
|
|
1570
|
+
failures
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
const before = system.candidatesToCss(options.corpus);
|
|
1574
|
+
const after = fontSystem.candidatesToCss(options.corpus);
|
|
1575
|
+
const corpusChanged = options.corpus.some((candidate, index) => before[index] !== after[index]);
|
|
1576
|
+
const accepted = {};
|
|
1577
|
+
for (const { probe, name } of proposals) {
|
|
1578
|
+
const canonical = corpusChanged ? null : canonicalCandidate(fontSystem, probe.candidate);
|
|
1579
|
+
const expected = aliasedCandidate(probe.candidate, `font-${name}`);
|
|
1580
|
+
if (canonical !== expected || !fontShapesAccept(fontSystem, probe.candidate, expected, reservations)) {
|
|
1581
|
+
failures.add(probe.candidate);
|
|
1582
|
+
continue;
|
|
1583
|
+
}
|
|
1584
|
+
aliases[probe.candidate] = expected;
|
|
1585
|
+
accepted[name] = probe.value;
|
|
1586
|
+
}
|
|
1587
|
+
return {
|
|
1588
|
+
aliases,
|
|
1589
|
+
tokens: accepted,
|
|
1590
|
+
failures
|
|
1591
|
+
};
|
|
1592
|
+
}
|
|
1593
|
+
function fontTokenStacks(themeTokens, referenceTokens = /* @__PURE__ */ new Set()) {
|
|
1594
|
+
const stacks = /* @__PURE__ */ new Map();
|
|
1595
|
+
for (const [token, value] of Object.entries(themeTokens)) {
|
|
1596
|
+
if (!token.startsWith("font-")) continue;
|
|
1597
|
+
if (referenceTokens.has(token)) continue;
|
|
1598
|
+
const parsed = fontFamilyStack(value);
|
|
1599
|
+
if (parsed !== null) stacks.set(token, parsed.value);
|
|
1600
|
+
}
|
|
1601
|
+
return stacks;
|
|
1602
|
+
}
|
|
1603
|
+
function matchingFontTokens(stacks, normalizedStack) {
|
|
1604
|
+
return [...stacks].filter(([, stack]) => stack === normalizedStack).map(([token]) => token).sort();
|
|
1605
|
+
}
|
|
1606
|
+
//#endregion
|
|
1282
1607
|
//#region src/plan/media.ts
|
|
1283
1608
|
const GENERATED_NAMESPACE = "twm-media";
|
|
1284
1609
|
/**
|
|
@@ -1535,7 +1860,9 @@ function analyzeVueSource(compiler, path, source) {
|
|
|
1535
1860
|
const { descriptor, errors } = compiler.parse(source, { filename: path });
|
|
1536
1861
|
const styleRanges = descriptor.styles.map((style) => ({
|
|
1537
1862
|
start: style.loc.start.offset,
|
|
1538
|
-
end: style.loc.end.offset
|
|
1863
|
+
end: style.loc.end.offset,
|
|
1864
|
+
lang: style.lang ?? "css",
|
|
1865
|
+
...style.src !== void 0 ? { src: style.src } : {}
|
|
1539
1866
|
}));
|
|
1540
1867
|
const byteRanges = (offset) => ({
|
|
1541
1868
|
warnings: warnings.map((warning) => ({
|
|
@@ -1544,6 +1871,7 @@ function analyzeVueSource(compiler, path, source) {
|
|
|
1544
1871
|
end: offset(warning.end)
|
|
1545
1872
|
})),
|
|
1546
1873
|
styleRanges: styleRanges.map((range) => ({
|
|
1874
|
+
...range,
|
|
1547
1875
|
start: offset(range.start),
|
|
1548
1876
|
end: offset(range.end)
|
|
1549
1877
|
}))
|
|
@@ -1684,6 +2012,14 @@ function analyzeVueSource(compiler, path, source) {
|
|
|
1684
2012
|
moduleClosureBroken: false,
|
|
1685
2013
|
referencesUseCssModule: false,
|
|
1686
2014
|
expressionReferences: /* @__PURE__ */ new Set(),
|
|
2015
|
+
templatePrefixes: /* @__PURE__ */ new Set(),
|
|
2016
|
+
entityClassTokens: [],
|
|
2017
|
+
styleAttributeValues: [],
|
|
2018
|
+
styleAttributesUnverifiable: false,
|
|
2019
|
+
customProperties: /* @__PURE__ */ new Set(),
|
|
2020
|
+
customPropertiesUnbounded: false,
|
|
2021
|
+
stylesheetLinks: [],
|
|
2022
|
+
stylesheetLinksUnverifiable: false,
|
|
1687
2023
|
expressionPath: scriptLang === "ts" || scriptLang === "tsx" ? "Component.ts" : "Component.js"
|
|
1688
2024
|
};
|
|
1689
2025
|
visitTemplateNode(source, template.ast, state);
|
|
@@ -1699,8 +2035,11 @@ function analyzeVueSource(compiler, path, source) {
|
|
|
1699
2035
|
let setupAnalysis;
|
|
1700
2036
|
let scriptUsesCssModule = false;
|
|
1701
2037
|
let scriptHasDynamicImport = false;
|
|
2038
|
+
const scriptCustomProperties = [];
|
|
2039
|
+
let scriptCustomPropertiesUnbounded = false;
|
|
1702
2040
|
const scriptVueReferences = [];
|
|
1703
2041
|
const scriptVueGlobPatterns = [];
|
|
2042
|
+
const scriptTemplatePrefixes = [];
|
|
1704
2043
|
let scriptVueGlobUnverifiable = false;
|
|
1705
2044
|
const styleImports = [descriptor.script, descriptor.scriptSetup].flatMap((script) => {
|
|
1706
2045
|
if (!script) return [];
|
|
@@ -1716,6 +2055,9 @@ function analyzeVueSource(compiler, path, source) {
|
|
|
1716
2055
|
scriptImports.push(...analysis.imports);
|
|
1717
2056
|
scriptVueReferences.push(...analysis.imports.filter((record) => !record.typeOnly).map((record) => record.specifier));
|
|
1718
2057
|
scriptVueGlobPatterns.push(...analysis.vueGlobPatterns);
|
|
2058
|
+
scriptTemplatePrefixes.push(...analysis.templatePrefixes);
|
|
2059
|
+
scriptCustomProperties.push(...analysis.customProperties);
|
|
2060
|
+
if (analysis.customPropertiesUnbounded) scriptCustomPropertiesUnbounded = true;
|
|
1719
2061
|
if (analysis.vueGlobUnverifiable) scriptVueGlobUnverifiable = true;
|
|
1720
2062
|
return analysis.staticImports;
|
|
1721
2063
|
} catch {
|
|
@@ -1790,6 +2132,14 @@ function analyzeVueSource(compiler, path, source) {
|
|
|
1790
2132
|
scriptVueReferences,
|
|
1791
2133
|
scriptVueGlobPatterns,
|
|
1792
2134
|
scriptVueGlobUnverifiable,
|
|
2135
|
+
templatePrefixes: [.../* @__PURE__ */ new Set([...state.templatePrefixes, ...scriptTemplatePrefixes])],
|
|
2136
|
+
templateClassTokens: [...new Set(state.entityClassTokens)],
|
|
2137
|
+
templateStyleValues: state.styleAttributeValues,
|
|
2138
|
+
templateStylesUnverifiable: state.styleAttributesUnverifiable,
|
|
2139
|
+
scriptCustomProperties: [...scriptCustomProperties, ...state.customProperties],
|
|
2140
|
+
scriptCustomPropertiesUnbounded: scriptCustomPropertiesUnbounded || state.customPropertiesUnbounded,
|
|
2141
|
+
templateStylesheetLinks: state.stylesheetLinks,
|
|
2142
|
+
templateStylesheetLinksUnverifiable: state.stylesheetLinksUnverifiable,
|
|
1793
2143
|
hasOpaqueStyleBlocks: opaqueStyleBlocks,
|
|
1794
2144
|
styleBlockImports: styleBlockImports.map((entry) => ({
|
|
1795
2145
|
...entry,
|
|
@@ -1822,6 +2172,29 @@ function verifyVueSource(compiler, path, source, includeUnscoped = false) {
|
|
|
1822
2172
|
function visitTemplateNode(source, node, state) {
|
|
1823
2173
|
if (node.type === NODE_ELEMENT) {
|
|
1824
2174
|
if (node.tagType === TAG_SLOT) state.hasSlot = true;
|
|
2175
|
+
for (const prop of node.props ?? []) {
|
|
2176
|
+
if (prop.type === PROP_ATTRIBUTE && prop.name === "style" && prop.value?.content) state.styleAttributeValues.push(prop.value.content);
|
|
2177
|
+
if (prop.type === PROP_DIRECTIVE && prop.name === "bind") {
|
|
2178
|
+
const argument = prop.arg?.isStatic === true ? prop.arg.content : void 0;
|
|
2179
|
+
if (prop.arg === void 0 || argument === void 0 || argument === "style") state.styleAttributesUnverifiable = true;
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
if (node.tag === "link" && node.tagType === TAG_ELEMENT) {
|
|
2183
|
+
let rel;
|
|
2184
|
+
let href;
|
|
2185
|
+
let dynamic = false;
|
|
2186
|
+
for (const prop of node.props ?? []) if (prop.type === PROP_ATTRIBUTE) {
|
|
2187
|
+
if (prop.name === "rel") rel = prop.value?.content ?? "";
|
|
2188
|
+
if (prop.name === "href") href = prop.value?.content ?? "";
|
|
2189
|
+
} else if (prop.type === PROP_DIRECTIVE && prop.name === "bind") {
|
|
2190
|
+
const argument = prop.arg?.isStatic ? prop.arg.content : void 0;
|
|
2191
|
+
if (argument === void 0 || argument === "rel" || argument === "href") dynamic = true;
|
|
2192
|
+
}
|
|
2193
|
+
if ((rel?.toLowerCase().split(/\s+/) ?? []).includes("stylesheet") || rel === void 0 && dynamic) {
|
|
2194
|
+
if (href !== void 0 && !dynamic) state.stylesheetLinks.push(href);
|
|
2195
|
+
else state.stylesheetLinksUnverifiable = true;
|
|
2196
|
+
} else if (dynamic) state.stylesheetLinksUnverifiable = true;
|
|
2197
|
+
}
|
|
1825
2198
|
const bindingClasses = [];
|
|
1826
2199
|
let classOpaque = false;
|
|
1827
2200
|
let moduleBinding;
|
|
@@ -1851,11 +2224,15 @@ function visitTemplateNode(source, node, state) {
|
|
|
1851
2224
|
if (!provenModuleExpression && expression?.usesCssModule) state.moduleClosureBroken = true;
|
|
1852
2225
|
if (expression?.referencesUseCssModule) state.referencesUseCssModule = true;
|
|
1853
2226
|
for (const name of expression?.references ?? []) state.expressionReferences.add(name);
|
|
2227
|
+
for (const prefix of expression?.templatePrefixes ?? []) state.templatePrefixes.add(prefix);
|
|
2228
|
+
for (const name of expression?.customProperties ?? []) state.customProperties.add(name);
|
|
2229
|
+
if (expression?.customPropertiesUnbounded) state.customPropertiesUnbounded = true;
|
|
1854
2230
|
if (prop.arg && !prop.arg.isStatic && prop.arg.content) {
|
|
1855
2231
|
const argExpression = templateExpression(state.expressionPath, prop.arg.content);
|
|
1856
2232
|
state.moduleClosureBroken ||= argExpression?.usesCssModule ?? true;
|
|
1857
2233
|
state.referencesUseCssModule ||= argExpression?.referencesUseCssModule ?? false;
|
|
1858
2234
|
for (const name of argExpression?.references ?? []) state.expressionReferences.add(name);
|
|
2235
|
+
for (const prefix of argExpression?.templatePrefixes ?? []) state.templatePrefixes.add(prefix);
|
|
1859
2236
|
}
|
|
1860
2237
|
if (prop.name === "html") state.vHtml = true;
|
|
1861
2238
|
}
|
|
@@ -1875,6 +2252,7 @@ function visitTemplateNode(source, node, state) {
|
|
|
1875
2252
|
state.moduleClosureBroken ||= interpolation?.usesCssModule ?? true;
|
|
1876
2253
|
state.referencesUseCssModule ||= interpolation?.referencesUseCssModule ?? false;
|
|
1877
2254
|
for (const name of interpolation?.references ?? []) state.expressionReferences.add(name);
|
|
2255
|
+
for (const prefix of interpolation?.templatePrefixes ?? []) state.templatePrefixes.add(prefix);
|
|
1878
2256
|
}
|
|
1879
2257
|
for (const child of node.children ?? []) visitTemplateNode(source, child, state);
|
|
1880
2258
|
}
|
|
@@ -1899,18 +2277,24 @@ function templateHandler(source) {
|
|
|
1899
2277
|
vueModuleMember: null,
|
|
1900
2278
|
usesCssModule: analysis.usesCssModule,
|
|
1901
2279
|
referencesUseCssModule: analysis.hasUnboundUseCssModule,
|
|
1902
|
-
references: analysis.unboundReferences
|
|
2280
|
+
references: analysis.unboundReferences,
|
|
2281
|
+
templatePrefixes: analysis.templatePrefixes,
|
|
2282
|
+
customProperties: analysis.customProperties,
|
|
2283
|
+
customPropertiesUnbounded: analysis.customPropertiesUnbounded
|
|
1903
2284
|
};
|
|
1904
2285
|
} catch {
|
|
1905
2286
|
return;
|
|
1906
2287
|
}
|
|
1907
2288
|
}
|
|
1908
2289
|
function templateSite(source, node, bindingClasses, classOpaque, state) {
|
|
1909
|
-
|
|
1910
|
-
const
|
|
1911
|
-
|
|
1912
|
-
const
|
|
1913
|
-
if (
|
|
2290
|
+
const classProp = staticAttribute(node, "class");
|
|
2291
|
+
const idProp = staticAttribute(node, "id");
|
|
2292
|
+
let classAttribute = literalAttribute(source, classProp);
|
|
2293
|
+
const idAttribute = literalAttribute(source, idProp);
|
|
2294
|
+
if (classProp && !classAttribute || idProp && !idAttribute) {
|
|
2295
|
+
if (classProp && !classAttribute) {
|
|
2296
|
+
for (const token of classProp.value?.content.split(/[\t\n\f\r ]+/) ?? []) if (token !== "") state.entityClassTokens.push(token);
|
|
2297
|
+
}
|
|
1914
2298
|
state.dynamic = true;
|
|
1915
2299
|
return { idAttribute };
|
|
1916
2300
|
}
|
|
@@ -1931,8 +2315,10 @@ function templateSite(source, node, bindingClasses, classOpaque, state) {
|
|
|
1931
2315
|
matchClasses
|
|
1932
2316
|
};
|
|
1933
2317
|
}
|
|
1934
|
-
function
|
|
1935
|
-
|
|
2318
|
+
function staticAttribute(node, name) {
|
|
2319
|
+
return node.props?.find((prop) => prop.type === PROP_ATTRIBUTE && prop.name === name);
|
|
2320
|
+
}
|
|
2321
|
+
function literalAttribute(source, prop) {
|
|
1936
2322
|
if (!prop?.value) return void 0;
|
|
1937
2323
|
const start = prop.value.loc.start.offset;
|
|
1938
2324
|
const end = prop.value.loc.end.offset;
|
|
@@ -2404,7 +2790,16 @@ async function preparePackageVue({ packageRoot, styleCompilers, sourceFiles, sty
|
|
|
2404
2790
|
htmlScriptText: analysis.scriptText
|
|
2405
2791
|
} : {},
|
|
2406
2792
|
sourceImports: analysis.scriptImports,
|
|
2407
|
-
sourceImportsUnverifiable: analysis.scriptImportsUnverifiable
|
|
2793
|
+
sourceImportsUnverifiable: analysis.scriptImportsUnverifiable,
|
|
2794
|
+
templatePrefixes: analysis.templatePrefixes,
|
|
2795
|
+
templateInjectsMarkup: analysis.vHtml,
|
|
2796
|
+
templateClassTokens: analysis.templateClassTokens,
|
|
2797
|
+
templateStyleValues: analysis.templateStyleValues,
|
|
2798
|
+
templateStylesUnverifiable: analysis.templateStylesUnverifiable,
|
|
2799
|
+
sourceCustomProperties: analysis.scriptCustomProperties,
|
|
2800
|
+
sourceCustomPropertiesUnbounded: analysis.scriptCustomPropertiesUnbounded,
|
|
2801
|
+
templateStylesheetLinks: analysis.templateStylesheetLinks,
|
|
2802
|
+
templateStylesheetLinksUnverifiable: analysis.templateStylesheetLinksUnverifiable
|
|
2408
2803
|
});
|
|
2409
2804
|
}
|
|
2410
2805
|
return {
|
|
@@ -2538,7 +2933,8 @@ async function migrate(options = {}) {
|
|
|
2538
2933
|
styleDependents: indexStylesheetDependents(styleSources),
|
|
2539
2934
|
vueStyleRanges,
|
|
2540
2935
|
entryCatalog: tailwindEntryCatalog(styleSources, scope.pathOwners),
|
|
2541
|
-
ignoredPaths:
|
|
2936
|
+
ignoredPaths: await scannerIgnoredPaths(workspaceRoot, sourceFiles.map((file) => file.path)),
|
|
2937
|
+
fontAllocations: /* @__PURE__ */ new Map()
|
|
2542
2938
|
};
|
|
2543
2939
|
const failures = [];
|
|
2544
2940
|
const plans = [];
|
|
@@ -2555,6 +2951,14 @@ async function migrate(options = {}) {
|
|
|
2555
2951
|
members.push(preparation);
|
|
2556
2952
|
groups.set(preparation.tailwind.path, members);
|
|
2557
2953
|
}
|
|
2954
|
+
for (const [, members] of groups) for (const [token, value] of Object.entries(members[0].tailwind.themeTokens)) {
|
|
2955
|
+
if (!token.startsWith("font-")) continue;
|
|
2956
|
+
const parsed = fontFamilyStack(value);
|
|
2957
|
+
const name = token.replace(/^font-/, "");
|
|
2958
|
+
const stack = parsed && parsed.value;
|
|
2959
|
+
const existing = context.fontAllocations.get(name);
|
|
2960
|
+
context.fontAllocations.set(name, existing === void 0 || existing === stack ? stack : null);
|
|
2961
|
+
}
|
|
2558
2962
|
for (const [, members] of [...groups].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)) for (const result of await planPreparedGroup(context, members)) if (result.failure) failures.push(result.failure);
|
|
2559
2963
|
else if (result.plan) plans.push(result.plan);
|
|
2560
2964
|
const originals = new Map([...styleSources, ...sourceFiles.map((file) => [file.path, file.source])]);
|
|
@@ -2847,6 +3251,174 @@ async function planPreparedGroup(context, members) {
|
|
|
2847
3251
|
return leftPath < rightPath ? -1 : leftPath > rightPath ? 1 : 0;
|
|
2848
3252
|
});
|
|
2849
3253
|
const entry = active[0].tailwind;
|
|
3254
|
+
const reservations = spellingReservations(context.styleSources, entry.graphSources.map((graphSource) => [`${graphSource.path}.graph.css`, graphSource.source]), new Set(entry.graphSources.map((graphSource) => graphSource.path).filter((path) => path.includes("\0"))));
|
|
3255
|
+
const groupFiles = new Map(active.flatMap((member) => member.files.map((file) => [file.path, file])));
|
|
3256
|
+
const mentionedSegments = /* @__PURE__ */ new Set();
|
|
3257
|
+
const mentionedSources = [...[...groupFiles.values()].map((file) => file.source), ...entry.graphSources.map((graphSource) => graphSource.source)];
|
|
3258
|
+
const addSegment = (token) => {
|
|
3259
|
+
if (token === "") return;
|
|
3260
|
+
const segment = utilitySegment(token).replace(/!$/, "");
|
|
3261
|
+
if (segment) mentionedSegments.add(segment);
|
|
3262
|
+
};
|
|
3263
|
+
for (const source of mentionedSources) {
|
|
3264
|
+
for (const token of source.split(/[\s"'`<>=,;{}()\\]+/)) addSegment(token);
|
|
3265
|
+
for (const inline of source.matchAll(/inline\(([^)]*)\)/g)) for (const candidate of inline[1].matchAll(/([^\s"',{}]*)\{([^{}]*)\}([^\s"',{}]*)/g)) for (const option of candidate[2].split(",")) {
|
|
3266
|
+
const range = /^(\d+)\.\.(\d+)(?:\.\.(\d+))?$/.exec(option);
|
|
3267
|
+
const stride = range === null ? 0 : Number(range[3] ?? "1");
|
|
3268
|
+
if (range !== null && stride > 0) {
|
|
3269
|
+
const from = Number(range[1]);
|
|
3270
|
+
const to = Number(range[2]);
|
|
3271
|
+
let steps = 0;
|
|
3272
|
+
for (let value = Math.min(from, to); value <= Math.max(from, to); value += stride) {
|
|
3273
|
+
addSegment(`${candidate[1]}${value}${candidate[3]}`);
|
|
3274
|
+
if ((steps += 1) >= 1e3) break;
|
|
3275
|
+
}
|
|
3276
|
+
continue;
|
|
3277
|
+
}
|
|
3278
|
+
addSegment(`${candidate[1]}${option}${candidate[3]}`);
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
const fileRoots = new Map(active.flatMap((member) => member.files.map((file) => [file.path, member.packageRoot])));
|
|
3282
|
+
const workspaceEntries = new Set([...context.entryCatalog.values()].flat());
|
|
3283
|
+
for (const file of groupFiles.values()) {
|
|
3284
|
+
if (reservations.unbounded) break;
|
|
3285
|
+
if (context.ignoredPaths.has(file.path)) {
|
|
3286
|
+
reservations.unbounded = true;
|
|
3287
|
+
break;
|
|
3288
|
+
}
|
|
3289
|
+
const opaqueStylesheetLink = (link) => {
|
|
3290
|
+
const resolved = localHtmlReference(fileRoots.get(file.path) ?? context.workspaceRoot, dirname(file.path), link);
|
|
3291
|
+
return resolved === void 0 || !context.styleSources.has(resolved) || workspaceEntries.has(resolved) && resolved !== entry.path;
|
|
3292
|
+
};
|
|
3293
|
+
const opaqueStylesheetImports = (records) => {
|
|
3294
|
+
for (const record of records) {
|
|
3295
|
+
if (record.typeOnly) continue;
|
|
3296
|
+
const specifier = record.specifier.split(/[?#]/, 1)[0];
|
|
3297
|
+
if (!isStylesheetPath(specifier)) {
|
|
3298
|
+
if (extname(specifier) !== "" || specifier.startsWith(".")) continue;
|
|
3299
|
+
try {
|
|
3300
|
+
const resolved = createRequire(file.path).resolve(specifier);
|
|
3301
|
+
if (isStylesheetPath(resolved) && !context.styleSources.has(resolved)) reservations.unbounded = true;
|
|
3302
|
+
} catch {}
|
|
3303
|
+
continue;
|
|
3304
|
+
}
|
|
3305
|
+
const resolved = specifier.startsWith(".") ? resolve(dirname(file.path), specifier) : void 0;
|
|
3306
|
+
if (resolved === void 0 || !context.styleSources.has(resolved) || workspaceEntries.has(resolved) && resolved !== entry.path) reservations.unbounded = true;
|
|
3307
|
+
}
|
|
3308
|
+
};
|
|
3309
|
+
if (extname(file.path) === ".html") {
|
|
3310
|
+
try {
|
|
3311
|
+
const parsed = parseHtmlSource(file.path, file.source);
|
|
3312
|
+
if (parsed.hasStyle) reservations.unbounded = true;
|
|
3313
|
+
if (parsed.bases.length > 0) reservations.unbounded = true;
|
|
3314
|
+
for (const link of parsed.links) if (opaqueStylesheetLink(link.href)) reservations.unbounded = true;
|
|
3315
|
+
for (const [index, value] of parsed.styleAttributeValues.entries()) {
|
|
3316
|
+
if (TEMPLATE_MARKERS.test(value)) {
|
|
3317
|
+
reservations.propertiesUnbounded = true;
|
|
3318
|
+
continue;
|
|
3319
|
+
}
|
|
3320
|
+
scanInlineStyleReservations(`${file.path}.style.${index}.css`, value, reservations);
|
|
3321
|
+
}
|
|
3322
|
+
for (const value of parsed.dynamicClassValues) {
|
|
3323
|
+
const masked = value.replace(TEMPLATE_EXPRESSIONS, "\0");
|
|
3324
|
+
if (TEMPLATE_MARKERS.test(masked)) {
|
|
3325
|
+
reservations.unbounded = true;
|
|
3326
|
+
continue;
|
|
3327
|
+
}
|
|
3328
|
+
for (const token of masked.split(/\s+/)) {
|
|
3329
|
+
if (token === "") continue;
|
|
3330
|
+
const index = token.indexOf("\0");
|
|
3331
|
+
if (index < 0) reservations.names.add(token);
|
|
3332
|
+
else if (index === 0) reservations.unbounded = true;
|
|
3333
|
+
else reservations.prefixes.add(token.slice(0, index));
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
if (parsed.handlerText.trim() !== "") try {
|
|
3337
|
+
const handlers = sourceAnalysis(`${file.path}.handlers.js`, parsed.handlerText);
|
|
3338
|
+
for (const prefix of handlers.templatePrefixes) reservations.prefixes.add(prefix);
|
|
3339
|
+
for (const property of handlers.customProperties) reservations.properties.add(property);
|
|
3340
|
+
if (handlers.customPropertiesUnbounded) reservations.propertiesUnbounded = true;
|
|
3341
|
+
} catch {
|
|
3342
|
+
reservations.unbounded = true;
|
|
3343
|
+
}
|
|
3344
|
+
if (parsed.scriptText.trim() !== "") try {
|
|
3345
|
+
const script = sourceAnalysis(`${file.path}.inline.js`, parsed.scriptText);
|
|
3346
|
+
for (const prefix of script.templatePrefixes) reservations.prefixes.add(prefix);
|
|
3347
|
+
for (const property of script.customProperties) reservations.properties.add(property);
|
|
3348
|
+
if (script.customPropertiesUnbounded) reservations.propertiesUnbounded = true;
|
|
3349
|
+
opaqueStylesheetImports(script.imports);
|
|
3350
|
+
} catch {
|
|
3351
|
+
reservations.unbounded = true;
|
|
3352
|
+
}
|
|
3353
|
+
} catch {
|
|
3354
|
+
reservations.unbounded = true;
|
|
3355
|
+
}
|
|
3356
|
+
continue;
|
|
3357
|
+
}
|
|
3358
|
+
if (extname(file.path) === ".vue") {
|
|
3359
|
+
if (file.templatePrefixes === void 0 || file.sourceImportsUnverifiable === true || file.templateInjectsMarkup === true) {
|
|
3360
|
+
reservations.unbounded = true;
|
|
3361
|
+
continue;
|
|
3362
|
+
}
|
|
3363
|
+
const styleRanges = context.vueStyleRanges.get(file.path);
|
|
3364
|
+
if (styleRanges === void 0) {
|
|
3365
|
+
reservations.unbounded = true;
|
|
3366
|
+
continue;
|
|
3367
|
+
}
|
|
3368
|
+
const fileBytes = Buffer.from(file.source);
|
|
3369
|
+
for (const [index, range] of styleRanges.entries()) {
|
|
3370
|
+
if (range.src !== void 0) {
|
|
3371
|
+
const resolved = range.src.startsWith(".") ? resolve(dirname(file.path), range.src) : void 0;
|
|
3372
|
+
if (resolved === void 0 || !context.styleSources.has(resolved)) reservations.unbounded = true;
|
|
3373
|
+
continue;
|
|
3374
|
+
}
|
|
3375
|
+
const block = fileBytes.subarray(range.start, range.end).toString();
|
|
3376
|
+
if (![
|
|
3377
|
+
"css",
|
|
3378
|
+
"scss",
|
|
3379
|
+
"sass",
|
|
3380
|
+
"less"
|
|
3381
|
+
].includes(range.lang)) {
|
|
3382
|
+
reservations.unbounded = true;
|
|
3383
|
+
continue;
|
|
3384
|
+
}
|
|
3385
|
+
scanStylesheetReservations(`${file.path}.block.${index}.${range.lang}`, block, context.styleSources, reservations);
|
|
3386
|
+
}
|
|
3387
|
+
for (const prefix of file.templatePrefixes) reservations.prefixes.add(prefix);
|
|
3388
|
+
for (const token of file.templateClassTokens ?? []) reservations.names.add(token);
|
|
3389
|
+
for (const property of file.sourceCustomProperties ?? []) reservations.properties.add(property);
|
|
3390
|
+
if (file.sourceCustomPropertiesUnbounded === true) reservations.propertiesUnbounded = true;
|
|
3391
|
+
if (file.templateStylesUnverifiable === true) reservations.propertiesUnbounded = true;
|
|
3392
|
+
for (const [index, value] of (file.templateStyleValues ?? []).entries()) scanInlineStyleReservations(`${file.path}.style.${index}.css`, value, reservations);
|
|
3393
|
+
if (file.templateStylesheetLinksUnverifiable === true) reservations.unbounded = true;
|
|
3394
|
+
for (const link of file.templateStylesheetLinks ?? []) if (opaqueStylesheetLink(link)) reservations.unbounded = true;
|
|
3395
|
+
opaqueStylesheetImports(file.sourceImports ?? []);
|
|
3396
|
+
continue;
|
|
3397
|
+
}
|
|
3398
|
+
if (![
|
|
3399
|
+
".js",
|
|
3400
|
+
".jsx",
|
|
3401
|
+
".ts",
|
|
3402
|
+
".tsx",
|
|
3403
|
+
".mjs",
|
|
3404
|
+
".cjs",
|
|
3405
|
+
".mts",
|
|
3406
|
+
".cts"
|
|
3407
|
+
].includes(extname(file.path))) continue;
|
|
3408
|
+
try {
|
|
3409
|
+
const analysis = sourceAnalysis(file.path, file.source);
|
|
3410
|
+
for (const prefix of analysis.templatePrefixes) reservations.prefixes.add(prefix);
|
|
3411
|
+
for (const property of analysis.customProperties) reservations.properties.add(property);
|
|
3412
|
+
if (analysis.customPropertiesUnbounded) reservations.propertiesUnbounded = true;
|
|
3413
|
+
opaqueStylesheetImports(analysis.imports);
|
|
3414
|
+
if (analysis.rendersStyleElement) reservations.unbounded = true;
|
|
3415
|
+
if (analysis.injectsMarkup) reservations.unbounded = true;
|
|
3416
|
+
if (analysis.stylesheetLinksUnverifiable) reservations.unbounded = true;
|
|
3417
|
+
for (const link of analysis.stylesheetLinks) if (opaqueStylesheetLink(link)) reservations.unbounded = true;
|
|
3418
|
+
} catch {
|
|
3419
|
+
reservations.unbounded = true;
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
2850
3422
|
const recoverGroup = (error, fatal = false) => {
|
|
2851
3423
|
if (!options.force || fatal) throw error;
|
|
2852
3424
|
for (const member of active) results.push({ failure: packageFailure(workspaceRoot, member.packageRoot, error) });
|
|
@@ -2900,28 +3472,30 @@ async function planPreparedGroup(context, members) {
|
|
|
2900
3472
|
if (active.length === 0) return results;
|
|
2901
3473
|
let names = extraction?.names;
|
|
2902
3474
|
let generated = extraction?.generated ?? [];
|
|
2903
|
-
if (options.extractMediaQueries !== false)
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
} else {
|
|
2914
|
-
try {
|
|
2915
|
-
extraction = planMediaExtraction({
|
|
2916
|
-
collections,
|
|
2917
|
-
tailwind: entry,
|
|
2918
|
-
scannedSources: context.sourceFiles.map((file) => file.source)
|
|
3475
|
+
if (options.extractMediaQueries !== false) {
|
|
3476
|
+
if (!groupWritable) {
|
|
3477
|
+
names = {};
|
|
3478
|
+
generated = [];
|
|
3479
|
+
if (collections.some((collection) => collection.components.length > 0)) mediaWarnings.push({
|
|
3480
|
+
code: "media-query-definition-fallback",
|
|
3481
|
+
file: entry.path,
|
|
3482
|
+
start: 0,
|
|
3483
|
+
end: 0,
|
|
3484
|
+
message: "The Tailwind entry could not be edited safely, so media behavior was preserved with arbitrary variants."
|
|
2919
3485
|
});
|
|
2920
|
-
}
|
|
2921
|
-
|
|
3486
|
+
} else {
|
|
3487
|
+
try {
|
|
3488
|
+
extraction = planMediaExtraction({
|
|
3489
|
+
collections,
|
|
3490
|
+
tailwind: entry,
|
|
3491
|
+
scannedSources: context.sourceFiles.map((file) => file.source)
|
|
3492
|
+
});
|
|
3493
|
+
} catch (error) {
|
|
3494
|
+
return recoverGroup(error, !isRecoverablePlanningError(error));
|
|
3495
|
+
}
|
|
3496
|
+
names = extraction.names;
|
|
3497
|
+
generated = extraction.generated;
|
|
2922
3498
|
}
|
|
2923
|
-
names = extraction.names;
|
|
2924
|
-
generated = extraction.generated;
|
|
2925
3499
|
}
|
|
2926
3500
|
let plan = {
|
|
2927
3501
|
files: [],
|
|
@@ -2935,6 +3509,11 @@ async function planPreparedGroup(context, members) {
|
|
|
2935
3509
|
};
|
|
2936
3510
|
let augmented = entry.css;
|
|
2937
3511
|
const blocked = /* @__PURE__ */ new Map();
|
|
3512
|
+
let candidateAliases;
|
|
3513
|
+
let canonicalized = false;
|
|
3514
|
+
let fontTokens = {};
|
|
3515
|
+
let fontFailures = /* @__PURE__ */ new Set();
|
|
3516
|
+
let composeEntry = () => entry.css;
|
|
2938
3517
|
finalization: for (;;) {
|
|
2939
3518
|
const baseIdentities = new Set(atRuleIdentities(`${entry.path}.group-base.css`, [entry.css, ...entry.graphSources.map((graphSource) => graphSource.source)].join("\n")));
|
|
2940
3519
|
const sheets = active.flatMap((member) => member.stylesheets.map((stylesheet) => ({
|
|
@@ -2997,6 +3576,7 @@ async function planPreparedGroup(context, members) {
|
|
|
2997
3576
|
utilityPrefix: entry.designSystem.theme.prefix,
|
|
2998
3577
|
themeTokens: entry.themeTokens,
|
|
2999
3578
|
...names ? { mediaNames: names } : {},
|
|
3579
|
+
...candidateAliases ? { candidateAliases } : {},
|
|
3000
3580
|
...groupWritable ? {} : { entryWritable: false },
|
|
3001
3581
|
files
|
|
3002
3582
|
};
|
|
@@ -3018,7 +3598,11 @@ async function planPreparedGroup(context, members) {
|
|
|
3018
3598
|
generated
|
|
3019
3599
|
} : void 0;
|
|
3020
3600
|
const used = currentExtraction ? usedGeneratedDefinitions(currentExtraction, plan) : [];
|
|
3021
|
-
|
|
3601
|
+
composeEntry = (tokens) => {
|
|
3602
|
+
const withFonts = appendFontTheme(composed, tokens);
|
|
3603
|
+
return currentExtraction ? appendMediaDefinitions(withFonts, used) : withFonts;
|
|
3604
|
+
};
|
|
3605
|
+
augmented = composeEntry(fontTokens);
|
|
3022
3606
|
let system;
|
|
3023
3607
|
try {
|
|
3024
3608
|
system = augmented === entry.css ? entry.designSystem : await entry.loadWith(augmented);
|
|
@@ -3041,13 +3625,65 @@ async function planPreparedGroup(context, members) {
|
|
|
3041
3625
|
continue planning;
|
|
3042
3626
|
}
|
|
3043
3627
|
}
|
|
3044
|
-
const failing = invalidCandidates(
|
|
3628
|
+
const failing = invalidCandidates(system, plan.candidates);
|
|
3045
3629
|
if (failing.length > 0) {
|
|
3046
3630
|
if (!accumulateBlockedRules(blocked, plan, failing)) throw new Error(`Tailwind did not generate CSS for candidate: ${failing[0]}`);
|
|
3047
3631
|
continue planning;
|
|
3048
3632
|
}
|
|
3633
|
+
if (!canonicalized) {
|
|
3634
|
+
canonicalized = true;
|
|
3635
|
+
const aliases = acceptedCandidateAliases(system, plan.candidateProbes ?? [], reservations);
|
|
3636
|
+
const fonts = await registerFontTokens({
|
|
3637
|
+
system,
|
|
3638
|
+
entryCss: augmented,
|
|
3639
|
+
loadWith: entry.loadWith,
|
|
3640
|
+
themeTokens: entry.themeTokens,
|
|
3641
|
+
probes: plan.fontFamilyProbes ?? [],
|
|
3642
|
+
corpus: [.../* @__PURE__ */ new Set([...plan.candidates, ...plan.candidateProbes ?? []])],
|
|
3643
|
+
reservations,
|
|
3644
|
+
existingAliases: aliases,
|
|
3645
|
+
generate: groupWritable,
|
|
3646
|
+
mentionedSegments,
|
|
3647
|
+
referenceTokens: entry.referenceTokens,
|
|
3648
|
+
globalAllocations: context.fontAllocations
|
|
3649
|
+
});
|
|
3650
|
+
Object.assign(aliases, fonts.aliases);
|
|
3651
|
+
fontTokens = fonts.tokens;
|
|
3652
|
+
fontFailures = fonts.failures;
|
|
3653
|
+
for (const [name, value] of Object.entries(fonts.tokens)) context.fontAllocations.set(name, value);
|
|
3654
|
+
if (Object.keys(aliases).length > 0) {
|
|
3655
|
+
candidateAliases = aliases;
|
|
3656
|
+
continue planning;
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3049
3659
|
break;
|
|
3050
3660
|
}
|
|
3661
|
+
const ruleStatus = new Map(plan.rules.map((rule) => [`${rule.stylesheet}:${rule.ruleId.start}-${rule.ruleId.end}`, rule.status]));
|
|
3662
|
+
const warned = /* @__PURE__ */ new Set();
|
|
3663
|
+
for (const probe of plan.fontFamilyProbes ?? []) {
|
|
3664
|
+
if (probe.firstFamily.kind !== "name") continue;
|
|
3665
|
+
if (candidateAliases?.[probe.candidate] !== void 0) continue;
|
|
3666
|
+
const ruleKey = `${probe.stylesheet}:${probe.ruleId.start}-${probe.ruleId.end}`;
|
|
3667
|
+
if (ruleStatus.get(ruleKey) !== "retained" || warned.has(ruleKey)) continue;
|
|
3668
|
+
const failed = fontFailures.has(probe.candidate);
|
|
3669
|
+
if (groupWritable && !failed) continue;
|
|
3670
|
+
warned.add(ruleKey);
|
|
3671
|
+
plan.warnings.push({
|
|
3672
|
+
code: groupWritable ? "font-theme-registration-failed" : "font-theme-registration-required",
|
|
3673
|
+
file: sheets[probe.stylesheet].stylesheet.cssPath,
|
|
3674
|
+
start: probe.authoredSpan.start,
|
|
3675
|
+
end: probe.authoredSpan.end,
|
|
3676
|
+
message: groupWritable ? "A safe Tailwind font theme token could not be registered, so the rule is retained." : "The font family requires a Tailwind theme token, but the Tailwind entry cannot be edited, so the rule is retained."
|
|
3677
|
+
});
|
|
3678
|
+
}
|
|
3679
|
+
if (Object.keys(fontTokens).length > 0) {
|
|
3680
|
+
const applied = new Set(plan.candidates.map((candidate) => utilitySegment(candidate).replace(/!$/, "")));
|
|
3681
|
+
const pruned = Object.fromEntries(Object.entries(fontTokens).filter(([name]) => applied.has(`font-${name}`)));
|
|
3682
|
+
if (Object.keys(pruned).length !== Object.keys(fontTokens).length) {
|
|
3683
|
+
fontTokens = pruned;
|
|
3684
|
+
augmented = composeEntry(pruned);
|
|
3685
|
+
}
|
|
3686
|
+
}
|
|
3051
3687
|
for (const [index, rules] of blocked) {
|
|
3052
3688
|
const cssPath = sheets[index].stylesheet.cssPath;
|
|
3053
3689
|
for (const { authoredSpan, candidates } of rules.values()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tw-migrate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Preview and migrate React, Vue, and HTML CSS to Tailwind utilities",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codemod",
|
|
@@ -62,11 +62,11 @@
|
|
|
62
62
|
"vue": "catalog:"
|
|
63
63
|
},
|
|
64
64
|
"optionalDependencies": {
|
|
65
|
-
"tw-migrate-darwin-arm64": "0.
|
|
66
|
-
"tw-migrate-darwin-x64": "0.
|
|
67
|
-
"tw-migrate-linux-arm64-gnu": "0.
|
|
68
|
-
"tw-migrate-linux-x64-gnu": "0.
|
|
69
|
-
"tw-migrate-win32-x64-msvc": "0.
|
|
65
|
+
"tw-migrate-darwin-arm64": "0.6.0",
|
|
66
|
+
"tw-migrate-darwin-x64": "0.6.0",
|
|
67
|
+
"tw-migrate-linux-arm64-gnu": "0.6.0",
|
|
68
|
+
"tw-migrate-linux-x64-gnu": "0.6.0",
|
|
69
|
+
"tw-migrate-win32-x64-msvc": "0.6.0"
|
|
70
70
|
},
|
|
71
71
|
"napi": {
|
|
72
72
|
"binaryName": "tw-migrate",
|