@nifrajs/web 3.1.0 → 3.2.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/dist/build-plan.d.ts +228 -0
- package/dist/build-plan.d.ts.map +1 -0
- package/dist/build-plan.js +514 -0
- package/dist/build-plan.js.map +1 -0
- package/dist/build-vite.d.ts +2 -1
- package/dist/build-vite.d.ts.map +1 -1
- package/dist/build-vite.js.map +1 -1
- package/dist/build.d.ts +2 -197
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +61 -502
- package/dist/build.js.map +1 -1
- package/dist/chunk-names.d.ts.map +1 -1
- package/dist/chunk-names.js +3 -2
- package/dist/chunk-names.js.map +1 -1
- package/dist/dev-ssr-graph.d.ts.map +1 -1
- package/dist/dev-ssr-graph.js +63 -28
- package/dist/dev-ssr-graph.js.map +1 -1
- package/dist/diagnostic.d.ts.map +1 -1
- package/dist/diagnostic.js +9 -2
- package/dist/diagnostic.js.map +1 -1
- package/dist/fonts-google.d.ts.map +1 -1
- package/dist/fonts-google.js +3 -1
- package/dist/fonts-google.js.map +1 -1
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +2 -1
- package/dist/fs.js.map +1 -1
- package/dist/index.d.ts +15 -609
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -1935
- package/dist/index.js.map +1 -1
- package/dist/internal/codegen.d.ts +68 -0
- package/dist/internal/codegen.d.ts.map +1 -0
- package/dist/internal/codegen.js +317 -0
- package/dist/internal/codegen.js.map +1 -0
- package/dist/internal/head-merge.d.ts +2 -0
- package/dist/internal/head-merge.d.ts.map +1 -1
- package/dist/internal/head-merge.js +17 -1
- package/dist/internal/head-merge.js.map +1 -1
- package/dist/internal/parity.d.ts +7 -0
- package/dist/internal/parity.d.ts.map +1 -1
- package/dist/internal/parity.js +212 -30
- package/dist/internal/parity.js.map +1 -1
- package/dist/internal/render-document.d.ts +372 -0
- package/dist/internal/render-document.d.ts.map +1 -0
- package/dist/internal/render-document.js +744 -0
- package/dist/internal/render-document.js.map +1 -0
- package/dist/internal/runtime-contract.d.ts +48 -0
- package/dist/internal/runtime-contract.d.ts.map +1 -0
- package/dist/internal/runtime-contract.js +27 -0
- package/dist/internal/runtime-contract.js.map +1 -0
- package/dist/internal/web-app.d.ts +171 -0
- package/dist/internal/web-app.d.ts.map +1 -0
- package/dist/internal/web-app.js +844 -0
- package/dist/internal/web-app.js.map +1 -0
- package/dist/islands.d.ts +48 -0
- package/dist/islands.d.ts.map +1 -1
- package/dist/islands.js +79 -0
- package/dist/islands.js.map +1 -1
- package/dist/module-graph.d.ts.map +1 -1
- package/dist/module-graph.js +22 -9
- package/dist/module-graph.js.map +1 -1
- package/dist/nano.d.ts +125 -0
- package/dist/nano.d.ts.map +1 -0
- package/dist/nano.js +190 -0
- package/dist/nano.js.map +1 -0
- package/dist/plugins/css-modules.js +2 -2
- package/dist/plugins/css-modules.js.map +1 -1
- package/dist/plugins/kit.d.ts +12 -0
- package/dist/plugins/kit.d.ts.map +1 -1
- package/dist/plugins/kit.js +62 -12
- package/dist/plugins/kit.js.map +1 -1
- package/dist/plugins/postcss.js +2 -2
- package/dist/plugins/postcss.js.map +1 -1
- package/dist/plugins/scss.js +2 -2
- package/dist/plugins/scss.js.map +1 -1
- package/dist/plugins/stylex.d.ts +70 -0
- package/dist/plugins/stylex.d.ts.map +1 -0
- package/dist/plugins/stylex.js +166 -0
- package/dist/plugins/stylex.js.map +1 -0
- package/dist/plugins/svg.js +2 -2
- package/dist/plugins/svg.js.map +1 -1
- package/dist/router.d.ts +1 -1
- package/dist/router.js +23 -23
- package/dist/router.js.map +1 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +26 -10
- package/dist/vite.js.map +1 -1
- package/package.json +39 -3
- package/src/build-plan.ts +741 -0
- package/src/build-vite.ts +1 -4
- package/src/build.ts +84 -716
- package/src/chunk-names.ts +3 -2
- package/src/dev-ssr-graph.ts +63 -28
- package/src/diagnostic.ts +14 -2
- package/src/fonts-google.ts +3 -1
- package/src/fs.ts +2 -1
- package/src/index.ts +73 -2705
- package/src/internal/codegen.ts +400 -0
- package/src/internal/head-merge.ts +19 -1
- package/src/internal/parity.ts +215 -35
- package/src/internal/render-document.ts +1116 -0
- package/src/internal/runtime-contract.ts +54 -0
- package/src/internal/web-app.ts +1114 -0
- package/src/islands.ts +85 -0
- package/src/module-graph.ts +23 -9
- package/src/nano.ts +246 -0
- package/src/plugins/css-modules.ts +2 -2
- package/src/plugins/kit.ts +65 -12
- package/src/plugins/postcss.ts +2 -2
- package/src/plugins/scss.ts +2 -2
- package/src/plugins/stylex.ts +298 -0
- package/src/plugins/svg.ts +2 -2
- package/src/router.ts +24 -24
- package/src/vite.ts +33 -9
package/src/internal/parity.ts
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* manifest normalization so those callers cannot quietly grow separate rules.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { existsSync, readdirSync, readFileSync, realpathSync } from "node:fs"
|
|
9
|
+
import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs"
|
|
10
10
|
import { lstat, realpath, stat } from "node:fs/promises"
|
|
11
|
-
import { dirname, join, relative, resolve, sep } from "node:path"
|
|
11
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path"
|
|
12
12
|
import {
|
|
13
13
|
matchesSingleCopyDeclaration,
|
|
14
14
|
readSingleCopyDeclaration,
|
|
@@ -32,6 +32,12 @@ const MAX_LINK_PROBES = 4_096
|
|
|
32
32
|
export interface IdentityParityCopy {
|
|
33
33
|
readonly version: string
|
|
34
34
|
readonly path: string
|
|
35
|
+
/**
|
|
36
|
+
* The resolved realpath of the copy. `path` is display-relative to the invoked directory, which
|
|
37
|
+
* reads well in a report but cannot be pasted into a resolver or an editor from anywhere else.
|
|
38
|
+
* Absent on a finding built by hand (a test fixture, an older cached result).
|
|
39
|
+
*/
|
|
40
|
+
readonly absolutePath?: string
|
|
35
41
|
readonly importers: readonly string[]
|
|
36
42
|
}
|
|
37
43
|
|
|
@@ -156,14 +162,121 @@ export interface DevelopmentParityInput {
|
|
|
156
162
|
}
|
|
157
163
|
|
|
158
164
|
const SOURCE_EXTENSIONS = /\.(?:c|m)?(?:j|t)sx?$|\.(?:mdx|svelte|vue)$/
|
|
159
|
-
/** A
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
/** A stylesheet path that the source scanner can prove is a real explicit import. */
|
|
166
|
+
const CSS_SPECIFIER = /\.(?:css|scss|sass|less|styl)(?:\?[^"'`]*)?$/i
|
|
167
|
+
|
|
168
|
+
type CssToken =
|
|
169
|
+
| { readonly kind: "identifier"; readonly value: string; readonly lineStart: boolean }
|
|
170
|
+
| { readonly kind: "string"; readonly value: string }
|
|
171
|
+
| { readonly kind: "punctuator"; readonly value: string }
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Tokenize only the small part of TypeScript/JS needed for the CSS contract.
|
|
175
|
+
*
|
|
176
|
+
* A regex over source text is not sound here: documentation routes contain code examples such as
|
|
177
|
+
* `import "./app.css"` in template literals, and comments can contain the same text. Those are not
|
|
178
|
+
* imports and must not make a style-free production build fail. This intentionally conservative lexer
|
|
179
|
+
* drops comments and complete template literals (including `${...}` expressions); missing a stylesheet
|
|
180
|
+
* is the passing direction of this parity check, while claiming one that is not imported is not.
|
|
181
|
+
*/
|
|
182
|
+
const cssTokens = (source: string): readonly CssToken[] => {
|
|
183
|
+
const tokens: CssToken[] = []
|
|
184
|
+
let lineStart = true
|
|
185
|
+
for (let index = 0; index < source.length; ) {
|
|
186
|
+
const char = source[index]
|
|
187
|
+
if (char === undefined) break
|
|
188
|
+
if (/\s/.test(char)) {
|
|
189
|
+
if (char === "\n" || char === "\r") lineStart = true
|
|
190
|
+
index++
|
|
191
|
+
continue
|
|
192
|
+
}
|
|
193
|
+
if (char === "/" && source[index + 1] === "/") {
|
|
194
|
+
index += 2
|
|
195
|
+
while (index < source.length && source[index] !== "\n") index++
|
|
196
|
+
continue
|
|
197
|
+
}
|
|
198
|
+
if (char === "/" && source[index + 1] === "*") {
|
|
199
|
+
const commentStart = index
|
|
200
|
+
index += 2
|
|
201
|
+
while (index < source.length && !(source[index] === "*" && source[index + 1] === "/")) index++
|
|
202
|
+
index = Math.min(source.length, index + 2)
|
|
203
|
+
if (/[\r\n]/.test(source.slice(commentStart, index))) lineStart = true
|
|
204
|
+
continue
|
|
205
|
+
}
|
|
206
|
+
if (char === "`") {
|
|
207
|
+
const templateStart = index
|
|
208
|
+
index++
|
|
209
|
+
while (index < source.length) {
|
|
210
|
+
const templateChar = source[index]
|
|
211
|
+
if (templateChar === "\\") {
|
|
212
|
+
index += 2
|
|
213
|
+
continue
|
|
214
|
+
}
|
|
215
|
+
index++
|
|
216
|
+
if (templateChar === "`") break
|
|
217
|
+
}
|
|
218
|
+
lineStart = /[\r\n]/.test(source.slice(templateStart, index))
|
|
219
|
+
continue
|
|
220
|
+
}
|
|
221
|
+
if (char === '"' || char === "'") {
|
|
222
|
+
const quote = char
|
|
223
|
+
const start = ++index
|
|
224
|
+
while (index < source.length) {
|
|
225
|
+
const stringChar = source[index]
|
|
226
|
+
if (stringChar === "\\") {
|
|
227
|
+
index += 2
|
|
228
|
+
continue
|
|
229
|
+
}
|
|
230
|
+
index++
|
|
231
|
+
if (stringChar === quote) break
|
|
232
|
+
}
|
|
233
|
+
tokens.push({ kind: "string", value: source.slice(start, Math.max(start, index - 1)) })
|
|
234
|
+
lineStart = false
|
|
235
|
+
continue
|
|
236
|
+
}
|
|
237
|
+
if (/[A-Za-z_$]/.test(char)) {
|
|
238
|
+
const start = index++
|
|
239
|
+
while (index < source.length && /[A-Za-z0-9_$]/.test(source[index] ?? "")) index++
|
|
240
|
+
tokens.push({ kind: "identifier", value: source.slice(start, index), lineStart })
|
|
241
|
+
lineStart = false
|
|
242
|
+
continue
|
|
243
|
+
}
|
|
244
|
+
tokens.push({ kind: "punctuator", value: char })
|
|
245
|
+
lineStart = false
|
|
246
|
+
index++
|
|
247
|
+
}
|
|
248
|
+
return tokens
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const isStylesheetToken = (token: CssToken | undefined): boolean =>
|
|
252
|
+
token?.kind === "string" && CSS_SPECIFIER.test(token.value)
|
|
253
|
+
|
|
254
|
+
/** Detect static imports, re-exports, literal dynamic imports, and literal require calls. */
|
|
255
|
+
const hasStylesheetImport = (source: string): boolean => {
|
|
256
|
+
const tokens = cssTokens(source)
|
|
257
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
258
|
+
const token = tokens[index]
|
|
259
|
+
if (token?.kind !== "identifier") continue
|
|
260
|
+
const next = tokens[index + 1]
|
|
261
|
+
if (token.value === "import") {
|
|
262
|
+
if (token.lineStart && isStylesheetToken(next)) return true
|
|
263
|
+
if (next?.value === "(" && isStylesheetToken(tokens[index + 2])) return true
|
|
264
|
+
}
|
|
265
|
+
if (token.value === "require" && next?.value === "(" && isStylesheetToken(tokens[index + 2]))
|
|
266
|
+
return true
|
|
267
|
+
if (token.value === "from" && isStylesheetToken(next)) {
|
|
268
|
+
for (let previous = index - 1; previous >= 0; previous--) {
|
|
269
|
+
const candidate = tokens[previous]
|
|
270
|
+
if (candidate?.kind === "identifier" && candidate.lineStart) {
|
|
271
|
+
if (candidate.value === "import" || candidate.value === "export") return true
|
|
272
|
+
break
|
|
273
|
+
}
|
|
274
|
+
if (candidate?.value === ";") break
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return false
|
|
279
|
+
}
|
|
167
280
|
/** A single-file-component `<style>` block (Svelte/Vue). The bundler extracts these into the app
|
|
168
281
|
* stylesheet even though no `import "...css"` statement exists, so the dev contract must count them
|
|
169
282
|
* or a scoped-style component would look style-free next to a production manifest that carries css. */
|
|
@@ -219,8 +332,85 @@ const pathExists = (path: string): Promise<boolean> =>
|
|
|
219
332
|
() => false,
|
|
220
333
|
)
|
|
221
334
|
|
|
222
|
-
|
|
223
|
-
|
|
335
|
+
/** The real path, or the input when it cannot be resolved. Roots are compared against each other and
|
|
336
|
+
* printed side by side, so they have to be normalized the same way (`/var` vs `/private/var`). */
|
|
337
|
+
const realpathOrSelf = (path: string): string => {
|
|
338
|
+
try {
|
|
339
|
+
// The native Windows resolver is the one that knows how to expand 8.3 aliases such as
|
|
340
|
+
// `RUNNER~1`. The portable resolver can preserve the spelling it was given, which makes two
|
|
341
|
+
// handles to the same checkout look like different install roots.
|
|
342
|
+
return process.platform === "win32" ? realpathSync.native(path) : realpathSync(path)
|
|
343
|
+
} catch {
|
|
344
|
+
return path
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/** Canonical comparison form for containment. Windows paths are case-insensitive and CI can expose
|
|
349
|
+
* the same directory once through a short name (`RUNNER~1`) and once through its long name. */
|
|
350
|
+
const comparisonPath = (path: string): string => {
|
|
351
|
+
const resolved = realpathOrSelf(resolve(path))
|
|
352
|
+
const native = resolved.replaceAll("/", sep)
|
|
353
|
+
return process.platform === "win32" ? native.toLowerCase() : native
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Windows may expose the same directory through a long path and an 8.3 short path even after
|
|
358
|
+
* `realpathSync` has normalized both inputs. When the lexical comparison above says "outside", walk
|
|
359
|
+
* the target's existing ancestors and compare the filesystem identity of each one. `statSync` with
|
|
360
|
+
* bigint fields avoids truncating Windows file IDs, and a missing identity fails closed.
|
|
361
|
+
*/
|
|
362
|
+
const pathInsideByFilesystemIdentity = (root: string, path: string): boolean => {
|
|
363
|
+
if (process.platform !== "win32") return false
|
|
364
|
+
const rootIdentity = (() => {
|
|
365
|
+
try {
|
|
366
|
+
const info = statSync(realpathOrSelf(resolve(root)), { bigint: true })
|
|
367
|
+
if (info.ino === 0n) return undefined
|
|
368
|
+
return { dev: info.dev, ino: info.ino }
|
|
369
|
+
} catch {
|
|
370
|
+
try {
|
|
371
|
+
const info = statSync(realpathOrSelf(resolve(root)))
|
|
372
|
+
if (info.ino === 0) return undefined
|
|
373
|
+
return { dev: BigInt(info.dev), ino: BigInt(info.ino) }
|
|
374
|
+
} catch {
|
|
375
|
+
return undefined
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
})()
|
|
379
|
+
if (rootIdentity === undefined) return false
|
|
380
|
+
|
|
381
|
+
let current = realpathOrSelf(resolve(path))
|
|
382
|
+
for (;;) {
|
|
383
|
+
try {
|
|
384
|
+
const info = statSync(current, { bigint: true })
|
|
385
|
+
if (info.ino !== 0n && info.dev === rootIdentity.dev && info.ino === rootIdentity.ino)
|
|
386
|
+
return true
|
|
387
|
+
} catch {
|
|
388
|
+
try {
|
|
389
|
+
const info = statSync(current)
|
|
390
|
+
if (
|
|
391
|
+
info.ino !== 0 &&
|
|
392
|
+
BigInt(info.dev) === rootIdentity.dev &&
|
|
393
|
+
BigInt(info.ino) === rootIdentity.ino
|
|
394
|
+
)
|
|
395
|
+
return true
|
|
396
|
+
} catch {
|
|
397
|
+
// A non-existent leaf can still have an existing parent. Keep walking; if no ancestor can
|
|
398
|
+
// prove containment, the answer remains false rather than trusting a spelling alone.
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const parent = dirname(current)
|
|
402
|
+
if (parent === current) return false
|
|
403
|
+
current = parent
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export const pathInside = (root: string, path: string): boolean => {
|
|
408
|
+
const distance = relative(comparisonPath(root), comparisonPath(path))
|
|
409
|
+
const lexicallyInside =
|
|
410
|
+
distance === "" ||
|
|
411
|
+
(distance !== ".." && !distance.startsWith(`..${sep}`) && !isAbsolute(distance))
|
|
412
|
+
return lexicallyInside || pathInsideByFilesystemIdentity(root, path)
|
|
413
|
+
}
|
|
224
414
|
|
|
225
415
|
const workspaceContains = async (
|
|
226
416
|
root: string,
|
|
@@ -233,27 +423,12 @@ const workspaceContains = async (
|
|
|
233
423
|
for await (const match of new Bun.Glob(packagePattern).scan({ cwd: root, dot: false })) {
|
|
234
424
|
if (match.split(/[\\/]/).includes("node_modules")) continue
|
|
235
425
|
const packageRoot = resolve(root, dirname(match))
|
|
236
|
-
|
|
237
|
-
if (
|
|
238
|
-
targetRelative === "" ||
|
|
239
|
-
(!targetRelative.startsWith(`..${sep}`) && targetRelative !== "..")
|
|
240
|
-
)
|
|
241
|
-
return true
|
|
426
|
+
if (pathInside(packageRoot, target)) return true
|
|
242
427
|
}
|
|
243
428
|
}
|
|
244
429
|
return false
|
|
245
430
|
}
|
|
246
431
|
|
|
247
|
-
/** The real path, or the input when it cannot be resolved. Roots are compared against each other and
|
|
248
|
-
* printed side by side, so they have to be normalized the same way (`/var` vs `/private/var`). */
|
|
249
|
-
const realpathOrSelf = (path: string): string => {
|
|
250
|
-
try {
|
|
251
|
-
return realpathSync(path)
|
|
252
|
-
} catch {
|
|
253
|
-
return path
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
432
|
/** Resolve the workspace root once, even when the caller starts in a workspace package. */
|
|
258
433
|
export async function resolveParityWorkspaceRoot(
|
|
259
434
|
start: string,
|
|
@@ -392,7 +567,7 @@ const linkedPackageRoots = async (
|
|
|
392
567
|
|
|
393
568
|
export const displayPath = (cwd: string, path: string): string => {
|
|
394
569
|
const rel = relative(cwd, path)
|
|
395
|
-
return rel === "" ? "." : rel
|
|
570
|
+
return rel === "" ? "." : rel.replaceAll("\\", "/")
|
|
396
571
|
}
|
|
397
572
|
|
|
398
573
|
/**
|
|
@@ -465,7 +640,7 @@ const describeScope = (
|
|
|
465
640
|
if (requestedRoot === scanRoot) return undefined
|
|
466
641
|
const outside = paths.filter((path) => !pathInside(requestedRoot, path))
|
|
467
642
|
if (outside.length === 0) return undefined
|
|
468
|
-
const here =
|
|
643
|
+
const here = displayPath(scanRoot, requestedRoot) || "."
|
|
469
644
|
return `${outside.length} of these copies ${outside.length === 1 ? "is" : "are"} outside ${here}, so this fails every build in the workspace, including apps that never import the package. The scan is anchored on the workspace on purpose: a copy reached through a workspace-linked dependency cannot be seen from ${here} alone, and scoping to it would miss the case this check exists for. Fix the copies where they live, or declare the package single-copy.`
|
|
470
645
|
}
|
|
471
646
|
|
|
@@ -577,6 +752,7 @@ export async function collectIdentityParity(
|
|
|
577
752
|
.map(([path, copy]) => ({
|
|
578
753
|
version: copy.version,
|
|
579
754
|
path: displayPath(requestedRoot, path),
|
|
755
|
+
absolutePath: path,
|
|
580
756
|
importers: [...copy.importers].sort(),
|
|
581
757
|
}))
|
|
582
758
|
const topology = describeTopology(requestedRoot, scanRoot, absolutePaths)
|
|
@@ -775,7 +951,11 @@ const sourceFilesUnder = (root: string): readonly string[] => {
|
|
|
775
951
|
const files: string[] = []
|
|
776
952
|
const walk = (current: string): void => {
|
|
777
953
|
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
778
|
-
if (
|
|
954
|
+
if (
|
|
955
|
+
["node_modules", "dist", "dist-node", "build", ".git", ".nifra", "coverage"].includes(
|
|
956
|
+
entry.name,
|
|
957
|
+
)
|
|
958
|
+
)
|
|
779
959
|
continue
|
|
780
960
|
const path = join(current, entry.name)
|
|
781
961
|
if (entry.isDirectory()) walk(path)
|
|
@@ -814,7 +994,7 @@ export function collectDevelopmentParityInput(
|
|
|
814
994
|
const sourceRoot = dirname(resolve(routesDir))
|
|
815
995
|
const css = sourceFilesUnder(sourceRoot).some((file) => {
|
|
816
996
|
const content = readFileSync(file, "utf8")
|
|
817
|
-
return
|
|
997
|
+
return hasStylesheetImport(content) || (isSingleFileComponent(file) && SFC_STYLE.test(content))
|
|
818
998
|
})
|
|
819
999
|
? ["css:present"]
|
|
820
1000
|
: []
|
|
@@ -855,9 +1035,9 @@ export function compareManifestParity(
|
|
|
855
1035
|
return differences
|
|
856
1036
|
}
|
|
857
1037
|
|
|
858
|
-
/** The forms
|
|
1038
|
+
/** The forms the source scanner cannot prove, quoted in a css parity failure so the reader knows where to look. */
|
|
859
1039
|
const CSS_SCANNER_BLIND_SPOTS =
|
|
860
|
-
"a
|
|
1040
|
+
"a bare package subpath (exports), an @import inside a stylesheet, template interpolation, or plugin-injected css"
|
|
861
1041
|
|
|
862
1042
|
const symmetricDifference = (
|
|
863
1043
|
a: readonly string[],
|
|
@@ -881,7 +1061,7 @@ function explainParityDifference(
|
|
|
881
1061
|
const shipped = (production.css ?? []).join(", ")
|
|
882
1062
|
const where = development.sourceRoot === undefined ? "" : ` under ${development.sourceRoot}`
|
|
883
1063
|
return (
|
|
884
|
-
`css: production ships [${shipped || "(none)"}] but the development scanner found
|
|
1064
|
+
`css: production ships [${shipped || "(none)"}] but the development scanner found a static ` +
|
|
885
1065
|
`stylesheet import${where} - it is blind to ${CSS_SCANNER_BLIND_SPOTS}`
|
|
886
1066
|
)
|
|
887
1067
|
}
|