@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
package/dist/atomic.css
CHANGED
|
@@ -191,7 +191,9 @@
|
|
|
191
191
|
outline: none;
|
|
192
192
|
|
|
193
193
|
&:focus-visible {
|
|
194
|
-
outline:
|
|
194
|
+
outline-style: solid;
|
|
195
|
+
outline-width: var(--nave-border-width-focus, 2px);
|
|
196
|
+
outline-color: var(--nave-color-border-focus, currentColor);
|
|
195
197
|
outline-offset: 2px;
|
|
196
198
|
}
|
|
197
199
|
}
|
package/dist/atoms.d.ts
CHANGED
|
@@ -208,6 +208,10 @@ declare const atoms: {
|
|
|
208
208
|
/**
|
|
209
209
|
* focusRing — keyboard focus indicator.
|
|
210
210
|
* :focus-visible — keyboard users see it, mouse users do not.
|
|
211
|
+
*
|
|
212
|
+
* Longhands with fallbacks, not an `outline` shorthand, which computes to `none` as a whole when
|
|
213
|
+
* a `var()` in it has no usable value: no token layer, a missing property, or a wrong-typed value
|
|
214
|
+
* in an unregistered property. The fallbacks are 2px and `currentColor`, never a literal colour.
|
|
211
215
|
*/
|
|
212
216
|
readonly focusRing: {
|
|
213
217
|
readonly declarations: {
|
|
@@ -215,7 +219,9 @@ declare const atoms: {
|
|
|
215
219
|
};
|
|
216
220
|
readonly pseudos: {
|
|
217
221
|
readonly ':focus-visible': {
|
|
218
|
-
readonly outline: "
|
|
222
|
+
readonly 'outline-style': "solid";
|
|
223
|
+
readonly 'outline-width': "var(--nave-border-width-focus, 2px)";
|
|
224
|
+
readonly 'outline-color': "var(--nave-color-border-focus, currentColor)";
|
|
219
225
|
readonly 'outline-offset': "2px";
|
|
220
226
|
};
|
|
221
227
|
};
|
|
@@ -405,7 +411,7 @@ type AtomName = keyof typeof atoms;
|
|
|
405
411
|
declare const toClassName: (name: AtomName) => string;
|
|
406
412
|
/**
|
|
407
413
|
* Map of atom name → global CSS class name.
|
|
408
|
-
* Used by cx() and the
|
|
414
|
+
* Used by cx() and the directive core's diagnostic hints.
|
|
409
415
|
* Only covers Nave built-in atoms — consumer atoms are @nave-directive only.
|
|
410
416
|
*/
|
|
411
417
|
declare const atomClassMap: Record<AtomName, string>;
|
package/dist/atoms.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { atomClassMap, atoms, toClassName } from './chunk-
|
|
1
|
+
export { atomClassMap, atoms, toClassName } from './chunk-LIUB4EES.js';
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runCheck } from './chunk-XQQCDUV6.js';
|
|
3
|
+
import { splitByteOrderMark, foldedText } from './chunk-IZQLFUQU.js';
|
|
4
|
+
import { formatFinding } from './chunk-UANF3FGO.js';
|
|
5
|
+
import { validateExtendAtomsHostFree } from './chunk-R6JJZGXC.js';
|
|
6
|
+
import { resolveExtendSpecifier, applyExtendModule } from './chunk-NKUZNRF4.js';
|
|
7
|
+
import { snapshotExtendMap, expandText } from './chunk-S7DZUVV3.js';
|
|
8
|
+
import { createPositionFinder, tokenize, atKeywordName } from './chunk-NFS2REPQ.js';
|
|
9
|
+
import './chunk-LIUB4EES.js';
|
|
10
|
+
import { parseArgs } from 'util';
|
|
11
|
+
import { realpathSync, watch, existsSync, statSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
|
|
12
|
+
import path2 from 'path';
|
|
13
|
+
|
|
14
|
+
// src/bin-usage.ts
|
|
15
|
+
var USAGE = `Usage:
|
|
16
|
+
navecss-core check --source=<file-or-dir> [--source=<file-or-dir> ...]
|
|
17
|
+
navecss-core expand --source=<file> --out=<file> [--source=<file> --out=<file> ...] [--extend=<module>] [--watch]`;
|
|
18
|
+
function readValues(args) {
|
|
19
|
+
try {
|
|
20
|
+
const { values, positionals } = parseArgs({
|
|
21
|
+
args: [...args],
|
|
22
|
+
options: {
|
|
23
|
+
extend: { type: "string", multiple: true },
|
|
24
|
+
out: { type: "string", multiple: true },
|
|
25
|
+
source: { type: "string", multiple: true },
|
|
26
|
+
watch: { type: "boolean" }
|
|
27
|
+
},
|
|
28
|
+
strict: true,
|
|
29
|
+
allowPositionals: true
|
|
30
|
+
});
|
|
31
|
+
if (positionals.length > 0) {
|
|
32
|
+
return `expand does not take a positional argument: ${positionals[0]}`;
|
|
33
|
+
}
|
|
34
|
+
if ((values.extend?.length ?? 0) > 1) return "expand takes one --extend.";
|
|
35
|
+
return {
|
|
36
|
+
extend: values.extend?.[0],
|
|
37
|
+
out: values.out ?? [],
|
|
38
|
+
source: values.source ?? [],
|
|
39
|
+
watch: values.watch === true
|
|
40
|
+
};
|
|
41
|
+
} catch (error) {
|
|
42
|
+
return `expand: ${error.message.split("\n", 1)[0]}`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function isSameFile(first, second) {
|
|
46
|
+
if (path2.resolve(first) === path2.resolve(second)) return true;
|
|
47
|
+
if (!existsSync(first) || !existsSync(second)) return false;
|
|
48
|
+
const a = statSync(first);
|
|
49
|
+
const b = statSync(second);
|
|
50
|
+
return a.dev === b.dev && a.ino === b.ino;
|
|
51
|
+
}
|
|
52
|
+
function problemWith(raw) {
|
|
53
|
+
if (raw.source.length === 0) return "expand requires at least one --source=<file>.";
|
|
54
|
+
if (raw.out.length === 0) return "expand requires --out=<file> for each --source.";
|
|
55
|
+
if (raw.source.length !== raw.out.length) {
|
|
56
|
+
return `expand takes one --out for each --source, matched by order: got ${raw.source.length} --source and ${raw.out.length} --out.`;
|
|
57
|
+
}
|
|
58
|
+
if ([...raw.source, ...raw.out, raw.extend ?? "."].includes("")) {
|
|
59
|
+
return "expand: a --source, --out or --extend value is empty.";
|
|
60
|
+
}
|
|
61
|
+
const outs = raw.out.map((file) => path2.resolve(file));
|
|
62
|
+
const inputs = [
|
|
63
|
+
...raw.source.map((file) => ({ file, name: "a --source" })),
|
|
64
|
+
...raw.extend === void 0 ? [] : [{ file: raw.extend, name: "the --extend module" }]
|
|
65
|
+
];
|
|
66
|
+
for (const out of outs) {
|
|
67
|
+
const input = inputs.find(({ file }) => isSameFile(file, out));
|
|
68
|
+
if (input !== void 0) {
|
|
69
|
+
return `expand: --out ${out} is also ${input.name}; it would overwrite its own input.`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (new Set(outs).size !== outs.length)
|
|
73
|
+
return "expand: two files would be written to the same --out.";
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
function parseExpandArgs(args) {
|
|
77
|
+
const raw = readValues(args);
|
|
78
|
+
if (typeof raw === "string") return { kind: "usageError", message: raw };
|
|
79
|
+
const problem = problemWith(raw);
|
|
80
|
+
if (problem !== void 0) return { kind: "usageError", message: problem };
|
|
81
|
+
return {
|
|
82
|
+
kind: "job",
|
|
83
|
+
job: {
|
|
84
|
+
extend: raw.extend,
|
|
85
|
+
pairs: raw.source.map((source, index) => ({ source, out: raw.out[index] })),
|
|
86
|
+
watch: raw.watch
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/directive/bare-imports.ts
|
|
92
|
+
var OPENERS = /* @__PURE__ */ new Set(["(-token", "[-token", "function-token", "{-token"]);
|
|
93
|
+
var CLOSERS = /* @__PURE__ */ new Set([")-token", "]-token", "}-token"]);
|
|
94
|
+
var SCHEME = /^[a-z][a-z\d+.-]*:/i;
|
|
95
|
+
function isTrivia(token) {
|
|
96
|
+
return token.type === "whitespace-token" || token.type === "comment";
|
|
97
|
+
}
|
|
98
|
+
function specifierOf(prelude) {
|
|
99
|
+
const [first, second] = prelude.filter((token) => !isTrivia(token));
|
|
100
|
+
if (first?.type === "string-token" || first?.type === "url-token") {
|
|
101
|
+
return first.structured.value;
|
|
102
|
+
}
|
|
103
|
+
const isUrlFunction = first?.type === "function-token" && first.structured.value.toLowerCase() === "url";
|
|
104
|
+
if (isUrlFunction && second?.type === "string-token") {
|
|
105
|
+
return second.structured.value;
|
|
106
|
+
}
|
|
107
|
+
return void 0;
|
|
108
|
+
}
|
|
109
|
+
function isBare(specifier, hasFile) {
|
|
110
|
+
const isUrlForm = withoutQueryOrFragment(specifier) === "" || specifier.startsWith("/") || specifier.startsWith("./") || specifier.startsWith("../") || SCHEME.test(specifier);
|
|
111
|
+
return !isUrlForm && !hasFile(decoded(withoutQueryOrFragment(specifier)));
|
|
112
|
+
}
|
|
113
|
+
function withoutQueryOrFragment(specifier) {
|
|
114
|
+
const ends = [specifier.indexOf("?"), specifier.indexOf("#")].filter((at) => at !== -1);
|
|
115
|
+
return ends.length === 0 ? specifier : specifier.slice(0, Math.min(...ends));
|
|
116
|
+
}
|
|
117
|
+
function decoded(path4) {
|
|
118
|
+
try {
|
|
119
|
+
return decodeURIComponent(path4);
|
|
120
|
+
} catch {
|
|
121
|
+
return path4;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function endOfPrelude(tokens, from) {
|
|
125
|
+
const end = tokens.findIndex(
|
|
126
|
+
(token, index) => index >= from && (token.type === "semicolon-token" || token.type === "{-token")
|
|
127
|
+
);
|
|
128
|
+
return end === -1 ? tokens.length : end;
|
|
129
|
+
}
|
|
130
|
+
function readImport(tokens, index, hasFile) {
|
|
131
|
+
const end = endOfPrelude(tokens, index + 1);
|
|
132
|
+
const specifier = specifierOf(tokens.slice(index + 1, end));
|
|
133
|
+
if (specifier === void 0 || !isBare(specifier, hasFile)) return { diagnostic: void 0, end };
|
|
134
|
+
const lastIndex = tokens[end]?.type === "semicolon-token" ? end : end - 1;
|
|
135
|
+
const diagnostic = {
|
|
136
|
+
code: "bare-import",
|
|
137
|
+
offset: tokens[index].startIndex,
|
|
138
|
+
endOffset: tokens[lastIndex].endIndex,
|
|
139
|
+
text: specifier
|
|
140
|
+
};
|
|
141
|
+
return { diagnostic, end };
|
|
142
|
+
}
|
|
143
|
+
function findBareImports(css, hasFile) {
|
|
144
|
+
const tokens = tokenize(css);
|
|
145
|
+
const found = [];
|
|
146
|
+
let depth = 0;
|
|
147
|
+
let index = 0;
|
|
148
|
+
while (index < tokens.length) {
|
|
149
|
+
const token = tokens[index];
|
|
150
|
+
let next = index + 1;
|
|
151
|
+
if (OPENERS.has(token.type)) depth++;
|
|
152
|
+
else if (CLOSERS.has(token.type)) depth = Math.max(0, depth - 1);
|
|
153
|
+
else if (depth === 0 && token.type === "at-keyword-token" && atKeywordName(token) === "import") {
|
|
154
|
+
const read = readImport(tokens, index, hasFile);
|
|
155
|
+
if (read.diagnostic) found.push(read.diagnostic);
|
|
156
|
+
next = read.end + 1;
|
|
157
|
+
}
|
|
158
|
+
index = next;
|
|
159
|
+
}
|
|
160
|
+
return found;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/expand-pass.ts
|
|
164
|
+
function bareImportDiagnostics(css, source, out, outputs) {
|
|
165
|
+
const directory = path2.dirname(path2.resolve(out));
|
|
166
|
+
const positionAt = createPositionFinder(css);
|
|
167
|
+
const hasFile = (specifier) => {
|
|
168
|
+
const target = path2.resolve(directory, specifier);
|
|
169
|
+
return outputs.has(target) || statSync(target, { throwIfNoEntry: false })?.isFile() === true;
|
|
170
|
+
};
|
|
171
|
+
return findBareImports(css, hasFile).map((diagnostic) => {
|
|
172
|
+
const position = positionAt(diagnostic.offset);
|
|
173
|
+
return {
|
|
174
|
+
...diagnostic,
|
|
175
|
+
severity: "error",
|
|
176
|
+
file: source,
|
|
177
|
+
line: position.line,
|
|
178
|
+
column: position.column + 1
|
|
179
|
+
};
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
function expandOne(pair, extend, outputs) {
|
|
183
|
+
const source = pair.source;
|
|
184
|
+
let raw;
|
|
185
|
+
try {
|
|
186
|
+
raw = readFileSync(source, "utf8");
|
|
187
|
+
} catch {
|
|
188
|
+
return { kind: "unreadable" };
|
|
189
|
+
}
|
|
190
|
+
const { bom, text } = splitByteOrderMark(raw);
|
|
191
|
+
try {
|
|
192
|
+
const result = expandText(text, { extend, onUnknown: "error", from: source });
|
|
193
|
+
const problems = [
|
|
194
|
+
...result.diagnostics,
|
|
195
|
+
...bareImportDiagnostics(text, source, pair.out, outputs)
|
|
196
|
+
];
|
|
197
|
+
const report = foldedText(problems, source, extend);
|
|
198
|
+
return report === void 0 ? { kind: "expanded", css: bom + result.css } : { kind: "problems", report };
|
|
199
|
+
} catch (error) {
|
|
200
|
+
return { kind: "problems", report: `${source}: ${error.message}` };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function statusOf(unreadable, reports) {
|
|
204
|
+
if (unreadable.length > 0) return 2;
|
|
205
|
+
return reports.length > 0 ? 1 : 0;
|
|
206
|
+
}
|
|
207
|
+
function writeOutputs(outputs) {
|
|
208
|
+
const written = [];
|
|
209
|
+
for (const { css, pair } of outputs) {
|
|
210
|
+
try {
|
|
211
|
+
mkdirSync(path2.dirname(path2.resolve(pair.out)), { recursive: true });
|
|
212
|
+
writeFileSync(pair.out, css, "utf8");
|
|
213
|
+
} catch (error) {
|
|
214
|
+
const reason = error.message;
|
|
215
|
+
return {
|
|
216
|
+
written,
|
|
217
|
+
writeFailure: `Could not write ${pair.out}: ${reason}. No file after it was written.`
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
written.push(pair);
|
|
221
|
+
}
|
|
222
|
+
return { written, writeFailure: void 0 };
|
|
223
|
+
}
|
|
224
|
+
function expandPass(job, extend) {
|
|
225
|
+
const reports = [];
|
|
226
|
+
const unreadable = [];
|
|
227
|
+
const outputs = [];
|
|
228
|
+
const outPaths = new Set(job.pairs.map((pair) => path2.resolve(pair.out)));
|
|
229
|
+
for (const pair of job.pairs) {
|
|
230
|
+
const outcome = expandOne(pair, extend, outPaths);
|
|
231
|
+
if (outcome.kind === "unreadable") unreadable.push(pair.source);
|
|
232
|
+
else if (outcome.kind === "problems") reports.push(outcome.report);
|
|
233
|
+
else outputs.push({ css: outcome.css, pair });
|
|
234
|
+
}
|
|
235
|
+
const status = statusOf(unreadable, reports);
|
|
236
|
+
if (status !== 0) return { expanded: [], reports, status, unreadable, writeFailure: void 0 };
|
|
237
|
+
const { written, writeFailure } = writeOutputs(outputs);
|
|
238
|
+
return {
|
|
239
|
+
expanded: written,
|
|
240
|
+
reports,
|
|
241
|
+
status: writeFailure === void 0 ? 0 : 2,
|
|
242
|
+
unreadable,
|
|
243
|
+
writeFailure
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
var SETTLE_MS = 40;
|
|
247
|
+
function watchFiles(files, onChange) {
|
|
248
|
+
const wanted = new Set(files.map((file) => path2.resolve(file)));
|
|
249
|
+
let timer;
|
|
250
|
+
const schedule = () => {
|
|
251
|
+
clearTimeout(timer);
|
|
252
|
+
timer = setTimeout(onChange, SETTLE_MS);
|
|
253
|
+
};
|
|
254
|
+
const watchedNames = new Set(wanted);
|
|
255
|
+
const directories = new Set([...wanted].map((file) => path2.dirname(file)));
|
|
256
|
+
for (const file of wanted) {
|
|
257
|
+
try {
|
|
258
|
+
const target = realpathSync(file);
|
|
259
|
+
watchedNames.add(target);
|
|
260
|
+
directories.add(path2.dirname(target));
|
|
261
|
+
} catch {
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const watchers = [];
|
|
265
|
+
try {
|
|
266
|
+
for (const directory of directories) {
|
|
267
|
+
watchers.push(
|
|
268
|
+
watch(directory, (_event, name) => {
|
|
269
|
+
if (name === null || watchedNames.has(path2.join(directory, name))) schedule();
|
|
270
|
+
})
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
} catch (error) {
|
|
274
|
+
for (const watcher of watchers) watcher.close();
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
277
|
+
return () => {
|
|
278
|
+
clearTimeout(timer);
|
|
279
|
+
for (const watcher of watchers) watcher.close();
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/expand-command.ts
|
|
284
|
+
async function loadExtend(file, cache) {
|
|
285
|
+
if (file === void 0) return {};
|
|
286
|
+
let loaded = {};
|
|
287
|
+
await applyExtendModule(file, cache, (value) => {
|
|
288
|
+
loaded = snapshotExtendMap(value);
|
|
289
|
+
validateExtendAtomsHostFree(loaded);
|
|
290
|
+
});
|
|
291
|
+
return loaded;
|
|
292
|
+
}
|
|
293
|
+
function print(result) {
|
|
294
|
+
for (const { source, out } of result.expanded) console.log(`Expanded ${source} to ${out}.`);
|
|
295
|
+
for (const report of result.reports) console.error(report);
|
|
296
|
+
for (const source of result.unreadable) {
|
|
297
|
+
console.error(`Could not read ${source}, so no file was written.`);
|
|
298
|
+
}
|
|
299
|
+
if (result.writeFailure !== void 0) console.error(result.writeFailure);
|
|
300
|
+
}
|
|
301
|
+
async function runOnce(job, extendFile, cache) {
|
|
302
|
+
let extend;
|
|
303
|
+
try {
|
|
304
|
+
extend = await loadExtend(extendFile, cache);
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error(error.message);
|
|
307
|
+
return 2;
|
|
308
|
+
}
|
|
309
|
+
const result = expandPass(job, extend);
|
|
310
|
+
print(result);
|
|
311
|
+
return result.status;
|
|
312
|
+
}
|
|
313
|
+
async function watchAndRun(run, files) {
|
|
314
|
+
let isRunning = true;
|
|
315
|
+
let isPending = false;
|
|
316
|
+
const runAgain = async () => {
|
|
317
|
+
if (isRunning) {
|
|
318
|
+
isPending = true;
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
isRunning = true;
|
|
322
|
+
do {
|
|
323
|
+
isPending = false;
|
|
324
|
+
await run();
|
|
325
|
+
} while (isPending);
|
|
326
|
+
isRunning = false;
|
|
327
|
+
};
|
|
328
|
+
try {
|
|
329
|
+
watchFiles(files, () => void runAgain());
|
|
330
|
+
} catch (error) {
|
|
331
|
+
console.error(`Could not watch: ${error.message}`);
|
|
332
|
+
return 2;
|
|
333
|
+
}
|
|
334
|
+
do {
|
|
335
|
+
isPending = false;
|
|
336
|
+
await run();
|
|
337
|
+
} while (isPending);
|
|
338
|
+
isRunning = false;
|
|
339
|
+
return new Promise(() => {
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
async function runExpand(args) {
|
|
343
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
344
|
+
console.log(USAGE);
|
|
345
|
+
return 0;
|
|
346
|
+
}
|
|
347
|
+
const parsed = parseExpandArgs(args);
|
|
348
|
+
if (parsed.kind === "usageError") {
|
|
349
|
+
console.error(`${parsed.message}
|
|
350
|
+
${USAGE}`);
|
|
351
|
+
return 2;
|
|
352
|
+
}
|
|
353
|
+
const { job } = parsed;
|
|
354
|
+
let extendFile;
|
|
355
|
+
try {
|
|
356
|
+
extendFile = job.extend === void 0 ? void 0 : resolveExtendSpecifier(job.extend);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
console.error(error.message);
|
|
359
|
+
return 2;
|
|
360
|
+
}
|
|
361
|
+
const cache = /* @__PURE__ */ new Map();
|
|
362
|
+
const run = () => runOnce(job, extendFile, cache);
|
|
363
|
+
if (!job.watch) return run();
|
|
364
|
+
const watched = [...job.pairs.map((pair) => pair.source), ...extendFile ? [extendFile] : []];
|
|
365
|
+
return watchAndRun(run, watched);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/bin.ts
|
|
369
|
+
function isHelpRequest(args) {
|
|
370
|
+
return args.includes("--help") || args.includes("-h");
|
|
371
|
+
}
|
|
372
|
+
function parseSourceArgs(args) {
|
|
373
|
+
try {
|
|
374
|
+
const { values, positionals } = parseArgs({
|
|
375
|
+
args,
|
|
376
|
+
options: { source: { type: "string", multiple: true } },
|
|
377
|
+
strict: true,
|
|
378
|
+
allowPositionals: true
|
|
379
|
+
});
|
|
380
|
+
if (positionals.length > 0) {
|
|
381
|
+
return { kind: "unexpectedArgument", argument: positionals[0] };
|
|
382
|
+
}
|
|
383
|
+
return { kind: "source", source: values.source };
|
|
384
|
+
} catch {
|
|
385
|
+
return { kind: "usageError" };
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function printFindings(result) {
|
|
389
|
+
for (const finding of result.findings) console.log(formatFinding(finding));
|
|
390
|
+
}
|
|
391
|
+
function printCleanSummary(result, source) {
|
|
392
|
+
if (result.status !== 0) return;
|
|
393
|
+
const plural = result.stylesheetsRead === 1 ? "stylesheet" : "stylesheets";
|
|
394
|
+
console.log(
|
|
395
|
+
`Checked ${result.stylesheetsRead} ${plural} under ${source.join(", ")}; found no @nave directive.`
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
function reportPathProblems(result, source, unreadablePaths, notStylesheetPaths) {
|
|
399
|
+
for (const badPath of unreadablePaths) {
|
|
400
|
+
console.error(`Could not read ${badPath}, so it was not checked.`);
|
|
401
|
+
}
|
|
402
|
+
for (const badPath of notStylesheetPaths) {
|
|
403
|
+
console.error(`${badPath} is not a stylesheet file, so it was not checked.`);
|
|
404
|
+
}
|
|
405
|
+
if (result.status === 2 && unreadablePaths.length === 0 && notStylesheetPaths.length === 0) {
|
|
406
|
+
console.error(`No stylesheet found under ${source.join(", ")}; nothing was checked.`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
async function runCheck2(args) {
|
|
410
|
+
if (isHelpRequest(args)) {
|
|
411
|
+
console.log(USAGE);
|
|
412
|
+
return 0;
|
|
413
|
+
}
|
|
414
|
+
const parsed = parseSourceArgs(args);
|
|
415
|
+
if (parsed.kind === "unexpectedArgument") {
|
|
416
|
+
console.error(`check does not take a positional argument: ${parsed.argument}
|
|
417
|
+
${USAGE}`);
|
|
418
|
+
return 2;
|
|
419
|
+
}
|
|
420
|
+
const source = parsed.kind === "source" ? parsed.source : void 0;
|
|
421
|
+
if (!source || source.length === 0 || source.includes("")) {
|
|
422
|
+
console.error(`check requires at least one --source=<path>.
|
|
423
|
+
${USAGE}`);
|
|
424
|
+
return 2;
|
|
425
|
+
}
|
|
426
|
+
const { result, unreadablePaths, notStylesheetPaths } = await runCheck(source);
|
|
427
|
+
printFindings(result);
|
|
428
|
+
printCleanSummary(result, source);
|
|
429
|
+
reportPathProblems(result, source, unreadablePaths, notStylesheetPaths);
|
|
430
|
+
return result.status;
|
|
431
|
+
}
|
|
432
|
+
async function main() {
|
|
433
|
+
const args = process.argv.slice(2);
|
|
434
|
+
if (args.length === 0) {
|
|
435
|
+
console.error(USAGE);
|
|
436
|
+
return 2;
|
|
437
|
+
}
|
|
438
|
+
if (isHelpRequest([args[0]])) {
|
|
439
|
+
console.log(USAGE);
|
|
440
|
+
return 0;
|
|
441
|
+
}
|
|
442
|
+
const [subcommand, ...rest] = args;
|
|
443
|
+
if (subcommand === "expand") return runExpand(rest);
|
|
444
|
+
if (subcommand !== "check") {
|
|
445
|
+
console.error(`Unknown subcommand "${subcommand}".
|
|
446
|
+
${USAGE}`);
|
|
447
|
+
return 2;
|
|
448
|
+
}
|
|
449
|
+
return runCheck2(rest);
|
|
450
|
+
}
|
|
451
|
+
process.exitCode = await main();
|
package/dist/check.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
interface Finding {
|
|
2
|
+
readonly file: string;
|
|
3
|
+
readonly line: number;
|
|
4
|
+
readonly column: number;
|
|
5
|
+
readonly text: string;
|
|
6
|
+
readonly selector?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface CheckOptions {
|
|
10
|
+
readonly source: readonly string[];
|
|
11
|
+
}
|
|
12
|
+
interface CheckResult {
|
|
13
|
+
readonly status: 0 | 1 | 2;
|
|
14
|
+
readonly findings: readonly Finding[];
|
|
15
|
+
readonly stylesheetsRead: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* `check({ source })`, the core logic behind both the `navecss-core
|
|
20
|
+
* check` bin and the `@navecss/core/check` subpath. Exit contract mirrors
|
|
21
|
+
* `navecss-tokens`: `0` read at least one stylesheet and found none, `1`
|
|
22
|
+
* found at least one, `2` a usage error, an unreadable path, or no
|
|
23
|
+
* stylesheet found at all.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
`check({ source })`: reads every stylesheet under `source`, reporting every surviving `@nave`.
|
|
28
|
+
*/
|
|
29
|
+
declare function check(options: CheckOptions): Promise<CheckResult>;
|
|
30
|
+
|
|
31
|
+
export { type CheckOptions, type CheckResult, type Finding, check };
|
package/dist/check.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { runCheck } from './chunk-XQQCDUV6.js';
|
|
2
|
+
import './chunk-UANF3FGO.js';
|
|
3
|
+
import './chunk-NFS2REPQ.js';
|
|
4
|
+
|
|
5
|
+
// src/directive/check.ts
|
|
6
|
+
async function check(options) {
|
|
7
|
+
const run = await runCheck(options.source);
|
|
8
|
+
return run.result;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { check };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { formatDiagnostic, foldLocated } from './chunk-S7DZUVV3.js';
|
|
2
|
+
|
|
3
|
+
// src/byte-order-mark.ts
|
|
4
|
+
var BYTE_ORDER_MARK = "\uFEFF";
|
|
5
|
+
function splitByteOrderMark(raw) {
|
|
6
|
+
return raw.startsWith(BYTE_ORDER_MARK) ? { bom: BYTE_ORDER_MARK, text: raw.slice(BYTE_ORDER_MARK.length) } : { bom: "", text: raw };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// src/directive/report-text.ts
|
|
10
|
+
function inSourceOrder(diagnostics) {
|
|
11
|
+
return diagnostics.toSorted((a, b) => a.offset - b.offset);
|
|
12
|
+
}
|
|
13
|
+
function warningTexts(diagnostics, file, extend) {
|
|
14
|
+
return inSourceOrder(diagnostics).map(
|
|
15
|
+
(diagnostic) => `${file}:${diagnostic.line}:${diagnostic.column}: ${formatDiagnostic(diagnostic, { extend })}`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
function foldedText(diagnostics, file, extend) {
|
|
19
|
+
const ordered = inSourceOrder(diagnostics);
|
|
20
|
+
const first = ordered[0];
|
|
21
|
+
if (first === void 0) return void 0;
|
|
22
|
+
const located = ordered.map((diagnostic) => ({
|
|
23
|
+
text: formatDiagnostic(diagnostic, { extend }),
|
|
24
|
+
line: diagnostic.line,
|
|
25
|
+
column: diagnostic.column
|
|
26
|
+
}));
|
|
27
|
+
return `${file}:${first.line}:${first.column}: ${foldLocated(located)}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { foldedText, splitByteOrderMark, warningTexts };
|
|
@@ -169,6 +169,10 @@ var atoms = {
|
|
|
169
169
|
/**
|
|
170
170
|
* focusRing — keyboard focus indicator.
|
|
171
171
|
* :focus-visible — keyboard users see it, mouse users do not.
|
|
172
|
+
*
|
|
173
|
+
* Longhands with fallbacks, not an `outline` shorthand, which computes to `none` as a whole when
|
|
174
|
+
* a `var()` in it has no usable value: no token layer, a missing property, or a wrong-typed value
|
|
175
|
+
* in an unregistered property. The fallbacks are 2px and `currentColor`, never a literal colour.
|
|
172
176
|
*/
|
|
173
177
|
focusRing: {
|
|
174
178
|
declarations: {
|
|
@@ -176,7 +180,9 @@ var atoms = {
|
|
|
176
180
|
},
|
|
177
181
|
pseudos: {
|
|
178
182
|
":focus-visible": {
|
|
179
|
-
outline: "
|
|
183
|
+
"outline-style": "solid",
|
|
184
|
+
"outline-width": "var(--nave-border-width-focus, 2px)",
|
|
185
|
+
"outline-color": "var(--nave-color-border-focus, currentColor)",
|
|
180
186
|
"outline-offset": "2px"
|
|
181
187
|
}
|
|
182
188
|
}
|