@navecss/core 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -1,17 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import { runCheck } from './chunk-XQQCDUV6.js';
3
- import { splitByteOrderMark, foldedText } from './chunk-2A7IUZBH.js';
4
- import { formatFinding } from './chunk-UANF3FGO.js';
5
- import { validateExtendAtomsHostFree } from './chunk-LQSGQ2OA.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-LADFXRWQ.js';
8
- import { createPositionFinder, tokenize, atKeywordName } from './chunk-NFS2REPQ.js';
8
+ import { snapshotExtendMap, expandText } from './chunk-MWHZ26S4.js';
9
+ import { createPositionFinder, atKeywordName } from './chunk-UVHGEVMV.js';
9
10
  import './chunk-SYNQJNVB.js';
10
11
  import './chunk-CUL5PLPI.js';
12
+ import { tokenize } from './chunk-2K5EBBF2.js';
11
13
  import './chunk-GZL2VW7C.js';
12
14
  import { parseArgs } from 'util';
13
15
  import { realpathSync, watch, existsSync, statSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
14
16
  import path2 from 'path';
17
+ import { createHash } from 'crypto';
15
18
 
16
19
  // src/bin-usage.ts
17
20
  var USAGE = `Usage:
@@ -197,9 +200,10 @@ function expandOne(pair, extend, outputs) {
197
200
  ...bareImportDiagnostics(text, source, pair.out, outputs)
198
201
  ];
199
202
  const report = foldedText(problems, source, extend);
200
- 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 };
201
205
  } catch (error) {
202
- return { kind: "problems", report: `${source}: ${error.message}` };
206
+ return { kind: "problems", report: `${source}: ${error.message}`, warnings: [] };
203
207
  }
204
208
  }
205
209
  function statusOf(unreadable, reports) {
@@ -225,40 +229,110 @@ function writeOutputs(outputs) {
225
229
  }
226
230
  function expandPass(job, extend) {
227
231
  const reports = [];
232
+ const warnings = [];
228
233
  const unreadable = [];
229
234
  const outputs = [];
230
235
  const outPaths = new Set(job.pairs.map((pair) => path2.resolve(pair.out)));
231
236
  for (const pair of job.pairs) {
232
237
  const outcome = expandOne(pair, extend, outPaths);
233
- if (outcome.kind === "unreadable") unreadable.push(pair.source);
234
- 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);
235
244
  else outputs.push({ css: outcome.css, pair });
236
245
  }
237
246
  const status = statusOf(unreadable, reports);
238
- 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
+ }
239
250
  const { written, writeFailure } = writeOutputs(outputs);
240
251
  return {
241
252
  expanded: written,
242
253
  reports,
243
254
  status: writeFailure === void 0 ? 0 : 2,
244
255
  unreadable,
256
+ warnings,
245
257
  writeFailure
246
258
  };
247
259
  }
248
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
+ }
249
314
  function watchFiles(files, onChange) {
250
315
  const wanted = new Set(files.map((file) => path2.resolve(file)));
316
+ const seen = seenFiles(wanted, Date.now());
251
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
+ };
252
326
  const schedule = () => {
253
327
  clearTimeout(timer);
254
- timer = setTimeout(onChange, SETTLE_MS);
328
+ timer = setTimeout(settle, SETTLE_MS);
255
329
  };
256
- const watchedNames = new Set(wanted);
330
+ const watchedNames = new Map([...wanted].map((file) => [file, file]));
257
331
  const directories = new Set([...wanted].map((file) => path2.dirname(file)));
258
332
  for (const file of wanted) {
259
333
  try {
260
334
  const target = realpathSync(file);
261
- watchedNames.add(target);
335
+ if (!watchedNames.has(target)) watchedNames.set(target, file);
262
336
  directories.add(path2.dirname(target));
263
337
  } catch {
264
338
  }
@@ -268,7 +342,11 @@ function watchFiles(files, onChange) {
268
342
  for (const directory of directories) {
269
343
  watchers.push(
270
344
  watch(directory, (_event, name) => {
271
- 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();
272
350
  })
273
351
  );
274
352
  }
@@ -276,8 +354,12 @@ function watchFiles(files, onChange) {
276
354
  for (const watcher of watchers) watcher.close();
277
355
  throw error;
278
356
  }
357
+ const poll = setInterval(() => {
358
+ if (seen.isStampChanged()) schedule();
359
+ }, POLL_MS);
279
360
  return () => {
280
361
  clearTimeout(timer);
362
+ clearInterval(poll);
281
363
  for (const watcher of watchers) watcher.close();
282
364
  };
283
365
  }
@@ -294,6 +376,7 @@ async function loadExtend(file, cache) {
294
376
  }
295
377
  function print(result) {
296
378
  for (const { source, out } of result.expanded) console.log(`Expanded ${source} to ${out}.`);
379
+ for (const warning of result.warnings) console.error(warning);
297
380
  for (const report of result.reports) console.error(report);
298
381
  for (const source of result.unreadable) {
299
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-LADFXRWQ.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) {