@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.
Files changed (172) hide show
  1. package/AGENTS.md +107 -46
  2. package/CHANGELOG.md +345 -0
  3. package/README.md +188 -52
  4. package/dist/{brand-DcoVkh_S.d.ts → assets-BIsyMQ92.d.ts} +41 -11
  5. package/dist/{email/brand.js → brand-C0MKTwmX.js} +64 -4
  6. package/dist/{theme-css.d.ts → brand.d.ts} +12 -3
  7. package/dist/brand.js +4 -0
  8. package/dist/channel-specs-C2feJwuP.d.ts +13 -0
  9. package/dist/compiler.d.ts +88 -115
  10. package/dist/compiler.js +750 -171
  11. package/dist/components-BZCqegS8.js +1864 -0
  12. package/dist/diagnostic-model-DJtqbETd.js +38 -0
  13. package/dist/{diagnostics-OcQKQYSY.d.ts → diagnostic-model-DxthUZBT.d.ts} +3 -21
  14. package/dist/{diagnostics-BMYtBCQk.d.ts → diagnostics-B-Gym34R.d.ts} +8 -4
  15. package/dist/{email/diagnostics.js → diagnostics-CnKFefKz.js} +4 -3
  16. package/dist/diagnostics.d.ts +410 -2
  17. package/dist/diagnostics.js +402 -52
  18. package/dist/edit.d.ts +234 -0
  19. package/dist/edit.js +947 -0
  20. package/dist/email/jsx-dev-runtime.d.ts +2 -2
  21. package/dist/email/jsx-dev-runtime.js +1 -2
  22. package/dist/email/jsx-runtime.d.ts +1 -1
  23. package/dist/email/jsx-runtime.js +1 -85
  24. package/dist/{email/components.d.ts → email.d.ts} +22 -11
  25. package/dist/email.js +2 -0
  26. package/dist/fmt.d.ts +32 -0
  27. package/dist/fmt.js +174 -0
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.js +2 -0
  30. package/dist/{render-vAv_4r9v.js → ir-CLhlklKV.js} +96 -279
  31. package/dist/jsx-runtime-CBlexoeR.js +98 -0
  32. package/dist/{elements-BzjjnoCS.d.ts → jsx-runtime-ylIOnaSf.d.ts} +76 -76
  33. package/dist/profile-pWGtWZXF.js +3803 -0
  34. package/dist/render-BPonDpCl.d.ts +539 -0
  35. package/dist/render-BQmfh94k.js +257 -0
  36. package/dist/render.d.ts +35 -0
  37. package/dist/render.js +49 -0
  38. package/dist/sms.d.ts +16 -0
  39. package/dist/sms.js +12 -0
  40. package/dist/source-locations-C8wCsiAy.d.ts +23 -0
  41. package/dist/template-DMPwxMSq.d.ts +65 -0
  42. package/dist/template-zLUjcwfF.js +42 -0
  43. package/dist/{theme-css-hFqcPSar.js → theme-css-JtRCm1fb.js} +2 -2
  44. package/dist/whatsapp.d.ts +57 -0
  45. package/dist/whatsapp.js +22 -0
  46. package/generated/contract.json +1788 -0
  47. package/generated/reference.md +360 -0
  48. package/package.json +46 -239
  49. package/samva-brand.d.ts +4 -4
  50. package/skills/sml/SKILL.md +123 -0
  51. package/dist/ast-BrDtApwS.d.ts +0 -107
  52. package/dist/ast.d.ts +0 -2
  53. package/dist/ast.js +0 -124
  54. package/dist/candidate.d.ts +0 -29
  55. package/dist/candidate.js +0 -88
  56. package/dist/catalog-CKuOooB8.d.ts +0 -354
  57. package/dist/checks-D73Xizbw.js +0 -59366
  58. package/dist/checks.d.ts +0 -33
  59. package/dist/checks.js +0 -2
  60. package/dist/classes.d.ts +0 -26
  61. package/dist/classes.js +0 -184
  62. package/dist/compile-sms.d.ts +0 -28
  63. package/dist/compile-sms.js +0 -44
  64. package/dist/compile-whatsapp.d.ts +0 -56
  65. package/dist/compile-whatsapp.js +0 -186
  66. package/dist/compile.d.ts +0 -18
  67. package/dist/compile.js +0 -29
  68. package/dist/components.d.ts +0 -289
  69. package/dist/components.js +0 -106
  70. package/dist/conditionals.d.ts +0 -10
  71. package/dist/conditionals.js +0 -74
  72. package/dist/corpus-CPPfgAHN.d.ts +0 -106
  73. package/dist/css-CS2jLID6.d.ts +0 -109
  74. package/dist/email/assets.d.ts +0 -40
  75. package/dist/email/assets.js +0 -63
  76. package/dist/email/brand-footer.d.ts +0 -34
  77. package/dist/email/brand-footer.js +0 -62
  78. package/dist/email/brand.d.ts +0 -2
  79. package/dist/email/components.js +0 -240
  80. package/dist/email/diagnostics.d.ts +0 -2
  81. package/dist/email/elements.d.ts +0 -2
  82. package/dist/email/elements.js +0 -299
  83. package/dist/email/html.d.ts +0 -2
  84. package/dist/email/html.js +0 -278
  85. package/dist/email/render.d.ts +0 -50
  86. package/dist/email/render.js +0 -3
  87. package/dist/email/selection.d.ts +0 -2
  88. package/dist/email/selection.js +0 -91
  89. package/dist/email/source-edit.d.ts +0 -67
  90. package/dist/email/source-edit.js +0 -185
  91. package/dist/email/stylesheets.d.ts +0 -15
  92. package/dist/email/stylesheets.js +0 -48
  93. package/dist/email/text.d.ts +0 -7
  94. package/dist/email/text.js +0 -100
  95. package/dist/emit.d.ts +0 -51
  96. package/dist/emit.js +0 -151
  97. package/dist/html-Bl9BAdqc.d.ts +0 -42
  98. package/dist/interpolate.d.ts +0 -24
  99. package/dist/interpolate.js +0 -43
  100. package/dist/jsx-dev-runtime.d.ts +0 -11
  101. package/dist/jsx-dev-runtime.js +0 -15
  102. package/dist/jsx-runtime-I-1nCtl2.d.ts +0 -57
  103. package/dist/jsx-runtime.d.ts +0 -2
  104. package/dist/jsx-runtime.js +0 -121
  105. package/dist/lexer.d.ts +0 -60
  106. package/dist/lexer.js +0 -281
  107. package/dist/model.d.ts +0 -24
  108. package/dist/model.js +0 -77
  109. package/dist/ops-BvwqHl7V.js +0 -630
  110. package/dist/ops.d.ts +0 -114
  111. package/dist/ops.js +0 -2
  112. package/dist/parser-t29cHLVL.d.ts +0 -396
  113. package/dist/parser.d.ts +0 -2
  114. package/dist/parser.js +0 -859
  115. package/dist/predicate-BI6uRRM-.js +0 -549
  116. package/dist/predicate-M-05Nd7R.d.ts +0 -56
  117. package/dist/predicate.d.ts +0 -2
  118. package/dist/predicate.js +0 -2
  119. package/dist/printer.d.ts +0 -6
  120. package/dist/printer.js +0 -122
  121. package/dist/registry-Dcp6QEK_.d.ts +0 -73
  122. package/dist/registry.d.ts +0 -2
  123. package/dist/registry.js +0 -646
  124. package/dist/render-template.d.ts +0 -45
  125. package/dist/render-template.js +0 -76
  126. package/dist/selection-CCArx9lU.d.ts +0 -91
  127. package/dist/showcase-egkusQBf.d.ts +0 -415
  128. package/dist/sms-segments-y4FpPilJ.d.ts +0 -13
  129. package/dist/sms-segments.d.ts +0 -2
  130. package/dist/sms-segments.js +0 -46
  131. package/dist/source-locations-DrCM82Ym.d.ts +0 -67
  132. package/dist/source-locations.d.ts +0 -2
  133. package/dist/source-locations.js +0 -102
  134. package/dist/starters/category-mapping.d.ts +0 -13
  135. package/dist/starters/category-mapping.js +0 -49
  136. package/dist/starters/corpus.d.ts +0 -2
  137. package/dist/starters/corpus.js +0 -194
  138. package/dist/starters/library.d.ts +0 -18
  139. package/dist/starters/library.js +0 -692
  140. package/dist/starters/public-gallery.d.ts +0 -266
  141. package/dist/starters/public-gallery.js +0 -916
  142. package/dist/starters/public-handoff.d.ts +0 -164
  143. package/dist/starters/public-handoff.js +0 -361
  144. package/dist/starters/public-preview-artifacts.d.ts +0 -43
  145. package/dist/starters/public-preview-artifacts.js +0 -104
  146. package/dist/starters/public-preview.d.ts +0 -61
  147. package/dist/starters/public-preview.js +0 -83
  148. package/dist/starters/showcase.d.ts +0 -2
  149. package/dist/starters/showcase.js +0 -335
  150. package/dist/starters/sources.d.ts +0 -45
  151. package/dist/starters/sources.js +0 -7762
  152. package/dist/starters/theme.d.ts +0 -45
  153. package/dist/starters/theme.js +0 -77
  154. package/dist/starters.d.ts +0 -2
  155. package/dist/starters.js +0 -874
  156. package/dist/tailwind-BYq7Lo6u.js +0 -727
  157. package/dist/template-DWftqh4x.d.ts +0 -60
  158. package/dist/template.d.ts +0 -2
  159. package/dist/template.js +0 -37
  160. package/dist/theme-css.js +0 -2
  161. package/dist/tsx-source-instrumentation.d.ts +0 -30
  162. package/dist/tsx-source-instrumentation.js +0 -115
  163. package/dist/tsx-tracer.d.ts +0 -112
  164. package/dist/tsx-tracer.js +0 -985
  165. package/dist/variables-J_L3qxRa.d.ts +0 -77
  166. package/dist/variables.d.ts +0 -2
  167. package/dist/variables.js +0 -141
  168. package/dist/version-BITr58kk.js +0 -96
  169. package/generated/sml-agent-contract.json +0 -709
  170. package/generated/sml-email-agent-contract.json +0 -615
  171. package/generated/sml-email-language-reference.md +0 -94
  172. package/generated/sml-language-reference.md +0 -195
@@ -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
@@ -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
@@ -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