@samva/markup 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +53 -41
- package/CHANGELOG.md +379 -0
- package/README.md +6 -6
- package/dist/assets-B0yseju_.d.ts +115 -0
- package/dist/brand-DTctcDmf.js +202 -0
- package/dist/brand-plugin-CHhcei-j.d.ts +32 -0
- package/dist/{theme-css.d.ts → brand.d.ts} +15 -4
- package/dist/brand.js +4 -0
- package/dist/compiler.d.ts +236 -18
- package/dist/compiler.js +1315 -13
- package/dist/{email/components.js → components-Bl6DT21n.js} +4 -4
- package/dist/diagnostic-model-DJtqbETd.js +38 -0
- package/dist/diagnostic-model-DxthUZBT.d.ts +31 -0
- package/dist/{email/diagnostics.js → diagnostics-CnKFefKz.js} +2 -2
- package/dist/diagnostics.d.ts +406 -27
- package/dist/diagnostics.js +402 -34
- package/dist/edit.d.ts +234 -0
- package/dist/{channel-edit.js → edit.js} +341 -3
- package/dist/email/jsx-dev-runtime.d.ts +1 -1
- package/dist/email/jsx-dev-runtime.js +1 -2
- package/dist/email/jsx-runtime.d.ts +1 -1
- package/dist/email/jsx-runtime.js +1 -85
- package/dist/{email/components.d.ts → email.d.ts} +2 -2
- package/dist/email.js +2 -0
- package/dist/html-DjPxwjLi.js +1637 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{cascade-t6kxlgEI.js → ir-De6pTH51.js} +8 -4
- package/dist/jsx-runtime-CBlexoeR.js +98 -0
- package/dist/{elements-cqldpaJt.d.ts → jsx-runtime-ylIOnaSf.d.ts} +74 -74
- package/dist/{profile-BH-OtdDA.js → profile-PUfi3GPH.js} +166 -32
- package/dist/{email/render.js → render-D2ht57ZZ.js} +103 -8
- package/dist/render-DQKz4Aco.d.ts +548 -0
- package/dist/render.d.ts +35 -0
- package/dist/render.js +49 -0
- package/dist/sms.d.ts +1 -1
- package/dist/sms.js +1 -1
- package/dist/{template-_QE_rQND.d.ts → template-DMPwxMSq.d.ts} +2 -2
- package/dist/{template.js → template-zLUjcwfF.js} +2 -2
- package/dist/{theme-css-hFqcPSar.js → theme-css-CEUrDpqq.js} +3 -3
- package/dist/whatsapp.d.ts +1 -1
- package/dist/whatsapp.js +1 -1
- package/generated/contract.json +18 -12
- package/generated/reference.md +19 -12
- package/package.json +39 -122
- package/samva-brand.d.ts +4 -4
- package/skills/sml/SKILL.md +2 -2
- package/dist/assets-C5duwTya.d.ts +0 -40
- package/dist/brand-B-0bN_Ir.d.ts +0 -88
- package/dist/channel-edit.d.ts +0 -121
- package/dist/channel-specs-B6M_HlaV.js +0 -29
- package/dist/compatibility-DbISoFIq.js +0 -589
- package/dist/css-BVQKICKC.d.ts +0 -109
- package/dist/diagnostic-codes.d.ts +0 -403
- package/dist/diagnostic-codes.js +0 -399
- package/dist/email/assets.d.ts +0 -2
- package/dist/email/assets.js +0 -63
- package/dist/email/brand-footer.d.ts +0 -34
- package/dist/email/brand-footer.js +0 -63
- package/dist/email/brand.d.ts +0 -2
- package/dist/email/brand.js +0 -148
- package/dist/email/diagnostics.d.ts +0 -2
- package/dist/email/elements.d.ts +0 -2
- package/dist/email/elements.js +0 -299
- package/dist/email/html.d.ts +0 -2
- package/dist/email/html.js +0 -321
- package/dist/email/render.d.ts +0 -40
- package/dist/email/text.d.ts +0 -7
- package/dist/email/text.js +0 -100
- package/dist/html-BJepOwZ4.d.ts +0 -56
- package/dist/ir-aaqNE987.d.ts +0 -252
- package/dist/ir.d.ts +0 -2
- package/dist/ir.js +0 -7
- package/dist/preview-Ekdg1dXG.d.ts +0 -100
- package/dist/preview.d.ts +0 -2
- package/dist/preview.js +0 -160
- package/dist/render-ir.d.ts +0 -19
- package/dist/render-ir.js +0 -826
- package/dist/sms-segments.d.ts +0 -13
- package/dist/sms-segments.js +0 -46
- package/dist/source-edit.d.ts +0 -116
- package/dist/source-edit.js +0 -343
- package/dist/source-locations.d.ts +0 -2
- package/dist/source-locations.js +0 -16
- package/dist/static.d.ts +0 -57
- package/dist/static.js +0 -69
- package/dist/tailwind-CMYpZf1v.js +0 -614
- package/dist/template.d.ts +0 -2
- package/dist/theme-css.js +0 -2
- package/dist/theme-imports-lvBw3AD1.js +0 -109
- package/dist/transform-Ct2OMkFN.d.ts +0 -153
- package/dist/unsubscribe-link-CoesnQDU.js +0 -26
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as TEMPLATE_ID_PATTERN, o as TemplateInput, r as SmlTemplate, s as defineTemplate, t as EmailChannel } from "./template-
|
|
1
|
+
import { i as TEMPLATE_ID_PATTERN, o as TemplateInput, r as SmlTemplate, s as defineTemplate, t as EmailChannel } from "./template-DMPwxMSq.js";
|
|
2
2
|
export { type EmailChannel, type SmlTemplate, TEMPLATE_ID_PATTERN, type TemplateInput, defineTemplate };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { r as defineTemplate, t as TEMPLATE_ID_PATTERN } from "./template-zLUjcwfF.js";
|
|
2
2
|
export { TEMPLATE_ID_PATTERN, defineTemplate };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as isObject } from "./guards-eV1qLbf2.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { a as kebab, n as declarationValue, r as escapeAttribute } from "./html-DjPxwjLi.js";
|
|
3
|
+
import { t as EmailNode } from "./jsx-runtime-CBlexoeR.js";
|
|
4
4
|
//#region src/email/font-stacks.ts
|
|
5
5
|
/** Generic families every email client understands as the last resort in a stack. */
|
|
6
6
|
const GENERIC_FAMILIES = /* @__PURE__ */ new Set([
|
|
@@ -574,6 +574,10 @@ const applyStylesheet = (root, sheet, options = {}) => {
|
|
|
574
574
|
};
|
|
575
575
|
};
|
|
576
576
|
//#endregion
|
|
577
|
-
|
|
577
|
+
//#region src/ir.ts
|
|
578
|
+
/** The IR version this package writes. A publication pins the version it was built with. */
|
|
579
|
+
const SML_IR_VERSION = 1;
|
|
580
|
+
//#endregion
|
|
581
|
+
export { downlevelMediaQuery as a, renderHeadCss as c, matchesSelector as i, resolveVariables as l, applyStylesheet as n, finalizeValue as o, classListOf as r, foldCalc as s, SML_IR_VERSION as t, unquote as u };
|
|
578
582
|
|
|
579
|
-
//# sourceMappingURL=
|
|
583
|
+
//# sourceMappingURL=ir-De6pTH51.js.map
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { t as hasProperty } from "./guards-eV1qLbf2.js";
|
|
2
|
+
//#region src/source-locations.ts
|
|
3
|
+
/** The JSX prop that carries a {@link JsxSource} to the email JSX runtime. */
|
|
4
|
+
const JSX_SOURCE_LOCATION_PROP = "__samva_source_location";
|
|
5
|
+
/** Normalize the optional payload a JSX transform attaches. */
|
|
6
|
+
const completeJsxSourceLocation = (source) => {
|
|
7
|
+
if (source?.fileName === void 0 || source.lineNumber === void 0 || source.columnNumber === void 0) return;
|
|
8
|
+
return Object.freeze({
|
|
9
|
+
fileName: source.fileName,
|
|
10
|
+
lineNumber: source.lineNumber,
|
|
11
|
+
columnNumber: source.columnNumber
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/email/jsx-runtime.ts
|
|
16
|
+
/** Constructors for the authoring tree. The tree is data; nothing here validates it. */
|
|
17
|
+
const EmailNode = {
|
|
18
|
+
Text: (fields) => ({
|
|
19
|
+
type: "Text",
|
|
20
|
+
...fields
|
|
21
|
+
}),
|
|
22
|
+
Element: (fields) => ({
|
|
23
|
+
type: "Element",
|
|
24
|
+
...fields
|
|
25
|
+
}),
|
|
26
|
+
Fragment: (fields) => ({
|
|
27
|
+
type: "Fragment",
|
|
28
|
+
...fields
|
|
29
|
+
}),
|
|
30
|
+
Raw: (fields) => ({
|
|
31
|
+
type: "Raw",
|
|
32
|
+
...fields
|
|
33
|
+
})
|
|
34
|
+
};
|
|
35
|
+
const EMAIL_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
36
|
+
"Text",
|
|
37
|
+
"Element",
|
|
38
|
+
"Fragment",
|
|
39
|
+
"Raw"
|
|
40
|
+
]);
|
|
41
|
+
/** True for a node this runtime built; anything else is not a child it can place. */
|
|
42
|
+
const isEmailNode = (input) => hasProperty(input, "type") && typeof input.type === "string" && EMAIL_NODE_TYPES.has(input.type);
|
|
43
|
+
const Fragment = Symbol.for("@samva/markup/email/Fragment");
|
|
44
|
+
const callSites = [];
|
|
45
|
+
/** Call sites enclosing the element being built, innermost first. */
|
|
46
|
+
const currentEmailOrigins = () => [...callSites].reverse();
|
|
47
|
+
const reported = [];
|
|
48
|
+
/** Record a finding from inside a compiler primitive, located at the authoring call site. */
|
|
49
|
+
const reportEmailDiagnostic = (diagnostic) => {
|
|
50
|
+
reported.push({
|
|
51
|
+
...diagnostic,
|
|
52
|
+
origins: currentEmailOrigins()
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
/** Take and clear the findings primitives reported during a render. */
|
|
56
|
+
const drainEmailDiagnostics = () => reported.splice(0);
|
|
57
|
+
/** Normalize actual values without interpolation or recording proxies. */
|
|
58
|
+
const normalizeChildren = (input) => {
|
|
59
|
+
if (input === null || input === void 0 || typeof input === "boolean") return [];
|
|
60
|
+
if (typeof input === "string") return [EmailNode.Text({ value: input })];
|
|
61
|
+
if (typeof input === "number" && Number.isFinite(input)) return [EmailNode.Text({ value: String(input) })];
|
|
62
|
+
if (Array.isArray(input)) return input.flatMap(normalizeChildren);
|
|
63
|
+
if (isEmailNode(input)) return [input];
|
|
64
|
+
throw new TypeError("Email children must be synchronous text, finite numbers, elements, or arrays");
|
|
65
|
+
};
|
|
66
|
+
/** Compatibility markup emitted by a compiler primitive, with the text it stands for. */
|
|
67
|
+
const rawMarkup = (html, text = "") => EmailNode.Raw({
|
|
68
|
+
html,
|
|
69
|
+
text,
|
|
70
|
+
origins: currentEmailOrigins()
|
|
71
|
+
});
|
|
72
|
+
/** Automatic JSX factory for ordinary HTML and synchronous imported components. */
|
|
73
|
+
const jsx = (type, props) => {
|
|
74
|
+
const { children, [JSX_SOURCE_LOCATION_PROP]: source, ...attributes } = props ?? {};
|
|
75
|
+
const origin = completeJsxSourceLocation(source);
|
|
76
|
+
if (type === Fragment) return EmailNode.Fragment({ children: normalizeChildren(children) });
|
|
77
|
+
if (typeof type === "string") return EmailNode.Element({
|
|
78
|
+
tag: type,
|
|
79
|
+
props: attributes,
|
|
80
|
+
children: normalizeChildren(children),
|
|
81
|
+
origins: origin === void 0 ? currentEmailOrigins() : [origin, ...currentEmailOrigins()],
|
|
82
|
+
authored: origin !== void 0
|
|
83
|
+
});
|
|
84
|
+
if (origin !== void 0) callSites.push(origin);
|
|
85
|
+
try {
|
|
86
|
+
return EmailNode.Fragment({ children: normalizeChildren(type({
|
|
87
|
+
...attributes,
|
|
88
|
+
children
|
|
89
|
+
})) });
|
|
90
|
+
} finally {
|
|
91
|
+
if (origin !== void 0) callSites.pop();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const jsxs = jsx;
|
|
95
|
+
//#endregion
|
|
96
|
+
export { jsx as a, rawMarkup as c, completeJsxSourceLocation as d, drainEmailDiagnostics as i, reportEmailDiagnostic as l, Fragment as n, jsxs as o, currentEmailOrigins as r, normalizeChildren as s, EmailNode as t, JSX_SOURCE_LOCATION_PROP as u };
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=jsx-runtime-CBlexoeR.js.map
|
|
@@ -1,77 +1,5 @@
|
|
|
1
1
|
import { r as JsxSourceLocation } from "./source-locations-C8wCsiAy.js";
|
|
2
2
|
import { r as EmailDiagnostic } from "./diagnostics-B-Gym34R.js";
|
|
3
|
-
//#region src/email/jsx-runtime.d.ts
|
|
4
|
-
/** Derived authoring tree; source stays canonical. Origins run from inner element to component call sites. */
|
|
5
|
-
type EmailNode = {
|
|
6
|
-
readonly type: "Text";
|
|
7
|
-
readonly value: string;
|
|
8
|
-
} | {
|
|
9
|
-
readonly type: "Element";
|
|
10
|
-
readonly tag: string;
|
|
11
|
-
readonly props: Readonly<Record<string, unknown>>;
|
|
12
|
-
readonly children: readonly EmailNode[];
|
|
13
|
-
readonly origins: readonly JsxSourceLocation[];
|
|
14
|
-
/**
|
|
15
|
-
* True when this element is written at `origins[0]`; false when a compiler
|
|
16
|
-
* primitive generated it and `origins[0]` is the call site that asked for
|
|
17
|
-
* it. Both point at the same authoring target, but only an authored element
|
|
18
|
-
* can have its own props edited there.
|
|
19
|
-
*/
|
|
20
|
-
readonly authored: boolean;
|
|
21
|
-
} | {
|
|
22
|
-
readonly type: "Fragment";
|
|
23
|
-
readonly children: readonly EmailNode[];
|
|
24
|
-
} |
|
|
25
|
-
/**
|
|
26
|
-
* Compatibility markup a compiler primitive emits verbatim — conditional
|
|
27
|
-
* comments and VML. Only the primitives in `./components` construct it;
|
|
28
|
-
* nothing an author passes as a string reaches this node.
|
|
29
|
-
*/
|
|
30
|
-
{
|
|
31
|
-
readonly type: "Raw";
|
|
32
|
-
readonly html: string;
|
|
33
|
-
readonly text: string;
|
|
34
|
-
readonly origins: readonly JsxSourceLocation[];
|
|
35
|
-
};
|
|
36
|
-
type EmailNodeOf<Type extends EmailNode["type"]> = Extract<EmailNode, {
|
|
37
|
-
readonly type: Type;
|
|
38
|
-
}>;
|
|
39
|
-
type EmailNodeFields<Type extends EmailNode["type"]> = Omit<EmailNodeOf<Type>, "type">;
|
|
40
|
-
/** Constructors for the authoring tree. The tree is data; nothing here validates it. */
|
|
41
|
-
declare const EmailNode: {
|
|
42
|
-
readonly Text: (fields: EmailNodeFields<"Text">) => EmailNodeOf<"Text">;
|
|
43
|
-
readonly Element: (fields: EmailNodeFields<"Element">) => EmailNodeOf<"Element">;
|
|
44
|
-
readonly Fragment: (fields: EmailNodeFields<"Fragment">) => EmailNodeOf<"Fragment">;
|
|
45
|
-
readonly Raw: (fields: EmailNodeFields<"Raw">) => EmailNodeOf<"Raw">;
|
|
46
|
-
};
|
|
47
|
-
type EmailChild = EmailNode | string | number | boolean | null | undefined | readonly EmailChild[];
|
|
48
|
-
type EmailComponent<P = never> = (props: P) => EmailChild;
|
|
49
|
-
declare const Fragment: unique symbol;
|
|
50
|
-
/** Call sites enclosing the element being built, innermost first. */
|
|
51
|
-
declare const currentEmailOrigins: () => readonly JsxSourceLocation[];
|
|
52
|
-
/** Record a finding from inside a compiler primitive, located at the authoring call site. */
|
|
53
|
-
declare const reportEmailDiagnostic: (diagnostic: Omit<EmailDiagnostic, "origins">) => void;
|
|
54
|
-
/** Take and clear the findings primitives reported during a render. */
|
|
55
|
-
declare const drainEmailDiagnostics: () => readonly EmailDiagnostic[];
|
|
56
|
-
/** Normalize actual values without interpolation or recording proxies. */
|
|
57
|
-
declare const normalizeChildren: (input: unknown) => EmailNode[];
|
|
58
|
-
/** Compatibility markup emitted by a compiler primitive, with the text it stands for. */
|
|
59
|
-
declare const rawMarkup: (html: string, text?: string) => EmailNode;
|
|
60
|
-
/** Automatic JSX factory for ordinary HTML and synchronous imported components. */
|
|
61
|
-
declare const jsx: (type: string | EmailComponent<Record<string, unknown>> | typeof Fragment, props: Record<string, unknown> | null) => EmailNode;
|
|
62
|
-
declare const jsxs: typeof jsx;
|
|
63
|
-
declare namespace JSX {
|
|
64
|
-
type Element = EmailNode;
|
|
65
|
-
type ElementType = string | EmailComponent | typeof Fragment;
|
|
66
|
-
interface ElementChildrenAttribute {
|
|
67
|
-
readonly children: object;
|
|
68
|
-
}
|
|
69
|
-
interface IntrinsicAttributes {
|
|
70
|
-
readonly key?: string | number | undefined;
|
|
71
|
-
}
|
|
72
|
-
interface IntrinsicElements extends EmailIntrinsicElements {}
|
|
73
|
-
}
|
|
74
|
-
//#endregion
|
|
75
3
|
//#region src/email/elements.d.ts
|
|
76
4
|
/** How one attribute is spelled and what values it accepts. */
|
|
77
5
|
interface EmailAttributeSpec {
|
|
@@ -695,5 +623,77 @@ type ChildrenProp<Content extends EmailElementSpec["content"]> = Content extends
|
|
|
695
623
|
/** Props each supported tag accepts, derived from `EMAIL_ELEMENTS`. */
|
|
696
624
|
type EmailIntrinsicElements = { readonly [Tag in EmailTag]: EmailGlobalProps & AttributeProps<typeof EMAIL_GLOBAL_ATTRIBUTES> & AttributeProps<(typeof EMAIL_ELEMENTS)[Tag]["attributes"]> & ChildrenProp<(typeof EMAIL_ELEMENTS)[Tag]["content"]>; };
|
|
697
625
|
//#endregion
|
|
698
|
-
|
|
699
|
-
|
|
626
|
+
//#region src/email/jsx-runtime.d.ts
|
|
627
|
+
/** Derived authoring tree; source stays canonical. Origins run from inner element to component call sites. */
|
|
628
|
+
type EmailNode = {
|
|
629
|
+
readonly type: "Text";
|
|
630
|
+
readonly value: string;
|
|
631
|
+
} | {
|
|
632
|
+
readonly type: "Element";
|
|
633
|
+
readonly tag: string;
|
|
634
|
+
readonly props: Readonly<Record<string, unknown>>;
|
|
635
|
+
readonly children: readonly EmailNode[];
|
|
636
|
+
readonly origins: readonly JsxSourceLocation[];
|
|
637
|
+
/**
|
|
638
|
+
* True when this element is written at `origins[0]`; false when a compiler
|
|
639
|
+
* primitive generated it and `origins[0]` is the call site that asked for
|
|
640
|
+
* it. Both point at the same authoring target, but only an authored element
|
|
641
|
+
* can have its own props edited there.
|
|
642
|
+
*/
|
|
643
|
+
readonly authored: boolean;
|
|
644
|
+
} | {
|
|
645
|
+
readonly type: "Fragment";
|
|
646
|
+
readonly children: readonly EmailNode[];
|
|
647
|
+
} |
|
|
648
|
+
/**
|
|
649
|
+
* Compatibility markup a compiler primitive emits verbatim — conditional
|
|
650
|
+
* comments and VML. Only the primitives in `./components` construct it;
|
|
651
|
+
* nothing an author passes as a string reaches this node.
|
|
652
|
+
*/
|
|
653
|
+
{
|
|
654
|
+
readonly type: "Raw";
|
|
655
|
+
readonly html: string;
|
|
656
|
+
readonly text: string;
|
|
657
|
+
readonly origins: readonly JsxSourceLocation[];
|
|
658
|
+
};
|
|
659
|
+
type EmailNodeOf<Type extends EmailNode["type"]> = Extract<EmailNode, {
|
|
660
|
+
readonly type: Type;
|
|
661
|
+
}>;
|
|
662
|
+
type EmailNodeFields<Type extends EmailNode["type"]> = Omit<EmailNodeOf<Type>, "type">;
|
|
663
|
+
/** Constructors for the authoring tree. The tree is data; nothing here validates it. */
|
|
664
|
+
declare const EmailNode: {
|
|
665
|
+
readonly Text: (fields: EmailNodeFields<"Text">) => EmailNodeOf<"Text">;
|
|
666
|
+
readonly Element: (fields: EmailNodeFields<"Element">) => EmailNodeOf<"Element">;
|
|
667
|
+
readonly Fragment: (fields: EmailNodeFields<"Fragment">) => EmailNodeOf<"Fragment">;
|
|
668
|
+
readonly Raw: (fields: EmailNodeFields<"Raw">) => EmailNodeOf<"Raw">;
|
|
669
|
+
};
|
|
670
|
+
type EmailChild = EmailNode | string | number | boolean | null | undefined | readonly EmailChild[];
|
|
671
|
+
type EmailComponent<P = never> = (props: P) => EmailChild;
|
|
672
|
+
declare const Fragment: unique symbol;
|
|
673
|
+
/** Call sites enclosing the element being built, innermost first. */
|
|
674
|
+
declare const currentEmailOrigins: () => readonly JsxSourceLocation[];
|
|
675
|
+
/** Record a finding from inside a compiler primitive, located at the authoring call site. */
|
|
676
|
+
declare const reportEmailDiagnostic: (diagnostic: Omit<EmailDiagnostic, "origins">) => void;
|
|
677
|
+
/** Take and clear the findings primitives reported during a render. */
|
|
678
|
+
declare const drainEmailDiagnostics: () => readonly EmailDiagnostic[];
|
|
679
|
+
/** Normalize actual values without interpolation or recording proxies. */
|
|
680
|
+
declare const normalizeChildren: (input: unknown) => EmailNode[];
|
|
681
|
+
/** Compatibility markup emitted by a compiler primitive, with the text it stands for. */
|
|
682
|
+
declare const rawMarkup: (html: string, text?: string) => EmailNode;
|
|
683
|
+
/** Automatic JSX factory for ordinary HTML and synchronous imported components. */
|
|
684
|
+
declare const jsx: (type: string | EmailComponent<Record<string, unknown>> | typeof Fragment, props: Record<string, unknown> | null) => EmailNode;
|
|
685
|
+
declare const jsxs: typeof jsx;
|
|
686
|
+
declare namespace JSX {
|
|
687
|
+
type Element = EmailNode;
|
|
688
|
+
type ElementType = string | EmailComponent | typeof Fragment;
|
|
689
|
+
interface ElementChildrenAttribute {
|
|
690
|
+
readonly children: object;
|
|
691
|
+
}
|
|
692
|
+
interface IntrinsicAttributes {
|
|
693
|
+
readonly key?: string | number | undefined;
|
|
694
|
+
}
|
|
695
|
+
interface IntrinsicElements extends EmailIntrinsicElements {}
|
|
696
|
+
}
|
|
697
|
+
//#endregion
|
|
698
|
+
export { isUrlAttribute as C, emailElement as S, EmailGlobalProps as _, JSX as a, EmailStyle as b, jsx as c, rawMarkup as d, reportEmailDiagnostic as f, EmailElementSpec as g, EmailAttributeSpec as h, Fragment as i, jsxs as l, EMAIL_GLOBAL_ATTRIBUTES as m, EmailComponent as n, currentEmailOrigins as o, EMAIL_ELEMENTS as p, EmailNode as r, drainEmailDiagnostics as s, EmailChild as t, normalizeChildren as u, EmailIntrinsicElements as v, EmailTag as x, EmailLayout as y };
|
|
699
|
+
//# sourceMappingURL=jsx-runtime-ylIOnaSf.d.ts.map
|
|
@@ -1,20 +1,113 @@
|
|
|
1
1
|
import { n as isObject } from "./guards-eV1qLbf2.js";
|
|
2
2
|
import { n as inputSchema, r as jsonSchema } from "./input-schema-zjkgrXn2.js";
|
|
3
|
-
import {
|
|
3
|
+
import { n as TEMPLATE_NAME_PATTERN, t as TEMPLATE_ID_PATTERN } from "./template-zLUjcwfF.js";
|
|
4
|
+
import { D as samvaBrandPlugin, E as reservedBrandAttributes, O as BRAND_FOOTER_MARKER, S as emailElement, T as parseBrandSpecifier, b as EMAIL_ELEMENTS, s as serializeStyle, t as checkAttribute } from "./html-DjPxwjLi.js";
|
|
4
5
|
import { FORMATTER_NAMES } from "./fmt.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { n as
|
|
8
|
-
import {
|
|
9
|
-
import { brandComponents } from "./
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
6
|
+
import { i as drainEmailDiagnostics, t as EmailNode } from "./jsx-runtime-CBlexoeR.js";
|
|
7
|
+
import { a as Email, d as equalColumnWidth, i as Divider, l as Section, n as Column, o as Image, r as Columns, s as Link, t as Button, u as Spacer } from "./components-Bl6DT21n.js";
|
|
8
|
+
import { n as applyStylesheet } from "./ir-De6pTH51.js";
|
|
9
|
+
import { i as lineColumn, t as buildLineMap } from "./diagnostic-model-DJtqbETd.js";
|
|
10
|
+
import { l as isAssetPath, n as brandComponents } from "./brand-DTctcDmf.js";
|
|
11
|
+
import { parse } from "css-tree";
|
|
12
|
+
import { parse as parse$1 } from "@babel/parser";
|
|
13
|
+
//#region src/email/theme-imports.ts
|
|
14
|
+
/** The directory part of a project path, with no trailing slash (`""` at the root). */
|
|
15
|
+
const projectDirectory = (path) => path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : "";
|
|
16
|
+
/** A relative or root-absolute stylesheet reference, resolved from a project directory. */
|
|
17
|
+
const resolveFromDirectory = (directory, reference) => {
|
|
18
|
+
const segments = reference.startsWith("/") || directory === "" ? [] : directory.split("/");
|
|
19
|
+
for (const segment of reference.split("/")) {
|
|
20
|
+
if (segment === "" || segment === ".") continue;
|
|
21
|
+
if (segment === "..") {
|
|
22
|
+
if (segments.length === 0) return void 0;
|
|
23
|
+
segments.pop();
|
|
24
|
+
} else segments.push(segment);
|
|
25
|
+
}
|
|
26
|
+
return segments.length === 0 ? void 0 : segments.join("/");
|
|
27
|
+
};
|
|
28
|
+
const importTarget = (node) => {
|
|
29
|
+
if (node.type !== "Atrule" || node.prelude === null || node.prelude.type !== "AtrulePrelude") return { extra: true };
|
|
30
|
+
const parts = [...node.prelude.children].filter((part) => part.type !== "WhiteSpace");
|
|
31
|
+
const first = parts[0];
|
|
32
|
+
const target = first?.type === "String" ? first.value : first?.type === "Url" ? first.value : void 0;
|
|
33
|
+
return target === void 0 ? { extra: true } : {
|
|
34
|
+
target,
|
|
35
|
+
extra: parts.length > 1
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Walk the theme's `@import` graph. Project `.css` files resolve from the
|
|
40
|
+
* importing file's directory; the plugin's brand specifier, bare or with a
|
|
41
|
+
* slug, is recorded for the brand the build supplies. Anything else is an
|
|
42
|
+
* error.
|
|
43
|
+
*/
|
|
44
|
+
const resolveThemeImports = (themePath, themeCss, files, brandPlugin) => {
|
|
45
|
+
const brand = JSON.stringify(brandPlugin.specifier);
|
|
46
|
+
const stylesheets = /* @__PURE__ */ new Map([[themePath, themeCss]]);
|
|
47
|
+
const brandSpecifiers = /* @__PURE__ */ new Set();
|
|
48
|
+
const diagnostics = [];
|
|
49
|
+
const visit = (path, source, chain) => {
|
|
50
|
+
const lineMap = buildLineMap(source);
|
|
51
|
+
const ast = parse(source, {
|
|
52
|
+
positions: true,
|
|
53
|
+
parseValue: false,
|
|
54
|
+
parseRulePrelude: false
|
|
55
|
+
});
|
|
56
|
+
if (ast.type !== "StyleSheet") return;
|
|
57
|
+
for (const node of ast.children) {
|
|
58
|
+
if (node.type !== "Atrule" || node.name.toLowerCase() !== "import") continue;
|
|
59
|
+
const position = lineColumn(lineMap, node.loc?.start.offset ?? 0);
|
|
60
|
+
const origins = [{
|
|
61
|
+
fileName: path,
|
|
62
|
+
lineNumber: position.line,
|
|
63
|
+
columnNumber: position.column
|
|
64
|
+
}];
|
|
65
|
+
const refuse = (message) => diagnostics.push({
|
|
66
|
+
code: "css-import-unresolved",
|
|
67
|
+
severity: "error",
|
|
68
|
+
message,
|
|
69
|
+
origins
|
|
70
|
+
});
|
|
71
|
+
const { target, extra } = importTarget(node);
|
|
72
|
+
if (target === void 0) {
|
|
73
|
+
refuse(`${path}: @import needs a quoted stylesheet path or ${brand}.`);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (extra) {
|
|
77
|
+
refuse(`${path}: @import ${JSON.stringify(target)} takes no layer, supports or media conditions; the import order is the layering.`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (parseBrandSpecifier(target, brandPlugin) !== void 0) {
|
|
81
|
+
brandSpecifiers.add(target);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (!/^\.{0,2}\//.test(target) || !/\.css$/i.test(target.replace(/[?#].*$/, ""))) {
|
|
85
|
+
refuse(`${path}: @import ${JSON.stringify(target)} is not a project stylesheet or ${brand}. The compiler supplies Tailwind itself; import project .css files by relative path.`);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const resolved = resolveFromDirectory(projectDirectory(path), target.replace(/[?#].*$/, ""));
|
|
89
|
+
const content = resolved === void 0 ? void 0 : files[resolved];
|
|
90
|
+
if (resolved === void 0 || typeof content !== "string") {
|
|
91
|
+
refuse(`${path}: @import ${JSON.stringify(target)} names no stylesheet in the project.`);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (chain.includes(resolved)) {
|
|
95
|
+
refuse(`${path}: @import ${JSON.stringify(target)} imports itself through ${chain.join(" → ")}.`);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (stylesheets.has(resolved)) continue;
|
|
99
|
+
stylesheets.set(resolved, content);
|
|
100
|
+
visit(resolved, content, [...chain, resolved]);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
visit(themePath, themeCss, [themePath]);
|
|
104
|
+
return {
|
|
105
|
+
stylesheets,
|
|
106
|
+
brandSpecifiers: [...brandSpecifiers].sort(),
|
|
107
|
+
diagnostics
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
//#endregion
|
|
18
111
|
//#region src/sml/diagnostics.ts
|
|
19
112
|
/** Collects the findings of one compile. Every finding carries a code, a location and a fix. */
|
|
20
113
|
var Findings = class {
|
|
@@ -125,7 +218,7 @@ const parseSource = (path, source) => {
|
|
|
125
218
|
const position = (offset) => lineColumn(lines, offset);
|
|
126
219
|
let program;
|
|
127
220
|
try {
|
|
128
|
-
program = parse(source, {
|
|
221
|
+
program = parse$1(source, {
|
|
129
222
|
plugins: ["typescript", "jsx"],
|
|
130
223
|
sourceType: "module"
|
|
131
224
|
}).program;
|
|
@@ -163,6 +256,32 @@ const isValueNode = (node) => {
|
|
|
163
256
|
return true;
|
|
164
257
|
};
|
|
165
258
|
//#endregion
|
|
259
|
+
//#region src/channel-specs.ts
|
|
260
|
+
const SMS_CATEGORIES = [
|
|
261
|
+
"transactional",
|
|
262
|
+
"promotional",
|
|
263
|
+
"service_implicit"
|
|
264
|
+
];
|
|
265
|
+
const WHATSAPP_CATEGORIES = [
|
|
266
|
+
"marketing",
|
|
267
|
+
"utility",
|
|
268
|
+
"authentication"
|
|
269
|
+
];
|
|
270
|
+
const WHATSAPP_HEADER_TYPES = [
|
|
271
|
+
"text",
|
|
272
|
+
"image",
|
|
273
|
+
"video",
|
|
274
|
+
"document"
|
|
275
|
+
];
|
|
276
|
+
/** The attributes `<Sms>` accepts. */
|
|
277
|
+
const SMS_ATTRIBUTES = ["category"];
|
|
278
|
+
/** The attributes `<WhatsApp>` accepts. */
|
|
279
|
+
const WHATSAPP_ATTRIBUTES = [
|
|
280
|
+
"name",
|
|
281
|
+
"language",
|
|
282
|
+
"category"
|
|
283
|
+
];
|
|
284
|
+
//#endregion
|
|
166
285
|
//#region src/whatsapp-template.ts
|
|
167
286
|
const isConcat = (value) => typeof value === "object" && value !== null && "concat" in value;
|
|
168
287
|
/**
|
|
@@ -968,7 +1087,7 @@ const columnsEach = (call, bindings) => {
|
|
|
968
1087
|
/** Run a compiler primitive at compile time and lower what it returns. */
|
|
969
1088
|
const runBuiltinComponent = (call) => {
|
|
970
1089
|
const { lowerer, ctx, node, binding } = call;
|
|
971
|
-
const specifier = binding.
|
|
1090
|
+
const specifier = binding.specifier;
|
|
972
1091
|
if (binding.kind === "markup" && (specifier === "@samva/markup/sms" || specifier === "@samva/markup/whatsapp")) {
|
|
973
1092
|
const lowered = call.channel?.(call);
|
|
974
1093
|
if (lowered !== void 0) return lowered;
|
|
@@ -976,20 +1095,20 @@ const runBuiltinComponent = (call) => {
|
|
|
976
1095
|
return [];
|
|
977
1096
|
}
|
|
978
1097
|
const exported = binding.imported;
|
|
979
|
-
const isEmail = binding.kind === "markup" && specifier === "@samva/markup/email
|
|
1098
|
+
const isEmail = binding.kind === "markup" && specifier === "@samva/markup/email";
|
|
980
1099
|
const isBrand = binding.kind === "brand";
|
|
981
1100
|
if (!(isEmail || isBrand)) {
|
|
982
|
-
lowerer.error(ctx, node, "unknown-component", `<${call.name}> is not a component the profile can read.`,
|
|
1101
|
+
lowerer.error(ctx, node, "unknown-component", `<${call.name}> is not a component the profile can read.`, `Import components from @samva/markup/email, ${call.brandPlugin.specifier}, or the project.`);
|
|
983
1102
|
return [];
|
|
984
1103
|
}
|
|
985
1104
|
let fn;
|
|
986
1105
|
if (isEmail) fn = COMPONENTS[exported];
|
|
987
1106
|
else {
|
|
988
1107
|
if (call.brand === void 0) {
|
|
989
|
-
lowerer.error(ctx, node, "brand-unavailable", `<${call.name}> comes from
|
|
1108
|
+
lowerer.error(ctx, node, "brand-unavailable", `<${call.name}> comes from ${binding.specifier}, and this build has no brand to resolve it.`, "Compile with a brand, or remove the brand components.");
|
|
990
1109
|
return [];
|
|
991
1110
|
}
|
|
992
|
-
const brand = brandComponents(call.brand);
|
|
1111
|
+
const brand = brandComponents(call.brand, call.brandPlugin);
|
|
993
1112
|
fn = exported === "BrandLogo" ? brand.BrandLogo : exported === "BrandFooter" ? brand.BrandFooter : void 0;
|
|
994
1113
|
}
|
|
995
1114
|
if (fn === void 0 || !Object.hasOwn(COMPONENTS, exported) && isEmail) {
|
|
@@ -1281,8 +1400,10 @@ var Lowerer = class {
|
|
|
1281
1400
|
options;
|
|
1282
1401
|
sources = [];
|
|
1283
1402
|
uniqueId = 0;
|
|
1403
|
+
reservedAttributes;
|
|
1284
1404
|
constructor(options) {
|
|
1285
1405
|
this.options = options;
|
|
1406
|
+
this.reservedAttributes = reservedBrandAttributes(options.brandPlugin);
|
|
1286
1407
|
}
|
|
1287
1408
|
get findings() {
|
|
1288
1409
|
return this.options.findings;
|
|
@@ -2004,8 +2125,8 @@ var Lowerer = class {
|
|
|
2004
2125
|
this.error(ctx, attribute, isHtml ? "dangerous-html" : name === "ref" ? "unsupported-attribute" : "event-handler", isHtml ? "`dangerouslySetInnerHTML` injects unescaped markup." : name === "ref" ? "A `ref` needs a running component." : `\`${name}\` is an event handler; email clients strip scripting and Samva refuses it.`, isHtml ? "Write the markup as elements." : "Remove it.");
|
|
2005
2126
|
continue;
|
|
2006
2127
|
}
|
|
2007
|
-
if (
|
|
2008
|
-
this.error(ctx, attribute, "reserved-attribute", `\`${name}\` is written only by BrandFooter from
|
|
2128
|
+
if (this.reservedAttributes.has(name)) {
|
|
2129
|
+
this.error(ctx, attribute, "reserved-attribute", `\`${name}\` is written only by BrandFooter from ${this.options.brandPlugin.specifier}.`, "Render <BrandFooter /> instead of marking an element as the footer.");
|
|
2009
2130
|
continue;
|
|
2010
2131
|
}
|
|
2011
2132
|
if (name === "className" || name === "class") {
|
|
@@ -2029,7 +2150,7 @@ var Lowerer = class {
|
|
|
2029
2150
|
}
|
|
2030
2151
|
if (value === void 0) continue;
|
|
2031
2152
|
if (value === false || value === null) continue;
|
|
2032
|
-
if (value ===
|
|
2153
|
+
if (value === this.options.brandPlugin.unsubscribeUrlPlaceholder) {
|
|
2033
2154
|
this.error(ctx, attribute, "reserved-url", "That URL is the unsubscribe placeholder only BrandFooter can write.", "Render <BrandFooter /> instead.");
|
|
2034
2155
|
continue;
|
|
2035
2156
|
}
|
|
@@ -2176,6 +2297,7 @@ var Lowerer = class {
|
|
|
2176
2297
|
name,
|
|
2177
2298
|
binding: imported,
|
|
2178
2299
|
brand: this.options.brand,
|
|
2300
|
+
brandPlugin: this.options.brandPlugin,
|
|
2179
2301
|
channel: this.options.channel
|
|
2180
2302
|
});
|
|
2181
2303
|
let fn;
|
|
@@ -2187,7 +2309,7 @@ var Lowerer = class {
|
|
|
2187
2309
|
value: local.value
|
|
2188
2310
|
};
|
|
2189
2311
|
if (fn === void 0 || !/Function|Arrow/.test(fn.value.type)) {
|
|
2190
|
-
this.error(ctx, node, "unknown-component", `<${name}> is not a component the profile can read.`, "Import it from @samva/markup/email
|
|
2312
|
+
this.error(ctx, node, "unknown-component", `<${name}> is not a component the profile can read.`, "Import it from @samva/markup/email, or from a project file that exports it as a function.");
|
|
2191
2313
|
return [];
|
|
2192
2314
|
}
|
|
2193
2315
|
return this.partial(ctx, node, attributes, name, fn.module, fn.value);
|
|
@@ -3217,7 +3339,7 @@ const lowerProject = (options, project, findings, inputs) => {
|
|
|
3217
3339
|
staticContext,
|
|
3218
3340
|
assetUrl: urlFor,
|
|
3219
3341
|
brand: options.brand,
|
|
3220
|
-
|
|
3342
|
+
brandPlugin: options.brandPlugin ?? samvaBrandPlugin,
|
|
3221
3343
|
channel: channelLowering
|
|
3222
3344
|
});
|
|
3223
3345
|
const rootContext = (target) => ({
|
|
@@ -3407,6 +3529,8 @@ const lowerProject = (options, project, findings, inputs) => {
|
|
|
3407
3529
|
//#region src/sml/project.ts
|
|
3408
3530
|
const MARKUP_SPECIFIER = "@samva/markup";
|
|
3409
3531
|
const MARKUP_PREFIX = "@samva/markup/";
|
|
3532
|
+
/** Entries a template could import that moved; each is reported with its replacement. */
|
|
3533
|
+
const MOVED_MARKUP_ENTRIES = { "@samva/markup/email/components": "@samva/markup/email" };
|
|
3410
3534
|
const SCRIPT_EXTENSIONS = [".tsx", ".ts"];
|
|
3411
3535
|
/** Resolve a relative specifier to a project file, or `undefined`. */
|
|
3412
3536
|
const resolveSpecifier = (from, specifier, files) => {
|
|
@@ -3419,7 +3543,7 @@ const resolveSpecifier = (from, specifier, files) => {
|
|
|
3419
3543
|
const isScriptPath = (path) => /\.[cm]?[jt]sx?$/.test(path);
|
|
3420
3544
|
const declaredNames = (pattern) => pattern.type === "Identifier" ? [text(pattern, "name") ?? ""] : [];
|
|
3421
3545
|
/** Load the entry and everything it imports from the project. */
|
|
3422
|
-
const loadProject = (files, entryPath, findings, loadOptions
|
|
3546
|
+
const loadProject = (files, entryPath, findings, loadOptions) => {
|
|
3423
3547
|
const modules = /* @__PURE__ */ new Map();
|
|
3424
3548
|
const unresolved = /* @__PURE__ */ new Set();
|
|
3425
3549
|
const loading = /* @__PURE__ */ new Set();
|
|
@@ -3486,6 +3610,12 @@ const loadProject = (files, entryPath, findings, loadOptions = {}) => {
|
|
|
3486
3610
|
const imported = child(node, "imported");
|
|
3487
3611
|
return imported === void 0 ? local(node) : text(imported, "name") ?? text(imported, "value") ?? local(node);
|
|
3488
3612
|
};
|
|
3613
|
+
const moved = MOVED_MARKUP_ENTRIES[specifier];
|
|
3614
|
+
if (moved !== void 0) {
|
|
3615
|
+
findings.add(parsed, child(statement, "source") ?? statement, "moved-import", `\`${specifier}\` moved to \`${moved}\`; the imported names are unchanged.`, `Import from "${moved}".`);
|
|
3616
|
+
failed = true;
|
|
3617
|
+
break;
|
|
3618
|
+
}
|
|
3489
3619
|
if (specifier === MARKUP_SPECIFIER || specifier.startsWith(MARKUP_PREFIX)) {
|
|
3490
3620
|
for (const item of specifiers) {
|
|
3491
3621
|
if (text(item, "importKind") === "type") continue;
|
|
@@ -3498,7 +3628,7 @@ const loadProject = (files, entryPath, findings, loadOptions = {}) => {
|
|
|
3498
3628
|
}
|
|
3499
3629
|
break;
|
|
3500
3630
|
}
|
|
3501
|
-
if (
|
|
3631
|
+
if (parseBrandSpecifier(specifier, loadOptions.brandPlugin) !== void 0) {
|
|
3502
3632
|
for (const item of specifiers) if (text(item, "importKind") !== "type") imports.set(local(item), {
|
|
3503
3633
|
kind: "brand",
|
|
3504
3634
|
specifier,
|
|
@@ -3631,13 +3761,17 @@ const asFiles = (files, entry) => typeof files === "string" ? { [entry]: files }
|
|
|
3631
3761
|
* and the entry's path. Imports of files that are absent are trusted to exist, and the brand,
|
|
3632
3762
|
* Tailwind and theme are not resolved, so the result holds only findings the source itself owns.
|
|
3633
3763
|
*/
|
|
3634
|
-
const checkStaticProfile = (files, entry = DEFAULT_ENTRY) => {
|
|
3764
|
+
const checkStaticProfile = (files, entry = DEFAULT_ENTRY, brandPlugin = samvaBrandPlugin) => {
|
|
3635
3765
|
const findings = new Findings();
|
|
3636
|
-
const project = loadProject(asFiles(files, entry), entry, findings, {
|
|
3766
|
+
const project = loadProject(asFiles(files, entry), entry, findings, {
|
|
3767
|
+
brandPlugin,
|
|
3768
|
+
lenient: true
|
|
3769
|
+
});
|
|
3637
3770
|
if (project === void 0) return findings.items;
|
|
3638
3771
|
lowerProject({
|
|
3639
3772
|
entry,
|
|
3640
|
-
brand: void 0
|
|
3773
|
+
brand: void 0,
|
|
3774
|
+
brandPlugin
|
|
3641
3775
|
}, project, findings, {
|
|
3642
3776
|
assets: [],
|
|
3643
3777
|
stylesheets: /* @__PURE__ */ new Map(),
|
|
@@ -3665,6 +3799,6 @@ const introducedProfileErrors = (before, after) => {
|
|
|
3665
3799
|
return added;
|
|
3666
3800
|
};
|
|
3667
3801
|
//#endregion
|
|
3668
|
-
export { lowerProject as a, completed as i, introducedProfileErrors as n,
|
|
3802
|
+
export { lowerProject as a, WHATSAPP_HEADER_TYPES as c, resolveFromDirectory as d, resolveThemeImports as f, completed as i, Findings as l, introducedProfileErrors as n, SMS_CATEGORIES as o, loadProject as r, WHATSAPP_CATEGORIES as s, checkStaticProfile as t, projectDirectory as u };
|
|
3669
3803
|
|
|
3670
|
-
//# sourceMappingURL=profile-
|
|
3804
|
+
//# sourceMappingURL=profile-PUfi3GPH.js.map
|