rainbowindex 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +135 -0
- package/README.md +27 -4
- package/dist/browser.d.ts +2 -2
- package/dist/browser.mjs +1 -1
- package/dist/chunk-3HRMFZGE.mjs +19 -0
- package/dist/{chunk-AZXWJ625.mjs → chunk-53WD6U2X.mjs} +826 -610
- package/dist/{chunk-IE6N76PW.mjs → chunk-6GTG5SZJ.mjs} +5269 -4660
- package/dist/{chunk-AUWGHSTO.mjs → chunk-IYIUS7AE.mjs} +64 -126
- package/dist/{chunk-SOMDX7V6.mjs → chunk-KRZL4IDK.mjs} +337 -421
- package/dist/chunk-WYMCN5OC.mjs +244 -0
- package/dist/cli.mjs +280 -277
- package/dist/{index-CfDtWufj.d.ts → context-ruu2x_jR.d.ts} +15 -70
- package/dist/editor.d.ts +98 -28
- package/dist/editor.mjs +37 -8
- package/dist/{index-DK6APAGD.d.ts → index-Dp6i5TSv.d.ts} +16 -12
- package/dist/index.d.ts +4 -4
- package/dist/index.mjs +4 -4
- package/dist/safelist-D9-Plqta.d.ts +109 -0
- package/dist/vite.mjs +80 -83
- package/package.json +2 -1
- package/dist/chunk-RHOQSN2B.mjs +0 -691
- package/dist/safelist-CH3_PywB.d.ts +0 -43
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
RI_IMPORT_SPECIFIER_ALTERNATION,
|
|
6
6
|
SAFE_FONT_FAMILY_RE,
|
|
7
7
|
SHADOW_VAR_REF_RE,
|
|
8
|
+
analyzeProjectCSS,
|
|
8
9
|
codepointCompare,
|
|
9
10
|
compileCSSFunctions,
|
|
10
11
|
createCompiler,
|
|
@@ -18,341 +19,12 @@ import {
|
|
|
18
19
|
renderCSS,
|
|
19
20
|
scanCSSForTokenUsage,
|
|
20
21
|
withTimeout
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-6GTG5SZJ.mjs";
|
|
22
23
|
import {
|
|
23
24
|
checkPaletteContrast,
|
|
24
25
|
generateAllColorVariables,
|
|
25
26
|
generateThemeOverrides
|
|
26
|
-
} from "./chunk-
|
|
27
|
-
|
|
28
|
-
// src/scanner/glob-utils.ts
|
|
29
|
-
import { isAbsolute, win32 } from "path";
|
|
30
|
-
function validateGlobPattern(pattern) {
|
|
31
|
-
if (!pattern?.trim()) {
|
|
32
|
-
return "Glob pattern is empty.";
|
|
33
|
-
}
|
|
34
|
-
if (pattern.includes("\0")) {
|
|
35
|
-
return "Glob pattern contains a null byte, which is invalid in file paths.";
|
|
36
|
-
}
|
|
37
|
-
if (isAbsolute(pattern) || win32.isAbsolute(pattern)) {
|
|
38
|
-
return `Glob pattern "${pattern}" must be relative, not absolute.`;
|
|
39
|
-
}
|
|
40
|
-
const segments = pattern.split(/[\\/]+/);
|
|
41
|
-
for (const seg of segments) {
|
|
42
|
-
if (seg === "..") {
|
|
43
|
-
return `Glob pattern "${pattern}" must not traverse parent directories (".."). Restructure your project layout so source files are within the project root, or use @source with a pattern rooted at the project directory.`;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// src/scanner/sources.ts
|
|
50
|
-
import { readFile, stat } from "fs/promises";
|
|
51
|
-
import { resolve as resolve2 } from "path";
|
|
52
|
-
import { glob } from "tinyglobby";
|
|
53
|
-
|
|
54
|
-
// src/scanner/package-discovery.ts
|
|
55
|
-
import { existsSync, readFileSync, realpathSync, statSync } from "fs";
|
|
56
|
-
import { dirname, join, posix, resolve } from "path";
|
|
57
|
-
var EMPTY = Object.freeze({ sources: [], warnings: [] });
|
|
58
|
-
var discoveryCache = /* @__PURE__ */ new Map();
|
|
59
|
-
function discoverPackageSafelistSources(cwd) {
|
|
60
|
-
const cwdAbs = resolve(cwd);
|
|
61
|
-
let mtimeMs;
|
|
62
|
-
try {
|
|
63
|
-
mtimeMs = statSync(join(cwdAbs, "package.json")).mtimeMs;
|
|
64
|
-
} catch {
|
|
65
|
-
return EMPTY;
|
|
66
|
-
}
|
|
67
|
-
const cached = discoveryCache.get(cwdAbs);
|
|
68
|
-
if (cached && cached.mtimeMs === mtimeMs) return cached.result;
|
|
69
|
-
const result = runDiscovery(cwdAbs);
|
|
70
|
-
discoveryCache.set(cwdAbs, { mtimeMs, result });
|
|
71
|
-
return result;
|
|
72
|
-
}
|
|
73
|
-
function runDiscovery(cwdAbs) {
|
|
74
|
-
let consumer;
|
|
75
|
-
try {
|
|
76
|
-
consumer = readPackageJson(join(cwdAbs, "package.json"));
|
|
77
|
-
} catch {
|
|
78
|
-
return EMPTY;
|
|
79
|
-
}
|
|
80
|
-
const deps = [
|
|
81
|
-
...Object.keys(consumer.dependencies ?? {}),
|
|
82
|
-
...Object.keys(consumer.peerDependencies ?? {})
|
|
83
|
-
];
|
|
84
|
-
if (deps.length === 0) return EMPTY;
|
|
85
|
-
const sources = [];
|
|
86
|
-
const warnings = [];
|
|
87
|
-
for (const depName of deps) {
|
|
88
|
-
const depPkgPath = findDepPackageJson(cwdAbs, depName);
|
|
89
|
-
if (!depPkgPath) {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
let depPkg;
|
|
93
|
-
try {
|
|
94
|
-
depPkg = readPackageJson(depPkgPath);
|
|
95
|
-
} catch (err) {
|
|
96
|
-
warnings.push(
|
|
97
|
-
`[RI-1410] Could not read ${depName}/package.json during safelist discovery: ${errMessage(err)}`
|
|
98
|
-
);
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
const patterns = depPkg.rainbowindex?.safelistSources;
|
|
102
|
-
if (patterns == null) continue;
|
|
103
|
-
if (!Array.isArray(patterns)) {
|
|
104
|
-
warnings.push(
|
|
105
|
-
`[RI-1410] Invalid rainbowindex.safelistSources in ${depName} \u2014 expected an array of glob strings.`
|
|
106
|
-
);
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
if (patterns.length === 0) continue;
|
|
110
|
-
const depRoot = realpathOrFallback(dirname(depPkgPath)).replace(/\\/g, "/");
|
|
111
|
-
for (const pattern of patterns) {
|
|
112
|
-
if (typeof pattern !== "string" || !pattern) {
|
|
113
|
-
warnings.push(
|
|
114
|
-
`[RI-1410] Invalid rainbowindex.safelistSources entry in ${depName} \u2014 expected a non-empty glob string.`
|
|
115
|
-
);
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
if (validateGlobPattern(pattern) !== null) {
|
|
119
|
-
warnings.push(
|
|
120
|
-
`[RI-1410] Invalid rainbowindex.safelistSources entry "${pattern}" in ${depName} \u2014 patterns must be relative to the package root and must not traverse parent directories.`
|
|
121
|
-
);
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
const absolute = posix.normalize(`${depRoot}/${pattern.replace(/^\.\//, "")}`);
|
|
125
|
-
if (absolute !== depRoot && !absolute.startsWith(`${depRoot}/`)) {
|
|
126
|
-
warnings.push(
|
|
127
|
-
`[RI-1410] Invalid rainbowindex.safelistSources entry "${pattern}" in ${depName} \u2014 resolved outside the package root.`
|
|
128
|
-
);
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
sources.push({ pattern: absolute, negated: false, inline: false, absolute: true });
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return { sources, warnings };
|
|
135
|
-
}
|
|
136
|
-
function readPackageJson(path) {
|
|
137
|
-
const raw = readFileSync(path, "utf8");
|
|
138
|
-
return JSON.parse(raw);
|
|
139
|
-
}
|
|
140
|
-
function findDepPackageJson(cwd, depName) {
|
|
141
|
-
let dir = cwd;
|
|
142
|
-
while (true) {
|
|
143
|
-
const candidate = join(dir, "node_modules", depName, "package.json");
|
|
144
|
-
if (existsSync(candidate)) return candidate;
|
|
145
|
-
const parent = dirname(dir);
|
|
146
|
-
if (parent === dir) return null;
|
|
147
|
-
dir = parent;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
function realpathOrFallback(path) {
|
|
151
|
-
try {
|
|
152
|
-
return realpathSync(path);
|
|
153
|
-
} catch {
|
|
154
|
-
return path;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
function errMessage(err) {
|
|
158
|
-
return err instanceof Error ? err.message : String(err);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// src/scanner/sources.ts
|
|
162
|
-
var DEFAULT_PATTERNS = Object.freeze([
|
|
163
|
-
"index.html",
|
|
164
|
-
"src/**/*.{html,js,jsx,ts,tsx,mdx,vue,svelte}"
|
|
165
|
-
]);
|
|
166
|
-
var DEFAULT_EXCLUDES = Object.freeze([
|
|
167
|
-
"node_modules/**",
|
|
168
|
-
"dist/**",
|
|
169
|
-
"build/**",
|
|
170
|
-
"coverage/**",
|
|
171
|
-
"public/**",
|
|
172
|
-
"**/*.config.*",
|
|
173
|
-
"**/*.d.ts"
|
|
174
|
-
]);
|
|
175
|
-
var MAX_FILE_SIZE = 1048576;
|
|
176
|
-
var MAX_INLINE_SOURCE_SIZE = 102400;
|
|
177
|
-
var GLOB_TIMEOUT_MS = 3e4;
|
|
178
|
-
var GLOB_TIMEOUT_MESSAGE = `glob() timed out after ${GLOB_TIMEOUT_MS / 1e3}s \u2014 check for network filesystem issues or overly broad @source patterns`;
|
|
179
|
-
function collectPatterns(sources) {
|
|
180
|
-
const includePatterns = [];
|
|
181
|
-
const nodeModulesIncludePatterns = [];
|
|
182
|
-
const excludePatterns = [...DEFAULT_EXCLUDES];
|
|
183
|
-
const hasUserPositiveGlobs = sources.some((s) => !s.inline && !s.negated && !s.absolute);
|
|
184
|
-
if (!hasUserPositiveGlobs) {
|
|
185
|
-
includePatterns.push(...DEFAULT_PATTERNS);
|
|
186
|
-
}
|
|
187
|
-
const warnings = [];
|
|
188
|
-
for (const src of sources) {
|
|
189
|
-
if (src.inline) continue;
|
|
190
|
-
if (!src.absolute) {
|
|
191
|
-
const err = validateGlobPattern(src.pattern);
|
|
192
|
-
if (err) {
|
|
193
|
-
warnings.push(`[RI-1404] @source pattern rejected: ${err}`);
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
if (src.negated) {
|
|
198
|
-
excludePatterns.push(src.pattern);
|
|
199
|
-
} else if (src.absolute || src.pattern.replace(/^\.\//, "").startsWith("node_modules/")) {
|
|
200
|
-
nodeModulesIncludePatterns.push(src.pattern);
|
|
201
|
-
} else {
|
|
202
|
-
includePatterns.push(src.pattern);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return {
|
|
206
|
-
includePatterns,
|
|
207
|
-
nodeModulesIncludePatterns,
|
|
208
|
-
excludePatterns,
|
|
209
|
-
warnings,
|
|
210
|
-
hasUserPositiveGlobs
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
async function resolveSourceFilesAsync(sources, cwd, hasInlineClasses = false) {
|
|
214
|
-
const {
|
|
215
|
-
includePatterns,
|
|
216
|
-
nodeModulesIncludePatterns,
|
|
217
|
-
excludePatterns,
|
|
218
|
-
warnings,
|
|
219
|
-
hasUserPositiveGlobs
|
|
220
|
-
} = collectPatterns(sources);
|
|
221
|
-
const allIncludes = [...includePatterns, ...nodeModulesIncludePatterns];
|
|
222
|
-
if (allIncludes.length === 0) return { files: [], warnings };
|
|
223
|
-
try {
|
|
224
|
-
const globPasses = [];
|
|
225
|
-
if (includePatterns.length > 0) {
|
|
226
|
-
globPasses.push(
|
|
227
|
-
withTimeout(
|
|
228
|
-
glob(includePatterns, { cwd, ignore: excludePatterns }),
|
|
229
|
-
GLOB_TIMEOUT_MS,
|
|
230
|
-
GLOB_TIMEOUT_MESSAGE
|
|
231
|
-
)
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
if (nodeModulesIncludePatterns.length > 0) {
|
|
235
|
-
const relaxedExcludes = excludePatterns.filter((p) => p !== "node_modules/**");
|
|
236
|
-
globPasses.push(
|
|
237
|
-
withTimeout(
|
|
238
|
-
glob(nodeModulesIncludePatterns, { cwd, ignore: relaxedExcludes }),
|
|
239
|
-
GLOB_TIMEOUT_MS,
|
|
240
|
-
GLOB_TIMEOUT_MESSAGE
|
|
241
|
-
)
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
const matched = (await Promise.all(globPasses)).flat();
|
|
245
|
-
const files = [...new Set(matched.map((f) => resolve2(cwd, f)))].sort(codepointCompare);
|
|
246
|
-
if (files.length === 0 && !(hasInlineClasses && !hasUserPositiveGlobs)) {
|
|
247
|
-
warnings.push(
|
|
248
|
-
`[RI-1401] No source files found matching ${allIncludes.map((p) => `"${p}"`).join(", ")} \u2014 check @source paths or project structure.`
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
return { files, warnings };
|
|
252
|
-
} catch (err) {
|
|
253
|
-
warnings.push(
|
|
254
|
-
`[RI-1402] Invalid glob pattern \u2014 skipping. Check @source syntax. (${err instanceof Error ? err.message : String(err)})`
|
|
255
|
-
);
|
|
256
|
-
return { files: [], warnings };
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
function collectInlineClasses(sources) {
|
|
260
|
-
const classes = /* @__PURE__ */ new Set();
|
|
261
|
-
const warnings = [];
|
|
262
|
-
for (const src of sources) {
|
|
263
|
-
if (!src.inline) continue;
|
|
264
|
-
const items = src.classes ?? [];
|
|
265
|
-
let contentLength = 0;
|
|
266
|
-
for (const cls of items) contentLength += cls.length;
|
|
267
|
-
if (contentLength > MAX_INLINE_SOURCE_SIZE) {
|
|
268
|
-
warnings.push(
|
|
269
|
-
`[RI-1406] Inline @source content exceeds ${MAX_INLINE_SOURCE_SIZE} byte limit (${contentLength} bytes) \u2014 skipping.`
|
|
270
|
-
);
|
|
271
|
-
continue;
|
|
272
|
-
}
|
|
273
|
-
for (const cls of items) {
|
|
274
|
-
if (cls) classes.add(cls);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
return { classes, warnings };
|
|
278
|
-
}
|
|
279
|
-
var FILE_IO_TIMEOUT_MS = 1e4;
|
|
280
|
-
async function scanOneFile(file) {
|
|
281
|
-
const warnings = [];
|
|
282
|
-
try {
|
|
283
|
-
const fileSize = await withTimeout(
|
|
284
|
-
stat(file).then((s) => s.size),
|
|
285
|
-
FILE_IO_TIMEOUT_MS,
|
|
286
|
-
"stat() timed out"
|
|
287
|
-
);
|
|
288
|
-
if (fileSize > MAX_FILE_SIZE) {
|
|
289
|
-
return {
|
|
290
|
-
classes: null,
|
|
291
|
-
warnings,
|
|
292
|
-
failure: `[RI-1405] Skipping source file "${file}" (${fileSize} bytes) \u2014 exceeds ${MAX_FILE_SIZE} byte limit.`
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
const content = await withTimeout(
|
|
296
|
-
readFile(file, "utf-8"),
|
|
297
|
-
FILE_IO_TIMEOUT_MS,
|
|
298
|
-
"readFile() timed out"
|
|
299
|
-
);
|
|
300
|
-
return {
|
|
301
|
-
classes: extractClassesFromSource({ path: file, content }, warnings),
|
|
302
|
-
warnings,
|
|
303
|
-
failure: null
|
|
304
|
-
};
|
|
305
|
-
} catch (err) {
|
|
306
|
-
return {
|
|
307
|
-
classes: null,
|
|
308
|
-
warnings,
|
|
309
|
-
failure: `[RI-1403] Could not read source file "${file}" \u2014 skipping. (${err instanceof Error ? err.message : String(err)})`
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
async function scanSourceFilesAsync(sources, cwd) {
|
|
314
|
-
const { classes: allClasses, warnings: inlineWarnings } = collectInlineClasses(sources);
|
|
315
|
-
const allWarnings = [...inlineWarnings];
|
|
316
|
-
const { files, warnings: resolveWarnings } = await resolveSourceFilesAsync(
|
|
317
|
-
sources,
|
|
318
|
-
cwd,
|
|
319
|
-
allClasses.size > 0
|
|
320
|
-
);
|
|
321
|
-
allWarnings.push(...resolveWarnings);
|
|
322
|
-
const CONCURRENCY_LIMIT = 32;
|
|
323
|
-
const results = new Array(files.length);
|
|
324
|
-
let nextIndex = 0;
|
|
325
|
-
const worker = async () => {
|
|
326
|
-
while (nextIndex < files.length) {
|
|
327
|
-
const index = nextIndex++;
|
|
328
|
-
results[index] = await scanOneFile(files[index]);
|
|
329
|
-
}
|
|
330
|
-
};
|
|
331
|
-
await Promise.all(Array.from({ length: Math.min(CONCURRENCY_LIMIT, files.length) }, worker));
|
|
332
|
-
const seen = new Set(allWarnings);
|
|
333
|
-
for (const result of results) {
|
|
334
|
-
if (result.failure) {
|
|
335
|
-
allWarnings.push(result.failure);
|
|
336
|
-
seen.add(result.failure);
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
if (result.classes) {
|
|
340
|
-
for (const cls of result.classes) {
|
|
341
|
-
allClasses.add(cls);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
pushWarningsDeduped(allWarnings, result.warnings, seen);
|
|
345
|
-
}
|
|
346
|
-
return { classes: allClasses, warnings: allWarnings };
|
|
347
|
-
}
|
|
348
|
-
async function collectProjectClasses(themeSources, surfaceSources, cwd, warnings, warningSeen) {
|
|
349
|
-
const discovered = discoverPackageSafelistSources(cwd);
|
|
350
|
-
pushWarningsDeduped(warnings, discovered.warnings, warningSeen);
|
|
351
|
-
const allSources = [...themeSources, ...surfaceSources, ...discovered.sources];
|
|
352
|
-
const scanResult = await scanSourceFilesAsync(allSources, cwd);
|
|
353
|
-
pushWarningsDeduped(warnings, scanResult.warnings, warningSeen);
|
|
354
|
-
return scanResult.classes;
|
|
355
|
-
}
|
|
27
|
+
} from "./chunk-KRZL4IDK.mjs";
|
|
356
28
|
|
|
357
29
|
// src/integrations/font-providers/google/state.ts
|
|
358
30
|
var googleFontInternals = {
|
|
@@ -366,13 +38,13 @@ var googleFontInternals = {
|
|
|
366
38
|
// src/integrations/font-providers/google/cache.ts
|
|
367
39
|
import { createHash, randomUUID } from "crypto";
|
|
368
40
|
import { open as fsOpen, writeFile, rename, unlink, mkdir } from "fs/promises";
|
|
369
|
-
import { resolve
|
|
41
|
+
import { resolve, dirname, join, isAbsolute } from "path";
|
|
370
42
|
import { isAbsolute as win32IsAbsolute } from "path/win32";
|
|
371
43
|
var MAX_FONT_CACHE_TTL_SECONDS = 30 * 24 * 60 * 60;
|
|
372
44
|
function getFontCacheDir() {
|
|
373
45
|
if (googleFontInternals.validatedCacheDir !== null) return googleFontInternals.validatedCacheDir;
|
|
374
46
|
const raw = process.env.RI_CACHE_DIR || "node_modules/.cache/rainbowindex";
|
|
375
|
-
if (
|
|
47
|
+
if (isAbsolute(raw) || win32IsAbsolute(raw)) {
|
|
376
48
|
throw new Error(
|
|
377
49
|
`[RI-1208] RI_CACHE_DIR must be a relative path, got absolute path: "${raw}". Use a relative path like "node_modules/.cache/rainbowindex".`
|
|
378
50
|
);
|
|
@@ -392,7 +64,7 @@ function getResolvedCachePath() {
|
|
|
392
64
|
if (googleFontInternals.resolvedCachePath !== null && googleFontInternals.validatedCacheDir !== null) {
|
|
393
65
|
return googleFontInternals.resolvedCachePath;
|
|
394
66
|
}
|
|
395
|
-
googleFontInternals.resolvedCachePath =
|
|
67
|
+
googleFontInternals.resolvedCachePath = resolve(process.cwd(), getFontCacheFile());
|
|
396
68
|
return googleFontInternals.resolvedCachePath;
|
|
397
69
|
}
|
|
398
70
|
function getFontCacheTTL() {
|
|
@@ -475,12 +147,12 @@ async function loadFontCache(ignoreExpiry = false) {
|
|
|
475
147
|
async function saveFontCache() {
|
|
476
148
|
try {
|
|
477
149
|
const cachePath = getResolvedCachePath();
|
|
478
|
-
await mkdir(
|
|
150
|
+
await mkdir(dirname(cachePath), { recursive: true });
|
|
479
151
|
const entries = Array.from(googleFontInternals.googleFontState.cache.values());
|
|
480
152
|
const entriesJson = JSON.stringify(entries);
|
|
481
153
|
const checksum = createHash("sha256").update(entriesJson).digest("hex");
|
|
482
154
|
const payload = `{"checksum":${JSON.stringify(checksum)},"entries":${entriesJson}}`;
|
|
483
|
-
const tmpPath =
|
|
155
|
+
const tmpPath = join(dirname(cachePath), `.google-${process.pid}-${randomUUID()}.tmp`);
|
|
484
156
|
try {
|
|
485
157
|
await writeFile(tmpPath, payload);
|
|
486
158
|
await rename(tmpPath, cachePath);
|
|
@@ -583,272 +255,763 @@ async function fetchGoogleFontMetadata() {
|
|
|
583
255
|
clearTimeout(timeout);
|
|
584
256
|
}
|
|
585
257
|
}
|
|
586
|
-
throw new Error("[RI-1205] Exhausted Google Fonts metadata fetch retries.");
|
|
258
|
+
throw new Error("[RI-1205] Exhausted Google Fonts metadata fetch retries.");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// src/integrations/font-providers/google/index.ts
|
|
262
|
+
var FETCH_RETRY_COOLDOWN_MS = 3e4;
|
|
263
|
+
async function fetchGoogleFontList() {
|
|
264
|
+
if (googleFontInternals.googleFontState.fetched) return;
|
|
265
|
+
if (googleFontInternals.googleFontListPromise) return googleFontInternals.googleFontListPromise;
|
|
266
|
+
if (googleFontInternals.lastFetchFailureMs > 0 && Date.now() - googleFontInternals.lastFetchFailureMs < FETCH_RETRY_COOLDOWN_MS) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const localPromise = (async () => {
|
|
270
|
+
try {
|
|
271
|
+
const isOffline = process.env.RI_OFFLINE === "1" || process.env.RI_OFFLINE === "true";
|
|
272
|
+
if (isOffline) {
|
|
273
|
+
if (await loadFontCache(true)) return;
|
|
274
|
+
console.warn(
|
|
275
|
+
`[RI-1206] RI_OFFLINE is set but no local font cache found at ${getFontCacheFile()}. Run once with network access to populate it. Non-variable fonts will default to weight "100 900" and may produce broken Google Fonts URLs \u2014 set an explicit weight in the @font directive to avoid this.`
|
|
276
|
+
);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (await loadFontCache()) return;
|
|
280
|
+
const fetchDisabled = process.env.RI_FETCH_FONTS === "0" || process.env.RI_FETCH_FONTS === "false";
|
|
281
|
+
if (fetchDisabled) {
|
|
282
|
+
if (await loadFontCache(true)) return;
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (typeof globalThis.fetch !== "function") {
|
|
286
|
+
console.warn(
|
|
287
|
+
"[RI-1212] Global fetch() is not available. Google Fonts metadata requires Node.js >= 18. Skipping font fetch."
|
|
288
|
+
);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (isRIDebug()) {
|
|
292
|
+
console.warn("[RI-DEBUG] Fetching Google Fonts metadata...");
|
|
293
|
+
}
|
|
294
|
+
try {
|
|
295
|
+
const newCache = await fetchGoogleFontMetadata();
|
|
296
|
+
googleFontInternals.googleFontState = { cache: newCache, fetched: true };
|
|
297
|
+
await saveFontCache();
|
|
298
|
+
return;
|
|
299
|
+
} catch (err) {
|
|
300
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
301
|
+
if (message.startsWith("[RI-1207]")) {
|
|
302
|
+
console.warn(`${message} Skipping.`);
|
|
303
|
+
if (await loadFontCache(true)) return;
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (await loadFontCache(true)) return;
|
|
307
|
+
console.warn(
|
|
308
|
+
`[RI-1205] Could not fetch Google Fonts metadata after 3 attempts (${message}). Fonts without explicit weight/style will default to "100 900" + "normal italic" \u2014 non-variable fonts may produce broken Google Fonts URLs. Run with network access to populate the metadata cache, or set an explicit weight/style in the @font directive.`
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
} finally {
|
|
312
|
+
if (!googleFontInternals.googleFontState.fetched) {
|
|
313
|
+
googleFontInternals.lastFetchFailureMs = Date.now();
|
|
314
|
+
}
|
|
315
|
+
googleFontInternals.googleFontListPromise = null;
|
|
316
|
+
}
|
|
317
|
+
})();
|
|
318
|
+
googleFontInternals.googleFontListPromise = localPromise;
|
|
319
|
+
return localPromise;
|
|
320
|
+
}
|
|
321
|
+
var refreshMemo = /* @__PURE__ */ new WeakMap();
|
|
322
|
+
function refreshFontWeightDefaults(fonts) {
|
|
323
|
+
const state = googleFontInternals.googleFontState;
|
|
324
|
+
const memo = refreshMemo.get(fonts);
|
|
325
|
+
if (memo && memo.state === state) return memo.result;
|
|
326
|
+
let anyChanged = false;
|
|
327
|
+
const refreshed = fonts.map((slot) => {
|
|
328
|
+
if (slot.kind !== "google") return slot;
|
|
329
|
+
const meta = googleFontInternals.googleFontState.cache.get(slot.family);
|
|
330
|
+
if (!meta) return slot;
|
|
331
|
+
let changed = false;
|
|
332
|
+
const faces = slot.faces.map((f) => {
|
|
333
|
+
let next = f;
|
|
334
|
+
if (!f._weightExplicit) {
|
|
335
|
+
const wghtAxis = meta.axes?.find((a) => a.tag === "wght");
|
|
336
|
+
const axisWeight = wghtAxis && wghtAxis.start !== wghtAxis.end ? `${wghtAxis.start} ${wghtAxis.end}` : "400";
|
|
337
|
+
if (next.weight !== axisWeight) next = { ...next, weight: axisWeight };
|
|
338
|
+
}
|
|
339
|
+
if (!f._styleExplicit) {
|
|
340
|
+
const italAxis = meta.axes?.find((a) => a.tag === "ital");
|
|
341
|
+
const axisStyle = italAxis ? "normal italic" : "normal";
|
|
342
|
+
if (next.style !== axisStyle) next = { ...next, style: axisStyle };
|
|
343
|
+
}
|
|
344
|
+
if (next !== f) changed = true;
|
|
345
|
+
return next;
|
|
346
|
+
});
|
|
347
|
+
if (changed) anyChanged = true;
|
|
348
|
+
return changed ? { ...slot, faces } : slot;
|
|
349
|
+
});
|
|
350
|
+
const result = anyChanged ? refreshed : fonts;
|
|
351
|
+
refreshMemo.set(fonts, { state, result });
|
|
352
|
+
return result;
|
|
353
|
+
}
|
|
354
|
+
var FONT_FETCH_TIMEOUT_MS = 1e4;
|
|
355
|
+
async function resolveGoogleFonts(fonts) {
|
|
356
|
+
if (!fonts.some((slot) => slot.kind === "google")) return fonts;
|
|
357
|
+
try {
|
|
358
|
+
await withTimeout(
|
|
359
|
+
fetchGoogleFontList(),
|
|
360
|
+
FONT_FETCH_TIMEOUT_MS,
|
|
361
|
+
"[RI-1213] Google Fonts metadata fetch timed out"
|
|
362
|
+
);
|
|
363
|
+
} catch {
|
|
364
|
+
console.warn(
|
|
365
|
+
`[RI-1213] Could not fetch Google Fonts metadata within ${FONT_FETCH_TIMEOUT_MS / 1e3}s \u2014 proceeding with default font weights. Variable-weight fonts may use "400" instead of their full range.`
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
return refreshFontWeightDefaults(fonts);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// src/integrations/font-providers/metrics-data.ts
|
|
372
|
+
var FONT_METRICS_TABLE = {
|
|
373
|
+
abel: [2006, -604, 0, 2048, 783, "sans-serif"],
|
|
374
|
+
alegreya: [1016, -345, 0, 1e3, 410, "serif"],
|
|
375
|
+
"anonymous pro": [1675, -373, 0, 2048, 1118, "monospace"],
|
|
376
|
+
archivo: [878, -210, 0, 1e3, 440, "sans-serif"],
|
|
377
|
+
arial: [1854, -434, 67, 2048, 913, "sans-serif"],
|
|
378
|
+
asap: [934, -212, 0, 1e3, 442, "sans-serif"],
|
|
379
|
+
barlow: [1e3, -200, 0, 1e3, 431, "sans-serif"],
|
|
380
|
+
bitter: [935, -265, 0, 1e3, 465, "serif"],
|
|
381
|
+
"bricolage grotesque": [930, -270, 0, 1e3, 470, "sans-serif"],
|
|
382
|
+
cabin: [1930, -500, 0, 2e3, 844, "sans-serif"],
|
|
383
|
+
catamaran: [1100, -540, 0, 1e3, 411, "sans-serif"],
|
|
384
|
+
chivo: [940, -250, 0, 1e3, 478, "sans-serif"],
|
|
385
|
+
"cormorant garamond": [924, -287, 0, 1e3, 394, "serif"],
|
|
386
|
+
"courier new": [1705, -615, 0, 2048, 1229, "monospace"],
|
|
387
|
+
"crimson pro": [918, -220, 0, 1024, 410, "serif"],
|
|
388
|
+
"crimson text": [972, -359, 0, 1024, 405, "serif"],
|
|
389
|
+
"dm sans": [992, -310, 0, 1e3, 466, "sans-serif"],
|
|
390
|
+
"dm serif display": [1036, -335, 0, 1e3, 446, "serif"],
|
|
391
|
+
dosis: [1027, -237, 0, 1e3, 377, "sans-serif"],
|
|
392
|
+
"eb garamond": [1007, -298, 0, 1e3, 385, "serif"],
|
|
393
|
+
epilogue: [1580, -470, 0, 2e3, 990, "sans-serif"],
|
|
394
|
+
"exo 2": [999, -201, 0, 1e3, 455, "sans-serif"],
|
|
395
|
+
figtree: [950, -250, 0, 1e3, 449, "sans-serif"],
|
|
396
|
+
"fira code": [1980, -644, 0, 2e3, 1200, "monospace"],
|
|
397
|
+
"fira sans": [935, -265, 0, 1e3, 458, "sans-serif"],
|
|
398
|
+
fraunces: [1956, -510, 0, 2e3, 938, "serif"],
|
|
399
|
+
gabarito: [940, -260, 0, 1e3, 442, "display"],
|
|
400
|
+
"geist mono": [1005, -295, 0, 1e3, 600, "monospace"],
|
|
401
|
+
geist: [1005, -295, 0, 1e3, 467, "sans-serif"],
|
|
402
|
+
georgia: [1878, -449, 0, 2048, 913, "serif"],
|
|
403
|
+
heebo: [2146, -862, 0, 2048, 912, "sans-serif"],
|
|
404
|
+
"helvetica neue": [952, -213, 28, 1e3, 450, "sans-serif"],
|
|
405
|
+
helvetica: [1577, -471, 0, 2048, 913, "sans-serif"],
|
|
406
|
+
hind: [1055, -546, 0, 1e3, 429, "sans-serif"],
|
|
407
|
+
"ibm plex mono": [1025, -275, 0, 1e3, 600, "monospace"],
|
|
408
|
+
"ibm plex sans": [1025, -275, 0, 1e3, 451, "sans-serif"],
|
|
409
|
+
"ibm plex serif": [1025, -275, 0, 1e3, 473, "serif"],
|
|
410
|
+
inconsolata: [859, -190, 0, 1e3, 500, "monospace"],
|
|
411
|
+
"instrument sans": [970, -250, 0, 1e3, 458, "sans-serif"],
|
|
412
|
+
"instrument serif": [990, -310, 0, 1e3, 341, "serif"],
|
|
413
|
+
inter: [1984, -494, 0, 2048, 978, "sans-serif"],
|
|
414
|
+
"jetbrains mono": [1020, -300, 0, 1e3, 600, "monospace"],
|
|
415
|
+
"josefin sans": [750, -250, 0, 1e3, 456, "sans-serif"],
|
|
416
|
+
kanit: [1100, -395, 0, 1e3, 452, "sans-serif"],
|
|
417
|
+
karla: [1834, -504, 0, 2e3, 913, "sans-serif"],
|
|
418
|
+
lato: [1974, -426, 0, 2e3, 871, "sans-serif"],
|
|
419
|
+
lexend: [1e3, -250, 0, 1e3, 490, "sans-serif"],
|
|
420
|
+
"libre baskerville": [970, -270, 0, 1e3, 517, "serif"],
|
|
421
|
+
"libre franklin": [966, -246, 0, 1e3, 465, "sans-serif"],
|
|
422
|
+
lora: [1006, -274, 0, 1e3, 468, "serif"],
|
|
423
|
+
manrope: [2132, -600, 0, 2e3, 920, "sans-serif"],
|
|
424
|
+
"maven pro": [965, -210, 0, 1e3, 462, "sans-serif"],
|
|
425
|
+
merriweather: [1968, -546, 0, 2e3, 970, "serif"],
|
|
426
|
+
montserrat: [968, -251, 0, 1e3, 503, "sans-serif"],
|
|
427
|
+
mulish: [1005, -250, 0, 1e3, 464, "sans-serif"],
|
|
428
|
+
newsreader: [1470, -530, 0, 2e3, 857, "serif"],
|
|
429
|
+
"noto sans": [1069, -293, 0, 1e3, 474, "sans-serif"],
|
|
430
|
+
"noto serif": [1069, -293, 0, 1e3, 481, "serif"],
|
|
431
|
+
"nunito sans": [1011, -353, 0, 1e3, 452, "sans-serif"],
|
|
432
|
+
nunito: [1011, -353, 0, 1e3, 452, "sans-serif"],
|
|
433
|
+
onest: [970, -305, 0, 1e3, 469, "sans-serif"],
|
|
434
|
+
"open sans": [2189, -600, 0, 2048, 960, "sans-serif"],
|
|
435
|
+
oswald: [1193, -289, 0, 1e3, 363, "sans-serif"],
|
|
436
|
+
outfit: [1e3, -260, 0, 1e3, 445, "sans-serif"],
|
|
437
|
+
overpass: [1766, -766, 0, 2e3, 898, "sans-serif"],
|
|
438
|
+
oxygen: [2103, -483, 0, 2048, 923, "sans-serif"],
|
|
439
|
+
"playfair display": [1082, -251, 0, 1e3, 452, "serif"],
|
|
440
|
+
"plus jakarta sans": [1038, -222, 0, 1e3, 468, "sans-serif"],
|
|
441
|
+
poppins: [1050, -350, 100, 1e3, 500, "sans-serif"],
|
|
442
|
+
"pt sans": [1018, -276, 0, 1e3, 431, "sans-serif"],
|
|
443
|
+
"pt serif": [1039, -286, 0, 1e3, 448, "serif"],
|
|
444
|
+
"public sans": [1900, -450, 0, 2e3, 935, "sans-serif"],
|
|
445
|
+
quicksand: [1e3, -250, 0, 1e3, 465, "sans-serif"],
|
|
446
|
+
raleway: [940, -234, 0, 1e3, 463, "sans-serif"],
|
|
447
|
+
"red hat display": [1018, -305, 0, 1e3, 442, "sans-serif"],
|
|
448
|
+
"red hat text": [1018, -305, 0, 1e3, 447, "sans-serif"],
|
|
449
|
+
"roboto condensed": [1900, -500, 0, 2048, 811, "sans-serif"],
|
|
450
|
+
"roboto flex": [1900, -500, 0, 2048, 908, "sans-serif"],
|
|
451
|
+
"roboto mono": [2146, -555, 0, 2048, 1229, "monospace"],
|
|
452
|
+
"roboto slab": [2146, -555, 0, 2048, 972, "serif"],
|
|
453
|
+
roboto: [1900, -500, 0, 2048, 911, "sans-serif"],
|
|
454
|
+
rubik: [935, -250, 0, 1e3, 468, "sans-serif"],
|
|
455
|
+
"schibsted grotesk": [2e3, -528, 0, 2048, 954, "sans-serif"],
|
|
456
|
+
"segoe ui": [2210, -514, 0, 2048, 908, "sans-serif"],
|
|
457
|
+
sora: [970, -290, 0, 1e3, 507, "sans-serif"],
|
|
458
|
+
"source code pro": [984, -273, 0, 1e3, 600, "monospace"],
|
|
459
|
+
"source sans 3": [1024, -400, 0, 1e3, 418, "sans-serif"],
|
|
460
|
+
"source serif 4": [1036, -335, 0, 1e3, 479, "serif"],
|
|
461
|
+
"space grotesk": [984, -292, 0, 1e3, 489, "sans-serif"],
|
|
462
|
+
"space mono": [1120, -361, 0, 1e3, 612, "monospace"],
|
|
463
|
+
spectral: [1059, -463, 0, 1e3, 446, "serif"],
|
|
464
|
+
tahoma: [2049, -423, 0, 2048, 917, "sans-serif"],
|
|
465
|
+
"times new roman": [1825, -443, 87, 2048, 832, "serif"],
|
|
466
|
+
"titillium web": [1133, -388, 0, 1e3, 421, "sans-serif"],
|
|
467
|
+
"trebuchet ms": [1923, -455, 0, 2048, 934, "sans-serif"],
|
|
468
|
+
"ubuntu mono": [830, -170, 0, 1e3, 500, "monospace"],
|
|
469
|
+
ubuntu: [932, -189, 28, 1e3, 455, "sans-serif"],
|
|
470
|
+
urbanist: [1900, -500, 0, 2e3, 883, "sans-serif"],
|
|
471
|
+
"varela round": [918, -286, 0, 1e3, 478, "sans-serif"],
|
|
472
|
+
verdana: [2059, -430, 0, 2048, 1049, "sans-serif"],
|
|
473
|
+
"victor mono": [1100, -250, 0, 1e3, 600, "monospace"],
|
|
474
|
+
vollkorn: [952, -441, 0, 1e3, 438, "serif"],
|
|
475
|
+
"work sans": [930, -243, 0, 1e3, 499, "sans-serif"],
|
|
476
|
+
"zilla slab": [944, -256, 0, 1e3, 434, "serif"]
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
// src/integrations/font-providers/metrics.ts
|
|
480
|
+
var CATEGORY_FALLBACK = {
|
|
481
|
+
"sans-serif": "Arial",
|
|
482
|
+
serif: "Times New Roman",
|
|
483
|
+
monospace: "Courier New"
|
|
484
|
+
};
|
|
485
|
+
function lookupFontMetrics(family) {
|
|
486
|
+
return FONT_METRICS_TABLE[family.trim().toLowerCase()];
|
|
487
|
+
}
|
|
488
|
+
var round4 = (n) => Math.round(n * 1e4) / 1e4;
|
|
489
|
+
function computeFallbackMetrics(fallbackName, font, fallbackFont) {
|
|
490
|
+
const [ascent, descent, lineGap, unitsPerEm, xWidthAvg] = font;
|
|
491
|
+
const [, , , fbUnitsPerEm, fbXWidthAvg] = fallbackFont;
|
|
492
|
+
const sizeAdjust = xWidthAvg / unitsPerEm / (fbXWidthAvg / fbUnitsPerEm);
|
|
493
|
+
return {
|
|
494
|
+
fallback: fallbackName,
|
|
495
|
+
sizeAdjust: round4(sizeAdjust * 100),
|
|
496
|
+
ascent: round4(ascent / unitsPerEm / sizeAdjust * 100),
|
|
497
|
+
descent: round4(Math.abs(descent) / unitsPerEm / sizeAdjust * 100),
|
|
498
|
+
lineGap: round4(lineGap / unitsPerEm / sizeAdjust * 100)
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
function resolveAutoMetrics(family, fallbackStack, explicitFallback) {
|
|
502
|
+
const font = lookupFontMetrics(family);
|
|
503
|
+
if (!font) return null;
|
|
504
|
+
const fallbackName = explicitFallback ?? fallbackStack.find((f) => lookupFontMetrics(f)) ?? CATEGORY_FALLBACK[font[5]] ?? "Arial";
|
|
505
|
+
const fallbackFont = lookupFontMetrics(fallbackName);
|
|
506
|
+
if (!fallbackFont) return null;
|
|
507
|
+
return computeFallbackMetrics(fallbackName, font, fallbackFont);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// src/integrations/font-providers/index.ts
|
|
511
|
+
var SYSTEM_STACKS = Object.freeze({
|
|
512
|
+
sans: 'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
|
|
513
|
+
serif: 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
|
|
514
|
+
mono: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'
|
|
515
|
+
});
|
|
516
|
+
function getFallbackStack(slot) {
|
|
517
|
+
if (!SYSTEM_STACKS[slot] && isRIDebug()) {
|
|
518
|
+
console.warn(`[RI-DEBUG] Unknown font slot "${slot}" \u2014 falling back to sans stack.`);
|
|
519
|
+
}
|
|
520
|
+
return SYSTEM_STACKS[slot] || SYSTEM_STACKS.sans;
|
|
521
|
+
}
|
|
522
|
+
function googleFontsUrl(family, face) {
|
|
523
|
+
const encodedFamily = encodeURIComponent(family).replace(/%20/g, "+");
|
|
524
|
+
let axisParam;
|
|
525
|
+
if (face.weight.includes(",")) {
|
|
526
|
+
const weights = face.weight.split(",").map((w) => w.trim());
|
|
527
|
+
if (face.style.includes("italic")) {
|
|
528
|
+
const tuples = weights.flatMap((w) => [`0,${w}`, `1,${w}`]);
|
|
529
|
+
axisParam = `ital,wght@${tuples.join(";")}`;
|
|
530
|
+
} else {
|
|
531
|
+
axisParam = `wght@${weights.join(";")}`;
|
|
532
|
+
}
|
|
533
|
+
} else if (face.weight.includes(" ")) {
|
|
534
|
+
const range = face.weight.replace(" ", "..");
|
|
535
|
+
axisParam = face.style.includes("italic") ? `ital,wght@0,${range};1,${range}` : `wght@${range}`;
|
|
536
|
+
} else {
|
|
537
|
+
const w = face.weight || "400";
|
|
538
|
+
axisParam = face.style.includes("italic") ? `ital,wght@0,${w};1,${w}` : `wght@${w}`;
|
|
539
|
+
}
|
|
540
|
+
const display = face.display || "swap";
|
|
541
|
+
return `https://fonts.googleapis.com/css2?family=${encodedFamily}:${axisParam}&display=${display}`;
|
|
542
|
+
}
|
|
543
|
+
function escapeFontFamily(name) {
|
|
544
|
+
return name.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\0/g, "\\0").replace(/\n/g, "\\a ").replace(/\r/g, "\\d ");
|
|
545
|
+
}
|
|
546
|
+
function generateFallbackFontFace(family, metrics) {
|
|
547
|
+
const safeFamily = escapeFontFamily(family);
|
|
548
|
+
return `@font-face {
|
|
549
|
+
font-family: "${safeFamily} Fallback";
|
|
550
|
+
src: local("${escapeFontFamily(metrics.fallback)}");
|
|
551
|
+
size-adjust: ${metrics.sizeAdjust}%;
|
|
552
|
+
ascent-override: ${metrics.ascent}%;
|
|
553
|
+
descent-override: ${metrics.descent}%;
|
|
554
|
+
line-gap-override: ${metrics.lineGap}%;
|
|
555
|
+
}`;
|
|
556
|
+
}
|
|
557
|
+
var FONT_FORMAT_MIME = {
|
|
558
|
+
woff2: "font/woff2",
|
|
559
|
+
woff: "font/woff",
|
|
560
|
+
truetype: "font/ttf",
|
|
561
|
+
opentype: "font/otf"
|
|
562
|
+
};
|
|
563
|
+
function inferFontFormat(path) {
|
|
564
|
+
if (path.endsWith(".woff2")) return "woff2";
|
|
565
|
+
if (path.endsWith(".woff")) return "woff";
|
|
566
|
+
if (path.endsWith(".ttf")) return "truetype";
|
|
567
|
+
if (path.endsWith(".otf")) return "opentype";
|
|
568
|
+
return "woff2";
|
|
569
|
+
}
|
|
570
|
+
function generateWebFontFace(family, face) {
|
|
571
|
+
if (face.provider === "system" || !face.provider) return null;
|
|
572
|
+
if (face.provider === "google") {
|
|
573
|
+
return { type: "import", css: `@import url("${googleFontsUrl(family, face)}");` };
|
|
574
|
+
}
|
|
575
|
+
const safeProvider = escapeFontFamily(face.provider);
|
|
576
|
+
const format = inferFontFormat(face.provider);
|
|
577
|
+
const src = `url("${safeProvider}") format("${format}")`;
|
|
578
|
+
const declarations = [` font-family: "${escapeFontFamily(family)}";`, ` src: ${src};`];
|
|
579
|
+
if (face.weight) declarations.push(` font-weight: ${face.weight};`);
|
|
580
|
+
if (face.style && face.style !== "normal") declarations.push(` font-style: ${face.style};`);
|
|
581
|
+
if (face.display) declarations.push(` font-display: ${face.display};`);
|
|
582
|
+
if (face.unicodeRange) declarations.push(` unicode-range: ${face.unicodeRange};`);
|
|
583
|
+
return { type: "font-face", css: `@font-face {
|
|
584
|
+
${declarations.join("\n")}
|
|
585
|
+
}` };
|
|
586
|
+
}
|
|
587
|
+
function normalizeLocalStyle(style, family, warnings) {
|
|
588
|
+
if (style.includes(" ") && !style.startsWith("oblique")) {
|
|
589
|
+
const first = style.split(/\s+/)[0];
|
|
590
|
+
warnings.push(
|
|
591
|
+
`[RI-1203] Local font "${family}" has a compound font-style "${style}" \u2014 a single @font-face takes one style. Split upright and italic into separate @face blocks (or use the italic: shorthand). Using "${first}".`
|
|
592
|
+
);
|
|
593
|
+
return first;
|
|
594
|
+
}
|
|
595
|
+
return style;
|
|
596
|
+
}
|
|
597
|
+
function resolveSlotMetrics(slot, warnings) {
|
|
598
|
+
const cfg = slot.metrics;
|
|
599
|
+
if (cfg === null) return null;
|
|
600
|
+
if (cfg?.sizeAdjust !== void 0) {
|
|
601
|
+
return {
|
|
602
|
+
fallback: cfg.fallback || slot.fallback[0] || "Arial",
|
|
603
|
+
sizeAdjust: cfg.sizeAdjust,
|
|
604
|
+
ascent: cfg.ascent,
|
|
605
|
+
descent: cfg.descent,
|
|
606
|
+
lineGap: cfg.lineGap
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
const resolved = resolveAutoMetrics(slot.family, slot.fallback, cfg?.fallback);
|
|
610
|
+
if (!resolved && cfg?.fallback) {
|
|
611
|
+
const missing = lookupFontMetrics(slot.family) ? cfg.fallback : slot.family;
|
|
612
|
+
warnings.push(
|
|
613
|
+
`[RI-1220] @font slot "${slot.slot}" requests metrics matching against "${cfg.fallback}", but "${missing}" is not in the built-in metrics table \u2014 no fallback @font-face was generated. Provide the four percentages explicitly (metrics: "${cfg.fallback}" <size-adjust> <ascent> <descent> <line-gap>) or use \`metrics: none\`.`
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
return resolved;
|
|
617
|
+
}
|
|
618
|
+
function generateFontCSS(slot) {
|
|
619
|
+
const imports = [];
|
|
620
|
+
const fontFaces = [];
|
|
621
|
+
const variables = [];
|
|
622
|
+
const warnings = [];
|
|
623
|
+
const pushFeatureVars = () => {
|
|
624
|
+
if (slot.features) variables.push(`--font-${slot.slot}--features: ${slot.features};`);
|
|
625
|
+
if (slot.variation) variables.push(`--font-${slot.slot}--variations: ${slot.variation};`);
|
|
626
|
+
};
|
|
627
|
+
if (slot.kind === "system") {
|
|
628
|
+
variables.push(`--font-${slot.slot}: ${getFallbackStack(slot.slot)};`);
|
|
629
|
+
return { imports, fontFaces, variables, warnings };
|
|
630
|
+
}
|
|
631
|
+
if (slot.kind === "manual") {
|
|
632
|
+
const stack = [`"${escapeFontFamily(slot.family)}"`, ...slot.fallback].join(", ");
|
|
633
|
+
variables.push(`--font-${slot.slot}: ${stack};`);
|
|
634
|
+
pushFeatureVars();
|
|
635
|
+
return { imports, fontFaces, variables, warnings };
|
|
636
|
+
}
|
|
637
|
+
const metrics = resolveSlotMetrics(slot, warnings);
|
|
638
|
+
if (metrics) fontFaces.push(generateFallbackFontFace(slot.family, metrics));
|
|
639
|
+
for (const face of slot.faces) {
|
|
640
|
+
if (face.provider !== "google" && !face.provider.startsWith("/") && !face.provider.startsWith("http") && !face.provider.startsWith(".")) {
|
|
641
|
+
warnings.push(
|
|
642
|
+
`[RI-1201] Unknown font provider "${face.provider}" for "${slot.family}" \u2014 supported: google, or a file path/URL.`
|
|
643
|
+
);
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
const emitFace = face.provider === "google" ? face : { ...face, style: normalizeLocalStyle(face.style, slot.family, warnings) };
|
|
647
|
+
const webFont = generateWebFontFace(slot.family, emitFace);
|
|
648
|
+
if (!webFont) continue;
|
|
649
|
+
if (webFont.type === "import") imports.push(webFont.css);
|
|
650
|
+
else fontFaces.push(webFont.css);
|
|
651
|
+
}
|
|
652
|
+
const fallbackStack = slot.fallback.length > 0 ? slot.fallback.join(", ") : getFallbackStack(slot.slot);
|
|
653
|
+
const safeFamily = escapeFontFamily(slot.family);
|
|
654
|
+
const stackParts = [`"${safeFamily}"`];
|
|
655
|
+
if (metrics) stackParts.push(`"${safeFamily} Fallback"`);
|
|
656
|
+
stackParts.push(fallbackStack);
|
|
657
|
+
variables.push(`--font-${slot.slot}: ${stackParts.join(", ")};`);
|
|
658
|
+
pushFeatureVars();
|
|
659
|
+
return { imports, fontFaces, variables, warnings };
|
|
660
|
+
}
|
|
661
|
+
function getFontPreloadLinks(slots) {
|
|
662
|
+
const links = [];
|
|
663
|
+
const seen = /* @__PURE__ */ new Set();
|
|
664
|
+
for (const slot of slots) {
|
|
665
|
+
for (const face of slot.faces) {
|
|
666
|
+
if (!face.preload) continue;
|
|
667
|
+
if (face.provider === "system" || face.provider === "google" || !face.provider) continue;
|
|
668
|
+
if (seen.has(face.provider)) continue;
|
|
669
|
+
seen.add(face.provider);
|
|
670
|
+
links.push({
|
|
671
|
+
href: face.provider,
|
|
672
|
+
as: "font",
|
|
673
|
+
type: FONT_FORMAT_MIME[inferFontFormat(face.provider)],
|
|
674
|
+
crossorigin: true
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return links;
|
|
587
679
|
}
|
|
588
680
|
|
|
589
|
-
// src/
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
681
|
+
// src/scanner/sources.ts
|
|
682
|
+
import { readFile, stat } from "fs/promises";
|
|
683
|
+
import { resolve as resolve3 } from "path";
|
|
684
|
+
import { glob } from "tinyglobby";
|
|
685
|
+
|
|
686
|
+
// src/scanner/glob-utils.ts
|
|
687
|
+
import { isAbsolute as isAbsolute2, win32 } from "path";
|
|
688
|
+
function validateGlobPattern(pattern) {
|
|
689
|
+
if (!pattern?.trim()) {
|
|
690
|
+
return "Glob pattern is empty.";
|
|
596
691
|
}
|
|
597
|
-
|
|
692
|
+
if (pattern.includes("\0")) {
|
|
693
|
+
return "Glob pattern contains a null byte, which is invalid in file paths.";
|
|
694
|
+
}
|
|
695
|
+
if (isAbsolute2(pattern) || win32.isAbsolute(pattern)) {
|
|
696
|
+
return `Glob pattern "${pattern}" must be relative, not absolute.`;
|
|
697
|
+
}
|
|
698
|
+
const segments = pattern.split(/[\\/]+/);
|
|
699
|
+
for (const seg of segments) {
|
|
700
|
+
if (seg === "..") {
|
|
701
|
+
return `Glob pattern "${pattern}" must not traverse parent directories (".."). Restructure your project layout so source files are within the project root, or use @source with a pattern rooted at the project directory.`;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return null;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// src/scanner/package-discovery.ts
|
|
708
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "fs";
|
|
709
|
+
import { dirname as dirname2, join as join2, posix, resolve as resolve2 } from "path";
|
|
710
|
+
var EMPTY = Object.freeze({ sources: [], warnings: [] });
|
|
711
|
+
var discoveryCache = /* @__PURE__ */ new Map();
|
|
712
|
+
function discoverPackageSafelistSources(cwd) {
|
|
713
|
+
const cwdAbs = resolve2(cwd);
|
|
714
|
+
let mtimeMs;
|
|
715
|
+
try {
|
|
716
|
+
mtimeMs = statSync(join2(cwdAbs, "package.json")).mtimeMs;
|
|
717
|
+
} catch {
|
|
718
|
+
return EMPTY;
|
|
719
|
+
}
|
|
720
|
+
const cached = discoveryCache.get(cwdAbs);
|
|
721
|
+
if (cached && cached.mtimeMs === mtimeMs) return cached.result;
|
|
722
|
+
const result = runDiscovery(cwdAbs);
|
|
723
|
+
discoveryCache.set(cwdAbs, { mtimeMs, result });
|
|
724
|
+
return result;
|
|
725
|
+
}
|
|
726
|
+
function runDiscovery(cwdAbs) {
|
|
727
|
+
let consumer;
|
|
728
|
+
try {
|
|
729
|
+
consumer = readPackageJson(join2(cwdAbs, "package.json"));
|
|
730
|
+
} catch {
|
|
731
|
+
return EMPTY;
|
|
732
|
+
}
|
|
733
|
+
const deps = [
|
|
734
|
+
...Object.keys(consumer.dependencies ?? {}),
|
|
735
|
+
...Object.keys(consumer.peerDependencies ?? {})
|
|
736
|
+
];
|
|
737
|
+
if (deps.length === 0) return EMPTY;
|
|
738
|
+
const sources = [];
|
|
739
|
+
const warnings = [];
|
|
740
|
+
for (const depName of deps) {
|
|
741
|
+
const depPkgPath = findDepPackageJson(cwdAbs, depName);
|
|
742
|
+
if (!depPkgPath) {
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
let depPkg;
|
|
598
746
|
try {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
747
|
+
depPkg = readPackageJson(depPkgPath);
|
|
748
|
+
} catch (err) {
|
|
749
|
+
warnings.push(
|
|
750
|
+
`[RI-1410] Could not read ${depName}/package.json during safelist discovery: ${errMessage(err)}`
|
|
751
|
+
);
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
const patterns = depPkg.rainbowindex?.safelistSources;
|
|
755
|
+
if (patterns == null) continue;
|
|
756
|
+
if (!Array.isArray(patterns)) {
|
|
757
|
+
warnings.push(
|
|
758
|
+
`[RI-1410] Invalid rainbowindex.safelistSources in ${depName} \u2014 expected an array of glob strings.`
|
|
759
|
+
);
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
if (patterns.length === 0) continue;
|
|
763
|
+
const depRoot = realpathOrFallback(dirname2(depPkgPath)).replace(/\\/g, "/");
|
|
764
|
+
for (const pattern of patterns) {
|
|
765
|
+
if (typeof pattern !== "string" || !pattern) {
|
|
766
|
+
warnings.push(
|
|
767
|
+
`[RI-1410] Invalid rainbowindex.safelistSources entry in ${depName} \u2014 expected a non-empty glob string.`
|
|
604
768
|
);
|
|
605
|
-
|
|
606
|
-
}
|
|
607
|
-
if (await loadFontCache()) return;
|
|
608
|
-
const fetchDisabled = process.env.RI_FETCH_FONTS === "0" || process.env.RI_FETCH_FONTS === "false";
|
|
609
|
-
if (fetchDisabled) {
|
|
610
|
-
if (await loadFontCache(true)) return;
|
|
611
|
-
return;
|
|
769
|
+
continue;
|
|
612
770
|
}
|
|
613
|
-
if (
|
|
614
|
-
|
|
615
|
-
|
|
771
|
+
if (validateGlobPattern(pattern) !== null) {
|
|
772
|
+
warnings.push(
|
|
773
|
+
`[RI-1410] Invalid rainbowindex.safelistSources entry "${pattern}" in ${depName} \u2014 patterns must be relative to the package root and must not traverse parent directories.`
|
|
616
774
|
);
|
|
617
|
-
|
|
618
|
-
}
|
|
619
|
-
if (isRIDebug()) {
|
|
620
|
-
console.warn("[RI-DEBUG] Fetching Google Fonts metadata...");
|
|
775
|
+
continue;
|
|
621
776
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
return;
|
|
627
|
-
} catch (err) {
|
|
628
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
629
|
-
if (message.startsWith("[RI-1207]")) {
|
|
630
|
-
console.warn(`${message} Skipping.`);
|
|
631
|
-
if (await loadFontCache(true)) return;
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
if (await loadFontCache(true)) return;
|
|
635
|
-
console.warn(
|
|
636
|
-
`[RI-1205] Could not fetch Google Fonts metadata after 3 attempts (${message}). Fonts without explicit weight/style will default to "100 900" + "normal italic" \u2014 non-variable fonts may produce broken Google Fonts URLs. Run with network access to populate the metadata cache, or set an explicit weight/style in the @font directive.`
|
|
777
|
+
const absolute = posix.normalize(`${depRoot}/${pattern.replace(/^\.\//, "")}`);
|
|
778
|
+
if (absolute !== depRoot && !absolute.startsWith(`${depRoot}/`)) {
|
|
779
|
+
warnings.push(
|
|
780
|
+
`[RI-1410] Invalid rainbowindex.safelistSources entry "${pattern}" in ${depName} \u2014 resolved outside the package root.`
|
|
637
781
|
);
|
|
782
|
+
continue;
|
|
638
783
|
}
|
|
639
|
-
|
|
640
|
-
if (!googleFontInternals.googleFontState.fetched) {
|
|
641
|
-
googleFontInternals.lastFetchFailureMs = Date.now();
|
|
642
|
-
}
|
|
643
|
-
googleFontInternals.googleFontListPromise = null;
|
|
784
|
+
sources.push({ pattern: absolute, negated: false, inline: false, absolute: true });
|
|
644
785
|
}
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
return localPromise;
|
|
786
|
+
}
|
|
787
|
+
return { sources, warnings };
|
|
648
788
|
}
|
|
649
|
-
function
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
const meta = googleFontInternals.googleFontState.cache.get(slot.family);
|
|
653
|
-
if (!meta) return slot;
|
|
654
|
-
let changed = false;
|
|
655
|
-
const faces = slot.faces.map((f) => {
|
|
656
|
-
let next = f;
|
|
657
|
-
if (!f._weightExplicit) {
|
|
658
|
-
const wghtAxis = meta.axes?.find((a) => a.tag === "wght");
|
|
659
|
-
const axisWeight = wghtAxis && wghtAxis.start !== wghtAxis.end ? `${wghtAxis.start} ${wghtAxis.end}` : "400";
|
|
660
|
-
if (next.weight !== axisWeight) next = { ...next, weight: axisWeight };
|
|
661
|
-
}
|
|
662
|
-
if (!f._styleExplicit) {
|
|
663
|
-
const italAxis = meta.axes?.find((a) => a.tag === "ital");
|
|
664
|
-
const axisStyle = italAxis ? "normal italic" : "normal";
|
|
665
|
-
if (next.style !== axisStyle) next = { ...next, style: axisStyle };
|
|
666
|
-
}
|
|
667
|
-
if (next !== f) changed = true;
|
|
668
|
-
return next;
|
|
669
|
-
});
|
|
670
|
-
return changed ? { ...slot, faces } : slot;
|
|
671
|
-
});
|
|
789
|
+
function readPackageJson(path) {
|
|
790
|
+
const raw = readFileSync(path, "utf8");
|
|
791
|
+
return JSON.parse(raw);
|
|
672
792
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
793
|
+
function findDepPackageJson(cwd, depName) {
|
|
794
|
+
let dir = cwd;
|
|
795
|
+
while (true) {
|
|
796
|
+
const candidate = join2(dir, "node_modules", depName, "package.json");
|
|
797
|
+
if (existsSync(candidate)) return candidate;
|
|
798
|
+
const parent = dirname2(dir);
|
|
799
|
+
if (parent === dir) return null;
|
|
800
|
+
dir = parent;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
function realpathOrFallback(path) {
|
|
676
804
|
try {
|
|
677
|
-
|
|
678
|
-
fetchGoogleFontList(),
|
|
679
|
-
FONT_FETCH_TIMEOUT_MS,
|
|
680
|
-
"[RI-1213] Google Fonts metadata fetch timed out"
|
|
681
|
-
);
|
|
805
|
+
return realpathSync(path);
|
|
682
806
|
} catch {
|
|
683
|
-
|
|
684
|
-
`[RI-1213] Could not fetch Google Fonts metadata within ${FONT_FETCH_TIMEOUT_MS / 1e3}s \u2014 proceeding with default font weights. Variable-weight fonts may use "400" instead of their full range.`
|
|
685
|
-
);
|
|
807
|
+
return path;
|
|
686
808
|
}
|
|
687
|
-
|
|
809
|
+
}
|
|
810
|
+
function errMessage(err) {
|
|
811
|
+
return err instanceof Error ? err.message : String(err);
|
|
688
812
|
}
|
|
689
813
|
|
|
690
|
-
// src/
|
|
691
|
-
var
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
814
|
+
// src/scanner/sources.ts
|
|
815
|
+
var DEFAULT_PATTERNS = Object.freeze([
|
|
816
|
+
"index.html",
|
|
817
|
+
"src/**/*.{html,js,jsx,ts,tsx,mdx,vue,svelte}"
|
|
818
|
+
]);
|
|
819
|
+
var DEFAULT_EXCLUDES = Object.freeze([
|
|
820
|
+
"node_modules/**",
|
|
821
|
+
"dist/**",
|
|
822
|
+
"build/**",
|
|
823
|
+
"coverage/**",
|
|
824
|
+
"public/**",
|
|
825
|
+
"**/*.config.*",
|
|
826
|
+
"**/*.d.ts"
|
|
827
|
+
]);
|
|
828
|
+
var MAX_FILE_SIZE = 1048576;
|
|
829
|
+
var MAX_INLINE_SOURCE_SIZE = 102400;
|
|
830
|
+
var GLOB_TIMEOUT_MS = 3e4;
|
|
831
|
+
var GLOB_TIMEOUT_MESSAGE = `glob() timed out after ${GLOB_TIMEOUT_MS / 1e3}s \u2014 check for network filesystem issues or overly broad @source patterns`;
|
|
832
|
+
function collectPatterns(sources) {
|
|
833
|
+
const includePatterns = [];
|
|
834
|
+
const nodeModulesIncludePatterns = [];
|
|
835
|
+
const excludePatterns = [...DEFAULT_EXCLUDES];
|
|
836
|
+
const hasUserPositiveGlobs = sources.some((s) => !s.inline && !s.negated && !s.absolute);
|
|
837
|
+
if (!hasUserPositiveGlobs) {
|
|
838
|
+
includePatterns.push(...DEFAULT_PATTERNS);
|
|
699
839
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
840
|
+
const warnings = [];
|
|
841
|
+
for (const src of sources) {
|
|
842
|
+
if (src.inline) continue;
|
|
843
|
+
if (!src.absolute) {
|
|
844
|
+
const err = validateGlobPattern(src.pattern);
|
|
845
|
+
if (err) {
|
|
846
|
+
warnings.push(`[RI-1404] @source pattern rejected: ${err}`);
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
if (src.negated) {
|
|
851
|
+
excludePatterns.push(src.pattern);
|
|
852
|
+
} else if (src.absolute || src.pattern.replace(/^\.\//, "").startsWith("node_modules/")) {
|
|
853
|
+
nodeModulesIncludePatterns.push(src.pattern);
|
|
710
854
|
} else {
|
|
711
|
-
|
|
855
|
+
includePatterns.push(src.pattern);
|
|
712
856
|
}
|
|
713
|
-
} else if (face.weight.includes(" ")) {
|
|
714
|
-
const range = face.weight.replace(" ", "..");
|
|
715
|
-
axisParam = face.style.includes("italic") ? `ital,wght@0,${range};1,${range}` : `wght@${range}`;
|
|
716
|
-
} else {
|
|
717
|
-
const w = face.weight || "400";
|
|
718
|
-
axisParam = face.style.includes("italic") ? `ital,wght@0,${w};1,${w}` : `wght@${w}`;
|
|
719
|
-
}
|
|
720
|
-
const display = face.display || "swap";
|
|
721
|
-
const subset = face.subset && face.subset !== "latin" ? `&subset=${encodeURIComponent(face.subset)}` : "";
|
|
722
|
-
return `https://fonts.googleapis.com/css2?family=${encodedFamily}:${axisParam}&display=${display}${subset}`;
|
|
723
|
-
}
|
|
724
|
-
function escapeFontFamily(name) {
|
|
725
|
-
return name.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\0/g, "\\0").replace(/\n/g, "\\a ").replace(/\r/g, "\\d ");
|
|
726
|
-
}
|
|
727
|
-
function generateFallbackFontFace(family, metrics) {
|
|
728
|
-
const safeFamily = escapeFontFamily(family);
|
|
729
|
-
return `@font-face {
|
|
730
|
-
font-family: "${safeFamily} Fallback";
|
|
731
|
-
src: local("${metrics.fallback}");
|
|
732
|
-
size-adjust: ${metrics.sizeAdjust}%;
|
|
733
|
-
ascent-override: ${metrics.ascent}%;
|
|
734
|
-
descent-override: ${metrics.descent}%;
|
|
735
|
-
line-gap-override: ${metrics.lineGap}%;
|
|
736
|
-
}`;
|
|
737
|
-
}
|
|
738
|
-
var FONT_FORMAT_MIME = {
|
|
739
|
-
woff2: "font/woff2",
|
|
740
|
-
woff: "font/woff",
|
|
741
|
-
truetype: "font/ttf",
|
|
742
|
-
opentype: "font/otf"
|
|
743
|
-
};
|
|
744
|
-
function inferFontFormat(path) {
|
|
745
|
-
if (path.endsWith(".woff2")) return "woff2";
|
|
746
|
-
if (path.endsWith(".woff")) return "woff";
|
|
747
|
-
if (path.endsWith(".ttf")) return "truetype";
|
|
748
|
-
if (path.endsWith(".otf")) return "opentype";
|
|
749
|
-
return "woff2";
|
|
750
|
-
}
|
|
751
|
-
function generateWebFontFace(family, face) {
|
|
752
|
-
if (face.provider === "system" || !face.provider) return null;
|
|
753
|
-
if (face.provider === "google") {
|
|
754
|
-
return { type: "import", css: `@import url("${googleFontsUrl(family, face)}");` };
|
|
755
857
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
if (face.unicodeRange) declarations.push(` unicode-range: ${face.unicodeRange};`);
|
|
764
|
-
return { type: "font-face", css: `@font-face {
|
|
765
|
-
${declarations.join("\n")}
|
|
766
|
-
}` };
|
|
858
|
+
return {
|
|
859
|
+
includePatterns,
|
|
860
|
+
nodeModulesIncludePatterns,
|
|
861
|
+
excludePatterns,
|
|
862
|
+
warnings,
|
|
863
|
+
hasUserPositiveGlobs
|
|
864
|
+
};
|
|
767
865
|
}
|
|
768
|
-
function
|
|
769
|
-
|
|
770
|
-
|
|
866
|
+
async function resolveSourceFilesAsync(sources, cwd, hasInlineClasses = false) {
|
|
867
|
+
const {
|
|
868
|
+
includePatterns,
|
|
869
|
+
nodeModulesIncludePatterns,
|
|
870
|
+
excludePatterns,
|
|
871
|
+
warnings,
|
|
872
|
+
hasUserPositiveGlobs
|
|
873
|
+
} = collectPatterns(sources);
|
|
874
|
+
const allIncludes = [...includePatterns, ...nodeModulesIncludePatterns];
|
|
875
|
+
if (allIncludes.length === 0) return { files: [], warnings };
|
|
876
|
+
try {
|
|
877
|
+
const globPasses = [];
|
|
878
|
+
if (includePatterns.length > 0) {
|
|
879
|
+
globPasses.push(
|
|
880
|
+
withTimeout(
|
|
881
|
+
glob(includePatterns, { cwd, ignore: excludePatterns }),
|
|
882
|
+
GLOB_TIMEOUT_MS,
|
|
883
|
+
GLOB_TIMEOUT_MESSAGE
|
|
884
|
+
)
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
if (nodeModulesIncludePatterns.length > 0) {
|
|
888
|
+
const relaxedExcludes = excludePatterns.filter((p) => p !== "node_modules/**");
|
|
889
|
+
globPasses.push(
|
|
890
|
+
withTimeout(
|
|
891
|
+
glob(nodeModulesIncludePatterns, { cwd, ignore: relaxedExcludes }),
|
|
892
|
+
GLOB_TIMEOUT_MS,
|
|
893
|
+
GLOB_TIMEOUT_MESSAGE
|
|
894
|
+
)
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
const matched = (await Promise.all(globPasses)).flat();
|
|
898
|
+
const files = [...new Set(matched.map((f) => resolve3(cwd, f)))].sort(codepointCompare);
|
|
899
|
+
if (files.length === 0 && !(hasInlineClasses && !hasUserPositiveGlobs)) {
|
|
900
|
+
warnings.push(
|
|
901
|
+
`[RI-1401] No source files found matching ${allIncludes.map((p) => `"${p}"`).join(", ")} \u2014 check @source paths or project structure.`
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
return { files, warnings };
|
|
905
|
+
} catch (err) {
|
|
771
906
|
warnings.push(
|
|
772
|
-
`[RI-
|
|
907
|
+
`[RI-1402] Invalid glob pattern \u2014 skipping. Check @source syntax. (${err instanceof Error ? err.message : String(err)})`
|
|
773
908
|
);
|
|
774
|
-
return
|
|
909
|
+
return { files: [], warnings };
|
|
775
910
|
}
|
|
776
|
-
return style;
|
|
777
911
|
}
|
|
778
|
-
function
|
|
779
|
-
const
|
|
780
|
-
const fontFaces = [];
|
|
781
|
-
const variables = [];
|
|
912
|
+
function collectInlineClasses(sources) {
|
|
913
|
+
const classes = /* @__PURE__ */ new Set();
|
|
782
914
|
const warnings = [];
|
|
783
|
-
const
|
|
784
|
-
if (
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
return { imports, fontFaces, variables, warnings };
|
|
790
|
-
}
|
|
791
|
-
if (slot.kind === "manual") {
|
|
792
|
-
const stack = [`"${escapeFontFamily(slot.family)}"`, ...slot.fallback].join(", ");
|
|
793
|
-
variables.push(`--font-${slot.slot}: ${stack};`);
|
|
794
|
-
pushFeatureVars();
|
|
795
|
-
return { imports, fontFaces, variables, warnings };
|
|
796
|
-
}
|
|
797
|
-
const { sizeAdjust, ascent, descent, lineGap } = slot;
|
|
798
|
-
const hasMetrics = sizeAdjust !== void 0 && ascent !== void 0 && descent !== void 0 && lineGap !== void 0;
|
|
799
|
-
if (hasMetrics) {
|
|
800
|
-
const metricsFallbackFont = slot.metricsFallback || slot.fallback[0] || "Arial";
|
|
801
|
-
fontFaces.push(
|
|
802
|
-
generateFallbackFontFace(slot.family, {
|
|
803
|
-
fallback: metricsFallbackFont,
|
|
804
|
-
sizeAdjust,
|
|
805
|
-
ascent,
|
|
806
|
-
descent,
|
|
807
|
-
lineGap
|
|
808
|
-
})
|
|
809
|
-
);
|
|
810
|
-
}
|
|
811
|
-
for (const face of slot.faces) {
|
|
812
|
-
if (face.provider !== "google" && !face.provider.startsWith("/") && !face.provider.startsWith("http") && !face.provider.startsWith(".")) {
|
|
915
|
+
for (const src of sources) {
|
|
916
|
+
if (!src.inline) continue;
|
|
917
|
+
const items = src.classes ?? [];
|
|
918
|
+
let contentLength = 0;
|
|
919
|
+
for (const cls of items) contentLength += cls.length;
|
|
920
|
+
if (contentLength > MAX_INLINE_SOURCE_SIZE) {
|
|
813
921
|
warnings.push(
|
|
814
|
-
`[RI-
|
|
922
|
+
`[RI-1406] Inline @source content exceeds ${MAX_INLINE_SOURCE_SIZE} byte limit (${contentLength} bytes) \u2014 skipping.`
|
|
815
923
|
);
|
|
816
924
|
continue;
|
|
817
925
|
}
|
|
818
|
-
const
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
if (webFont.type === "import") imports.push(webFont.css);
|
|
822
|
-
else fontFaces.push(webFont.css);
|
|
926
|
+
for (const cls of items) {
|
|
927
|
+
if (cls) classes.add(cls);
|
|
928
|
+
}
|
|
823
929
|
}
|
|
824
|
-
|
|
825
|
-
const safeFamily = escapeFontFamily(slot.family);
|
|
826
|
-
const stackParts = [`"${safeFamily}"`];
|
|
827
|
-
if (hasMetrics) stackParts.push(`"${safeFamily} Fallback"`);
|
|
828
|
-
stackParts.push(fallbackStack);
|
|
829
|
-
variables.push(`--font-${slot.slot}: ${stackParts.join(", ")};`);
|
|
830
|
-
pushFeatureVars();
|
|
831
|
-
return { imports, fontFaces, variables, warnings };
|
|
930
|
+
return { classes, warnings };
|
|
832
931
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
932
|
+
var FILE_IO_TIMEOUT_MS = 1e4;
|
|
933
|
+
var SCAN_CACHE_MAX_ENTRIES = 2e4;
|
|
934
|
+
var scanCache = /* @__PURE__ */ new Map();
|
|
935
|
+
async function scanOneFile(file) {
|
|
936
|
+
const warnings = [];
|
|
937
|
+
try {
|
|
938
|
+
const stats = await withTimeout(stat(file), FILE_IO_TIMEOUT_MS, "stat() timed out");
|
|
939
|
+
const cached = scanCache.get(file);
|
|
940
|
+
if (cached && cached.mtimeMs === stats.mtimeMs && cached.size === stats.size) {
|
|
941
|
+
return cached.result;
|
|
942
|
+
}
|
|
943
|
+
let result;
|
|
944
|
+
if (stats.size > MAX_FILE_SIZE) {
|
|
945
|
+
result = {
|
|
946
|
+
classes: null,
|
|
947
|
+
warnings,
|
|
948
|
+
failure: `[RI-1405] Skipping source file "${file}" (${stats.size} bytes) \u2014 exceeds ${MAX_FILE_SIZE} byte limit.`
|
|
949
|
+
};
|
|
950
|
+
} else {
|
|
951
|
+
const content = await withTimeout(
|
|
952
|
+
readFile(file, "utf-8"),
|
|
953
|
+
FILE_IO_TIMEOUT_MS,
|
|
954
|
+
"readFile() timed out"
|
|
955
|
+
);
|
|
956
|
+
result = {
|
|
957
|
+
classes: extractClassesFromSource({ path: file, content }, warnings),
|
|
958
|
+
warnings,
|
|
959
|
+
failure: null
|
|
960
|
+
};
|
|
849
961
|
}
|
|
962
|
+
if (scanCache.size >= SCAN_CACHE_MAX_ENTRIES) scanCache.clear();
|
|
963
|
+
scanCache.set(file, { mtimeMs: stats.mtimeMs, size: stats.size, result });
|
|
964
|
+
return result;
|
|
965
|
+
} catch (err) {
|
|
966
|
+
return {
|
|
967
|
+
classes: null,
|
|
968
|
+
warnings,
|
|
969
|
+
failure: `[RI-1403] Could not read source file "${file}" \u2014 skipping. (${err instanceof Error ? err.message : String(err)})`
|
|
970
|
+
};
|
|
850
971
|
}
|
|
851
|
-
|
|
972
|
+
}
|
|
973
|
+
async function scanSourceFilesAsync(sources, cwd) {
|
|
974
|
+
const { classes: allClasses, warnings: inlineWarnings } = collectInlineClasses(sources);
|
|
975
|
+
const allWarnings = [...inlineWarnings];
|
|
976
|
+
const { files, warnings: resolveWarnings } = await resolveSourceFilesAsync(
|
|
977
|
+
sources,
|
|
978
|
+
cwd,
|
|
979
|
+
allClasses.size > 0
|
|
980
|
+
);
|
|
981
|
+
allWarnings.push(...resolveWarnings);
|
|
982
|
+
const CONCURRENCY_LIMIT = 32;
|
|
983
|
+
const results = new Array(files.length);
|
|
984
|
+
let nextIndex = 0;
|
|
985
|
+
const worker = async () => {
|
|
986
|
+
while (nextIndex < files.length) {
|
|
987
|
+
const index = nextIndex++;
|
|
988
|
+
results[index] = await scanOneFile(files[index]);
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
await Promise.all(Array.from({ length: Math.min(CONCURRENCY_LIMIT, files.length) }, worker));
|
|
992
|
+
const seen = new Set(allWarnings);
|
|
993
|
+
for (const result of results) {
|
|
994
|
+
if (result.failure) {
|
|
995
|
+
allWarnings.push(result.failure);
|
|
996
|
+
seen.add(result.failure);
|
|
997
|
+
continue;
|
|
998
|
+
}
|
|
999
|
+
if (result.classes) {
|
|
1000
|
+
for (const cls of result.classes) {
|
|
1001
|
+
allClasses.add(cls);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
pushWarningsDeduped(allWarnings, result.warnings, seen);
|
|
1005
|
+
}
|
|
1006
|
+
return { classes: allClasses, warnings: allWarnings };
|
|
1007
|
+
}
|
|
1008
|
+
async function collectProjectClasses(themeSources, surfaceSources, cwd, warnings, warningSeen) {
|
|
1009
|
+
const discovered = discoverPackageSafelistSources(cwd);
|
|
1010
|
+
pushWarningsDeduped(warnings, discovered.warnings, warningSeen);
|
|
1011
|
+
const allSources = [...themeSources, ...surfaceSources, ...discovered.sources];
|
|
1012
|
+
const scanResult = await scanSourceFilesAsync(allSources, cwd);
|
|
1013
|
+
pushWarningsDeduped(warnings, scanResult.warnings, warningSeen);
|
|
1014
|
+
return scanResult.classes;
|
|
852
1015
|
}
|
|
853
1016
|
|
|
854
1017
|
// src/css/strip.ts
|
|
@@ -1459,7 +1622,7 @@ function collectPropertyBlocks(registered, engineProperties) {
|
|
|
1459
1622
|
return blocks;
|
|
1460
1623
|
}
|
|
1461
1624
|
function assembleSections(compilation, theme, fontOutputCache) {
|
|
1462
|
-
const
|
|
1625
|
+
const baseSections = [];
|
|
1463
1626
|
const fontImports = [];
|
|
1464
1627
|
const fontFaceBlocks = [];
|
|
1465
1628
|
const assemblyWarnings = [];
|
|
@@ -1480,18 +1643,16 @@ function assembleSections(compilation, theme, fontOutputCache) {
|
|
|
1480
1643
|
}
|
|
1481
1644
|
}
|
|
1482
1645
|
}
|
|
1483
|
-
|
|
1484
|
-
sections.push(fontImports.join("\n"));
|
|
1485
|
-
}
|
|
1646
|
+
const importsSection = fontImports.length > 0 ? fontImports.join("\n") : null;
|
|
1486
1647
|
const propertyBlocks = collectPropertyBlocks(theme.registeredProperties, compilation.properties);
|
|
1487
1648
|
if (propertyBlocks.length > 0) {
|
|
1488
|
-
|
|
1649
|
+
baseSections.push(propertyBlocks.join("\n\n"));
|
|
1489
1650
|
}
|
|
1490
1651
|
if (fontFaceBlocks.length > 0) {
|
|
1491
|
-
|
|
1652
|
+
baseSections.push(fontFaceBlocks.join("\n\n"));
|
|
1492
1653
|
}
|
|
1493
1654
|
const tokenLayer = generateTokenLayer(theme, compilation, fontOutputCache);
|
|
1494
|
-
if (tokenLayer)
|
|
1655
|
+
if (tokenLayer) baseSections.push(tokenLayer);
|
|
1495
1656
|
for (const w of checkPaletteContrast(theme.colors, compilation.usedColorStops)) {
|
|
1496
1657
|
assemblyWarnings.push(w);
|
|
1497
1658
|
}
|
|
@@ -1500,25 +1661,32 @@ function assembleSections(compilation, theme, fontOutputCache) {
|
|
|
1500
1661
|
compilation.usedColorStops.get("theme")
|
|
1501
1662
|
);
|
|
1502
1663
|
if (themeOverrides.length > 0) {
|
|
1503
|
-
|
|
1664
|
+
baseSections.push(themeOverrides.join("\n\n"));
|
|
1504
1665
|
}
|
|
1505
1666
|
const cornerShape = generateCornerShapeBlock(theme);
|
|
1506
|
-
if (cornerShape)
|
|
1667
|
+
if (cornerShape) baseSections.push(cornerShape);
|
|
1507
1668
|
if (compilation.keyframes.length > 0) {
|
|
1508
|
-
|
|
1669
|
+
baseSections.push(compilation.keyframes.join("\n\n"));
|
|
1509
1670
|
}
|
|
1510
1671
|
const preflight = generatePreflight(theme.preflight);
|
|
1511
|
-
if (preflight)
|
|
1672
|
+
if (preflight) baseSections.push(preflight);
|
|
1512
1673
|
const utilityCSS = renderCSS({
|
|
1513
1674
|
...compilation,
|
|
1514
1675
|
properties: [],
|
|
1515
1676
|
keyframes: []
|
|
1516
1677
|
});
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1678
|
+
const utilitiesSection = utilityCSS !== "" ? utilityCSS : null;
|
|
1679
|
+
const parts = {
|
|
1680
|
+
imports: importsSection,
|
|
1681
|
+
base: baseSections,
|
|
1682
|
+
utilities: utilitiesSection
|
|
1683
|
+
};
|
|
1684
|
+
const sections = theme.layer ? applyLayerWrapping(parts, theme.layer) : [
|
|
1685
|
+
...importsSection !== null ? [importsSection] : [],
|
|
1686
|
+
...baseSections,
|
|
1687
|
+
...utilitiesSection !== null ? [utilitiesSection] : []
|
|
1688
|
+
];
|
|
1689
|
+
return { sections, warnings: assemblyWarnings };
|
|
1522
1690
|
}
|
|
1523
1691
|
function wrapInLayer(content, layerName) {
|
|
1524
1692
|
const indented = content.replace(/^(?=.)/gm, " ");
|
|
@@ -1526,42 +1694,36 @@ function wrapInLayer(content, layerName) {
|
|
|
1526
1694
|
${indented}
|
|
1527
1695
|
}`;
|
|
1528
1696
|
}
|
|
1529
|
-
function applyLayerWrapping(
|
|
1530
|
-
const
|
|
1531
|
-
|
|
1532
|
-
const contentSections = hasImportSection ? sections.slice(1) : [...sections];
|
|
1697
|
+
function applyLayerWrapping(parts, layer) {
|
|
1698
|
+
const sections = [];
|
|
1699
|
+
if (parts.imports !== null) sections.push(parts.imports);
|
|
1533
1700
|
if (layer.wrapAll) {
|
|
1534
|
-
const joined = contentSections.join("\n\n");
|
|
1535
|
-
sections.length = 0;
|
|
1536
|
-
if (importSection) sections.push(importSection);
|
|
1537
1701
|
sections.push(`@layer ${layer.wrapAll};`);
|
|
1702
|
+
const joined = [...parts.base, ...parts.utilities !== null ? [parts.utilities] : []].join(
|
|
1703
|
+
"\n\n"
|
|
1704
|
+
);
|
|
1538
1705
|
if (joined) sections.push(wrapInLayer(joined, layer.wrapAll));
|
|
1539
|
-
return;
|
|
1706
|
+
return sections;
|
|
1540
1707
|
}
|
|
1541
|
-
const utilitySection = hasUtilitySection ? contentSections.pop() ?? null : null;
|
|
1542
|
-
const baseSections = contentSections;
|
|
1543
|
-
const finalSections = [];
|
|
1544
|
-
if (importSection) finalSections.push(importSection);
|
|
1545
1708
|
if (layer.order && layer.order.length > 0) {
|
|
1546
|
-
|
|
1709
|
+
sections.push(`@layer ${layer.order.join(", ")};`);
|
|
1547
1710
|
}
|
|
1548
|
-
if (
|
|
1549
|
-
const baseJoined =
|
|
1711
|
+
if (parts.base.length > 0) {
|
|
1712
|
+
const baseJoined = parts.base.join("\n\n");
|
|
1550
1713
|
if (layer.base) {
|
|
1551
|
-
|
|
1714
|
+
sections.push(wrapInLayer(baseJoined, layer.base));
|
|
1552
1715
|
} else {
|
|
1553
|
-
|
|
1716
|
+
sections.push(baseJoined);
|
|
1554
1717
|
}
|
|
1555
1718
|
}
|
|
1556
|
-
if (
|
|
1719
|
+
if (parts.utilities !== null) {
|
|
1557
1720
|
if (layer.utilities) {
|
|
1558
|
-
|
|
1721
|
+
sections.push(wrapInLayer(parts.utilities, layer.utilities));
|
|
1559
1722
|
} else {
|
|
1560
|
-
|
|
1723
|
+
sections.push(parts.utilities);
|
|
1561
1724
|
}
|
|
1562
1725
|
}
|
|
1563
|
-
sections
|
|
1564
|
-
sections.push(...finalSections);
|
|
1726
|
+
return sections;
|
|
1565
1727
|
}
|
|
1566
1728
|
|
|
1567
1729
|
// src/project/pipeline.ts
|
|
@@ -1575,13 +1737,20 @@ function collectApplyClassNames(css, warnings) {
|
|
|
1575
1737
|
}
|
|
1576
1738
|
return classes;
|
|
1577
1739
|
}
|
|
1740
|
+
var effectiveThemeMemo = /* @__PURE__ */ new WeakMap();
|
|
1578
1741
|
async function finalizeProjectCompilation(options) {
|
|
1579
1742
|
const { analysis } = options;
|
|
1580
1743
|
let effectiveTheme = analysis.theme;
|
|
1581
1744
|
if (options.resolveFonts) {
|
|
1582
1745
|
const resolvedFonts = await options.resolveFonts(analysis.theme.fonts);
|
|
1583
1746
|
if (resolvedFonts !== analysis.theme.fonts) {
|
|
1584
|
-
|
|
1747
|
+
const memo = effectiveThemeMemo.get(analysis.theme);
|
|
1748
|
+
if (memo && memo.fonts === resolvedFonts) {
|
|
1749
|
+
effectiveTheme = memo.theme;
|
|
1750
|
+
} else {
|
|
1751
|
+
effectiveTheme = { ...analysis.theme, fonts: [...resolvedFonts] };
|
|
1752
|
+
effectiveThemeMemo.set(analysis.theme, { fonts: resolvedFonts, theme: effectiveTheme });
|
|
1753
|
+
}
|
|
1585
1754
|
}
|
|
1586
1755
|
}
|
|
1587
1756
|
const expansionWarnings = [];
|
|
@@ -1624,12 +1793,59 @@ async function finalizeProjectCompilation(options) {
|
|
|
1624
1793
|
};
|
|
1625
1794
|
}
|
|
1626
1795
|
|
|
1796
|
+
// src/project/scan.ts
|
|
1797
|
+
var lastAnalysis = null;
|
|
1798
|
+
function analyzeProjectCSSMemo(css) {
|
|
1799
|
+
if (lastAnalysis === null || lastAnalysis.css !== css) {
|
|
1800
|
+
lastAnalysis = { css, analysis: analyzeProjectCSS(css) };
|
|
1801
|
+
}
|
|
1802
|
+
const cached = lastAnalysis.analysis;
|
|
1803
|
+
return {
|
|
1804
|
+
...cached,
|
|
1805
|
+
warnings: [...cached.warnings],
|
|
1806
|
+
warningSeen: new Set(cached.warningSeen),
|
|
1807
|
+
diagnostics: [...cached.diagnostics]
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
async function compileScannedProject(options) {
|
|
1811
|
+
const analysis = analyzeProjectCSSMemo(options.css);
|
|
1812
|
+
const resolveFonts = options.resolveFonts ?? resolveGoogleFonts;
|
|
1813
|
+
const fontsReady = Promise.resolve(resolveFonts(analysis.theme.fonts));
|
|
1814
|
+
fontsReady.catch(() => {
|
|
1815
|
+
});
|
|
1816
|
+
const surfaceSources = [];
|
|
1817
|
+
for (const pattern of options.surfacePatterns ?? []) {
|
|
1818
|
+
const error = validateGlobPattern(pattern);
|
|
1819
|
+
if (error) {
|
|
1820
|
+
const warning = options.onInvalidPattern(error);
|
|
1821
|
+
if (warning !== void 0) {
|
|
1822
|
+
pushWarningsDeduped(analysis.warnings, [warning], analysis.warningSeen);
|
|
1823
|
+
}
|
|
1824
|
+
continue;
|
|
1825
|
+
}
|
|
1826
|
+
surfaceSources.push({ pattern, negated: false, inline: false });
|
|
1827
|
+
}
|
|
1828
|
+
const classNames = await collectProjectClasses(
|
|
1829
|
+
analysis.theme.sources,
|
|
1830
|
+
surfaceSources,
|
|
1831
|
+
options.cwd,
|
|
1832
|
+
analysis.warnings,
|
|
1833
|
+
analysis.warningSeen
|
|
1834
|
+
);
|
|
1835
|
+
const compiled = await finalizeProjectCompilation({
|
|
1836
|
+
css: options.css,
|
|
1837
|
+
classNames,
|
|
1838
|
+
analysis,
|
|
1839
|
+
resolveFonts: () => fontsReady
|
|
1840
|
+
});
|
|
1841
|
+
return { compiled, warningSeen: analysis.warningSeen };
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1627
1844
|
export {
|
|
1628
|
-
validateGlobPattern,
|
|
1629
|
-
DEFAULT_PATTERNS,
|
|
1630
|
-
DEFAULT_EXCLUDES,
|
|
1631
|
-
collectProjectClasses,
|
|
1632
1845
|
resolveGoogleFonts,
|
|
1633
1846
|
getFontPreloadLinks,
|
|
1634
|
-
|
|
1847
|
+
DEFAULT_PATTERNS,
|
|
1848
|
+
DEFAULT_EXCLUDES,
|
|
1849
|
+
finalizeProjectCompilation,
|
|
1850
|
+
compileScannedProject
|
|
1635
1851
|
};
|