@navecss/core 0.2.0 → 0.4.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,279 @@
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';
3
+
4
+ /**
5
+ * The few shapes of the ESTree `this.parse` returns that the collector reads, declared here
6
+ * rather than imported: the plugin takes no dependency, and a node is read through a handful of
7
+ * typed accessors instead of a full ESTree type package.
8
+ */
9
+ interface AstNode {
10
+ readonly type: string;
11
+ readonly start: number;
12
+ readonly end: number;
13
+ readonly [key: string]: unknown;
14
+ }
15
+
16
+ /**
17
+ * One thing the collector could not read in a module, before it has a position or a path: what
18
+ * it is, where it starts (an offset into the module as the post-order half saw it), the
19
+ * construct as the plugin reads it, and the sentence that follows the construct in the report.
20
+ */
21
+ /**
22
+ * The kinds a report groups its remedy lines by; the order here is the order the lines print.
23
+ */
24
+ declare const PROBLEM_KINDS: readonly ["argument", "reference", "reexport", "declared", "concatenation", "dynamic", "unknown", "own", "unreadable"];
25
+ type ProblemKind = (typeof PROBLEM_KINDS)[number];
26
+ interface Problem {
27
+ readonly kind: ProblemKind;
28
+ readonly offset: number;
29
+ /**
30
+ * The construct, quoted as the plugin reads it, before it is cut for the report.
31
+ */
32
+ readonly construct: string;
33
+ /**
34
+ * The sentence after the construct.
35
+ */
36
+ readonly text: string;
37
+ /**
38
+ * For an unknown atom with no near candidate: the report closes with `Available:`.
39
+ */
40
+ readonly needsAvailable?: boolean;
41
+ /**
42
+ * For a re-export: whether listing the module in `cxModules` would clear it, which it does not
43
+ * when the module re-exports the `cx` of a module that is already listed (a second hop).
44
+ */
45
+ readonly isListable?: boolean;
46
+ /**
47
+ * For a re-export of Nave's `cx`: whether it gives `cx` under a name other than `cx`, as a default
48
+ * or inside a namespace. A listed module may not do that, so listing the module would only turn
49
+ * this problem into another one: no `cxModules` entry clears it.
50
+ */
51
+ readonly isRenamed?: boolean;
52
+ /**
53
+ * For a problem that gives Nave's `cx` out of its module, or a namespace holding it, or the `cx`
54
+ * of a module listed in `cxModules`: the names it is exported under (`default` for the default
55
+ * export), so a build-end check can tell whether an importer takes it.
56
+ */
57
+ readonly exportedAs?: readonly string[];
58
+ /**
59
+ * For a problem in a module listed in `cxModules`: whether the module also exports Nave's own
60
+ * `cx`. An importer's `cx` is then Nave's, so what the build says of its calls is not an echo of
61
+ * this problem and stays when the problem is reported.
62
+ */
63
+ readonly hasNaveCx?: boolean;
64
+ }
65
+
66
+ /**
67
+ * Replaces the text from `start` up to `end` of the module with `text`.
68
+ */
69
+ interface Edit {
70
+ readonly start: number;
71
+ readonly end: number;
72
+ readonly text: string;
73
+ }
74
+
75
+ /**
76
+ * What a compiled Vue component's script exposes to its template, and how a template reads it:
77
+ * `$setup.<name>`.
78
+ */
79
+
80
+ type SetupExposure = {
81
+ readonly kind: 'cx';
82
+ } | {
83
+ readonly kind: 'values';
84
+ readonly values: readonly string[];
85
+ };
86
+ type SetupExposures = ReadonlyMap<string, SetupExposure>;
87
+
88
+ interface ReadOptions {
89
+ /**
90
+ * The import specifiers that bind Nave's `cx`: its own module, and every module listed in
91
+ * `cxModules` that this module imports.
92
+ */
93
+ readonly cxSources: ReadonlySet<string>;
94
+ /**
95
+ * The specifiers in `cxSources` that name a module listed in `cxModules`.
96
+ */
97
+ readonly declaredSources?: ReadonlySet<string>;
98
+ /**
99
+ * Whether this module is itself listed in `cxModules`, so its exports of `cx` are judged as a
100
+ * listed module's.
101
+ */
102
+ readonly isDeclared?: boolean;
103
+ /**
104
+ * The names of the consumer's own atoms, which have no class.
105
+ */
106
+ readonly ownAtoms: ReadonlySet<string>;
107
+ /**
108
+ * Whether this module belongs to a dependency, where a Nave class built from pieces is only
109
+ * refused if the module also imports `cx`.
110
+ */
111
+ readonly isDependency: boolean;
112
+ /**
113
+ * For a compiled Vue template: what its component's script exposes to it.
114
+ */
115
+ readonly setup?: SetupExposures | undefined;
116
+ /**
117
+ * Whether the module is a compiled Vue component's `<script setup>`.
118
+ */
119
+ readonly isVueScript?: boolean;
120
+ /**
121
+ * Set in a build: the reading also returns the edits that turn each call it read into the
122
+ * string the call returns, and `keep` is the map a `cx.dynamic()` call looks a name up in.
123
+ */
124
+ readonly rewrite?: {
125
+ readonly keep: Readonly<Record<string, string>>;
126
+ };
127
+ }
128
+ interface ModuleReading {
129
+ /**
130
+ * Built-in atoms named by a readable `cx()` call.
131
+ */
132
+ readonly atoms: Set<string>;
133
+ /**
134
+ * Built-in atoms whose class is written whole in the module.
135
+ */
136
+ readonly classes: Set<string>;
137
+ readonly problems: Problem[];
138
+ /**
139
+ * Where `cx.dynamic()` is called, and the call as the plugin reads it.
140
+ */
141
+ readonly dynamicCalls: {
142
+ readonly construct: string;
143
+ readonly offset: number;
144
+ }[];
145
+ /**
146
+ * For a compiled Vue `<script setup>`: what it exposes to its template.
147
+ */
148
+ readonly exposes: Map<string, SetupExposure>;
149
+ /**
150
+ * Whether the module imports from a `cx` source, whether or not it uses the import.
151
+ */
152
+ readonly usesCx: boolean;
153
+ /**
154
+ * With `rewrite` set: the edits that replace the calls the reading found, empty otherwise.
155
+ */
156
+ readonly edits: Edit[];
157
+ }
158
+
159
+ /**
160
+ * Reads one module for the atoms its `cx()` calls and written classes name, and for everything
161
+ * in it the build cannot read. Pure: it takes the parsed module and returns what it found, so the
162
+ * host (the post-order plugin) decides where the findings go.
163
+ */
164
+
165
+ declare const CX_SOURCE = "@navecss/core/cx";
166
+
167
+ /**
168
+ * Reads `program`, the parse of `code`.
169
+ */
170
+ declare function readModule(code: string, program: AstNode, options: ReadOptions): ModuleReading;
171
+
172
+ /**
173
+ * What an importer takes from a module that gives out Nave's `cx`: which of its `cx` exports it
174
+ * imports, re-exports or reads off a namespace. The test is by name, not by file: an importer that
175
+ * takes only `Button` from a module which also exports `cx` as `cn` takes no `cx` from it, so
176
+ * nothing of its own routes calls through `cn`. A take the parse cannot tell apart (a namespace
177
+ * passed on, a computed key, a rest element, an `import()` used any other way) counts as taken, and
178
+ * so does an importer whose text names the module in no way the build reads (a `require()`).
179
+ */
180
+
181
+ /**
182
+ * How an importer takes the module: by the names it imports or re-exports, as a namespace, as
183
+ * `export *`, as an `import()`, or in a way the build cannot read.
184
+ */
185
+ type TakeShape = 'dynamic-import' | 'names' | 'namespace' | 'star' | 'unreadable';
186
+ interface Taken {
187
+ readonly shape: TakeShape;
188
+ /**
189
+ * The `cx` exports the importer takes by name; none when it takes the module whole.
190
+ */
191
+ readonly names: readonly string[];
192
+ /**
193
+ * Whether the importer only passes on what it takes: a named re-export, or an import that it
194
+ * then exports.
195
+ */
196
+ readonly isPassedOn?: boolean;
197
+ /**
198
+ * Whether the importer does nothing with what it takes but re-export it: `export ... from`, or an
199
+ * import that every use of is an export. A forward is followed to what takes it; it is not a
200
+ * take on the `cxModules` route.
201
+ */
202
+ readonly isForward?: boolean;
203
+ /**
204
+ * Whether the take comes from what an earlier server command left in the cache directory.
205
+ */
206
+ readonly isRecorded?: boolean;
207
+ }
208
+ interface Take extends Taken {
209
+ /**
210
+ * The importer's path, relative to the project root, with forward slashes.
211
+ */
212
+ readonly taker: string;
213
+ /**
214
+ * Whether the module taken from is reached through another module of its package, which
215
+ * re-exports the `cx` it gives out.
216
+ */
217
+ readonly isReached: boolean;
218
+ }
219
+
220
+ /**
221
+ * The report a build fails with when it meets a use it cannot read: a first line counting the
222
+ * problems and stating the cause, one line per problem with a 1-based, root-relative position,
223
+ * and the remedy block printed once, last. Under the default nobody set the option, so the first
224
+ * line never names it, and no remedy printed for application code is `atomic: 'all'`: an agent
225
+ * takes the first remedy that clears the error, and that one always does.
226
+ */
227
+
228
+ interface LocatedProblem extends Problem {
229
+ /**
230
+ * The module's path, relative to the project root, with forward slashes.
231
+ */
232
+ readonly file: string;
233
+ readonly line: number;
234
+ readonly column: number;
235
+ /**
236
+ * Set when the source map does not lead to the authored position: `line` and `column` are then
237
+ * zero and mean nothing, and this is the sentence that ends the problem's line.
238
+ */
239
+ readonly unknownLine?: string | undefined;
240
+ /**
241
+ * The package name when the module lies under `node_modules`.
242
+ */
243
+ readonly pkg?: string | undefined;
244
+ /**
245
+ * The id of the module the problem is in, for a problem the build-end check looks into the
246
+ * module graph for.
247
+ */
248
+ readonly moduleId?: string | undefined;
249
+ /**
250
+ * For a dependency's re-export of `cx`: the specifier that resolves from the project root to the
251
+ * module that holds it, when a `cxModules` entry can name it.
252
+ */
253
+ readonly specifier?: string | undefined;
254
+ /**
255
+ * For a dependency's listed module that exports a `cx` the build does not follow: the
256
+ * `cxModules` entries, as written, that name it.
257
+ */
258
+ readonly entries?: readonly string[] | undefined;
259
+ /**
260
+ * For a module of a package `keepFor` lists that gives `cx` out under another name: the
261
+ * application modules that take one of its `cx` exports, which `keepFor` does not clear.
262
+ */
263
+ readonly takes?: readonly Take[] | undefined;
264
+ /**
265
+ * For a dependency module that re-exports the `cx` of a module `cxModules` lists: the modules of
266
+ * other dependencies that call through it, root-relative, which only the package's `keepFor`
267
+ * entry can stand in for.
268
+ */
269
+ readonly callers?: readonly string[] | undefined;
270
+ }
271
+ declare const REPORT_CAUSE = "the build cannot tell which atoms these apply, and it ships only the atoms it can read.";
272
+ /**
273
+ * The build report for every problem of an environment. `configured` is the `cxModules` list the
274
+ * 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.
276
+ */
277
+ declare function buildReport(problems: readonly LocatedProblem[], configured: readonly string[], home: string): string;
278
+
279
+ export { type AstNode, CX_SOURCE, type LocatedProblem, type ModuleReading, type Problem, type ProblemKind, REPORT_CAUSE, type ReadOptions, buildReport, readModule };
@@ -0,0 +1,4 @@
1
+ export { CX_SOURCE, REPORT_CAUSE, assertOptions, buildReport, keptAtoms, readModule, resolveUsedOptions } from './chunk-E5F6YF7A.js';
2
+ import './chunk-SYNQJNVB.js';
3
+ import './chunk-CUL5PLPI.js';
4
+ import './chunk-GZL2VW7C.js';
package/dist/cx.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AtomName } from './atoms.js';
1
+ import { A as AtomName } from './atom-names-2OreuBx9.js';
2
2
 
3
3
  /**
4
4
  * cx — typed atomic class composer for Nave.
@@ -9,10 +9,13 @@ import { AtomName } from './atoms.js';
9
9
  * unknown name is a compile error.
10
10
  * cx.raw(...) — any class string, returned untouched. The declared way to
11
11
  * step outside the system.
12
+ * cx.dynamic(name) — one atom chosen at run time, for a name the Vite plugin's build
13
+ * cannot read (listed in the plugin's `keep` option).
12
14
  *
13
- * Both accept falsy arguments (undefined, null, false) and filter them out, so
14
- * conditional classes work the same on either side. Both return a
15
- * space-separated class string for className={}.
15
+ * cx() and cx.raw() accept falsy arguments (undefined, null, false) and filter
16
+ * them out, so conditional classes work the same on either side. Both return a
17
+ * space-separated class string for className={}. cx.dynamic() takes one atom
18
+ * name, or a falsy value, and returns that atom's class, or an empty string.
16
19
  *
17
20
  * Validation, not just autocomplete:
18
21
  * cx() takes AtomName, so a typo'd name ('interactve'), a name typed from
@@ -40,6 +43,14 @@ import { AtomName } from './atoms.js';
40
43
  * An atom name held in a variable needs a literal type. `const n = 'flex'`
41
44
  * is one; a let binding, an array element or an object property widens to
42
45
  * string, so annotate it with the exported AtomName type (or `as const`).
46
+ * Under the Vite plugin the build reads the value as well as the type, and
47
+ * only a value it can list builds. It can list a string literal, null,
48
+ * undefined and false; a condition choosing between values it can list;
49
+ * and a binding in the same file that is never assigned again (a const,
50
+ * say) and is bound to a value it can list, another such binding included.
51
+ * An array element or an object property fails the build whatever its
52
+ * type. To choose by a value, call cx() once per case and pick between the
53
+ * results: ({ row: cx('flex'), grid: cx('grid') })[variant].
43
54
  *
44
55
  * Why cx.raw() never maps:
45
56
  * cx() resolves an atom name to that atom's global class, so cx('container')
@@ -67,6 +78,14 @@ type Falsy = false | null | undefined;
67
78
  interface Cx {
68
79
  (...args: (AtomName | Falsy)[]): string;
69
80
  raw: (...args: (Falsy | string)[]) => string;
81
+ /**
82
+ * One atom chosen at run time, for a name the build cannot read. Under the Vite plugin the
83
+ * name must be one the plugin ships: list every atom it can take in `keep` (or, for a package,
84
+ * in `keepFor`). Without the plugin it maps through every atom, like `cx()`, except that a
85
+ * name that is no atom returns `''` instead of passing through, so it never carries a class
86
+ * from outside Nave.
87
+ */
88
+ dynamic: (name: AtomName | Falsy) => string;
70
89
  }
71
90
  declare const cx: Cx;
72
91
 
package/dist/cx.js CHANGED
@@ -1,9 +1,27 @@
1
- import { atomClassMap } from './chunk-BVON7XKC.js';
1
+ import { atomClassMap } from './chunk-GZL2VW7C.js';
2
2
 
3
3
  // src/cx.ts
4
+ var decodedKeep = /* @__PURE__ */ new Map();
5
+ function decodedMap(text) {
6
+ let map = decodedKeep.get(text);
7
+ if (map === void 0) {
8
+ map = JSON.parse(text);
9
+ decodedKeep.set(text, map);
10
+ }
11
+ return map;
12
+ }
4
13
  var cx = (...args) => args.filter(Boolean).map(
5
14
  (arg) => Object.hasOwn(atomClassMap, arg) ? atomClassMap[arg] : arg
6
15
  ).join(" ");
7
16
  cx.raw = (...args) => args.filter(Boolean).join(" ");
17
+ cx.dynamic = (name) => {
18
+ if (!name) return "";
19
+ if (typeof __NAVE_KEEP_CLASSES__ === "string") {
20
+ const decoded = decodedMap(__NAVE_KEEP_CLASSES__);
21
+ return Object.hasOwn(decoded, name) ? decoded[name] ?? "" : "";
22
+ }
23
+ const map = typeof __NAVE_KEEP_CLASSES__ === "undefined" ? atomClassMap : __NAVE_KEEP_CLASSES__;
24
+ return Object.hasOwn(map, name) ? map[name] : "";
25
+ };
8
26
 
9
27
  export { cx };
@@ -0,0 +1,51 @@
1
+ import { AtomDefinition } from './atoms.js';
2
+ import './atom-names-2OreuBx9.js';
3
+
4
+ interface NaveLightningOptions {
5
+ /**
6
+ * Consumer-defined atoms merged with Nave built-in atoms. Consumer atoms win on name collision.
7
+ * These atoms resolve via @nave only. No global class. Not available in cx().
8
+ *
9
+ * An object only: `expand` is synchronous, for `transform()`, so it cannot load a module.
10
+ * Import your atoms into the script that calls `navePlugin()`.
11
+ */
12
+ extend?: Record<string, AtomDefinition>;
13
+ /**
14
+ * Behaviour on a problem in a @nave directive: an unknown atom name, no atom named at all,
15
+ * anything but a name between the names (a comma, a string), a {} block, or a place the
16
+ * directive cannot expand. Under 'error', every such problem in one stylesheet is reported in
17
+ * one thrown error.
18
+ * 'warn' — print each with `console.warn` and skip it
19
+ * 'error' — throw (default)
20
+ * 'ignore' — silently skip
21
+ */
22
+ onUnknown?: 'warn' | 'error' | 'ignore';
23
+ }
24
+ /**
25
+ * What `navePlugin()` returns: `expand` for `transform()`, and `resolver` for `bundleAsync()`.
26
+ */
27
+ interface NaveLightningAdapter {
28
+ /**
29
+ * Expands the directives of `code` (the stylesheet's text, or its bytes) and returns the result
30
+ * as bytes with its source map, ready to pass to `transform()` as `code` and `inputSourceMap`.
31
+ * `filename` is what a message names.
32
+ */
33
+ expand(code: string | Uint8Array, filename: string): {
34
+ code: Uint8Array;
35
+ map: string;
36
+ };
37
+ /**
38
+ * For `bundleAsync()`: `read` reads a file and returns its expanded text. It returns no source
39
+ * map, and the inserted text adds no line breaks, so line numbers hold. A leading byte order
40
+ * mark is dropped, since the file is joined into a bundle, where one would sit mid-way.
41
+ */
42
+ resolver: {
43
+ read(filePath: string): string;
44
+ };
45
+ }
46
+ /**
47
+ * The Lightning CSS adapter: `expand` for `transform()` and `resolver` for `bundleAsync()`.
48
+ */
49
+ declare function navePlugin(options?: NaveLightningOptions): NaveLightningAdapter;
50
+
51
+ export { type NaveLightningAdapter, type NaveLightningOptions, navePlugin as default, navePlugin };
@@ -0,0 +1,44 @@
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';
5
+ import './chunk-SYNQJNVB.js';
6
+ import './chunk-CUL5PLPI.js';
7
+ import './chunk-GZL2VW7C.js';
8
+ import { readFileSync } from 'fs';
9
+
10
+ function report(diagnostics, file, extend, onUnknown) {
11
+ if (onUnknown === "ignore" || diagnostics.length === 0) return;
12
+ if (onUnknown === "warn") {
13
+ for (const text of warningTexts(diagnostics, file, extend)) console.warn(text);
14
+ return;
15
+ }
16
+ throw new Error(foldedText(diagnostics, file, extend));
17
+ }
18
+ function navePlugin(options = {}) {
19
+ const onUnknown = options.onUnknown ?? "error";
20
+ const extend = snapshotExtendMap(options.extend ?? {});
21
+ validateExtendAtomsHostFree(extend);
22
+ const expandToText = (text, file) => {
23
+ const result = expandText(text, { extend, onUnknown, from: file });
24
+ report(result.diagnostics, file, extend, onUnknown);
25
+ return result;
26
+ };
27
+ return {
28
+ expand(code, filename) {
29
+ const raw = typeof code === "string" ? code : new TextDecoder("utf-8", { ignoreBOM: true }).decode(code);
30
+ const { bom, text } = splitByteOrderMark(raw);
31
+ const { css, map } = expandToText(text, filename);
32
+ return { code: new TextEncoder().encode(bom + css), map };
33
+ },
34
+ resolver: {
35
+ read: (filePath) => {
36
+ const { text } = splitByteOrderMark(readFileSync(filePath, "utf8"));
37
+ return expandToText(text, filePath).css;
38
+ }
39
+ }
40
+ };
41
+ }
42
+ var lightningcss_default = navePlugin;
43
+
44
+ export { lightningcss_default as default, navePlugin };
@@ -12,13 +12,13 @@
12
12
  * WHAT YOU MUST SUPPLY. This entry ships Nave's reset and atomic layers and
13
13
  * imports no token values at all, so every '--nave-' custom property those
14
14
  * two layers read has to come from your own stylesheet, not only the colours.
15
- * A property you do not define is not an error: the declaration that reads it is
16
- * discarded, and the property does NOT fall back to whatever Nave's earlier rules
17
- * set for it, so the element silently loses that piece of styling.
18
- * To pick one that matters, the focus outline on '.nave-focus-ring' is built
19
- * from '--nave-border-width-focus', so a token layer that defines every colour
20
- * and not that width leaves an element carrying that class with no visible
21
- * outline when it is focused.
15
+ * A property you do not define is not an error: a declaration that reads it with
16
+ * no fallback is discarded, and the property does NOT fall back to whatever Nave's
17
+ * earlier rules set for it, so the element silently loses that piece of styling.
18
+ * To pick one, '.nave-border' is a single 'border' shorthand built from
19
+ * '--nave-border-width-sm' and '--nave-color-border-default', so a token layer
20
+ * that defines every width and not that colour leaves an element carrying that
21
+ * class with no border from it at all, not a border in some other colour.
22
22
  *
23
23
  * Nave ships a contract manifest listing the custom properties it requires
24
24
  * ('@navecss/tokens/core-contract'). The manifest is generated by scanning
@@ -0,0 +1 @@
1
+ module.exports = require('./postcss.js').navePlugin
@@ -0,0 +1,2 @@
1
+ import { navePlugin } from './postcss.js'
2
+ export = navePlugin
package/dist/postcss.d.ts CHANGED
@@ -1,14 +1,17 @@
1
1
  import { Plugin } from 'postcss';
2
2
  import { AtomDefinition } from './atoms.js';
3
+ import './atom-names-2OreuBx9.js';
3
4
 
4
5
  /**
5
6
  * Nave PostCSS plugin — resolves @nave directives.
6
7
  *
7
- * Inlines atomic utility declarations at build time.
8
- * Pseudo rules, @media and @container blocks are emitted as native CSS
9
- * nesting inside the parent rule (`&:focus-visible { … }`), never hoisted
10
- * out as sibling rules. Browser floor: Baseline 2024.
11
- * See https://github.com/navecss/navecss/blob/main/docs/04-adr/0001-native-css-nesting.md
8
+ * A thin adapter over the host-free directive core (`src/directive/`):
9
+ * `plan()` decides what a directive expands to and where it goes, this
10
+ * file only wires the PostCSS plugin lifecycle to `handleAtRule()`
11
+ * (`postcss-at-rule.ts`), which walks the AST to answer `plan()`'s three
12
+ * facts and splices its result back in as PostCSS nodes. Setup, options and
13
+ * observable behaviour are unchanged except where this package's CHANGELOG
14
+ * says so.
12
15
  *
13
16
  * Setup:
14
17
  * import { navePlugin } from '@navecss/core/postcss'
@@ -16,12 +19,12 @@ import { AtomDefinition } from './atoms.js';
16
19
  * navePlugin({ extend: myAtoms }) // Nave + consumer atoms
17
20
  * navePlugin({ onUnknown: 'warn' }) // Log and skip instead of failing the build
18
21
  *
19
- * `extend` is trusted, consumer-authored code, run at build time in the same file that could
20
- * already run arbitrary JavaScript — it is not sanitised input. `validateExtendAtoms` still
21
- * parses every declaration, pseudo selector key, and media/container condition an atom carries,
22
- * and rejects any that does not parse as exactly that one construct, since a typo there is
23
- * otherwise silent CSS injection into the generated output rather than a build error at the
24
- * point of the mistake.
22
+ * `extend` is trusted, consumer-authored code (your build config, or the module a path names),
23
+ * which can already run arbitrary JavaScript at build time: it is not sanitised input. Every
24
+ * declaration, pseudo selector key, and media/container condition an atom carries is still
25
+ * parsed, and one that does not parse as exactly that one construct is refused, since a typo
26
+ * there is otherwise silent CSS injection into the generated output rather than a build error
27
+ * at the point of the mistake.
25
28
  *
26
29
  * Consumer atoms:
27
30
  * import type { AtomDefinition } from '@navecss/core/postcss'
@@ -52,11 +55,23 @@ interface NavePluginOptions {
52
55
  * Consumer-defined atoms merged with Nave built-in atoms.
53
56
  * Consumer atoms win on name collision — your system owns its vocabulary.
54
57
  * These atoms resolve via @nave only. No global class. Not available in cx().
58
+ *
59
+ * A string is a path to a module instead, resolved against `process.cwd()`
60
+ * at construction (a path that names no file throws right there, naming
61
+ * the path and the directory; a package name is not looked up), then
62
+ * loaded for its default export on every run and re-read whenever the
63
+ * file's own bytes change (the modules it imports are not). Pass a
64
+ * path when your host's build cache needs to see it as a dependency: an
65
+ * inline object is invisible to a cached host's own cache key. A path
66
+ * makes the plugin async: use `process(css).then(cb)`, not the sync `.css`
67
+ * getter.
55
68
  */
56
- extend?: Record<string, AtomDefinition>;
69
+ extend?: Record<string, AtomDefinition> | string;
57
70
  /**
58
- * Behaviour on an unknown atom name, or a @nave directive that names no
59
- * atom at all.
71
+ * Behaviour on a problem in a @nave directive: an unknown atom name, no
72
+ * atom named at all, anything but a name between the names (a comma, a
73
+ * string), a {} block, or a place the directive cannot expand. Under
74
+ * 'error', every such problem in one stylesheet is reported in one error.
60
75
  * 'warn' — log and skip
61
76
  * 'error' — throw, failing the build (default)
62
77
  * 'ignore' — silently skip
@@ -68,4 +83,4 @@ declare const navePlugin: {
68
83
  postcss: boolean;
69
84
  };
70
85
 
71
- export { AtomDefinition, type NavePluginOptions, navePlugin };
86
+ export { AtomDefinition, type NavePluginOptions, navePlugin as default, navePlugin };