@caretcms/caretize 0.1.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/README.md +70 -0
- package/dist/backup.d.ts +30 -0
- package/dist/backup.d.ts.map +1 -0
- package/dist/backup.js +89 -0
- package/dist/backup.js.map +1 -0
- package/dist/bind-collection.d.ts +56 -0
- package/dist/bind-collection.d.ts.map +1 -0
- package/dist/bind-collection.js +140 -0
- package/dist/bind-collection.js.map +1 -0
- package/dist/bind-route.d.ts +40 -0
- package/dist/bind-route.d.ts.map +1 -0
- package/dist/bind-route.js +150 -0
- package/dist/bind-route.js.map +1 -0
- package/dist/cli-args.d.ts +30 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +118 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +356 -0
- package/dist/cli.js.map +1 -0
- package/dist/detect.d.ts +76 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +237 -0
- package/dist/detect.js.map +1 -0
- package/dist/discover.d.ts +13 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/discover.js +84 -0
- package/dist/discover.js.map +1 -0
- package/dist/frontmatter.d.ts +26 -0
- package/dist/frontmatter.d.ts.map +1 -0
- package/dist/frontmatter.js +52 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/identifiers.d.ts +26 -0
- package/dist/identifiers.d.ts.map +1 -0
- package/dist/identifiers.js +34 -0
- package/dist/identifiers.js.map +1 -0
- package/dist/import-wrap.d.ts +56 -0
- package/dist/import-wrap.d.ts.map +1 -0
- package/dist/import-wrap.js +149 -0
- package/dist/import-wrap.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/name.d.ts +49 -0
- package/dist/name.d.ts.map +1 -0
- package/dist/name.js +164 -0
- package/dist/name.js.map +1 -0
- package/dist/output.d.ts +30 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +110 -0
- package/dist/output.js.map +1 -0
- package/dist/parse.d.ts +86 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +92 -0
- package/dist/parse.js.map +1 -0
- package/dist/plan.d.ts +46 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +76 -0
- package/dist/plan.js.map +1 -0
- package/dist/preflight.d.ts +19 -0
- package/dist/preflight.d.ts.map +1 -0
- package/dist/preflight.js +95 -0
- package/dist/preflight.js.map +1 -0
- package/dist/prop-hoist.d.ts +67 -0
- package/dist/prop-hoist.d.ts.map +1 -0
- package/dist/prop-hoist.js +232 -0
- package/dist/prop-hoist.js.map +1 -0
- package/dist/props.d.ts +38 -0
- package/dist/props.d.ts.map +1 -0
- package/dist/props.js +116 -0
- package/dist/props.js.map +1 -0
- package/dist/report.d.ts +42 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +35 -0
- package/dist/report.js.map +1 -0
- package/dist/resolve.d.ts +15 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +35 -0
- package/dist/resolve.js.map +1 -0
- package/dist/run.d.ts +52 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +213 -0
- package/dist/run.js.map +1 -0
- package/dist/splice.d.ts +43 -0
- package/dist/splice.d.ts.map +1 -0
- package/dist/splice.js +90 -0
- package/dist/splice.js.map +1 -0
- package/dist/usage.d.ts +90 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +249 -0
- package/dist/usage.js.map +1 -0
- package/dist/wrap.d.ts +72 -0
- package/dist/wrap.d.ts.map +1 -0
- package/dist/wrap.js +170 -0
- package/dist/wrap.js.map +1 -0
- package/dist/write.d.ts +28 -0
- package/dist/write.d.ts.map +1 -0
- package/dist/write.js +37 -0
- package/dist/write.js.map +1 -0
- package/package.json +54 -0
package/dist/detect.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detection: decide which tag nodes are edit candidates.
|
|
3
|
+
*
|
|
4
|
+
* The guiding rule — proven necessary by real-world fixtures — is that the
|
|
5
|
+
* candidate set must equal *what the CaretCMS rewrite engine can actually
|
|
6
|
+
* round-trip*. The engine only swaps the text content of a pure text-leaf
|
|
7
|
+
* element (no child markup, unless `data-caret-rich`) and the `src` of an
|
|
8
|
+
* `<img>`. So we tag exactly those and nothing else; anything we can't faithfully
|
|
9
|
+
* edit is skipped (with a reason) rather than tagged into an inert binding.
|
|
10
|
+
*/
|
|
11
|
+
import { isTagNode } from "./parse.js";
|
|
12
|
+
// Content tags we'll tag, with their confidence tier. Anything not listed is
|
|
13
|
+
// treated as non-content and skipped (whitelist, not blacklist).
|
|
14
|
+
//
|
|
15
|
+
// The key set MUST equal core's REWRITABLE_TEXT_TAGS (runtime/rewrite.ts):
|
|
16
|
+
// the rewrite engine only injects stored values into those tags, so tagging
|
|
17
|
+
// anything else mints a binding that saves through the editor but never
|
|
18
|
+
// renders to visitors. Held in lockstep by tests/unit/contracts-parity.test.ts.
|
|
19
|
+
export const CONFIDENCE = {
|
|
20
|
+
h1: "high", h2: "high", h3: "high", h4: "high", h5: "high", h6: "high",
|
|
21
|
+
p: "high", blockquote: "high",
|
|
22
|
+
li: "medium", a: "medium", button: "medium", figcaption: "medium",
|
|
23
|
+
summary: "medium", caption: "medium", dt: "medium", dd: "medium",
|
|
24
|
+
th: "medium", td: "medium", label: "medium", legend: "medium",
|
|
25
|
+
span: "low", strong: "low", em: "low", small: "low",
|
|
26
|
+
};
|
|
27
|
+
/** Can the rewrite engine swap this tag's text content? Gate for every tier
|
|
28
|
+
* that emits a text binding (tag pass via CONFIDENCE, bind tiers directly). */
|
|
29
|
+
export function isRewritableTextTag(tag) {
|
|
30
|
+
return tag.toLowerCase() in CONFIDENCE;
|
|
31
|
+
}
|
|
32
|
+
// Always requires the method call (`.map(`); also captures the immediate
|
|
33
|
+
// receiver identifier when it's a plain variable (group 1), so callers can tell
|
|
34
|
+
// whether the loop iterates a name a wrap tier already made editable. Receiver
|
|
35
|
+
// is optional in the capture so chained/expression receivers (`foo().map(`)
|
|
36
|
+
// still flag — they just carry no receiver to match against. Group 2 = method.
|
|
37
|
+
const ITERATOR_RE = /(?:([A-Za-z_$][\w$]*)\s*)?\.\s*(map|filter|forEach|flatMap|reduce)\s*\(/;
|
|
38
|
+
// Inline formatting tags the rich-text sanitizer keeps. MUST match
|
|
39
|
+
// RICH_ALLOWED_TAGS in core's runtime/rich-allowlist.ts (and the hand-mirrored
|
|
40
|
+
// static/cms/editor/sanitize.js) — anything outside this set is unwrapped on
|
|
41
|
+
// save, so promoting it would not round-trip. Held in lockstep by
|
|
42
|
+
// tests/unit/contracts-parity.test.ts.
|
|
43
|
+
export const RICH_INLINE_TAGS = new Set([
|
|
44
|
+
"b", "strong", "i", "em", "u", "s", "a", "br", "sub", "sup",
|
|
45
|
+
]);
|
|
46
|
+
// Attributes the sanitizer keeps. Everything else (notably `class`) is stripped
|
|
47
|
+
// on save unless blessed via the runtime `allowedClasses` option — which the
|
|
48
|
+
// CLI can't see — so caretize stays conservative and treats any attribute here
|
|
49
|
+
// other than these as lossy. MUST match core's RICH_ALLOWED_ATTRS/SAFE_HREF_RE
|
|
50
|
+
// (same parity test).
|
|
51
|
+
export const RICH_SAFE_ATTRS = {
|
|
52
|
+
a: new Set(["href", "target", "rel"]),
|
|
53
|
+
};
|
|
54
|
+
export const SAFE_HREF_RE = /^(?:https?:|mailto:|tel:|\/)/i;
|
|
55
|
+
/** Are this element's own attributes within the sanitizer's keep set? */
|
|
56
|
+
function inlineAttrsSafe(node) {
|
|
57
|
+
const allowed = RICH_SAFE_ATTRS[node.name.toLowerCase()];
|
|
58
|
+
for (const a of node.attributes ?? []) {
|
|
59
|
+
if (!allowed || !allowed.has(a.name))
|
|
60
|
+
return false;
|
|
61
|
+
if (a.name === "href" && !SAFE_HREF_RE.test(a.value ?? ""))
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Classify a mixed-content element's subtree for rich promotion. Walks every
|
|
68
|
+
* descendant: a non-inline tag / component / expression makes it "not-inline";
|
|
69
|
+
* an inline tag carrying a stripped attribute makes it "rich-unsafe-attrs";
|
|
70
|
+
* otherwise "rich-safe". `attrsClean` threads the downgrade through recursion.
|
|
71
|
+
*/
|
|
72
|
+
function classifyRichShape(node) {
|
|
73
|
+
let attrsClean = true;
|
|
74
|
+
const visit = (n) => {
|
|
75
|
+
for (const child of n.children ?? []) {
|
|
76
|
+
if (child.type === "text")
|
|
77
|
+
continue;
|
|
78
|
+
if (child.type === "expression")
|
|
79
|
+
return "not-inline";
|
|
80
|
+
if (isTagNode(child)) {
|
|
81
|
+
if (child.type !== "element")
|
|
82
|
+
return "not-inline"; // component/fragment
|
|
83
|
+
if (!RICH_INLINE_TAGS.has(child.name.toLowerCase()))
|
|
84
|
+
return "not-inline";
|
|
85
|
+
if (!inlineAttrsSafe(child))
|
|
86
|
+
attrsClean = false;
|
|
87
|
+
const nested = visit(child);
|
|
88
|
+
if (nested === "not-inline")
|
|
89
|
+
return "not-inline";
|
|
90
|
+
}
|
|
91
|
+
// comments/doctype are inert — ignore
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
};
|
|
95
|
+
if (visit(node) === "not-inline")
|
|
96
|
+
return "not-inline";
|
|
97
|
+
return attrsClean ? "rich-safe" : "rich-unsafe-attrs";
|
|
98
|
+
}
|
|
99
|
+
function attr(node, name) {
|
|
100
|
+
return node.attributes.find((a) => a.name === name)?.value;
|
|
101
|
+
}
|
|
102
|
+
function hasCaretAttr(node) {
|
|
103
|
+
return node.attributes.some((a) => a.name === "data-caret" || a.name === "data-caret-scope");
|
|
104
|
+
}
|
|
105
|
+
/** Direct text content of an element (concatenated text-node children). */
|
|
106
|
+
function directText(node) {
|
|
107
|
+
const parts = [];
|
|
108
|
+
for (const child of node.children ?? []) {
|
|
109
|
+
if (child.type === "text")
|
|
110
|
+
parts.push(child.value ?? "");
|
|
111
|
+
}
|
|
112
|
+
return parts.join("");
|
|
113
|
+
}
|
|
114
|
+
/** Classify the children of a (non-image) element. */
|
|
115
|
+
function childShape(node) {
|
|
116
|
+
let sawText = false;
|
|
117
|
+
for (const child of node.children ?? []) {
|
|
118
|
+
if (child.type === "text") {
|
|
119
|
+
if ((child.value ?? "").trim() !== "")
|
|
120
|
+
sawText = true;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (child.type === "expression")
|
|
124
|
+
return "has-expression";
|
|
125
|
+
if (isTagNode(child))
|
|
126
|
+
return "has-element";
|
|
127
|
+
// comments/doctype/etc. are inert — ignore
|
|
128
|
+
}
|
|
129
|
+
return sawText ? "pure-text" : "empty";
|
|
130
|
+
}
|
|
131
|
+
function nearestExpressionAncestor(ancestors) {
|
|
132
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
133
|
+
if (ancestors[i].type === "expression")
|
|
134
|
+
return ancestors[i];
|
|
135
|
+
}
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
/** Concatenated text of an expression node's direct text children (the JS). */
|
|
139
|
+
function expressionText(expr) {
|
|
140
|
+
const parts = [];
|
|
141
|
+
for (const child of expr.children ?? []) {
|
|
142
|
+
if (child.type === "text")
|
|
143
|
+
parts.push(child.value ?? "");
|
|
144
|
+
}
|
|
145
|
+
return parts.join("");
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Walk a parsed root and classify every tag node.
|
|
149
|
+
*
|
|
150
|
+
* `walk` is injected (the caller passes `walkTags`) so this module stays a pure
|
|
151
|
+
* classifier with no parse dependency beyond the node shape.
|
|
152
|
+
*/
|
|
153
|
+
export function detect(root, walk, options = {}) {
|
|
154
|
+
const candidates = [];
|
|
155
|
+
const skipped = [];
|
|
156
|
+
const flagsByOffset = new Map();
|
|
157
|
+
// Nodes promoted to data-caret-rich. Their descendants must NOT be tagged
|
|
158
|
+
// separately (the rich field owns the whole subtree); walkTags is preorder,
|
|
159
|
+
// so a host is recorded before its descendants are visited.
|
|
160
|
+
const richHosts = new Set();
|
|
161
|
+
walk(root, (node, ancestors) => {
|
|
162
|
+
const tag = node.name;
|
|
163
|
+
const startOffset = node.position?.start.offset ?? -1;
|
|
164
|
+
const skip = (reason) => {
|
|
165
|
+
skipped.push({ decision: "skip", node, tag, startOffset, reason });
|
|
166
|
+
};
|
|
167
|
+
if (hasCaretAttr(node))
|
|
168
|
+
return skip("already-tagged");
|
|
169
|
+
// Inside a node we already promoted to rich → the rich field owns it.
|
|
170
|
+
if (ancestors.some((a) => richHosts.has(a)))
|
|
171
|
+
return skip("inside-rich");
|
|
172
|
+
if (node.type !== "element")
|
|
173
|
+
return skip("component"); // component/custom-element/fragment
|
|
174
|
+
// Dynamic context: inside any {expression}. Record an iterator flag if the
|
|
175
|
+
// enclosing expression looks like a .map()/.filter() loop.
|
|
176
|
+
const expr = nearestExpressionAncestor(ancestors);
|
|
177
|
+
if (expr) {
|
|
178
|
+
const text = expressionText(expr);
|
|
179
|
+
const m = ITERATOR_RE.exec(text);
|
|
180
|
+
const exprOffset = expr.position?.start.offset ?? startOffset;
|
|
181
|
+
if (m && !flagsByOffset.has(exprOffset)) {
|
|
182
|
+
flagsByOffset.set(exprOffset, {
|
|
183
|
+
startOffset: exprOffset, method: m[2], receiver: m[1],
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return skip(m ? "inside-iterator" : "inside-expression");
|
|
187
|
+
}
|
|
188
|
+
if (tag === "img") {
|
|
189
|
+
const src = attr(node, "src");
|
|
190
|
+
if (src === undefined || src.trim() === "")
|
|
191
|
+
return skip("empty");
|
|
192
|
+
candidates.push({
|
|
193
|
+
decision: "tag", kind: "image", node, tag, startOffset,
|
|
194
|
+
confidence: "high", text: src,
|
|
195
|
+
});
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (!(tag in CONFIDENCE))
|
|
199
|
+
return skip("not-content");
|
|
200
|
+
const shape = childShape(node);
|
|
201
|
+
if (shape === "has-expression")
|
|
202
|
+
return skip("dynamic-content");
|
|
203
|
+
if (shape === "empty")
|
|
204
|
+
return skip("empty");
|
|
205
|
+
if (shape === "has-element") {
|
|
206
|
+
// Mixed content: a block/component child can't round-trip as a field, but
|
|
207
|
+
// a subtree of only sanitizer-safe inline markup can — as a rich field.
|
|
208
|
+
// Only promote genuine content hosts (block text / links), not the
|
|
209
|
+
// low-confidence generic inline containers (span/div/strong/em/…).
|
|
210
|
+
const richShape = classifyRichShape(node);
|
|
211
|
+
const promotable = CONFIDENCE[tag] !== "low";
|
|
212
|
+
if (richShape === "not-inline" || !promotable)
|
|
213
|
+
return skip("mixed-children");
|
|
214
|
+
if (richShape === "rich-unsafe-attrs")
|
|
215
|
+
return skip("rich-unsafe-attrs");
|
|
216
|
+
// rich-safe: promote only when opted in; otherwise report it as eligible.
|
|
217
|
+
if (!options.rich)
|
|
218
|
+
return skip("rich-eligible");
|
|
219
|
+
richHosts.add(node);
|
|
220
|
+
candidates.push({
|
|
221
|
+
decision: "tag", kind: "text", node, tag, startOffset,
|
|
222
|
+
confidence: "medium", text: directText(node).trim(), rich: true,
|
|
223
|
+
});
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
candidates.push({
|
|
227
|
+
decision: "tag", kind: "text", node, tag, startOffset,
|
|
228
|
+
confidence: CONFIDENCE[tag], text: directText(node).trim(),
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
return {
|
|
232
|
+
candidates,
|
|
233
|
+
skipped,
|
|
234
|
+
flags: [...flagsByOffset.values()].sort((a, b) => a.startOffset - b.startOffset),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discovery: find the `.astro` files to consider, relative to the project root.
|
|
3
|
+
*
|
|
4
|
+
* Never descends into build/vendor/output dirs, and never touches symlinks that
|
|
5
|
+
* escape the project root (a safety invariant — caretize only edits files
|
|
6
|
+
* inside the project it was pointed at).
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Walk `target` (a file or directory) under `rootDir`, returning project-root-
|
|
10
|
+
* relative POSIX-ish paths of `.astro` files, sorted for deterministic order.
|
|
11
|
+
*/
|
|
12
|
+
export declare function discoverAstroFiles(rootDir: string, target?: string): string[];
|
|
13
|
+
//# sourceMappingURL=discover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2BH;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CA8C7E"}
|
package/dist/discover.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discovery: find the `.astro` files to consider, relative to the project root.
|
|
3
|
+
*
|
|
4
|
+
* Never descends into build/vendor/output dirs, and never touches symlinks that
|
|
5
|
+
* escape the project root (a safety invariant — caretize only edits files
|
|
6
|
+
* inside the project it was pointed at).
|
|
7
|
+
*/
|
|
8
|
+
import { readdirSync, statSync, lstatSync, realpathSync } from "node:fs";
|
|
9
|
+
import { join, relative, resolve, sep } from "node:path";
|
|
10
|
+
const SKIP_DIRS = new Set([
|
|
11
|
+
"node_modules",
|
|
12
|
+
".astro",
|
|
13
|
+
".caret",
|
|
14
|
+
".git",
|
|
15
|
+
"dist",
|
|
16
|
+
".vercel",
|
|
17
|
+
".netlify",
|
|
18
|
+
".output",
|
|
19
|
+
]);
|
|
20
|
+
function isSkippedFile(name) {
|
|
21
|
+
if (!name.endsWith(".astro"))
|
|
22
|
+
return true;
|
|
23
|
+
return /\.(test|spec)\.astro$/.test(name);
|
|
24
|
+
}
|
|
25
|
+
/** True if `child` resolves to a path inside `root` (no symlink escape). */
|
|
26
|
+
function isInside(root, child) {
|
|
27
|
+
const rel = relative(root, child);
|
|
28
|
+
return rel === "" || (!rel.startsWith("..") && !resolve(rel).startsWith(".."));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Walk `target` (a file or directory) under `rootDir`, returning project-root-
|
|
32
|
+
* relative POSIX-ish paths of `.astro` files, sorted for deterministic order.
|
|
33
|
+
*/
|
|
34
|
+
export function discoverAstroFiles(rootDir, target) {
|
|
35
|
+
const root = resolve(rootDir);
|
|
36
|
+
const start = resolve(root, target ?? "src");
|
|
37
|
+
const found = [];
|
|
38
|
+
const visit = (abs) => {
|
|
39
|
+
let st;
|
|
40
|
+
try {
|
|
41
|
+
st = lstatSync(abs);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Reject symlinks that point outside the project root.
|
|
47
|
+
if (st.isSymbolicLink()) {
|
|
48
|
+
let realPath;
|
|
49
|
+
try {
|
|
50
|
+
realPath = realpathSync(abs);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (!isInside(root, realPath))
|
|
56
|
+
return;
|
|
57
|
+
st = statSync(abs);
|
|
58
|
+
}
|
|
59
|
+
if (st.isDirectory()) {
|
|
60
|
+
const base = abs.split(sep).pop() ?? "";
|
|
61
|
+
if (SKIP_DIRS.has(base))
|
|
62
|
+
return;
|
|
63
|
+
let entries;
|
|
64
|
+
try {
|
|
65
|
+
entries = readdirSync(abs);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
for (const name of entries.sort())
|
|
71
|
+
visit(join(abs, name));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (st.isFile()) {
|
|
75
|
+
const base = abs.split(sep).pop() ?? "";
|
|
76
|
+
if (isSkippedFile(base))
|
|
77
|
+
return;
|
|
78
|
+
found.push(relative(root, abs).split(sep).join("/"));
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
visit(start);
|
|
82
|
+
return found;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=discover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frontmatter parsing primitives shared by the wrap tiers (`wrap.ts`'s same-file
|
|
3
|
+
* loop detection and `props.ts`'s cross-file prop detection). Kept neutral so
|
|
4
|
+
* those two passes are siblings rather than one depending on the other.
|
|
5
|
+
*/
|
|
6
|
+
/** Inner content range of the frontmatter fence, or null if there is none. */
|
|
7
|
+
export declare function frontmatterRange(source: string): {
|
|
8
|
+
start: number;
|
|
9
|
+
end: number;
|
|
10
|
+
} | null;
|
|
11
|
+
/** Names of frontmatter consts whose initializer is an array/object literal. */
|
|
12
|
+
export declare function literalConstNames(fmText: string): Set<string>;
|
|
13
|
+
export type ImportKind = "json" | "module";
|
|
14
|
+
/**
|
|
15
|
+
* Default-import bindings whose data could be wrapped with `editable()`, mapped
|
|
16
|
+
* to their specifier + kind. Only DEFAULT imports of a `.json` data file or a
|
|
17
|
+
* `.js`/`.ts` module are returned — named imports (`import { x }`), namespace
|
|
18
|
+
* imports (`import * as`), `import type`, and bare/package or extensionless
|
|
19
|
+
* specifiers are excluded, because their value shape can't be assumed safe to
|
|
20
|
+
* stega-encode. `import faqs from "./data/faqs.json"` → `faqs → {json}`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function importBindingNames(fmText: string): Map<string, {
|
|
23
|
+
specifier: string;
|
|
24
|
+
importKind: ImportKind;
|
|
25
|
+
}>;
|
|
26
|
+
//# sourceMappingURL=frontmatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../src/frontmatter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAOtF;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAM7D;AAED,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE3C;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,GACb,GAAG,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,UAAU,CAAA;CAAE,CAAC,CAa5D"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frontmatter parsing primitives shared by the wrap tiers (`wrap.ts`'s same-file
|
|
3
|
+
* loop detection and `props.ts`'s cross-file prop detection). Kept neutral so
|
|
4
|
+
* those two passes are siblings rather than one depending on the other.
|
|
5
|
+
*/
|
|
6
|
+
/** Inner content range of the frontmatter fence, or null if there is none. */
|
|
7
|
+
export function frontmatterRange(source) {
|
|
8
|
+
if (!source.startsWith("---"))
|
|
9
|
+
return null;
|
|
10
|
+
const firstNL = source.indexOf("\n");
|
|
11
|
+
if (firstNL < 0)
|
|
12
|
+
return null;
|
|
13
|
+
const close = source.indexOf("\n---", firstNL);
|
|
14
|
+
if (close < 0)
|
|
15
|
+
return null;
|
|
16
|
+
return { start: firstNL + 1, end: close };
|
|
17
|
+
}
|
|
18
|
+
/** Names of frontmatter consts whose initializer is an array/object literal. */
|
|
19
|
+
export function literalConstNames(fmText) {
|
|
20
|
+
const set = new Set();
|
|
21
|
+
const re = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*([[{])/g;
|
|
22
|
+
let m;
|
|
23
|
+
while ((m = re.exec(fmText)))
|
|
24
|
+
set.add(m[1]);
|
|
25
|
+
return set;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Default-import bindings whose data could be wrapped with `editable()`, mapped
|
|
29
|
+
* to their specifier + kind. Only DEFAULT imports of a `.json` data file or a
|
|
30
|
+
* `.js`/`.ts` module are returned — named imports (`import { x }`), namespace
|
|
31
|
+
* imports (`import * as`), `import type`, and bare/package or extensionless
|
|
32
|
+
* specifiers are excluded, because their value shape can't be assumed safe to
|
|
33
|
+
* stega-encode. `import faqs from "./data/faqs.json"` → `faqs → {json}`.
|
|
34
|
+
*/
|
|
35
|
+
export function importBindingNames(fmText) {
|
|
36
|
+
const out = new Map();
|
|
37
|
+
const re = /\bimport\s+(?!type\b)([A-Za-z_$][\w$]*)\s+from\s*['"]([^'"]+)['"]/g;
|
|
38
|
+
let m;
|
|
39
|
+
while ((m = re.exec(fmText))) {
|
|
40
|
+
const [, name, spec] = m;
|
|
41
|
+
let importKind;
|
|
42
|
+
if (/\.json$/.test(spec))
|
|
43
|
+
importKind = "json";
|
|
44
|
+
else if (/\.(?:js|ts|mjs|cjs|mts|cts)$/.test(spec))
|
|
45
|
+
importKind = "module";
|
|
46
|
+
else
|
|
47
|
+
continue; // bare/package or extensionless — shape unknown, leave alone
|
|
48
|
+
out.set(name, { specifier: spec, importKind });
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../src/frontmatter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,8EAA8E;AAC9E,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,OAAO,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3B,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;AAC5C,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,MAAM,EAAE,GAAG,wDAAwD,CAAC;IACpE,IAAI,CAAyB,CAAC;IAC9B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,OAAO,GAAG,CAAC;AACb,CAAC;AAID;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAc;IAEd,MAAM,GAAG,GAAG,IAAI,GAAG,EAAyD,CAAC;IAC7E,MAAM,EAAE,GAAG,oEAAoE,CAAC;IAChF,IAAI,CAAyB,CAAC;IAC9B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,UAAsB,CAAC;QAC3B,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,UAAU,GAAG,MAAM,CAAC;aACzC,IAAI,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,UAAU,GAAG,QAAQ,CAAC;;YACrE,SAAS,CAAC,6DAA6D;QAC5E,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared JS-identifier primitives used across the wrap/usage/resolve passes.
|
|
3
|
+
*
|
|
4
|
+
* Centralised so "what is an identifier" and "how do we match a reference to
|
|
5
|
+
* one" have a single definition — the matcher in particular is safety-relevant
|
|
6
|
+
* (a missed reference can let `editable()` encode into an attribute), so it must
|
|
7
|
+
* not drift between callers.
|
|
8
|
+
*/
|
|
9
|
+
/** A valid JS identifier (ASCII letters/digits plus `_` and `$`). */
|
|
10
|
+
export declare const IDENT: RegExp;
|
|
11
|
+
export declare function isIdentifier(s: string): boolean;
|
|
12
|
+
/** Escape a string for safe literal use inside a `RegExp`. */
|
|
13
|
+
export declare function escapeRe(s: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* A regex matching a whole-identifier reference to `name` in JS source.
|
|
16
|
+
*
|
|
17
|
+
* Uses `$`/`_`-aware lookaround boundaries rather than `\b`: `\b` sits between a
|
|
18
|
+
* word and a non-word char, so `\b$item\b` does NOT match `$item` (the `$` is a
|
|
19
|
+
* non-word char with no word char before it). Lookaround treats `$` and `_` as
|
|
20
|
+
* identifier characters, so `$item` and `item$` are bounded correctly while a
|
|
21
|
+
* substring like `item` inside `items` is still rejected.
|
|
22
|
+
*/
|
|
23
|
+
export declare function identRefRe(name: string, flags?: string): RegExp;
|
|
24
|
+
/** True when `js` references any of the given identifiers as a whole word. */
|
|
25
|
+
export declare function referencesIdent(js: string, names: readonly string[]): boolean;
|
|
26
|
+
//# sourceMappingURL=identifiers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identifiers.d.ts","sourceRoot":"","sources":["../src/identifiers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qEAAqE;AACrE,eAAO,MAAM,KAAK,QAAuB,CAAC;AAE1C,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,8DAA8D;AAC9D,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,MAAM,CAE3D;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAE7E"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared JS-identifier primitives used across the wrap/usage/resolve passes.
|
|
3
|
+
*
|
|
4
|
+
* Centralised so "what is an identifier" and "how do we match a reference to
|
|
5
|
+
* one" have a single definition — the matcher in particular is safety-relevant
|
|
6
|
+
* (a missed reference can let `editable()` encode into an attribute), so it must
|
|
7
|
+
* not drift between callers.
|
|
8
|
+
*/
|
|
9
|
+
/** A valid JS identifier (ASCII letters/digits plus `_` and `$`). */
|
|
10
|
+
export const IDENT = /^[A-Za-z_$][\w$]*$/;
|
|
11
|
+
export function isIdentifier(s) {
|
|
12
|
+
return IDENT.test(s);
|
|
13
|
+
}
|
|
14
|
+
/** Escape a string for safe literal use inside a `RegExp`. */
|
|
15
|
+
export function escapeRe(s) {
|
|
16
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A regex matching a whole-identifier reference to `name` in JS source.
|
|
20
|
+
*
|
|
21
|
+
* Uses `$`/`_`-aware lookaround boundaries rather than `\b`: `\b` sits between a
|
|
22
|
+
* word and a non-word char, so `\b$item\b` does NOT match `$item` (the `$` is a
|
|
23
|
+
* non-word char with no word char before it). Lookaround treats `$` and `_` as
|
|
24
|
+
* identifier characters, so `$item` and `item$` are bounded correctly while a
|
|
25
|
+
* substring like `item` inside `items` is still rejected.
|
|
26
|
+
*/
|
|
27
|
+
export function identRefRe(name, flags = "") {
|
|
28
|
+
return new RegExp(`(?<![\\w$])${escapeRe(name)}(?![\\w$])`, flags);
|
|
29
|
+
}
|
|
30
|
+
/** True when `js` references any of the given identifiers as a whole word. */
|
|
31
|
+
export function referencesIdent(js, names) {
|
|
32
|
+
return names.some((n) => identRefRe(n).test(js));
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=identifiers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identifiers.js","sourceRoot":"","sources":["../src/identifiers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qEAAqE;AACrE,MAAM,CAAC,MAAM,KAAK,GAAG,oBAAoB,CAAC;AAE1C,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACvB,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,QAAQ,CAAC,CAAS;IAChC,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,KAAK,GAAG,EAAE;IACjD,OAAO,IAAI,MAAM,CAAC,cAAc,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;AACrE,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,eAAe,CAAC,EAAU,EAAE,KAAwB;IAClE,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier-3 editable() wrapping for IMPORTED data — PURE INSERTION.
|
|
3
|
+
*
|
|
4
|
+
* A very common Astro pattern keeps list content in a data file and renders it
|
|
5
|
+
* with `.map()`:
|
|
6
|
+
*
|
|
7
|
+
* import faqs from "./data/faqs.json";
|
|
8
|
+
* …
|
|
9
|
+
* {faqs.map((f) => <details>{f.question}…</details>)}
|
|
10
|
+
*
|
|
11
|
+
* caretize's Tier-1 wrap only sees inline-literal consts (`const x = [...]`), so
|
|
12
|
+
* this content was previously left un-editable. Tier-3 makes it editable WITHOUT
|
|
13
|
+
* moving any code: it suffixes the import binding and rebinds the original name
|
|
14
|
+
* to an `editable()` wrap of it —
|
|
15
|
+
*
|
|
16
|
+
* import faqsRaw from "./data/faqs.json";
|
|
17
|
+
* const faqs = await editable("pages::home::faqs", faqsRaw);
|
|
18
|
+
*
|
|
19
|
+
* Every original character survives in order (the import binding only GAINS a
|
|
20
|
+
* suffix; a new const line is inserted), so the result is a provable pure
|
|
21
|
+
* insertion — the same subsequence safety contract as Tier-1. The `.map()`
|
|
22
|
+
* consumer is untouched and now reads the stega-encoded, click-to-edit value.
|
|
23
|
+
*/
|
|
24
|
+
import { type AstroNode } from "./parse.js";
|
|
25
|
+
import { type ImportKind } from "./frontmatter.js";
|
|
26
|
+
import { type WrapResult, type WrapTarget } from "./wrap.js";
|
|
27
|
+
/** An import binding the wrapper could target, before safety is verified. */
|
|
28
|
+
export interface ImportWrapCandidate {
|
|
29
|
+
/** The original local binding, e.g. `faqs`. */
|
|
30
|
+
varName: string;
|
|
31
|
+
/** Binding key, e.g. `pages::home::faqs`. */
|
|
32
|
+
key: string;
|
|
33
|
+
/** The import specifier, e.g. `./data/faqs.json` (for display). */
|
|
34
|
+
specifier: string;
|
|
35
|
+
/** Whether the source is a `.json` data file or a `.js`/`.ts` module. */
|
|
36
|
+
importKind: ImportKind;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Rebind an imported `varName` to `await editable("<key>", <varName>Raw)` and
|
|
40
|
+
* ensure the import is present. Pure insertion: the import binding gains a
|
|
41
|
+
* suffix and a new const line is inserted; nothing is deleted.
|
|
42
|
+
*/
|
|
43
|
+
export declare function wrapImport(source: string, varName: string, key: string): WrapResult;
|
|
44
|
+
/**
|
|
45
|
+
* Detect Tier-3 candidates: DEFAULT imports of a `.json`/module data file that
|
|
46
|
+
* are `.map()`/`.flatMap()`'d in the template. Key is derived from the file's
|
|
47
|
+
* scope (`collection::id`) plus the binding name as the field.
|
|
48
|
+
*/
|
|
49
|
+
export declare function detectImportWrapCandidates(source: string, relPath: string): ImportWrapCandidate[];
|
|
50
|
+
/**
|
|
51
|
+
* Safety-checked Tier-3 detection: import-backed candidates minus any whose
|
|
52
|
+
* mapped fields flow into a native-element attribute (where `editable()`'s stega
|
|
53
|
+
* encoding would corrupt an href/src/class/…). Mirrors `detectWrapTargetsSafe`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function detectImportWrapTargetsSafe(source: string, relPath: string, root?: AstroNode): Promise<WrapTarget[]>;
|
|
56
|
+
//# sourceMappingURL=import-wrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-wrap.d.ts","sourceRoot":"","sources":["../src/import-wrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAA0B,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAErF,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAClC,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,UAAU,EAAE,UAAU,CAAC;CACxB;AAWD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAuDnF;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,mBAAmB,EAAE,CAuCvB;AAED;;;;GAIG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,SAAS,GACf,OAAO,CAAC,UAAU,EAAE,CAAC,CAOvB"}
|