@samva/markup 0.18.0 → 0.19.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 +11 -4
- package/CHANGELOG.md +36 -0
- package/README.md +89 -5
- package/dist/assets-BSpgZOSR.js +61 -0
- package/dist/assets-k7_v3IOJ.d.ts +39 -0
- package/dist/{assets-CS5Xa_mr.d.ts → brand-Bxb6Sm7y.d.ts} +34 -41
- package/dist/brand.d.ts +2 -2
- package/dist/brand.js +3 -3
- package/dist/compatibility.d.ts +15 -5
- package/dist/compatibility.js +48 -5
- package/dist/compile-C1GWLQ5C.js +490 -0
- package/dist/compile-CIeuGmP-.d.ts +98 -0
- package/dist/{compiled-template-Bc4Dq9zy.d.ts → compiled-template-CPDHvFlt.d.ts} +157 -6
- package/dist/compiler.d.ts +4 -89
- package/dist/compiler.js +8 -486
- package/dist/components-DNBCz20_.js +3267 -0
- package/dist/{css-color-DLM12Osu.js → dark-scheme-BGDCCaUq.js} +12 -1
- package/dist/{diagnostic-codes-DZKb0Xcg.js → diagnostic-codes-BCwfUgnF.js} +33 -36
- package/dist/diagnostic-model-DJtqbETd.js +36 -0
- package/dist/{diagnostics-D4vSLQrO.d.ts → diagnostics-C3TSx6SU.d.ts} +22 -2
- package/dist/diagnostics.d.ts +33 -1
- package/dist/diagnostics.js +2 -1
- package/dist/edit.d.ts +15 -4
- package/dist/edit.js +67 -4
- package/dist/email/jsx-dev-runtime.d.ts +2 -2
- package/dist/email/jsx-runtime.d.ts +1 -1
- package/dist/email.d.ts +6 -2
- package/dist/email.js +6 -2
- package/dist/index.d.ts +1 -1
- package/dist/{jsx-runtime-DL8NVF1f.d.ts → jsx-runtime-HHfrVZEf.d.ts} +1 -2
- package/dist/markdown.d.ts +69 -0
- package/dist/markdown.js +95 -0
- package/dist/parse-Bex7QFqj.js +921 -0
- package/dist/{preview-rNoJBEIY.d.ts → preview-DRLWgmfg.d.ts} +3 -3
- package/dist/profile-DFk4gs7o.js +56 -0
- package/dist/{profile-JTvgCD6X.js → project-B6D64SZS.js} +497 -234
- package/dist/render.d.ts +8 -8
- package/dist/render.js +1 -2
- package/dist/{css-B_aVKrDo.js → selectors-CKGuc9MO.js} +158 -4
- package/dist/sms.d.ts +1 -1
- package/dist/{template-DmNcoH4Q.d.ts → template-4Dse7_OC.d.ts} +1 -1
- package/dist/{theme-css-P9F5S7Ic.js → theme-css-BujDuuEA.js} +1 -2
- package/dist/whatsapp.d.ts +1 -1
- package/generated/contract.json +41 -1
- package/generated/reference.md +97 -89
- package/package.json +15 -4
- package/dist/brand-CIiEqkU7.js +0 -193
- package/dist/brand-plugin-Dj9SMpfS.d.ts +0 -33
- package/dist/components-C5850-Qg.js +0 -409
- package/dist/dark-scheme-CbCwGDWH.js +0 -12
- package/dist/html-DJUK2YJF.js +0 -483
- package/dist/render-template-DwPDm3Hq.js +0 -1226
- package/dist/selectors-CYWCoxZr.js +0 -157
- package/dist/source-locations-C8wCsiAy.d.ts +0 -22
|
@@ -312,4 +312,15 @@ const downlevelColors = (value) => {
|
|
|
312
312
|
return result;
|
|
313
313
|
};
|
|
314
314
|
//#endregion
|
|
315
|
-
|
|
315
|
+
//#region src/email/dark-scheme.ts
|
|
316
|
+
/** The media query a theme's dark-mode block and every generated dark-mode rule sit in. */
|
|
317
|
+
const DARK_SCHEME_MEDIA = "@media (prefers-color-scheme: dark)";
|
|
318
|
+
const DARK_SCHEME_QUERY = /^(?:only\s+)?(?:screen\s+and\s+)?\(\s*prefers-color-scheme\s*:\s*dark\s*\)$/i;
|
|
319
|
+
/** Whether a `@media` prelude is exactly the dark-mode query. */
|
|
320
|
+
const isDarkSchemeQuery = (prelude) => DARK_SCHEME_QUERY.test(prelude.trim());
|
|
321
|
+
/** Whether a rule's enclosing conditions are exactly the dark-mode query. */
|
|
322
|
+
const isDarkSchemeCondition = (conditions) => conditions.length === 1 && /^@media\s/i.test(conditions[0]) && isDarkSchemeQuery(conditions[0].replace(/^@media\s+/i, ""));
|
|
323
|
+
/** Whether any enclosing condition is the dark-mode query, as in a size query nested inside it. */
|
|
324
|
+
const withinDarkScheme = (conditions) => conditions.some((condition) => isDarkSchemeCondition([condition]));
|
|
325
|
+
//#endregion
|
|
326
|
+
export { downlevelColors as a, withinDarkScheme as i, isDarkSchemeCondition as n, parseColor as o, isDarkSchemeQuery as r, DARK_SCHEME_MEDIA as t };
|
|
@@ -1,41 +1,34 @@
|
|
|
1
|
-
//#region src/diagnostic-model.ts
|
|
2
|
-
/** Newline-offset line map. Build once per source, query with `lineColumn`. */
|
|
3
|
-
const buildLineMap = (source) => {
|
|
4
|
-
const lineStarts = [0];
|
|
5
|
-
for (let index = 0; index < source.length; index++) {
|
|
6
|
-
const code = source.charCodeAt(index);
|
|
7
|
-
if (code === 10 || code === 13 && source.charCodeAt(index + 1) !== 10 || code === 8232 || code === 8233) lineStarts.push(index + 1);
|
|
8
|
-
}
|
|
9
|
-
return lineStarts;
|
|
10
|
-
};
|
|
11
|
-
/** 1-based line/column for an absolute offset, via binary search. */
|
|
12
|
-
const lineColumn = (lineMap, offset) => {
|
|
13
|
-
let low = 0;
|
|
14
|
-
let high = lineMap.length - 1;
|
|
15
|
-
while (low < high) {
|
|
16
|
-
const middle = low + high + 1 >> 1;
|
|
17
|
-
if (lineMap[middle] <= offset) low = middle;
|
|
18
|
-
else high = middle - 1;
|
|
19
|
-
}
|
|
20
|
-
return {
|
|
21
|
-
line: low + 1,
|
|
22
|
-
column: offset - lineMap[low] + 1
|
|
23
|
-
};
|
|
24
|
-
};
|
|
25
|
-
/** Build one formatter for a batch of diagnostics against the same source. */
|
|
26
|
-
const createDiagnosticFormatter = (source) => {
|
|
27
|
-
const lineMap = buildLineMap(source);
|
|
28
|
-
return (diagnostic) => {
|
|
29
|
-
const { line, column } = lineColumn(lineMap, diagnostic.span.start);
|
|
30
|
-
return `line ${line}:${column} ${diagnostic.message}`;
|
|
31
|
-
};
|
|
32
|
-
};
|
|
33
|
-
/** "line 12:8 unknown class ..." — the shape agents iterate on. */
|
|
34
|
-
const formatDiagnostic = (source, diagnostic) => createDiagnosticFormatter(source)(diagnostic);
|
|
35
|
-
//#endregion
|
|
36
1
|
//#region src/diagnostic-codes.ts
|
|
37
2
|
const codes = (table) => table;
|
|
38
3
|
const DIAGNOSTIC_CODES = codes({
|
|
4
|
+
"issue-layout": {
|
|
5
|
+
summary: "A newsletter issue layout does not declare only an email channel.",
|
|
6
|
+
fix: "Use an email-only defineTemplate layout with one IssueContent slot."
|
|
7
|
+
},
|
|
8
|
+
"issue-slot": {
|
|
9
|
+
summary: "An issue layout does not contain exactly one unconditional content slot.",
|
|
10
|
+
fix: "Import IssueContent from @samva/markup/email and place one <IssueContent /> outside loops and conditions."
|
|
11
|
+
},
|
|
12
|
+
"markdown-syntax": {
|
|
13
|
+
summary: "An issue contains syntax outside SML Markdown.",
|
|
14
|
+
fix: "Write Markdown prose and supported SML component tags."
|
|
15
|
+
},
|
|
16
|
+
"markdown-expression": {
|
|
17
|
+
summary: "An issue contains an expression or spread, which is never evaluated.",
|
|
18
|
+
fix: "Replace expressions with literal prose or quoted static component attributes."
|
|
19
|
+
},
|
|
20
|
+
"markdown-html": {
|
|
21
|
+
summary: "An issue contains raw HTML rather than a supported SML component.",
|
|
22
|
+
fix: "Use Markdown syntax or a component from the closed SML component set."
|
|
23
|
+
},
|
|
24
|
+
"markdown-frontmatter": {
|
|
25
|
+
summary: "Issue frontmatter is missing or does not match the typed contract.",
|
|
26
|
+
fix: "Provide title, slug and topic strings; use only the documented preheader, seo and gate fields."
|
|
27
|
+
},
|
|
28
|
+
"markdown-prop": {
|
|
29
|
+
summary: "A Markdown component has an unknown, missing or invalid literal prop.",
|
|
30
|
+
fix: "Use the component's documented props with quoted static values or bare boolean flags."
|
|
31
|
+
},
|
|
39
32
|
"no-template": {
|
|
40
33
|
summary: "The entry does not default-export a template.",
|
|
41
34
|
fix: "Default-export defineTemplate({ ... }) imported from @samva/markup."
|
|
@@ -392,6 +385,10 @@ const DIAGNOSTIC_CODES = codes({
|
|
|
392
385
|
summary: "The project imports two different brands.",
|
|
393
386
|
fix: "Import one brand specifier everywhere."
|
|
394
387
|
},
|
|
388
|
+
"email-component-degradation": {
|
|
389
|
+
summary: "A component loses content or behavior when lowered to email.",
|
|
390
|
+
fix: "Compare the email and web previews; provide an email-safe equivalent for content readers need."
|
|
391
|
+
},
|
|
395
392
|
"caniemail/unknown-coverage": {
|
|
396
393
|
summary: "A feature has no client-support data.",
|
|
397
394
|
fix: "Verify it in the target clients."
|
|
@@ -470,4 +467,4 @@ const DIAGNOSTIC_CODE_FAMILIES = { "caniemail/": {
|
|
|
470
467
|
fix: "Use the alternative the message names, such as an email component instead of display: flex."
|
|
471
468
|
} };
|
|
472
469
|
//#endregion
|
|
473
|
-
export {
|
|
470
|
+
export { DIAGNOSTIC_CODE_FAMILIES as n, hasUpgradeRecipe as r, DIAGNOSTIC_CODES as t };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region src/diagnostic-model.ts
|
|
2
|
+
/** Newline-offset line map. Build once per source, query with `lineColumn`. */
|
|
3
|
+
const buildLineMap = (source) => {
|
|
4
|
+
const lineStarts = [0];
|
|
5
|
+
for (let index = 0; index < source.length; index++) {
|
|
6
|
+
const code = source.charCodeAt(index);
|
|
7
|
+
if (code === 10 || code === 13 && source.charCodeAt(index + 1) !== 10 || code === 8232 || code === 8233) lineStarts.push(index + 1);
|
|
8
|
+
}
|
|
9
|
+
return lineStarts;
|
|
10
|
+
};
|
|
11
|
+
/** 1-based line/column for an absolute offset, via binary search. */
|
|
12
|
+
const lineColumn = (lineMap, offset) => {
|
|
13
|
+
let low = 0;
|
|
14
|
+
let high = lineMap.length - 1;
|
|
15
|
+
while (low < high) {
|
|
16
|
+
const middle = low + high + 1 >> 1;
|
|
17
|
+
if (lineMap[middle] <= offset) low = middle;
|
|
18
|
+
else high = middle - 1;
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
line: low + 1,
|
|
22
|
+
column: offset - lineMap[low] + 1
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
/** Build one formatter for a batch of diagnostics against the same source. */
|
|
26
|
+
const createDiagnosticFormatter = (source) => {
|
|
27
|
+
const lineMap = buildLineMap(source);
|
|
28
|
+
return (diagnostic) => {
|
|
29
|
+
const { line, column } = lineColumn(lineMap, diagnostic.span.start);
|
|
30
|
+
return `line ${line}:${column} ${diagnostic.message}`;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
/** "line 12:8 unknown class ..." — the shape agents iterate on. */
|
|
34
|
+
const formatDiagnostic = (source, diagnostic) => createDiagnosticFormatter(source)(diagnostic);
|
|
35
|
+
//#endregion
|
|
36
|
+
export { lineColumn as i, createDiagnosticFormatter as n, formatDiagnostic as r, buildLineMap as t };
|
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/source-locations.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The source payload a JSX transform may attach to an element. The transform may omit any field
|
|
4
|
+
* for synthetic calls; only a complete payload can identify a source origin safely.
|
|
5
|
+
*/
|
|
6
|
+
interface JsxSource {
|
|
7
|
+
readonly fileName?: string | undefined;
|
|
8
|
+
readonly lineNumber?: number | undefined;
|
|
9
|
+
readonly columnNumber?: number | undefined;
|
|
10
|
+
}
|
|
11
|
+
/** A complete, normalized source origin for a JSX element. */
|
|
12
|
+
interface JsxSourceLocation {
|
|
13
|
+
readonly fileName: string;
|
|
14
|
+
readonly lineNumber: number;
|
|
15
|
+
readonly columnNumber: number;
|
|
16
|
+
}
|
|
17
|
+
/** The JSX prop that carries a {@link JsxSource} to the email JSX runtime. */
|
|
18
|
+
declare const JSX_SOURCE_LOCATION_PROP: "__samva_source_location";
|
|
19
|
+
/** Normalize the optional payload a JSX transform attaches. */
|
|
20
|
+
declare const completeJsxSourceLocation: (source: JsxSource | null | undefined) => JsxSourceLocation | undefined;
|
|
21
|
+
//#endregion
|
|
2
22
|
//#region src/email/diagnostics.d.ts
|
|
3
23
|
type EmailDiagnosticSeverity = "error" | "warning" | "info";
|
|
4
24
|
/** What the emitted document establishes beyond the client feature matrix. */
|
|
@@ -39,4 +59,4 @@ declare const formatEmailDiagnostic: (diagnostic: TemplateDiagnostic) => string;
|
|
|
39
59
|
/** True when a set of findings must stop the compile or refuse a publication. */
|
|
40
60
|
declare const hasBlockingDiagnostic: (diagnostics: readonly EmailDiagnostic[]) => boolean;
|
|
41
61
|
//#endregion
|
|
42
|
-
export { formatEmailDiagnostic as a, TemplateDiagnostic as i, EmailDiagnostic as n, hasBlockingDiagnostic as o, EmailDiagnosticSeverity as r, EmailCompatibilityAssessment as t };
|
|
62
|
+
export { formatEmailDiagnostic as a, JsxSource as c, TemplateDiagnostic as i, JsxSourceLocation as l, EmailDiagnostic as n, hasBlockingDiagnostic as o, EmailDiagnosticSeverity as r, JSX_SOURCE_LOCATION_PROP as s, EmailCompatibilityAssessment as t, completeJsxSourceLocation as u };
|
package/dist/diagnostics.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as formatEmailDiagnostic, i as TemplateDiagnostic, n as EmailDiagnostic, o as hasBlockingDiagnostic, r as EmailDiagnosticSeverity, t as EmailCompatibilityAssessment } from "./diagnostics-
|
|
1
|
+
import { a as formatEmailDiagnostic, i as TemplateDiagnostic, n as EmailDiagnostic, o as hasBlockingDiagnostic, r as EmailDiagnosticSeverity, t as EmailCompatibilityAssessment } from "./diagnostics-C3TSx6SU.js";
|
|
2
2
|
import { a as createDiagnosticFormatter, i as buildLineMap, n as DiagnosticRepair, o as formatDiagnostic, r as Span, s as lineColumn, t as Diagnostic } from "./diagnostic-model-DxthUZBT.js";
|
|
3
3
|
//#region src/diagnostic-codes.d.ts
|
|
4
4
|
interface DiagnosticCodeSpec {
|
|
@@ -12,6 +12,34 @@ interface DiagnosticCodeSpec {
|
|
|
12
12
|
readonly channelOnly?: true;
|
|
13
13
|
}
|
|
14
14
|
declare const DIAGNOSTIC_CODES: {
|
|
15
|
+
readonly "issue-layout": {
|
|
16
|
+
readonly summary: "A newsletter issue layout does not declare only an email channel.";
|
|
17
|
+
readonly fix: "Use an email-only defineTemplate layout with one IssueContent slot.";
|
|
18
|
+
};
|
|
19
|
+
readonly "issue-slot": {
|
|
20
|
+
readonly summary: "An issue layout does not contain exactly one unconditional content slot.";
|
|
21
|
+
readonly fix: "Import IssueContent from @samva/markup/email and place one <IssueContent /> outside loops and conditions.";
|
|
22
|
+
};
|
|
23
|
+
readonly "markdown-syntax": {
|
|
24
|
+
readonly summary: "An issue contains syntax outside SML Markdown.";
|
|
25
|
+
readonly fix: "Write Markdown prose and supported SML component tags.";
|
|
26
|
+
};
|
|
27
|
+
readonly "markdown-expression": {
|
|
28
|
+
readonly summary: "An issue contains an expression or spread, which is never evaluated.";
|
|
29
|
+
readonly fix: "Replace expressions with literal prose or quoted static component attributes.";
|
|
30
|
+
};
|
|
31
|
+
readonly "markdown-html": {
|
|
32
|
+
readonly summary: "An issue contains raw HTML rather than a supported SML component.";
|
|
33
|
+
readonly fix: "Use Markdown syntax or a component from the closed SML component set.";
|
|
34
|
+
};
|
|
35
|
+
readonly "markdown-frontmatter": {
|
|
36
|
+
readonly summary: "Issue frontmatter is missing or does not match the typed contract.";
|
|
37
|
+
readonly fix: "Provide title, slug and topic strings; use only the documented preheader, seo and gate fields.";
|
|
38
|
+
};
|
|
39
|
+
readonly "markdown-prop": {
|
|
40
|
+
readonly summary: "A Markdown component has an unknown, missing or invalid literal prop.";
|
|
41
|
+
readonly fix: "Use the component's documented props with quoted static values or bare boolean flags.";
|
|
42
|
+
};
|
|
15
43
|
readonly "no-template": {
|
|
16
44
|
readonly summary: "The entry does not default-export a template.";
|
|
17
45
|
readonly fix: "Default-export defineTemplate({ ... }) imported from @samva/markup.";
|
|
@@ -356,6 +384,10 @@ declare const DIAGNOSTIC_CODES: {
|
|
|
356
384
|
readonly summary: "The project imports two different brands.";
|
|
357
385
|
readonly fix: "Import one brand specifier everywhere.";
|
|
358
386
|
};
|
|
387
|
+
readonly "email-component-degradation": {
|
|
388
|
+
readonly summary: "A component loses content or behavior when lowered to email.";
|
|
389
|
+
readonly fix: "Compare the email and web previews; provide an email-safe equivalent for content readers need.";
|
|
390
|
+
};
|
|
359
391
|
readonly "caniemail/unknown-coverage": {
|
|
360
392
|
readonly summary: "A feature has no client-support data.";
|
|
361
393
|
readonly fix: "Verify it in the target clients.";
|
package/dist/diagnostics.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { n as formatEmailDiagnostic, r as hasBlockingDiagnostic } from "./diagnostics-C0Tr8-eQ.js";
|
|
2
|
-
import {
|
|
2
|
+
import { i as lineColumn, n as createDiagnosticFormatter, r as formatDiagnostic, t as buildLineMap } from "./diagnostic-model-DJtqbETd.js";
|
|
3
|
+
import { n as DIAGNOSTIC_CODE_FAMILIES, r as hasUpgradeRecipe, t as DIAGNOSTIC_CODES } from "./diagnostic-codes-BCwfUgnF.js";
|
|
3
4
|
export { DIAGNOSTIC_CODES, DIAGNOSTIC_CODE_FAMILIES, buildLineMap, createDiagnosticFormatter, formatDiagnostic, formatEmailDiagnostic, hasBlockingDiagnostic, hasUpgradeRecipe, lineColumn };
|
package/dist/edit.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as emailAuthoringTarget, c as emailSelectionIndex, d as previewEmail, i as INSTANCE_PATH_ATTRIBUTE, l as emailSelectionSiblings, n as EmailPreview, o as emailDefinitionInstances, r as EmittedPosition, s as emailSelectionAncestry, t as EmailElementSelection, u as emailSelections } from "./preview-
|
|
1
|
+
import { c as JsxSource, l as JsxSourceLocation, s as JSX_SOURCE_LOCATION_PROP, u as completeJsxSourceLocation } from "./diagnostics-C3TSx6SU.js";
|
|
2
|
+
import { a as emailAuthoringTarget, c as emailSelectionIndex, d as previewEmail, i as INSTANCE_PATH_ATTRIBUTE, l as emailSelectionSiblings, n as EmailPreview, o as emailDefinitionInstances, r as EmittedPosition, s as emailSelectionAncestry, t as EmailElementSelection, u as emailSelections } from "./preview-DRLWgmfg.js";
|
|
3
3
|
import { n as WhatsAppCategory, r as WhatsAppHeaderType, t as SmsCategory } from "./channel-specs-C2feJwuP.js";
|
|
4
4
|
//#region src/source-edit.d.ts
|
|
5
5
|
/** One exact source replacement; before text guards against concurrent changes. */
|
|
@@ -23,7 +23,7 @@ interface SourceProperty {
|
|
|
23
23
|
readonly literalType: "string" | "number" | "boolean";
|
|
24
24
|
readonly start: number;
|
|
25
25
|
readonly end: number;
|
|
26
|
-
readonly syntax: "attribute" | "child" | "value";
|
|
26
|
+
readonly syntax: "attribute" | "child" | "value" | "markdown" | "markdown-attribute" | "markdown-boolean";
|
|
27
27
|
}
|
|
28
28
|
interface SourceElement {
|
|
29
29
|
readonly start: number;
|
|
@@ -45,6 +45,13 @@ interface SourceRange {
|
|
|
45
45
|
}
|
|
46
46
|
/** Resolve a renderer origin against the unchanged authored entry, never against HTML offsets. */
|
|
47
47
|
declare const inspectSourceElement: (source: string, file: string, origin: JsxSourceLocation) => SourceElement | null;
|
|
48
|
+
/**
|
|
49
|
+
* Resolve a Markdown render origin to authored prose or component attributes. A tag distinguishes
|
|
50
|
+
* nodes sharing an origin; without one, prefer an editable authored element over its wrapper.
|
|
51
|
+
*/
|
|
52
|
+
declare const inspectMarkdownNode: (source: string, file: string, origin: JsxSourceLocation, options?: {
|
|
53
|
+
readonly tag?: string | undefined;
|
|
54
|
+
}) => SourceElement | null;
|
|
48
55
|
/** Literal controls cannot overwrite a binding or computed expression. */
|
|
49
56
|
declare const literalReplacement: (source: string, field: SourceProperty, value: string) => SourceReplacement | null;
|
|
50
57
|
/** Explicit expression changes are proposals: callers show before/after and require Apply. */
|
|
@@ -63,6 +70,10 @@ interface ProfileGuard {
|
|
|
63
70
|
readonly files?: Readonly<Record<string, string>> | undefined;
|
|
64
71
|
}
|
|
65
72
|
interface ApplyOptions {
|
|
73
|
+
/** Validate edits as an issue, including its closed grammar and frontmatter. */
|
|
74
|
+
readonly markdown?: {
|
|
75
|
+
readonly file: string;
|
|
76
|
+
} | undefined;
|
|
66
77
|
readonly profile?: ProfileGuard | undefined;
|
|
67
78
|
}
|
|
68
79
|
/** Apply exact, nonoverlapping edits and refuse syntax damage without changing the original. */
|
|
@@ -230,4 +241,4 @@ declare const readWhatsAppChannel: (source: string) => WhatsAppChannelForm | und
|
|
|
230
241
|
/** The source replacements that apply one form change to the WhatsApp body. */
|
|
231
242
|
declare const editWhatsAppChannel: (source: string, edit: WhatsAppEdit) => ChannelEditResult;
|
|
232
243
|
//#endregion
|
|
233
|
-
export { ApplyOptions, AttributeState, ChannelEditResult, ChannelSegment, ClassNameOperation, type EmailElementSelection, type EmailPreview, type EmittedPosition, INSTANCE_PATH_ATTRIBUTE, JSX_SOURCE_LOCATION_PROP, JsxSource, JsxSourceLocation, LockedSegment, ProfileGuard, type SmsCategory, SmsChannelForm, SmsEdit, SourceEditResult, SourceElement, SourceFixture, SourceProperty, SourceProposal, SourceRange, SourceReplacement, TextSegment, WhatsAppButtonKind, WhatsAppButtonRow, type WhatsAppCategory, WhatsAppChannelForm, WhatsAppEdit, type WhatsAppHeaderType, WhatsAppPart, applySourceReplacements, classNameEdit, completeJsxSourceLocation, editSmsChannel, editWhatsAppChannel, emailAuthoringTarget, emailDefinitionInstances, emailSelectionAncestry, emailSelectionIndex, emailSelectionSiblings, emailSelections, expressionReplacement, inspectSourceElement, inspectSourceFixtures, literalReplacement, previewEmail, readSmsChannel, readWhatsAppChannel, structuralReplacements };
|
|
244
|
+
export { ApplyOptions, AttributeState, ChannelEditResult, ChannelSegment, ClassNameOperation, type EmailElementSelection, type EmailPreview, type EmittedPosition, INSTANCE_PATH_ATTRIBUTE, JSX_SOURCE_LOCATION_PROP, JsxSource, JsxSourceLocation, LockedSegment, ProfileGuard, type SmsCategory, SmsChannelForm, SmsEdit, SourceEditResult, SourceElement, SourceFixture, SourceProperty, SourceProposal, SourceRange, SourceReplacement, TextSegment, WhatsAppButtonKind, WhatsAppButtonRow, type WhatsAppCategory, WhatsAppChannelForm, WhatsAppEdit, type WhatsAppHeaderType, WhatsAppPart, applySourceReplacements, classNameEdit, completeJsxSourceLocation, editSmsChannel, editWhatsAppChannel, emailAuthoringTarget, emailDefinitionInstances, emailSelectionAncestry, emailSelectionIndex, emailSelectionSiblings, emailSelections, expressionReplacement, inspectMarkdownNode, inspectSourceElement, inspectSourceFixtures, literalReplacement, previewEmail, readSmsChannel, readWhatsAppChannel, structuralReplacements };
|
package/dist/edit.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { n as isObject } from "./guards-eV1qLbf2.js";
|
|
2
|
-
import {
|
|
2
|
+
import { C as emailSelectionAncestry, D as previewEmail, E as emailSelections, S as emailDefinitionInstances, T as emailSelectionSiblings, b as INSTANCE_PATH_ATTRIBUTE, w as emailSelectionIndex, x as emailAuthoringTarget } from "./components-DNBCz20_.js";
|
|
3
3
|
import { d as completeJsxSourceLocation, u as JSX_SOURCE_LOCATION_PROP } from "./jsx-runtime-qL19u3Fy.js";
|
|
4
|
-
import {
|
|
5
|
-
import { c as
|
|
4
|
+
import { t as buildLineMap } from "./diagnostic-model-DJtqbETd.js";
|
|
5
|
+
import { c as WHATSAPP_HEADER_TYPES, o as SMS_CATEGORIES, s as WHATSAPP_CATEGORIES } from "./project-B6D64SZS.js";
|
|
6
|
+
import { n as introducedErrors, t as checkTemplate } from "./profile-DFk4gs7o.js";
|
|
7
|
+
import { t as parseIssue } from "./parse-Bex7QFqj.js";
|
|
6
8
|
import { parse } from "@babel/parser";
|
|
7
9
|
//#region src/source-edit.ts
|
|
8
10
|
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";
|
|
@@ -118,12 +120,63 @@ const inspectSourceElement = (source, file, origin) => {
|
|
|
118
120
|
});
|
|
119
121
|
return found;
|
|
120
122
|
};
|
|
123
|
+
/**
|
|
124
|
+
* Resolve a Markdown render origin to authored prose or component attributes. A tag distinguishes
|
|
125
|
+
* nodes sharing an origin; without one, prefer an editable authored element over its wrapper.
|
|
126
|
+
*/
|
|
127
|
+
const inspectMarkdownNode = (source, file, origin, options = {}) => {
|
|
128
|
+
if (file !== origin.fileName) return null;
|
|
129
|
+
const issue = parseIssue(source, file);
|
|
130
|
+
if (issue.diagnostics.some((item) => item.severity === "error")) return null;
|
|
131
|
+
const candidates = issue.sourceNodes.filter((item) => item.src[0] === origin.lineNumber && item.src[1] === origin.columnNumber && item.kind !== "text");
|
|
132
|
+
const found = options.tag === void 0 ? candidates.find((item) => item.properties.length > 0) ?? candidates[0] : candidates.find((item) => item.tag === options.tag);
|
|
133
|
+
if (found === void 0) return null;
|
|
134
|
+
return {
|
|
135
|
+
start: found.start,
|
|
136
|
+
end: found.end,
|
|
137
|
+
contentStart: found.contentStart,
|
|
138
|
+
contentEnd: found.contentEnd,
|
|
139
|
+
tag: found.tag,
|
|
140
|
+
attributeInsertion: found.contentStart ?? found.end,
|
|
141
|
+
hasSpread: false,
|
|
142
|
+
structural: false,
|
|
143
|
+
previous: null,
|
|
144
|
+
next: null,
|
|
145
|
+
properties: found.properties.map((field) => ({
|
|
146
|
+
...field,
|
|
147
|
+
value: String(field.value),
|
|
148
|
+
kind: "literal",
|
|
149
|
+
syntax: field.syntax === "quoted" ? "markdown-attribute" : field.syntax === "boolean" ? "markdown-boolean" : "markdown"
|
|
150
|
+
}))
|
|
151
|
+
};
|
|
152
|
+
};
|
|
121
153
|
const PLAIN_JSX_TEXT = /^[^\s{}<>&](?:[^{}<>&\r\n\u2028\u2029]*[^\s{}<>&])?$/;
|
|
154
|
+
/** Keep prose readable while protecting inline syntax and the surrounding block's delimiters. */
|
|
155
|
+
const escapeMarkdownProse = (source, field, value) => {
|
|
156
|
+
let escaped = value.replace(/[\\`*_[\]<{]/g, "\\$&").replace(/&(?=(?:[a-z][a-z\d]{1,31}|#\d{1,7}|#x[\da-f]{1,6});)/gi, "\\&").replace(/[\r\n\t]|^ +| +$/g, (whitespace) => [...whitespace].map((character) => `&#${character.charCodeAt(0)};`).join(""));
|
|
157
|
+
const block = parseIssue(source, "source.md").sourceNodes.find((authored) => authored.kind === "block" && authored.properties.some((candidate) => candidate.name === "children" && candidate.start === field.start && candidate.end === field.end));
|
|
158
|
+
const prefix = block === void 0 ? void 0 : source.slice(block.start, field.start);
|
|
159
|
+
if (prefix !== void 0 && /^ {0,3}$/.test(prefix)) escaped = escaped.replace(/^(?:#{1,6}(?=[ \t]|$)|>|[-+](?=[ \t]|$)|(?:-+|=+)(?=[ \t]*$)|~{3,})/, "\\$&").replace(/^(\d{1,9})([.)])(?=[ \t]|$)/, "$1\\$2");
|
|
160
|
+
const lineEnd = source.slice(field.end).search(/[\r\n]/);
|
|
161
|
+
const suffix = source.slice(field.end, lineEnd < 0 ? source.length : field.end + lineEnd);
|
|
162
|
+
if (prefix !== void 0 && /^ {0,3}#{1,6}[ \t]+$/.test(prefix) && /^[ \t]*$/.test(suffix)) escaped = escaped.replace(/(^|[ \t])(#+)$/, "$1\\$2");
|
|
163
|
+
return escaped;
|
|
164
|
+
};
|
|
122
165
|
/** Literal controls cannot overwrite a binding or computed expression. */
|
|
123
166
|
const literalReplacement = (source, field, value) => {
|
|
124
167
|
if (field.kind !== "literal") return null;
|
|
125
168
|
if (field.literalType === "number" && (value.trim() === "" || !Number.isFinite(Number(value)))) return null;
|
|
126
169
|
if (field.literalType === "boolean" && value !== "true" && value !== "false") return null;
|
|
170
|
+
if (field.syntax === "markdown" || field.syntax === "markdown-attribute" || field.syntax === "markdown-boolean") {
|
|
171
|
+
const encoded = value.replaceAll("&", "&").replaceAll("\"", """).replaceAll("<", "<").replaceAll("{", "{").replaceAll("}", "}").replace(/[\r\n\t]/g, (character) => `&#${character.charCodeAt(0)};`);
|
|
172
|
+
const markdown = field.name === "children" ? escapeMarkdownProse(source, field, value) : value.replace(/[!-/:-@[-`{-~]/g, "\\$&").replace(field.name === "href" || field.name === "src" ? /\s/g : /[\r\n\t]|^ +| +$/g, (whitespace) => [...whitespace].map((character) => `&#${character.charCodeAt(0)};`).join(""));
|
|
173
|
+
return {
|
|
174
|
+
start: field.start,
|
|
175
|
+
end: field.end,
|
|
176
|
+
before: source.slice(field.start, field.end),
|
|
177
|
+
after: field.syntax === "markdown" ? markdown : field.syntax === "markdown-boolean" ? `${field.name}="${encoded}"` : `"${encoded}"`
|
|
178
|
+
};
|
|
179
|
+
}
|
|
127
180
|
const literal = field.literalType === "string" ? JSON.stringify(value) : field.literalType === "number" ? String(Number(value)) : value;
|
|
128
181
|
const before = source.slice(field.start, field.end);
|
|
129
182
|
const plainText = field.syntax === "child" && !before.startsWith("{") && PLAIN_JSX_TEXT.test(value);
|
|
@@ -166,6 +219,16 @@ const applySourceReplacements = (source, edits, options = {}) => {
|
|
|
166
219
|
result = result.slice(0, edit.start) + edit.after + result.slice(edit.end);
|
|
167
220
|
boundary = edit.start;
|
|
168
221
|
}
|
|
222
|
+
if (options.markdown !== void 0) {
|
|
223
|
+
const finding = parseIssue(result, options.markdown.file).diagnostics.find((item) => item.severity === "error");
|
|
224
|
+
return finding === void 0 ? {
|
|
225
|
+
ok: true,
|
|
226
|
+
source: result
|
|
227
|
+
} : {
|
|
228
|
+
ok: false,
|
|
229
|
+
reason: `This change is not valid SML Markdown: ${finding.message}`
|
|
230
|
+
};
|
|
231
|
+
}
|
|
169
232
|
if (parsed(result) === null) return {
|
|
170
233
|
ok: false,
|
|
171
234
|
reason: "This change is not valid TSX. Review it in Source."
|
|
@@ -940,4 +1003,4 @@ const editWhatsAppChannel = (source, edit) => {
|
|
|
940
1003
|
}
|
|
941
1004
|
};
|
|
942
1005
|
//#endregion
|
|
943
|
-
export { INSTANCE_PATH_ATTRIBUTE, JSX_SOURCE_LOCATION_PROP, applySourceReplacements, classNameEdit, completeJsxSourceLocation, editSmsChannel, editWhatsAppChannel, emailAuthoringTarget, emailDefinitionInstances, emailSelectionAncestry, emailSelectionIndex, emailSelectionSiblings, emailSelections, expressionReplacement, inspectSourceElement, inspectSourceFixtures, literalReplacement, previewEmail, readSmsChannel, readWhatsAppChannel, structuralReplacements };
|
|
1006
|
+
export { INSTANCE_PATH_ATTRIBUTE, JSX_SOURCE_LOCATION_PROP, applySourceReplacements, classNameEdit, completeJsxSourceLocation, editSmsChannel, editWhatsAppChannel, emailAuthoringTarget, emailDefinitionInstances, emailSelectionAncestry, emailSelectionIndex, emailSelectionSiblings, emailSelections, expressionReplacement, inspectMarkdownNode, inspectSourceElement, inspectSourceFixtures, literalReplacement, previewEmail, readSmsChannel, readWhatsAppChannel, structuralReplacements };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as Fragment, i as EmailNode, n as EmailComponent, o as JSX } from "../jsx-runtime-
|
|
1
|
+
import { c as JsxSource } from "../diagnostics-C3TSx6SU.js";
|
|
2
|
+
import { a as Fragment, i as EmailNode, n as EmailComponent, o as JSX } from "../jsx-runtime-HHfrVZEf.js";
|
|
3
3
|
//#region src/email/jsx-dev-runtime.d.ts
|
|
4
4
|
/** Development JSX retains compiler-provided origins directly on the semantic tree. */
|
|
5
5
|
declare const jsxDEV: (type: string | EmailComponent<Record<string, unknown>> | typeof Fragment, props: Record<string, unknown> | null, _key?: unknown, _staticChildren?: boolean, source?: JsxSource) => EmailNode;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as Fragment, c as currentEmailOrigins, d as normalizeChildren, f as rawMarkup, i as EmailNode, l as jsx, n as EmailComponent, o as JSX, p as withEmailDiagnostics, r as EmailFinding, s as collectEmailDiagnostics, t as EmailChild, u as jsxs } from "../jsx-runtime-
|
|
1
|
+
import { a as Fragment, c as currentEmailOrigins, d as normalizeChildren, f as rawMarkup, i as EmailNode, l as jsx, n as EmailComponent, o as JSX, p as withEmailDiagnostics, r as EmailFinding, s as collectEmailDiagnostics, t as EmailChild, u as jsxs } from "../jsx-runtime-HHfrVZEf.js";
|
|
2
2
|
export { EmailChild, EmailComponent, EmailFinding, EmailNode, Fragment, JSX, collectEmailDiagnostics, currentEmailOrigins, jsx, jsxs, normalizeChildren, rawMarkup, withEmailDiagnostics };
|
package/dist/email.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as EmailNode, t as EmailChild, x as EmailStyle } from "./jsx-runtime-
|
|
1
|
+
import { i as EmailNode, t as EmailChild, x as EmailStyle } from "./jsx-runtime-HHfrVZEf.js";
|
|
2
2
|
//#region src/email/components.d.ts
|
|
3
3
|
/** Marks the hidden preview block so the renderer can read it back and the text lane can keep it. */
|
|
4
4
|
declare const PREHEADER_ATTRIBUTE = "data-samva-preheader";
|
|
@@ -175,4 +175,8 @@ interface LinkProps {
|
|
|
175
175
|
*/
|
|
176
176
|
declare const Link: ({ className, href, target, rel, style, children }: LinkProps) => EmailNode;
|
|
177
177
|
//#endregion
|
|
178
|
-
|
|
178
|
+
//#region src/issue-content.d.ts
|
|
179
|
+
/** Static layout directive replaced by parsed issue content during compilation. Takes no props. */
|
|
180
|
+
declare const IssueContent: () => EmailNode | null;
|
|
181
|
+
//#endregion
|
|
182
|
+
export { Button, ButtonProps, Column, ColumnProps, Columns, ColumnsProps, Divider, DividerProps, Email, EmailProps, Image, ImageProps, IssueContent, Link, LinkProps, PREHEADER_ATTRIBUTE, Section, SectionProps, Spacer, SpacerProps, equalColumnWidth, preheaderNode };
|
package/dist/email.js
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
import { a as Email, c as PREHEADER_ATTRIBUTE, d as equalColumnWidth, f as preheaderNode, 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-
|
|
2
|
-
|
|
1
|
+
import { a as Email, c as PREHEADER_ATTRIBUTE, d as equalColumnWidth, f as preheaderNode, 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-DNBCz20_.js";
|
|
2
|
+
//#region src/issue-content.ts
|
|
3
|
+
/** Static layout directive replaced by parsed issue content during compilation. Takes no props. */
|
|
4
|
+
const IssueContent = () => null;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { Button, Column, Columns, Divider, Email, Image, IssueContent, Link, PREHEADER_ATTRIBUTE, Section, Spacer, equalColumnWidth, preheaderNode };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as TemplateInput, n as SmlTemplate, o as defineTemplate, r as TEMPLATE_ID_PATTERN, t as EmailChannel } from "./template-
|
|
1
|
+
import { a as TemplateInput, n as SmlTemplate, o as defineTemplate, r as TEMPLATE_ID_PATTERN, t as EmailChannel } from "./template-4Dse7_OC.js";
|
|
2
2
|
export { type EmailChannel, type SmlTemplate, TEMPLATE_ID_PATTERN, type TemplateInput, defineTemplate };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as EmailDiagnostic } from "./diagnostics-D4vSLQrO.js";
|
|
1
|
+
import { l as JsxSourceLocation, n as EmailDiagnostic } from "./diagnostics-C3TSx6SU.js";
|
|
3
2
|
//#region src/email/elements.d.ts
|
|
4
3
|
/** How one attribute is spelled and what values it accepts. */
|
|
5
4
|
interface EmailAttributeSpec {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { n as EmailDiagnostic } from "./diagnostics-C3TSx6SU.js";
|
|
2
|
+
import { g as CompiledSource, u as CompiledNode } from "./compiled-template-CPDHvFlt.js";
|
|
3
|
+
import { r as CompileResult, t as CompileTemplateOptions } from "./compile-CIeuGmP-.js";
|
|
4
|
+
//#region src/markdown/types.d.ts
|
|
5
|
+
interface IssueFrontmatter {
|
|
6
|
+
readonly title: string;
|
|
7
|
+
readonly slug: string;
|
|
8
|
+
readonly topic: string;
|
|
9
|
+
readonly preheader?: string;
|
|
10
|
+
readonly seo?: {
|
|
11
|
+
readonly title?: string;
|
|
12
|
+
readonly description?: string;
|
|
13
|
+
readonly image?: string;
|
|
14
|
+
};
|
|
15
|
+
/** Number of leading top-level blocks visible before the subscription gate. */
|
|
16
|
+
readonly gate?: {
|
|
17
|
+
readonly leadIn: number;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Value offsets are UTF-16, end-exclusive; quoted spans include both literal quotes. */
|
|
21
|
+
interface MarkdownSourceProperty {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly value: string | number | boolean;
|
|
24
|
+
readonly literalType: "string" | "number" | "boolean";
|
|
25
|
+
readonly start: number;
|
|
26
|
+
readonly end: number;
|
|
27
|
+
readonly syntax: "quoted" | "boolean" | "markdown";
|
|
28
|
+
}
|
|
29
|
+
/** Authored ranges, before decoding entities or removing Markdown delimiters. */
|
|
30
|
+
interface MarkdownSourceNode {
|
|
31
|
+
readonly kind: "component" | "block" | "inline" | "text";
|
|
32
|
+
readonly start: number;
|
|
33
|
+
readonly end: number;
|
|
34
|
+
readonly contentStart: number;
|
|
35
|
+
readonly contentEnd: number;
|
|
36
|
+
readonly tag: string;
|
|
37
|
+
readonly src: CompiledSource;
|
|
38
|
+
readonly properties: readonly MarkdownSourceProperty[];
|
|
39
|
+
}
|
|
40
|
+
interface ParseIssueResult {
|
|
41
|
+
/** Empty whenever any diagnostic is an error. Brand components await host resolution. */
|
|
42
|
+
readonly nodes: readonly CompiledNode[];
|
|
43
|
+
readonly frontmatter: IssueFrontmatter | undefined;
|
|
44
|
+
readonly diagnostics: readonly EmailDiagnostic[];
|
|
45
|
+
readonly sources: readonly string[];
|
|
46
|
+
readonly sourceNodes: readonly MarkdownSourceNode[];
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/markdown/parse.d.ts
|
|
50
|
+
/** Parse a static issue without importing, evaluating or passing through authored HTML. */
|
|
51
|
+
declare const parseIssue: (source: string, file: string) => ParseIssueResult;
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/issue.d.ts
|
|
54
|
+
/** An issue's Markdown path and the ordinary TSX template used as its layout. */
|
|
55
|
+
interface CompileIssueOptions extends Omit<CompileTemplateOptions, "entry" | "issueContent"> {
|
|
56
|
+
readonly entry: string;
|
|
57
|
+
readonly layout?: string | undefined;
|
|
58
|
+
}
|
|
59
|
+
interface IssueCompileResult extends CompileResult {
|
|
60
|
+
readonly frontmatter: IssueFrontmatter | undefined;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Compile an issue inside layout.tsx (or the selected layout), without evaluating either file.
|
|
64
|
+
* The slug identifies the issue, title is its email subject, and preheader is its inbox preview.
|
|
65
|
+
* The layout supplies the input schema, fixtures, brand, theme and one IssueContent slot.
|
|
66
|
+
*/
|
|
67
|
+
declare const compileIssue: (options: CompileIssueOptions) => Promise<IssueCompileResult>;
|
|
68
|
+
//#endregion
|
|
69
|
+
export { CompileIssueOptions, IssueCompileResult, type IssueFrontmatter, type MarkdownSourceNode, type MarkdownSourceProperty, type ParseIssueResult, compileIssue, parseIssue };
|
package/dist/markdown.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { t as compileTemplate } from "./compile-C1GWLQ5C.js";
|
|
2
|
+
import { t as parseIssue } from "./parse-Bex7QFqj.js";
|
|
3
|
+
//#region src/issue.ts
|
|
4
|
+
/**
|
|
5
|
+
* Compile an issue inside layout.tsx (or the selected layout), without evaluating either file.
|
|
6
|
+
* The slug identifies the issue, title is its email subject, and preheader is its inbox preview.
|
|
7
|
+
* The layout supplies the input schema, fixtures, brand, theme and one IssueContent slot.
|
|
8
|
+
*/
|
|
9
|
+
const compileIssue = async (options) => {
|
|
10
|
+
const source = options.files[options.entry];
|
|
11
|
+
if (typeof source !== "string") return {
|
|
12
|
+
template: void 0,
|
|
13
|
+
frontmatter: void 0,
|
|
14
|
+
fixtures: {},
|
|
15
|
+
assets: [],
|
|
16
|
+
diagnostics: [{
|
|
17
|
+
code: "markdown-syntax",
|
|
18
|
+
severity: "error",
|
|
19
|
+
message: `Issue ${options.entry} is missing or is not text.`,
|
|
20
|
+
origins: [{
|
|
21
|
+
fileName: options.entry,
|
|
22
|
+
lineNumber: 1,
|
|
23
|
+
columnNumber: 1
|
|
24
|
+
}]
|
|
25
|
+
}]
|
|
26
|
+
};
|
|
27
|
+
const issue = parseIssue(source, options.entry);
|
|
28
|
+
if (issue.frontmatter === void 0 || issue.diagnostics.some((item) => item.severity === "error")) return {
|
|
29
|
+
template: void 0,
|
|
30
|
+
frontmatter: issue.frontmatter,
|
|
31
|
+
fixtures: {},
|
|
32
|
+
assets: [],
|
|
33
|
+
diagnostics: issue.diagnostics
|
|
34
|
+
};
|
|
35
|
+
const layout = options.layout ?? "layout.tsx";
|
|
36
|
+
if (typeof options.files[layout] !== "string") return {
|
|
37
|
+
template: void 0,
|
|
38
|
+
frontmatter: issue.frontmatter,
|
|
39
|
+
fixtures: {},
|
|
40
|
+
assets: [],
|
|
41
|
+
diagnostics: [{
|
|
42
|
+
code: "issue-layout",
|
|
43
|
+
severity: "error",
|
|
44
|
+
message: `Issue layout ${layout} is missing or is not text.`,
|
|
45
|
+
origins: [{
|
|
46
|
+
fileName: layout,
|
|
47
|
+
lineNumber: 1,
|
|
48
|
+
columnNumber: 1
|
|
49
|
+
}]
|
|
50
|
+
}]
|
|
51
|
+
};
|
|
52
|
+
const compiled = await compileTemplate({
|
|
53
|
+
...options,
|
|
54
|
+
entry: layout,
|
|
55
|
+
issueContent: {
|
|
56
|
+
file: options.entry,
|
|
57
|
+
nodes: issue.nodes
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
const diagnostics = [...issue.diagnostics, ...compiled.diagnostics];
|
|
61
|
+
const template = compiled.template;
|
|
62
|
+
if (template?.email === void 0 || template.sms !== void 0 || template.whatsapp !== void 0) return {
|
|
63
|
+
...compiled,
|
|
64
|
+
template: void 0,
|
|
65
|
+
frontmatter: issue.frontmatter,
|
|
66
|
+
diagnostics: template === void 0 ? diagnostics : [...diagnostics, {
|
|
67
|
+
code: "issue-layout",
|
|
68
|
+
severity: "error",
|
|
69
|
+
message: "An issue layout must declare only an email channel.",
|
|
70
|
+
origins: [{
|
|
71
|
+
fileName: layout,
|
|
72
|
+
lineNumber: 1,
|
|
73
|
+
columnNumber: 1
|
|
74
|
+
}]
|
|
75
|
+
}]
|
|
76
|
+
};
|
|
77
|
+
const { preheader: _preheader, ...email } = template.email;
|
|
78
|
+
const result = {
|
|
79
|
+
...template,
|
|
80
|
+
template: issue.frontmatter.slug,
|
|
81
|
+
email: {
|
|
82
|
+
...email,
|
|
83
|
+
subject: issue.frontmatter.title,
|
|
84
|
+
...issue.frontmatter.preheader === void 0 ? {} : { preheader: issue.frontmatter.preheader }
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
...compiled,
|
|
89
|
+
template: result,
|
|
90
|
+
frontmatter: issue.frontmatter,
|
|
91
|
+
diagnostics
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
//#endregion
|
|
95
|
+
export { compileIssue, parseIssue };
|