@navecss/core 0.3.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.
package/dist/bin.js CHANGED
@@ -1,15 +1,20 @@
1
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';
2
+ import { runCheck } from './chunk-CTFEYJBE.js';
3
+ import { splitByteOrderMark } from './chunk-2TNTYI4L.js';
4
+ import { foldedText, orderWarningTexts } from './chunk-KKUYU75D.js';
5
+ import { formatFinding } from './chunk-ZNTNLGEC.js';
6
+ import { validateExtendAtomsHostFree } from './chunk-63H2FLGU.js';
6
7
  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';
8
+ import { snapshotExtendMap, expandText } from './chunk-MWHZ26S4.js';
9
+ import { createPositionFinder, atKeywordName } from './chunk-UVHGEVMV.js';
10
+ import './chunk-SYNQJNVB.js';
11
+ import './chunk-CUL5PLPI.js';
12
+ import { tokenize } from './chunk-2K5EBBF2.js';
13
+ import './chunk-GZL2VW7C.js';
10
14
  import { parseArgs } from 'util';
11
15
  import { realpathSync, watch, existsSync, statSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
12
16
  import path2 from 'path';
17
+ import { createHash } from 'crypto';
13
18
 
14
19
  // src/bin-usage.ts
15
20
  var USAGE = `Usage:
@@ -195,9 +200,10 @@ function expandOne(pair, extend, outputs) {
195
200
  ...bareImportDiagnostics(text, source, pair.out, outputs)
196
201
  ];
197
202
  const report = foldedText(problems, source, extend);
198
- return report === void 0 ? { kind: "expanded", css: bom + result.css } : { kind: "problems", report };
203
+ const warnings = orderWarningTexts(result.warnings, source);
204
+ return report === void 0 ? { kind: "expanded", css: bom + result.css, warnings } : { kind: "problems", report, warnings };
199
205
  } catch (error) {
200
- return { kind: "problems", report: `${source}: ${error.message}` };
206
+ return { kind: "problems", report: `${source}: ${error.message}`, warnings: [] };
201
207
  }
202
208
  }
203
209
  function statusOf(unreadable, reports) {
@@ -223,40 +229,110 @@ function writeOutputs(outputs) {
223
229
  }
224
230
  function expandPass(job, extend) {
225
231
  const reports = [];
232
+ const warnings = [];
226
233
  const unreadable = [];
227
234
  const outputs = [];
228
235
  const outPaths = new Set(job.pairs.map((pair) => path2.resolve(pair.out)));
229
236
  for (const pair of job.pairs) {
230
237
  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);
238
+ if (outcome.kind === "unreadable") {
239
+ unreadable.push(pair.source);
240
+ continue;
241
+ }
242
+ warnings.push(...outcome.warnings);
243
+ if (outcome.kind === "problems") reports.push(outcome.report);
233
244
  else outputs.push({ css: outcome.css, pair });
234
245
  }
235
246
  const status = statusOf(unreadable, reports);
236
- if (status !== 0) return { expanded: [], reports, status, unreadable, writeFailure: void 0 };
247
+ if (status !== 0) {
248
+ return { expanded: [], reports, status, unreadable, warnings, writeFailure: void 0 };
249
+ }
237
250
  const { written, writeFailure } = writeOutputs(outputs);
238
251
  return {
239
252
  expanded: written,
240
253
  reports,
241
254
  status: writeFailure === void 0 ? 0 : 2,
242
255
  unreadable,
256
+ warnings,
243
257
  writeFailure
244
258
  };
245
259
  }
246
260
  var SETTLE_MS = 40;
261
+ var POLL_MS = 1e3;
262
+ function stampOf(file) {
263
+ try {
264
+ const { ino, mtimeMs, size } = statSync(file);
265
+ return `${String(ino)}:${String(mtimeMs)}:${String(size)}`;
266
+ } catch {
267
+ return "";
268
+ }
269
+ }
270
+ function digestOf(file) {
271
+ try {
272
+ return createHash("sha256").update(readFileSync(file)).digest("hex");
273
+ } catch {
274
+ return "";
275
+ }
276
+ }
277
+ var START_WINDOW_MS = 2e3;
278
+ function isModifiedSince(file, ms) {
279
+ try {
280
+ return Math.floor(statSync(file).mtimeMs / 1e3) >= Math.floor(ms / 1e3);
281
+ } catch {
282
+ return false;
283
+ }
284
+ }
285
+ function seenFiles(files, startMs) {
286
+ const stamps = /* @__PURE__ */ new Map();
287
+ const digests = /* @__PURE__ */ new Map();
288
+ for (const file of files) {
289
+ stamps.set(file, stampOf(file));
290
+ if (isModifiedSince(file, startMs - START_WINDOW_MS)) digests.set(file, digestOf(file));
291
+ }
292
+ return {
293
+ isStampChanged: () => [...files].some((file) => stampOf(file) !== stamps.get(file)),
294
+ isContentChanged: (named) => {
295
+ let isChanged = false;
296
+ for (const file of named) {
297
+ const before = digests.get(file);
298
+ if (before === void 0 || stampOf(file) !== stamps.get(file)) continue;
299
+ const digest = digestOf(file);
300
+ if (digest !== before) isChanged = true;
301
+ digests.set(file, digest);
302
+ }
303
+ return isChanged;
304
+ },
305
+ record: () => {
306
+ for (const file of files) {
307
+ const stamp = stampOf(file);
308
+ if (stamp !== stamps.get(file)) digests.set(file, digestOf(file));
309
+ stamps.set(file, stamp);
310
+ }
311
+ }
312
+ };
313
+ }
247
314
  function watchFiles(files, onChange) {
248
315
  const wanted = new Set(files.map((file) => path2.resolve(file)));
316
+ const seen = seenFiles(wanted, Date.now());
249
317
  let timer;
318
+ const named = /* @__PURE__ */ new Set();
319
+ const settle = () => {
320
+ const files2 = [...named];
321
+ named.clear();
322
+ if (!seen.isStampChanged() && !seen.isContentChanged(files2)) return;
323
+ seen.record();
324
+ onChange();
325
+ };
250
326
  const schedule = () => {
251
327
  clearTimeout(timer);
252
- timer = setTimeout(onChange, SETTLE_MS);
328
+ timer = setTimeout(settle, SETTLE_MS);
253
329
  };
254
- const watchedNames = new Set(wanted);
330
+ const watchedNames = new Map([...wanted].map((file) => [file, file]));
255
331
  const directories = new Set([...wanted].map((file) => path2.dirname(file)));
256
332
  for (const file of wanted) {
257
333
  try {
258
334
  const target = realpathSync(file);
259
- watchedNames.add(target);
335
+ if (!watchedNames.has(target)) watchedNames.set(target, file);
260
336
  directories.add(path2.dirname(target));
261
337
  } catch {
262
338
  }
@@ -266,7 +342,11 @@ function watchFiles(files, onChange) {
266
342
  for (const directory of directories) {
267
343
  watchers.push(
268
344
  watch(directory, (_event, name) => {
269
- if (name === null || watchedNames.has(path2.join(directory, name))) schedule();
345
+ const file = name === null ? void 0 : watchedNames.get(path2.join(directory, name));
346
+ if (name !== null && file === void 0) return;
347
+ const files2 = file === void 0 ? wanted : [file];
348
+ for (const each of files2) named.add(each);
349
+ schedule();
270
350
  })
271
351
  );
272
352
  }
@@ -274,8 +354,12 @@ function watchFiles(files, onChange) {
274
354
  for (const watcher of watchers) watcher.close();
275
355
  throw error;
276
356
  }
357
+ const poll = setInterval(() => {
358
+ if (seen.isStampChanged()) schedule();
359
+ }, POLL_MS);
277
360
  return () => {
278
361
  clearTimeout(timer);
362
+ clearInterval(poll);
279
363
  for (const watcher of watchers) watcher.close();
280
364
  };
281
365
  }
@@ -292,6 +376,7 @@ async function loadExtend(file, cache) {
292
376
  }
293
377
  function print(result) {
294
378
  for (const { source, out } of result.expanded) console.log(`Expanded ${source} to ${out}.`);
379
+ for (const warning of result.warnings) console.error(warning);
295
380
  for (const report of result.reports) console.error(report);
296
381
  for (const source of result.unreadable) {
297
382
  console.error(`Could not read ${source}, so no file was written.`);
package/dist/check.d.ts CHANGED
@@ -28,4 +28,5 @@ interface CheckResult {
28
28
  */
29
29
  declare function check(options: CheckOptions): Promise<CheckResult>;
30
30
 
31
- export { type CheckOptions, type CheckResult, type Finding, check };
31
+ export { check };
32
+ export type { CheckOptions, CheckResult, Finding };
package/dist/check.js CHANGED
@@ -1,6 +1,7 @@
1
- import { runCheck } from './chunk-XQQCDUV6.js';
2
- import './chunk-UANF3FGO.js';
3
- import './chunk-NFS2REPQ.js';
1
+ import { runCheck } from './chunk-CTFEYJBE.js';
2
+ import './chunk-ZNTNLGEC.js';
3
+ import './chunk-UVHGEVMV.js';
4
+ import './chunk-2K5EBBF2.js';
4
5
 
5
6
  // src/directive/check.ts
6
7
  async function check(options) {
@@ -1,193 +1,3 @@
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
-
56
- // src/directive/bracket-match.ts
57
- var OPENERS = /* @__PURE__ */ new Set(["(-token", "[-token", "function-token", "{-token"]);
58
- var CLOSERS = /* @__PURE__ */ new Set([")-token", "]-token", "}-token"]);
59
- function isMirrorCloser(openerType, closerType) {
60
- if (closerType === ")-token") return openerType === "(-token" || openerType === "function-token";
61
- if (closerType === "]-token") return openerType === "[-token";
62
- return openerType === "{-token";
63
- }
64
- function matchBrackets(tokens) {
65
- const closerFor = new Int32Array(tokens.length).fill(-1);
66
- const opens = [];
67
- for (const [i, token] of tokens.entries()) {
68
- const { type } = token;
69
- if (OPENERS.has(type)) {
70
- opens.push(i);
71
- continue;
72
- }
73
- if (!CLOSERS.has(type)) continue;
74
- const openIndex = opens.at(-1);
75
- if (openIndex === void 0 || !isMirrorCloser(tokens[openIndex].type, type)) continue;
76
- opens.pop();
77
- closerFor[openIndex] = i;
78
- }
79
- return closerFor;
80
- }
81
- function matchedCloseWithin(closerFor, openIndex, limit) {
82
- const closeIndex = closerFor[openIndex];
83
- return closeIndex !== -1 && closeIndex < limit ? closeIndex : void 0;
84
- }
85
-
86
- // src/directive/block-reader.ts
87
- function scanItem(tokens, start, limit, closerFor) {
88
- let i = start;
89
- while (i < limit) {
90
- const type = tokens[i].type;
91
- if (OPENERS.has(type)) {
92
- const closeIndex = matchedCloseWithin(closerFor, i, limit);
93
- if (type === "{-token") {
94
- return {
95
- end: closeIndex === void 0 ? limit : closeIndex + 1,
96
- consumedSemicolon: false,
97
- trailingBlock: { openIndex: i, closeIndex }
98
- };
99
- }
100
- i = closeIndex === void 0 ? limit : closeIndex + 1;
101
- continue;
102
- }
103
- if (CLOSERS.has(type)) {
104
- if (i === start) return { end: i + 1, consumedSemicolon: false };
105
- i++;
106
- continue;
107
- }
108
- if (type === "semicolon-token") return { end: i + 1, consumedSemicolon: true };
109
- i++;
110
- }
111
- return { end: i, consumedSemicolon: false };
112
- }
113
- function scanCustomPropertyValue(tokens, start, limit, closerFor) {
114
- let i = start;
115
- while (i < limit) {
116
- const type = tokens[i].type;
117
- if (OPENERS.has(type)) {
118
- const closeIndex = matchedCloseWithin(closerFor, i, limit);
119
- i = closeIndex === void 0 ? limit : closeIndex + 1;
120
- continue;
121
- }
122
- if (type === "semicolon-token") return i + 1;
123
- i++;
124
- }
125
- return i;
126
- }
127
- function isColonSoonAfter(tokens, i, limit) {
128
- let j = i;
129
- while (j < limit && (tokens[j].type === "whitespace-token" || tokens[j].type === "comment")) j++;
130
- return tokens[j]?.type === "colon-token";
131
- }
132
- function isCustomPropertyName(token) {
133
- return token.type === "ident-token" && (token.structured?.value).startsWith("--");
134
- }
135
- function atKeywordName(token) {
136
- return (token.structured?.value).toLowerCase();
137
- }
138
- function toDeclarationItem(start, end) {
139
- return { kind: "declaration", start, end };
140
- }
141
- function preludeEndOffset(tokens, start, scan, preludeStart) {
142
- if (scan.trailingBlock) return tokens[scan.trailingBlock.openIndex].startIndex;
143
- const lastContentIndex = scan.consumedSemicolon ? scan.end - 2 : scan.end - 1;
144
- return lastContentIndex >= start ? tokens[lastContentIndex].endIndex : preludeStart;
145
- }
146
- function toAtRuleItem(tokens, start, scan) {
147
- const nameToken = tokens[start];
148
- const preludeStart = tokens[start + 1]?.startIndex ?? nameToken.endIndex;
149
- return {
150
- kind: "at-rule",
151
- start,
152
- end: scan.end,
153
- atKeyword: atKeywordName(nameToken),
154
- preludeOffset: preludeStart,
155
- preludeEndOffset: preludeEndOffset(tokens, start + 1, scan, preludeStart),
156
- ...scan.trailingBlock && {
157
- blockStart: scan.trailingBlock.openIndex + 1,
158
- blockEnd: scan.trailingBlock.closeIndex ?? scan.end
159
- }
160
- };
161
- }
162
- function toRuleOrInvalidItem(start, scan) {
163
- if (!scan.trailingBlock) return { kind: "invalid", start, end: scan.end };
164
- return {
165
- kind: "rule",
166
- start,
167
- end: scan.end,
168
- blockStart: scan.trailingBlock.openIndex + 1,
169
- blockEnd: scan.trailingBlock.closeIndex ?? scan.end
170
- };
171
- }
172
- function readItem(tokens, start, limit, closerFor) {
173
- const first = tokens[start];
174
- if (first.type === "at-keyword-token") {
175
- return toAtRuleItem(tokens, start, scanItem(tokens, start + 1, limit, closerFor));
176
- }
177
- if (first.type === "ident-token" && isColonSoonAfter(tokens, start + 1, limit)) {
178
- if (isCustomPropertyName(first)) {
179
- return toDeclarationItem(start, scanCustomPropertyValue(tokens, start + 1, limit, closerFor));
180
- }
181
- const scan = scanItem(tokens, start + 1, limit, closerFor);
182
- if (!scan.trailingBlock) return toDeclarationItem(start, scan.end);
183
- return toRuleOrInvalidItem(start, scan);
184
- }
185
- return toRuleOrInvalidItem(start, scanItem(tokens, start, limit, closerFor));
186
- }
187
- function isNaveAtKeyword(token) {
188
- return token.type === "at-keyword-token" && atKeywordName(token) === "nave";
189
- }
190
-
191
1
  // src/directive/tokenizer/comment.ts
192
2
  function tryConsumeComment(s, start) {
193
3
  if (s.peek() !== "/" || s.peek(1) !== "*") return void 0;
@@ -744,4 +554,4 @@ function tokenize(css) {
744
554
  return tokens;
745
555
  }
746
556
 
747
- export { OPENERS, atKeywordName, createPositionFinder, isNaveAtKeyword, matchBrackets, readItem, reportDiagnostics, tokenize };
557
+ export { tokenize };
@@ -0,0 +1,7 @@
1
+ // src/byte-order-mark.ts
2
+ var BYTE_ORDER_MARK = "\uFEFF";
3
+ function splitByteOrderMark(raw) {
4
+ return raw.startsWith(BYTE_ORDER_MARK) ? { bom: BYTE_ORDER_MARK, text: raw.slice(BYTE_ORDER_MARK.length) } : { bom: "", text: raw };
5
+ }
6
+
7
+ export { splitByteOrderMark };
@@ -1,5 +1,6 @@
1
- import { walkExtendAtoms, hasEscapedCommentOpener, isOnlyInert, hasTopLevelComment, hasUnstrippedTrailingSpace, hasUnstrippedLeadingSpace, lastNonWhitespace, isInert, hasPaddedComment, hasBrokenString, hasUrl, hasStrayCurlyCloser, anchorSelectorList, hasEscapedWhitespace, hasHtmlBreakout, hasTopLevelColon } from './chunk-S7DZUVV3.js';
2
- import { tokenize, matchBrackets, readItem } from './chunk-NFS2REPQ.js';
1
+ import { walkExtendAtoms, hasEscapedCommentOpener, isOnlyInert, hasTopLevelComment, hasUnstrippedTrailingSpace, hasUnstrippedLeadingSpace, lastNonWhitespace, isInert, hasPaddedComment, hasBrokenString, hasUrl, hasStrayCurlyCloser, anchorSelectorList, hasEscapedWhitespace, hasHtmlBreakout, hasTopLevelColon } from './chunk-MWHZ26S4.js';
2
+ import { matchBrackets, readItem } from './chunk-UVHGEVMV.js';
3
+ import { tokenize } from './chunk-2K5EBBF2.js';
3
4
 
4
5
  // src/validate-extend-host-free.ts
5
6
  function readSoleBlockItem(text) {