@navecss/core 0.1.1 → 0.3.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/ATOMS.md +23 -23
- package/CONSUMER-ATOMS.md +41 -8
- package/LICENSE +36 -0
- package/README.md +670 -53
- package/dist/atomic.css +3 -1
- package/dist/atoms.d.ts +8 -2
- package/dist/atoms.js +1 -1
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +451 -0
- package/dist/check.d.ts +31 -0
- package/dist/check.js +11 -0
- package/dist/chunk-IZQLFUQU.js +30 -0
- package/dist/{chunk-BVON7XKC.js → chunk-LIUB4EES.js} +7 -1
- package/dist/chunk-NFS2REPQ.js +747 -0
- package/dist/chunk-NKUZNRF4.js +42 -0
- package/dist/chunk-R6JJZGXC.js +94 -0
- package/dist/chunk-S7DZUVV3.js +1386 -0
- package/dist/chunk-UANF3FGO.js +115 -0
- package/dist/chunk-XQQCDUV6.js +152 -0
- package/dist/cx.d.ts +27 -7
- package/dist/cx.js +6 -1
- package/dist/lightningcss.d.ts +50 -0
- package/dist/lightningcss.js +42 -0
- package/dist/no-tokens.css +7 -7
- package/dist/postcss.cjs +1 -0
- package/dist/postcss.d.cts +2 -0
- package/dist/postcss.d.ts +30 -9
- package/dist/postcss.js +263 -185
- package/dist/standalone.css +1137 -0
- package/dist/vite.d.ts +430 -0
- package/dist/vite.js +5212 -0
- package/nave.css-data.json +12 -0
- package/package.json +55 -18
- package/skills/navecss/SKILL.md +317 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { createPositionFinder, tokenize, matchBrackets, readItem, isNaveAtKeyword } from './chunk-NFS2REPQ.js';
|
|
2
|
+
|
|
3
|
+
// src/directive/find-survivors.ts
|
|
4
|
+
var NATURAL_STOP_CHARS = /* @__PURE__ */ new Set(["\n", ";", "}"]);
|
|
5
|
+
function naturalTextAfter(css, offset) {
|
|
6
|
+
let end = css.length;
|
|
7
|
+
for (let i = offset; i < css.length; i++) {
|
|
8
|
+
if (NATURAL_STOP_CHARS.has(css[i])) {
|
|
9
|
+
end = i;
|
|
10
|
+
break;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return css.slice(offset, end).trim();
|
|
14
|
+
}
|
|
15
|
+
function isStructural(token) {
|
|
16
|
+
return token.type !== "whitespace-token" && token.type !== "comment";
|
|
17
|
+
}
|
|
18
|
+
function skipInert(tokens, i, limit) {
|
|
19
|
+
let j = i;
|
|
20
|
+
while (j < limit && !isStructural(tokens[j])) j++;
|
|
21
|
+
return j;
|
|
22
|
+
}
|
|
23
|
+
var Scan = class {
|
|
24
|
+
closerFor;
|
|
25
|
+
css;
|
|
26
|
+
survivors = [];
|
|
27
|
+
tokens;
|
|
28
|
+
constructor(css) {
|
|
29
|
+
this.css = css;
|
|
30
|
+
this.tokens = tokenize(css);
|
|
31
|
+
this.closerFor = matchBrackets(this.tokens);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
function scanForNaveTokens(scan, start, end, selector) {
|
|
35
|
+
for (let i = start; i < end; i++) {
|
|
36
|
+
const token = scan.tokens[i];
|
|
37
|
+
if (isNaveAtKeyword(token))
|
|
38
|
+
scan.survivors.push({
|
|
39
|
+
offset: token.startIndex,
|
|
40
|
+
text: naturalTextAfter(scan.css, token.startIndex),
|
|
41
|
+
selector
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function ruleSelector(scan, item) {
|
|
46
|
+
const braceIndex = item.blockStart - 1;
|
|
47
|
+
return scan.css.slice(scan.tokens[item.start].startIndex, scan.tokens[braceIndex].startIndex).trim();
|
|
48
|
+
}
|
|
49
|
+
function childFrameFor(scan, item, selector) {
|
|
50
|
+
if (item.blockStart === void 0 || item.blockEnd === void 0) return void 0;
|
|
51
|
+
const childSelector = item.kind === "rule" ? ruleSelector(scan, item) : selector;
|
|
52
|
+
return {
|
|
53
|
+
i: skipInert(scan.tokens, item.blockStart, item.blockEnd),
|
|
54
|
+
limit: item.blockEnd,
|
|
55
|
+
selector: childSelector
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function preludeEnd(item) {
|
|
59
|
+
return item.blockStart === void 0 ? item.end : item.blockStart - 1;
|
|
60
|
+
}
|
|
61
|
+
function visitItem(scan, item, selector) {
|
|
62
|
+
switch (item.kind) {
|
|
63
|
+
case "at-rule":
|
|
64
|
+
case "rule": {
|
|
65
|
+
scanForNaveTokens(scan, item.start, preludeEnd(item), selector);
|
|
66
|
+
return childFrameFor(scan, item, selector);
|
|
67
|
+
}
|
|
68
|
+
case "declaration":
|
|
69
|
+
case "invalid": {
|
|
70
|
+
scanForNaveTokens(scan, item.start, item.end, selector);
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function walkBlock(scan, start, limit, selector) {
|
|
76
|
+
const stack = [{ i: skipInert(scan.tokens, start, limit), limit, selector }];
|
|
77
|
+
while (stack.length > 0) {
|
|
78
|
+
const frame = stack.at(-1);
|
|
79
|
+
if (frame.i >= frame.limit) {
|
|
80
|
+
stack.pop();
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const item = readItem(scan.tokens, frame.i, frame.limit, scan.closerFor);
|
|
84
|
+
frame.i = skipInert(scan.tokens, item.end, frame.limit);
|
|
85
|
+
const child = visitItem(scan, item, frame.selector);
|
|
86
|
+
if (child) stack.push(child);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function findSurvivors(css) {
|
|
90
|
+
const scan = new Scan(css);
|
|
91
|
+
walkBlock(scan, 0, scan.tokens.length, void 0);
|
|
92
|
+
return scan.survivors;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/directive/findings.ts
|
|
96
|
+
function findingsInText(file, raw) {
|
|
97
|
+
const css = raw.startsWith("\uFEFF") ? raw.slice(1) : raw;
|
|
98
|
+
const positionAt = createPositionFinder(css);
|
|
99
|
+
return findSurvivors(css).map((survivor) => {
|
|
100
|
+
const position = positionAt(survivor.offset);
|
|
101
|
+
return {
|
|
102
|
+
file,
|
|
103
|
+
line: position.line,
|
|
104
|
+
column: position.column + 1,
|
|
105
|
+
text: survivor.text,
|
|
106
|
+
...survivor.selector !== void 0 && { selector: survivor.selector }
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function formatFinding(finding) {
|
|
111
|
+
const where = finding.selector === void 0 ? "" : ` (in ${finding.selector.replaceAll(/\s+/g, " ")})`;
|
|
112
|
+
return `${finding.file}:${finding.line}:${finding.column}: ${finding.text}${where}`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export { findingsInText, formatFinding };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { findingsInText } from './chunk-UANF3FGO.js';
|
|
2
|
+
import { readFile, stat, realpath, readdir } from 'fs/promises';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
var NotAStylesheetPathError = class extends Error {
|
|
6
|
+
path;
|
|
7
|
+
constructor(path2) {
|
|
8
|
+
super(`${path2} is not a stylesheet file`);
|
|
9
|
+
this.path = path2;
|
|
10
|
+
this.name = "NotAStylesheetPathError";
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
function hasCssExtension(name) {
|
|
14
|
+
return path.extname(name).toLowerCase() === ".css";
|
|
15
|
+
}
|
|
16
|
+
async function entryKind(entry, entryPath, state) {
|
|
17
|
+
if (entry.isDirectory()) return "directory";
|
|
18
|
+
if (entry.isFile()) return hasCssExtension(entry.name) ? "css-file" : "skip";
|
|
19
|
+
if (!entry.isSymbolicLink()) return "skip";
|
|
20
|
+
let targetStats;
|
|
21
|
+
try {
|
|
22
|
+
targetStats = await stat(entryPath);
|
|
23
|
+
} catch {
|
|
24
|
+
if (hasCssExtension(entry.name)) state.unreadablePaths.push(entryPath);
|
|
25
|
+
return "skip";
|
|
26
|
+
}
|
|
27
|
+
if (targetStats.isDirectory()) return "directory";
|
|
28
|
+
if (targetStats.isFile()) return hasCssExtension(entry.name) ? "css-file" : "skip";
|
|
29
|
+
return "skip";
|
|
30
|
+
}
|
|
31
|
+
async function listSubdirectory(entryPath, state) {
|
|
32
|
+
let canonicalPath;
|
|
33
|
+
try {
|
|
34
|
+
canonicalPath = await realpath(entryPath);
|
|
35
|
+
} catch {
|
|
36
|
+
state.unreadablePaths.push(entryPath);
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
if (state.visitedDirs.has(canonicalPath)) return [];
|
|
40
|
+
state.visitedDirs.add(canonicalPath);
|
|
41
|
+
return listDirectory(entryPath, state);
|
|
42
|
+
}
|
|
43
|
+
async function cssFileIfNew(entryPath, state) {
|
|
44
|
+
let canonicalFile;
|
|
45
|
+
try {
|
|
46
|
+
canonicalFile = await realpath(entryPath);
|
|
47
|
+
} catch {
|
|
48
|
+
state.unreadablePaths.push(entryPath);
|
|
49
|
+
return void 0;
|
|
50
|
+
}
|
|
51
|
+
if (state.reportedRealFiles.has(canonicalFile)) return void 0;
|
|
52
|
+
state.reportedRealFiles.add(canonicalFile);
|
|
53
|
+
return entryPath;
|
|
54
|
+
}
|
|
55
|
+
async function listDirectory(dir, state) {
|
|
56
|
+
let entries;
|
|
57
|
+
try {
|
|
58
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
59
|
+
} catch {
|
|
60
|
+
state.unreadablePaths.push(dir);
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
const files = [];
|
|
64
|
+
for (const entry of entries) {
|
|
65
|
+
const entryPath = path.join(dir, entry.name);
|
|
66
|
+
const kind = await entryKind(entry, entryPath, state);
|
|
67
|
+
if (kind === "directory") {
|
|
68
|
+
files.push(...await listSubdirectory(entryPath, state));
|
|
69
|
+
} else if (kind === "css-file") {
|
|
70
|
+
const file = await cssFileIfNew(entryPath, state);
|
|
71
|
+
if (file) files.push(file);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return files;
|
|
75
|
+
}
|
|
76
|
+
async function listCssFiles(source) {
|
|
77
|
+
const stats = await stat(source);
|
|
78
|
+
if (!stats.isDirectory()) {
|
|
79
|
+
if (!stats.isFile()) throw new NotAStylesheetPathError(source);
|
|
80
|
+
return { files: [source], unreadablePaths: [] };
|
|
81
|
+
}
|
|
82
|
+
const canonicalRoot = await realpath(source);
|
|
83
|
+
const state = {
|
|
84
|
+
reportedRealFiles: /* @__PURE__ */ new Set(),
|
|
85
|
+
unreadablePaths: [],
|
|
86
|
+
visitedDirs: /* @__PURE__ */ new Set([canonicalRoot])
|
|
87
|
+
};
|
|
88
|
+
const files = await listDirectory(source, state);
|
|
89
|
+
return { files, unreadablePaths: state.unreadablePaths };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/directive/check-run.ts
|
|
93
|
+
async function wasFileRead(file, findings) {
|
|
94
|
+
let raw;
|
|
95
|
+
try {
|
|
96
|
+
raw = await readFile(file, "utf8");
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
for (const finding of findingsInText(file, raw)) findings.push(finding);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
async function sourceOutcome(source) {
|
|
104
|
+
try {
|
|
105
|
+
return { kind: "listing", listing: await listCssFiles(source) };
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (error instanceof NotAStylesheetPathError) return { kind: "not-a-stylesheet" };
|
|
108
|
+
return { kind: "unreadable" };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function statusFor(hasUnreadableSource, stylesheetsRead, findingCount) {
|
|
112
|
+
if (hasUnreadableSource || stylesheetsRead === 0) return 2;
|
|
113
|
+
return findingCount > 0 ? 1 : 0;
|
|
114
|
+
}
|
|
115
|
+
async function runCheck(source) {
|
|
116
|
+
const findings = [];
|
|
117
|
+
const unreadablePaths = [];
|
|
118
|
+
const notStylesheetPaths = [];
|
|
119
|
+
let stylesheetsRead = 0;
|
|
120
|
+
let hasUnreadableSource = false;
|
|
121
|
+
for (const one of source) {
|
|
122
|
+
const outcome = await sourceOutcome(one);
|
|
123
|
+
if (outcome.kind === "unreadable") {
|
|
124
|
+
hasUnreadableSource = true;
|
|
125
|
+
unreadablePaths.push(one);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (outcome.kind === "not-a-stylesheet") {
|
|
129
|
+
hasUnreadableSource = true;
|
|
130
|
+
notStylesheetPaths.push(one);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const listing = outcome.listing;
|
|
134
|
+
if (listing.unreadablePaths.length > 0) {
|
|
135
|
+
hasUnreadableSource = true;
|
|
136
|
+
unreadablePaths.push(...listing.unreadablePaths);
|
|
137
|
+
}
|
|
138
|
+
for (const file of listing.files) {
|
|
139
|
+
const wasReadable = await wasFileRead(file, findings);
|
|
140
|
+
if (wasReadable) {
|
|
141
|
+
stylesheetsRead++;
|
|
142
|
+
} else {
|
|
143
|
+
hasUnreadableSource = true;
|
|
144
|
+
unreadablePaths.push(file);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const status = statusFor(hasUnreadableSource, stylesheetsRead, findings.length);
|
|
149
|
+
return { result: { status, findings, stylesheetsRead }, unreadablePaths, notStylesheetPaths };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export { runCheck };
|
package/dist/cx.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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')
|
|
@@ -51,10 +62,11 @@ import { AtomName } from './atoms.js';
|
|
|
51
62
|
* relative, absolute, gap, truncate, interactive.
|
|
52
63
|
*
|
|
53
64
|
* What is NOT checked:
|
|
54
|
-
* Nothing here looks at the rest of the className attribute.
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
65
|
+
* Nothing here looks at the rest of the className attribute. @navecss/eslint-plugin does:
|
|
66
|
+
* install it for an ENFORCED escape channel, reporting an undeclared literal class written
|
|
67
|
+
* in className and requiring a reason on a cx.raw() call that carries one (a class the
|
|
68
|
+
* project declares as its own passes by design). Without it, cx.raw() is only a DECLARED
|
|
69
|
+
* escape channel (greppable: `grep -r 'cx.raw'`).
|
|
58
70
|
*
|
|
59
71
|
* Note on consumer atoms:
|
|
60
72
|
* Atoms defined via navePlugin({ extend }) are @nave-directive only.
|
|
@@ -66,6 +78,14 @@ type Falsy = false | null | undefined;
|
|
|
66
78
|
interface Cx {
|
|
67
79
|
(...args: (AtomName | Falsy)[]): string;
|
|
68
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;
|
|
69
89
|
}
|
|
70
90
|
declare const cx: Cx;
|
|
71
91
|
|
package/dist/cx.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { atomClassMap } from './chunk-
|
|
1
|
+
import { atomClassMap } from './chunk-LIUB4EES.js';
|
|
2
2
|
|
|
3
3
|
// src/cx.ts
|
|
4
4
|
var cx = (...args) => args.filter(Boolean).map(
|
|
5
5
|
(arg) => Object.hasOwn(atomClassMap, arg) ? atomClassMap[arg] : arg
|
|
6
6
|
).join(" ");
|
|
7
7
|
cx.raw = (...args) => args.filter(Boolean).join(" ");
|
|
8
|
+
cx.dynamic = (name) => {
|
|
9
|
+
if (!name) return "";
|
|
10
|
+
const map = typeof __NAVE_KEEP_CLASSES__ === "undefined" ? atomClassMap : __NAVE_KEEP_CLASSES__;
|
|
11
|
+
return Object.hasOwn(map, name) ? map[name] : "";
|
|
12
|
+
};
|
|
8
13
|
|
|
9
14
|
export { cx };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { AtomDefinition } from './atoms.js';
|
|
2
|
+
|
|
3
|
+
interface NaveLightningOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Consumer-defined atoms merged with Nave built-in atoms. Consumer atoms win on name collision.
|
|
6
|
+
* These atoms resolve via @nave only. No global class. Not available in cx().
|
|
7
|
+
*
|
|
8
|
+
* An object only: `expand` is synchronous, for `transform()`, so it cannot load a module.
|
|
9
|
+
* Import your atoms into the script that calls `navePlugin()`.
|
|
10
|
+
*/
|
|
11
|
+
extend?: Record<string, AtomDefinition>;
|
|
12
|
+
/**
|
|
13
|
+
* Behaviour on a problem in a @nave directive: an unknown atom name, no atom named at all,
|
|
14
|
+
* anything but a name between the names (a comma, a string), a {} block, or a place the
|
|
15
|
+
* directive cannot expand. Under 'error', every such problem in one stylesheet is reported in
|
|
16
|
+
* one thrown error.
|
|
17
|
+
* 'warn' — print each with `console.warn` and skip it
|
|
18
|
+
* 'error' — throw (default)
|
|
19
|
+
* 'ignore' — silently skip
|
|
20
|
+
*/
|
|
21
|
+
onUnknown?: 'warn' | 'error' | 'ignore';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* What `navePlugin()` returns: `expand` for `transform()`, and `resolver` for `bundleAsync()`.
|
|
25
|
+
*/
|
|
26
|
+
interface NaveLightningAdapter {
|
|
27
|
+
/**
|
|
28
|
+
* Expands the directives of `code` (the stylesheet's text, or its bytes) and returns the result
|
|
29
|
+
* as bytes with its source map, ready to pass to `transform()` as `code` and `inputSourceMap`.
|
|
30
|
+
* `filename` is what a message names.
|
|
31
|
+
*/
|
|
32
|
+
expand(code: string | Uint8Array, filename: string): {
|
|
33
|
+
code: Uint8Array;
|
|
34
|
+
map: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* For `bundleAsync()`: `read` reads a file and returns its expanded text. It returns no source
|
|
38
|
+
* map, and the inserted text adds no line breaks, so line numbers hold. A leading byte order
|
|
39
|
+
* mark is dropped, since the file is joined into a bundle, where one would sit mid-way.
|
|
40
|
+
*/
|
|
41
|
+
resolver: {
|
|
42
|
+
read(filePath: string): string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The Lightning CSS adapter: `expand` for `transform()` and `resolver` for `bundleAsync()`.
|
|
47
|
+
*/
|
|
48
|
+
declare function navePlugin(options?: NaveLightningOptions): NaveLightningAdapter;
|
|
49
|
+
|
|
50
|
+
export { type NaveLightningAdapter, type NaveLightningOptions, navePlugin as default, navePlugin };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { splitByteOrderMark, warningTexts, foldedText } from './chunk-IZQLFUQU.js';
|
|
2
|
+
import { validateExtendAtomsHostFree } from './chunk-R6JJZGXC.js';
|
|
3
|
+
import { snapshotExtendMap, expandText } from './chunk-S7DZUVV3.js';
|
|
4
|
+
import './chunk-NFS2REPQ.js';
|
|
5
|
+
import './chunk-LIUB4EES.js';
|
|
6
|
+
import { readFileSync } from 'fs';
|
|
7
|
+
|
|
8
|
+
function report(diagnostics, file, extend, onUnknown) {
|
|
9
|
+
if (onUnknown === "ignore" || diagnostics.length === 0) return;
|
|
10
|
+
if (onUnknown === "warn") {
|
|
11
|
+
for (const text of warningTexts(diagnostics, file, extend)) console.warn(text);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
throw new Error(foldedText(diagnostics, file, extend));
|
|
15
|
+
}
|
|
16
|
+
function navePlugin(options = {}) {
|
|
17
|
+
const onUnknown = options.onUnknown ?? "error";
|
|
18
|
+
const extend = snapshotExtendMap(options.extend ?? {});
|
|
19
|
+
validateExtendAtomsHostFree(extend);
|
|
20
|
+
const expandToText = (text, file) => {
|
|
21
|
+
const result = expandText(text, { extend, onUnknown, from: file });
|
|
22
|
+
report(result.diagnostics, file, extend, onUnknown);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
expand(code, filename) {
|
|
27
|
+
const raw = typeof code === "string" ? code : new TextDecoder("utf-8", { ignoreBOM: true }).decode(code);
|
|
28
|
+
const { bom, text } = splitByteOrderMark(raw);
|
|
29
|
+
const { css, map } = expandToText(text, filename);
|
|
30
|
+
return { code: new TextEncoder().encode(bom + css), map };
|
|
31
|
+
},
|
|
32
|
+
resolver: {
|
|
33
|
+
read: (filePath) => {
|
|
34
|
+
const { text } = splitByteOrderMark(readFileSync(filePath, "utf8"));
|
|
35
|
+
return expandToText(text, filePath).css;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
var lightningcss_default = navePlugin;
|
|
41
|
+
|
|
42
|
+
export { lightningcss_default as default, navePlugin };
|
package/dist/no-tokens.css
CHANGED
|
@@ -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:
|
|
16
|
-
* discarded, and the property does NOT fall back to whatever Nave's
|
|
17
|
-
* set for it, so the element silently loses that piece of styling.
|
|
18
|
-
* To pick one
|
|
19
|
-
*
|
|
20
|
-
* and not that
|
|
21
|
-
*
|
|
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
|
package/dist/postcss.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./postcss.js').navePlugin
|
package/dist/postcss.d.ts
CHANGED
|
@@ -4,11 +4,13 @@ import { AtomDefinition } from './atoms.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* Nave PostCSS plugin — resolves @nave directives.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* A thin adapter over the host-free directive core (`src/directive/`):
|
|
8
|
+
* `plan()` decides what a directive expands to and where it goes, this
|
|
9
|
+
* file only wires the PostCSS plugin lifecycle to `handleAtRule()`
|
|
10
|
+
* (`postcss-at-rule.ts`), which walks the AST to answer `plan()`'s three
|
|
11
|
+
* facts and splices its result back in as PostCSS nodes. Setup, options and
|
|
12
|
+
* observable behaviour are unchanged except where this package's CHANGELOG
|
|
13
|
+
* says so.
|
|
12
14
|
*
|
|
13
15
|
* Setup:
|
|
14
16
|
* import { navePlugin } from '@navecss/core/postcss'
|
|
@@ -16,6 +18,13 @@ import { AtomDefinition } from './atoms.js';
|
|
|
16
18
|
* navePlugin({ extend: myAtoms }) // Nave + consumer atoms
|
|
17
19
|
* navePlugin({ onUnknown: 'warn' }) // Log and skip instead of failing the build
|
|
18
20
|
*
|
|
21
|
+
* `extend` is trusted, consumer-authored code (your build config, or the module a path names),
|
|
22
|
+
* which can already run arbitrary JavaScript at build time: it is not sanitised input. Every
|
|
23
|
+
* declaration, pseudo selector key, and media/container condition an atom carries is still
|
|
24
|
+
* parsed, and one that does not parse as exactly that one construct is refused, since a typo
|
|
25
|
+
* there is otherwise silent CSS injection into the generated output rather than a build error
|
|
26
|
+
* at the point of the mistake.
|
|
27
|
+
*
|
|
19
28
|
* Consumer atoms:
|
|
20
29
|
* import type { AtomDefinition } from '@navecss/core/postcss'
|
|
21
30
|
* import { media } from '@navecss/tokens/breakpoints'
|
|
@@ -45,11 +54,23 @@ interface NavePluginOptions {
|
|
|
45
54
|
* Consumer-defined atoms merged with Nave built-in atoms.
|
|
46
55
|
* Consumer atoms win on name collision — your system owns its vocabulary.
|
|
47
56
|
* These atoms resolve via @nave only. No global class. Not available in cx().
|
|
57
|
+
*
|
|
58
|
+
* A string is a path to a module instead, resolved against `process.cwd()`
|
|
59
|
+
* at construction (a path that names no file throws right there, naming
|
|
60
|
+
* the path and the directory; a package name is not looked up), then
|
|
61
|
+
* loaded for its default export on every run and re-read whenever the
|
|
62
|
+
* file's own bytes change (the modules it imports are not). Pass a
|
|
63
|
+
* path when your host's build cache needs to see it as a dependency: an
|
|
64
|
+
* inline object is invisible to a cached host's own cache key. A path
|
|
65
|
+
* makes the plugin async: use `process(css).then(cb)`, not the sync `.css`
|
|
66
|
+
* getter.
|
|
48
67
|
*/
|
|
49
|
-
extend?: Record<string, AtomDefinition
|
|
68
|
+
extend?: Record<string, AtomDefinition> | string;
|
|
50
69
|
/**
|
|
51
|
-
* Behaviour on
|
|
52
|
-
* atom at all
|
|
70
|
+
* Behaviour on a problem in a @nave directive: an unknown atom name, no
|
|
71
|
+
* atom named at all, anything but a name between the names (a comma, a
|
|
72
|
+
* string), a {} block, or a place the directive cannot expand. Under
|
|
73
|
+
* 'error', every such problem in one stylesheet is reported in one error.
|
|
53
74
|
* 'warn' — log and skip
|
|
54
75
|
* 'error' — throw, failing the build (default)
|
|
55
76
|
* 'ignore' — silently skip
|
|
@@ -61,4 +82,4 @@ declare const navePlugin: {
|
|
|
61
82
|
postcss: boolean;
|
|
62
83
|
};
|
|
63
84
|
|
|
64
|
-
export { AtomDefinition, type NavePluginOptions, navePlugin };
|
|
85
|
+
export { AtomDefinition, type NavePluginOptions, navePlugin as default, navePlugin };
|