@samva/markup 0.7.0 → 0.9.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 (176) hide show
  1. package/AGENTS.md +104 -37
  2. package/README.md +188 -52
  3. package/dist/assets-C5duwTya.d.ts +40 -0
  4. package/dist/brand-B-0bN_Ir.d.ts +88 -0
  5. package/dist/{render-aaJBnsXK.js → cascade-t6kxlgEI.js} +130 -160
  6. package/dist/channel-edit.d.ts +121 -0
  7. package/dist/channel-edit.js +609 -0
  8. package/dist/channel-specs-B6M_HlaV.js +29 -0
  9. package/dist/channel-specs-C2feJwuP.d.ts +13 -0
  10. package/dist/compatibility-DbISoFIq.js +589 -0
  11. package/dist/compiler.d.ts +19 -249
  12. package/dist/compiler.js +10 -1408
  13. package/dist/{css-COcFOT1y.d.ts → css-BVQKICKC.d.ts} +30 -4
  14. package/dist/diagnostic-codes.d.ts +403 -0
  15. package/dist/diagnostic-codes.js +399 -0
  16. package/dist/{diagnostics-BMYtBCQk.d.ts → diagnostics-B-Gym34R.d.ts} +8 -4
  17. package/dist/diagnostics.d.ts +31 -2
  18. package/dist/diagnostics.js +1 -19
  19. package/dist/{elements-BzjjnoCS.d.ts → elements-cqldpaJt.d.ts} +3 -3
  20. package/dist/email/assets.d.ts +2 -0
  21. package/dist/email/assets.js +63 -0
  22. package/dist/email/brand-footer.d.ts +34 -0
  23. package/dist/email/brand-footer.js +63 -0
  24. package/dist/email/brand.d.ts +2 -0
  25. package/dist/email/brand.js +148 -0
  26. package/dist/email/components.d.ts +21 -10
  27. package/dist/email/components.js +46 -11
  28. package/dist/email/diagnostics.d.ts +2 -2
  29. package/dist/email/diagnostics.js +2 -1
  30. package/dist/email/elements.d.ts +1 -1
  31. package/dist/email/html.d.ts +2 -42
  32. package/dist/email/html.js +56 -2
  33. package/dist/email/jsx-dev-runtime.d.ts +2 -2
  34. package/dist/email/jsx-runtime.d.ts +1 -1
  35. package/dist/email/render.d.ts +10 -13
  36. package/dist/email/render.js +163 -6
  37. package/dist/email/text.d.ts +1 -1
  38. package/dist/fmt.d.ts +32 -0
  39. package/dist/fmt.js +174 -0
  40. package/dist/html-BJepOwZ4.d.ts +56 -0
  41. package/dist/index.d.ts +2 -0
  42. package/dist/index.js +2 -0
  43. package/dist/ir-aaqNE987.d.ts +252 -0
  44. package/dist/ir.d.ts +2 -0
  45. package/dist/ir.js +7 -0
  46. package/dist/preview-Ekdg1dXG.d.ts +100 -0
  47. package/dist/preview.d.ts +2 -0
  48. package/dist/preview.js +160 -0
  49. package/dist/profile-BH-OtdDA.js +3670 -0
  50. package/dist/render-ir.d.ts +19 -0
  51. package/dist/render-ir.js +826 -0
  52. package/dist/sms-segments.d.ts +13 -2
  53. package/dist/sms.d.ts +16 -0
  54. package/dist/sms.js +12 -0
  55. package/dist/{email/source-edit.d.ts → source-edit.d.ts} +55 -6
  56. package/dist/source-edit.js +343 -0
  57. package/dist/source-locations-C8wCsiAy.d.ts +23 -0
  58. package/dist/source-locations.d.ts +2 -2
  59. package/dist/source-locations.js +3 -89
  60. package/dist/static.d.ts +57 -0
  61. package/dist/static.js +69 -0
  62. package/dist/tailwind-CMYpZf1v.js +614 -0
  63. package/dist/template-_QE_rQND.d.ts +65 -0
  64. package/dist/template.d.ts +2 -2
  65. package/dist/template.js +15 -10
  66. package/dist/theme-css-hFqcPSar.js +587 -0
  67. package/dist/theme-css.d.ts +57 -16
  68. package/dist/theme-css.js +2 -509
  69. package/dist/theme-imports-lvBw3AD1.js +109 -0
  70. package/dist/transform-Ct2OMkFN.d.ts +153 -0
  71. package/dist/unsubscribe-link-CoesnQDU.js +26 -0
  72. package/dist/whatsapp.d.ts +57 -0
  73. package/dist/whatsapp.js +22 -0
  74. package/generated/contract.json +1782 -0
  75. package/generated/reference.md +353 -0
  76. package/package.json +56 -154
  77. package/samva-brand.d.ts +14 -0
  78. package/skills/sml/SKILL.md +123 -0
  79. package/dist/ast-BrDtApwS.d.ts +0 -107
  80. package/dist/ast.d.ts +0 -2
  81. package/dist/ast.js +0 -124
  82. package/dist/candidate.d.ts +0 -29
  83. package/dist/candidate.js +0 -88
  84. package/dist/catalog-60eaYC7b.d.ts +0 -356
  85. package/dist/checks-DBweSIPN.js +0 -58954
  86. package/dist/checks.d.ts +0 -33
  87. package/dist/checks.js +0 -2
  88. package/dist/classes.d.ts +0 -27
  89. package/dist/classes.js +0 -184
  90. package/dist/compile-sms.d.ts +0 -28
  91. package/dist/compile-sms.js +0 -44
  92. package/dist/compile-whatsapp.d.ts +0 -56
  93. package/dist/compile-whatsapp.js +0 -186
  94. package/dist/compile.d.ts +0 -18
  95. package/dist/compile.js +0 -29
  96. package/dist/components.d.ts +0 -289
  97. package/dist/components.js +0 -106
  98. package/dist/conditionals.d.ts +0 -10
  99. package/dist/conditionals.js +0 -74
  100. package/dist/corpus-BjYMqOoY.d.ts +0 -106
  101. package/dist/diagnostics-OcQKQYSY.d.ts +0 -49
  102. package/dist/email/selection.d.ts +0 -91
  103. package/dist/email/selection.js +0 -91
  104. package/dist/email/source-edit.js +0 -185
  105. package/dist/email/stylesheets.d.ts +0 -15
  106. package/dist/email/stylesheets.js +0 -45
  107. package/dist/emit.d.ts +0 -51
  108. package/dist/emit.js +0 -151
  109. package/dist/footer.d.ts +0 -66
  110. package/dist/footer.js +0 -93
  111. package/dist/interpolate.d.ts +0 -24
  112. package/dist/interpolate.js +0 -43
  113. package/dist/jsx-dev-runtime.d.ts +0 -11
  114. package/dist/jsx-dev-runtime.js +0 -15
  115. package/dist/jsx-runtime-I-1nCtl2.d.ts +0 -57
  116. package/dist/jsx-runtime.d.ts +0 -2
  117. package/dist/jsx-runtime.js +0 -121
  118. package/dist/lexer.d.ts +0 -60
  119. package/dist/lexer.js +0 -281
  120. package/dist/model.d.ts +0 -24
  121. package/dist/model.js +0 -77
  122. package/dist/ops-2aiiYO7m.js +0 -630
  123. package/dist/ops.d.ts +0 -114
  124. package/dist/ops.js +0 -2
  125. package/dist/parser-t29cHLVL.d.ts +0 -396
  126. package/dist/parser.d.ts +0 -2
  127. package/dist/parser.js +0 -859
  128. package/dist/predicate-BI6uRRM-.js +0 -549
  129. package/dist/predicate-M-05Nd7R.d.ts +0 -56
  130. package/dist/predicate.d.ts +0 -2
  131. package/dist/predicate.js +0 -2
  132. package/dist/presets/category-mapping.d.ts +0 -13
  133. package/dist/presets/category-mapping.js +0 -49
  134. package/dist/presets/corpus.d.ts +0 -2
  135. package/dist/presets/corpus.js +0 -194
  136. package/dist/presets/public-gallery.d.ts +0 -266
  137. package/dist/presets/public-gallery.js +0 -916
  138. package/dist/presets/public-handoff.d.ts +0 -164
  139. package/dist/presets/public-handoff.js +0 -361
  140. package/dist/presets/public-preview-artifacts.d.ts +0 -43
  141. package/dist/presets/public-preview-artifacts.js +0 -104
  142. package/dist/presets/public-preview.d.ts +0 -61
  143. package/dist/presets/public-preview.js +0 -83
  144. package/dist/presets/showcase.d.ts +0 -2
  145. package/dist/presets/showcase.js +0 -627
  146. package/dist/presets/sources.d.ts +0 -38
  147. package/dist/presets/sources.js +0 -6083
  148. package/dist/presets.d.ts +0 -2
  149. package/dist/presets.js +0 -881
  150. package/dist/printer.d.ts +0 -6
  151. package/dist/printer.js +0 -122
  152. package/dist/registry.d.ts +0 -42
  153. package/dist/registry.js +0 -646
  154. package/dist/render-template.d.ts +0 -45
  155. package/dist/render-template.js +0 -76
  156. package/dist/showcase-DGZAWn4Z.d.ts +0 -456
  157. package/dist/sms-segments-y4FpPilJ.d.ts +0 -13
  158. package/dist/source-locations-DrCM82Ym.d.ts +0 -67
  159. package/dist/template-DWftqh4x.d.ts +0 -60
  160. package/dist/theme-6ApjjiBo.d.ts +0 -116
  161. package/dist/theme.d.ts +0 -2
  162. package/dist/theme.js +0 -118
  163. package/dist/tokens.d.ts +0 -6
  164. package/dist/tokens.js +0 -417
  165. package/dist/tsx-source-instrumentation.d.ts +0 -30
  166. package/dist/tsx-source-instrumentation.js +0 -115
  167. package/dist/tsx-tracer.d.ts +0 -112
  168. package/dist/tsx-tracer.js +0 -985
  169. package/dist/variables-J_L3qxRa.d.ts +0 -77
  170. package/dist/variables.d.ts +0 -2
  171. package/dist/variables.js +0 -141
  172. package/dist/version-BITr58kk.js +0 -96
  173. package/generated/sml-agent-contract.json +0 -709
  174. package/generated/sml-email-agent-contract.json +0 -615
  175. package/generated/sml-email-language-reference.md +0 -94
  176. package/generated/sml-language-reference.md +0 -195
@@ -1,91 +0,0 @@
1
- import { i as JsxSourceLocation } from "../source-locations-DrCM82Ym.js";
2
- //#region src/email/selection.d.ts
3
- /**
4
- * The attribute a preview stamps so a click in a rendered document resolves to
5
- * exactly one rendered element. A delivered message never carries it.
6
- */
7
- declare const INSTANCE_PATH_ATTRIBUTE = "data-samva-instance";
8
- /** Where one element sits in the emitted document, with the authoring that produced it. */
9
- interface EmittedPosition {
10
- readonly start: number;
11
- readonly end: number;
12
- readonly origins: ReadonlyArray<JsxSourceLocation>;
13
- /**
14
- * Dot-separated element indices from the document root, assigned as the
15
- * document is written. It is the identity of one rendered instance.
16
- */
17
- readonly instancePath: string;
18
- readonly tag: string;
19
- /** False when a compiler primitive generated this element. */
20
- readonly authored: boolean;
21
- }
22
- /** One rendered element, and the authoring it came from. */
23
- interface EmailElementSelection {
24
- /**
25
- * Dot-separated element indices from the document root. It is the identity
26
- * of one rendered instance, which is what tells two iterations of the same
27
- * authored element apart: their origins are identical.
28
- */
29
- readonly instancePath: string;
30
- readonly tag: string;
31
- /**
32
- * The authored JSX element first, then each enclosing component call site,
33
- * innermost first. Empty when the compiler generated this element with no
34
- * authoring on the stack — the hidden preheader block, for instance.
35
- */
36
- readonly origins: ReadonlyArray<JsxSourceLocation>;
37
- /**
38
- * False when a compiler primitive generated this element. `origins[0]` is
39
- * then the authoring element that asked for it rather than this element
40
- * itself, so the wrapper selects the primitive the author wrote.
41
- */
42
- readonly authored: boolean;
43
- /** 1-based position among the rendered elements sharing this origin chain and tag. */
44
- readonly occurrence: number;
45
- /**
46
- * How many rendered elements share that chain and tag. Above one, one piece
47
- * of authoring produced several rendered elements — a loop over a component,
48
- * or a primitive that generates a repeated cell — so an edit there changes
49
- * all of them.
50
- */
51
- readonly occurrences: number;
52
- /** Absent for the document root. */
53
- readonly parentPath?: string;
54
- /** Offset range in the rendered HTML this selection was taken from. */
55
- readonly start: number;
56
- readonly end: number;
57
- }
58
- /**
59
- * Turn one render's emitted positions into selections, in document order.
60
- *
61
- * The serializer records an element once its content is written, so positions
62
- * arrive innermost-first; document order is what an outline and an ancestry
63
- * walk both need.
64
- */
65
- declare const emailSelections: (positions: readonly EmittedPosition[]) => readonly EmailElementSelection[];
66
- /** Look selections up by the instance path a rendered document carries. */
67
- declare const emailSelectionIndex: (selections: readonly EmailElementSelection[]) => ReadonlyMap<string, EmailElementSelection>;
68
- /**
69
- * The authoring element a selection edits. For an authored element that is the
70
- * element itself; for a generated wrapper it is the primitive that asked for
71
- * it. Undefined only where the compiler generated markup with no authoring on
72
- * the stack, which nothing can edit in place.
73
- */
74
- declare const emailAuthoringTarget: (selection: EmailElementSelection) => JsxSourceLocation | undefined;
75
- /** Root-first ancestry of a selection, itself last. */
76
- declare const emailSelectionAncestry: (index: ReadonlyMap<string, EmailElementSelection>, instancePath: string) => readonly EmailElementSelection[];
77
- /**
78
- * Every rendered instance produced through the same call site, in document
79
- * order. One entry means the call ran once; several mean it repeated.
80
- */
81
- declare const emailSelectionSiblings: (selections: readonly EmailElementSelection[], selection: EmailElementSelection) => readonly EmailElementSelection[];
82
- /**
83
- * Every rendered instance of the same authored element, across every call site
84
- * that reaches it. A shared component edited at its definition changes all of
85
- * these; edited at one call site it changes only `emailSelectionSiblings`. A
86
- * control has to say which of the two it is doing before it applies.
87
- */
88
- declare const emailDefinitionInstances: (selections: readonly EmailElementSelection[], selection: EmailElementSelection) => readonly EmailElementSelection[];
89
- //#endregion
90
- export { EmailElementSelection, EmittedPosition, INSTANCE_PATH_ATTRIBUTE, emailAuthoringTarget, emailDefinitionInstances, emailSelectionAncestry, emailSelectionIndex, emailSelectionSiblings, emailSelections };
91
- //# sourceMappingURL=selection.d.ts.map
@@ -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,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-COcFOT1y.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,45 +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 diagnostics = registry.flatMap(({ sheet }) => sheet.diagnostics);
27
- for (const { sheet } of registry) {
28
- Object.assign(variables, sheet.variables);
29
- headAtRules.push(...sheet.headAtRules);
30
- }
31
- return {
32
- rules,
33
- headAtRules,
34
- variables,
35
- diagnostics
36
- };
37
- };
38
- /** True when a template registered no styles at all, so the render can skip the pass. */
39
- const hasStylesheets = () => registry.length > 0;
40
- /** The registry as it stands, or `undefined` when no sheet registered. */
41
- const snapshotStylesheets = () => hasStylesheets() ? collectStylesheets() : void 0;
42
- //#endregion
43
- export { collectStylesheets, hasStylesheets, registerStylesheet, resetStylesheets, snapshotStylesheets };
44
-
45
- //# sourceMappingURL=stylesheets.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
package/dist/emit.js DELETED
@@ -1,151 +0,0 @@
1
- import { sameTemplateSourceLocations } from "./source-locations.js";
2
- import { isRootTag } from "./ast.js";
3
- import { createVariablesProxy } from "./variables.js";
4
- import { renderTemplate } from "./render-template.js";
5
- import { Result } from "better-result";
6
- //#region src/emit.ts
7
- /**
8
- * emitModule — the module-level emit boundary for the TSX authoring layer.
9
- * The vite plugin does the importing: TSX runs at the
10
- * USER's build time and this receives the already-evaluated module object (a
11
- * record of its exports). emitModule stays bundler-agnostic — it never touches
12
- * the filesystem, esbuild, or vite. It reads only `moduleExports.default`,
13
- * runs that function through renderTemplate (print → parse validation), and
14
- * additionally renders it TWICE to guard emission determinism (a build-time
15
- * clock or RNG produces a different second emission and is reported as
16
- * unstable).
17
- *
18
- * Named exports are ordinary helpers and are never inspected or invoked. A
19
- * missing or invalid default is a module-level diagnostic (the common "forgot
20
- * to default-export the function" / "imported the wrong file" mistake).
21
- *
22
- * Default-export classification
23
- *
24
- * This recorder recognizes function exports returning a channel root element.
25
- * SMS and WhatsApp use the recorded AST; legacy Email roots remain recognizable
26
- * so the compiler can report the executable defineEmail requirement. Executable
27
- * email definitions are discovered by their definition marker before this path.
28
- * Structured function exports have no marker and are classified by probing.
29
- *
30
- * The proxy makes the probe cheap and side-effect-free for well-formed
31
- * templates: property accesses record variables and return refs; no network,
32
- * no I/O. A default export that throws when called with a proxy, or that
33
- * returns a non-root node, is `invalid-default-export` rather than a crash.
34
- *
35
- * A genuine template whose body throws (a real authoring bug — coercing a
36
- * variable, a malformed child) is therefore classified as an invalid default.
37
- * That is acceptable because the same bug breaks the template's real emit
38
- * regardless. Confirmed templates are rendered by renderTemplate, so a
39
- * template function must be a pure builder of its AST — which the
40
- * recording-proxy model already requires.
41
- */
42
- /** Probe the default export: is it a function that yields a channel root document? */
43
- const isTemplateFunction = (value) => {
44
- if (typeof value !== "function") return false;
45
- const { proxy } = createVariablesProxy();
46
- let result;
47
- try {
48
- result = value(proxy);
49
- } catch {
50
- return false;
51
- }
52
- return result !== null && typeof result === "object" && result.kind === "element" && isRootTag(result.tag);
53
- };
54
- const MISSING_DEFAULT = {
55
- code: "missing-default-export",
56
- message: "no default export — a structured template must default-export a function returning <Sms> or <WhatsApp>. Email templates must default-export defineEmail with an input schema and fixtures. Named exports are ignored.",
57
- span: {
58
- start: 0,
59
- end: 0
60
- }
61
- };
62
- const INVALID_DEFAULT = {
63
- code: "invalid-default-export",
64
- message: "the default export is not a structured Samva template returning <Sms> or <WhatsApp>. Email templates must default-export defineEmail with an input schema and fixtures.",
65
- span: {
66
- start: 0,
67
- end: 0
68
- }
69
- };
70
- const unstableDiagnostic = (name) => ({
71
- code: "unstable-emission",
72
- message: `template "${name}" emits different source on each render — its output depends on non-deterministic build-time data (a clock or random source); templates must be pure functions of their variables`,
73
- span: {
74
- start: 0,
75
- end: 0
76
- }
77
- });
78
- /**
79
- * Emits the one supported template from an already-evaluated module object:
80
- * `moduleExports.default`. Named exports are never probed. `moduleExports` is
81
- * a plain record of the module's exports (the vite plugin imports the TSX;
82
- * this stays bundler-agnostic and pure).
83
- *
84
- * A missing default yields `missing-default-export`. A present default that
85
- * is not a template function yields `invalid-default-export`. Both fail the
86
- * aggregate with an empty `templates` array.
87
- */
88
- const emitModule = (moduleExports) => {
89
- if (!Object.hasOwn(moduleExports, "default")) return {
90
- ok: false,
91
- templates: [],
92
- diagnostics: [MISSING_DEFAULT]
93
- };
94
- const value = moduleExports.default;
95
- if (!isTemplateFunction(value)) return {
96
- ok: false,
97
- templates: [],
98
- diagnostics: [INVALID_DEFAULT]
99
- };
100
- const template = emitOne("default", value);
101
- return {
102
- ok: template.ok,
103
- templates: [template],
104
- diagnostics: []
105
- };
106
- };
107
- /**
108
- * Renders one confirmed template with the determinism guard. The first render is
109
- * the authoritative emission; the second exists only to detect instability
110
- * (a build-time clock or random source). Both renders run at BUILD time only
111
- * (the plugin) —
112
- * runtime send-path rendering never double-renders. For a deterministic template
113
- * the second render is pure redundant work at build time; that is the accepted
114
- * cost of the guard, paid once per template per build.
115
- */
116
- const emitOne = (name, template) => {
117
- const first = renderTemplate(template);
118
- if (Result.isError(first)) return {
119
- name,
120
- ok: false,
121
- source: void 0,
122
- variables: [],
123
- sourceLocations: {
124
- kind: "unavailable",
125
- reason: "render-failed"
126
- },
127
- diagnostics: first.error.diagnostics
128
- };
129
- const rendered = first.value;
130
- const second = renderTemplate(template);
131
- if (Result.isOk(second) && second.value.source === rendered.source && sameTemplateSourceLocations(second.value.sourceLocations, rendered.sourceLocations)) return {
132
- name,
133
- ok: true,
134
- source: rendered.source,
135
- variables: rendered.variables,
136
- sourceLocations: rendered.sourceLocations,
137
- diagnostics: []
138
- };
139
- return {
140
- name,
141
- ok: false,
142
- source: rendered.source,
143
- variables: rendered.variables,
144
- sourceLocations: rendered.sourceLocations,
145
- diagnostics: [unstableDiagnostic(name)]
146
- };
147
- };
148
- //#endregion
149
- export { emitModule };
150
-
151
- //# sourceMappingURL=emit.js.map