@samva/markup 0.8.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 (171) hide show
  1. package/AGENTS.md +90 -37
  2. package/README.md +188 -52
  3. package/dist/assets-C5duwTya.d.ts +40 -0
  4. package/dist/{brand-DcoVkh_S.d.ts → brand-B-0bN_Ir.d.ts} +2 -2
  5. package/dist/{render-vAv_4r9v.js → cascade-t6kxlgEI.js} +93 -280
  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 +10 -257
  12. package/dist/compiler.js +10 -730
  13. package/dist/{css-CS2jLID6.d.ts → css-BVQKICKC.d.ts} +3 -3
  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 -40
  21. package/dist/email/brand-footer.d.ts +2 -2
  22. package/dist/email/brand-footer.js +4 -3
  23. package/dist/email/brand.d.ts +1 -1
  24. package/dist/email/brand.js +1 -1
  25. package/dist/email/components.d.ts +21 -10
  26. package/dist/email/components.js +46 -11
  27. package/dist/email/diagnostics.d.ts +2 -2
  28. package/dist/email/diagnostics.js +2 -1
  29. package/dist/email/elements.d.ts +1 -1
  30. package/dist/email/html.d.ts +2 -2
  31. package/dist/email/html.js +45 -2
  32. package/dist/email/jsx-dev-runtime.d.ts +2 -2
  33. package/dist/email/jsx-runtime.d.ts +1 -1
  34. package/dist/email/render.d.ts +8 -18
  35. package/dist/email/render.js +163 -3
  36. package/dist/email/text.d.ts +1 -1
  37. package/dist/fmt.d.ts +32 -0
  38. package/dist/fmt.js +174 -0
  39. package/dist/{html-Bl9BAdqc.d.ts → html-BJepOwZ4.d.ts} +19 -5
  40. package/dist/index.d.ts +2 -0
  41. package/dist/index.js +2 -0
  42. package/dist/ir-aaqNE987.d.ts +252 -0
  43. package/dist/ir.d.ts +2 -0
  44. package/dist/ir.js +7 -0
  45. package/dist/preview-Ekdg1dXG.d.ts +100 -0
  46. package/dist/preview.d.ts +2 -0
  47. package/dist/preview.js +160 -0
  48. package/dist/profile-BH-OtdDA.js +3670 -0
  49. package/dist/render-ir.d.ts +19 -0
  50. package/dist/render-ir.js +826 -0
  51. package/dist/sms-segments.d.ts +13 -2
  52. package/dist/sms.d.ts +16 -0
  53. package/dist/sms.js +12 -0
  54. package/dist/{email/source-edit.d.ts → source-edit.d.ts} +55 -6
  55. package/dist/source-edit.js +343 -0
  56. package/dist/source-locations-C8wCsiAy.d.ts +23 -0
  57. package/dist/source-locations.d.ts +2 -2
  58. package/dist/source-locations.js +3 -89
  59. package/dist/static.d.ts +57 -0
  60. package/dist/static.js +69 -0
  61. package/dist/tailwind-CMYpZf1v.js +614 -0
  62. package/dist/template-_QE_rQND.d.ts +65 -0
  63. package/dist/template.d.ts +2 -2
  64. package/dist/template.js +15 -10
  65. package/dist/theme-css.d.ts +1 -1
  66. package/dist/theme-imports-lvBw3AD1.js +109 -0
  67. package/dist/transform-Ct2OMkFN.d.ts +153 -0
  68. package/dist/whatsapp.d.ts +57 -0
  69. package/dist/whatsapp.js +22 -0
  70. package/generated/contract.json +1782 -0
  71. package/generated/reference.md +353 -0
  72. package/package.json +41 -151
  73. package/skills/sml/SKILL.md +123 -0
  74. package/dist/ast-BrDtApwS.d.ts +0 -107
  75. package/dist/ast.d.ts +0 -2
  76. package/dist/ast.js +0 -124
  77. package/dist/candidate.d.ts +0 -29
  78. package/dist/candidate.js +0 -88
  79. package/dist/catalog-CKuOooB8.d.ts +0 -354
  80. package/dist/checks-D73Xizbw.js +0 -59366
  81. package/dist/checks.d.ts +0 -33
  82. package/dist/checks.js +0 -2
  83. package/dist/classes.d.ts +0 -26
  84. package/dist/classes.js +0 -184
  85. package/dist/compile-sms.d.ts +0 -28
  86. package/dist/compile-sms.js +0 -44
  87. package/dist/compile-whatsapp.d.ts +0 -56
  88. package/dist/compile-whatsapp.js +0 -186
  89. package/dist/compile.d.ts +0 -18
  90. package/dist/compile.js +0 -29
  91. package/dist/components.d.ts +0 -289
  92. package/dist/components.js +0 -106
  93. package/dist/conditionals.d.ts +0 -10
  94. package/dist/conditionals.js +0 -74
  95. package/dist/corpus-CPPfgAHN.d.ts +0 -106
  96. package/dist/diagnostics-OcQKQYSY.d.ts +0 -49
  97. package/dist/email/selection.d.ts +0 -2
  98. package/dist/email/selection.js +0 -91
  99. package/dist/email/source-edit.js +0 -185
  100. package/dist/email/stylesheets.d.ts +0 -15
  101. package/dist/email/stylesheets.js +0 -48
  102. package/dist/emit.d.ts +0 -51
  103. package/dist/emit.js +0 -151
  104. package/dist/interpolate.d.ts +0 -24
  105. package/dist/interpolate.js +0 -43
  106. package/dist/jsx-dev-runtime.d.ts +0 -11
  107. package/dist/jsx-dev-runtime.js +0 -15
  108. package/dist/jsx-runtime-I-1nCtl2.d.ts +0 -57
  109. package/dist/jsx-runtime.d.ts +0 -2
  110. package/dist/jsx-runtime.js +0 -121
  111. package/dist/lexer.d.ts +0 -60
  112. package/dist/lexer.js +0 -281
  113. package/dist/model.d.ts +0 -24
  114. package/dist/model.js +0 -77
  115. package/dist/ops-BvwqHl7V.js +0 -630
  116. package/dist/ops.d.ts +0 -114
  117. package/dist/ops.js +0 -2
  118. package/dist/parser-t29cHLVL.d.ts +0 -396
  119. package/dist/parser.d.ts +0 -2
  120. package/dist/parser.js +0 -859
  121. package/dist/predicate-BI6uRRM-.js +0 -549
  122. package/dist/predicate-M-05Nd7R.d.ts +0 -56
  123. package/dist/predicate.d.ts +0 -2
  124. package/dist/predicate.js +0 -2
  125. package/dist/printer.d.ts +0 -6
  126. package/dist/printer.js +0 -122
  127. package/dist/registry-Dcp6QEK_.d.ts +0 -73
  128. package/dist/registry.d.ts +0 -2
  129. package/dist/registry.js +0 -646
  130. package/dist/render-template.d.ts +0 -45
  131. package/dist/render-template.js +0 -76
  132. package/dist/selection-CCArx9lU.d.ts +0 -91
  133. package/dist/showcase-egkusQBf.d.ts +0 -415
  134. package/dist/sms-segments-y4FpPilJ.d.ts +0 -13
  135. package/dist/source-locations-DrCM82Ym.d.ts +0 -67
  136. package/dist/starters/category-mapping.d.ts +0 -13
  137. package/dist/starters/category-mapping.js +0 -49
  138. package/dist/starters/corpus.d.ts +0 -2
  139. package/dist/starters/corpus.js +0 -194
  140. package/dist/starters/library.d.ts +0 -18
  141. package/dist/starters/library.js +0 -692
  142. package/dist/starters/public-gallery.d.ts +0 -266
  143. package/dist/starters/public-gallery.js +0 -916
  144. package/dist/starters/public-handoff.d.ts +0 -164
  145. package/dist/starters/public-handoff.js +0 -361
  146. package/dist/starters/public-preview-artifacts.d.ts +0 -43
  147. package/dist/starters/public-preview-artifacts.js +0 -104
  148. package/dist/starters/public-preview.d.ts +0 -61
  149. package/dist/starters/public-preview.js +0 -83
  150. package/dist/starters/showcase.d.ts +0 -2
  151. package/dist/starters/showcase.js +0 -335
  152. package/dist/starters/sources.d.ts +0 -45
  153. package/dist/starters/sources.js +0 -7762
  154. package/dist/starters/theme.d.ts +0 -45
  155. package/dist/starters/theme.js +0 -77
  156. package/dist/starters.d.ts +0 -2
  157. package/dist/starters.js +0 -874
  158. package/dist/tailwind-BYq7Lo6u.js +0 -727
  159. package/dist/template-DWftqh4x.d.ts +0 -60
  160. package/dist/tsx-source-instrumentation.d.ts +0 -30
  161. package/dist/tsx-source-instrumentation.js +0 -115
  162. package/dist/tsx-tracer.d.ts +0 -112
  163. package/dist/tsx-tracer.js +0 -985
  164. package/dist/variables-J_L3qxRa.d.ts +0 -77
  165. package/dist/variables.d.ts +0 -2
  166. package/dist/variables.js +0 -141
  167. package/dist/version-BITr58kk.js +0 -96
  168. package/generated/sml-agent-contract.json +0 -709
  169. package/generated/sml-email-agent-contract.json +0 -615
  170. package/generated/sml-email-language-reference.md +0 -94
  171. 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,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/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
@@ -1,24 +0,0 @@
1
- //#region src/interpolate.d.ts
2
- type DocumentVariableType = "string" | "number" | "boolean" | "date" | "array" | "object";
3
- /** One entry of a template's declared variable schema. */
4
- interface DocumentVariable {
5
- readonly name: string;
6
- readonly type: DocumentVariableType;
7
- readonly required: boolean;
8
- readonly default?: unknown;
9
- readonly description?: string | undefined;
10
- }
11
- declare const variableReferencePattern: RegExp;
12
- declare const getVariableValue: (variables: Record<string, unknown>, variableSchema: ReadonlyArray<DocumentVariable>, name: string) => unknown;
13
- declare const stringifyVariable: (value: unknown) => string;
14
- interface InterpolationResult {
15
- readonly rendered: string;
16
- /** Required variables that resolved to nothing (no value, no default). */
17
- readonly missing: ReadonlySet<string>;
18
- }
19
- declare const interpolate: (text: string, variables: Record<string, unknown>, variableSchema: ReadonlyArray<DocumentVariable>) => InterpolationResult;
20
- declare const variableReferences: (text: string) => Set<string>;
21
- declare const addVariableReferences: (target: Set<string>, text: string) => void;
22
- //#endregion
23
- export { DocumentVariable, DocumentVariableType, InterpolationResult, addVariableReferences, getVariableValue, interpolate, stringifyVariable, variableReferencePattern, variableReferences };
24
- //# sourceMappingURL=interpolate.d.ts.map
@@ -1,43 +0,0 @@
1
- //#region src/interpolate.ts
2
- const variableReferencePattern = /\$\{\s*\/?([\w.]+)\s*\}|\{\{\s*([\w.]+)\s*\}\}/g;
3
- const getVariableValue = (variables, variableSchema, name) => {
4
- const schema = variableSchema.find((item) => item.name === name);
5
- const value = variables[name];
6
- if (value !== void 0 && value !== null) return value;
7
- return schema?.default;
8
- };
9
- const stringifyVariable = (value) => {
10
- if (value === void 0 || value === null) return "";
11
- if (value instanceof Date) return value.toISOString();
12
- if (Array.isArray(value)) return value.join(", ");
13
- if (typeof value === "object") return JSON.stringify(value);
14
- return String(value);
15
- };
16
- const interpolate = (text, variables, variableSchema) => {
17
- const missing = /* @__PURE__ */ new Set();
18
- const substitute = (name) => {
19
- const value = getVariableValue(variables, variableSchema, name);
20
- const schema = variableSchema.find((item) => item.name === name);
21
- if ((value === void 0 || value === null) && schema?.required) missing.add(name);
22
- return stringifyVariable(value);
23
- };
24
- return {
25
- rendered: text.replace(variableReferencePattern, (_match, dollarName, braceName) => substitute(dollarName ?? braceName ?? "")),
26
- missing
27
- };
28
- };
29
- const variableReferences = (text) => {
30
- const names = /* @__PURE__ */ new Set();
31
- for (const match of text.matchAll(variableReferencePattern)) {
32
- const name = match[1] ?? match[2];
33
- if (name !== void 0 && name !== "") names.add(name);
34
- }
35
- return names;
36
- };
37
- const addVariableReferences = (target, text) => {
38
- variableReferences(text).forEach((name) => target.add(name));
39
- };
40
- //#endregion
41
- export { addVariableReferences, getVariableValue, interpolate, stringifyVariable, variableReferencePattern, variableReferences };
42
-
43
- //# sourceMappingURL=interpolate.js.map
@@ -1,11 +0,0 @@
1
- import { r as JsxSource } from "./source-locations-DrCM82Ym.js";
2
- import { a as JsxResult, i as JSX, n as Fragment, t as Component } from "./jsx-runtime-I-1nCtl2.js";
3
- //#region src/jsx-dev-runtime.d.ts
4
- interface JsxDevProps {
5
- readonly children?: unknown;
6
- readonly [key: string]: unknown;
7
- }
8
- declare const jsxDEV: <P>(type: Component<P> | typeof Fragment, props: P, _key?: string | number | null, _isStaticChildren?: boolean, source?: JsxSource | null, _self?: unknown) => JsxResult;
9
- //#endregion
10
- export { Fragment, type JSX, JsxDevProps, jsxDEV };
11
- //# sourceMappingURL=jsx-dev-runtime.d.ts.map
@@ -1,15 +0,0 @@
1
- import { attachJsxSourceLocation, completeJsxSourceLocation } from "./source-locations.js";
2
- import { Fragment, jsx } from "./jsx-runtime.js";
3
- //#region src/jsx-dev-runtime.ts
4
- const jsxDEV = (type, props, _key, _isStaticChildren, source, _self) => {
5
- const result = jsx(type, props);
6
- if (type !== Fragment && result.kind === "element") {
7
- const location = completeJsxSourceLocation(source);
8
- if (location !== void 0) attachJsxSourceLocation(result, location);
9
- }
10
- return result;
11
- };
12
- //#endregion
13
- export { Fragment, jsxDEV };
14
-
15
- //# sourceMappingURL=jsx-dev-runtime.js.map