@samva/markup 0.8.0 → 0.10.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/AGENTS.md +107 -46
- package/CHANGELOG.md +345 -0
- package/README.md +188 -52
- package/dist/{brand-DcoVkh_S.d.ts → assets-BIsyMQ92.d.ts} +41 -11
- package/dist/{email/brand.js → brand-C0MKTwmX.js} +64 -4
- package/dist/{theme-css.d.ts → brand.d.ts} +12 -3
- package/dist/brand.js +4 -0
- package/dist/channel-specs-C2feJwuP.d.ts +13 -0
- package/dist/compiler.d.ts +88 -115
- package/dist/compiler.js +750 -171
- package/dist/components-BZCqegS8.js +1864 -0
- package/dist/diagnostic-model-DJtqbETd.js +38 -0
- package/dist/{diagnostics-OcQKQYSY.d.ts → diagnostic-model-DxthUZBT.d.ts} +3 -21
- package/dist/{diagnostics-BMYtBCQk.d.ts → diagnostics-B-Gym34R.d.ts} +8 -4
- package/dist/{email/diagnostics.js → diagnostics-CnKFefKz.js} +4 -3
- package/dist/diagnostics.d.ts +410 -2
- package/dist/diagnostics.js +402 -52
- package/dist/edit.d.ts +234 -0
- package/dist/edit.js +947 -0
- package/dist/email/jsx-dev-runtime.d.ts +2 -2
- package/dist/email/jsx-dev-runtime.js +1 -2
- package/dist/email/jsx-runtime.d.ts +1 -1
- package/dist/email/jsx-runtime.js +1 -85
- package/dist/{email/components.d.ts → email.d.ts} +22 -11
- package/dist/email.js +2 -0
- package/dist/fmt.d.ts +32 -0
- package/dist/fmt.js +174 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/{render-vAv_4r9v.js → ir-CLhlklKV.js} +96 -279
- package/dist/jsx-runtime-CBlexoeR.js +98 -0
- package/dist/{elements-BzjjnoCS.d.ts → jsx-runtime-ylIOnaSf.d.ts} +76 -76
- package/dist/profile-pWGtWZXF.js +3803 -0
- package/dist/render-BPonDpCl.d.ts +539 -0
- package/dist/render-BQmfh94k.js +257 -0
- package/dist/render.d.ts +35 -0
- package/dist/render.js +49 -0
- package/dist/sms.d.ts +16 -0
- package/dist/sms.js +12 -0
- package/dist/source-locations-C8wCsiAy.d.ts +23 -0
- package/dist/template-DMPwxMSq.d.ts +65 -0
- package/dist/template-zLUjcwfF.js +42 -0
- package/dist/{theme-css-hFqcPSar.js → theme-css-JtRCm1fb.js} +2 -2
- package/dist/whatsapp.d.ts +57 -0
- package/dist/whatsapp.js +22 -0
- package/generated/contract.json +1788 -0
- package/generated/reference.md +360 -0
- package/package.json +46 -239
- package/samva-brand.d.ts +4 -4
- package/skills/sml/SKILL.md +123 -0
- package/dist/ast-BrDtApwS.d.ts +0 -107
- package/dist/ast.d.ts +0 -2
- package/dist/ast.js +0 -124
- package/dist/candidate.d.ts +0 -29
- package/dist/candidate.js +0 -88
- package/dist/catalog-CKuOooB8.d.ts +0 -354
- package/dist/checks-D73Xizbw.js +0 -59366
- package/dist/checks.d.ts +0 -33
- package/dist/checks.js +0 -2
- package/dist/classes.d.ts +0 -26
- package/dist/classes.js +0 -184
- package/dist/compile-sms.d.ts +0 -28
- package/dist/compile-sms.js +0 -44
- package/dist/compile-whatsapp.d.ts +0 -56
- package/dist/compile-whatsapp.js +0 -186
- package/dist/compile.d.ts +0 -18
- package/dist/compile.js +0 -29
- package/dist/components.d.ts +0 -289
- package/dist/components.js +0 -106
- package/dist/conditionals.d.ts +0 -10
- package/dist/conditionals.js +0 -74
- package/dist/corpus-CPPfgAHN.d.ts +0 -106
- package/dist/css-CS2jLID6.d.ts +0 -109
- package/dist/email/assets.d.ts +0 -40
- package/dist/email/assets.js +0 -63
- package/dist/email/brand-footer.d.ts +0 -34
- package/dist/email/brand-footer.js +0 -62
- package/dist/email/brand.d.ts +0 -2
- package/dist/email/components.js +0 -240
- package/dist/email/diagnostics.d.ts +0 -2
- package/dist/email/elements.d.ts +0 -2
- package/dist/email/elements.js +0 -299
- package/dist/email/html.d.ts +0 -2
- package/dist/email/html.js +0 -278
- package/dist/email/render.d.ts +0 -50
- package/dist/email/render.js +0 -3
- package/dist/email/selection.d.ts +0 -2
- package/dist/email/selection.js +0 -91
- package/dist/email/source-edit.d.ts +0 -67
- package/dist/email/source-edit.js +0 -185
- package/dist/email/stylesheets.d.ts +0 -15
- package/dist/email/stylesheets.js +0 -48
- package/dist/email/text.d.ts +0 -7
- package/dist/email/text.js +0 -100
- package/dist/emit.d.ts +0 -51
- package/dist/emit.js +0 -151
- package/dist/html-Bl9BAdqc.d.ts +0 -42
- package/dist/interpolate.d.ts +0 -24
- package/dist/interpolate.js +0 -43
- package/dist/jsx-dev-runtime.d.ts +0 -11
- package/dist/jsx-dev-runtime.js +0 -15
- package/dist/jsx-runtime-I-1nCtl2.d.ts +0 -57
- package/dist/jsx-runtime.d.ts +0 -2
- package/dist/jsx-runtime.js +0 -121
- package/dist/lexer.d.ts +0 -60
- package/dist/lexer.js +0 -281
- package/dist/model.d.ts +0 -24
- package/dist/model.js +0 -77
- package/dist/ops-BvwqHl7V.js +0 -630
- package/dist/ops.d.ts +0 -114
- package/dist/ops.js +0 -2
- package/dist/parser-t29cHLVL.d.ts +0 -396
- package/dist/parser.d.ts +0 -2
- package/dist/parser.js +0 -859
- package/dist/predicate-BI6uRRM-.js +0 -549
- package/dist/predicate-M-05Nd7R.d.ts +0 -56
- package/dist/predicate.d.ts +0 -2
- package/dist/predicate.js +0 -2
- package/dist/printer.d.ts +0 -6
- package/dist/printer.js +0 -122
- package/dist/registry-Dcp6QEK_.d.ts +0 -73
- package/dist/registry.d.ts +0 -2
- package/dist/registry.js +0 -646
- package/dist/render-template.d.ts +0 -45
- package/dist/render-template.js +0 -76
- package/dist/selection-CCArx9lU.d.ts +0 -91
- package/dist/showcase-egkusQBf.d.ts +0 -415
- package/dist/sms-segments-y4FpPilJ.d.ts +0 -13
- package/dist/sms-segments.d.ts +0 -2
- package/dist/sms-segments.js +0 -46
- package/dist/source-locations-DrCM82Ym.d.ts +0 -67
- package/dist/source-locations.d.ts +0 -2
- package/dist/source-locations.js +0 -102
- package/dist/starters/category-mapping.d.ts +0 -13
- package/dist/starters/category-mapping.js +0 -49
- package/dist/starters/corpus.d.ts +0 -2
- package/dist/starters/corpus.js +0 -194
- package/dist/starters/library.d.ts +0 -18
- package/dist/starters/library.js +0 -692
- package/dist/starters/public-gallery.d.ts +0 -266
- package/dist/starters/public-gallery.js +0 -916
- package/dist/starters/public-handoff.d.ts +0 -164
- package/dist/starters/public-handoff.js +0 -361
- package/dist/starters/public-preview-artifacts.d.ts +0 -43
- package/dist/starters/public-preview-artifacts.js +0 -104
- package/dist/starters/public-preview.d.ts +0 -61
- package/dist/starters/public-preview.js +0 -83
- package/dist/starters/showcase.d.ts +0 -2
- package/dist/starters/showcase.js +0 -335
- package/dist/starters/sources.d.ts +0 -45
- package/dist/starters/sources.js +0 -7762
- package/dist/starters/theme.d.ts +0 -45
- package/dist/starters/theme.js +0 -77
- package/dist/starters.d.ts +0 -2
- package/dist/starters.js +0 -874
- package/dist/tailwind-BYq7Lo6u.js +0 -727
- package/dist/template-DWftqh4x.d.ts +0 -60
- package/dist/template.d.ts +0 -2
- package/dist/template.js +0 -37
- package/dist/theme-css.js +0 -2
- package/dist/tsx-source-instrumentation.d.ts +0 -30
- package/dist/tsx-source-instrumentation.js +0 -115
- package/dist/tsx-tracer.d.ts +0 -112
- package/dist/tsx-tracer.js +0 -985
- package/dist/variables-J_L3qxRa.d.ts +0 -77
- package/dist/variables.d.ts +0 -2
- package/dist/variables.js +0 -141
- package/dist/version-BITr58kk.js +0 -96
- package/generated/sml-agent-contract.json +0 -709
- package/generated/sml-email-agent-contract.json +0 -615
- package/generated/sml-email-language-reference.md +0 -94
- package/generated/sml-language-reference.md +0 -195
package/dist/email/selection.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
//#region src/email/selection.ts
|
|
2
|
-
/**
|
|
3
|
-
* The attribute a preview stamps so a click in a rendered document resolves to
|
|
4
|
-
* exactly one rendered element. A delivered message never carries it.
|
|
5
|
-
*/
|
|
6
|
-
const INSTANCE_PATH_ATTRIBUTE = "data-samva-instance";
|
|
7
|
-
const locationKey = (origin) => `${origin.fileName}:${origin.lineNumber}:${origin.columnNumber}`;
|
|
8
|
-
/** One call site: the authored element and every component call enclosing it. */
|
|
9
|
-
const callSiteKey = (selection) => `${selection.tag}\u0000${selection.origins.map(locationKey).join("\0")}`;
|
|
10
|
-
/** One authored element, however many call sites reach it. */
|
|
11
|
-
const definitionKey = (selection) => {
|
|
12
|
-
const authoring = selection.origins[0];
|
|
13
|
-
return `${selection.tag}\u0000${authoring === void 0 ? "" : locationKey(authoring)}`;
|
|
14
|
-
};
|
|
15
|
-
const parentOf = (instancePath) => {
|
|
16
|
-
const cut = instancePath.lastIndexOf(".");
|
|
17
|
-
return cut === -1 ? void 0 : instancePath.slice(0, cut);
|
|
18
|
-
};
|
|
19
|
-
/**
|
|
20
|
-
* Turn one render's emitted positions into selections, in document order.
|
|
21
|
-
*
|
|
22
|
-
* The serializer records an element once its content is written, so positions
|
|
23
|
-
* arrive innermost-first; document order is what an outline and an ancestry
|
|
24
|
-
* walk both need.
|
|
25
|
-
*/
|
|
26
|
-
const emailSelections = (positions) => {
|
|
27
|
-
const counts = /* @__PURE__ */ new Map();
|
|
28
|
-
for (const position of positions) {
|
|
29
|
-
const key = callSiteKey(position);
|
|
30
|
-
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
31
|
-
}
|
|
32
|
-
const seen = /* @__PURE__ */ new Map();
|
|
33
|
-
return [...positions].toSorted((left, right) => left.start === right.start ? right.end - left.end : left.start - right.start).map((position) => {
|
|
34
|
-
const key = callSiteKey(position);
|
|
35
|
-
const occurrence = (seen.get(key) ?? 0) + 1;
|
|
36
|
-
seen.set(key, occurrence);
|
|
37
|
-
const parentPath = parentOf(position.instancePath);
|
|
38
|
-
return {
|
|
39
|
-
instancePath: position.instancePath,
|
|
40
|
-
tag: position.tag,
|
|
41
|
-
origins: position.origins,
|
|
42
|
-
authored: position.authored,
|
|
43
|
-
occurrence,
|
|
44
|
-
occurrences: counts.get(key) ?? 1,
|
|
45
|
-
...parentPath === void 0 ? {} : { parentPath },
|
|
46
|
-
start: position.start,
|
|
47
|
-
end: position.end
|
|
48
|
-
};
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
/** Look selections up by the instance path a rendered document carries. */
|
|
52
|
-
const emailSelectionIndex = (selections) => new Map(selections.map((selection) => [selection.instancePath, selection]));
|
|
53
|
-
/**
|
|
54
|
-
* The authoring element a selection edits. For an authored element that is the
|
|
55
|
-
* element itself; for a generated wrapper it is the primitive that asked for
|
|
56
|
-
* it. Undefined only where the compiler generated markup with no authoring on
|
|
57
|
-
* the stack, which nothing can edit in place.
|
|
58
|
-
*/
|
|
59
|
-
const emailAuthoringTarget = (selection) => selection.origins[0];
|
|
60
|
-
/** Root-first ancestry of a selection, itself last. */
|
|
61
|
-
const emailSelectionAncestry = (index, instancePath) => {
|
|
62
|
-
const chain = [];
|
|
63
|
-
let current = index.get(instancePath);
|
|
64
|
-
while (current !== void 0) {
|
|
65
|
-
chain.unshift(current);
|
|
66
|
-
current = current.parentPath === void 0 ? void 0 : index.get(current.parentPath);
|
|
67
|
-
}
|
|
68
|
-
return chain;
|
|
69
|
-
};
|
|
70
|
-
/**
|
|
71
|
-
* Every rendered instance produced through the same call site, in document
|
|
72
|
-
* order. One entry means the call ran once; several mean it repeated.
|
|
73
|
-
*/
|
|
74
|
-
const emailSelectionSiblings = (selections, selection) => {
|
|
75
|
-
const key = callSiteKey(selection);
|
|
76
|
-
return selections.filter((candidate) => callSiteKey(candidate) === key);
|
|
77
|
-
};
|
|
78
|
-
/**
|
|
79
|
-
* Every rendered instance of the same authored element, across every call site
|
|
80
|
-
* that reaches it. A shared component edited at its definition changes all of
|
|
81
|
-
* these; edited at one call site it changes only `emailSelectionSiblings`. A
|
|
82
|
-
* control has to say which of the two it is doing before it applies.
|
|
83
|
-
*/
|
|
84
|
-
const emailDefinitionInstances = (selections, selection) => {
|
|
85
|
-
const key = definitionKey(selection);
|
|
86
|
-
return selections.filter((candidate) => definitionKey(candidate) === key);
|
|
87
|
-
};
|
|
88
|
-
//#endregion
|
|
89
|
-
export { INSTANCE_PATH_ATTRIBUTE, emailAuthoringTarget, emailDefinitionInstances, emailSelectionAncestry, emailSelectionIndex, emailSelectionSiblings, emailSelections };
|
|
90
|
-
|
|
91
|
-
//# sourceMappingURL=selection.js.map
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { i as JsxSourceLocation } from "../source-locations-DrCM82Ym.js";
|
|
2
|
-
//#region src/email/source-edit.d.ts
|
|
3
|
-
/** One exact source replacement; before text guards against concurrent changes. */
|
|
4
|
-
interface SourceReplacement {
|
|
5
|
-
readonly start: number;
|
|
6
|
-
readonly end: number;
|
|
7
|
-
readonly before: string;
|
|
8
|
-
readonly after: string;
|
|
9
|
-
}
|
|
10
|
-
type SourceEditResult = {
|
|
11
|
-
readonly ok: true;
|
|
12
|
-
readonly source: string;
|
|
13
|
-
} | {
|
|
14
|
-
readonly ok: false;
|
|
15
|
-
readonly reason: string;
|
|
16
|
-
};
|
|
17
|
-
interface SourceProperty {
|
|
18
|
-
readonly name: string;
|
|
19
|
-
readonly kind: "literal" | "binding" | "computed";
|
|
20
|
-
readonly value: string;
|
|
21
|
-
readonly literalType: "string" | "number" | "boolean";
|
|
22
|
-
readonly start: number;
|
|
23
|
-
readonly end: number;
|
|
24
|
-
readonly syntax: "attribute" | "child" | "value";
|
|
25
|
-
}
|
|
26
|
-
interface SourceElement {
|
|
27
|
-
readonly start: number;
|
|
28
|
-
readonly end: number;
|
|
29
|
-
readonly contentStart: number | null;
|
|
30
|
-
readonly contentEnd: number | null;
|
|
31
|
-
readonly tag: string;
|
|
32
|
-
readonly attributeInsertion: number;
|
|
33
|
-
readonly hasSpread: boolean;
|
|
34
|
-
readonly properties: readonly SourceProperty[];
|
|
35
|
-
readonly structural: boolean;
|
|
36
|
-
}
|
|
37
|
-
/** Resolve a renderer origin against the unchanged authored entry, never against HTML offsets. */
|
|
38
|
-
declare const inspectSourceElement: (source: string, file: string, origin: JsxSourceLocation) => SourceElement | null;
|
|
39
|
-
/** Literal controls cannot overwrite a binding or computed expression. */
|
|
40
|
-
declare const literalReplacement: (source: string, field: SourceProperty, value: string) => SourceReplacement | null;
|
|
41
|
-
/** Explicit expression changes are proposals: callers show before/after and require Apply. */
|
|
42
|
-
declare const expressionReplacement: (source: string, field: SourceProperty, expression: string) => SourceReplacement;
|
|
43
|
-
/** Apply exact, nonoverlapping edits and refuse syntax damage without changing the original. */
|
|
44
|
-
declare const applySourceReplacements: (source: string, edits: readonly SourceReplacement[]) => SourceEditResult;
|
|
45
|
-
/** Structural edits name a whole authored block; loop iterations are never source nodes. */
|
|
46
|
-
declare const structuralReplacements: (source: string, element: SourceElement, operation: {
|
|
47
|
-
readonly kind: "delete";
|
|
48
|
-
} | {
|
|
49
|
-
readonly kind: "insert";
|
|
50
|
-
readonly tsx: string;
|
|
51
|
-
} | {
|
|
52
|
-
readonly kind: "move";
|
|
53
|
-
readonly destination: SourceElement;
|
|
54
|
-
}) => readonly SourceReplacement[] | null;
|
|
55
|
-
interface SourceFixture {
|
|
56
|
-
readonly name: string;
|
|
57
|
-
readonly start: number;
|
|
58
|
-
readonly end: number;
|
|
59
|
-
readonly source: string;
|
|
60
|
-
}
|
|
61
|
-
/** Declared fixture values are separate source targets from body bindings and literals. */
|
|
62
|
-
declare const inspectSourceFixtures: (source: string) => readonly SourceFixture[];
|
|
63
|
-
/** Adding a style literal is unambiguous only when no spread can supply that property. */
|
|
64
|
-
declare const addStyleClasses: (element: SourceElement, value: string) => SourceReplacement | null;
|
|
65
|
-
//#endregion
|
|
66
|
-
export { SourceEditResult, SourceElement, SourceFixture, SourceProperty, SourceReplacement, addStyleClasses, applySourceReplacements, expressionReplacement, inspectSourceElement, inspectSourceFixtures, literalReplacement, structuralReplacements };
|
|
67
|
-
//# sourceMappingURL=source-edit.d.ts.map
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import { n as isObject } from "../guards-eV1qLbf2.js";
|
|
2
|
-
import { buildLineMap } from "../diagnostics.js";
|
|
3
|
-
import { parse } from "@babel/parser";
|
|
4
|
-
//#region src/email/source-edit.ts
|
|
5
|
-
const node = (value) => isObject(value) && "type" in value && typeof value.type === "string" && "start" in value && typeof value.start === "number" && "end" in value && typeof value.end === "number";
|
|
6
|
-
const children = (value) => Array.isArray(value) ? value.filter(node) : [];
|
|
7
|
-
const walk = (value, visit, parent = null) => {
|
|
8
|
-
if (Array.isArray(value)) {
|
|
9
|
-
for (const item of value) walk(item, visit, parent);
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
if (!node(value)) return;
|
|
13
|
-
visit(value, parent);
|
|
14
|
-
for (const [key, child] of Object.entries(value)) if (key !== "loc" && key !== "extra" && key !== "comments") walk(child, visit, value);
|
|
15
|
-
};
|
|
16
|
-
const parsed = (source) => {
|
|
17
|
-
try {
|
|
18
|
-
const result = parse(source, {
|
|
19
|
-
sourceType: "module",
|
|
20
|
-
plugins: ["typescript", "jsx"]
|
|
21
|
-
});
|
|
22
|
-
return node(result) ? result : null;
|
|
23
|
-
} catch {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
const binding = (value) => value.type === "Identifier" || value.type === "MemberExpression" && value.computed === false && node(value.object) && binding(value.object);
|
|
28
|
-
const property = (name, value, source, syntax) => {
|
|
29
|
-
const expression = value.type === "JSXExpressionContainer" && node(value.expression) ? value.expression : value;
|
|
30
|
-
const literal = expression.type === "StringLiteral" || expression.type === "NumericLiteral" || expression.type === "BooleanLiteral" || expression.type === "JSXText";
|
|
31
|
-
return {
|
|
32
|
-
name,
|
|
33
|
-
literalType: expression.type === "NumericLiteral" ? "number" : expression.type === "BooleanLiteral" ? "boolean" : "string",
|
|
34
|
-
kind: literal ? "literal" : binding(expression) ? "binding" : "computed",
|
|
35
|
-
value: literal && (typeof expression.value === "string" || typeof expression.value === "number" || typeof expression.value === "boolean") ? String(expression.value) : source.slice(expression.start, expression.end),
|
|
36
|
-
start: value.start,
|
|
37
|
-
end: value.end,
|
|
38
|
-
syntax
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
/** Resolve a renderer origin against the unchanged authored entry, never against HTML offsets. */
|
|
42
|
-
const inspectSourceElement = (source, file, origin) => {
|
|
43
|
-
if (file !== origin.fileName) return null;
|
|
44
|
-
const tree = parsed(source);
|
|
45
|
-
if (tree === null) return null;
|
|
46
|
-
const lines = buildLineMap(source);
|
|
47
|
-
const lineStart = lines[origin.lineNumber - 1];
|
|
48
|
-
if (lineStart === void 0 || !Number.isInteger(origin.columnNumber) || origin.columnNumber < 1) return null;
|
|
49
|
-
const offset = lineStart + origin.columnNumber - 1;
|
|
50
|
-
if (offset >= (lines[origin.lineNumber] ?? source.length)) return null;
|
|
51
|
-
let found = null;
|
|
52
|
-
walk(tree, (element, parent) => {
|
|
53
|
-
if (element.type !== "JSXElement" || element.start !== offset || !node(element.openingElement)) return;
|
|
54
|
-
const opening = element.openingElement;
|
|
55
|
-
const properties = [];
|
|
56
|
-
for (const attribute of children(opening.attributes)) if (attribute.type === "JSXAttribute" && node(attribute.name) && typeof attribute.name.name === "string" && node(attribute.value)) {
|
|
57
|
-
const value = attribute.value;
|
|
58
|
-
const expression = value.type === "JSXExpressionContainer" && node(value.expression) ? value.expression : null;
|
|
59
|
-
if (attribute.name.name === "style" && expression?.type === "ObjectExpression" && !children(expression.properties).some((member) => member.type === "SpreadElement")) for (const member of children(expression.properties)) {
|
|
60
|
-
if (member.type !== "ObjectProperty" || member.computed === true || !node(member.key) || !node(member.value)) continue;
|
|
61
|
-
const key = member.key.type === "Identifier" ? member.key.name : member.key.value;
|
|
62
|
-
if (typeof key === "string") properties.push(property(`style.${key}`, member.value, source, "value"));
|
|
63
|
-
}
|
|
64
|
-
else properties.push(property(attribute.name.name, value, source, "attribute"));
|
|
65
|
-
}
|
|
66
|
-
const meaningful = children(element.children).filter((child) => child.type !== "JSXText" || source.slice(child.start, child.end).trim() !== "");
|
|
67
|
-
if (meaningful.length === 1 && meaningful[0] !== void 0 && meaningful[0].type !== "JSXElement" && meaningful[0].type !== "JSXFragment") properties.unshift(property("children", meaningful[0], source, "child"));
|
|
68
|
-
found = {
|
|
69
|
-
start: element.start,
|
|
70
|
-
end: element.end,
|
|
71
|
-
contentStart: opening.selfClosing === true ? null : opening.end,
|
|
72
|
-
contentEnd: node(element.closingElement) ? element.closingElement.start : null,
|
|
73
|
-
tag: node(opening.name) ? source.slice(opening.name.start, opening.name.end) : "element",
|
|
74
|
-
properties,
|
|
75
|
-
attributeInsertion: opening.end - (opening.selfClosing === true ? 2 : 1),
|
|
76
|
-
hasSpread: children(opening.attributes).some((attribute) => attribute.type === "JSXSpreadAttribute"),
|
|
77
|
-
structural: parent?.type === "JSXElement" || parent?.type === "JSXFragment"
|
|
78
|
-
};
|
|
79
|
-
});
|
|
80
|
-
return found;
|
|
81
|
-
};
|
|
82
|
-
/** Literal controls cannot overwrite a binding or computed expression. */
|
|
83
|
-
const literalReplacement = (source, field, value) => {
|
|
84
|
-
if (field.kind !== "literal") return null;
|
|
85
|
-
if (field.literalType === "number" && (value.trim() === "" || !Number.isFinite(Number(value)))) return null;
|
|
86
|
-
if (field.literalType === "boolean" && value !== "true" && value !== "false") return null;
|
|
87
|
-
const literal = field.literalType === "string" ? JSON.stringify(value) : field.literalType === "number" ? String(Number(value)) : value;
|
|
88
|
-
return {
|
|
89
|
-
start: field.start,
|
|
90
|
-
end: field.end,
|
|
91
|
-
before: source.slice(field.start, field.end),
|
|
92
|
-
after: field.syntax === "child" || field.syntax === "attribute" && field.literalType !== "string" ? `{${literal}}` : literal
|
|
93
|
-
};
|
|
94
|
-
};
|
|
95
|
-
/** Explicit expression changes are proposals: callers show before/after and require Apply. */
|
|
96
|
-
const expressionReplacement = (source, field, expression) => ({
|
|
97
|
-
start: field.start,
|
|
98
|
-
end: field.end,
|
|
99
|
-
before: source.slice(field.start, field.end),
|
|
100
|
-
after: field.syntax === "value" ? expression : `{${expression}}`
|
|
101
|
-
});
|
|
102
|
-
/** Apply exact, nonoverlapping edits and refuse syntax damage without changing the original. */
|
|
103
|
-
const applySourceReplacements = (source, edits) => {
|
|
104
|
-
const sorted = [...edits].sort((a, b) => b.start - a.start);
|
|
105
|
-
let boundary = source.length;
|
|
106
|
-
let result = source;
|
|
107
|
-
for (const edit of sorted) {
|
|
108
|
-
if (edit.start < 0 || edit.end < edit.start || edit.end > boundary || source.slice(edit.start, edit.end) !== edit.before) return {
|
|
109
|
-
ok: false,
|
|
110
|
-
reason: "Source changed at this target. Select it again before applying the edit."
|
|
111
|
-
};
|
|
112
|
-
result = result.slice(0, edit.start) + edit.after + result.slice(edit.end);
|
|
113
|
-
boundary = edit.start;
|
|
114
|
-
}
|
|
115
|
-
return parsed(result) === null ? {
|
|
116
|
-
ok: false,
|
|
117
|
-
reason: "This change is not valid TSX. Review it in Source."
|
|
118
|
-
} : {
|
|
119
|
-
ok: true,
|
|
120
|
-
source: result
|
|
121
|
-
};
|
|
122
|
-
};
|
|
123
|
-
/** Structural edits name a whole authored block; loop iterations are never source nodes. */
|
|
124
|
-
const structuralReplacements = (source, element, operation) => {
|
|
125
|
-
if (operation.kind === "insert") {
|
|
126
|
-
if (element.contentEnd === null) return null;
|
|
127
|
-
return [{
|
|
128
|
-
start: element.contentEnd,
|
|
129
|
-
end: element.contentEnd,
|
|
130
|
-
before: "",
|
|
131
|
-
after: operation.tsx
|
|
132
|
-
}];
|
|
133
|
-
}
|
|
134
|
-
if (!element.structural) return null;
|
|
135
|
-
const removal = {
|
|
136
|
-
start: element.start,
|
|
137
|
-
end: element.end,
|
|
138
|
-
before: source.slice(element.start, element.end),
|
|
139
|
-
after: ""
|
|
140
|
-
};
|
|
141
|
-
if (operation.kind === "delete") return [removal];
|
|
142
|
-
const destination = operation.destination.contentEnd;
|
|
143
|
-
if (destination === null || destination >= element.start && destination <= element.end) return null;
|
|
144
|
-
return [removal, {
|
|
145
|
-
start: destination,
|
|
146
|
-
end: destination,
|
|
147
|
-
before: "",
|
|
148
|
-
after: removal.before
|
|
149
|
-
}];
|
|
150
|
-
};
|
|
151
|
-
/** Declared fixture values are separate source targets from body bindings and literals. */
|
|
152
|
-
const inspectSourceFixtures = (source) => {
|
|
153
|
-
const tree = parsed(source);
|
|
154
|
-
if (tree === null) return [];
|
|
155
|
-
const found = [];
|
|
156
|
-
walk(tree, (call) => {
|
|
157
|
-
if (call.type !== "CallExpression" || !node(call.callee) || call.callee.type !== "Identifier" || call.callee.name !== "defineEmail") return;
|
|
158
|
-
const definition = children(call.arguments)[0];
|
|
159
|
-
if (definition?.type !== "ObjectExpression") return;
|
|
160
|
-
const fixtures = children(definition.properties).find((field) => field.type === "ObjectProperty" && node(field.key) && field.key.name === "fixtures" && field.computed !== true);
|
|
161
|
-
if (fixtures === void 0 || !node(fixtures.value) || fixtures.value.type !== "ObjectExpression") return;
|
|
162
|
-
for (const field of children(fixtures.value.properties)) {
|
|
163
|
-
if (field.type !== "ObjectProperty" || field.computed === true || !node(field.key) || !node(field.value)) continue;
|
|
164
|
-
const name = field.key.type === "Identifier" ? field.key.name : field.key.value;
|
|
165
|
-
if (typeof name === "string") found.push({
|
|
166
|
-
name,
|
|
167
|
-
start: field.value.start,
|
|
168
|
-
end: field.value.end,
|
|
169
|
-
source: source.slice(field.value.start, field.value.end)
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
return found;
|
|
174
|
-
};
|
|
175
|
-
/** Adding a style literal is unambiguous only when no spread can supply that property. */
|
|
176
|
-
const addStyleClasses = (element, value) => element.hasSpread || element.properties.some((field) => field.name === "className") ? null : {
|
|
177
|
-
start: element.attributeInsertion,
|
|
178
|
-
end: element.attributeInsertion,
|
|
179
|
-
before: "",
|
|
180
|
-
after: ` className=${JSON.stringify(value)}`
|
|
181
|
-
};
|
|
182
|
-
//#endregion
|
|
183
|
-
export { addStyleClasses, applySourceReplacements, expressionReplacement, inspectSourceElement, inspectSourceFixtures, literalReplacement, structuralReplacements };
|
|
184
|
-
|
|
185
|
-
//# sourceMappingURL=source-edit.js.map
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { i as Stylesheet } from "../css-CS2jLID6.js";
|
|
2
|
-
//#region src/email/stylesheets.d.ts
|
|
3
|
-
/** Called by a compiler-generated stylesheet module at import time. */
|
|
4
|
-
declare const registerStylesheet: (sheet: Stylesheet) => void;
|
|
5
|
-
/** Drop every registration. The render entry calls this before a render. */
|
|
6
|
-
declare const resetStylesheets: () => void;
|
|
7
|
-
/** Every registered sheet flattened into one, with order preserved across sheets. */
|
|
8
|
-
declare const collectStylesheets: () => Stylesheet;
|
|
9
|
-
/** True when a template registered no styles at all, so the render can skip the pass. */
|
|
10
|
-
declare const hasStylesheets: () => boolean;
|
|
11
|
-
/** The registry as it stands, or `undefined` when no sheet registered. */
|
|
12
|
-
declare const snapshotStylesheets: () => Stylesheet | undefined;
|
|
13
|
-
//#endregion
|
|
14
|
-
export { collectStylesheets, hasStylesheets, registerStylesheet, resetStylesheets, snapshotStylesheets };
|
|
15
|
-
//# sourceMappingURL=stylesheets.d.ts.map
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
//#region src/email/stylesheets.ts
|
|
2
|
-
let registry = [];
|
|
3
|
-
let nextBase = 0;
|
|
4
|
-
/** Called by a compiler-generated stylesheet module at import time. */
|
|
5
|
-
const registerStylesheet = (sheet) => {
|
|
6
|
-
const size = sheet.rules.length;
|
|
7
|
-
registry.push({
|
|
8
|
-
sheet,
|
|
9
|
-
base: nextBase
|
|
10
|
-
});
|
|
11
|
-
nextBase += size + 1;
|
|
12
|
-
};
|
|
13
|
-
/** Drop every registration. The render entry calls this before a render. */
|
|
14
|
-
const resetStylesheets = () => {
|
|
15
|
-
registry = [];
|
|
16
|
-
nextBase = 0;
|
|
17
|
-
};
|
|
18
|
-
/** Every registered sheet flattened into one, with order preserved across sheets. */
|
|
19
|
-
const collectStylesheets = () => {
|
|
20
|
-
const rules = registry.flatMap(({ sheet, base }) => sheet.rules.map((rule) => ({
|
|
21
|
-
...rule,
|
|
22
|
-
order: base + rule.order
|
|
23
|
-
})));
|
|
24
|
-
const variables = {};
|
|
25
|
-
const headAtRules = [];
|
|
26
|
-
const fontFaces = /* @__PURE__ */ new Map();
|
|
27
|
-
const diagnostics = registry.flatMap(({ sheet }) => sheet.diagnostics);
|
|
28
|
-
for (const { sheet } of registry) {
|
|
29
|
-
Object.assign(variables, sheet.variables);
|
|
30
|
-
headAtRules.push(...sheet.headAtRules);
|
|
31
|
-
for (const face of sheet.fontFaces) fontFaces.set(face.css, face);
|
|
32
|
-
}
|
|
33
|
-
return {
|
|
34
|
-
rules,
|
|
35
|
-
headAtRules,
|
|
36
|
-
fontFaces: [...fontFaces.values()],
|
|
37
|
-
variables,
|
|
38
|
-
diagnostics
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
/** True when a template registered no styles at all, so the render can skip the pass. */
|
|
42
|
-
const hasStylesheets = () => registry.length > 0;
|
|
43
|
-
/** The registry as it stands, or `undefined` when no sheet registered. */
|
|
44
|
-
const snapshotStylesheets = () => hasStylesheets() ? collectStylesheets() : void 0;
|
|
45
|
-
//#endregion
|
|
46
|
-
export { collectStylesheets, hasStylesheets, registerStylesheet, resetStylesheets, snapshotStylesheets };
|
|
47
|
-
|
|
48
|
-
//# sourceMappingURL=stylesheets.js.map
|
package/dist/email/text.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { m as EmailNode } from "../elements-BzjjnoCS.js";
|
|
2
|
-
//#region src/email/text.d.ts
|
|
3
|
-
/** Derive the plain-text alternative from the semantic tree in reading order. */
|
|
4
|
-
declare const deriveEmailText: (node: EmailNode) => string;
|
|
5
|
-
//#endregion
|
|
6
|
-
export { deriveEmailText };
|
|
7
|
-
//# sourceMappingURL=text.d.ts.map
|
package/dist/email/text.js
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { emailElement } from "./elements.js";
|
|
2
|
-
//#region src/email/text.ts
|
|
3
|
-
const PARAGRAPH = "\n\n";
|
|
4
|
-
const LINE = "\n";
|
|
5
|
-
const INVISIBLE = /\u200b|\u200c|\u200d|\u200e|\u200f|\u2060|\ufeff/g;
|
|
6
|
-
const isLayoutTable = (props) => props.role === "presentation" || props.role === "none";
|
|
7
|
-
/**
|
|
8
|
-
* A data-table cell that reads as nothing and holds only imagery with empty alt text carries no
|
|
9
|
-
* column of its own; an empty cell without such an image is a positional placeholder.
|
|
10
|
-
*/
|
|
11
|
-
const isDecorativeCell = (cell, context) => {
|
|
12
|
-
const images = collect(cell.children, (tag) => tag === "img", () => true);
|
|
13
|
-
return images.length > 0 && images.every((image) => image.props.alt === "") && childText(cell, context).trim() === "";
|
|
14
|
-
};
|
|
15
|
-
/** Wrap a block's content in separators; empty blocks contribute nothing. */
|
|
16
|
-
const block = (inner, separator) => {
|
|
17
|
-
const trimmed = inner.replace(/^\s+/, "").replace(/\s+$/, "");
|
|
18
|
-
return trimmed === "" ? "" : `${separator}${trimmed}${separator}`;
|
|
19
|
-
};
|
|
20
|
-
const collect = (nodes, keep, descend) => nodes.flatMap((child) => {
|
|
21
|
-
if (child.type === "Fragment") return collect(child.children, keep, descend);
|
|
22
|
-
if (child.type !== "Element") return [];
|
|
23
|
-
if (keep(child.tag)) return [child];
|
|
24
|
-
return descend(child.tag) ? collect(child.children, keep, descend) : [];
|
|
25
|
-
});
|
|
26
|
-
const TABLE_SECTIONS = /* @__PURE__ */ new Set([
|
|
27
|
-
"thead",
|
|
28
|
-
"tbody",
|
|
29
|
-
"tfoot"
|
|
30
|
-
]);
|
|
31
|
-
const childText = (node, context) => node.children.map((child) => derive(child, context)).join("");
|
|
32
|
-
const derive = (node, context) => {
|
|
33
|
-
switch (node.type) {
|
|
34
|
-
case "Text": return context.preformatted ? node.value : node.value.replace(INVISIBLE, "").replace(/[^\S\n]+/g, " ");
|
|
35
|
-
case "Raw": return node.text;
|
|
36
|
-
case "Fragment": return node.children.map((child) => derive(child, context)).join("");
|
|
37
|
-
case "Element": {
|
|
38
|
-
const spec = emailElement(node.tag);
|
|
39
|
-
if (spec === void 0) return childText(node, context);
|
|
40
|
-
switch (spec.layout) {
|
|
41
|
-
case "metadata": return "";
|
|
42
|
-
case "document":
|
|
43
|
-
case "inline":
|
|
44
|
-
case "list-item": return childText(node, context);
|
|
45
|
-
case "break": return LINE;
|
|
46
|
-
case "rule": return `${PARAGRAPH}---${PARAGRAPH}`;
|
|
47
|
-
case "image": {
|
|
48
|
-
const alt = typeof node.props.alt === "string" ? node.props.alt.trim() : "";
|
|
49
|
-
return alt === "" ? "" : `[${alt}]`;
|
|
50
|
-
}
|
|
51
|
-
case "link": {
|
|
52
|
-
const label = childText(node, context).replace(/\s+/g, " ").trim();
|
|
53
|
-
const href = typeof node.props.href === "string" ? node.props.href : "";
|
|
54
|
-
if (href === "" || href === label) return label;
|
|
55
|
-
return label === "" ? href : `${label} (${href})`;
|
|
56
|
-
}
|
|
57
|
-
case "list": {
|
|
58
|
-
const indent = " ".repeat(context.listDepth);
|
|
59
|
-
const start = Number(node.props.start ?? 1);
|
|
60
|
-
const first = Number.isFinite(start) ? start : 1;
|
|
61
|
-
const nested = {
|
|
62
|
-
...context,
|
|
63
|
-
listDepth: context.listDepth + 1
|
|
64
|
-
};
|
|
65
|
-
const lines = collect(node.children, (tag) => tag === "li", () => false).map((item, index) => {
|
|
66
|
-
const marker = node.tag === "ol" ? `${first + index}.` : "-";
|
|
67
|
-
const continuation = `${indent}${" ".repeat(marker.length + 1)}`;
|
|
68
|
-
const [head = "", ...rest] = derive(item, nested).replace(/^\s+/, "").replace(/\s+$/, "").replace(/\n{2,}/g, LINE).split("\n");
|
|
69
|
-
return [`${indent}${marker} ${head}`, ...rest.map((line) => line.trim() === "" ? "" : /^\s/.test(line) ? line : `${continuation}${line}`)].join(LINE);
|
|
70
|
-
});
|
|
71
|
-
return block(lines.join(LINE), PARAGRAPH);
|
|
72
|
-
}
|
|
73
|
-
case "table": {
|
|
74
|
-
if (isLayoutTable(node.props)) return childText(node, context);
|
|
75
|
-
const rows = collect(node.children, (tag) => tag === "tr", (tag) => TABLE_SECTIONS.has(tag)).map((row) => collect(row.children, (tag) => tag === "td" || tag === "th", () => false).filter((cell) => !isDecorativeCell(cell, context)).map((cell) => childText(cell, context).replace(/\s+/g, " ").trim()).join(" | "));
|
|
76
|
-
return block(rows.join(LINE), PARAGRAPH);
|
|
77
|
-
}
|
|
78
|
-
case "row": return block(collect(node.children, (tag) => tag === "td" || tag === "th", () => false).map((cell) => childText(cell, context).replace(/^\s+|\s+$/g, "")).filter((cell) => cell !== "").join(LINE), LINE);
|
|
79
|
-
case "table-section":
|
|
80
|
-
case "cell": return block(childText(node, context), LINE);
|
|
81
|
-
case "preformatted": return block(childText(node, {
|
|
82
|
-
...context,
|
|
83
|
-
preformatted: true
|
|
84
|
-
}), PARAGRAPH);
|
|
85
|
-
case "block": return block(childText(node, context), PARAGRAPH);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
/** Collapse the separators the walk emits into readable plain text. */
|
|
91
|
-
const normalize = (value) => value.replace(/\r\n?/g, "\n").split("\n").map((line) => line.replace(/[^\S\n]+$/, "")).join("\n").replace(/\n{3,}/g, PARAGRAPH).trim();
|
|
92
|
-
/** Derive the plain-text alternative from the semantic tree in reading order. */
|
|
93
|
-
const deriveEmailText = (node) => normalize(derive(node, {
|
|
94
|
-
preformatted: false,
|
|
95
|
-
listDepth: 0
|
|
96
|
-
}));
|
|
97
|
-
//#endregion
|
|
98
|
-
export { deriveEmailText };
|
|
99
|
-
|
|
100
|
-
//# sourceMappingURL=text.js.map
|
package/dist/emit.d.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { t as Diagnostic } from "./diagnostics-OcQKQYSY.js";
|
|
2
|
-
import { s as UnboundTemplateSourceLocations } from "./source-locations-DrCM82Ym.js";
|
|
3
|
-
import { TemplateVariable } from "./render-template.js";
|
|
4
|
-
//#region src/emit.d.ts
|
|
5
|
-
/**
|
|
6
|
-
* One template's emission outcome. `ok` is true only when the template rendered
|
|
7
|
-
* cleanly with zero diagnostics (valid grammar AND stable emission). `source` is
|
|
8
|
-
* the first render's canonical SML whenever the render itself succeeded — it is
|
|
9
|
-
* present even for an unstable template (so the build can show what it produced),
|
|
10
|
-
* and `undefined` only when the print → parse validation failed outright.
|
|
11
|
-
*/
|
|
12
|
-
type EmittedTemplate = {
|
|
13
|
-
readonly name: string;
|
|
14
|
-
readonly ok: true;
|
|
15
|
-
readonly source: string;
|
|
16
|
-
readonly variables: ReadonlyArray<TemplateVariable>;
|
|
17
|
-
readonly sourceLocations: UnboundTemplateSourceLocations;
|
|
18
|
-
readonly diagnostics: readonly [];
|
|
19
|
-
} | {
|
|
20
|
-
readonly name: string;
|
|
21
|
-
readonly ok: false;
|
|
22
|
-
readonly source: string | undefined;
|
|
23
|
-
readonly variables: ReadonlyArray<TemplateVariable>;
|
|
24
|
-
readonly sourceLocations: UnboundTemplateSourceLocations;
|
|
25
|
-
readonly diagnostics: ReadonlyArray<Diagnostic>;
|
|
26
|
-
};
|
|
27
|
-
/** The aggregate emit result for a module. */
|
|
28
|
-
interface EmittedModule {
|
|
29
|
-
/** True when the default export was a template and it emitted cleanly. */
|
|
30
|
-
readonly ok: boolean;
|
|
31
|
-
readonly templates: ReadonlyArray<EmittedTemplate>;
|
|
32
|
-
/**
|
|
33
|
-
* Module-level diagnostics not tied to a single template — currently missing
|
|
34
|
-
* or invalid default. Per-template failures live on each entry.
|
|
35
|
-
*/
|
|
36
|
-
readonly diagnostics: ReadonlyArray<Diagnostic>;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Emits the one supported template from an already-evaluated module object:
|
|
40
|
-
* `moduleExports.default`. Named exports are never probed. `moduleExports` is
|
|
41
|
-
* a plain record of the module's exports (the vite plugin imports the TSX;
|
|
42
|
-
* this stays bundler-agnostic and pure).
|
|
43
|
-
*
|
|
44
|
-
* A missing default yields `missing-default-export`. A present default that
|
|
45
|
-
* is not a template function yields `invalid-default-export`. Both fail the
|
|
46
|
-
* aggregate with an empty `templates` array.
|
|
47
|
-
*/
|
|
48
|
-
declare const emitModule: (moduleExports: Record<string, unknown>) => EmittedModule;
|
|
49
|
-
//#endregion
|
|
50
|
-
export { EmittedModule, EmittedTemplate, emitModule };
|
|
51
|
-
//# sourceMappingURL=emit.d.ts.map
|