@navecss/core 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vite.js CHANGED
@@ -1,17 +1,18 @@
1
- import { findingsInText, formatFinding } from './chunk-UANF3FGO.js';
2
- import { assertOptions, resolveUsedOptions, keptAtoms, atomsWrittenIn, applyEdits, compareText, withoutRemoved, CX_SOURCE, toSourceMap, isSecondHop, buildReport, moduleReport, isListableReexport, readModule, combinedMapOf, placerFor, cxModulesArray, nodeAt, staticStringOf, pushAll, childrenOf, nodesAt, LINE_UNKNOWN_WITHOUT_MAP, LINE_UNKNOWN_UNMAPPED, exportNameOf, atomOfClass, analyze, memberReadOf, patternDestructuring, stringAt, propertyNameOf } from './chunk-E5F6YF7A.js';
3
- import { validateExtendAtomsHostFree } from './chunk-LQSGQ2OA.js';
1
+ import { formatFinding, findingsInText } from './chunk-ZNTNLGEC.js';
2
+ import { assertOptions, resolveUsedOptions, keptAtoms, isStylesheetId, canHoldDirective, withRestorers, pruneAtomicLayer, atomsWrittenIn, applyEdits, forgetDeclared, filePathOf, compareText, isStylesheetPath, inspectAtomicLayer, fileOf, withoutRemoved, CX_SOURCE, decideReexports, isUnreadStylesheet, declaredModulesFor, isAboutListed, isDependencyId, toSourceMap, unprunedAtoms, judgeReexports, buildReport, specifiersOf, atomOfClass, isCssModuleId, moduleReport, specifiersIn, takesIn, UNREADABLE, CLASS_MAP_NOT_PARSED, isNode, recognisedKey, isListedFile, readModule, combinedMapOf, placerFor, isRelative, cxModulesArray, nodesAt, nodeAt, isNamingListed, staticStringOf, LINE_UNKNOWN_WITHOUT_MAP, LINE_UNKNOWN_UNMAPPED, isResolvingTo, resolveOrNull } from './chunk-EYMFWE3A.js';
3
+ import { validateExtendAtomsHostFree } from './chunk-B5CWZIBK.js';
4
4
  import { resolveExtendSpecifier, applyExtendModule } from './chunk-NKUZNRF4.js';
5
- import { snapshotExtendMap, expandText, foldLocated } from './chunk-LADFXRWQ.js';
6
- import { tokenize } from './chunk-NFS2REPQ.js';
5
+ import { snapshotExtendMap, expandText, foldLocated } from './chunk-KDLLTOAM.js';
6
+ import './chunk-UVHGEVMV.js';
7
7
  import { decodeIncomingMap, formatDiagnostic } from './chunk-SYNQJNVB.js';
8
8
  import './chunk-CUL5PLPI.js';
9
+ import './chunk-2K5EBBF2.js';
9
10
  import { atomClassMap } from './chunk-GZL2VW7C.js';
11
+ import { mkdirSync, writeFileSync, readFileSync, existsSync, realpathSync, statSync } from 'fs';
10
12
  import { readFile } from 'fs/promises';
11
- import path2 from 'path';
13
+ import path from 'path';
12
14
  import { fileURLToPath } from 'url';
13
15
  import { randomUUID } from 'crypto';
14
- import { mkdirSync, writeFileSync, readFileSync, existsSync, realpathSync, statSync } from 'fs';
15
16
 
16
17
  // src/vite-builds.ts
17
18
  function htmlPageOf(id) {
@@ -92,6 +93,14 @@ function secondHalf(pickers) {
92
93
  },
93
94
  buildEnd(error) {
94
95
  return pickers.forHook(this).collect.buildEnd.call(this, error);
96
+ },
97
+ // The same step as the first half's, ahead of every other plugin's: a plugin that inlines or
98
+ // renames a stylesheet in its own `generateBundle` has not yet done so.
99
+ generateBundle: {
100
+ order: "pre",
101
+ handler(options, bundle) {
102
+ pickers.forHook(this).collect.generateBundle.handler.call(this, options, bundle);
103
+ }
95
104
  }
96
105
  };
97
106
  }
@@ -147,134 +156,250 @@ function shareAcrossBuilds(assemble2) {
147
156
  return [firstHalf(pickers, configure), secondHalf(pickers)];
148
157
  }
149
158
 
150
- // src/vite-css-id.ts
151
- var STYLESHEET = /\.(?:css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/;
152
- var SPECIAL_QUERY = /[?&](?:worker|sharedworker|raw|url)\b/;
153
- function isStylesheetPath(id) {
154
- return STYLESHEET.test(id);
159
+ // src/vite-dev-reloads.ts
160
+ function freshReloads() {
161
+ return {
162
+ stale: /* @__PURE__ */ new Set(),
163
+ reloading: /* @__PURE__ */ new Map(),
164
+ listening: 0,
165
+ unheard: /* @__PURE__ */ new Set(),
166
+ reloadTimer: void 0,
167
+ readAhead: 0
168
+ };
155
169
  }
156
- function isStylesheetId(id) {
157
- return STYLESHEET.test(id) && !SPECIAL_QUERY.test(id);
170
+ function noteReloadSent(state, id) {
171
+ state.reloading.set(id, Date.now());
172
+ if (state.listening === 0) state.unheard.add(id);
158
173
  }
159
- function canHoldDirective(code) {
160
- return /@[n\\]/i.test(code);
174
+
175
+ // src/vite-report.ts
176
+ function withSourceRoot(source, sourceRoot) {
177
+ if (!sourceRoot || /^(?:[a-z][a-z\d+.-]*:|\/)/i.test(source)) return source;
178
+ return `${sourceRoot.replace(/\/$/, "")}/${source}`;
161
179
  }
162
- function filePathOf(id) {
163
- const query = id.indexOf("?");
164
- return query === -1 ? id : id.slice(0, query);
180
+ function locateAt(position, text, input) {
181
+ const origin = input.incoming?.originalPositionFor({
182
+ line: position.line,
183
+ column: position.column - 1
184
+ });
185
+ if (origin === void 0) {
186
+ return {
187
+ text,
188
+ file: input.file,
189
+ isProcessed: true,
190
+ line: position.line,
191
+ column: position.column
192
+ };
193
+ }
194
+ return {
195
+ text,
196
+ file: withSourceRoot(origin.source, input.incoming?.sourceRoot),
197
+ isProcessed: false,
198
+ line: origin.line,
199
+ column: origin.column + 1
200
+ };
165
201
  }
166
-
167
- // src/vite-cx-text-test.ts
168
- function literalPattern(text) {
169
- return text.replaceAll(/[$()*+.?[\\\]^{|}]/g, String.raw`\$&`);
170
- }
171
- var INSIDE = String.raw`[^\n'"\x60\\]{0,512}`;
172
- var EXTENSION = String.raw`\.[^\n'"\x60\\/?#.]*`;
173
- var QUOTE = String.raw`['"\x60]`;
174
- function textTestOf(names) {
175
- if (names.size === 0) return void 0;
176
- const listed = [...names].map((name) => literalPattern(name)).join("|");
177
- const named = `${QUOTE}(?:${INSIDE}/)?(?:${listed})(?:${EXTENSION})?/*(?:[?#]${INSIDE})?${QUOTE}`;
178
- const directory = String.raw`(?:\bfrom|\bimport)\s*(?:\(\s*)?${QUOTE}\.{1,2}(?:/\.{1,2})*/?${QUOTE}`;
179
- return new RegExp(names.has("index") ? `${named}|${directory}` : named);
202
+ function locate(diagnostic, input) {
203
+ return locateAt(diagnostic, formatDiagnostic(diagnostic, { extend: input.extend }), input);
180
204
  }
181
-
182
- // src/vite-cx-modules.ts
183
- function isRelative(text) {
184
- const cut = text.search(/[#?]/);
185
- const path10 = cut === -1 ? text : text.slice(0, cut);
186
- return path10 === "." || path10 === ".." || path10.startsWith("./") || path10.startsWith("../");
205
+ function withNoMapNote(body, input, at) {
206
+ if (!at.isProcessed) return body;
207
+ const reason = input.incoming === void 0 ? "no source map" : "the source map does not cover it";
208
+ const note = `(position in ${input.file} as processed; ${reason})`;
209
+ const lines = body.split("\n");
210
+ const hasAvailable = lines.at(-1)?.startsWith("Available: ") === true;
211
+ lines.splice(hasAvailable ? -1 : lines.length, 0, note);
212
+ return lines.join("\n");
187
213
  }
188
- function fileOf(id) {
189
- return filePathOf(id).replaceAll("\\", "/");
214
+ function logFor(message, input, at) {
215
+ return {
216
+ message,
217
+ id: input.id,
218
+ loc: { file: at.file, line: at.line, column: at.column - 1 }
219
+ };
190
220
  }
191
- function namesOf(file) {
192
- const name = path2.basename(file, path2.extname(file));
193
- return name === "index" ? [name, path2.basename(path2.dirname(file))] : [name];
221
+ function frame(at) {
222
+ return `${at.file}:${at.line}:${at.column}: `;
194
223
  }
195
- function entriesNaming(declared, id) {
196
- const file = fileOf(id);
197
- return declared.ids.entries().filter(([, resolved]) => fileOf(resolved) === file).map(([entry]) => entry).toArray();
224
+ function reportDiagnostics(input) {
225
+ if (input.onUnknown === "ignore" || input.diagnostics.length === 0) return;
226
+ const inSourceOrder = input.diagnostics.toSorted((a, b) => a.offset - b.offset);
227
+ const located = inSourceOrder.map((diagnostic) => locate(diagnostic, input));
228
+ if (input.onUnknown === "warn") {
229
+ for (const at of located) {
230
+ const message2 = withNoMapNote(`${frame(at)}${at.text}`, input, at);
231
+ input.ctx.warn(logFor(message2, input, at));
232
+ }
233
+ return;
234
+ }
235
+ const first = located[0];
236
+ const message = withNoMapNote(`${frame(first)}${foldLocated(located)}`, input, first);
237
+ input.ctx.error(logFor(message, input, first));
198
238
  }
199
- async function declaredModulesFor(context, resolver, environment) {
200
- const { cxModules } = context.options;
201
- if (cxModules.length === 0) return void 0;
202
- const known = context.state.declared.get(environment);
203
- if (known) return known;
204
- const made = resolveEntries(context, resolver);
205
- context.state.declared.set(environment, made);
206
- return made;
239
+ function placeOrderWarnings(input) {
240
+ return input.warnings.map((warning) => {
241
+ const at = locateAt(warning, warning.text, input);
242
+ const message = withNoMapNote(`${frame(at)}${at.text}`, input, at);
243
+ return {
244
+ key: `${at.file}\0${at.line}\0${at.column}\0${at.text}`,
245
+ print: () => {
246
+ input.ctx.warn(logFor(message, input, at));
247
+ }
248
+ };
249
+ });
207
250
  }
208
- async function resolveOrNull(resolver, source, importer) {
209
- try {
210
- return await resolver.resolve?.(source, importer) ?? void 0;
211
- } catch {
212
- return void 0;
251
+
252
+ // src/vite-order-warnings.ts
253
+ function freshWarningMemory() {
254
+ return { printed: /* @__PURE__ */ new Set(), lastSets: /* @__PURE__ */ new Map() };
255
+ }
256
+ function noteNoDirective(memory, id) {
257
+ memory.lastSets.delete(id);
258
+ }
259
+ function reportOrderWarnings(input, memory, command) {
260
+ const placed = placeOrderWarnings(input);
261
+ if (command === "serve") {
262
+ const set = placed.map((warning) => warning.key).join("\n");
263
+ if (memory.lastSets.get(input.id) === set) return;
264
+ memory.lastSets.set(input.id, set);
265
+ for (const warning of placed) warning.print();
266
+ return;
267
+ }
268
+ for (const warning of placed) {
269
+ if (memory.printed.has(warning.key)) continue;
270
+ memory.printed.add(warning.key);
271
+ warning.print();
213
272
  }
214
273
  }
215
- async function resolveEntries(context, resolver) {
216
- const from = path2.join(context.root, "index.html");
217
- const nave = await resolveOrNull(resolver, CX_SOURCE, from);
218
- const naveFile = nave === void 0 ? void 0 : fileOf(nave.id);
219
- const resolved = await Promise.all(
220
- context.options.cxModules.map(async (entry) => {
221
- const found = await resolveOrNull(resolver, entry, from);
222
- return [entry, found?.id];
223
- })
224
- );
225
- const kept = resolved.filter(
226
- ([entry, id]) => entry !== CX_SOURCE && (id === void 0 || fileOf(id) !== naveFile)
227
- );
228
- const ids = new Map(kept.filter((pair) => pair[1] !== void 0));
229
- const files = new Set(ids.values().map((id) => fileOf(id)));
230
- const names = new Set(files.values().flatMap((file) => namesOf(file)));
274
+
275
+ // src/vite-writers.ts
276
+ function freshWriters() {
231
277
  return {
232
- entries: new Set(kept.map(([entry]) => entry).filter((entry) => !isRelative(entry))),
233
- ids,
234
- files,
235
- names,
236
- textTest: textTestOf(names)
278
+ emittedBy: void 0,
279
+ firstWriter: void 0,
280
+ fixed: /* @__PURE__ */ new Map(),
281
+ writers: /* @__PURE__ */ new Map(),
282
+ whole: /* @__PURE__ */ new Set(),
283
+ wholeAssets: /* @__PURE__ */ new Set()
237
284
  };
238
285
  }
239
- function forgetDeclared(state, environment) {
240
- state.declared.delete(environment);
241
- const prefix = `${environment}\0`;
242
- for (const key of state.recognised.keys())
243
- if (key.startsWith(prefix)) state.recognised.delete(key);
286
+ function hasStylesheet(state, name) {
287
+ const prefix = `${name}\0`;
288
+ return state.sheets.keys().some((key) => key.startsWith(prefix));
289
+ }
290
+ function forgetWriters(state) {
291
+ state.emitted = void 0;
292
+ state.emittedBy = void 0;
293
+ state.firstWriter = void 0;
294
+ state.fixed.clear();
295
+ state.writers.clear();
296
+ state.whole.clear();
297
+ state.wholeAssets.clear();
298
+ }
299
+
300
+ // src/vite-state.ts
301
+ var byConfig = /* @__PURE__ */ new WeakMap();
302
+ var byBuild = /* @__PURE__ */ new WeakMap();
303
+ function buildOf(config) {
304
+ return config.command === "build" ? config.inlineConfig : void 0;
305
+ }
306
+ function mapOf(registry2, owner) {
307
+ const found = registry2.get(owner) ?? /* @__PURE__ */ new Map();
308
+ registry2.set(owner, found);
309
+ return found;
310
+ }
311
+ function freshState() {
312
+ return {
313
+ started: false,
314
+ modules: /* @__PURE__ */ new Map(),
315
+ exposures: /* @__PURE__ */ new Map(),
316
+ templateLinks: /* @__PURE__ */ new Map(),
317
+ packages: /* @__PURE__ */ new Set(),
318
+ declared: /* @__PURE__ */ new Map(),
319
+ recognised: /* @__PURE__ */ new Map(),
320
+ sheets: /* @__PURE__ */ new Map(),
321
+ pages: /* @__PURE__ */ new Map(),
322
+ emitted: void 0,
323
+ ...freshWriters(),
324
+ externals: /* @__PURE__ */ new Set(),
325
+ clientEnded: false,
326
+ serverImports: [],
327
+ client: void 0,
328
+ expectedEnvironments: void 0,
329
+ builtEnvironments: /* @__PURE__ */ new Set(),
330
+ warned: /* @__PURE__ */ new Set(),
331
+ served: /* @__PURE__ */ new Map(),
332
+ ...freshReloads(),
333
+ htmlRead: false,
334
+ serverTransformed: false,
335
+ directiveExpanded: false,
336
+ markupJudged: false,
337
+ warmedUp: false,
338
+ requested: /* @__PURE__ */ new Set(),
339
+ orderWarnings: freshWarningMemory()
340
+ };
341
+ }
342
+ function stateFor(root, config) {
343
+ const known = byConfig.get(config)?.get(root);
344
+ if (known) return known;
345
+ const build = buildOf(config);
346
+ const shared = build && byBuild.get(build)?.get(root);
347
+ const state = shared && !shared.started ? shared : freshState();
348
+ mapOf(byConfig, config).set(root, state);
349
+ if (build) mapOf(byBuild, build).set(root, state);
350
+ return state;
351
+ }
352
+ function moduleKey(environment, id) {
353
+ return `${environment}\0${id}`;
244
354
  }
245
- function withoutTrailingSlashes(text) {
246
- let end = text.length;
247
- while (end > 0 && text[end - 1] === "/") end -= 1;
248
- return text.slice(0, end);
355
+ function collectedAtoms(state, kept) {
356
+ const atoms = new Set(kept);
357
+ for (const record of state.modules.values()) for (const atom of record.atoms) atoms.add(atom);
358
+ for (const page of state.pages.values()) for (const atom of page) atoms.add(atom);
359
+ return atoms;
249
360
  }
250
- function lastSegmentOf(specifier) {
251
- const cut = specifier.slice(1).search(/[#?]/);
252
- const bare = withoutTrailingSlashes(cut === -1 ? specifier : specifier.slice(0, cut + 1));
253
- const segment = bare.slice(bare.lastIndexOf("/") + 1);
254
- return segment === "." || segment === ".." ? "index" : segment;
361
+ function servedAtoms(state, kept) {
362
+ return withRestorers(collectedAtoms(state, kept));
255
363
  }
256
- function isNamingListed(specifier, declared) {
257
- const segment = lastSegmentOf(specifier);
258
- if (declared.names.has(segment)) return true;
259
- const extension = path2.extname(segment);
260
- return extension !== "" && declared.names.has(segment.slice(0, -extension.length));
364
+ function recordsOf(state, environment) {
365
+ const prefix = `${environment}\0`;
366
+ return [...state.modules].filter(([key]) => key.startsWith(prefix)).map(([, record]) => record);
261
367
  }
262
- function isAboutListed(input, declared) {
263
- if (!declared) return false;
264
- return isListedFile(declared, input.id) || isMentioningDeclared(input.code, declared);
368
+
369
+ // src/vite-css-capture.ts
370
+ var originals = /* @__PURE__ */ new WeakMap();
371
+ function transformHandler(plugin) {
372
+ const { transform } = plugin;
373
+ if (typeof transform === "function") return transform;
374
+ const handler = transform?.handler;
375
+ return typeof handler === "function" ? handler : void 0;
265
376
  }
266
- function isListedFile(declared, id) {
267
- return declared.files.has(fileOf(id));
377
+ function unwrapped(handler) {
378
+ let current = handler;
379
+ for (let next = originals.get(current); next; next = originals.get(current)) current = next;
380
+ return current;
268
381
  }
269
- function isMentioningDeclared(code, declared) {
270
- for (const entry of declared.entries) if (code.includes(entry)) return true;
271
- return declared.textTest?.test(code) ?? false;
382
+ function remember(context, environment, id, css) {
383
+ const key = moduleKey(environment, id);
384
+ const isPossible = css.includes("\\") || /layer/i.test(css);
385
+ if (isPossible && inspectAtomicLayer(css).hasLayer) context.state.sheets.set(key, css);
386
+ else context.state.sheets.delete(key);
272
387
  }
273
- function recognisedKey(environment, file, importer) {
274
- return `${environment}\0${file}\0${importer}`;
388
+ function captureStylesheets(context, plugins) {
389
+ const plugin = plugins?.find((candidate) => candidate.name === "vite:css-post");
390
+ const original = plugin && transformHandler(plugin);
391
+ if (!plugin || !original) return;
392
+ const wrapped = function(css, id) {
393
+ const environment = this.environment?.name ?? "client";
394
+ remember(context, environment, id, css);
395
+ return original.call(this, css, id);
396
+ };
397
+ originals.set(wrapped, original);
398
+ const { transform } = plugin;
399
+ plugin.transform = typeof transform === "function" ? wrapped : { ...transform, handler: wrapped };
275
400
  }
276
401
  var NAVE_MODULE_DIRS = ["src", "dist"].map(
277
- (name) => `${path2.resolve(path2.dirname(fileURLToPath(import.meta.url)), "..", name).replaceAll("\\", "/")}/`
402
+ (name) => `${path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", name).replaceAll("\\", "/")}/`
278
403
  );
279
404
  function pathOfId(id) {
280
405
  return filePathOf(id).replaceAll("\\", "/");
@@ -283,11 +408,8 @@ function isNaveOwn(id) {
283
408
  const file = pathOfId(id);
284
409
  return NAVE_MODULE_DIRS.some((directory) => file.startsWith(directory));
285
410
  }
286
- function isDependencyId(id) {
287
- return pathOfId(id).includes("/node_modules/");
288
- }
289
411
  async function packageNameOf(file, cache) {
290
- const directory = path2.dirname(file);
412
+ const directory = path.dirname(file);
291
413
  const cached = cache.get(directory);
292
414
  if (cached) return cached;
293
415
  const found = nameAbove(directory, cache);
@@ -296,13 +418,13 @@ async function packageNameOf(file, cache) {
296
418
  }
297
419
  async function nameAbove(directory, cache) {
298
420
  const name = await readName(directory);
299
- const parent = path2.dirname(directory);
421
+ const parent = path.dirname(directory);
300
422
  if (name !== void 0 || parent === directory) return name;
301
423
  return packageNameOf(directory, cache);
302
424
  }
303
425
  async function readName(directory) {
304
426
  try {
305
- const manifest = JSON.parse(await readFile(path2.join(directory, "package.json"), "utf8"));
427
+ const manifest = JSON.parse(await readFile(path.join(directory, "package.json"), "utf8"));
306
428
  return typeof manifest.name === "string" ? manifest.name : void 0;
307
429
  } catch {
308
430
  return void 0;
@@ -311,310 +433,408 @@ async function readName(directory) {
311
433
  function moduleLabel(root, id) {
312
434
  const file = filePathOf(id);
313
435
  if (file.startsWith("\0")) return String.raw`\0${file.slice(1)}`;
314
- if (!path2.isAbsolute(file)) return file;
315
- return path2.relative(root, file).replaceAll("\\", "/");
436
+ if (!path.isAbsolute(file)) return file;
437
+ return path.relative(root, file).replaceAll("\\", "/");
316
438
  }
317
439
 
318
- // src/vite-cx-importers.ts
319
- function importerIdsOf(ctx, cxId) {
320
- const info = ctx.getModuleInfo?.(cxId);
321
- return [.../* @__PURE__ */ new Set([...info?.importers ?? [], ...info?.dynamicImporters ?? []])];
440
+ // src/vite-emit.ts
441
+ var decoder = new TextDecoder();
442
+ async function refeed(ctx, css, id) {
443
+ const plugin = ctx.environment.plugins.find((candidate) => candidate.name === "vite:css-post");
444
+ const handler = plugin && transformHandler(plugin);
445
+ if (!handler) {
446
+ ctx.error(
447
+ "nave: this version of Vite has no CSS step the plugin knows how to hand a stylesheet back to, so it cannot remove the unused atoms. Ship every atom with atomic: 'all' in navePlugin() until the plugin supports this version of Vite."
448
+ );
449
+ }
450
+ await unwrapped(handler).call(ctx, css, id);
322
451
  }
323
- async function isStoodInFor(context, id) {
324
- if (!isDependencyId(id)) return false;
325
- const pkg = await packageNameOf(filePathOf(id), context.packageNames);
326
- return pkg !== void 0 && Object.hasOwn(context.options.keepFor, pkg);
452
+ var fedPages = /* @__PURE__ */ new WeakMap();
453
+ var warnedText = /* @__PURE__ */ new WeakMap();
454
+ function bookFor(book, emitted) {
455
+ const ids = book.get(emitted) ?? /* @__PURE__ */ new Set();
456
+ book.set(emitted, ids);
457
+ return ids;
327
458
  }
328
- async function candidateOf(ctx, context, id) {
329
- const code = ctx.getModuleInfo?.(id)?.code;
330
- if (code === null || code === void 0 || isNaveOwn(id)) return void 0;
331
- return await isStoodInFor(context, id) ? void 0 : { id, code };
459
+ function isPageStylesheet(id) {
460
+ return /[?&]html-proxy\b/.test(id);
332
461
  }
333
- function specifiersIn(program) {
334
- const found = /* @__PURE__ */ new Map();
335
- const stack = [program];
336
- while (stack.length > 0) {
337
- const node = stack.pop();
338
- const where = nodeAt(node, "source");
339
- const source = staticStringOf(where);
340
- if (where !== void 0 && source !== void 0) {
341
- found.set(source, Math.min(found.get(source) ?? Infinity, where.start));
342
- }
343
- pushAll(stack, childrenOf(node));
344
- }
345
- return [...found].toSorted(([, a], [, b]) => a - b).map(([source]) => source);
462
+ function pageStylesheets(ctx, state, emitted) {
463
+ const prefix = `${ctx.environment.name}\0`;
464
+ const fed = bookFor(fedPages, emitted);
465
+ const ids = state.sheets.keys().filter((key) => key.startsWith(prefix) && isPageStylesheet(key)).map((key) => key.slice(prefix.length)).filter((id) => !fed.has(id)).toArray();
466
+ for (const id of ids) fed.add(id);
467
+ return ids;
468
+ }
469
+ function isInlineImport(id) {
470
+ return /[?&]inline\b/.test(id) && !isPageStylesheet(id);
471
+ }
472
+ function isRawImport(id) {
473
+ return /[?&]raw\b/.test(id) && isStylesheetPath(id);
346
474
  }
347
- function specifiersOf(ctx, importer) {
475
+ function isLayerFile(id) {
348
476
  try {
349
- return specifiersIn(ctx.parse?.(importer.code));
477
+ return inspectAtomicLayer(readFileSync(filePathOf(id), "utf8")).hasLayer;
350
478
  } catch {
351
- return void 0;
479
+ return false;
352
480
  }
353
481
  }
354
- async function isResolvingToCx(ctx, specifier, importer, known) {
355
- if (specifier === CX_SOURCE) return false;
356
- const key = `${importer.id}\0${specifier}`;
357
- const answered = known.answers.get(key);
358
- if (answered !== void 0) return answered;
359
- const resolved = await ctx.resolve?.(specifier, importer.id);
360
- const isCx = resolved?.id === known.cxId;
361
- known.answers.set(key, isCx);
362
- return isCx;
363
- }
364
- function lineFor(label, specifiers) {
365
- if (specifiers.length === 0) {
366
- return `${label}: imports cx through a specifier the build does not read.`;
482
+ async function refeedChunkStylesheets(ctx, chunk, state, emitted) {
483
+ const prefix = `${ctx.environment.name}\0`;
484
+ for (const id of [...Object.keys(chunk.modules), ...pageStylesheets(ctx, state, emitted)]) {
485
+ const css = state.sheets.get(`${prefix}${id}`);
486
+ if (css === void 0 || isInlineImport(id)) continue;
487
+ await refeed(ctx, pruneAtomicLayer(css, emitted), id);
367
488
  }
368
- return `${label}: imports cx from ${specifiers.map((specifier) => `'${specifier}'`).join(", ")}.`;
369
489
  }
370
- async function lineOf(ctx, context, importer, known) {
371
- const written = specifiersOf(ctx, importer);
372
- const unread = [];
373
- const specifiers = written ?? [];
374
- for (const specifier of specifiers) {
375
- if (await isResolvingToCx(ctx, specifier, importer, known)) unread.push(specifier);
376
- }
377
- const label = moduleLabel(context.root, importer.id);
378
- if (unread.length > 0) return lineFor(label, unread);
379
- const isRead = written?.includes(CX_SOURCE) ?? importer.code.includes(CX_SOURCE);
380
- return isRead ? void 0 : lineFor(label, []);
490
+ function warnTextImports(ctx, chunk, context, emitted) {
491
+ const prefix = `${ctx.environment.name}\0`;
492
+ const warned = bookFor(warnedText, emitted);
493
+ for (const id of Object.keys(chunk.modules)) {
494
+ if (warned.has(id)) continue;
495
+ const isInline = isInlineImport(id) && context.state.sheets.has(`${prefix}${id}`);
496
+ const isRaw = !isInline && isRawImport(id) && isLayerFile(id);
497
+ if (!isInline && !isRaw) continue;
498
+ warned.add(id);
499
+ const query = isInline ? "?inline" : "?raw";
500
+ ctx.warn(
501
+ `${moduleLabel(context.root, id)} is imported with ${query}, so its stylesheet is text in the JavaScript, which the plugin does not filter: every atom in its atomic layer ships, not only the atoms this build emits. Import it without ${query} to have the layer filtered.`
502
+ );
503
+ }
381
504
  }
382
- async function checkCxImporters(ctx, context) {
383
- const resolved = await ctx.resolve?.(CX_SOURCE, path2.join(context.root, "index.html"));
384
- if (!resolved) return;
385
- const known = { cxId: resolved.id, answers: /* @__PURE__ */ new Map() };
386
- const lines = [];
387
- for (const id of importerIdsOf(ctx, resolved.id)) {
388
- const importer = await candidateOf(ctx, context, id);
389
- const line = importer && await lineOf(ctx, context, importer, known);
390
- if (line) lines.push(line);
505
+ function textOf(entry) {
506
+ const { source } = entry;
507
+ if (source === void 0) return "";
508
+ return typeof source === "string" ? source : decoder.decode(source);
509
+ }
510
+ function readLayers(bundle, emitted) {
511
+ const reading = /* @__PURE__ */ new Map();
512
+ const assets = Object.values(bundle).filter((entry) => entry.type === "asset" && entry.fileName.toLowerCase().endsWith(".css")).map((entry) => ({ fileName: entry.fileName, css: textOf(entry) }));
513
+ for (const { fileName, css } of assets) {
514
+ const seen = inspectAtomicLayer(css);
515
+ if (!seen.hasLayer) continue;
516
+ const extra = unprunedAtoms(css, emitted);
517
+ reading.set(fileName, {
518
+ seen: new Set(seen.atoms),
519
+ extra: extra.length > 0 ? `${fileName}: the atomic layer holds a rule for ${extra.join(", ")}, which the build did not emit.` : void 0
520
+ });
391
521
  }
392
- if (lines.length === 0) return;
393
- const count = lines.length === 1 ? "1 module imports" : `${lines.length} modules import`;
394
- ctx.error(
395
- [
396
- `${count} cx through a specifier the build does not follow, so the atoms its calls name would not ship:`,
397
- ...lines.toSorted(compareText),
398
- "Import cx from '@navecss/core/cx' directly where it is called."
399
- ].join("\n")
400
- );
522
+ return reading;
401
523
  }
402
-
403
- // src/vite-cx-reading.ts
404
- async function declaredReadingOf(context, resolver, declared, input) {
405
- const { environment, module } = input;
406
- const declaredSources = /* @__PURE__ */ new Set();
407
- if (!declared) return { declaredSources, isDeclared: false, onlyVia: void 0 };
408
- const files = /* @__PURE__ */ new Set();
409
- let isOnlyListed = true;
410
- for (const source of specifiersIn(module.program)) {
411
- if (source === CX_SOURCE) {
412
- isOnlyListed = false;
413
- continue;
414
- }
415
- const found = await listedFileOf(resolver, declared, source, module.id);
416
- if (!found) continue;
417
- declaredSources.add(source);
418
- if (found.file === void 0) {
419
- isOnlyListed = false;
420
- continue;
421
- }
422
- files.add(found.file);
423
- const key = recognisedKey(environment, found.file, module.id);
424
- context.state.recognised.set(
425
- key,
426
- /* @__PURE__ */ new Set([...context.state.recognised.get(key) ?? [], source])
427
- );
524
+ function mismatchLines(reading, emitted) {
525
+ const assets = reading.values().toArray();
526
+ const lines = assets.flatMap((asset) => asset.extra ? [asset.extra] : []);
527
+ const seenAnywhere = new Set(assets.flatMap((asset) => [...asset.seen]));
528
+ const missing = [...emitted.difference(seenAnywhere)];
529
+ if (reading.size > 0 && missing.length > 0) {
530
+ lines.push(`the atomic layers hold no rule for ${missing.join(", ")}, which the build emitted.`);
428
531
  }
429
- return {
430
- declaredSources,
431
- isDeclared: isListedFile(declared, module.id),
432
- onlyVia: isOnlyListed && files.size > 0 ? files : void 0
433
- };
532
+ return lines;
434
533
  }
435
- async function listedFileFrom(resolver, declared, source, importer) {
436
- const resolved = await resolveOrNull(resolver, source, importer);
437
- const file = resolved ? fileOf(resolved.id) : void 0;
438
- return file !== void 0 && declared.files.has(file) ? file : void 0;
534
+ function layerMessage(lines) {
535
+ return [
536
+ "nave: the built CSS does not hold exactly the atoms the build emitted, so the plugin could not remove the unused ones on this version of Vite.",
537
+ ...lines,
538
+ "Ship every atom with atomic: 'all' in navePlugin() until the plugin supports this version of Vite."
539
+ ].join("\n");
439
540
  }
440
- async function listedFileOf(resolver, declared, source, importer) {
441
- if (declared.entries.has(source)) {
442
- const id = source.startsWith("#") ? void 0 : declared.ids.get(source);
443
- if (id !== void 0) return { file: fileOf(id) };
444
- const file2 = await listedFileFrom(resolver, declared, source, importer);
445
- return file2 === void 0 && source.startsWith("#") ? void 0 : { file: file2 };
541
+ var HANDSHAKE_FILE = "nave-used-atoms.json";
542
+ function handshakePath(cacheDir) {
543
+ return path.join(cacheDir, HANDSHAKE_FILE);
544
+ }
545
+ function readHandshake(cacheDir) {
546
+ if (cacheDir === "") return void 0;
547
+ try {
548
+ const parsed2 = JSON.parse(readFileSync(handshakePath(cacheDir), "utf8"));
549
+ return Array.isArray(parsed2.emitted) && typeof parsed2.writer === "string" ? parsed2 : void 0;
550
+ } catch {
551
+ return void 0;
552
+ }
553
+ }
554
+ function writeHandshake(cacheDir, fields, warn) {
555
+ if (cacheDir === "") return;
556
+ const handshake = {
557
+ ...fields,
558
+ emitted: [...fields.emitted].toSorted(compareText),
559
+ writtenAt: fields.writtenAt ?? (/* @__PURE__ */ new Date()).toISOString(),
560
+ build: fields.build ?? randomUUID()
561
+ };
562
+ try {
563
+ mkdirSync(cacheDir, { recursive: true });
564
+ writeFileSync(handshakePath(cacheDir), `${JSON.stringify(handshake, void 0, 2)}
565
+ `);
566
+ } catch (error) {
567
+ const reason = error instanceof Error ? error.message : String(error);
568
+ warn?.(
569
+ `navePlugin(): could not write ${handshakePath(cacheDir)} (${reason}), so a build of the client and a build of the server run as two processes cannot share their atoms.`
570
+ );
446
571
  }
447
- if (!isNamingListed(source, declared)) return void 0;
448
- const file = await listedFileFrom(resolver, declared, source, importer);
449
- return file === void 0 ? void 0 : { file };
450
572
  }
451
573
 
452
- // src/vite-dev-keep.ts
453
- var KEEP_CONSTANT = "__NAVE_KEEP_CLASSES__";
454
- function keepMap(kept) {
455
- return Object.fromEntries(
456
- kept.filter((atom) => Object.hasOwn(atomClassMap, atom)).map((atom) => [atom, atomClassMap[atom]])
457
- );
574
+ // src/vite-emitted.ts
575
+ function positionText(place) {
576
+ return place.line === 0 ? place.file : `${place.file}:${place.line}:${place.column}`;
458
577
  }
459
- function devKeepMap(map, atoms) {
460
- return new Proxy(map, {
461
- getOwnPropertyDescriptor(target, key) {
462
- const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
463
- if (descriptor === void 0 && typeof key === "string") {
464
- const quoted2 = JSON.stringify(key);
465
- console.error(
466
- atoms.includes(key) ? `[nave] cx.dynamic(${quoted2}) applied no class: ${quoted2} is not in keep or in a keepFor list, and navePlugin() gives cx.dynamic() only the atoms in those lists, in a build as in dev. Add ${quoted2} to keep in navePlugin(), or, when a dependency makes the call, to its list in keepFor.` : `[nave] cx.dynamic(${quoted2}) applied no class: ${quoted2} is not a Nave atom.`
467
- );
468
- }
469
- return descriptor;
470
- }
471
- });
578
+ function packageRecords(context) {
579
+ const records = {};
580
+ for (const pkg of Object.keys(context.options.keepFor)) {
581
+ const modules = context.state.modules.values().filter((record) => record.pkg === pkg).toArray();
582
+ const places = modules.flatMap((record) => [...record.suppressed, ...record.dynamicCalls]);
583
+ records[pkg] = {
584
+ collected: [...new Set(modules.flatMap((record) => [...record.atoms]))].toSorted(compareText),
585
+ unreadable: places.map((place) => positionText(place))
586
+ };
587
+ }
588
+ return records;
472
589
  }
473
- function devKeepExpression(map, atoms) {
474
- return `(${devKeepMap.toString()})(${JSON.stringify(map)}, ${JSON.stringify(atoms)})`;
590
+ function unmatchedKeys(context) {
591
+ const transformed = new Set(context.state.modules.values().map((record) => record.pkg));
592
+ return Object.keys(context.options.keepFor).filter((pkg) => !transformed.has(pkg));
475
593
  }
476
- var installs = [];
477
- var baseline = { held: false, value: void 0 };
478
- function publish() {
479
- const newest = installs.at(-1);
480
- if (newest !== void 0) Object.assign(globalThis, { [KEEP_CONSTANT]: newest.map });
481
- else if (baseline.held) Object.assign(globalThis, { [KEEP_CONSTANT]: baseline.value });
482
- else Reflect.deleteProperty(globalThis, KEEP_CONSTANT);
594
+ function takenFromCache(context) {
595
+ const file = context.inProcess ? void 0 : readHandshake(context.cacheDir);
596
+ if (file === void 0) return { atoms: [], took: false };
597
+ if (file.writer !== "client") return { atoms: file.emitted, took: true };
598
+ const pending = file.pending ?? [];
599
+ return { atoms: pending, took: pending.length > 0 };
483
600
  }
484
- function installServerKeepMap(context, owner) {
485
- if (context.options.atomic !== "used") return;
486
- const map = devKeepMap(keepMap(context.kept), Object.keys(atomClassMap));
487
- if (installs.length === 0) {
488
- baseline.held = Object.hasOwn(globalThis, KEEP_CONSTANT);
489
- baseline.value = globalThis[KEEP_CONSTANT];
601
+ function canWriteStylesheets(context, environment) {
602
+ const { state } = context;
603
+ if (environment.config.consumer === "client")
604
+ return state.firstWriter !== "server" || hasStylesheet(state, environment.name);
605
+ if (!context.inProcess || state.firstWriter === "client") return false;
606
+ return environment.config.build?.emitAssets === true;
607
+ }
608
+ function emittedSet(context, environment, warn) {
609
+ const { state } = context;
610
+ const known = state.fixed.get(environment.name);
611
+ if (known) return known;
612
+ const atoms = collectedAtoms(state, context.kept);
613
+ const taken = takenFromCache(context);
614
+ for (const atom of taken.atoms) atoms.add(atom);
615
+ withRestorers(atoms);
616
+ state.fixed.set(environment.name, atoms);
617
+ if (state.emitted === void 0) {
618
+ state.emitted = atoms;
619
+ state.emittedBy = environment.name;
620
+ state.firstWriter = environment.config.consumer === "client" ? "client" : "server";
490
621
  }
491
- installs.push({ owner, map });
492
- publish();
622
+ writeHandshake(
623
+ context.cacheDir,
624
+ {
625
+ emitted: [...atoms],
626
+ writer: environment.config.consumer === "client" ? "client" : environment.name,
627
+ consumed: taken.took,
628
+ keepFor: packageRecords(context),
629
+ unmatchedKeepFor: unmatchedKeys(context)
630
+ },
631
+ warn
632
+ );
633
+ return atoms;
493
634
  }
494
- function removeServerKeepMap(owner) {
495
- const index = installs.findIndex((install) => install.owner === owner);
496
- if (index === -1) return;
497
- installs.splice(index, 1);
498
- publish();
635
+ function namingLines(ctx, context, atoms) {
636
+ return recordsOf(context.state, ctx.environment.name).flatMap(
637
+ (record) => atoms.filter((atom) => record.atoms.has(atom)).map((atom) => `${record.file}: names ${atom}.`)
638
+ );
499
639
  }
500
-
501
- // src/vite-dev-reloads.ts
502
- function freshReloads() {
503
- return {
504
- stale: /* @__PURE__ */ new Set(),
505
- reloading: /* @__PURE__ */ new Map(),
506
- listening: 0,
507
- unheard: /* @__PURE__ */ new Set(),
508
- reloadTimer: void 0,
509
- readAhead: 0
510
- };
640
+ function missingFromClient(ctx, context, missing) {
641
+ return [
642
+ "The client build already wrote its CSS without atoms this server build names, so their rules are missing from it:",
643
+ ...namingLines(ctx, context, missing),
644
+ `They are recorded in ${handshakePath(context.cacheDir)} for the next client build. List the atoms in keep in navePlugin(), or build the client again, then the server (vite build, then vite build --ssr). To keep this from recurring, build the server first, then the client (vite build --ssr, then vite build).`
645
+ ].join("\n");
511
646
  }
512
- function noteReloadSent(state, id) {
513
- state.reloading.set(id, Date.now());
514
- if (state.listening === 0) state.unheard.add(id);
647
+ function notInLastClient(ctx, context, missing) {
648
+ return [
649
+ `${handshakePath(context.cacheDir)} was written by a client build that was already used, so this server build could not be checked against the CSS. These atoms it names are not in that client build's set:`,
650
+ ...namingLines(ctx, context, missing),
651
+ "They are recorded in that file for a client build that runs next; to have them in the CSS, build the server first, then the client (vite build --ssr, then vite build)."
652
+ ].join("\n");
653
+ }
654
+ function recordForNextClient(ctx, context, atoms, held) {
655
+ writeHandshake(
656
+ context.cacheDir,
657
+ { emitted: [...withRestorers(new Set(atoms))], writer: ctx.environment.name, consumed: false },
658
+ (message) => ctx.warn(message)
659
+ );
660
+ const missing = [...atoms].filter((atom) => !held.includes(atom)).toSorted(compareText);
661
+ if (missing.length > 0) ctx.warn(notInLastClient(ctx, context, missing));
662
+ }
663
+ function checkWaitingClient(ctx, context, atoms, file) {
664
+ const missing = [...atoms].filter((atom) => !file.emitted.includes(atom)).toSorted(compareText);
665
+ if (missing.length === 0) {
666
+ writeHandshake(context.cacheDir, { ...file, consumed: true, pending: [] }, (message) => {
667
+ ctx.warn(message);
668
+ });
669
+ return;
670
+ }
671
+ const pending = [.../* @__PURE__ */ new Set([...file.pending ?? [], ...withRestorers(new Set(atoms))])];
672
+ writeHandshake(context.cacheDir, { ...file, pending }, (message) => ctx.warn(message));
673
+ ctx.error(missingFromClient(ctx, context, missing));
674
+ }
675
+ function checkServerInvocation(ctx, context) {
676
+ if (context.cacheDir === "" || context.inProcess || context.state.emitted) return;
677
+ const atoms = new Set(recordsOf(context.state, ctx.environment.name).flatMap((r) => [...r.atoms]));
678
+ const file = readHandshake(context.cacheDir);
679
+ if (file?.writer !== "client") {
680
+ const emitted = [...withRestorers(/* @__PURE__ */ new Set([...atoms, ...file?.emitted ?? []]))];
681
+ writeHandshake(
682
+ context.cacheDir,
683
+ { emitted, writer: ctx.environment.name, consumed: false },
684
+ (message) => ctx.warn(message)
685
+ );
686
+ } else if (file.consumed) {
687
+ recordForNextClient(ctx, context, atoms, file.emitted);
688
+ } else {
689
+ checkWaitingClient(ctx, context, atoms, file);
690
+ }
515
691
  }
516
692
 
517
- // src/vite-state.ts
518
- var byConfig = /* @__PURE__ */ new WeakMap();
519
- var byBuild = /* @__PURE__ */ new WeakMap();
520
- function buildOf(config) {
521
- return config.command === "build" ? config.inlineConfig : void 0;
693
+ // src/vite-scan.ts
694
+ var decoder2 = new TextDecoder();
695
+ function isCssAsset(entry) {
696
+ return entry.type === "asset" && entry.fileName.toLowerCase().endsWith(".css");
522
697
  }
523
- function mapOf(registry2, owner) {
524
- const found = registry2.get(owner) ?? /* @__PURE__ */ new Map();
525
- registry2.set(owner, found);
526
- return found;
698
+ function textOf2(entry) {
699
+ const { source } = entry;
700
+ if (source === void 0) return "";
701
+ return typeof source === "string" ? source : decoder2.decode(source);
527
702
  }
528
- function freshState() {
703
+ function scanMessage(findings, assetCount) {
704
+ const noun = assetCount === 1 ? "asset" : "assets";
705
+ return [
706
+ "@nave survived into the built CSS:",
707
+ ...findings.map((finding) => formatFinding(finding)),
708
+ `Scanned ${assetCount} CSS ${noun} this build wrote.`
709
+ ].join("\n");
710
+ }
711
+ function scanAssets(bundle) {
712
+ const assets = Object.values(bundle).filter((entry) => isCssAsset(entry));
529
713
  return {
530
- started: false,
531
- modules: /* @__PURE__ */ new Map(),
532
- exposures: /* @__PURE__ */ new Map(),
533
- templateLinks: /* @__PURE__ */ new Map(),
534
- packages: /* @__PURE__ */ new Set(),
535
- declared: /* @__PURE__ */ new Map(),
536
- recognised: /* @__PURE__ */ new Map(),
537
- sheets: /* @__PURE__ */ new Map(),
538
- pages: /* @__PURE__ */ new Map(),
539
- emitted: void 0,
540
- externals: /* @__PURE__ */ new Set(),
541
- clientEnded: false,
542
- serverImports: [],
543
- client: void 0,
544
- expectedEnvironments: void 0,
545
- builtEnvironments: /* @__PURE__ */ new Set(),
546
- warned: /* @__PURE__ */ new Set(),
547
- served: /* @__PURE__ */ new Map(),
548
- ...freshReloads(),
549
- htmlRead: false,
550
- serverTransformed: false,
551
- directiveExpanded: false,
552
- markupJudged: false,
553
- warmedUp: false,
554
- requested: /* @__PURE__ */ new Set()
714
+ findings: assets.flatMap((asset) => findingsInText(asset.fileName, textOf2(asset))),
715
+ names: assets.map((asset) => asset.fileName)
555
716
  };
556
717
  }
557
- function stateFor(root, config) {
558
- const known = byConfig.get(config)?.get(root);
559
- if (known) return known;
560
- const build = buildOf(config);
561
- const shared = build && byBuild.get(build)?.get(root);
562
- const state = shared && !shared.started ? shared : freshState();
563
- mapOf(byConfig, config).set(root, state);
564
- if (build) mapOf(byBuild, build).set(root, state);
565
- return state;
566
- }
567
- function moduleKey(environment, id) {
568
- return `${environment}\0${id}`;
718
+
719
+ // src/vite-whole.ts
720
+ function stylesheetsLeftWhole(context, environment, chunk) {
721
+ const { state } = context;
722
+ const whole = /* @__PURE__ */ new Set();
723
+ if (state.firstWriter !== "server" || environment.config.consumer !== "server") return whole;
724
+ const earlier = [...state.writers].filter(([name, consumer]) => name !== environment.name && consumer === "server").map(([name]) => name);
725
+ const ids = Object.keys(chunk.modules);
726
+ for (const id of ids) {
727
+ if (earlier.every((name) => !state.sheets.has(moduleKey(name, id)))) continue;
728
+ whole.add(id);
729
+ state.whole.add(moduleKey(environment.name, id));
730
+ }
731
+ return whole;
569
732
  }
570
- function collectedAtoms(state, kept) {
571
- const atoms = new Set(kept);
572
- for (const record of state.modules.values()) for (const atom of record.atoms) atoms.add(atom);
573
- for (const page of state.pages.values()) for (const atom of page) atoms.add(atom);
574
- return atoms;
733
+ function withoutWhole(chunk, whole) {
734
+ if (whole.size === 0) return chunk;
735
+ return {
736
+ modules: Object.fromEntries(Object.entries(chunk.modules).filter(([id]) => !whole.has(id)))
737
+ };
575
738
  }
576
- function withRestorers(atoms) {
577
- if (atoms.has("srOnly")) atoms.add("srOnlyFocusable");
578
- return atoms;
739
+ function assetKey(environment, entry) {
740
+ return `${environment}|${(entry.names ?? []).join(",")}|${(entry.originalFileNames ?? []).join(",")}`;
579
741
  }
580
- function servedAtoms(state, kept) {
581
- return withRestorers(collectedAtoms(state, kept));
742
+ function noteWholeAssets(context, environment, bundle) {
743
+ const { state } = context;
744
+ if (state.whole.size === 0) return;
745
+ const chunks = Object.values(bundle).filter((entry) => entry.type === "chunk");
746
+ for (const entry of chunks) {
747
+ const ids = Object.keys(entry.modules ?? {});
748
+ if (ids.every((id) => !state.whole.has(moduleKey(environment.name, id)))) continue;
749
+ const files = entry.viteMetadata?.importedCss ?? /* @__PURE__ */ new Set();
750
+ for (const file of files) {
751
+ const asset = bundle[file];
752
+ if (asset) state.wholeAssets.add(assetKey(environment.name, asset));
753
+ }
754
+ }
582
755
  }
583
- function recordsOf(state, environment) {
584
- const prefix = `${environment}\0`;
585
- return [...state.modules].filter(([key]) => key.startsWith(prefix)).map(([, record]) => record);
756
+ function withoutWholeAssets(context, environment, bundle) {
757
+ const { wholeAssets } = context.state;
758
+ if (wholeAssets.size === 0) return { ...bundle };
759
+ return Object.fromEntries(
760
+ Object.entries(bundle).filter(
761
+ ([, entry]) => !(entry.type === "asset" && wholeAssets.has(assetKey(environment.name, entry)))
762
+ )
763
+ );
586
764
  }
587
765
 
588
- // src/vite-setup-link.ts
589
- function importsContaining(program, marker) {
590
- const sources = nodesAt(program, "body").map(
591
- (node) => node.type === "ImportDeclaration" ? staticStringOf(nodeAt(node, "source")) : void 0
766
+ // src/vite-bundle.ts
767
+ var reads = /* @__PURE__ */ new WeakMap();
768
+ function sourceText(entry) {
769
+ return typeof entry.source === "string" ? entry.source : new TextDecoder().decode(entry.source);
770
+ }
771
+ function hasLayerAsset(bundle) {
772
+ return Object.values(bundle).some(
773
+ (entry) => entry.type === "asset" && entry.fileName.toLowerCase().endsWith(".css") && /@layer\s+atomic\b/.test(sourceText(entry))
592
774
  );
593
- return sources.filter((source) => source?.includes(marker) === true);
594
775
  }
595
- async function resolveId(ctx, source, importer) {
596
- const resolved = await ctx.resolve?.(source, importer);
597
- return resolved?.id;
776
+ function didWrite(context, environment, step) {
777
+ const { state } = context;
778
+ if (reads.get(step.output)?.wrote === true || hasLayerAsset(step.bundle)) {
779
+ state.writers.set(environment.name, environment.config.consumer);
780
+ return true;
781
+ }
782
+ if (state.emittedBy === environment.name && environment.config.consumer !== "client") {
783
+ state.emitted = void 0;
784
+ state.emittedBy = void 0;
785
+ state.firstWriter = void 0;
786
+ state.fixed.delete(environment.name);
787
+ }
788
+ return false;
789
+ }
790
+ function accumulate(step, layer, isWriter) {
791
+ const { bundle, output } = step;
792
+ const before = reads.get(output) ?? {
793
+ layer: /* @__PURE__ */ new Map(),
794
+ findings: /* @__PURE__ */ new Map(),
795
+ assets: /* @__PURE__ */ new Set(),
796
+ wrote: false
797
+ };
798
+ reads.set(output, before);
799
+ before.wrote ||= isWriter;
800
+ if (layer) for (const [name, asset] of layer) before.layer.set(name, asset);
801
+ const scanned = scanAssets(bundle);
802
+ for (const finding of scanned.findings) before.findings.set(formatFinding(finding), finding);
803
+ for (const name of scanned.names) before.assets.add(name);
804
+ return before;
805
+ }
806
+ function readWritten(ctx, context, step) {
807
+ const { environment } = ctx;
808
+ if (environment === void 0 || context.options.atomic !== "used") return void 0;
809
+ if (!canWriteStylesheets(context, environment)) return void 0;
810
+ if (!didWrite(context, environment, step)) return void 0;
811
+ noteWholeAssets(context, environment, step.bundle);
812
+ const emitted = emittedSet(context, environment, (message) => {
813
+ ctx.warn(message);
814
+ });
815
+ return {
816
+ emitted,
817
+ layer: readLayers(withoutWholeAssets(context, environment, step.bundle), emitted)
818
+ };
819
+ }
820
+ function failOnMismatch(ctx, lines) {
821
+ if (lines.length > 0) ctx.error({ message: layerMessage(lines) });
598
822
  }
599
- async function linkTemplates(context, ctx, input, scriptId) {
600
- const environment = ctx.environment?.name ?? "client";
601
- for (const template of importsContaining(input.program, "type=template")) {
602
- const templateId = await resolveId(ctx, template, input.id);
603
- if (templateId !== void 0) {
604
- context.state.templateLinks.set(moduleKey(environment, templateId), scriptId);
605
- }
606
- }
823
+ function failOnFindings(ctx, read) {
824
+ if (read.findings.size === 0) return;
825
+ ctx.error({ message: scanMessage(read.findings.values().toArray(), read.assets.size) });
607
826
  }
608
- async function setupExposuresFor(context, ctx, input) {
609
- const environment = ctx.environment?.name ?? "client";
610
- const [script] = importsContaining(input.program, "type=script");
611
- const scriptId = script === void 0 ? void 0 : await resolveId(ctx, script, input.id);
612
- await linkTemplates(context, ctx, input, scriptId ?? input.id);
613
- if (!input.code.includes("$setup")) return void 0;
614
- const owner = scriptId ?? context.state.templateLinks.get(moduleKey(environment, input.id));
615
- if (owner === void 0) return void 0;
616
- await ctx.load?.({ id: owner });
617
- return context.state.exposures.get(moduleKey(environment, owner));
827
+ function raise(ctx, seen) {
828
+ reads.delete(seen.output);
829
+ if (seen.emitted) failOnMismatch(ctx, mismatchLines(seen.read.layer, seen.emitted));
830
+ failOnFindings(ctx, seen.read);
831
+ }
832
+ function checkBundle(ctx, context, step) {
833
+ const { bundle, options, pass } = step;
834
+ const output = typeof options === "object" && options !== null ? options : bundle;
835
+ const written = readWritten(ctx, context, { bundle, output });
836
+ const read = accumulate({ bundle, output }, written?.layer, written !== void 0);
837
+ if (pass === "post") raise(ctx, { emitted: written?.emitted, output, read });
618
838
  }
619
839
 
620
840
  // src/vite-unread-record.ts
@@ -681,1781 +901,1009 @@ function textRecord(context, input) {
681
901
  problems: [],
682
902
  suppressed: [],
683
903
  dynamicCalls: [],
684
- pkg,
685
- file: moduleLabel(context.root, id)
686
- };
687
- }
688
-
689
- // src/vite-used.ts
690
- function createUsedContext(options, extend) {
691
- return {
692
- options,
693
- extend,
694
- kept: keptAtoms(options),
695
- packageNames: /* @__PURE__ */ new Map(),
696
- root: process.cwd(),
697
- command: "build",
698
- cacheDir: "",
699
- buildSourcemap: false,
700
- inProcess: false,
701
- isLibrary: false,
702
- logger: void 0,
703
- state: stateFor("", {})
704
- };
705
- }
706
- function configureUsed(context, config) {
707
- context.root = config.root;
708
- context.command = config.command;
709
- context.cacheDir = config.cacheDir ?? "";
710
- context.buildSourcemap = Boolean(config.build?.sourcemap);
711
- context.inProcess = config.builder !== void 0;
712
- context.isLibrary = Boolean(config.build?.lib);
713
- context.logger = config.logger;
714
- context.state = stateFor(config.root, config);
715
- }
716
- function isUsed(context) {
717
- return context.options.atomic === "used";
718
- }
719
- function ignoreWatch() {
720
- }
721
- async function ownAtomNames(context, watch = ignoreWatch) {
722
- try {
723
- const atoms = await context.extend.current(watch);
724
- return new Set(Object.keys(atoms).filter((name) => atoms[name]));
725
- } catch {
726
- return /* @__PURE__ */ new Set();
727
- }
728
- }
729
-
730
- // src/vite-collect-module.ts
731
- var ESCAPED_SPECIFIER = /(?:\bfrom|\bimport)[\s(]*(?:'[^'\\\n]*\\|"[^"\\\n]*\\|`[^`\\\n]*\\)/;
732
- function isMentioningAtoms(code) {
733
- return code.includes(CX_SOURCE) || code.includes("nave-") || code.includes("$setup") || code.includes("vue&type=") || ESCAPED_SPECIFIER.test(code);
734
- }
735
- var NO_PLACE = { line: 0, column: 0 };
736
- function isInlineScript(id) {
737
- return /[?&]html-proxy\b/.test(id);
738
- }
739
- function lineUnknownNote(context, id) {
740
- const isBuildWithoutMaps = context.command === "build" && !context.buildSourcemap;
741
- return isBuildWithoutMaps && !isInlineScript(id) ? LINE_UNKNOWN_WITHOUT_MAP : LINE_UNKNOWN_UNMAPPED;
742
- }
743
- function locate(problems, placing) {
744
- return problems.map((problem) => {
745
- const place = placing.place(problem.offset);
746
- const where = place ?? { ...NO_PLACE, unknownLine: placing.unknownLine };
747
- return { ...problem, file: placing.file, pkg: placing.pkg, moduleId: placing.id, ...where };
748
- });
749
- }
750
- function locateDynamic(reading, placing) {
751
- return reading.dynamicCalls.map(({ offset, construct }) => {
752
- const place = placing.place(offset);
753
- return {
754
- file: placing.file,
755
- construct,
756
- ...place ?? { ...NO_PLACE, unknownLine: placing.unknownLine }
757
- };
758
- });
759
- }
760
- var COMPILED_LANGS = /* @__PURE__ */ new Map([
761
- ["jsx", "jsx"],
762
- ["ts", "ts"],
763
- ["tsx", "tsx"]
764
- ]);
765
- function parseOrUndefined(ctx, code, moduleType) {
766
- try {
767
- const lang = moduleType === void 0 ? void 0 : COMPILED_LANGS.get(moduleType);
768
- return ctx.parse(code, lang && { lang });
769
- } catch {
770
- return void 0;
771
- }
772
- }
773
- async function authoredTextOf(id) {
774
- try {
775
- return await readFile(filePathOf(id), "utf8");
776
- } catch {
777
- return void 0;
778
- }
779
- }
780
- async function recordOf(context, ctx, input, reading) {
781
- const { code, id, pkg } = input;
782
- const hasPlaces = reading.problems.length + reading.dynamicCalls.length > 0;
783
- const map = hasPlaces ? combinedMapOf(ctx) : void 0;
784
- const authored = map ? await authoredTextOf(id) : void 0;
785
- const placing = {
786
- code,
787
- id,
788
- file: moduleLabel(context.root, id),
789
- pkg,
790
- place: placerFor(code, map, authored),
791
- unknownLine: lineUnknownNote(context, id)
792
- };
793
- const isStandingIn = isListed(context, pkg);
794
- return {
795
- atoms: /* @__PURE__ */ new Set([...reading.atoms, ...reading.classes]),
796
- problems: isStandingIn ? [] : locate(reading.problems, placing),
797
- suppressed: isStandingIn ? locate(reading.problems, placing) : [],
798
- dynamicCalls: locateDynamic(reading, placing),
799
- pkg,
800
- file: placing.file,
801
- ...reading.edits.length > 0 && { edits: reading.edits }
802
- };
803
- }
804
- function readOrUndefined(code, program, options) {
805
- try {
806
- return readModule(code, program, options);
807
- } catch (error) {
808
- if (error instanceof RangeError) return void 0;
809
- throw error;
810
- }
811
- }
812
- async function recordParsed(context, ctx, input, key) {
813
- const { code, id, pkg, program, declared } = input;
814
- const { declaredSources, isDeclared, onlyVia } = await declaredReadingOf(context, ctx, declared, {
815
- environment: ctx.environment?.name ?? "client",
816
- module: { code, id, program }
817
- });
818
- const reading = readOrUndefined(code, program, {
819
- cxSources: /* @__PURE__ */ new Set([CX_SOURCE, ...declaredSources]),
820
- declaredSources,
821
- isDeclared,
822
- ownAtoms: await ownAtomNames(context, (file) => {
823
- ctx.addWatchFile(file);
824
- }),
825
- isDependency: isDependencyId(id),
826
- isVueScript: /\.vue(?:$|\?)/.test(id),
827
- setup: await setupExposuresFor(context, ctx, { program, code, id }),
828
- // A build turns each call into the string it returns; the dev server keeps the functions.
829
- ...context.command === "build" && { rewrite: { keep: keepMap(context.kept) } }
830
- });
831
- if (!reading) {
832
- context.state.exposures.delete(key);
833
- return unreadableRecord(context, { code, id, pkg }, TOO_DEEP);
834
- }
835
- if (reading.exposes.size > 0) context.state.exposures.set(key, reading.exposes);
836
- else context.state.exposures.delete(key);
837
- if (pkg !== void 0 && reading.usesCx) context.state.packages.add(pkg);
838
- const record = await recordOf(context, ctx, { code, id, pkg }, reading);
839
- return onlyVia === void 0 ? record : { ...record, onlyVia };
840
- }
841
- async function recordModule(context, ctx, module) {
842
- const { code, id, moduleType } = module;
843
- const environment = ctx.environment?.name ?? "client";
844
- const key = moduleKey(environment, id);
845
- const declared = await declaredModulesFor(context, ctx, environment);
846
- if (!isMentioningAtoms(code) && !isAboutListed({ code, id }, declared)) {
847
- context.state.modules.delete(key);
848
- return void 0;
849
- }
850
- const isDependency = isDependencyId(id);
851
- const pkg = isDependency ? await packageNameOf(filePathOf(id), context.packageNames) : void 0;
852
- if (moduleType !== void 0 && !SOURCE_TYPES.has(moduleType)) {
853
- context.state.exposures.delete(key);
854
- const record2 = textRecord(context, { code, id, moduleType, pkg });
855
- context.state.modules.set(key, record2);
856
- return record2;
857
- }
858
- const program = parseOrUndefined(ctx, code, moduleType);
859
- const record = program ? await recordParsed(context, ctx, { code, id, pkg, program, declared }, key) : unreadableRecord(context, { code, id, pkg }, NOT_PARSED);
860
- if (!program) context.state.exposures.delete(key);
861
- context.state.modules.set(key, record);
862
- return record;
863
- }
864
-
865
- // src/vite-collect-problems.ts
866
- function listedAtomsOf(record, context) {
867
- if (record.pkg === void 0) return context.options.keep;
868
- const { keepFor } = context.options;
869
- return Object.hasOwn(keepFor, record.pkg) ? keepFor[record.pkg] : [];
870
- }
871
- function dynamicProblems(record, context) {
872
- const isCovered = listedAtomsOf(record, context).length > 0;
873
- if (isCovered) return [];
874
- return record.dynamicCalls.map((call) => ({
875
- kind: "dynamic",
876
- offset: 0,
877
- construct: call.construct,
878
- text: "",
879
- file: call.file,
880
- line: call.line,
881
- column: call.column,
882
- pkg: record.pkg,
883
- ...call.unknownLine !== void 0 && { unknownLine: call.unknownLine }
884
- }));
885
- }
886
- function problemsOf(record, context) {
887
- return [...record.problems, ...dynamicProblems(record, context)];
888
- }
889
- function failModule(ctx, context, input) {
890
- const { home, id, problems } = input;
891
- const message = moduleReport(problems, context.options.cxModules, home);
892
- const first = problems[0];
893
- return ctx.error({
894
- message,
895
- id,
896
- loc: { file: id, line: Math.max(first.line, 1), column: Math.max(first.column - 1, 0) }
897
- });
898
- }
899
- function failDevModule(ctx, context, input) {
900
- const { home, id, record } = input;
901
- if (context.command !== "serve" || record.pkg !== void 0) return;
902
- const problems = problemsOf(record, context);
903
- if (problems.length > 0) failModule(ctx, context, { home, id, problems });
904
- }
905
-
906
- // src/vite-cx-module-lines.ts
907
- function quoted(specifiers) {
908
- return specifiers.map((specifier) => `'${specifier}'`).join(", ");
909
- }
910
- function applicationLinesOf(item) {
911
- const entry = `'${item.entry}'`;
912
- const relative = item.specifiers.filter((specifier) => isRelative(specifier));
913
- const other = item.specifiers.filter((specifier) => !isRelative(specifier));
914
- const place = relative.length === 1 ? "its" : "their";
915
- return {
916
- relative: relative.length === 0 ? [] : [
917
- `${item.label}: imports ${entry} as ${quoted(relative)}. Write '${item.replacement}' in ${place} place.`
918
- ],
919
- other: other.length === 0 ? [] : [`${item.label}: imports ${entry} as ${quoted(other)}.`],
920
- none: item.specifiers.length > 0 ? [] : [
921
- `${item.label}: imports ${entry} through a specifier the build could not find in the file.`
922
- ]
904
+ pkg,
905
+ file: moduleLabel(context.root, id)
923
906
  };
924
907
  }
925
- function dependencyLineOf(item) {
926
- const entry = `'${item.entry}'`;
927
- const found = item.specifiers.length === 0 ? `imports ${entry} through a specifier the build could not find in the file.` : `imports ${entry} as ${quoted(item.specifiers)}.`;
928
- return `${item.label}: ${found} The file is in ${item.pkg}, a dependency, so it is not yours to change.`;
908
+
909
+ // src/vite-css-module-record.ts
910
+ function stringOf(node) {
911
+ return node?.type === "Literal" && typeof node.value === "string" ? node.value : void 0;
929
912
  }
930
- var RELATIVE_REMEDY = "Adding a relative specifier to cxModules would not clear this: a relative entry is read from the project root. Instead of the rewrite";
931
- var TYPESCRIPT_CONDITION = "Where a rewrite ends in .ts, .tsx, .mts or .cts, TypeScript accepts it only if your tsconfig sets allowImportingTsExtensions or rewriteRelativeImportExtensions; if it sets neither, use the alias the line above describes instead.";
932
- var NO_SPECIFIER_REMEDY = "Where the build could not find the specifier, import the module there by a specifier ending in the listed file's name, or by an alias or package name added to cxModules.";
933
- function applicationRemedyOf(items, lines, input) {
934
- const remedy = [];
935
- const relativeCount = lines.reduce((count, line) => count + line.relative.length, 0);
936
- if (relativeCount === 1) {
937
- remedy.push(
938
- `${RELATIVE_REMEDY} its line gives, the file can import the module through an alias for it, added to cxModules.`
939
- );
940
- } else if (relativeCount > 1) {
941
- remedy.push(
942
- `${RELATIVE_REMEDY} each line gives, a file can import the module through an alias for it, added to cxModules.`
943
- );
944
- }
945
- const hasTypeScriptRewrite = items.some(
946
- (item) => item.specifiers.some((specifier) => isRelative(specifier)) && /\.(?:ts|tsx|mts|cts)$/.test(item.replacement)
913
+ function namedStrings(declaration) {
914
+ return nodesAt(declaration, "declarations").map(
915
+ (declarator) => stringOf(nodeAt(declarator, "init")) ?? ""
947
916
  );
948
- if (hasTypeScriptRewrite) remedy.push(TYPESCRIPT_CONDITION);
949
- if (lines.some((line) => line.none.length > 0)) remedy.push(NO_SPECIFIER_REMEDY);
950
- const added = [
951
- ...new Set(
952
- items.flatMap((item) => item.specifiers.filter((specifier) => !isRelative(specifier)))
953
- )
954
- ];
955
- const array = cxModulesArray(input.configured, added);
956
- if (array !== void 0) {
957
- const each = added.length === 1 ? "the specifier" : "each specifier";
958
- remedy.push(`Add ${each}, as written, to cxModules in ${input.home}: cxModules: ${array}.`);
959
- }
960
- return remedy;
961
917
  }
962
- var LASTING_FIX_HERE = "The lasting fix there is the package's: import the module by a specifier ending in the listed file's name.";
963
- function packageRemedyOf(pkg, files, input) {
964
- if (input.hasKeepFor) {
965
- const file = files === 1 ? "a file" : "files";
966
- return `${pkg} also imports a module listed in cxModules in ${file} the build did not read; the keepFor entry above clears that too. ${LASTING_FIX_HERE}`;
967
- }
968
- return `For ${pkg}, list the atoms its calls can produce, from its documentation, under its name in ${input.home}: keepFor: { '${pkg}': ['<atom>'] }. Once the package is listed, its files no longer fail the build here, and the atoms you list stand in for the calls the build did not read. The lasting fix is the package's: import the module by a specifier ending in the listed file's name.`;
918
+ function defaultStrings(declaration) {
919
+ return nodesAt(declaration, "properties").map(
920
+ (property) => stringOf(nodeAt(property, "value")) ?? ""
921
+ );
969
922
  }
970
- function importerErrorOf(found, input) {
971
- const application = found.filter((item) => item.pkg === void 0);
972
- const dependencies = found.filter((item) => item.pkg !== void 0);
973
- const count = new Set(found.map((item) => item.importer)).size;
974
- const first = count === 1 ? "1 file imports a module listed in cxModules through a specifier the build does not recognise, so the build did not read the cx() calls made through it." : `${count} files import a module listed in cxModules through a specifier the build does not recognise, so the build did not read the cx() calls made through those specifiers.`;
975
- const lines = application.map((item) => applicationLinesOf(item));
976
- const byPackage = Map.groupBy(dependencies, (item) => item.pkg);
977
- const packages = byPackage.keys().toArray().toSorted(compareText);
978
- return [
979
- first,
980
- ...lines.flatMap((line) => [...line.relative, ...line.other, ...line.none]).toSorted(compareText),
981
- ...dependencies.map((item) => dependencyLineOf(item)).toSorted(compareText),
982
- ...applicationRemedyOf(application, lines, input),
983
- ...packages.map(
984
- (pkg) => packageRemedyOf(pkg, new Set(byPackage.get(pkg).map((item) => item.importer)).size, {
985
- hasKeepFor: input.report?.includes(`keepFor: { '${pkg}'`) ?? false,
986
- home: input.home
987
- })
988
- )
989
- ].join("\n");
923
+ function exportedStrings(program) {
924
+ const strings = nodesAt(program, "body").flatMap((statement) => {
925
+ const declaration = nodeAt(statement, "declaration");
926
+ if (declaration === void 0) return [];
927
+ if (statement.type === "ExportNamedDeclaration" && declaration.type === "VariableDeclaration") {
928
+ return namedStrings(declaration);
929
+ }
930
+ if (statement.type === "ExportDefaultDeclaration" && declaration.type === "ObjectExpression") {
931
+ return defaultStrings(declaration);
932
+ }
933
+ return [];
934
+ });
935
+ return strings.filter((text) => text !== "");
990
936
  }
991
- async function isResolvingTo(ctx, input) {
937
+ function cssModuleRecord(context, ctx, input) {
938
+ const { code, id, pkg } = input;
939
+ let program;
992
940
  try {
993
- const resolved = await ctx.resolve?.(input.specifier, input.from);
994
- return resolved !== null && resolved !== void 0 && fileOf(resolved.id) === input.file;
941
+ program = ctx.parse(code);
995
942
  } catch {
996
- return false;
997
- }
998
- }
999
- function importersOf(ctx, id) {
1000
- const info = ctx.getModuleInfo?.(id);
1001
- return [.../* @__PURE__ */ new Set([...info?.importers ?? [], ...info?.dynamicImporters ?? []])];
1002
- }
1003
- async function specifiersWritten(ctx, input) {
1004
- const { file, importer } = input;
1005
- const code = ctx.getModuleInfo?.(importer)?.code;
1006
- if (code === null || code === void 0) return [];
1007
- const written = specifiersOf(ctx, { id: importer, code }) ?? [];
1008
- const found = [];
1009
- for (const specifier of written) {
1010
- if (await isResolvingTo(ctx, { file, from: importer, specifier })) found.push(specifier);
1011
- }
1012
- return found;
1013
- }
1014
- async function specifiersInto(ctx, context, input) {
1015
- const found = /* @__PURE__ */ new Set();
1016
- for (const importer of importersOf(ctx, input.id)) {
1017
- const owner = isDependencyId(importer) ? await packageNameOf(filePathOf(importer), context.packageNames) : void 0;
1018
- if (owner === input.pkg) continue;
1019
- const written = await specifiersWritten(ctx, { file: fileOf(input.id), importer });
1020
- for (const specifier of written) found.add(specifier);
943
+ const unreadable = unreadableRecord(context, { code: "", id, pkg }, CLASS_MAP_NOT_PARSED);
944
+ return {
945
+ ...unreadable,
946
+ problems: [...unreadable.problems, ...unreadable.suppressed],
947
+ suppressed: []
948
+ };
1021
949
  }
1022
- return found.values().toArray().toSorted(compareText);
950
+ const strings = isNode(program) ? exportedStrings(program) : [];
951
+ return textRecord(context, { code: strings.join("\n"), id, pkg });
1023
952
  }
1024
- async function clearingSpecifier(ctx, context, input) {
1025
- const from = path2.join(context.root, "index.html");
1026
- const file = fileOf(input.id);
1027
- if (await isResolvingTo(ctx, { file, from, specifier: input.pkg })) return input.pkg;
1028
- const specifiers = await specifiersInto(ctx, context, input);
1029
- for (const specifier of specifiers) {
1030
- if (await isResolvingTo(ctx, { file, from, specifier })) return specifier;
1031
- }
953
+ function recordWithoutCalls(context, ctx, module) {
954
+ if (isCssModuleId(module.id)) return cssModuleRecord(context, ctx, module);
955
+ const { moduleType } = module;
956
+ if (moduleType !== void 0 && !SOURCE_TYPES.has(moduleType)) return textRecord(context, module);
1032
957
  return void 0;
1033
958
  }
1034
- async function withClearingSpecifiers(ctx, context, problems) {
1035
- const asked = /* @__PURE__ */ new Map();
1036
- const clearing = (pkg, id) => {
1037
- const known = asked.get(id);
1038
- if (known) return known;
1039
- const answer = clearingSpecifier(ctx, context, { id, pkg });
1040
- asked.set(id, answer);
1041
- return answer;
1042
- };
1043
- const entriesOf = async (id) => {
1044
- const declared = await declaredModulesFor(context, ctx, ctx.environment.name);
1045
- return declared ? entriesNaming(declared, id) : [];
1046
- };
1047
- const renamed = new Set(
1048
- problems.flatMap(
1049
- (problem) => problem.isRenamed === true && problem.moduleId !== void 0 ? [problem.moduleId] : []
1050
- )
1051
- );
1052
- return await Promise.all(
1053
- problems.map(async (problem) => {
1054
- const { moduleId, pkg } = problem;
1055
- if (pkg === void 0 || moduleId === void 0) return problem;
1056
- if (problem.kind === "declared") return { ...problem, entries: await entriesOf(moduleId) };
1057
- if (!isListableReexport(problem)) return problem;
1058
- if (renamed.has(moduleId)) return { ...problem, isRenamed: true };
1059
- const specifier = await clearing(pkg, moduleId);
1060
- return specifier === void 0 ? problem : { ...problem, specifier };
1061
- })
1062
- );
1063
- }
1064
959
 
1065
- // src/vite-cx-module-importers.ts
1066
- var RESOLVED_EXTENSIONS = /* @__PURE__ */ new Set([
1067
- ".js",
1068
- ".json",
1069
- ".jsx",
1070
- ".mjs",
1071
- ".mts",
1072
- ".ts",
1073
- ".tsx"
1074
- ]);
1075
- function entryOf(declared, id) {
1076
- const file = fileOf(id);
1077
- for (const [entry, resolved] of declared.ids) if (fileOf(resolved) === file) return entry;
1078
- return id;
1079
- }
1080
- async function replacementFor(ctx, input) {
1081
- const file = fileOf(input.id);
1082
- const extension = path2.posix.extname(file);
1083
- const relativeTo = (target) => {
1084
- const relative = path2.posix.relative(path2.posix.dirname(fileOf(input.importer)), target);
1085
- return relative.startsWith("../") ? relative : `./${relative}`;
1086
- };
1087
- if (!RESOLVED_EXTENSIONS.has(extension)) return relativeTo(file);
1088
- const short = relativeTo(file.slice(0, -extension.length));
1089
- const isLanding = await isResolvingTo(ctx, { file, from: input.importer, specifier: short });
1090
- return isLanding ? short : relativeTo(file);
1091
- }
1092
- async function unrecognisedSpecifiers(ctx, input) {
1093
- const { answers, id, importer, recognised } = input;
1094
- const known = { cxId: id, answers };
1095
- const found = [];
1096
- const unread = (specifiersOf(ctx, importer) ?? []).filter((text) => !recognised?.has(text));
1097
- for (const specifier of unread) {
1098
- if (await isResolvingToCx(ctx, specifier, importer, known)) found.push(specifier);
960
+ // src/vite-cx-reading.ts
961
+ async function declaredReadingOf(context, resolver, declared, input) {
962
+ const { environment, module } = input;
963
+ const declaredSources = /* @__PURE__ */ new Set();
964
+ if (!declared) return { declaredSources, isDeclared: false, onlyVia: void 0 };
965
+ const files = /* @__PURE__ */ new Set();
966
+ let isOnlyListed = true;
967
+ for (const source of specifiersIn(module.program)) {
968
+ if (source === CX_SOURCE) {
969
+ isOnlyListed = false;
970
+ continue;
971
+ }
972
+ const found = await listedFileOf(resolver, declared, source, module.id);
973
+ if (!found) continue;
974
+ declaredSources.add(source);
975
+ if (found.file === void 0) {
976
+ isOnlyListed = false;
977
+ continue;
978
+ }
979
+ files.add(found.file);
980
+ const key = recognisedKey(environment, found.file, module.id);
981
+ context.state.recognised.set(
982
+ key,
983
+ /* @__PURE__ */ new Set([...context.state.recognised.get(key) ?? [], source])
984
+ );
1099
985
  }
1100
- return found;
1101
- }
1102
- async function unrecognisedOf(ctx, context, input) {
1103
- const { answers, declared, id } = input;
1104
- const importer = await candidateOf(ctx, context, input.importer);
1105
- if (!importer) return void 0;
1106
- const recognised = context.state.recognised.get(
1107
- recognisedKey(ctx.environment.name, fileOf(id), importer.id)
1108
- );
1109
- const specifiers = await unrecognisedSpecifiers(ctx, { answers, id, importer, recognised });
1110
- if (recognised !== void 0 && specifiers.length === 0) return void 0;
1111
- const pkg = isDependencyId(importer.id) ? await packageNameOf(filePathOf(importer.id), context.packageNames) : void 0;
1112
986
  return {
1113
- importer: importer.id,
1114
- label: moduleLabel(context.root, importer.id),
1115
- entry: entryOf(declared, id),
1116
- specifiers,
1117
- replacement: pkg === void 0 ? await replacementFor(ctx, { id, importer: importer.id }) : "",
1118
- pkg
987
+ declaredSources,
988
+ isDeclared: isListedFile(declared, module.id),
989
+ onlyVia: isOnlyListed && files.size > 0 ? files : void 0
1119
990
  };
1120
991
  }
1121
- function listedIdsIn(ctx, declared) {
1122
- const ids = new Set(declared.ids.values());
1123
- const graph = ctx.getModuleIds?.() ?? [];
1124
- for (const id of graph) {
1125
- if (declared.files.has(fileOf(id))) ids.add(id);
992
+ async function listedFileFrom(resolver, declared, source, importer) {
993
+ const resolved = await resolveOrNull(resolver, source, importer);
994
+ const file = resolved ? fileOf(resolved.id) : void 0;
995
+ return file !== void 0 && declared.files.has(file) ? file : void 0;
996
+ }
997
+ async function listedFileOf(resolver, declared, source, importer) {
998
+ if (declared.entries.has(source)) {
999
+ const id = source.startsWith("#") ? void 0 : declared.ids.get(source);
1000
+ if (id !== void 0) return { file: fileOf(id) };
1001
+ const file2 = await listedFileFrom(resolver, declared, source, importer);
1002
+ return file2 === void 0 && source.startsWith("#") ? void 0 : { file: file2 };
1126
1003
  }
1127
- return ids;
1004
+ if (!isNamingListed(source, declared)) return void 0;
1005
+ const file = await listedFileFrom(resolver, declared, source, importer);
1006
+ return file === void 0 ? void 0 : { file };
1128
1007
  }
1129
- function merged(found, item, id) {
1130
- const key = `${item.importer}\0${fileOf(id)}`;
1131
- const specifiers = /* @__PURE__ */ new Set([...found.get(key)?.specifiers ?? [], ...item.specifiers]);
1132
- return [key, { ...item, specifiers: [...specifiers] }];
1008
+
1009
+ // src/vite-dev-keep.ts
1010
+ var KEEP_CONSTANT = "__NAVE_KEEP_CLASSES__";
1011
+ function keepMap(kept) {
1012
+ return Object.fromEntries(
1013
+ kept.filter((atom) => Object.hasOwn(atomClassMap, atom)).map((atom) => [atom, atomClassMap[atom]])
1014
+ );
1133
1015
  }
1134
- async function unrecognisedImporters(ctx, context, declared) {
1135
- const found = /* @__PURE__ */ new Map();
1136
- for (const id of listedIdsIn(ctx, declared)) {
1137
- const answers = /* @__PURE__ */ new Map();
1138
- for (const importer of importerIdsOf(ctx, id)) {
1139
- const item = await unrecognisedOf(ctx, context, { answers, declared, id, importer });
1140
- if (item) found.set(...merged(found, item, id));
1016
+ function devKeepMap(map, atoms) {
1017
+ return new Proxy(map, {
1018
+ getOwnPropertyDescriptor(target, key) {
1019
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
1020
+ if (descriptor === void 0 && typeof key === "string") {
1021
+ const quoted2 = JSON.stringify(key);
1022
+ console.error(
1023
+ atoms.includes(key) ? `[nave] cx.dynamic(${quoted2}) applied no class: ${quoted2} is not in keep or in a keepFor list, and navePlugin() gives cx.dynamic() only the atoms in those lists, in a build as in dev. Add ${quoted2} to keep in navePlugin(), or, when a dependency makes the call, to its list in keepFor.` : `[nave] cx.dynamic(${quoted2}) applied no class: ${quoted2} is not a Nave atom.`
1024
+ );
1025
+ }
1026
+ return descriptor;
1141
1027
  }
1142
- }
1143
- return found.values().toArray();
1028
+ });
1144
1029
  }
1145
- async function declaredImporterError(ctx, context, input) {
1146
- const declared = await declaredModulesFor(context, ctx, ctx.environment.name);
1147
- if (!declared) return void 0;
1148
- const found = await unrecognisedImporters(ctx, context, declared);
1149
- return found.length === 0 ? void 0 : importerErrorOf(found, input);
1030
+ function devKeepExpression(map, atoms) {
1031
+ return `(${devKeepMap.toString()})(${JSON.stringify(map)}, ${JSON.stringify(atoms)})`;
1150
1032
  }
1151
-
1152
- // src/vite-declared-echoes.ts
1153
- var ECHO_KINDS = /* @__PURE__ */ new Set([
1154
- "argument",
1155
- "dynamic",
1156
- "own",
1157
- "reference",
1158
- "unknown"
1159
- ]);
1160
- function withoutEchoes(modules) {
1161
- const broken = new Set(
1162
- modules.flatMap(
1163
- (module) => module.problems.flatMap(
1164
- (problem) => problem.kind === "declared" && problem.moduleId !== void 0 && !problem.hasNaveCx ? [fileOf(problem.moduleId)] : []
1165
- )
1166
- )
1167
- );
1168
- const isEcho = (module) => module.onlyVia !== void 0 && [...module.onlyVia].every((file) => broken.has(file));
1169
- return modules.flatMap(
1170
- (module) => isEcho(module) ? module.problems.filter((problem) => !ECHO_KINDS.has(problem.kind)) : module.problems
1171
- );
1033
+ var installs = [];
1034
+ var baseline = { held: false, value: void 0 };
1035
+ function publish() {
1036
+ const newest = installs.at(-1);
1037
+ if (newest !== void 0) Object.assign(globalThis, { [KEEP_CONSTANT]: newest.map });
1038
+ else if (baseline.held) Object.assign(globalThis, { [KEEP_CONSTANT]: baseline.value });
1039
+ else Reflect.deleteProperty(globalThis, KEEP_CONSTANT);
1040
+ }
1041
+ function installServerKeepMap(context, owner) {
1042
+ if (context.options.atomic !== "used") return;
1043
+ const map = devKeepMap(keepMap(context.kept), Object.keys(atomClassMap));
1044
+ if (installs.length === 0) {
1045
+ baseline.held = Object.hasOwn(globalThis, KEEP_CONSTANT);
1046
+ baseline.value = globalThis[KEEP_CONSTANT];
1047
+ }
1048
+ installs.push({ owner, map });
1049
+ publish();
1050
+ }
1051
+ function removeServerKeepMap(owner) {
1052
+ const index = installs.findIndex((install) => install.owner === owner);
1053
+ if (index === -1) return;
1054
+ installs.splice(index, 1);
1055
+ publish();
1172
1056
  }
1173
1057
 
1174
- // src/vite-markup.ts
1175
- var MARKUP_WARNING = `navePlugin(): Nave read no HTML page and no server render, so your pages are likely rendered where it cannot see them, such as in a backend's own templates. A Nave class written there has no rule, in dev or in the build, unless its atom is listed in keep; if most of your Nave classes are written there, set atomic: 'all' to ship every atom. See "Which atoms the build ships" in node_modules/@navecss/core/README.md.`;
1176
- function isMarkupCase(context) {
1177
- const { state } = context;
1178
- return context.options.keep.length === 0 && collectedAtoms(state, []).size === 0 && !state.directiveExpanded && !state.htmlRead && !state.serverTransformed && !context.isLibrary;
1058
+ // src/vite-serialised-css.ts
1059
+ var IDENTIFIER = /^[\w-]+/;
1060
+ var SELECTOR_REST = /^(?:[^{};'"`\\]|\\[nrt])*\{/;
1061
+ function isSerialisedStylesheet(code) {
1062
+ let mentions = 0;
1063
+ for (let at = code.indexOf("nave-"); at !== -1; at = code.indexOf("nave-", at + 5)) {
1064
+ const before = code[at - 1];
1065
+ if (before !== void 0 && /[\w-]/.test(before)) continue;
1066
+ const name = IDENTIFIER.exec(code.slice(at, at + 64))?.[0];
1067
+ if (name === void 0 || atomOfClass(name) === void 0) continue;
1068
+ mentions += 1;
1069
+ if (before !== "." || !SELECTOR_REST.test(code.slice(at + name.length, at + name.length + 600)))
1070
+ return false;
1071
+ }
1072
+ return mentions > 0;
1179
1073
  }
1180
- function judgeMarkup(context, warn) {
1181
- const { state } = context;
1182
- if (!isUsed(context) || state.markupJudged) return;
1183
- state.markupJudged = true;
1184
- if (isMarkupCase(context)) warn(MARKUP_WARNING);
1074
+
1075
+ // src/vite-setup-link.ts
1076
+ function importsContaining(program, marker) {
1077
+ const sources = nodesAt(program, "body").map(
1078
+ (node) => node.type === "ImportDeclaration" ? staticStringOf(nodeAt(node, "source")) : void 0
1079
+ );
1080
+ return sources.filter((source) => source?.includes(marker) === true);
1185
1081
  }
1186
- function expectBuilds(context, builder, warn) {
1187
- if (Object.values(builder.environments).some((environment) => environment.isBuilt)) {
1188
- judgeMarkup(context, warn);
1189
- return;
1082
+ async function resolveId(ctx, source, importer) {
1083
+ const resolved = await ctx.resolve?.(source, importer);
1084
+ return resolved?.id;
1085
+ }
1086
+ async function linkTemplates(context, ctx, input, scriptId) {
1087
+ const environment = ctx.environment?.name ?? "client";
1088
+ for (const template of importsContaining(input.program, "type=template")) {
1089
+ const templateId = await resolveId(ctx, template, input.id);
1090
+ if (templateId !== void 0) {
1091
+ context.state.templateLinks.set(moduleKey(environment, templateId), scriptId);
1092
+ }
1190
1093
  }
1191
- context.state.expectedEnvironments = new Set(Object.keys(builder.environments));
1192
1094
  }
1193
- function judgeAfterLastEnvironment(context, environment, warn) {
1194
- const { builtEnvironments, expectedEnvironments } = context.state;
1195
- builtEnvironments.add(environment.name);
1196
- const isLast = expectedEnvironments ? [...expectedEnvironments].every((name) => builtEnvironments.has(name)) : !context.inProcess && environment.config.consumer === "client";
1197
- if (isLast) judgeMarkup(context, warn);
1095
+ async function setupExposuresFor(context, ctx, input) {
1096
+ const environment = ctx.environment?.name ?? "client";
1097
+ const [script] = importsContaining(input.program, "type=script");
1098
+ const scriptId = script === void 0 ? void 0 : await resolveId(ctx, script, input.id);
1099
+ await linkTemplates(context, ctx, input, scriptId ?? input.id);
1100
+ if (!input.code.includes("$setup")) return void 0;
1101
+ const owner = scriptId ?? context.state.templateLinks.get(moduleKey(environment, input.id));
1102
+ if (owner === void 0) return void 0;
1103
+ await ctx.load?.({ id: owner });
1104
+ return context.state.exposures.get(moduleKey(environment, owner));
1198
1105
  }
1199
1106
 
1200
- // src/vite-dev-judge.ts
1201
- function isServedAsStylesheet(id) {
1202
- return isStylesheetId(id) && !/[?&]inline\b/.test(id);
1107
+ // src/vite-used.ts
1108
+ function createUsedContext(options, extend) {
1109
+ return {
1110
+ options,
1111
+ extend,
1112
+ kept: keptAtoms(options),
1113
+ packageNames: /* @__PURE__ */ new Map(),
1114
+ root: process.cwd(),
1115
+ command: "build",
1116
+ cacheDir: "",
1117
+ buildSourcemap: false,
1118
+ inProcess: false,
1119
+ isLibrary: false,
1120
+ logger: void 0,
1121
+ state: stateFor("", {})
1122
+ };
1203
1123
  }
1204
- function judge(context) {
1205
- judgeMarkup(context, (message) => context.logger?.warn(message));
1124
+ function configureUsed(context, config) {
1125
+ context.root = config.root;
1126
+ context.command = config.command;
1127
+ context.cacheDir = config.cacheDir ?? "";
1128
+ context.buildSourcemap = Boolean(config.build?.sourcemap);
1129
+ context.inProcess = config.builder !== void 0;
1130
+ context.isLibrary = Boolean(config.build?.lib);
1131
+ context.logger = config.logger;
1132
+ context.state = stateFor(config.root, config);
1206
1133
  }
1207
- function judgeWhenServed(context, id) {
1208
- const { state } = context;
1209
- if (!state.warmedUp || state.requested.has(filePathOf(id))) judge(context);
1134
+ function isUsed(context) {
1135
+ return context.options.atomic === "used";
1210
1136
  }
1211
- function stylesheetUrl(request, base) {
1212
- const url = request.url;
1213
- if (url === void 0 || !isServedAsStylesheet(url)) return void 0;
1214
- return base !== "/" && url.startsWith(base) ? `/${url.slice(base.length)}` : url;
1137
+ function ignoreWatch() {
1215
1138
  }
1216
- async function noteRequest(context, environment, url) {
1217
- const { state } = context;
1139
+ async function ownAtomNames(context, watch = ignoreWatch) {
1218
1140
  try {
1219
- const resolved = await environment.pluginContainer.resolveId(decodeURI(url));
1220
- if (resolved === null) return;
1221
- const file = filePathOf(resolved.id);
1222
- state.requested.add(file);
1223
- if (state.served.keys().some((id) => filePathOf(id) === file)) judge(context);
1141
+ const atoms = await context.extend.current(watch);
1142
+ return new Set(Object.keys(atoms).filter((name) => atoms[name]));
1224
1143
  } catch {
1144
+ return /* @__PURE__ */ new Set();
1225
1145
  }
1226
1146
  }
1227
- function noteStylesheetRequests(context, server) {
1228
- const { state } = context;
1229
- const environment = server.environments.client;
1230
- state.warmedUp = environment.config.dev.warmup.length > 0;
1231
- if (!state.warmedUp) return;
1232
- server.middlewares.use(async (request, _response, next) => {
1233
- const url = stylesheetUrl(request, server.config.base);
1234
- if (url !== void 0 && !state.markupJudged) await noteRequest(context, environment, url);
1235
- next();
1236
- });
1237
- }
1238
1147
 
1239
- // src/vite-dev-read.ts
1240
- function graphRoots(environment) {
1241
- return environment.moduleGraph.idToModuleMap.values().filter(
1242
- (module) => module.importers.size === 0 && (module.id === null || !isStylesheetId(module.id))
1243
- ).toArray();
1148
+ // src/vite-collect-module.ts
1149
+ var ESCAPED_SPECIFIER = /(?:\bfrom|\bimport)[\s(]*(?:'[^'\\\n]*\\|"[^"\\\n]*\\|`[^`\\\n]*\\)/;
1150
+ function isMentioningAtoms(code) {
1151
+ if (code.includes(CX_SOURCE) || ESCAPED_SPECIFIER.test(code)) return true;
1152
+ if (isSerialisedStylesheet(code)) return false;
1153
+ return ["nave-", "&#", "$setup", "vue&type="].some((mention) => code.includes(mention));
1244
1154
  }
1245
- async function transformQuietly(environment, url) {
1246
- try {
1247
- await environment.transformRequest(url);
1248
- } catch {
1249
- }
1155
+ var NO_PLACE = { line: 0, column: 0 };
1156
+ function isInlineScript(id) {
1157
+ return /[?&]html-proxy\b/.test(id);
1250
1158
  }
1251
- async function transformReachable(environment, roots) {
1252
- const seen = new Set(roots);
1253
- let level = [...roots];
1254
- while (level.length > 0) {
1255
- const next = [];
1256
- await Promise.all(
1257
- level.map(async (module) => {
1258
- if (module.id !== null && isStylesheetId(module.id)) return;
1259
- await transformQuietly(environment, module.url);
1260
- for (const imported of module.importedModules) {
1261
- if (seen.has(imported)) continue;
1262
- seen.add(imported);
1263
- next.push(imported);
1264
- }
1265
- })
1266
- );
1267
- level = next;
1268
- }
1159
+ function lineUnknownNote(context, id) {
1160
+ const isBuildWithoutMaps = context.command === "build" && !context.buildSourcemap;
1161
+ return isBuildWithoutMaps && !isInlineScript(id) ? LINE_UNKNOWN_WITHOUT_MAP : LINE_UNKNOWN_UNMAPPED;
1269
1162
  }
1270
-
1271
- // src/vite-dev-update.ts
1272
- function readerOf(environment) {
1273
- return {
1274
- transformRequest: (url) => environment.transformRequest?.(url) ?? Promise.resolve()
1275
- };
1163
+ function locate2(problems, placing) {
1164
+ return problems.map((problem) => {
1165
+ const place = placing.place(problem.offset);
1166
+ const where = place ?? { ...NO_PLACE, unknownLine: placing.unknownLine };
1167
+ return { ...problem, file: placing.file, pkg: placing.pkg, moduleId: placing.id, ...where };
1168
+ });
1276
1169
  }
1277
- async function readAhead(context, environment, scripts) {
1278
- const { state } = context;
1279
- const reader = readerOf(environment);
1280
- state.readAhead += 1;
1170
+ function locateDynamic(reading, placing) {
1171
+ return reading.dynamicCalls.map(({ offset, construct }) => {
1172
+ const place = placing.place(offset);
1173
+ return {
1174
+ file: placing.file,
1175
+ construct,
1176
+ ...place ?? { ...NO_PLACE, unknownLine: placing.unknownLine }
1177
+ };
1178
+ });
1179
+ }
1180
+ var COMPILED_LANGS = /* @__PURE__ */ new Map([
1181
+ ["jsx", "jsx"],
1182
+ ["ts", "ts"],
1183
+ ["tsx", "tsx"]
1184
+ ]);
1185
+ function parseOrUndefined(ctx, code, moduleType) {
1281
1186
  try {
1282
- for (const module of scripts) {
1283
- const known = new Set(module.importedModules);
1284
- environment.moduleGraph.invalidateModule(module);
1285
- await transformQuietly(reader, module.url);
1286
- const added = [...module.importedModules.difference(known)];
1287
- await transformReachable(reader, added);
1288
- }
1289
- } finally {
1290
- state.readAhead -= 1;
1187
+ const lang = moduleType === void 0 ? void 0 : COMPILED_LANGS.get(moduleType);
1188
+ return ctx.parse(code, lang && { lang });
1189
+ } catch {
1190
+ return void 0;
1291
1191
  }
1292
1192
  }
1293
- function takeStaleModules(context, environment) {
1294
- const { state } = context;
1295
- const ids = [...state.stale];
1296
- state.stale.clear();
1297
- clearTimeout(state.reloadTimer);
1298
- state.reloadTimer = void 0;
1299
- const sheets = [];
1300
- for (const id of ids) {
1301
- const module = environment.moduleGraph.getModuleById(id);
1302
- if (!module) continue;
1303
- noteReloadSent(state, id);
1304
- sheets.push(module);
1193
+ async function authoredTextOf(id) {
1194
+ try {
1195
+ return await readFile(filePathOf(id), "utf8");
1196
+ } catch {
1197
+ return void 0;
1305
1198
  }
1306
- return sheets;
1307
- }
1308
- async function modulesWithGrownStylesheets(context, ctx, hot) {
1309
- const { environment } = ctx;
1310
- if (environment.config?.consumer !== "client" || context.state.served.size === 0) return;
1311
- const scripts = (hot.modules ?? []).filter((m) => m.id !== null && !isStylesheetId(m.id));
1312
- if (scripts.length === 0) return;
1313
- await readAhead(context, environment, scripts);
1314
- const sheets = takeStaleModules(context, environment);
1315
- return sheets.length === 0 ? void 0 : [...hot.modules ?? [], ...sheets];
1316
1199
  }
1317
-
1318
- // src/vite-css-blocks.ts
1319
- function matchBraces(tokens) {
1320
- const matches = /* @__PURE__ */ new Map();
1321
- const open = [];
1322
- for (const [index, token] of tokens.entries()) {
1323
- if (token.type === "{-token") open.push(index);
1324
- else if (token.type === "}-token" && open.length > 0) matches.set(open.pop(), index);
1325
- }
1326
- return matches;
1327
- }
1328
- function isBlank(tokens, from, to) {
1329
- for (let index = from; index < to; index += 1) {
1330
- const type = tokens[index].type;
1331
- if (type !== "whitespace-token" && type !== "comment") return false;
1332
- }
1333
- return true;
1334
- }
1335
- var OPENERS = /* @__PURE__ */ new Set(["(-token", "[-token", "function-token"]);
1336
- var CLOSERS = /* @__PURE__ */ new Set([")-token", "]-token"]);
1337
- function readSheet(css) {
1338
- const tokens = tokenize(css);
1339
- const matches = matchBraces(tokens);
1200
+ async function recordOf(context, ctx, input, reading) {
1201
+ const { code, id, pkg } = input;
1202
+ const hasPlaces = reading.problems.length + reading.dynamicCalls.length > 0;
1203
+ const map = hasPlaces ? combinedMapOf(ctx) : void 0;
1204
+ const authored = map ? await authoredTextOf(id) : void 0;
1205
+ const placing = {
1206
+ code,
1207
+ id,
1208
+ file: moduleLabel(context.root, id),
1209
+ pkg,
1210
+ place: placerFor(code, map, authored),
1211
+ unknownLine: lineUnknownNote(context, id)
1212
+ };
1213
+ const isStandingIn = isListed(context, pkg);
1340
1214
  return {
1341
- tokens,
1342
- slice: (from, to) => css.slice(tokens[from]?.startIndex ?? css.length, tokens[to - 1]?.endIndex ?? 0),
1343
- statements(from, to) {
1344
- const found = [];
1345
- let start = from;
1346
- let index = from;
1347
- let depth = 0;
1348
- while (index < to) {
1349
- const type = tokens[index].type;
1350
- if (OPENERS.has(type)) depth += 1;
1351
- else if (CLOSERS.has(type)) depth = Math.max(0, depth - 1);
1352
- const close = type === "{-token" && depth === 0 ? matches.get(index) : void 0;
1353
- if (close !== void 0 && close < to) {
1354
- found.push({ from: start, to: close + 1, open: index, close });
1355
- index = close + 1;
1356
- start = index;
1357
- } else if (type === "semicolon-token") {
1358
- found.push({ from: start, to: index + 1 });
1359
- index += 1;
1360
- start = index;
1361
- } else {
1362
- index += 1;
1363
- }
1364
- }
1365
- if (!isBlank(tokens, start, to)) found.push({ from: start, to });
1366
- return found;
1367
- }
1215
+ atoms: /* @__PURE__ */ new Set([...reading.atoms, ...reading.classes]),
1216
+ problems: isStandingIn ? [] : locate2(reading.problems, placing),
1217
+ suppressed: isStandingIn ? locate2(reading.problems, placing) : [],
1218
+ dynamicCalls: locateDynamic(reading, placing),
1219
+ pkg,
1220
+ file: placing.file,
1221
+ ...reading.edits.length > 0 && { edits: reading.edits }
1368
1222
  };
1369
1223
  }
1370
- function firstSignificant(tokens, from, to) {
1371
- let index = from;
1372
- while (index < to && isBlank(tokens, index, index + 1)) index += 1;
1373
- return index;
1374
- }
1375
-
1376
- // src/vite-selector-atoms.ts
1377
- var OPENERS2 = /* @__PURE__ */ new Set(["(-token", "[-token", "function-token"]);
1378
- var CLOSERS2 = /* @__PURE__ */ new Set([")-token", "]-token"]);
1379
- function membersOf(sheet, from, to) {
1380
- const members = [];
1381
- let start = from;
1382
- let depth = 0;
1383
- for (let index = from; index < to; index += 1) {
1384
- const type = sheet.tokens[index].type;
1385
- if (OPENERS2.has(type)) depth += 1;
1386
- else if (CLOSERS2.has(type)) depth -= 1;
1387
- else if (type === "comma-token" && depth === 0) {
1388
- members.push([start, index]);
1389
- start = index + 1;
1390
- }
1391
- }
1392
- members.push([start, to]);
1393
- return members;
1394
- }
1395
- var closings = /* @__PURE__ */ new WeakMap();
1396
- function closesOf(sheet) {
1397
- const known = closings.get(sheet);
1398
- if (known) return known;
1399
- const closes = /* @__PURE__ */ new Map();
1400
- const open = [];
1401
- for (const [index, token] of sheet.tokens.entries()) {
1402
- if (OPENERS2.has(token.type)) open.push(index);
1403
- else if (CLOSERS2.has(token.type) && open.length > 0) closes.set(open.pop(), index);
1404
- }
1405
- closings.set(sheet, closes);
1406
- return closes;
1407
- }
1408
- function afterGroup(sheet, index, to) {
1409
- return Math.min((closesOf(sheet).get(index) ?? to - 1) + 1, to);
1410
- }
1411
- function isTransparentList(sheet, index) {
1412
- const token = sheet.tokens[index];
1413
- if (token.type !== "function-token" || sheet.tokens[index - 1]?.type !== "colon-token") {
1414
- return false;
1415
- }
1416
- const name = token.structured.value.toLowerCase();
1417
- return name === "is" || name === "where";
1418
- }
1419
- function atomAt(sheet, index, to) {
1420
- const name = sheet.tokens[index + 1];
1421
- if (index + 1 >= to || name?.type !== "ident-token") return void 0;
1422
- const dot = sheet.tokens[index];
1423
- if (dot.type !== "delim-token" || dot.raw !== ".") return void 0;
1424
- return atomOfClass(name.structured.value);
1425
- }
1426
- function atomsNamedBy(sheet, from, to) {
1427
- const atoms = [];
1428
- let index = from;
1429
- while (index < to) {
1430
- const isGroup = OPENERS2.has(sheet.tokens[index].type);
1431
- if (isGroup && !isTransparentList(sheet, index)) {
1432
- index = afterGroup(sheet, index, to);
1433
- continue;
1434
- }
1435
- const atom = isGroup ? void 0 : atomAt(sheet, index, to);
1436
- if (atom) atoms.push(atom);
1437
- index += 1;
1224
+ function readOrUndefined(code, program, options) {
1225
+ try {
1226
+ return readModule(code, program, options);
1227
+ } catch (error) {
1228
+ if (error instanceof RangeError) return void 0;
1229
+ throw error;
1438
1230
  }
1439
- return atoms;
1440
1231
  }
1441
- var newNeeds = () => ({ parts: [], size: 0 });
1442
- var newList = () => ({ finished: [], current: newNeeds() });
1443
- function closeList(done, parent) {
1444
- const all = [...done.finished, done.current];
1445
- if (all.some((needs) => needs.size === 0)) return;
1446
- for (const needs of all) {
1447
- parent.current.parts.push(needs.parts);
1448
- parent.current.size += needs.size;
1232
+ async function recordParsed(context, ctx, input, key) {
1233
+ const { code, id, pkg, program, declared } = input;
1234
+ const { declaredSources, isDeclared, onlyVia } = await declaredReadingOf(context, ctx, declared, {
1235
+ environment: ctx.environment?.name ?? "client",
1236
+ module: { code, id, program }
1237
+ });
1238
+ const reading = readOrUndefined(code, program, {
1239
+ cxSources: /* @__PURE__ */ new Set([CX_SOURCE, ...declaredSources]),
1240
+ declaredSources,
1241
+ isDeclared,
1242
+ ownAtoms: await ownAtomNames(context, (file) => {
1243
+ ctx.addWatchFile(file);
1244
+ }),
1245
+ isDependency: isDependencyId(id),
1246
+ isVueScript: /\.vue(?:$|\?)/.test(id),
1247
+ setup: await setupExposuresFor(context, ctx, { program, code, id }),
1248
+ // A build turns each call into the string it returns; the dev server keeps the functions.
1249
+ ...context.command === "build" && { rewrite: { keep: keepMap(context.kept) } }
1250
+ });
1251
+ if (!reading) {
1252
+ context.state.exposures.delete(key);
1253
+ return unreadableRecord(context, { code, id, pkg }, TOO_DEEP);
1449
1254
  }
1255
+ if (reading.exposes.size > 0) context.state.exposures.set(key, reading.exposes);
1256
+ else context.state.exposures.delete(key);
1257
+ if (pkg !== void 0 && reading.usesCx) context.state.packages.add(pkg);
1258
+ const record = await recordOf(context, ctx, { code, id, pkg }, reading);
1259
+ return onlyVia === void 0 ? record : { ...record, onlyVia };
1450
1260
  }
1451
- function flatten(parts) {
1452
- const atoms = [];
1453
- const stack = [parts];
1454
- while (stack.length > 0) {
1455
- const part = stack.pop();
1456
- if (typeof part === "string") atoms.push(part);
1457
- else for (let index = part.length - 1; index >= 0; index -= 1) stack.push(part[index]);
1261
+ async function recordModule(context, ctx, module) {
1262
+ const { code, id, moduleType } = module;
1263
+ const environment = ctx.environment?.name ?? "client";
1264
+ const key = moduleKey(environment, id);
1265
+ const declared = await declaredModulesFor(context, ctx, environment);
1266
+ if (!isMentioningAtoms(code) && !isAboutListed({ code, id }, declared)) {
1267
+ context.state.modules.delete(key);
1268
+ return void 0;
1458
1269
  }
1459
- return atoms;
1460
- }
1461
- function readToken(sheet, index, limit, lists) {
1462
- const type = sheet.tokens[index].type;
1463
- const list = lists.at(-1);
1464
- const isNested = lists.length > 1;
1465
- if (OPENERS2.has(type)) {
1466
- if (!isTransparentList(sheet, index)) return afterGroup(sheet, index, limit.to);
1467
- lists.push(newList());
1468
- } else if (isNested && type === ")-token") {
1469
- closeList(lists.pop(), lists.at(-1));
1470
- } else if (isNested && type === "comma-token") {
1471
- list.finished.push(list.current);
1472
- list.current = newNeeds();
1473
- } else {
1474
- const atom = atomAt(sheet, index, limit.to);
1475
- if (atom && !limit.emitted.has(atom)) {
1476
- list.current.parts.push(atom);
1477
- list.current.size += 1;
1478
- }
1270
+ const isDependency = isDependencyId(id);
1271
+ const pkg = isDependency ? await packageNameOf(filePathOf(id), context.packageNames) : void 0;
1272
+ const unparsed = recordWithoutCalls(context, ctx, { code, id, moduleType, pkg });
1273
+ if (unparsed) {
1274
+ context.state.exposures.delete(key);
1275
+ context.state.modules.set(key, unparsed);
1276
+ return unparsed;
1479
1277
  }
1480
- return index + 1;
1481
- }
1482
- function blockersOf(sheet, from, to, emitted) {
1483
- const lists = [newList()];
1484
- let index = from;
1485
- while (index < to) index = readToken(sheet, index, { emitted, to }, lists);
1486
- while (lists.length > 1) closeList(lists.pop(), lists.at(-1));
1487
- return flatten(lists[0].current.parts);
1278
+ const program = parseOrUndefined(ctx, code, moduleType);
1279
+ const record = program ? await recordParsed(context, ctx, { code, id, pkg, program, declared }, key) : unreadableRecord(context, { code, id, pkg }, NOT_PARSED);
1280
+ if (!program) context.state.exposures.delete(key);
1281
+ context.state.modules.set(key, record);
1282
+ return record;
1488
1283
  }
1489
1284
 
1490
- // src/vite-prune.ts
1491
- var GROUPS = /* @__PURE__ */ new Set([
1492
- "container",
1493
- "document",
1494
- "layer",
1495
- "media",
1496
- "scope",
1497
- "starting-style",
1498
- "supports"
1499
- ]);
1500
- function atKeywordAt(sheet, index) {
1501
- const token = sheet.tokens[index];
1502
- if (token?.type !== "at-keyword-token") return void 0;
1503
- return token.structured.value.toLowerCase();
1504
- }
1505
- function isAtomicLayer(sheet, statement) {
1506
- if (statement.open === void 0) return false;
1507
- const significant = [];
1508
- for (let index = statement.from; index < statement.open; index += 1) {
1509
- const type = sheet.tokens[index].type;
1510
- if (type !== "whitespace-token" && type !== "comment") significant.push(index);
1511
- }
1512
- const name = sheet.tokens[significant[1] ?? -1];
1513
- return significant.length === 2 && atKeywordAt(sheet, significant[0]) === "layer" && name?.type === "ident-token" && name.structured.value === "atomic";
1514
- }
1515
- var CONDITIONALS = /* @__PURE__ */ new Set(["container", "document", "media", "scope", "supports"]);
1516
- function layerBlocks(sheet, from, to) {
1517
- const found = [];
1518
- for (const statement of sheet.statements(from, to)) {
1519
- const { open, close } = statement;
1520
- if (open === void 0 || close === void 0) continue;
1521
- if (isAtomicLayer(sheet, statement)) found.push(statement);
1522
- else {
1523
- const keyword = atKeywordAt(sheet, firstSignificant(sheet.tokens, statement.from, open));
1524
- if (keyword !== void 0 && CONDITIONALS.has(keyword)) {
1525
- found.push(...layerBlocks(sheet, open + 1, close));
1526
- }
1527
- }
1528
- }
1529
- return found;
1285
+ // src/vite-collect-problems.ts
1286
+ function listedAtomsOf(record, context) {
1287
+ if (record.pkg === void 0) return context.options.keep;
1288
+ const { keepFor } = context.options;
1289
+ return Object.hasOwn(keepFor, record.pkg) ? keepFor[record.pkg] : [];
1530
1290
  }
1531
- function isEmptyRegion(sheet, from, to) {
1532
- return sheet.statements(from, to).length === 0;
1533
- }
1534
- function pruneRule(sheet, statement, emitted) {
1535
- const { from, open, close } = statement;
1536
- const first = firstSignificant(sheet.tokens, from, open);
1537
- const members = membersOf(sheet, first, open);
1538
- const kept = members.filter(([start, end]) => blockersOf(sheet, start, end, emitted).length === 0);
1539
- if (kept.length === members.length) return sheet.slice(from, close + 1);
1540
- if (kept.length === 0) return "";
1541
- let trailing = open;
1542
- while (trailing > first && firstSignificant(sheet.tokens, trailing - 1, trailing) === trailing) {
1543
- trailing -= 1;
1544
- }
1545
- const list = sheet.slice(first, trailing);
1546
- const separator = /,\s/.test(list) ? ", " : ",";
1547
- const text = kept.map(([start, end]) => sheet.slice(start, end).trim()).join(separator);
1548
- return `${sheet.slice(from, first)}${text}${sheet.slice(trailing, open)}${sheet.slice(open, close + 1)}`;
1549
- }
1550
- function pruneRegion(sheet, from, to, emitted) {
1551
- const statements = sheet.statements(from, to);
1552
- let out = "";
1553
- for (const statement of statements) out += pruneStatement(sheet, statement, emitted);
1554
- const last = statements.at(-1);
1555
- return last ? out + sheet.slice(last.to, to) : sheet.slice(from, to);
1556
- }
1557
- function pruneStatement(sheet, statement, emitted) {
1558
- const { open, close } = statement;
1559
- if (open === void 0 || close === void 0) return sheet.slice(statement.from, statement.to);
1560
- const first = firstSignificant(sheet.tokens, statement.from, open);
1561
- const keyword = atKeywordAt(sheet, first);
1562
- if (keyword === void 0) return pruneRule(sheet, statement, emitted);
1563
- if (!GROUPS.has(keyword)) return sheet.slice(statement.from, close + 1);
1564
- if (isEmptyRegion(sheet, open + 1, close)) return sheet.slice(statement.from, close + 1);
1565
- const inner = pruneRegion(sheet, open + 1, close, emitted);
1566
- if (inner.trim() === "") return "";
1567
- return `${sheet.slice(statement.from, open + 1)}${inner}}`;
1568
- }
1569
- function pruneAtomicLayer(css, emitted) {
1570
- if (!css.includes("@")) return css;
1571
- const sheet = readSheet(css);
1572
- let out = "";
1573
- let cursor = 0;
1574
- for (const statement of layerBlocks(sheet, 0, sheet.tokens.length)) {
1575
- const { open, close } = statement;
1576
- out += sheet.slice(cursor, open + 1) + pruneRegion(sheet, open + 1, close, emitted);
1577
- cursor = close;
1578
- }
1579
- return cursor === 0 ? css : out + sheet.slice(cursor, sheet.tokens.length);
1580
- }
1581
- function visitMembers(sheet, from, to, visit) {
1582
- for (const statement of sheet.statements(from, to)) {
1583
- const { open, close } = statement;
1584
- if (open === void 0 || close === void 0) continue;
1585
- const first = firstSignificant(sheet.tokens, statement.from, open);
1586
- const keyword = atKeywordAt(sheet, first);
1587
- if (keyword === void 0) {
1588
- for (const [start, end] of membersOf(sheet, first, open)) visit(start, end);
1589
- } else if (GROUPS.has(keyword)) {
1590
- visitMembers(sheet, open + 1, close, visit);
1591
- }
1592
- }
1291
+ function dynamicProblems(record, context) {
1292
+ const isCovered = listedAtomsOf(record, context).length > 0;
1293
+ if (isCovered) return [];
1294
+ return record.dynamicCalls.map((call) => ({
1295
+ kind: "dynamic",
1296
+ offset: 0,
1297
+ construct: call.construct,
1298
+ text: "",
1299
+ file: call.file,
1300
+ line: call.line,
1301
+ column: call.column,
1302
+ pkg: record.pkg,
1303
+ ...call.unknownLine !== void 0 && { unknownLine: call.unknownLine }
1304
+ }));
1593
1305
  }
1594
- function inspectAtomicLayer(css) {
1595
- const atoms = /* @__PURE__ */ new Set();
1596
- if (!css.includes("@")) return { hasLayer: false, atoms };
1597
- const sheet = readSheet(css);
1598
- const blocks = layerBlocks(sheet, 0, sheet.tokens.length);
1599
- for (const { open, close } of blocks) {
1600
- visitMembers(sheet, open + 1, close, (start, end) => {
1601
- for (const atom of atomsNamedBy(sheet, start, end)) atoms.add(atom);
1602
- });
1603
- }
1604
- return { hasLayer: blocks.length > 0, atoms };
1306
+ function problemsOf(record, context) {
1307
+ return [...record.problems, ...dynamicProblems(record, context)];
1605
1308
  }
1606
- function unprunedAtoms(css, emitted) {
1607
- if (!css.includes("@")) return [];
1608
- const atoms = /* @__PURE__ */ new Set();
1609
- const sheet = readSheet(css);
1610
- for (const { open, close } of layerBlocks(sheet, 0, sheet.tokens.length)) {
1611
- visitMembers(sheet, open + 1, close, (start, end) => {
1612
- for (const atom of blockersOf(sheet, start, end, emitted)) atoms.add(atom);
1613
- });
1614
- }
1615
- return [...atoms].toSorted((a, b) => a.localeCompare(b));
1309
+ function failModule(ctx, context, input) {
1310
+ const { home, id, problems } = input;
1311
+ const message = moduleReport(problems, context.options.cxModules, home);
1312
+ const first = problems[0];
1313
+ return ctx.error({
1314
+ message,
1315
+ id,
1316
+ loc: { file: id, line: Math.max(first.line, 1), column: Math.max(first.column - 1, 0) }
1317
+ });
1616
1318
  }
1617
-
1618
- // src/vite-dev.ts
1619
- var RELOAD_ANSWER_MS = 2e3;
1620
- var NO_MAP = JSON.stringify({ mappings: "" });
1621
- var DIRECT = /[?&]direct\b/;
1622
- function isInlinedInHtml(module) {
1623
- return module.id !== null && /[?&]html-proxy\b/.test(module.id);
1319
+ function failDevModule(ctx, context, input) {
1320
+ const { home, id, record } = input;
1321
+ if (context.command !== "serve" || record.pkg !== void 0) return;
1322
+ const problems = problemsOf(record, context);
1323
+ if (problems.length > 0) failModule(ctx, context, { home, id, problems });
1624
1324
  }
1625
- async function reloadQuietly(environment, module) {
1626
- try {
1627
- if (isInlinedInHtml(module)) environment.hot.send({ type: "full-reload", path: "*" });
1628
- else await environment.reloadModule(module);
1629
- } catch {
1630
- }
1325
+ function importerIdsOf(ctx, cxId) {
1326
+ const info = ctx.getModuleInfo?.(cxId);
1327
+ return [.../* @__PURE__ */ new Set([...info?.importers ?? [], ...info?.dynamicImporters ?? []])];
1631
1328
  }
1632
- function reloadStale(context) {
1633
- const { state } = context;
1634
- state.reloadTimer = void 0;
1635
- const ids = [...state.stale];
1636
- state.stale.clear();
1637
- for (const id of ids) {
1638
- const sheet = state.served.get(id);
1639
- const module = sheet?.environment.moduleGraph.getModuleById(id);
1640
- if (!sheet || !module) continue;
1641
- noteReloadSent(state, id);
1642
- void reloadQuietly(sheet.environment, module);
1643
- }
1329
+ async function isStoodInFor(context, id) {
1330
+ if (!isDependencyId(id)) return false;
1331
+ const pkg = await packageNameOf(filePathOf(id), context.packageNames);
1332
+ return pkg !== void 0 && Object.hasOwn(context.options.keepFor, pkg);
1644
1333
  }
1645
- function isReloading(context, id) {
1646
- const sent = context.state.reloading.get(id);
1647
- return sent !== void 0 && Date.now() - sent < RELOAD_ANSWER_MS;
1334
+ async function candidateOf(ctx, context, id) {
1335
+ const code = ctx.getModuleInfo?.(id)?.code;
1336
+ if (code === null || code === void 0 || isNaveOwn(id)) return void 0;
1337
+ return await isStoodInFor(context, id) ? void 0 : { id, code };
1648
1338
  }
1649
- function reloadOutgrown(context, ids) {
1650
- const { state } = context;
1651
- const atoms = servedAtoms(state, context.kept);
1652
- for (const id of ids) {
1653
- const sheet = state.served.get(id);
1654
- if (sheet && !atoms.isSubsetOf(sheet.atoms) && !isReloading(context, id)) state.stale.add(id);
1655
- }
1656
- if (state.stale.size > 0 && state.reloadTimer === void 0 && state.readAhead === 0) {
1657
- state.reloadTimer = setTimeout(() => {
1658
- reloadStale(context);
1659
- }, 0);
1660
- state.reloadTimer.unref();
1661
- }
1339
+ async function isResolvingToCx(ctx, specifier, importer, known) {
1340
+ if (specifier === CX_SOURCE) return false;
1341
+ const key = `${importer.id}\0${specifier}`;
1342
+ const answered = known.answers.get(key);
1343
+ if (answered !== void 0) return answered;
1344
+ const resolved = await ctx.resolve?.(specifier, importer.id);
1345
+ const isCx = resolved?.id === known.cxId;
1346
+ known.answers.set(key, isCx);
1347
+ return isCx;
1662
1348
  }
1663
- var devServing = {
1664
- modulesWithGrown: modulesWithGrownStylesheets,
1665
- async hold(environment, id) {
1666
- const module = environment.moduleGraph.getModuleById(id);
1667
- if (!module) return;
1668
- if (module.importers.size === 0 && DIRECT.test(id)) return;
1669
- await environment.waitForRequestsIdle(id);
1670
- await transformReachable(environment, graphRoots(environment));
1671
- },
1672
- notePages(context) {
1673
- const { state } = context;
1674
- const atoms = servedAtoms(state, context.kept);
1675
- for (const sheet of state.served) {
1676
- const [id, { atoms: served, environment }] = sheet;
1677
- if (atoms.isSubsetOf(served)) continue;
1678
- const module = environment.moduleGraph.getModuleById(id);
1679
- if (module) environment.moduleGraph.invalidateModule(module);
1680
- }
1681
- devServing.noteGrowth(context);
1682
- },
1683
- noteConnect(context) {
1684
- const { state } = context;
1685
- state.listening += 1;
1686
- if (state.unheard.size === 0) return;
1687
- const ids = [...state.unheard];
1688
- state.unheard.clear();
1689
- for (const id of ids) state.reloading.delete(id);
1690
- reloadOutgrown(context, ids);
1691
- },
1692
- noteDisconnect(context) {
1693
- const { state } = context;
1694
- state.listening = Math.max(0, state.listening - 1);
1695
- },
1696
- noteGrowth(context) {
1697
- if (context.state.served.size === 0) return;
1698
- reloadOutgrown(context, context.state.served.keys());
1349
+ function lineFor(label, specifiers) {
1350
+ if (specifiers.length === 0) {
1351
+ return `${label}: imports cx through a specifier the build does not read.`;
1699
1352
  }
1700
- };
1701
- async function serveStylesheet(context, ctx, stylesheet) {
1702
- const { css, id } = stylesheet;
1703
- const { environment } = ctx;
1704
- if (!environment || !isServedAsStylesheet(id)) return void 0;
1705
- const isPossible = css.includes("\\") || /layer/i.test(css);
1706
- if (!isPossible || !inspectAtomicLayer(css).hasLayer) return void 0;
1707
- const dev = environment;
1708
- if (environment.config.consumer === "client") await devServing.hold(dev, id);
1709
- judgeWhenServed(context, id);
1710
- if (environment.config.consumer !== "client") return void 0;
1711
- const atoms = servedAtoms(context.state, context.kept);
1712
- context.state.served.set(id, { environment: dev, atoms });
1713
- context.state.reloading.delete(id);
1714
- return { code: pruneAtomicLayer(css, atoms), map: NO_MAP };
1715
- }
1716
- var HANDSHAKE_FILE = "nave-used-atoms.json";
1717
- function handshakePath(cacheDir) {
1718
- return path2.join(cacheDir, HANDSHAKE_FILE);
1353
+ return `${label}: imports cx from ${specifiers.map((specifier) => `'${specifier}'`).join(", ")}.`;
1719
1354
  }
1720
- function readHandshake(cacheDir) {
1721
- if (cacheDir === "") return void 0;
1722
- try {
1723
- const parsed2 = JSON.parse(readFileSync(handshakePath(cacheDir), "utf8"));
1724
- return Array.isArray(parsed2.emitted) && typeof parsed2.writer === "string" ? parsed2 : void 0;
1725
- } catch {
1726
- return void 0;
1355
+ async function lineOf(ctx, context, importer, known) {
1356
+ const written = specifiersOf(ctx, importer);
1357
+ const unread = [];
1358
+ const specifiers = written ?? [];
1359
+ for (const specifier of specifiers) {
1360
+ if (await isResolvingToCx(ctx, specifier, importer, known)) unread.push(specifier);
1727
1361
  }
1362
+ const label = moduleLabel(context.root, importer.id);
1363
+ if (unread.length > 0) return lineFor(label, unread);
1364
+ const isRead = written?.includes(CX_SOURCE) ?? importer.code.includes(CX_SOURCE);
1365
+ return isRead ? void 0 : lineFor(label, []);
1728
1366
  }
1729
- function writeHandshake(cacheDir, fields, warn) {
1730
- if (cacheDir === "") return;
1731
- const handshake = {
1732
- ...fields,
1733
- emitted: [...fields.emitted].toSorted(compareText),
1734
- writtenAt: fields.writtenAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1735
- build: fields.build ?? randomUUID()
1736
- };
1737
- try {
1738
- mkdirSync(cacheDir, { recursive: true });
1739
- writeFileSync(handshakePath(cacheDir), `${JSON.stringify(handshake, void 0, 2)}
1740
- `);
1741
- } catch (error) {
1742
- const reason = error instanceof Error ? error.message : String(error);
1743
- warn?.(
1744
- `navePlugin(): could not write ${handshakePath(cacheDir)} (${reason}), so a build of the client and a build of the server run as two processes cannot share their atoms.`
1745
- );
1367
+ async function checkCxImporters(ctx, context) {
1368
+ const resolved = await ctx.resolve?.(CX_SOURCE, path.join(context.root, "index.html"));
1369
+ if (!resolved) return;
1370
+ const known = { cxId: resolved.id, answers: /* @__PURE__ */ new Map() };
1371
+ const lines = [];
1372
+ for (const id of importerIdsOf(ctx, resolved.id)) {
1373
+ const importer = await candidateOf(ctx, context, id);
1374
+ const line = importer && await lineOf(ctx, context, importer, known);
1375
+ if (line) lines.push(line);
1746
1376
  }
1377
+ if (lines.length === 0) return;
1378
+ const count = lines.length === 1 ? "1 module imports" : `${lines.length} modules import`;
1379
+ ctx.error(
1380
+ [
1381
+ `${count} cx through a specifier the build does not follow, so the atoms its calls name would not ship:`,
1382
+ ...lines.toSorted(compareText),
1383
+ "Import cx from '@navecss/core/cx' directly where it is called."
1384
+ ].join("\n")
1385
+ );
1747
1386
  }
1748
1387
 
1749
- // src/vite-emitted.ts
1750
- function positionText(place) {
1751
- return place.line === 0 ? place.file : `${place.file}:${place.line}:${place.column}`;
1752
- }
1753
- function packageRecords(context) {
1754
- const records = {};
1755
- for (const pkg of Object.keys(context.options.keepFor)) {
1756
- const modules = context.state.modules.values().filter((record) => record.pkg === pkg).toArray();
1757
- const places = modules.flatMap((record) => [...record.suppressed, ...record.dynamicCalls]);
1758
- records[pkg] = {
1759
- collected: [...new Set(modules.flatMap((record) => [...record.atoms]))].toSorted(compareText),
1760
- unreadable: places.map((place) => positionText(place))
1761
- };
1762
- }
1763
- return records;
1764
- }
1765
- function unmatchedKeys(context) {
1766
- const transformed = new Set(context.state.modules.values().map((record) => record.pkg));
1767
- return Object.keys(context.options.keepFor).filter((pkg) => !transformed.has(pkg));
1388
+ // src/vite-cx-module-lines.ts
1389
+ function quoted(specifiers) {
1390
+ return specifiers.map((specifier) => `'${specifier}'`).join(", ");
1768
1391
  }
1769
- function takenFromCache(context) {
1770
- const file = context.inProcess ? void 0 : readHandshake(context.cacheDir);
1771
- if (file === void 0) return { atoms: [], took: false };
1772
- if (file.writer !== "client") return { atoms: file.emitted, took: true };
1773
- const pending = file.pending ?? [];
1774
- return { atoms: pending, took: pending.length > 0 };
1392
+ function applicationLinesOf(item) {
1393
+ const entry = `'${item.entry}'`;
1394
+ const relative = item.specifiers.filter((specifier) => isRelative(specifier));
1395
+ const other = item.specifiers.filter((specifier) => !isRelative(specifier));
1396
+ const place = relative.length === 1 ? "its" : "their";
1397
+ return {
1398
+ relative: relative.length === 0 ? [] : [
1399
+ `${item.label}: imports ${entry} as ${quoted(relative)}. Write '${item.replacement}' in ${place} place.`
1400
+ ],
1401
+ other: other.length === 0 ? [] : [`${item.label}: imports ${entry} as ${quoted(other)}.`],
1402
+ none: item.specifiers.length > 0 ? [] : [
1403
+ `${item.label}: imports ${entry} through a specifier the build could not find in the file.`
1404
+ ]
1405
+ };
1775
1406
  }
1776
- function emittedSet(context, environment, warn) {
1777
- const { state } = context;
1778
- if (state.emitted) return state.emitted;
1779
- const atoms = collectedAtoms(state, context.kept);
1780
- if (environment.config.consumer !== "client") return atoms;
1781
- const taken = takenFromCache(context);
1782
- for (const atom of taken.atoms) atoms.add(atom);
1783
- withRestorers(atoms);
1784
- state.emitted = atoms;
1785
- writeHandshake(
1786
- context.cacheDir,
1787
- {
1788
- emitted: [...atoms],
1789
- writer: "client",
1790
- consumed: taken.took,
1791
- keepFor: packageRecords(context),
1792
- unmatchedKeepFor: unmatchedKeys(context)
1793
- },
1794
- warn
1795
- );
1796
- return atoms;
1407
+ function dependencyLineOf(item) {
1408
+ const entry = `'${item.entry}'`;
1409
+ const found = item.specifiers.length === 0 ? `imports ${entry} through a specifier the build could not find in the file.` : `imports ${entry} as ${quoted(item.specifiers)}.`;
1410
+ return `${item.label}: ${found} The file is in ${item.pkg}, a dependency, so it is not yours to change.`;
1797
1411
  }
1798
- function namingLines(ctx, context, atoms) {
1799
- return recordsOf(context.state, ctx.environment.name).flatMap(
1800
- (record) => atoms.filter((atom) => record.atoms.has(atom)).map((atom) => `${record.file}: names ${atom}.`)
1412
+ var RELATIVE_REMEDY = "Adding a relative specifier to cxModules would not clear this: a relative entry is read from the project root. Instead of the rewrite";
1413
+ var TYPESCRIPT_CONDITION = "Where a rewrite ends in .ts, .tsx, .mts or .cts, TypeScript accepts it only if your tsconfig sets allowImportingTsExtensions or rewriteRelativeImportExtensions; if it sets neither, use the alias the line above describes instead.";
1414
+ var NO_SPECIFIER_REMEDY = "Where the build could not find the specifier, import the module there by a specifier ending in the listed file's name, or by an alias or package name added to cxModules.";
1415
+ function applicationRemedyOf(items, lines, input) {
1416
+ const remedy = [];
1417
+ const relativeCount = lines.reduce((count, line) => count + line.relative.length, 0);
1418
+ if (relativeCount === 1) {
1419
+ remedy.push(
1420
+ `${RELATIVE_REMEDY} its line gives, the file can import the module through an alias for it, added to cxModules.`
1421
+ );
1422
+ } else if (relativeCount > 1) {
1423
+ remedy.push(
1424
+ `${RELATIVE_REMEDY} each line gives, a file can import the module through an alias for it, added to cxModules.`
1425
+ );
1426
+ }
1427
+ const hasTypeScriptRewrite = items.some(
1428
+ (item) => item.specifiers.some((specifier) => isRelative(specifier)) && /\.(?:ts|tsx|mts|cts)$/.test(item.replacement)
1801
1429
  );
1430
+ if (hasTypeScriptRewrite) remedy.push(TYPESCRIPT_CONDITION);
1431
+ if (lines.some((line) => line.none.length > 0)) remedy.push(NO_SPECIFIER_REMEDY);
1432
+ const added = [
1433
+ ...new Set(
1434
+ items.flatMap((item) => item.specifiers.filter((specifier) => !isRelative(specifier)))
1435
+ )
1436
+ ];
1437
+ const array = cxModulesArray(input.configured, added);
1438
+ if (array !== void 0) {
1439
+ const each = added.length === 1 ? "the specifier" : "each specifier";
1440
+ remedy.push(`Add ${each}, as written, to cxModules in ${input.home}: cxModules: ${array}.`);
1441
+ }
1442
+ return remedy;
1802
1443
  }
1803
- function missingFromClient(ctx, context, missing) {
1804
- return [
1805
- "The client build already wrote its CSS without atoms this server build names, so their rules are missing from it:",
1806
- ...namingLines(ctx, context, missing),
1807
- `They are recorded in ${handshakePath(context.cacheDir)} for the next client build. List the atoms in keep in navePlugin(), or build the client again, then the server (vite build, then vite build --ssr). To keep this from recurring, build the server first, then the client (vite build --ssr, then vite build).`
1808
- ].join("\n");
1444
+ var LASTING_FIX_HERE = "The lasting fix there is the package's: import the module by a specifier ending in the listed file's name.";
1445
+ function packageRemedyOf(pkg, files, input) {
1446
+ if (input.hasKeepFor) {
1447
+ const file = files === 1 ? "a file" : "files";
1448
+ return `${pkg} also imports a module listed in cxModules in ${file} the build did not read; the keepFor entry above clears that too. ${LASTING_FIX_HERE}`;
1449
+ }
1450
+ return `For ${pkg}, list the atoms its calls can produce, from its documentation, under its name in ${input.home}: keepFor: { '${pkg}': ['<atom>'] }. Once the package is listed, its files no longer fail the build here, and the atoms you list stand in for the calls the build did not read. The lasting fix is the package's: import the module by a specifier ending in the listed file's name.`;
1809
1451
  }
1810
- function notInLastClient(ctx, context, missing) {
1452
+ function importerErrorOf(found, input) {
1453
+ const application = found.filter((item) => item.pkg === void 0);
1454
+ const dependencies = found.filter((item) => item.pkg !== void 0);
1455
+ const count = new Set(found.map((item) => item.importer)).size;
1456
+ const first = count === 1 ? "1 file imports a module listed in cxModules through a specifier the build does not recognise, so the build did not read the cx() calls made through it." : `${count} files import a module listed in cxModules through a specifier the build does not recognise, so the build did not read the cx() calls made through those specifiers.`;
1457
+ const lines = application.map((item) => applicationLinesOf(item));
1458
+ const byPackage = Map.groupBy(dependencies, (item) => item.pkg);
1459
+ const packages = byPackage.keys().toArray().toSorted(compareText);
1811
1460
  return [
1812
- `${handshakePath(context.cacheDir)} was written by a client build that was already used, so this server build could not be checked against the CSS. These atoms it names are not in that client build's set:`,
1813
- ...namingLines(ctx, context, missing),
1814
- "They are recorded in that file for a client build that runs next; to have them in the CSS, build the server first, then the client (vite build --ssr, then vite build)."
1461
+ first,
1462
+ ...lines.flatMap((line) => [...line.relative, ...line.other, ...line.none]).toSorted(compareText),
1463
+ ...dependencies.map((item) => dependencyLineOf(item)).toSorted(compareText),
1464
+ ...applicationRemedyOf(application, lines, input),
1465
+ ...packages.map(
1466
+ (pkg) => packageRemedyOf(pkg, new Set(byPackage.get(pkg).map((item) => item.importer)).size, {
1467
+ hasKeepFor: input.report?.includes(`keepFor: { '${pkg}'`) ?? false,
1468
+ home: input.home
1469
+ })
1470
+ )
1815
1471
  ].join("\n");
1816
1472
  }
1817
- function recordForNextClient(ctx, context, atoms, held) {
1818
- writeHandshake(
1819
- context.cacheDir,
1820
- { emitted: [...withRestorers(new Set(atoms))], writer: ctx.environment.name, consumed: false },
1821
- (message) => ctx.warn(message)
1822
- );
1823
- const missing = [...atoms].filter((atom) => !held.includes(atom)).toSorted(compareText);
1824
- if (missing.length > 0) ctx.warn(notInLastClient(ctx, context, missing));
1825
- }
1826
- function checkWaitingClient(ctx, context, atoms, file) {
1827
- const missing = [...atoms].filter((atom) => !file.emitted.includes(atom)).toSorted(compareText);
1828
- if (missing.length === 0) {
1829
- writeHandshake(context.cacheDir, { ...file, consumed: true, pending: [] }, (message) => {
1830
- ctx.warn(message);
1831
- });
1832
- return;
1833
- }
1834
- const pending = [.../* @__PURE__ */ new Set([...file.pending ?? [], ...withRestorers(new Set(atoms))])];
1835
- writeHandshake(context.cacheDir, { ...file, pending }, (message) => ctx.warn(message));
1836
- ctx.error(missingFromClient(ctx, context, missing));
1473
+
1474
+ // src/vite-cx-module-importers.ts
1475
+ var RESOLVED_EXTENSIONS = /* @__PURE__ */ new Set([
1476
+ ".js",
1477
+ ".json",
1478
+ ".jsx",
1479
+ ".mjs",
1480
+ ".mts",
1481
+ ".ts",
1482
+ ".tsx"
1483
+ ]);
1484
+ function entryOf(declared, id) {
1485
+ const file = fileOf(id);
1486
+ for (const [entry, resolved] of declared.ids) if (fileOf(resolved) === file) return entry;
1487
+ return id;
1837
1488
  }
1838
- function checkServerInvocation(ctx, context) {
1839
- if (context.cacheDir === "" || context.inProcess || context.state.emitted) return;
1840
- const atoms = new Set(recordsOf(context.state, ctx.environment.name).flatMap((r) => [...r.atoms]));
1841
- const file = readHandshake(context.cacheDir);
1842
- if (file?.writer !== "client") {
1843
- const emitted = [...withRestorers(/* @__PURE__ */ new Set([...atoms, ...file?.emitted ?? []]))];
1844
- writeHandshake(
1845
- context.cacheDir,
1846
- { emitted, writer: ctx.environment.name, consumed: false },
1847
- (message) => ctx.warn(message)
1848
- );
1849
- } else if (file.consumed) {
1850
- recordForNextClient(ctx, context, atoms, file.emitted);
1851
- } else {
1852
- checkWaitingClient(ctx, context, atoms, file);
1853
- }
1489
+ async function replacementFor(ctx, input) {
1490
+ const file = fileOf(input.id);
1491
+ const extension = path.posix.extname(file);
1492
+ const relativeTo = (target) => {
1493
+ const relative = path.posix.relative(path.posix.dirname(fileOf(input.importer)), target);
1494
+ return relative.startsWith("../") ? relative : `./${relative}`;
1495
+ };
1496
+ if (!RESOLVED_EXTENSIONS.has(extension)) return relativeTo(file);
1497
+ const short = relativeTo(file.slice(0, -extension.length));
1498
+ const isLanding = await isResolvingTo(ctx, { file, from: input.importer, specifier: short });
1499
+ return isLanding ? short : relativeTo(file);
1854
1500
  }
1855
-
1856
- // src/vite-cx-reexported.ts
1857
- function declaredNames(pattern) {
1501
+ async function unrecognisedSpecifiers(ctx, input) {
1502
+ const { answers, id, importer, recognised } = input;
1503
+ const known = { cxId: id, answers };
1858
1504
  const found = [];
1859
- const stack = pattern ? [pattern] : [];
1860
- while (stack.length > 0) {
1861
- const node = stack.pop();
1862
- if (node.type === "Identifier") {
1863
- found.push(stringAt(node, "name") ?? "");
1864
- } else if (node.type === "AssignmentPattern") {
1865
- pushAll(
1866
- stack,
1867
- [nodeAt(node, "left")].filter((part) => part !== void 0)
1868
- );
1869
- } else {
1870
- const single = ["argument", "value"].map((key) => nodeAt(node, key));
1871
- const lists = ["properties", "elements"].flatMap((key) => nodesAt(node, key));
1872
- pushAll(
1873
- stack,
1874
- single.filter((part) => part !== void 0)
1875
- );
1876
- pushAll(stack, lists);
1877
- }
1505
+ const unread = (specifiersOf(ctx, importer) ?? []).filter((text) => !recognised?.has(text));
1506
+ for (const specifier of unread) {
1507
+ if (await isResolvingToCx(ctx, specifier, importer, known)) found.push(specifier);
1878
1508
  }
1879
1509
  return found;
1880
1510
  }
1881
- function ownExports(program) {
1882
- const found = /* @__PURE__ */ new Set();
1883
- for (const statement of nodesAt(program, "body")) {
1884
- const namespace = statement.type === "ExportAllDeclaration" ? exportNameOf(nodeAt(statement, "exported")) : void 0;
1885
- if (namespace !== void 0) found.add(namespace);
1886
- if (statement.type !== "ExportNamedDeclaration") continue;
1887
- const declaration = nodeAt(statement, "declaration");
1888
- const declared = nodesAt(declaration ?? statement, "declarations").flatMap(
1889
- (declarator) => declaredNames(nodeAt(declarator, "id"))
1890
- );
1891
- for (const name of [...declared, ...declaredNames(declaration && nodeAt(declaration, "id"))]) {
1892
- found.add(name);
1893
- }
1894
- for (const specifier of nodesAt(statement, "specifiers")) {
1895
- const name = exportNameOf(nodeAt(specifier, "exported"));
1896
- if (name !== void 0) found.add(name);
1897
- }
1898
- }
1899
- return found;
1511
+ async function unrecognisedOf(ctx, context, input) {
1512
+ const { answers, declared, id } = input;
1513
+ const importer = await candidateOf(ctx, context, input.importer);
1514
+ if (!importer) return void 0;
1515
+ const recognised = context.state.recognised.get(
1516
+ recognisedKey(ctx.environment.name, fileOf(id), importer.id)
1517
+ );
1518
+ const specifiers = await unrecognisedSpecifiers(ctx, { answers, id, importer, recognised });
1519
+ if (recognised !== void 0 && specifiers.length === 0) return void 0;
1520
+ const pkg = isDependencyId(importer.id) ? await packageNameOf(filePathOf(importer.id), context.packageNames) : void 0;
1521
+ return {
1522
+ importer: importer.id,
1523
+ label: moduleLabel(context.root, importer.id),
1524
+ entry: entryOf(declared, id),
1525
+ specifiers,
1526
+ replacement: pkg === void 0 ? await replacementFor(ctx, { id, importer: importer.id }) : "",
1527
+ pkg
1528
+ };
1900
1529
  }
1901
- function exportedBy(node, source, own) {
1902
- const from = staticStringOf(nodeAt(node, "source"));
1903
- if (from === void 0 || !source.reaches.has(from)) return [];
1904
- if (node.type === "ExportAllDeclaration") {
1905
- const namespace = exportNameOf(nodeAt(node, "exported"));
1906
- if (namespace !== void 0) return [namespace];
1907
- return source.names.values().filter((name) => name !== "default" && !own.has(name) && !source.rivals.has(name)).toArray();
1908
- }
1909
- return nodesAt(node, "specifiers").flatMap((specifier) => {
1910
- const local = exportNameOf(nodeAt(specifier, "local"));
1911
- const exported = exportNameOf(nodeAt(specifier, "exported"));
1912
- return local !== void 0 && exported !== void 0 && source.names.has(local) ? [exported] : [];
1913
- });
1530
+ function listedIdsIn(ctx, declared) {
1531
+ const ids = new Set(declared.ids.values());
1532
+ const graph = ctx.getModuleIds?.() ?? [];
1533
+ for (const id of graph) {
1534
+ if (declared.files.has(fileOf(id))) ids.add(id);
1535
+ }
1536
+ return ids;
1914
1537
  }
1915
- function isImportedFrom(binding, source) {
1916
- if (binding?.kind !== "import" || binding.source === void 0) return false;
1917
- if (!source.reaches.has(binding.source)) return false;
1918
- return binding.imported === "*" || source.names.has(binding.imported ?? "");
1919
- }
1920
- function exportedAs(analysis, reference) {
1921
- const parent = analysis.parentOf.get(reference.node);
1922
- if (parent?.type === "ExportDefaultDeclaration") return "default";
1923
- return parent?.type === "ExportSpecifier" ? exportNameOf(nodeAt(parent, "exported")) : void 0;
1924
- }
1925
- function starSources(program) {
1926
- return nodesAt(program, "body").flatMap((statement) => {
1927
- const source = staticStringOf(nodeAt(statement, "source"));
1928
- const isStar = statement.type === "ExportAllDeclaration" && !nodeAt(statement, "exported");
1929
- return isStar && source !== void 0 ? [source] : [];
1930
- });
1538
+ function merged(found, item, id) {
1539
+ const key = `${item.importer}\0${fileOf(id)}`;
1540
+ const specifiers = /* @__PURE__ */ new Set([...found.get(key)?.specifiers ?? [], ...item.specifiers]);
1541
+ return [key, { ...item, specifiers: [...specifiers] }];
1931
1542
  }
1932
- function reexportedNames(program, source) {
1933
- const found = /* @__PURE__ */ new Set();
1934
- const own = ownExports(program);
1935
- const stack = [program];
1936
- while (stack.length > 0) {
1937
- const node = stack.pop();
1938
- if (node.type === "ExportNamedDeclaration" || node.type === "ExportAllDeclaration") {
1939
- for (const name of exportedBy(node, source, own)) found.add(name);
1543
+ async function unrecognisedImporters(ctx, context, declared) {
1544
+ const found = /* @__PURE__ */ new Map();
1545
+ for (const id of listedIdsIn(ctx, declared)) {
1546
+ const answers = /* @__PURE__ */ new Map();
1547
+ for (const importer of importerIdsOf(ctx, id)) {
1548
+ const item = await unrecognisedOf(ctx, context, { answers, declared, id, importer });
1549
+ if (item) found.set(...merged(found, item, id));
1940
1550
  }
1941
- pushAll(stack, childrenOf(node));
1942
- }
1943
- const analysis = analyze(program);
1944
- for (const reference of analysis.references) {
1945
- const name = isImportedFrom(reference.binding, source) ? exportedAs(analysis, reference) : void 0;
1946
- if (name !== void 0) found.add(name);
1947
1551
  }
1948
- return [...found];
1552
+ return found.values().toArray();
1553
+ }
1554
+ async function declaredImporterError(ctx, context, input) {
1555
+ const declared = await declaredModulesFor(context, ctx, ctx.environment.name);
1556
+ if (!declared) return void 0;
1557
+ const found = await unrecognisedImporters(ctx, context, declared);
1558
+ return found.length === 0 ? void 0 : importerErrorOf(found, input);
1949
1559
  }
1950
1560
 
1951
- // src/vite-cx-star-exports.ts
1952
- function resolvedFile(ctx, resolutions, input) {
1953
- const key = `${input.id}\0${input.specifier}`;
1954
- const known = resolutions.get(key);
1955
- if (known) return known;
1956
- const asked = (async () => {
1957
- try {
1958
- const resolved = await ctx.resolve?.(input.specifier, input.id);
1959
- return resolved ? fileOf(resolved.id) : void 0;
1960
- } catch {
1961
- return void 0;
1962
- }
1963
- })();
1964
- resolutions.set(key, asked);
1965
- return asked;
1561
+ // src/vite-markup.ts
1562
+ var MARKUP_WARNING = `navePlugin(): Nave read no HTML page and no server render, so your pages are likely rendered where it cannot see them, such as in a backend's own templates. A Nave class written there has no rule, in dev or in the build, unless its atom is listed in keep; if most of your Nave classes are written there, set atomic: 'all' to ship every atom. See "Which atoms the build ships" in node_modules/@navecss/core/README.md.`;
1563
+ function isMarkupCase(context) {
1564
+ const { state } = context;
1565
+ return context.options.keep.length === 0 && collectedAtoms(state, []).size === 0 && !state.directiveExpanded && !state.htmlRead && !state.serverTransformed && !context.isLibrary;
1966
1566
  }
1967
- function ownNamesOf(program, file, cxExports) {
1968
- const found = { names: ownExports(program), cx: /* @__PURE__ */ new Set() };
1969
- for (const name of found.names) {
1970
- if (cxExports.get(file)?.has(name)) found.cx.add(name);
1971
- }
1972
- return found;
1567
+ function judgeMarkup(context, warn) {
1568
+ const { state } = context;
1569
+ if (!isUsed(context) || state.markupJudged) return;
1570
+ state.markupJudged = true;
1571
+ if (isMarkupCase(context)) warn(MARKUP_WARNING);
1973
1572
  }
1974
- function unreadProgram(ctx, input) {
1975
- const { file, seen } = input;
1976
- const code = ctx.getModuleInfo?.(file)?.code;
1977
- const isUnread = !seen.has(file) && typeof code === "string";
1978
- seen.add(file);
1979
- return isUnread ? ctx.parse?.(code) : void 0;
1980
- }
1981
- async function addStars(ctx, input) {
1982
- const { found, file, program, walk } = input;
1983
- for (const specifier of starSources(program)) {
1984
- const star = await resolvedFile(ctx, walk.resolutions, { id: file, specifier });
1985
- const below = star ? await exportsOf(ctx, { file: star, walk }) : void 0;
1986
- const names = below ? [...below.names] : [];
1987
- for (const name of names) {
1988
- found.names.add(name);
1989
- if (below?.cx.has(name)) found.cx.add(name);
1990
- }
1573
+ function expectBuilds(context, builder, warn) {
1574
+ if (Object.values(builder.environments).some((environment) => environment.isBuilt)) {
1575
+ judgeMarkup(context, warn);
1576
+ return;
1991
1577
  }
1578
+ context.state.expectedEnvironments = new Set(Object.keys(builder.environments));
1992
1579
  }
1993
- async function exportsOf(ctx, input) {
1994
- const { file, walk } = input;
1995
- const program = unreadProgram(ctx, { file, seen: walk.seen });
1996
- if (!program) return { names: /* @__PURE__ */ new Set(), cx: /* @__PURE__ */ new Set() };
1997
- const found = ownNamesOf(program, file, walk.cxExports);
1998
- await addStars(ctx, { found, file, program, walk });
1999
- return found;
2000
- }
2001
- async function rivalsOf(ctx, input) {
2002
- const { cxExports, id, program, reaches, resolutions } = input;
2003
- const rivals = /* @__PURE__ */ new Set();
2004
- const others = starSources(program).filter((text) => !reaches.has(text));
2005
- for (const specifier of others) {
2006
- const file = await resolvedFile(ctx, resolutions, { id, specifier });
2007
- const walk = { cxExports, resolutions, seen: /* @__PURE__ */ new Set() };
2008
- const exported = file ? await exportsOf(ctx, { file, walk }) : void 0;
2009
- const names = exported ? [...exported.names] : [];
2010
- const own = names.filter((text) => !exported?.cx.has(text));
2011
- for (const name of own) rivals.add(name);
2012
- }
2013
- return rivals;
1580
+ function judgeAfterLastEnvironment(context, environment, warn) {
1581
+ const { builtEnvironments, expectedEnvironments } = context.state;
1582
+ builtEnvironments.add(environment.name);
1583
+ const isLast = expectedEnvironments ? [...expectedEnvironments].every((name) => builtEnvironments.has(name)) : !context.inProcess && environment.config.consumer === "client";
1584
+ if (isLast) judgeMarkup(context, warn);
2014
1585
  }
2015
1586
 
2016
- // src/vite-listed-holders.ts
2017
- async function reachingSpecifiers(ctx, input) {
2018
- const { code, file, id, resolutions } = input;
2019
- const program = ctx.parse?.(code);
2020
- if (!program) return void 0;
2021
- const reaches = /* @__PURE__ */ new Set();
2022
- for (const specifier of specifiersIn(program)) {
2023
- if (await resolvedFile(ctx, resolutions, { id, specifier }) === file) reaches.add(specifier);
2024
- }
2025
- return { program, reaches };
2026
- }
2027
- async function reexportedBy(ctx, input) {
2028
- const code = ctx.getModuleInfo?.(input.id)?.code;
2029
- if (code === null || code === void 0) return [];
1587
+ // src/vite-dev-judge.ts
1588
+ function isServedAsStylesheet(id) {
1589
+ return isStylesheetId(id) && !/[?&]inline\b/.test(id);
1590
+ }
1591
+ function judge(context) {
1592
+ judgeMarkup(context, (message) => context.logger?.warn(message));
1593
+ }
1594
+ function judgeWhenServed(context, id) {
1595
+ const { state } = context;
1596
+ if (!state.warmedUp || state.requested.has(filePathOf(id))) judge(context);
1597
+ }
1598
+ function stylesheetUrl(request, base) {
1599
+ const url = request.url;
1600
+ if (url === void 0 || !isServedAsStylesheet(url)) return void 0;
1601
+ return base !== "/" && url.startsWith(base) ? `/${url.slice(base.length)}` : url;
1602
+ }
1603
+ async function noteRequest(context, environment, url) {
1604
+ const { state } = context;
2030
1605
  try {
2031
- const read = await reachingSpecifiers(ctx, { ...input, code });
2032
- if (!read || read.reaches.size === 0) return [];
2033
- const { program, reaches } = read;
2034
- const rivals = await rivalsOf(ctx, { ...input, program, reaches });
2035
- return reexportedNames(program, { names: input.names, reaches, rivals });
1606
+ const resolved = await environment.pluginContainer.resolveId(decodeURI(url));
1607
+ if (resolved === null) return;
1608
+ const file = filePathOf(resolved.id);
1609
+ state.requested.add(file);
1610
+ if (state.served.keys().some((id) => filePathOf(id) === file)) judge(context);
2036
1611
  } catch {
2037
- return [];
2038
- }
2039
- }
2040
- async function reexportsOf(ctx, input) {
2041
- const importers = importersOf(ctx, input.id).filter((importer) => isDependencyId(importer));
2042
- const found = /* @__PURE__ */ new Map();
2043
- for (const importer of importers) {
2044
- const added = await reexportedBy(ctx, { ...input, file: fileOf(input.id), id: importer });
2045
- if (added.length > 0) found.set(importer, added);
2046
1612
  }
2047
- return found;
2048
1613
  }
2049
- async function holdersOf(ctx, origin) {
2050
- const held = /* @__PURE__ */ new Map([[origin.id, origin.names]]);
2051
- const queue = [origin.id];
2052
- while (queue.length > 0) {
2053
- const id = queue.shift();
2054
- const reexports = await reexportsOf(ctx, {
2055
- cxExports: origin.cxExports,
2056
- id,
2057
- names: held.get(id),
2058
- resolutions: origin.resolutions
2059
- });
2060
- for (const [importer, added] of reexports) {
2061
- const known = held.get(importer) ?? /* @__PURE__ */ new Set();
2062
- const isNew = importer !== origin.id && added.some((name) => !known.has(name));
2063
- if (isNew) {
2064
- held.set(importer, /* @__PURE__ */ new Set([...known, ...added]));
2065
- queue.push(importer);
2066
- }
2067
- }
2068
- }
2069
- return held.entries().map(([id, names]) => ({ id, names, isReached: id !== origin.id })).toArray();
1614
+ function noteStylesheetRequests(context, server) {
1615
+ const { state } = context;
1616
+ const environment = server.environments.client;
1617
+ state.warmedUp = environment.config.dev.warmup.length > 0;
1618
+ if (!state.warmedUp) return;
1619
+ server.middlewares.use(async (request, _response, next) => {
1620
+ const url = stylesheetUrl(request, server.config.base);
1621
+ if (url !== void 0 && !state.markupJudged) await noteRequest(context, environment, url);
1622
+ next();
1623
+ });
2070
1624
  }
2071
1625
 
2072
- // src/vite-cx-take-keys.ts
2073
- function isCxName(names, name) {
2074
- return names === void 0 || names.has(name);
2075
- }
2076
- function patternKeys(pattern, names) {
2077
- const taken = [];
2078
- for (const property of nodesAt(pattern, "properties")) {
2079
- const key = property.type === "Property" ? propertyNameOf(nodeAt(property, "key"), property.computed === true) : void 0;
2080
- if (key === void 0) return void 0;
2081
- if (isCxName(names, key)) taken.push(key);
2082
- }
2083
- return taken;
2084
- }
2085
- function referenceTakes(analysis, node, names) {
2086
- const read = memberReadOf({ analysis }, node);
2087
- if (read) {
2088
- if (read.key === void 0) return void 0;
2089
- return isCxName(names, read.key) ? [read.key] : [];
2090
- }
2091
- const pattern = patternDestructuring({ analysis }, node);
2092
- return pattern?.type === "ObjectPattern" ? patternKeys(pattern, names) : void 0;
2093
- }
2094
- function outermost(analysis, node) {
2095
- let current = node;
2096
- let parent = analysis.parentOf.get(current);
2097
- while (parent?.type === "ParenthesizedExpression") {
2098
- current = parent;
2099
- parent = analysis.parentOf.get(current);
2100
- }
2101
- return current;
1626
+ // src/vite-dev-read.ts
1627
+ function graphRoots(environment) {
1628
+ return environment.moduleGraph.idToModuleMap.values().filter(
1629
+ (module) => module.importers.size === 0 && (module.id === null || !isStylesheetId(module.id))
1630
+ ).toArray();
2102
1631
  }
2103
- function thenCall(analysis, node) {
2104
- const member = analysis.parentOf.get(node);
2105
- if (member?.type !== "MemberExpression" || nodeAt(member, "object") !== node) return void 0;
2106
- const key = propertyNameOf(nodeAt(member, "property"), member.computed === true);
2107
- const call = analysis.parentOf.get(member);
2108
- return key === "then" && call?.type === "CallExpression" && nodeAt(call, "callee") === member ? call : void 0;
2109
- }
2110
- function thenCallback(analysis, node) {
2111
- const call = thenCall(analysis, node);
2112
- const callback = call && nodesAt(call, "arguments")[0];
2113
- const isInline = callback?.type === "ArrowFunctionExpression" || callback?.type === "FunctionExpression";
2114
- return isInline ? callback : void 0;
2115
- }
2116
- function referencesToParameter(analysis, owner, name) {
2117
- return analysis.references.filter(
2118
- ({ binding }) => binding?.kind === "param" && binding.owner === owner && binding.name === name
2119
- );
1632
+ var started = /* @__PURE__ */ new WeakMap();
1633
+ function noteStartedRequests(environment) {
1634
+ if (started.has(environment)) return;
1635
+ const open = /* @__PURE__ */ new Set();
1636
+ started.set(environment, open);
1637
+ const warmup = environment.warmupRequest.bind(environment);
1638
+ environment.warmupRequest = async (url) => {
1639
+ const request = warmup(url);
1640
+ open.add(request);
1641
+ try {
1642
+ await request;
1643
+ } finally {
1644
+ open.delete(request);
1645
+ }
1646
+ };
2120
1647
  }
2121
- function parameterTakes(analysis, callback, names) {
2122
- const parameter = nodesAt(callback, "params")[0];
2123
- if (parameter === void 0) return [];
2124
- if (parameter.type === "ObjectPattern") return patternKeys(parameter, names);
2125
- const name = parameter.type === "Identifier" ? stringAt(parameter, "name") : void 0;
2126
- if (name === void 0) return void 0;
2127
- const found = [];
2128
- for (const { binding, node } of referencesToParameter(analysis, callback, name)) {
2129
- const taken = binding?.writes === 0 ? referenceTakes(analysis, node, names) : void 0;
2130
- if (taken === void 0) return void 0;
2131
- pushAll(found, taken);
2132
- }
2133
- return found;
1648
+ async function waitForStartedRequests(environment) {
1649
+ const open = started.get(environment);
1650
+ if (open) await Promise.allSettled(open);
2134
1651
  }
2135
- function variableTakes(analysis, init, names) {
2136
- const declarator = analysis.parentOf.get(init);
2137
- const declaration = declarator && analysis.parentOf.get(declarator);
2138
- if (declaration && analysis.parentOf.get(declaration)?.type === "ExportNamedDeclaration") {
2139
- return void 0;
2140
- }
2141
- const found = [];
2142
- for (const { binding, node } of analysis.references) {
2143
- if (binding?.init !== init) continue;
2144
- const taken = binding.writes === 0 ? referenceTakes(analysis, node, names) : void 0;
2145
- if (taken === void 0) return void 0;
2146
- pushAll(found, taken);
1652
+ async function transformQuietly(environment, url) {
1653
+ try {
1654
+ await environment.transformRequest(url);
1655
+ } catch {
2147
1656
  }
2148
- return found;
2149
1657
  }
2150
- function awaitedKeys(analysis, awaited, names) {
2151
- const declarator = analysis.parentOf.get(awaited);
2152
- const isVariable = declarator?.type === "VariableDeclarator" && nodeAt(declarator, "init") === awaited && nodeAt(declarator, "id")?.type === "Identifier";
2153
- return isVariable ? variableTakes(analysis, awaited, names) : referenceTakes(analysis, awaited, names);
1658
+ function isEndOfRead(module) {
1659
+ return module.id !== null && isCssModuleId(module.id);
2154
1660
  }
2155
- function dynamicKeys(analysis, node, names) {
2156
- const awaited = analysis.parentOf.get(node);
2157
- if (awaited?.type === "AwaitExpression") {
2158
- return awaitedKeys(analysis, outermost(analysis, awaited), names);
1661
+ async function transformReachable(environment, roots) {
1662
+ const seen = new Set(roots);
1663
+ let level = [...roots];
1664
+ while (level.length > 0) {
1665
+ const next = [];
1666
+ await Promise.all(
1667
+ level.map(async (module) => {
1668
+ if (module.id !== null && isUnreadStylesheet(module.id)) return;
1669
+ await transformQuietly(environment, module.url);
1670
+ if (isEndOfRead(module)) return;
1671
+ for (const imported of module.importedModules) {
1672
+ if (seen.has(imported)) continue;
1673
+ seen.add(imported);
1674
+ next.push(imported);
1675
+ }
1676
+ })
1677
+ );
1678
+ level = next;
2159
1679
  }
2160
- const callback = thenCallback(analysis, node);
2161
- return callback && parameterTakes(analysis, callback, names);
2162
1680
  }
2163
1681
 
2164
- // src/vite-cx-takes.ts
2165
- var UNREADABLE = { shape: "unreadable", names: [] };
2166
- function namespaceTakes(analysis, references, names) {
2167
- const found = /* @__PURE__ */ new Set();
2168
- for (const reference of references) {
2169
- const taken = referenceTakes(analysis, reference.node, names);
2170
- if (taken === void 0) return { shape: "namespace", names: [] };
2171
- for (const name of taken) found.add(name);
2172
- }
2173
- return found.size === 0 ? void 0 : { shape: "namespace", names: [...found] };
2174
- }
2175
- function referencesTo(analysis, source, specifier) {
2176
- const local = exportNameOf(nodeAt(specifier, "local"));
2177
- return analysis.references.filter(({ binding }) => {
2178
- return binding?.kind === "import" && binding.imported === "*" && binding.source === source && binding.name === local;
2179
- });
2180
- }
2181
- function usesOf(analysis, source, specifier) {
2182
- const local = exportNameOf(nodeAt(specifier, "local"));
2183
- return analysis.references.filter(
2184
- ({ binding }) => binding?.kind === "import" && binding.source === source && binding.name === local
2185
- );
2186
- }
2187
- function isExportOf(analysis, reference) {
2188
- const parent = analysis.parentOf.get(reference.node);
2189
- return parent?.type === "ExportSpecifier" || parent?.type === "ExportDefaultDeclaration";
2190
- }
2191
- function passingOf(analysis, source, specifier) {
2192
- const uses = usesOf(analysis, source, specifier);
2193
- const exported = uses.filter((use) => isExportOf(analysis, use));
2194
- if (exported.length === 0) return {};
2195
- return exported.length === uses.length ? { isForward: true, isPassedOn: true } : { isPassedOn: true };
1682
+ // src/vite-dev-update.ts
1683
+ function readerOf(environment) {
1684
+ return {
1685
+ transformRequest: (url) => environment.transformRequest?.(url) ?? Promise.resolve()
1686
+ };
2196
1687
  }
2197
- function importTakes(node, source, scan) {
2198
- const found = [];
2199
- for (const specifier of nodesAt(node, "specifiers")) {
2200
- if (specifier.type === "ImportNamespaceSpecifier") {
2201
- const references = referencesTo(scan.analysis(), source, specifier);
2202
- const taken = namespaceTakes(scan.analysis(), references, scan.names);
2203
- const isForward = passingOf(scan.analysis(), source, specifier).isForward;
2204
- if (taken) found.push({ ...taken, ...isForward && { isForward } });
2205
- continue;
2206
- }
2207
- const imported = specifier.type === "ImportDefaultSpecifier" ? "default" : exportNameOf(nodeAt(specifier, "imported"));
2208
- if (imported !== void 0 && isCxName(scan.names, imported)) {
2209
- const passing = passingOf(scan.analysis(), source, specifier);
2210
- found.push({ shape: "names", names: [imported], ...passing });
1688
+ async function readAhead(context, environment, scripts) {
1689
+ const { state } = context;
1690
+ const reader = readerOf(environment);
1691
+ state.readAhead += 1;
1692
+ try {
1693
+ for (const module of scripts) {
1694
+ const known = new Set(module.importedModules);
1695
+ environment.moduleGraph.invalidateModule(module);
1696
+ await transformQuietly(reader, module.url);
1697
+ if (isEndOfRead(module)) continue;
1698
+ const added = [...module.importedModules.difference(known)];
1699
+ await transformReachable(reader, added);
2211
1700
  }
1701
+ } finally {
1702
+ state.readAhead -= 1;
2212
1703
  }
2213
- return found;
2214
1704
  }
2215
- function exportTakes(node, scan) {
2216
- if (node.type === "ExportAllDeclaration") {
2217
- if (nodeAt(node, "exported") !== void 0) {
2218
- return [{ shape: "namespace", names: [], isForward: true }];
2219
- }
2220
- const hasNamed = scan.names === void 0 || scan.names.keys().some((name) => name !== "default");
2221
- return hasNamed ? [{ shape: "star", names: [], isForward: true }] : [];
2222
- }
2223
- const re = nodesAt(node, "specifiers").map((specifier) => exportNameOf(nodeAt(specifier, "local"))).filter((name) => name !== void 0 && isCxName(scan.names, name));
2224
- return re.length === 0 ? [] : [{ shape: "names", names: re, isPassedOn: true, isForward: true }];
2225
- }
2226
- function dynamicTakes(node, scan) {
2227
- const keys = dynamicKeys(scan.analysis(), node, scan.names);
2228
- if (keys === void 0) return [{ shape: "dynamic-import", names: [] }];
2229
- return keys.length === 0 ? [] : [{ shape: "dynamic-import", names: keys }];
2230
- }
2231
- function takesOf(node, scan) {
2232
- const source = staticStringOf(nodeAt(node, "source"));
2233
- if (source === void 0 || !scan.reaches.has(source)) return [];
2234
- if (node.type === "ImportDeclaration") return importTakes(node, source, scan);
2235
- if (node.type === "ImportExpression") return dynamicTakes(node, scan);
2236
- return exportTakes(node, scan);
2237
- }
2238
- function takesIn(program, input) {
2239
- let analysis;
2240
- const scan = {
2241
- ...input,
2242
- analysis: () => analysis ??= analyze(program)
2243
- };
2244
- const found = [];
2245
- const stack = [program];
2246
- while (stack.length > 0) {
2247
- const node = stack.pop();
2248
- pushAll(found, takesOf(node, scan));
2249
- pushAll(stack, childrenOf(node));
1705
+ function takeStaleModules(context, environment) {
1706
+ const { state } = context;
1707
+ const ids = [...state.stale];
1708
+ state.stale.clear();
1709
+ clearTimeout(state.reloadTimer);
1710
+ state.reloadTimer = void 0;
1711
+ const sheets = [];
1712
+ for (const id of ids) {
1713
+ const module = environment.moduleGraph.getModuleById(id);
1714
+ if (!module) continue;
1715
+ noteReloadSent(state, id);
1716
+ sheets.push(module);
2250
1717
  }
2251
- return found;
1718
+ return sheets;
1719
+ }
1720
+ async function modulesWithGrownStylesheets(context, ctx, hot) {
1721
+ const { environment } = ctx;
1722
+ if (environment.config?.consumer !== "client" || context.state.served.size === 0) return;
1723
+ const scripts = (hot.modules ?? []).filter((m) => m.id !== null && !isUnreadStylesheet(m.id));
1724
+ if (scripts.length === 0) return;
1725
+ await readAhead(context, environment, scripts);
1726
+ const sheets = takeStaleModules(context, environment);
1727
+ return sheets.length === 0 ? void 0 : [...hot.modules ?? [], ...sheets];
2252
1728
  }
2253
1729
 
2254
- // src/vite-listed-routes.ts
2255
- async function takenBy(ctx, input) {
2256
- const { file, id, names, resolutions } = input;
2257
- const code = ctx.getModuleInfo?.(id)?.code;
1730
+ // src/vite-dev.ts
1731
+ var RELOAD_ANSWER_MS = 2e3;
1732
+ var NO_MAP = JSON.stringify({ mappings: "" });
1733
+ var DIRECT = /[?&]direct\b/;
1734
+ var HTML_PROXY = /[?&]html-proxy\b/;
1735
+ function isInlinedInHtml(module) {
1736
+ return module.id !== null && HTML_PROXY.test(module.id);
1737
+ }
1738
+ async function reloadQuietly(environment, module) {
2258
1739
  try {
2259
- const read = code === null || code === void 0 ? void 0 : await reachingSpecifiers(ctx, { code, file, id, resolutions });
2260
- if (!read || read.reaches.size === 0) return [UNREADABLE];
2261
- return takesIn(read.program, { names, reaches: read.reaches });
1740
+ if (isInlinedInHtml(module)) environment.hot.send({ type: "full-reload", path: "*" });
1741
+ else await environment.reloadModule(module);
2262
1742
  } catch {
2263
- return [UNREADABLE];
2264
- }
2265
- }
2266
- function merged2(takes) {
2267
- const byKey = /* @__PURE__ */ new Map();
2268
- for (const take of takes) {
2269
- const key = `${take.taker}\0${take.shape}`;
2270
- const known = byKey.get(key);
2271
- const names = /* @__PURE__ */ new Set([...known?.names ?? [], ...take.names]);
2272
- const isReached = take.isReached || known?.isReached === true;
2273
- const isPassedOn = take.isPassedOn === true || known?.isPassedOn === true;
2274
- byKey.set(key, { ...take, isPassedOn, isReached, names: [...names].toSorted(compareText) });
2275
- }
2276
- return byKey.values().toArray();
2277
- }
2278
- async function isCounted(context, input) {
2279
- const { importer, isEntry, pkg, wantCalls } = input;
2280
- if (wantCalls || !isDependencyId(importer)) return true;
2281
- if (!isEntry) return false;
2282
- return await packageNameOf(filePathOf(importer), context.packageNames) !== pkg;
2283
- }
2284
- async function routesOf(ctx, context, input) {
2285
- const { holder, importer, isEntry, resolutions, wantCalls } = input;
2286
- const isDependency = isDependencyId(importer);
2287
- const taker = moduleLabel(context.root, importer);
2288
- const taken = await takenBy(ctx, {
2289
- file: fileOf(holder.id),
2290
- id: importer,
2291
- names: holder.names,
2292
- resolutions
2293
- });
2294
- const takes = taken.map((take) => ({ ...take, taker, isReached: holder.isReached }));
2295
- if (isDependency) {
2296
- const calls = takes.filter((take) => take.isForward !== true);
2297
- return { entry: isEntry ? calls : [], told: [], calls: wantCalls ? calls : [] };
2298
1743
  }
2299
- const isCleared = (take) => isEntry && take.shape !== "unreadable";
2300
- return {
2301
- entry: takes.filter((take) => isCleared(take)),
2302
- told: takes.filter((take) => !isCleared(take)),
2303
- calls: []
2304
- };
2305
1744
  }
2306
- function serverTakesOf(found, holder) {
2307
- const takes = [];
2308
- for (const taken of found.takes) {
2309
- const names = taken.names.filter((name) => holder.names.has(name));
2310
- const isWhole = taken.names.length === 0;
2311
- const hasNamed = holder.names.keys().some((name) => name !== "default");
2312
- const isTaken = taken.shape === "star" ? hasNamed : isWhole || names.length > 0;
2313
- if (isTaken) {
2314
- const kept = isWhole ? taken.names : names;
2315
- takes.push({
2316
- ...taken,
2317
- names: kept,
2318
- taker: found.taker,
2319
- isReached: holder.isReached,
2320
- ...found.isRecorded === true && { isRecorded: true }
2321
- });
2322
- }
1745
+ function reloadStale(context) {
1746
+ const { state } = context;
1747
+ state.reloadTimer = void 0;
1748
+ const ids = [...state.stale];
1749
+ state.stale.clear();
1750
+ for (const id of ids) {
1751
+ const sheet = state.served.get(id);
1752
+ const module = sheet?.environment.moduleGraph.getModuleById(id);
1753
+ if (!sheet || !module) continue;
1754
+ noteReloadSent(state, id);
1755
+ void reloadQuietly(sheet.environment, module);
2323
1756
  }
2324
- return takes;
2325
1757
  }
2326
- async function takesFrom(ctx, context, input) {
2327
- const { holder, isEntry, pkg, resolutions, wantCalls } = input;
2328
- const importers = importersOf(ctx, holder.id);
2329
- const entry = [];
2330
- const told = [];
2331
- const calls = [];
2332
- for (const importer of importers) {
2333
- if (!await isCounted(context, { importer, isEntry, pkg, wantCalls })) {
2334
- continue;
1758
+ function isReloading(context, id) {
1759
+ const sent = context.state.reloading.get(id);
1760
+ return sent !== void 0 && Date.now() - sent < RELOAD_ANSWER_MS;
1761
+ }
1762
+ function reloadOutgrown(context, ids) {
1763
+ const { state } = context;
1764
+ const atoms = servedAtoms(state, context.kept);
1765
+ for (const id of ids) {
1766
+ const sheet = state.served.get(id);
1767
+ if (sheet && !atoms.isSubsetOf(sheet.atoms) && !isReloading(context, id)) state.stale.add(id);
1768
+ }
1769
+ if (state.stale.size > 0 && state.reloadTimer === void 0 && state.readAhead === 0) {
1770
+ state.reloadTimer = setTimeout(() => {
1771
+ reloadStale(context);
1772
+ }, 0);
1773
+ state.reloadTimer.unref();
1774
+ }
1775
+ }
1776
+ var devServing = {
1777
+ modulesWithGrown: modulesWithGrownStylesheets,
1778
+ async hold(environment, id) {
1779
+ const module = environment.moduleGraph.getModuleById(id);
1780
+ if (!module) return;
1781
+ if (module.importers.size === 0 && DIRECT.test(id) && !HTML_PROXY.test(id)) return;
1782
+ if (HTML_PROXY.test(id)) await devServing.startedRequests(environment);
1783
+ await environment.waitForRequestsIdle(id);
1784
+ await transformReachable(environment, graphRoots(environment));
1785
+ },
1786
+ startedRequests: waitForStartedRequests,
1787
+ notePages(context) {
1788
+ const { state } = context;
1789
+ const atoms = servedAtoms(state, context.kept);
1790
+ for (const sheet of state.served) {
1791
+ const [id, { atoms: served, environment }] = sheet;
1792
+ if (atoms.isSubsetOf(served)) continue;
1793
+ const module = environment.moduleGraph.getModuleById(id);
1794
+ if (module) environment.moduleGraph.invalidateModule(module);
2335
1795
  }
2336
- const routes = await routesOf(ctx, context, {
2337
- holder,
2338
- importer,
2339
- isEntry,
2340
- resolutions,
2341
- wantCalls
2342
- });
2343
- pushAll(entry, routes.entry);
2344
- pushAll(told, routes.told);
2345
- pushAll(calls, routes.calls);
1796
+ devServing.noteGrowth(context);
1797
+ },
1798
+ noteConnect(context) {
1799
+ const { state } = context;
1800
+ state.listening += 1;
1801
+ if (state.unheard.size === 0) return;
1802
+ const ids = [...state.unheard];
1803
+ state.unheard.clear();
1804
+ for (const id of ids) state.reloading.delete(id);
1805
+ reloadOutgrown(context, ids);
1806
+ },
1807
+ noteDisconnect(context) {
1808
+ const { state } = context;
1809
+ state.listening = Math.max(0, state.listening - 1);
1810
+ },
1811
+ noteGrowth(context) {
1812
+ if (context.state.served.size === 0) return;
1813
+ reloadOutgrown(context, context.state.served.keys());
1814
+ }
1815
+ };
1816
+ async function serveStylesheet(context, ctx, stylesheet) {
1817
+ const { css, id } = stylesheet;
1818
+ const { environment } = ctx;
1819
+ if (!environment || !isServedAsStylesheet(id)) return void 0;
1820
+ const isPossible = css.includes("\\") || /layer/i.test(css);
1821
+ if (!isPossible || !inspectAtomicLayer(css).hasLayer) return void 0;
1822
+ const dev = environment;
1823
+ if (environment.config.consumer === "client" && !isCssModuleId(id)) {
1824
+ await devServing.hold(dev, id);
2346
1825
  }
2347
- return { entry: merged2(entry), told: merged2(told), calls: merged2(calls) };
2348
- }
2349
- function serverRoutes(holder, input) {
2350
- const file = fileOf(holder.id);
2351
- const fromServer = input.serverImports.filter((one) => one.file === file);
2352
- const takes = fromServer.flatMap((found) => serverTakesOf(found, holder));
2353
- const isCleared = (take) => input.isEntry && take.shape !== "unreadable";
2354
- return {
2355
- entry: takes.filter((take) => isCleared(take)),
2356
- told: takes.filter((take) => !isCleared(take))
2357
- };
1826
+ judgeWhenServed(context, id);
1827
+ if (environment.config.consumer !== "client") return void 0;
1828
+ const atoms = servedAtoms(context.state, context.kept);
1829
+ context.state.served.set(id, { environment: dev, atoms });
1830
+ context.state.reloading.delete(id);
1831
+ return { code: pruneAtomicLayer(css, atoms), map: NO_MAP };
2358
1832
  }
2359
1833
 
2360
- // src/vite-listed-takes.ts
2361
- async function prepareOne(ctx, context, input) {
2362
- const { isHop, problems, walk } = input;
2363
- const first = problems[0];
2364
- const pkg = first.pkg;
2365
- const names = new Set(problems.flatMap((problem) => problem.exportedAs ?? []));
2366
- const hasEntry = !isHop && problems.some((problem) => problem.specifier !== void 0);
2367
- const holders = await holdersOf(ctx, { ...walk, id: first.moduleId, names });
2368
- const routed = await Promise.all(
2369
- holders.map(
2370
- (holder) => takesFrom(ctx, context, {
2371
- holder,
2372
- isEntry: hasEntry && !holder.isReached,
2373
- pkg,
2374
- resolutions: walk.resolutions,
2375
- wantCalls: isHop
2376
- })
2377
- )
2378
- );
2379
- const isListed2 = Object.hasOwn(context.options.keepFor, pkg);
2380
- return { hasEntry, holders, isHop, isListed: isListed2, problems, routed };
1834
+ // src/vite-order.ts
1835
+ var MISSING = "which the CSS already written does not hold.";
1836
+ function packageOfPath(file) {
1837
+ const marker = "node_modules/";
1838
+ const at = file.lastIndexOf(marker);
1839
+ if (at === -1) return void 0;
1840
+ const parts = file.slice(at + marker.length).split("/");
1841
+ return parts[0]?.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
1842
+ }
1843
+ function missLines(record, emitted) {
1844
+ return [...record.atoms].toSorted(compareText).filter((atom) => !emitted.has(atom)).map((atom) => `${record.file}: names ${atom}, ${MISSING}`);
1845
+ }
1846
+ function clientFirstMessage(lines) {
1847
+ return [
1848
+ "The build read atoms after it wrote the CSS, so their rules are missing from it:",
1849
+ ...lines,
1850
+ "List the atoms in keep in navePlugin(), or build the server environments first with builder.buildApp in your Vite config."
1851
+ ].join("\n");
2381
1852
  }
2382
- function decideOne(prepared, serverImports) {
2383
- const { hasEntry, holders, isHop, isListed: isListed2, problems, routed } = prepared;
2384
- const fromServer = holders.map(
2385
- (holder) => serverRoutes(holder, { isEntry: hasEntry && !holder.isReached, serverImports })
2386
- );
2387
- const entry = [...routed.flatMap((route) => route.entry), ...fromServer.flatMap((r) => r.entry)];
2388
- if (entry.length > 0) return problems.filter((problem) => problem.specifier !== void 0);
2389
- const told = merged2([
2390
- ...routed.flatMap((route) => route.told),
2391
- ...fromServer.flatMap((route) => route.told)
2392
- ]);
2393
- const callers = isHop && !isListed2 ? callersOf(routed) : [];
2394
- if (told.length === 0 && callers.length === 0) return [];
2395
- return problems.map((problem) => ({
2396
- ...problem,
2397
- ...told.length > 0 && { takes: told },
2398
- ...callers.length > 0 && { callers }
2399
- }));
1853
+ function serverFirstMessage(records, emitted) {
1854
+ const missing = records.filter((record) => missLines(record, emitted).length > 0);
1855
+ const packages = [
1856
+ ...new Set(
1857
+ missing.map((record) => packageOfPath(record.file)).filter((name) => name !== void 0)
1858
+ )
1859
+ ].toSorted(compareText);
1860
+ return [
1861
+ "The build read atoms after it wrote the CSS, so their rules are missing from it:",
1862
+ ...missing.flatMap((record) => missLines(record, emitted)).toSorted(compareText),
1863
+ "A server build wrote the CSS, and only a build after it read these modules, such as code only the browser runs (an Astro client:only component, or a <script> in an .astro file).",
1864
+ "List the atoms in keep in navePlugin(), or have the server render that code too (a hydrated island rather than client:only).",
1865
+ ...packages.map(
1866
+ (name) => `${name} is a dependency the server build left external: add it to resolve.noExternal in your Vite config (on Astro, under vite in astro.config) so the server build reads it.`
1867
+ )
1868
+ ].join("\n");
2400
1869
  }
2401
- function callersOf(routed) {
2402
- const callers = new Set(routed.flatMap((route) => route.calls.map((call) => call.taker)));
2403
- return [...callers].toSorted(compareText);
2404
- }
2405
- async function prepareReexports(ctx, context, standingIn) {
2406
- const exporting = standingIn.filter((problem) => problem.exportedAs !== void 0);
2407
- const located = await withClearingSpecifiers(ctx, context, exporting);
2408
- const byModule = /* @__PURE__ */ new Map();
2409
- const hops = /* @__PURE__ */ new Map();
2410
- for (const problem of located) {
2411
- if (problem.moduleId === void 0 || problem.pkg === void 0) continue;
2412
- const group = isSecondHop(problem) ? hops : byModule;
2413
- group.set(problem.moduleId, [...group.get(problem.moduleId) ?? [], problem]);
2414
- }
2415
- const cxExports = new Map(
2416
- [...byModule.values(), ...hops.values()].map((problems) => [
2417
- fileOf(problems[0].moduleId),
2418
- new Set(problems.flatMap((problem) => problem.exportedAs ?? []))
2419
- ])
2420
- );
2421
- const walk = { cxExports, resolutions: /* @__PURE__ */ new Map() };
2422
- return await Promise.all([
2423
- ...byModule.values().map((problems) => prepareOne(ctx, context, { isHop: false, problems, walk })),
2424
- ...hops.values().map((problems) => prepareOne(ctx, context, { isHop: true, problems, walk }))
2425
- ]);
1870
+ function readByWriters(state) {
1871
+ const read = /* @__PURE__ */ new Set();
1872
+ for (const key of state.modules.keys()) {
1873
+ const at = key.indexOf("\0");
1874
+ if (state.writers.has(key.slice(0, at))) read.add(key.slice(at + 1));
1875
+ }
1876
+ return read;
2426
1877
  }
2427
- function decideReexports(prepared, serverImports) {
2428
- return prepared.flatMap((one) => decideOne(one, serverImports));
1878
+ function lateRecords(ctx, state) {
1879
+ const read = readByWriters(state);
1880
+ const prefix = `${ctx.environment.name}\0`;
1881
+ return [...state.modules].filter(([key]) => key.startsWith(prefix) && !read.has(key.slice(prefix.length))).map(([, record]) => record);
2429
1882
  }
2430
-
2431
- // src/vite-order.ts
2432
1883
  function checkEnvironmentOrder(ctx, context) {
2433
- const { emitted } = context.state;
1884
+ const { state } = context;
1885
+ const { emitted } = state;
2434
1886
  if (emitted === void 0) return;
2435
- const lines = [];
2436
- for (const record of recordsOf(context.state, ctx.environment.name)) {
2437
- for (const atom of [...record.atoms].toSorted(compareText)) {
2438
- if (!emitted.has(atom)) {
2439
- lines.push(`${record.file}: names ${atom}, which the CSS already written does not hold.`);
2440
- }
2441
- }
1887
+ if (state.firstWriter === "server") {
1888
+ if (state.writers.has(ctx.environment.name) || hasStylesheet(state, ctx.environment.name))
1889
+ return;
1890
+ const late = lateRecords(ctx, state);
1891
+ if (late.some((record) => missLines(record, emitted).length > 0))
1892
+ ctx.error(serverFirstMessage(late, emitted));
1893
+ return;
2442
1894
  }
2443
- if (lines.length === 0) return;
2444
- ctx.error(
2445
- [
2446
- `The build read atoms after it wrote the CSS, so their rules are missing from it:`,
2447
- ...lines,
2448
- "List the atoms in keep in navePlugin(), or build the server environments first with builder.buildApp in your Vite config."
2449
- ].join("\n")
1895
+ const lines = recordsOf(state, ctx.environment.name).flatMap(
1896
+ (record) => missLines(record, emitted)
2450
1897
  );
1898
+ if (lines.length > 0) ctx.error(clientFirstMessage(lines));
2451
1899
  }
2452
1900
  var PACKAGE_NAME = /^(@[^/]+\/[^/]+|[^./@][^/]*)(\/.*)?$/;
2453
1901
  var EXTENSIONS = ["", ".js", ".mjs", ".cjs", "/index.js", "/index.mjs", "/index.cjs"];
2454
1902
  function packageDirectory(directory, name) {
2455
- for (let current = directory; ; current = path2.dirname(current)) {
2456
- const found = path2.join(current, "node_modules", name);
2457
- if (existsSync(path2.join(found, "package.json"))) return found;
2458
- if (path2.dirname(current) === current) return void 0;
1903
+ for (let current = directory; ; current = path.dirname(current)) {
1904
+ const found = path.join(current, "node_modules", name);
1905
+ if (existsSync(path.join(found, "package.json"))) return found;
1906
+ if (path.dirname(current) === current) return void 0;
2459
1907
  }
2460
1908
  }
2461
1909
  function targetFile(target, input) {
@@ -2486,7 +1934,7 @@ function existingFile(base) {
2486
1934
  }
2487
1935
  function manifestOf(directory) {
2488
1936
  try {
2489
- return JSON.parse(readFileSync(path2.join(directory, "package.json"), "utf8"));
1937
+ return JSON.parse(readFileSync(path.join(directory, "package.json"), "utf8"));
2490
1938
  } catch {
2491
1939
  return void 0;
2492
1940
  }
@@ -2501,12 +1949,12 @@ function relativeTarget(manifest, input) {
2501
1949
  }
2502
1950
  function serverFileOf(input) {
2503
1951
  const parts = PACKAGE_NAME.exec(input.specifier);
2504
- const directory = parts && packageDirectory(path2.dirname(input.from), parts[1]);
1952
+ const directory = parts && packageDirectory(path.dirname(input.from), parts[1]);
2505
1953
  const manifest = directory ? manifestOf(directory) : void 0;
2506
1954
  if (!parts || !directory || !manifest) return void 0;
2507
1955
  const subpath = parts[2] === void 0 ? "." : `.${parts[2]}`;
2508
1956
  const relative = relativeTarget(manifest, { conditions: new Set(input.conditions), subpath });
2509
- const file = relative === void 0 ? void 0 : existingFile(path2.join(directory, relative));
1957
+ const file = relative === void 0 ? void 0 : existingFile(path.join(directory, relative));
2510
1958
  return file === void 0 ? void 0 : realpathSync(file);
2511
1959
  }
2512
1960
  var PACKAGE_NAME2 = /^(@[^/]+\/[^/]+|[^./@][^/]*)/;
@@ -2515,21 +1963,24 @@ function packageOfSpecifier(specifier) {
2515
1963
  return PACKAGE_NAME2.exec(specifier)?.[1];
2516
1964
  }
2517
1965
  function isDeclaringCore(root, name) {
2518
- for (let directory = root; ; directory = path2.dirname(directory)) {
1966
+ for (let directory = root; ; directory = path.dirname(directory)) {
2519
1967
  try {
2520
1968
  const manifest = JSON.parse(
2521
- readFileSync(path2.join(directory, "node_modules", name, "package.json"), "utf8")
1969
+ readFileSync(path.join(directory, "node_modules", name, "package.json"), "utf8")
2522
1970
  );
2523
1971
  return ["dependencies", "peerDependencies", "optionalDependencies"].some(
2524
1972
  (field) => manifest[field]?.["@navecss/core"] !== void 0
2525
1973
  );
2526
1974
  } catch {
2527
- if (path2.dirname(directory) === directory) return false;
1975
+ if (path.dirname(directory) === directory) return false;
2528
1976
  }
2529
1977
  }
2530
1978
  }
2531
- function warningFor(name) {
2532
- return `${name} depends on @navecss/core, but no environment of this build transforms it, so the atoms its cx() calls name are not read. Add it to ssr.noExternal in your Vite config so the build reads it, or list the atoms its calls can produce under its name in keepFor in navePlugin().`;
1979
+ function noExternalKey(firstWriter) {
1980
+ return firstWriter === "server" ? "resolve.noExternal in your Vite config (on Astro, under vite in astro.config)" : "ssr.noExternal in your Vite config";
1981
+ }
1982
+ function warningFor(name, firstWriter) {
1983
+ return `${name} depends on @navecss/core, but no environment of this build transforms it, so the atoms its cx() calls name are not read. Add it to ${noExternalKey(firstWriter)} so the build reads it, or list the atoms its calls can produce under its name in keepFor in navePlugin().`;
2533
1984
  }
2534
1985
  function judgeExternals(ctx, context) {
2535
1986
  const { state } = context;
@@ -2537,7 +1988,7 @@ function judgeExternals(ctx, context) {
2537
1988
  if (state.packages.has(name) || state.warned.has(name)) continue;
2538
1989
  if (Object.hasOwn(context.options.keepFor, name)) continue;
2539
1990
  state.warned.add(name);
2540
- ctx.warn(warningFor(name));
1991
+ ctx.warn(warningFor(name, state.firstWriter));
2541
1992
  }
2542
1993
  state.externals.clear();
2543
1994
  }
@@ -2576,7 +2027,7 @@ function conditionsOf(ctx) {
2576
2027
  function importsOf(ctx, input) {
2577
2028
  const { externals, id, root } = input;
2578
2029
  const program = parsed(ctx, id);
2579
- const taker = path2.relative(root, filePathOf(id)).replaceAll("\\", "/");
2030
+ const taker = path.relative(root, filePathOf(id)).replaceAll("\\", "/");
2580
2031
  const conditions = conditionsOf(ctx);
2581
2032
  const named = program ? specifiersIn(program).filter((one) => externals.has(one)) : [];
2582
2033
  const info = ctx.getModuleInfo?.(id);
@@ -2613,7 +2064,7 @@ function serverImportsOf(ctx, context) {
2613
2064
  };
2614
2065
  const externals = new Set(ids.filter((id) => isListed2(id)));
2615
2066
  if (externals.size === 0) return [];
2616
- return ids.filter((id) => path2.isAbsolute(filePathOf(id)) && !isDependencyId(id) && !isNaveOwn(id)).flatMap((id) => importsOf(ctx, { externals, id, root: context.root }));
2067
+ return ids.filter((id) => path.isAbsolute(filePathOf(id)) && !isDependencyId(id) && !isNaveOwn(id)).flatMap((id) => importsOf(ctx, { externals, id, root: context.root }));
2617
2068
  }
2618
2069
  function buildKeyOf(ctx) {
2619
2070
  const build = ctx.environment.config.build;
@@ -2622,7 +2073,7 @@ function buildKeyOf(ctx) {
2622
2073
  function readEntries(context) {
2623
2074
  try {
2624
2075
  const parsedFile = JSON.parse(
2625
- readFileSync(path2.join(context.cacheDir, SERVER_TAKES_FILE), "utf8")
2076
+ readFileSync(path.join(context.cacheDir, SERVER_TAKES_FILE), "utf8")
2626
2077
  );
2627
2078
  const isEntries = typeof parsedFile === "object" && parsedFile !== null && !Array.isArray(parsedFile);
2628
2079
  return isEntries ? parsedFile : {};
@@ -2632,7 +2083,7 @@ function readEntries(context) {
2632
2083
  }
2633
2084
  function writeServerImports(ctx, context, imports) {
2634
2085
  if (context.cacheDir === "") return;
2635
- const file = path2.join(context.cacheDir, SERVER_TAKES_FILE);
2086
+ const file = path.join(context.cacheDir, SERVER_TAKES_FILE);
2636
2087
  try {
2637
2088
  mkdirSync(context.cacheDir, { recursive: true });
2638
2089
  const entries = { ...readEntries(context), [buildKeyOf(ctx)]: imports };
@@ -2647,7 +2098,7 @@ function writeServerImports(ctx, context, imports) {
2647
2098
  }
2648
2099
  function readServerImports(context) {
2649
2100
  if (context.cacheDir === "") return [];
2650
- return Object.values(readEntries(context)).flat().filter((found) => existsSync(path2.resolve(context.root, found.taker))).map((found) => ({ ...found, isRecorded: true }));
2101
+ return Object.values(readEntries(context)).flat().filter((found) => existsSync(path.resolve(context.root, found.taker))).map((found) => ({ ...found, isRecorded: true }));
2651
2102
  }
2652
2103
  function graphOf(ctx) {
2653
2104
  return new Set([...ctx.getModuleIds?.() ?? []].map((id) => fileOf(id)));
@@ -2673,18 +2124,19 @@ function recordedSentence(takers) {
2673
2124
  function recordedTakers(imports) {
2674
2125
  return imports.filter((found) => found.isRecorded === true).map((found) => found.taker);
2675
2126
  }
2676
- function warningFor2(pkg, input) {
2677
- const { found, root } = input;
2127
+ function warningFor2(pkg, found, context) {
2128
+ const { root } = context;
2129
+ const { firstWriter } = context.state;
2678
2130
  const listed = found.map((one) => {
2679
2131
  if (one.isUnreadable === true)
2680
2132
  return `${one.taker} loads '${one.specifier}' in a way the build cannot read as a take, such as a require() or a # import`;
2681
2133
  if (one.file === void 0)
2682
2134
  return `${one.taker} imports '${one.specifier}', and the build cannot tell which file the server loads for it`;
2683
- const file = path2.relative(root, one.file).replaceAll("\\", "/");
2135
+ const file = path.relative(root, one.file).replaceAll("\\", "/");
2684
2136
  return `${one.taker} imports '${one.specifier}', which the server loads as ${file}, a file no environment of this build transforms`;
2685
2137
  }).toSorted(compareText).join("; ");
2686
2138
  const note = recordedSentence(recordedTakers(found));
2687
- const warning = `${pkg}, which keepFor lists, is imported by your server code where the build cannot tell whether that code takes cx from it: ${listed}. Add ${pkg} to ssr.noExternal in your Vite config so the build reads the package and checks what your server code takes; where a file takes cx from it, import cx from @navecss/core/cx there instead.`;
2139
+ const warning = `${pkg}, which keepFor lists, is imported by your server code where the build cannot tell whether that code takes cx from it: ${listed}. Add ${pkg} to ${noExternalKey(firstWriter)} so the build reads the package and checks what your server code takes; where a file takes cx from it, import cx from @navecss/core/cx there instead.`;
2688
2140
  return note === void 0 ? warning : `${warning} ${note}`;
2689
2141
  }
2690
2142
  function warnUnknown(ctx, context, unknown) {
@@ -2696,37 +2148,37 @@ function warnUnknown(ctx, context, unknown) {
2696
2148
  }
2697
2149
  for (const [pkg, imports] of byPackage) {
2698
2150
  context.state.warned.add(pkg);
2699
- ctx.warn(warningFor2(pkg, { found: imports, root: context.root }));
2151
+ ctx.warn(warningFor2(pkg, imports, context));
2700
2152
  }
2701
2153
  }
2702
2154
 
2703
2155
  // src/vite-collect-plugin.ts
2704
2156
  var OPTIONS_HOME = "navePlugin()";
2705
2157
  function isReadable(context, id) {
2706
- return isUsed(context) && !isStylesheetId(id) && !isNaveOwn(id);
2158
+ return isUsed(context) && !isUnreadStylesheet(id) && !isNaveOwn(id);
2159
+ }
2160
+ function takesContextOf(context) {
2161
+ return {
2162
+ root: context.root,
2163
+ rootName: "Vite's root",
2164
+ options: context.options,
2165
+ state: context.state,
2166
+ packageOf: (id) => packageNameOf(filePathOf(id), context.packageNames),
2167
+ labelOf: (id) => moduleLabel(context.root, id)
2168
+ };
2707
2169
  }
2708
2170
  async function problemsOfBuild(ctx, context, serverImports) {
2709
2171
  const records = recordsOf(context.state, ctx.environment.name);
2710
- const kept = await withClearingSpecifiers(
2172
+ return await judgeReexports(
2711
2173
  ctx,
2712
- context,
2713
- withoutEchoes(
2714
- records.map((record) => ({
2715
- onlyVia: record.onlyVia,
2716
- problems: [
2717
- ...problemsOf(record, context),
2718
- ...record.suppressed.filter((problem) => problem.kind === "declared")
2719
- ]
2720
- }))
2721
- )
2174
+ takesContextOf(context),
2175
+ records.map((record) => ({
2176
+ onlyVia: record.onlyVia,
2177
+ problems: problemsOf(record, context),
2178
+ suppressed: record.suppressed
2179
+ })),
2180
+ serverImports
2722
2181
  );
2723
- const standingIn = [
2724
- ...records.flatMap((record) => record.suppressed),
2725
- ...kept.filter((problem) => isSecondHop(problem))
2726
- ];
2727
- const rest = kept.filter((problem) => !isSecondHop(problem));
2728
- const prepared = await prepareReexports(ctx, context, standingIn);
2729
- return { problems: [...rest, ...decideReexports(prepared, serverImports)], prepared };
2730
2182
  }
2731
2183
  function reportOf(context, problems) {
2732
2184
  const report = buildReport(problems, context.options.cxModules, OPTIONS_HOME);
@@ -2772,9 +2224,9 @@ function createCollectPlugin(context) {
2772
2224
  context.state.started = true;
2773
2225
  assertOptions(context.options, await ownAtomNames(context));
2774
2226
  forgetDeclared(context.state, this.environment.name);
2775
- if (this.environment.config.consumer !== "client") return;
2776
- context.state.emitted = void 0;
2777
- context.state.clientEnded = false;
2227
+ if (this.environment.config.consumer === "client") context.state.clientEnded = false;
2228
+ const isWriter = context.inProcess ? context.state.emittedBy === this.environment.name : this.environment.config.consumer === "client";
2229
+ if (isWriter) forgetWriters(context.state);
2778
2230
  },
2779
2231
  async transform(code, id, meta) {
2780
2232
  if (!isReadable(context, id)) return;
@@ -2794,6 +2246,12 @@ function createCollectPlugin(context) {
2794
2246
  if (context.command === "serve") devServing.notePages(context);
2795
2247
  }
2796
2248
  },
2249
+ generateBundle: {
2250
+ order: "pre",
2251
+ handler(options, bundle) {
2252
+ checkBundle(this, context, { bundle, options, pass: "pre" });
2253
+ }
2254
+ },
2797
2255
  async buildEnd(error) {
2798
2256
  if (error || !isUsed(context) || context.command !== "build") return;
2799
2257
  const graph = graphOf(this);
@@ -2855,142 +2313,6 @@ function usedEnvironmentConfig(context, name, options) {
2855
2313
  return { resolve: { noExternal: ["@navecss/core"] } };
2856
2314
  }
2857
2315
 
2858
- // src/vite-css-capture.ts
2859
- var originals = /* @__PURE__ */ new WeakMap();
2860
- function transformHandler(plugin) {
2861
- const { transform } = plugin;
2862
- if (typeof transform === "function") return transform;
2863
- const handler = transform?.handler;
2864
- return typeof handler === "function" ? handler : void 0;
2865
- }
2866
- function unwrapped(handler) {
2867
- let current = handler;
2868
- for (let next = originals.get(current); next; next = originals.get(current)) current = next;
2869
- return current;
2870
- }
2871
- function remember(context, environment, id, css) {
2872
- const key = moduleKey(environment, id);
2873
- const isPossible = css.includes("\\") || /layer/i.test(css);
2874
- if (isPossible && inspectAtomicLayer(css).hasLayer) context.state.sheets.set(key, css);
2875
- else context.state.sheets.delete(key);
2876
- }
2877
- function captureStylesheets(context, plugins) {
2878
- const plugin = plugins?.find((candidate) => candidate.name === "vite:css-post");
2879
- const original = plugin && transformHandler(plugin);
2880
- if (!plugin || !original) return;
2881
- const wrapped = function(css, id) {
2882
- const environment = this.environment?.name ?? "client";
2883
- remember(context, environment, id, css);
2884
- return original.call(this, css, id);
2885
- };
2886
- originals.set(wrapped, original);
2887
- const { transform } = plugin;
2888
- plugin.transform = typeof transform === "function" ? wrapped : { ...transform, handler: wrapped };
2889
- }
2890
- var decoder = new TextDecoder();
2891
- async function refeed(ctx, css, id) {
2892
- const plugin = ctx.environment.plugins.find((candidate) => candidate.name === "vite:css-post");
2893
- const handler = plugin && transformHandler(plugin);
2894
- if (!handler) {
2895
- ctx.error(
2896
- "nave: this version of Vite has no CSS step the plugin knows how to hand a stylesheet back to, so it cannot remove the unused atoms. Ship every atom with atomic: 'all' in navePlugin() until the plugin supports this version of Vite."
2897
- );
2898
- }
2899
- await unwrapped(handler).call(ctx, css, id);
2900
- }
2901
- var fedPages = /* @__PURE__ */ new WeakMap();
2902
- var warnedText = /* @__PURE__ */ new WeakMap();
2903
- function bookFor(book, emitted) {
2904
- const ids = book.get(emitted) ?? /* @__PURE__ */ new Set();
2905
- book.set(emitted, ids);
2906
- return ids;
2907
- }
2908
- function isPageStylesheet(id) {
2909
- return /[?&]html-proxy\b/.test(id);
2910
- }
2911
- function pageStylesheets(ctx, state, emitted) {
2912
- const prefix = `${ctx.environment.name}\0`;
2913
- const fed = bookFor(fedPages, emitted);
2914
- const ids = state.sheets.keys().filter((key) => key.startsWith(prefix) && isPageStylesheet(key)).map((key) => key.slice(prefix.length)).filter((id) => !fed.has(id)).toArray();
2915
- for (const id of ids) fed.add(id);
2916
- return ids;
2917
- }
2918
- function isInlineImport(id) {
2919
- return /[?&]inline\b/.test(id) && !isPageStylesheet(id);
2920
- }
2921
- function isRawImport(id) {
2922
- return /[?&]raw\b/.test(id) && isStylesheetPath(id);
2923
- }
2924
- function isLayerFile(id) {
2925
- try {
2926
- return inspectAtomicLayer(readFileSync(filePathOf(id), "utf8")).hasLayer;
2927
- } catch {
2928
- return false;
2929
- }
2930
- }
2931
- async function refeedChunkStylesheets(ctx, chunk, state, emitted) {
2932
- const prefix = `${ctx.environment.name}\0`;
2933
- for (const id of [...Object.keys(chunk.modules), ...pageStylesheets(ctx, state, emitted)]) {
2934
- const css = state.sheets.get(`${prefix}${id}`);
2935
- if (css === void 0 || isInlineImport(id)) continue;
2936
- await refeed(ctx, pruneAtomicLayer(css, emitted), id);
2937
- }
2938
- }
2939
- function warnTextImports(ctx, chunk, context, emitted) {
2940
- const prefix = `${ctx.environment.name}\0`;
2941
- const warned = bookFor(warnedText, emitted);
2942
- for (const id of Object.keys(chunk.modules)) {
2943
- if (warned.has(id)) continue;
2944
- const isInline = isInlineImport(id) && context.state.sheets.has(`${prefix}${id}`);
2945
- const isRaw = !isInline && isRawImport(id) && isLayerFile(id);
2946
- if (!isInline && !isRaw) continue;
2947
- warned.add(id);
2948
- const query = isInline ? "?inline" : "?raw";
2949
- ctx.warn(
2950
- `${moduleLabel(context.root, id)} is imported with ${query}, so its stylesheet is text in the JavaScript, which the plugin does not filter: every atom in its atomic layer ships, not only the atoms this build emits. Import it without ${query} to have the layer filtered.`
2951
- );
2952
- }
2953
- }
2954
- function textOf(entry) {
2955
- const { source } = entry;
2956
- if (source === void 0) return "";
2957
- return typeof source === "string" ? source : decoder.decode(source);
2958
- }
2959
- function mismatchLines(assets, emitted) {
2960
- const lines = [];
2961
- const seenAnywhere = /* @__PURE__ */ new Set();
2962
- let hasLayer = false;
2963
- for (const { fileName, css } of assets) {
2964
- const seen = inspectAtomicLayer(css);
2965
- if (!seen.hasLayer) continue;
2966
- hasLayer = true;
2967
- for (const atom of seen.atoms) seenAnywhere.add(atom);
2968
- const extra = unprunedAtoms(css, emitted);
2969
- if (extra.length > 0) {
2970
- lines.push(
2971
- `${fileName}: the atomic layer holds a rule for ${extra.join(", ")}, which the build did not emit.`
2972
- );
2973
- }
2974
- }
2975
- const missing = [...emitted.difference(seenAnywhere)];
2976
- if (hasLayer && missing.length > 0) {
2977
- lines.push(`the atomic layers hold no rule for ${missing.join(", ")}, which the build emitted.`);
2978
- }
2979
- return lines;
2980
- }
2981
- function checkAtomicLayers(ctx, bundle, emitted) {
2982
- const assets = Object.values(bundle).filter((entry) => entry.type === "asset" && entry.fileName.toLowerCase().endsWith(".css")).map((entry) => ({ fileName: entry.fileName, css: textOf(entry) }));
2983
- const lines = mismatchLines(assets, emitted);
2984
- if (lines.length === 0) return;
2985
- ctx.error({
2986
- message: [
2987
- "nave: the built CSS does not hold exactly the atoms the build emitted, so the plugin could not remove the unused ones on this version of Vite.",
2988
- ...lines,
2989
- "Ship every atom with atomic: 'all' in navePlugin() until the plugin supports this version of Vite."
2990
- ].join("\n")
2991
- });
2992
- }
2993
-
2994
2316
  // src/vite-extend.ts
2995
2317
  function snapshotAndValidate(value) {
2996
2318
  const snapshot = snapshotExtendMap(value);
@@ -3047,30 +2369,6 @@ function dropLightningNaveWarning(logger) {
3047
2369
  wrappers.set(logger, wrapper);
3048
2370
  }
3049
2371
 
3050
- // src/vite-scan.ts
3051
- var decoder2 = new TextDecoder();
3052
- function isCssAsset(entry) {
3053
- return entry.type === "asset" && entry.fileName.toLowerCase().endsWith(".css");
3054
- }
3055
- function textOf2(entry) {
3056
- const { source } = entry;
3057
- if (source === void 0) return "";
3058
- return typeof source === "string" ? source : decoder2.decode(source);
3059
- }
3060
- function messageFor(findings, assetCount) {
3061
- const noun = assetCount === 1 ? "asset" : "assets";
3062
- return [
3063
- "@nave survived into the built CSS:",
3064
- ...findings.map((finding) => formatFinding(finding)),
3065
- `Scanned ${assetCount} CSS ${noun} this build wrote.`
3066
- ].join("\n");
3067
- }
3068
- function scanBundle(ctx, bundle) {
3069
- const assets = Object.values(bundle).filter((entry) => isCssAsset(entry));
3070
- const findings = assets.flatMap((asset) => findingsInText(asset.fileName, textOf2(asset)));
3071
- if (findings.length > 0) ctx.error({ message: messageFor(findings, assets.length) });
3072
- }
3073
-
3074
2372
  // src/vite-server-hooks.ts
3075
2373
  function warnThrough(ctx) {
3076
2374
  return (message) => {
@@ -3083,6 +2381,7 @@ function serverHooks(context) {
3083
2381
  const devServer = server;
3084
2382
  const owner = devServer.environments.client;
3085
2383
  installServerKeepMap(context, owner);
2384
+ noteStartedRequests(owner);
3086
2385
  const close = devServer.close.bind(devServer);
3087
2386
  const noteConnect = () => {
3088
2387
  devServing.noteConnect(context);
@@ -3113,64 +2412,6 @@ function serverHooks(context) {
3113
2412
  };
3114
2413
  }
3115
2414
 
3116
- // src/vite-report.ts
3117
- function withSourceRoot(source, sourceRoot) {
3118
- if (!sourceRoot || /^(?:[a-z][a-z\d+.-]*:|\/)/i.test(source)) return source;
3119
- return `${sourceRoot.replace(/\/$/, "")}/${source}`;
3120
- }
3121
- function locate2(diagnostic, input) {
3122
- const text = formatDiagnostic(diagnostic, { extend: input.extend });
3123
- const origin = input.incoming?.originalPositionFor({
3124
- line: diagnostic.line,
3125
- column: diagnostic.column - 1
3126
- });
3127
- if (origin === void 0) {
3128
- const position = { line: diagnostic.line, column: diagnostic.column };
3129
- return { text, file: input.file, isProcessed: true, ...position };
3130
- }
3131
- return {
3132
- text,
3133
- file: withSourceRoot(origin.source, input.incoming?.sourceRoot),
3134
- isProcessed: false,
3135
- line: origin.line,
3136
- column: origin.column + 1
3137
- };
3138
- }
3139
- function withNoMapNote(body, input, at) {
3140
- if (!at.isProcessed) return body;
3141
- const reason = input.incoming === void 0 ? "no source map" : "the source map does not cover it";
3142
- const note = `(position in ${input.file} as processed; ${reason})`;
3143
- const lines = body.split("\n");
3144
- const hasAvailable = lines.at(-1)?.startsWith("Available: ") === true;
3145
- lines.splice(hasAvailable ? -1 : lines.length, 0, note);
3146
- return lines.join("\n");
3147
- }
3148
- function logFor(message, input, at) {
3149
- return {
3150
- message,
3151
- id: input.id,
3152
- loc: { file: at.file, line: at.line, column: at.column - 1 }
3153
- };
3154
- }
3155
- function frame(at) {
3156
- return `${at.file}:${at.line}:${at.column}: `;
3157
- }
3158
- function reportDiagnostics(input) {
3159
- if (input.onUnknown === "ignore" || input.diagnostics.length === 0) return;
3160
- const inSourceOrder = input.diagnostics.toSorted((a, b) => a.offset - b.offset);
3161
- const located = inSourceOrder.map((diagnostic) => locate2(diagnostic, input));
3162
- if (input.onUnknown === "warn") {
3163
- for (const at of located) {
3164
- const message2 = withNoMapNote(`${frame(at)}${at.text}`, input, at);
3165
- input.ctx.warn(logFor(message2, input, at));
3166
- }
3167
- return;
3168
- }
3169
- const first = located[0];
3170
- const message = withNoMapNote(`${frame(first)}${foldLocated(located)}`, input, first);
3171
- input.ctx.error(logFor(message, input, first));
3172
- }
3173
-
3174
2415
  // src/vite-transform.ts
3175
2416
  function incomingMapOf(input) {
3176
2417
  if (!input.hasStylesheetMaps) return void 0;
@@ -3184,15 +2425,22 @@ function transformStylesheet(input) {
3184
2425
  extend: input.extend,
3185
2426
  onUnknown: input.onUnknown,
3186
2427
  from: file,
3187
- inputSourceMap
2428
+ inputSourceMap,
2429
+ reportStaleMarkers: input.reportStaleMarkers
3188
2430
  });
2431
+ const incoming = inputSourceMap && decodeIncomingMap(inputSourceMap);
2432
+ reportOrderWarnings(
2433
+ { ctx: input.ctx, id: input.id, file, incoming, warnings: result.warnings },
2434
+ input.warnings.memory,
2435
+ input.warnings.command
2436
+ );
3189
2437
  reportDiagnostics({
3190
2438
  ctx: input.ctx,
3191
2439
  diagnostics: result.diagnostics,
3192
2440
  extend: input.extend,
3193
2441
  id: input.id,
3194
2442
  file,
3195
- incoming: inputSourceMap && decodeIncomingMap(inputSourceMap),
2443
+ incoming,
3196
2444
  onUnknown: input.onUnknown
3197
2445
  });
3198
2446
  return { code: result.css, map: result.map };
@@ -3205,8 +2453,18 @@ function hasStylesheetMapsFor(config) {
3205
2453
  function withForwardSlashes(file) {
3206
2454
  return file.replaceAll("\\", "/");
3207
2455
  }
3208
- function createStylesheets(extend, onUnknown) {
3209
- let hasStylesheetMaps = false;
2456
+ function settingsOf(config) {
2457
+ if (config === void 0) return { hasStylesheetMaps: false, reportStaleMarkers: true };
2458
+ return {
2459
+ hasStylesheetMaps: hasStylesheetMapsFor(config),
2460
+ reportStaleMarkers: config.css?.transformer !== "lightningcss"
2461
+ };
2462
+ }
2463
+ function warningsOf(context) {
2464
+ return { command: context.command, memory: context.state.orderWarnings };
2465
+ }
2466
+ function createStylesheets(extend, onUnknown, context) {
2467
+ let settings = settingsOf(void 0);
3210
2468
  const failed = /* @__PURE__ */ new Map();
3211
2469
  async function expand(ctx, code, id) {
3212
2470
  const environment = ctx.environment?.name ?? "client";
@@ -3220,19 +2478,26 @@ function createStylesheets(extend, onUnknown) {
3220
2478
  throw error;
3221
2479
  }
3222
2480
  failed.get(environment)?.delete(id);
3223
- return transformStylesheet({ ctx, code, id, extend: atoms, onUnknown, hasStylesheetMaps });
2481
+ return transformStylesheet({
2482
+ ctx,
2483
+ code,
2484
+ id,
2485
+ extend: atoms,
2486
+ onUnknown,
2487
+ ...settings,
2488
+ warnings: warningsOf(context)
2489
+ });
3224
2490
  }
3225
2491
  return {
3226
2492
  configure(config) {
3227
- hasStylesheetMaps = hasStylesheetMapsFor(config);
2493
+ settings = settingsOf(config);
3228
2494
  },
3229
2495
  async transform(ctx, code, id) {
3230
2496
  if (!isStylesheetId(id)) return;
3231
2497
  const environment = ctx.environment?.name ?? "client";
3232
- const hasDirective = canHoldDirective(code);
3233
- if (!hasDirective) failed.get(environment)?.delete(id);
3234
- const result = hasDirective ? await expand(ctx, code, id) : void 0;
3235
- return result;
2498
+ if (canHoldDirective(code)) return expand(ctx, code, id);
2499
+ failed.get(environment)?.delete(id);
2500
+ noteNoDirective(context.state.orderWarnings, id);
3236
2501
  },
3237
2502
  hotUpdate(ctx, hot) {
3238
2503
  const ids = failed.get(ctx.environment.name);
@@ -3254,7 +2519,7 @@ function createStylesheets(extend, onUnknown) {
3254
2519
  // src/vite.ts
3255
2520
  function assemble(options, extend) {
3256
2521
  const context = createUsedContext(resolveUsedOptions(options), extend);
3257
- const stylesheets = createStylesheets(extend, options.onUnknown ?? "error");
2522
+ const stylesheets = createStylesheets(extend, options.onUnknown ?? "error", context);
3258
2523
  const nave = {
3259
2524
  name: "nave",
3260
2525
  config: (_config, env) => usedConfig(context, env?.command),
@@ -3281,26 +2546,18 @@ function assemble(options, extend) {
3281
2546
  return devServing.modulesWithGrown(context, this, hot);
3282
2547
  },
3283
2548
  async renderChunk(_code, chunk) {
3284
- if (!isUsed(context) || this.environment.config.consumer !== "client") return;
2549
+ if (!isUsed(context) || !canWriteStylesheets(context, this.environment)) return;
3285
2550
  const emitted = emittedSet(context, this.environment, (message) => {
3286
2551
  this.warn(message);
3287
2552
  });
3288
- await refeedChunkStylesheets(this, chunk, context.state, emitted);
2553
+ const fed = withoutWhole(chunk, stylesheetsLeftWhole(context, this.environment, chunk));
2554
+ await refeedChunkStylesheets(this, fed, context.state, emitted);
3289
2555
  warnTextImports(this, chunk, context, emitted);
3290
2556
  },
3291
2557
  generateBundle: {
3292
2558
  order: "post",
3293
- handler(_options, bundle) {
3294
- if (isUsed(context) && this.environment?.config.consumer === "client") {
3295
- checkAtomicLayers(
3296
- this,
3297
- bundle,
3298
- emittedSet(context, this.environment, (message) => {
3299
- this.warn(message);
3300
- })
3301
- );
3302
- }
3303
- scanBundle(this, bundle);
2559
+ handler(options2, bundle) {
2560
+ checkBundle(this, context, { bundle, options: options2, pass: "post" });
3304
2561
  }
3305
2562
  }
3306
2563
  };