@navecss/core 0.2.0 → 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.
@@ -0,0 +1,42 @@
1
+ import crypto from 'crypto';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import url from 'url';
5
+
6
+ // src/postcss-extend-module.ts
7
+ function resolveExtendSpecifier(specifier, dir = process.cwd()) {
8
+ const file = path.resolve(dir, specifier);
9
+ const stats = fs.existsSync(file) ? fs.statSync(file) : void 0;
10
+ if (!stats?.isFile()) {
11
+ throw new Error(`@nave: cannot find extend module "${specifier}" from "${dir}"`);
12
+ }
13
+ return file;
14
+ }
15
+ function isJsonPath(file) {
16
+ return path.extname(file).toLowerCase() === ".json";
17
+ }
18
+ async function importExtendMap(moduleUrl, file) {
19
+ const value = isJsonPath(file) ? JSON.parse(fs.readFileSync(file, "utf8")) : (await import(moduleUrl)).default;
20
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
21
+ throw new Error(
22
+ `@nave: extend module "${file}" must have a default export that is a plain object`
23
+ );
24
+ }
25
+ return value;
26
+ }
27
+ function loadExtendModule(file, cache) {
28
+ const digest = crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
29
+ const moduleUrl = `${url.pathToFileURL(file).href}?v=${digest}`;
30
+ const cached = cache.get(moduleUrl);
31
+ if (cached) return cached;
32
+ const filePrefix = `${url.pathToFileURL(file).href}?v=`;
33
+ for (const key of cache.keys()) if (key.startsWith(filePrefix)) cache.delete(key);
34
+ const pending = importExtendMap(moduleUrl, file);
35
+ cache.set(moduleUrl, pending);
36
+ return pending;
37
+ }
38
+ async function applyExtendModule(file, cache, setExtend) {
39
+ setExtend(await loadExtendModule(file, cache));
40
+ }
41
+
42
+ export { applyExtendModule, resolveExtendSpecifier };
@@ -0,0 +1,94 @@
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';
3
+
4
+ // src/validate-extend-host-free.ts
5
+ function readSoleBlockItem(text) {
6
+ const tokens = tokenize(text);
7
+ if (tokens.length === 0 || isInert(tokens[0])) return void 0;
8
+ const closerFor = matchBrackets(tokens);
9
+ const item = readItem(tokens, 0, tokens.length, closerFor);
10
+ if (item.end !== tokens.length || item.blockStart === void 0) return void 0;
11
+ const last = tokens.length - 1;
12
+ if (item.blockEnd !== last || tokens[last].type !== "}-token") return void 0;
13
+ if (closerFor[item.blockStart - 1] !== last) return void 0;
14
+ return { item, wrapped: { tokens, closerFor } };
15
+ }
16
+ function isValueRoundTrip(wrapped, from, to, isCustom) {
17
+ const { tokens } = wrapped;
18
+ if (hasBrokenString(tokens, from, to)) return false;
19
+ if (isCustom) return true;
20
+ const last = lastNonWhitespace(tokens, from, to);
21
+ if (last >= from && tokens[last].type === "comment") return false;
22
+ return !hasTopLevelColon(wrapped, from, to);
23
+ }
24
+ function isSoleDeclaration(wrapped, start, end, prop) {
25
+ const { tokens, closerFor } = wrapped;
26
+ const name = tokens[start];
27
+ if (name?.type !== "ident-token" || name.raw !== prop) return false;
28
+ if (tokens[start + 1]?.type !== "colon-token") return false;
29
+ const declaration = readItem(tokens, start, end, closerFor);
30
+ if (declaration.kind !== "declaration") return false;
31
+ if (tokens[declaration.end - 1].type === "semicolon-token") return false;
32
+ if (!isOnlyInert(tokens, declaration.end, end)) return false;
33
+ return isValueRoundTrip(wrapped, start + 2, end, prop.startsWith("--"));
34
+ }
35
+ function isReadTwoWays(prop, value) {
36
+ return hasEscapedCommentOpener(prop) || hasEscapedCommentOpener(value) || hasEscapedWhitespace(prop);
37
+ }
38
+ function isRewrittenByPostcss(prop, value) {
39
+ if (hasHtmlBreakout(prop) || hasHtmlBreakout(value)) return true;
40
+ if (hasUnstrippedTrailingSpace(prop) || hasUnstrippedTrailingSpace(value)) return true;
41
+ return prop.startsWith("--") && value !== value.trimEnd();
42
+ }
43
+ function declarationRefusal(prop, value) {
44
+ if (isReadTwoWays(prop, value)) return "break-out";
45
+ const sole = readSoleBlockItem(`a{${prop}:${value}}`);
46
+ if (sole?.item.kind !== "rule") return "break-out";
47
+ const { blockStart, blockEnd } = sole.item;
48
+ if (!isSoleDeclaration(sole.wrapped, blockStart, blockEnd, prop)) return "break-out";
49
+ return isRewrittenByPostcss(prop, value) ? "rewritten" : void 0;
50
+ }
51
+ function propRefusal(prop) {
52
+ return declarationRefusal(prop, "0");
53
+ }
54
+ function isPreludeUnambiguous(wrapped, from, to) {
55
+ const { tokens } = wrapped;
56
+ return !hasPaddedComment(tokens, from, to) && !hasBrokenString(tokens, from, to) && !hasUrl(tokens, from, to) && !hasStrayCurlyCloser(wrapped, from, to);
57
+ }
58
+ function anchoredSelector(key) {
59
+ let selector;
60
+ try {
61
+ selector = anchorSelectorList(key);
62
+ } catch {
63
+ return void 0;
64
+ }
65
+ const isUnreadable = selector.startsWith("@") || selector.startsWith("--") || hasEscapedCommentOpener(selector);
66
+ return isUnreadable ? void 0 : selector;
67
+ }
68
+ function selectorRefusal(key) {
69
+ const selector = anchoredSelector(key);
70
+ if (selector === void 0) return "break-out";
71
+ const sole = readSoleBlockItem(`${selector}{}`);
72
+ if (sole?.item.kind !== "rule") return "break-out";
73
+ const { tokens } = sole.wrapped;
74
+ const { blockStart, blockEnd } = sole.item;
75
+ const prelude = blockStart - 1;
76
+ const isOneEmptyRule = isOnlyInert(tokens, blockStart, blockEnd) && tokens[lastNonWhitespace(tokens, 0, prelude)]?.type !== "comment" && isPreludeUnambiguous(sole.wrapped, 0, prelude);
77
+ return isOneEmptyRule ? void 0 : "break-out";
78
+ }
79
+ function conditionRefusal(atName, condition) {
80
+ if (hasEscapedCommentOpener(condition)) return "break-out";
81
+ const sole = readSoleBlockItem(`@${atName} ${condition}{}`);
82
+ if (sole?.item.kind !== "at-rule" || sole.item.atKeyword !== atName) return "break-out";
83
+ const { blockStart, blockEnd } = sole.item;
84
+ const prelude = blockStart - 1;
85
+ const isOneBodilessRule = isOnlyInert(sole.wrapped.tokens, blockStart, blockEnd) && !hasTopLevelComment(sole.wrapped, 1, prelude) && isPreludeUnambiguous(sole.wrapped, 1, prelude);
86
+ if (!isOneBodilessRule) return "break-out";
87
+ const isUntrimmed = hasUnstrippedTrailingSpace(condition) || hasUnstrippedLeadingSpace(condition);
88
+ return isUntrimmed ? "rewritten" : void 0;
89
+ }
90
+ function validateExtendAtomsHostFree(extend) {
91
+ walkExtendAtoms(extend, { declarationRefusal, propRefusal, selectorRefusal, conditionRefusal });
92
+ }
93
+
94
+ export { validateExtendAtomsHostFree };