@navecss/core 0.4.0 → 0.5.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.
@@ -0,0 +1,204 @@
1
+ // src/directive/expand-text-diagnostics.ts
2
+ function lineStartOffsets(text) {
3
+ const starts = [0];
4
+ let i = 0;
5
+ while (i < text.length) {
6
+ const c = text[i];
7
+ if (c === "\r") {
8
+ i++;
9
+ if (text[i] === "\n") i++;
10
+ starts.push(i);
11
+ continue;
12
+ }
13
+ if (c === "\n" || c === "\f") {
14
+ i++;
15
+ starts.push(i);
16
+ continue;
17
+ }
18
+ i++;
19
+ }
20
+ return starts;
21
+ }
22
+ function lastAtOrBefore(starts, offset) {
23
+ let low = 0;
24
+ let high = starts.length - 1;
25
+ while (low < high) {
26
+ const mid = low + high + 1 >> 1;
27
+ if (starts[mid] <= offset) low = mid;
28
+ else high = mid - 1;
29
+ }
30
+ return low;
31
+ }
32
+ function createPositionFinder(text) {
33
+ const starts = lineStartOffsets(text);
34
+ return (offset) => {
35
+ const line = lastAtOrBefore(starts, offset);
36
+ return { line: line + 1, column: offset - starts[line] };
37
+ };
38
+ }
39
+ function reportDiagnostics(diagnostics, css, options) {
40
+ const onUnknown = options.onUnknown ?? "error";
41
+ if (onUnknown === "ignore") return [];
42
+ const severity = onUnknown === "warn" ? "warning" : "error";
43
+ const positionAt = createPositionFinder(css);
44
+ return diagnostics.map((diagnostic) => {
45
+ const position = positionAt(diagnostic.offset);
46
+ return {
47
+ ...diagnostic,
48
+ severity,
49
+ ...options.from !== void 0 && { file: options.from },
50
+ line: position.line,
51
+ column: position.column + 1
52
+ };
53
+ });
54
+ }
55
+ function positionWarnings(warnings, css, file) {
56
+ const positionAt = createPositionFinder(css);
57
+ return warnings.toSorted((a, b) => a.offset - b.offset).map(({ offset, text }) => {
58
+ const position = positionAt(offset);
59
+ return {
60
+ text,
61
+ offset,
62
+ line: position.line,
63
+ column: position.column + 1,
64
+ ...file !== void 0 && { file }
65
+ };
66
+ });
67
+ }
68
+
69
+ // src/directive/bracket-match.ts
70
+ var OPENERS = /* @__PURE__ */ new Set(["(-token", "[-token", "function-token", "{-token"]);
71
+ var CLOSERS = /* @__PURE__ */ new Set([")-token", "]-token", "}-token"]);
72
+ function isMirrorCloser(openerType, closerType) {
73
+ if (closerType === ")-token") return openerType === "(-token" || openerType === "function-token";
74
+ if (closerType === "]-token") return openerType === "[-token";
75
+ return openerType === "{-token";
76
+ }
77
+ function matchBrackets(tokens) {
78
+ const closerFor = new Int32Array(tokens.length).fill(-1);
79
+ const opens = [];
80
+ for (const [i, token] of tokens.entries()) {
81
+ const { type } = token;
82
+ if (OPENERS.has(type)) {
83
+ opens.push(i);
84
+ continue;
85
+ }
86
+ if (!CLOSERS.has(type)) continue;
87
+ const openIndex = opens.at(-1);
88
+ if (openIndex === void 0 || !isMirrorCloser(tokens[openIndex].type, type)) continue;
89
+ opens.pop();
90
+ closerFor[openIndex] = i;
91
+ }
92
+ return closerFor;
93
+ }
94
+ function matchedCloseWithin(closerFor, openIndex, limit) {
95
+ const closeIndex = closerFor[openIndex];
96
+ return closeIndex !== -1 && closeIndex < limit ? closeIndex : void 0;
97
+ }
98
+
99
+ // src/directive/block-reader.ts
100
+ function scanItem(tokens, start, limit, closerFor) {
101
+ let i = start;
102
+ while (i < limit) {
103
+ const type = tokens[i].type;
104
+ if (OPENERS.has(type)) {
105
+ const closeIndex = matchedCloseWithin(closerFor, i, limit);
106
+ if (type === "{-token") {
107
+ return {
108
+ end: closeIndex === void 0 ? limit : closeIndex + 1,
109
+ consumedSemicolon: false,
110
+ trailingBlock: { openIndex: i, closeIndex }
111
+ };
112
+ }
113
+ i = closeIndex === void 0 ? limit : closeIndex + 1;
114
+ continue;
115
+ }
116
+ if (CLOSERS.has(type)) {
117
+ if (i === start) return { end: i + 1, consumedSemicolon: false };
118
+ i++;
119
+ continue;
120
+ }
121
+ if (type === "semicolon-token") return { end: i + 1, consumedSemicolon: true };
122
+ i++;
123
+ }
124
+ return { end: i, consumedSemicolon: false };
125
+ }
126
+ function scanCustomPropertyValue(tokens, start, limit, closerFor) {
127
+ let i = start;
128
+ while (i < limit) {
129
+ const type = tokens[i].type;
130
+ if (OPENERS.has(type)) {
131
+ const closeIndex = matchedCloseWithin(closerFor, i, limit);
132
+ i = closeIndex === void 0 ? limit : closeIndex + 1;
133
+ continue;
134
+ }
135
+ if (type === "semicolon-token") return i + 1;
136
+ i++;
137
+ }
138
+ return i;
139
+ }
140
+ function isColonSoonAfter(tokens, i, limit) {
141
+ let j = i;
142
+ while (j < limit && (tokens[j].type === "whitespace-token" || tokens[j].type === "comment")) j++;
143
+ return tokens[j]?.type === "colon-token";
144
+ }
145
+ function isCustomPropertyName(token) {
146
+ return token.type === "ident-token" && (token.structured?.value).startsWith("--");
147
+ }
148
+ function atKeywordName(token) {
149
+ return (token.structured?.value).toLowerCase();
150
+ }
151
+ function toDeclarationItem(start, end) {
152
+ return { kind: "declaration", start, end };
153
+ }
154
+ function preludeEndOffset(tokens, start, scan, preludeStart) {
155
+ if (scan.trailingBlock) return tokens[scan.trailingBlock.openIndex].startIndex;
156
+ const lastContentIndex = scan.consumedSemicolon ? scan.end - 2 : scan.end - 1;
157
+ return lastContentIndex >= start ? tokens[lastContentIndex].endIndex : preludeStart;
158
+ }
159
+ function toAtRuleItem(tokens, start, scan) {
160
+ const nameToken = tokens[start];
161
+ const preludeStart = tokens[start + 1]?.startIndex ?? nameToken.endIndex;
162
+ return {
163
+ kind: "at-rule",
164
+ start,
165
+ end: scan.end,
166
+ atKeyword: atKeywordName(nameToken),
167
+ preludeOffset: preludeStart,
168
+ preludeEndOffset: preludeEndOffset(tokens, start + 1, scan, preludeStart),
169
+ ...scan.trailingBlock && {
170
+ blockStart: scan.trailingBlock.openIndex + 1,
171
+ blockEnd: scan.trailingBlock.closeIndex ?? scan.end
172
+ }
173
+ };
174
+ }
175
+ function toRuleOrInvalidItem(start, scan) {
176
+ if (!scan.trailingBlock) return { kind: "invalid", start, end: scan.end };
177
+ return {
178
+ kind: "rule",
179
+ start,
180
+ end: scan.end,
181
+ blockStart: scan.trailingBlock.openIndex + 1,
182
+ blockEnd: scan.trailingBlock.closeIndex ?? scan.end
183
+ };
184
+ }
185
+ function readItem(tokens, start, limit, closerFor) {
186
+ const first = tokens[start];
187
+ if (first.type === "at-keyword-token") {
188
+ return toAtRuleItem(tokens, start, scanItem(tokens, start + 1, limit, closerFor));
189
+ }
190
+ if (first.type === "ident-token" && isColonSoonAfter(tokens, start + 1, limit)) {
191
+ if (isCustomPropertyName(first)) {
192
+ return toDeclarationItem(start, scanCustomPropertyValue(tokens, start + 1, limit, closerFor));
193
+ }
194
+ const scan = scanItem(tokens, start + 1, limit, closerFor);
195
+ if (!scan.trailingBlock) return toDeclarationItem(start, scan.end);
196
+ return toRuleOrInvalidItem(start, scan);
197
+ }
198
+ return toRuleOrInvalidItem(start, scanItem(tokens, start, limit, closerFor));
199
+ }
200
+ function isNaveAtKeyword(token) {
201
+ return token.type === "at-keyword-token" && atKeywordName(token) === "nave";
202
+ }
203
+
204
+ export { OPENERS, atKeywordName, createPositionFinder, isNaveAtKeyword, matchBrackets, positionWarnings, readItem, reportDiagnostics };
@@ -1,4 +1,5 @@
1
- import { createPositionFinder, tokenize, matchBrackets, readItem, isNaveAtKeyword } from './chunk-NFS2REPQ.js';
1
+ import { createPositionFinder, matchBrackets, readItem, isNaveAtKeyword } from './chunk-UVHGEVMV.js';
2
+ import { tokenize } from './chunk-2K5EBBF2.js';
2
3
 
3
4
  // src/directive/find-survivors.ts
4
5
  var NATURAL_STOP_CHARS = /* @__PURE__ */ new Set(["\n", ";", "}"]);
package/dist/collect.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { R as ResolvedUsedOptions, U as UsedAtomOptions, a as assertOptions, k as keptAtoms, r as resolveUsedOptions } from './vite-options-D1-hGA9L.js';
2
- import './atom-names-2OreuBx9.js';
1
+ export { R as ResolvedUsedOptions, U as UsedAtomOptions, a as assertOptions, k as keptAtoms, r as resolveUsedOptions } from './vite-options-sOCeGn9B.js';
2
+ import './atom-names-B8Ud-7zD.js';
3
3
 
4
4
  /**
5
5
  * The few shapes of the ESTree `this.parse` returns that the collector reads, declared here
@@ -169,6 +169,37 @@ declare const CX_SOURCE = "@navecss/core/cx";
169
169
  */
170
170
  declare function readModule(code: string, program: AstNode, options: ReadOptions): ModuleReading;
171
171
 
172
+ interface LayerReading {
173
+ /**
174
+ * Whether the stylesheet holds an `@layer atomic { ... }` block.
175
+ */
176
+ readonly hasLayer: boolean;
177
+ /**
178
+ * The built-in atoms whose class a rule in the layer selects.
179
+ */
180
+ readonly atoms: Set<string>;
181
+ }
182
+ /**
183
+ * `css` with every rule in an `@layer atomic` block that needs an atom outside `emitted` removed.
184
+ */
185
+ declare function pruneAtomicLayer(css: string, emitted: ReadonlySet<string>): string;
186
+ /**
187
+ * What the `@layer atomic` blocks of `css` hold.
188
+ */
189
+ declare function inspectAtomicLayer(css: string): LayerReading;
190
+ /**
191
+ * The atoms outside `emitted` that a rule left in the `@layer atomic` blocks of `css` still needs
192
+ * an element of: what a pruning to `emitted` would still remove, sorted.
193
+ */
194
+ declare function unprunedAtoms(css: string, emitted: ReadonlySet<string>): string[];
195
+
196
+ /**
197
+ * `atoms` with the atoms added that restore what an atom in it removes: `srOnlyFocusable` shows
198
+ * again on focus what `srOnly` hides, so a set that holds `srOnly` holds it too. This adds to the
199
+ * emitted set only, not to `keep` or to what `cx.dynamic()` accepts.
200
+ */
201
+ declare function withRestorers(atoms: Set<string>): Set<string>;
202
+
172
203
  /**
173
204
  * What an importer takes from a module that gives out Nave's `cx`: which of its `cx` exports it
174
205
  * imports, re-exports or reads off a namespace. The test is by name, not by file: an importer that
@@ -269,11 +300,18 @@ interface LocatedProblem extends Problem {
269
300
  readonly callers?: readonly string[] | undefined;
270
301
  }
271
302
  declare const REPORT_CAUSE = "the build cannot tell which atoms these apply, and it ships only the atoms it can read.";
303
+ /**
304
+ * The remedy lines only a host can write, given the application's problems: they print after the
305
+ * lines of the application's kinds and before any package's line.
306
+ */
307
+ type HostRemedies = (application: readonly LocatedProblem[]) => readonly string[];
272
308
  /**
273
309
  * The build report for every problem of an environment. `configured` is the `cxModules` list the
274
310
  * remedies that print one extend, and `home` is how the host spells where the options are written,
275
- * the call of its plugin or its wrapper: the one place the report names its host.
311
+ * the call of its plugin or its wrapper: the one place the report names its host. `hostRemedies`
312
+ * adds the lines only the host can write.
276
313
  */
277
- declare function buildReport(problems: readonly LocatedProblem[], configured: readonly string[], home: string): string;
314
+ declare function buildReport(problems: readonly LocatedProblem[], configured: readonly string[], home: string, hostRemedies?: HostRemedies): string;
278
315
 
279
- export { type AstNode, CX_SOURCE, type LocatedProblem, type ModuleReading, type Problem, type ProblemKind, REPORT_CAUSE, type ReadOptions, buildReport, readModule };
316
+ export { CX_SOURCE, REPORT_CAUSE, buildReport, inspectAtomicLayer, pruneAtomicLayer, readModule, unprunedAtoms, withRestorers };
317
+ export type { AstNode, HostRemedies, LayerReading, LocatedProblem, ModuleReading, Problem, ProblemKind, ReadOptions };
package/dist/collect.js CHANGED
@@ -1,4 +1,5 @@
1
- export { CX_SOURCE, REPORT_CAUSE, assertOptions, buildReport, keptAtoms, readModule, resolveUsedOptions } from './chunk-E5F6YF7A.js';
1
+ export { CX_SOURCE, REPORT_CAUSE, assertOptions, buildReport, inspectAtomicLayer, keptAtoms, pruneAtomicLayer, readModule, resolveUsedOptions, unprunedAtoms, withRestorers } from './chunk-BCUK4M4P.js';
2
2
  import './chunk-SYNQJNVB.js';
3
3
  import './chunk-CUL5PLPI.js';
4
+ import './chunk-2K5EBBF2.js';
4
5
  import './chunk-GZL2VW7C.js';
package/dist/css.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {}
package/dist/cx.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as AtomName } from './atom-names-2OreuBx9.js';
1
+ import { A as AtomName } from './atom-names-B8Ud-7zD.js';
2
2
 
3
3
  /**
4
4
  * cx — typed atomic class composer for Nave.
@@ -1,5 +1,5 @@
1
1
  import { AtomDefinition } from './atoms.js';
2
- import './atom-names-2OreuBx9.js';
2
+ import './atom-names-B8Ud-7zD.js';
3
3
 
4
4
  interface NaveLightningOptions {
5
5
  /**
@@ -48,4 +48,5 @@ interface NaveLightningAdapter {
48
48
  */
49
49
  declare function navePlugin(options?: NaveLightningOptions): NaveLightningAdapter;
50
50
 
51
- export { type NaveLightningAdapter, type NaveLightningOptions, navePlugin as default, navePlugin };
51
+ export { navePlugin as default, navePlugin };
52
+ export type { NaveLightningAdapter, NaveLightningOptions };
@@ -1,9 +1,11 @@
1
- import { splitByteOrderMark, warningTexts, foldedText } from './chunk-2A7IUZBH.js';
2
- import { validateExtendAtomsHostFree } from './chunk-LQSGQ2OA.js';
3
- import { snapshotExtendMap, expandText } from './chunk-LADFXRWQ.js';
4
- import './chunk-NFS2REPQ.js';
1
+ import { splitByteOrderMark } from './chunk-2TNTYI4L.js';
2
+ import { orderWarningTexts, warningTexts, foldedText } from './chunk-KKUYU75D.js';
3
+ import { validateExtendAtomsHostFree } from './chunk-63H2FLGU.js';
4
+ import { snapshotExtendMap, expandText } from './chunk-MWHZ26S4.js';
5
+ import './chunk-UVHGEVMV.js';
5
6
  import './chunk-SYNQJNVB.js';
6
7
  import './chunk-CUL5PLPI.js';
8
+ import './chunk-2K5EBBF2.js';
7
9
  import './chunk-GZL2VW7C.js';
8
10
  import { readFileSync } from 'fs';
9
11
 
@@ -21,6 +23,7 @@ function navePlugin(options = {}) {
21
23
  validateExtendAtomsHostFree(extend);
22
24
  const expandToText = (text, file) => {
23
25
  const result = expandText(text, { extend, onUnknown, from: file });
26
+ for (const line of orderWarningTexts(result.warnings, file)) console.warn(line);
24
27
  report(result.diagnostics, file, extend, onUnknown);
25
28
  return result;
26
29
  };
package/dist/postcss.d.ts CHANGED
@@ -1,6 +1,22 @@
1
- import { Plugin } from 'postcss';
1
+ import { Warning, Plugin } from 'postcss';
2
2
  import { AtomDefinition } from './atoms.js';
3
- import './atom-names-2OreuBx9.js';
3
+ import './atom-names-B8Ud-7zD.js';
4
+
5
+ /**
6
+ * The one place the PostCSS host reports a warning: `node.warn()`, except under Next.js's
7
+ * Turbopack, which drops every PostCSS warning. There (detected by `process.env.TURBOPACK` being
8
+ * set) the text is printed with `console.warn` behind the same `file:line:column: ` frame the
9
+ * Lightning CSS adapter prints, and `node.warn()` is not called as well. A tool that runs the
10
+ * plugin itself and reports its warnings turns the printing off (`printWarnings: false`): every
11
+ * warning is then raised, whatever `TURBOPACK` says.
12
+ */
13
+
14
+ /**
15
+ * Whether `warning` is an order warning (one `order-warnings.ts` or `shape-warnings.ts` makes)
16
+ * and not an `onUnknown` problem. No `onUnknown` governs it and it never fails a build, so a tool
17
+ * running the plugin with `printWarnings: false` reports it as a warning, never as a problem.
18
+ */
19
+ declare function isOrderWarning(warning: Warning): boolean;
4
20
 
5
21
  /**
6
22
  * Nave PostCSS plugin — resolves @nave directives.
@@ -18,6 +34,7 @@ import './atom-names-2OreuBx9.js';
18
34
  * navePlugin() // Nave atoms only, fails build on unknown atom
19
35
  * navePlugin({ extend: myAtoms }) // Nave + consumer atoms
20
36
  * navePlugin({ onUnknown: 'warn' }) // Log and skip instead of failing the build
37
+ * navePlugin({ onUnknown: 'warn', printWarnings: false }) // Raise warnings only, never print
21
38
  *
22
39
  * `extend` is trusted, consumer-authored code (your build config, or the module a path names),
23
40
  * which can already run arbitrary JavaScript at build time: it is not sanitised input. Every
@@ -77,10 +94,19 @@ interface NavePluginOptions {
77
94
  * 'ignore' — silently skip
78
95
  */
79
96
  onUnknown?: 'warn' | 'error' | 'ignore';
97
+ /**
98
+ * Whether a warning may be printed: a problem under 'warn', or an order warning. Under
99
+ * Turbopack (`TURBOPACK` set), which shows no PostCSS warning, the plugin prints each one with
100
+ * `console.warn` instead of raising it (default `true`). `false` raises it as a PostCSS warning
101
+ * only, whatever `TURBOPACK` says: for a tool that runs the plugin itself and reports the
102
+ * warnings it raises, telling the two apart with `isOrderWarning()`.
103
+ */
104
+ printWarnings?: boolean;
80
105
  }
81
106
  declare const navePlugin: {
82
107
  (options?: NavePluginOptions): Plugin;
83
108
  postcss: boolean;
84
109
  };
85
110
 
86
- export { AtomDefinition, type NavePluginOptions, navePlugin as default, navePlugin };
111
+ export { AtomDefinition, navePlugin as default, isOrderWarning, navePlugin };
112
+ export type { NavePluginOptions };