@travetto/email-inky 8.0.0-alpha.21 → 8.0.0-alpha.23

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/README.md CHANGED
@@ -19,21 +19,25 @@ This module provides [inky](https://github.com/zurb/inky) support via [JSX](http
19
19
  ```typescript
20
20
  /** @jsxImportSource @travetto/email-inky/support */
21
21
 
22
- import { Title, Container, Summary, Row, Column, If, Button, Value, InkyTemplate } from '@travetto/email-inky';
23
-
24
- export default <InkyTemplate>
25
- <Title>Test Email</Title>
26
- <Summary>Email Summary</Summary>
27
- <Container>
28
- <If attr='person'>
29
- <Row>
30
- <Column small={5}>
31
- <Button href='https://google.com/{{query}}'>Hello <Value attr='name' /></Button>
32
- </Column>
33
- </Row>
34
- </If>
35
- </Container>
36
- </InkyTemplate>;
22
+ import { Button, Column, Container, If, InkyTemplate, Row, Summary, Title, Value } from '@travetto/email-inky';
23
+
24
+ export default (
25
+ <InkyTemplate>
26
+ <Title>Test Email</Title>
27
+ <Summary>Email Summary</Summary>
28
+ <Container>
29
+ <If attr="person">
30
+ <Row>
31
+ <Column small={5}>
32
+ <Button href="https://google.com/{{query}}">
33
+ Hello <Value attr="name" />
34
+ </Button>
35
+ </Column>
36
+ </Row>
37
+ </If>
38
+ </Container>
39
+ </InkyTemplate>
40
+ );
37
41
  ```
38
42
 
39
43
  Please see the [inky](https://github.com/zurb/inky) documentation for more information on the [component specifications](#https-get-foundation-emails-docs-inky-html)
@@ -43,7 +47,7 @@ The underlying [Email](https://github.com/travetto/travetto/tree/main/module/ema
43
47
 
44
48
  This means this module, while showing [inky](https://github.com/zurb/inky) components, will ultimately produce HTML/markdown that is [mustache](https://github.com/janl/mustache.js/) compatible. The syntax used by [mustache](https://github.com/janl/mustache.js/) and the syntax used by [JSX](https://en.wikipedia.org/wiki/JSX_(JavaScript)) are in conflict due to both of the tools relying on the uniqueness of `{}` brackets.
45
49
 
46
- To that end, the module introduces additional components ([If Component](https://github.com/travetto/travetto/tree/main/module/email-inky/src/components.ts#L28), [Unless Component](https://github.com/travetto/travetto/tree/main/module/email-inky/src/components.ts#L30), and [For Component](https://github.com/travetto/travetto/tree/main/module/email-inky/src/components.ts#L31)) to assist with control flow logic. When it comes to variable substitution, and a desire to intermingle seamlessly with component properties, `{{value}}` can be used within a string value. To leverage substitutions outside of string contexts, a more formal version can be found in the [Value Component](https://github.com/travetto/travetto/tree/main/module/email-inky/src/components.ts#L29) component, but this cannot be integrated into properties (e.g. an href).
50
+ To that end, the module introduces additional components ([If Component](https://github.com/travetto/travetto/tree/main/module/email-inky/src/components.ts#L32), [Unless Component](https://github.com/travetto/travetto/tree/main/module/email-inky/src/components.ts#L34), and [For Component](https://github.com/travetto/travetto/tree/main/module/email-inky/src/components.ts#L35)) to assist with control flow logic. When it comes to variable substitution, and a desire to intermingle seamlessly with component properties, `{{value}}` can be used within a string value. To leverage substitutions outside of string contexts, a more formal version can be found in the [Value Component](https://github.com/travetto/travetto/tree/main/module/email-inky/src/components.ts#L33) component, but this cannot be integrated into properties (e.g. an href).
47
51
 
48
52
  ## Template Extension Points
49
53
  The template extension points are defined at:
package/__index__.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export * from './src/components.ts';
2
- export * from './src/wrapper.ts';
3
- export * from './src/render/renderer.ts';
2
+ export * from './src/render/common.ts';
3
+ export * from './src/render/context.ts';
4
4
  export * from './src/render/html.ts';
5
5
  export * from './src/render/markdown.ts';
6
+ export * from './src/render/renderer.ts';
6
7
  export * from './src/render/subject.ts';
7
- export * from './src/render/context.ts';
8
- export * from './src/render/common.ts';
8
+ export * from './src/wrapper.ts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/email-inky",
3
- "version": "8.0.0-alpha.21",
3
+ "version": "8.0.0-alpha.23",
4
4
  "type": "module",
5
5
  "description": "Email Inky templating module",
6
6
  "keywords": [
@@ -28,14 +28,14 @@
28
28
  "directory": "module/email-inky"
29
29
  },
30
30
  "dependencies": {
31
- "@travetto/config": "^8.0.0-alpha.20",
32
- "@travetto/di": "^8.0.0-alpha.19",
33
- "@travetto/email": "^8.0.0-alpha.20",
34
- "@travetto/runtime": "^8.0.0-alpha.19",
31
+ "@travetto/config": "^8.0.0-alpha.22",
32
+ "@travetto/di": "^8.0.0-alpha.20",
33
+ "@travetto/email": "^8.0.0-alpha.22",
34
+ "@travetto/runtime": "^8.0.0-alpha.20",
35
35
  "foundation-emails": "^2.5.1"
36
36
  },
37
37
  "devDependencies": {
38
- "@travetto/email-compiler": "^8.0.0-alpha.21"
38
+ "@travetto/email-compiler": "^8.0.0-alpha.23"
39
39
  },
40
40
  "peerDependenciesMeta": {
41
41
  "@travetto/cli": {
package/src/components.ts CHANGED
@@ -1,41 +1,60 @@
1
1
  import { TypedObject } from '@travetto/runtime';
2
2
 
3
- import type { JSXElement, JSXComponentFunction as CompFn } from '../support/jsx-runtime.ts';
3
+ import type { JSXComponentFunction as CompFn, JSXElement } from '../support/jsx-runtime.ts';
4
4
 
5
5
  const EMPTY: JSXElement = { type: '', key: '', props: {} };
6
6
 
7
7
  export const Column: CompFn<{
8
8
  valign?: string;
9
9
  align?: string;
10
- small?: number; smallOffset?: number; hideSmall?: boolean;
11
- large?: number; largeOffset?: number; hideLarge?: boolean;
10
+ small?: number;
11
+ smallOffset?: number;
12
+ hideSmall?: boolean;
13
+ large?: number;
14
+ largeOffset?: number;
15
+ hideLarge?: boolean;
12
16
  noExpander?: boolean;
13
17
  }> = () => EMPTY;
14
18
  export const Title: CompFn<{}> = () => EMPTY;
15
19
  export const Summary: CompFn<{}> = () => EMPTY;
16
20
  export const HLine: CompFn<{}> = () => EMPTY;
17
21
  export const Row: CompFn<{}> = () => EMPTY;
18
- export const Button: CompFn<{ href: string, target?: string, expanded?: boolean }> = () => EMPTY;
22
+ export const Button: CompFn<{ href: string; target?: string; expanded?: boolean }> = () => EMPTY;
19
23
  export const Container: CompFn<{}> = () => EMPTY;
20
24
  export const BlockGrid: CompFn<{ up?: number }> = () => EMPTY;
21
25
  export const Menu: CompFn<{}> = () => EMPTY;
22
- export const Item: CompFn<{ href: string, target?: string }> = () => EMPTY;
26
+ export const Item: CompFn<{ href: string; target?: string }> = () => EMPTY;
23
27
  export const Center: CompFn<{}> = () => EMPTY;
24
28
  export const Callout: CompFn<{}> = () => EMPTY;
25
- export const Spacer: CompFn<{ small?: number, large?: number, size?: number }> = () => EMPTY;
29
+ export const Spacer: CompFn<{ small?: number; large?: number; size?: number }> = () => EMPTY;
26
30
  export const Wrapper: CompFn<{}> = () => EMPTY;
27
31
 
28
32
  export const If: CompFn<{ attr: string }> = () => EMPTY;
29
- export const Value: CompFn<{ attr: string, raw?: boolean }> = () => EMPTY;
33
+ export const Value: CompFn<{ attr: string; raw?: boolean }> = () => EMPTY;
30
34
  export const Unless: CompFn<{ attr: string }> = () => EMPTY;
31
35
  export const For: CompFn<{ attr: string }> = () => EMPTY;
32
36
  export const InkyTemplate: CompFn<{}> = () => EMPTY;
33
37
 
34
38
  export const c = {
35
- Wrapper, Container, InkyTemplate,
36
- Column, Title, Summary, HLine, Row, Button,
37
- BlockGrid, Menu, Item, Center, Callout, Spacer,
38
- If, Unless, For, Value
39
+ Wrapper,
40
+ Container,
41
+ InkyTemplate,
42
+ Column,
43
+ Title,
44
+ Summary,
45
+ HLine,
46
+ Row,
47
+ Button,
48
+ BlockGrid,
49
+ Menu,
50
+ Item,
51
+ Center,
52
+ Callout,
53
+ Spacer,
54
+ If,
55
+ Unless,
56
+ For,
57
+ Value
39
58
  } as const;
40
59
 
41
60
  for (const key of TypedObject.keys(c)) {
@@ -46,7 +65,7 @@ type C = typeof c;
46
65
 
47
66
  // @ts-expect-error
48
67
  export type JSXElementByFn<K extends keyof C> = JSXElement<C[K], Parameters<C[K]>[0]>;
49
- export type JSXElements = { [K in keyof C]: JSXElementByFn<K>; };
68
+ export type JSXElements = { [K in keyof C]: JSXElementByFn<K> };
50
69
 
51
70
  export const EMPTY_ELEMENT = EMPTY;
52
71
 
@@ -1,4 +1,4 @@
1
- import { type JSXElement, isJSXElement } from '../../support/jsx-runtime.ts';
1
+ import { isJSXElement, type JSXElement } from '../../support/jsx-runtime.ts';
2
2
 
3
3
  export const getChildren = (node: JSXElement): JSXElement[] => {
4
4
  const kids = node?.props?.children;
@@ -42,12 +42,17 @@ export const classString = (existing: string | undefined, ...toAdd: string[]): s
42
42
  return out.join(' ');
43
43
  };
44
44
 
45
- export const combinePropsToString = (allowedProps: Set<string>, props: { className?: string } & Record<string, unknown>, addClasses: string[] = []): string => {
45
+ export const combinePropsToString = (
46
+ allowedProps: Set<string>,
47
+ props: { className?: string } & Record<string, unknown>,
48
+ addClasses: string[] = []
49
+ ): string => {
46
50
  const out = { ...props, className: classString(props.className, ...addClasses) };
47
51
  return Object.entries(out)
48
52
  .filter(([key, value]) => allowedProps.has(key) && value !== undefined && value !== null && value !== '')
49
53
  .map(([key, value]) => [key === 'className' ? 'class' : key, value])
50
- .map(([key, value]) => `${key}="${value}"`).join(' ');
54
+ .map(([key, value]) => `${key}="${value}"`)
55
+ .join(' ');
51
56
  };
52
57
 
53
- export const isOfType = (node: JSXElement, type: string): boolean => typeof node.type === 'function' && node.type.name === type;
58
+ export const isOfType = (node: JSXElement, type: string): boolean => typeof node.type === 'function' && node.type.name === type;
@@ -1,16 +1,15 @@
1
+ import { EmailResourceLoader, type EmailTemplateLocation } from '@travetto/email';
1
2
  import { castTo, type FileLoader } from '@travetto/runtime';
2
- import { type EmailTemplateLocation, EmailResourceLoader } from '@travetto/email';
3
3
 
4
- import { type JSXElementByFn, c } from '../components.ts';
5
4
  import { createElement } from '../../support/jsx-runtime.ts';
5
+ import { c, type JSXElementByFn } from '../components.ts';
6
6
 
7
- export type RenderContextInit = EmailTemplateLocation & { loader?: FileLoader, columnCount?: number };
7
+ export type RenderContextInit = EmailTemplateLocation & { loader?: FileLoader; columnCount?: number };
8
8
 
9
9
  /**
10
10
  * Render Context
11
11
  */
12
12
  export class RenderContext implements RenderContextInit {
13
-
14
13
  columnCount: number = 12;
15
14
  file: string;
16
15
  module: string;
@@ -27,4 +26,4 @@ export class RenderContext implements RenderContextInit {
27
26
  createElement<K extends keyof typeof c>(name: K, props: JSXElementByFn<K>['props']): JSXElementByFn<K> {
28
27
  return castTo(createElement(castTo<Record<string, string>>(c)[name], props));
29
28
  }
30
- }
29
+ }
@@ -1,7 +1,7 @@
1
1
  import type { JSXElement } from '../../support/jsx-runtime.ts';
2
2
  import type { RenderProvider, RenderState } from '../types.ts';
3
- import type { RenderContext } from './context.ts';
4
3
  import { classString, combinePropsToString, getChildren, isOfType, visit } from './common.ts';
4
+ import type { RenderContext } from './context.ts';
5
5
 
6
6
  export const SUMMARY_STYLE = Object.entries({
7
7
  display: 'none',
@@ -12,12 +12,25 @@ export const SUMMARY_STYLE = Object.entries({
12
12
  'max-width': '0px',
13
13
  opacity: '0',
14
14
  overflow: 'hidden'
15
- }).map(([key, value]) => `${key}: ${value}`).join('; ');
15
+ })
16
+ .map(([key, value]) => `${key}: ${value}`)
17
+ .join('; ');
16
18
 
17
19
  const allowedProps = new Set([
18
- 'className', 'id', 'dir', 'name', 'src',
19
- 'alt', 'href', 'title', 'height', 'target',
20
- 'width', 'style', 'align', 'valign'
20
+ 'className',
21
+ 'id',
22
+ 'dir',
23
+ 'name',
24
+ 'src',
25
+ 'alt',
26
+ 'href',
27
+ 'title',
28
+ 'height',
29
+ 'target',
30
+ 'width',
31
+ 'style',
32
+ 'align',
33
+ 'valign'
21
34
  ]);
22
35
 
23
36
  const propsToString = combinePropsToString.bind(null, allowedProps);
@@ -44,15 +57,21 @@ export const Html: RenderProvider<RenderContext> = {
44
57
  // Force summary to top, and title to head
45
58
  const final = wrapper
46
59
  .replace('<!-- BODY -->', html)
47
- .replace(/<title>.*?<\/title>/, a => { headerTop.push(a); return ''; })
48
- .replace(/<span[^>]+id="summary"[^>]*>(.*?)<\/span>/sm, a => { bodyTop.push(a); return ''; })
60
+ .replace(/<title>.*?<\/title>/, a => {
61
+ headerTop.push(a);
62
+ return '';
63
+ })
64
+ .replace(/<span[^>]+id="summary"[^>]*>(.*?)<\/span>/ms, a => {
65
+ bodyTop.push(a);
66
+ return '';
67
+ })
49
68
  .replace(/<head( [^>]*)?>/, tag => `${tag}\n${headerTop.join('\n')}`)
50
69
  .replace(/<body[^>]*>/, tag => `${tag}\n${bodyTop.join('\n')}`);
51
70
 
52
71
  // Allow tag suffixes/prefixes via comments
53
72
  html = final
54
73
  .replace(/\s*<!--\s*[$]:([^ -]+)\s*-->\s*(<\/[^>]+>)/g, (_, suffix, tag) => `${tag}${suffix}`)
55
- .replace(/(<[^\/][^>]+>)\s*<!--\s*[#]:([^ ]+)\s*-->\s*/g, (_, tag, prefix) => `${prefix}${tag}`);
74
+ .replace(/(<[^/][^>]+>)\s*<!--\s*[#]:([^ ]+)\s*-->\s*/g, (_, tag, prefix) => `${prefix}${tag}`);
56
75
  }
57
76
 
58
77
  return html;
@@ -61,16 +80,32 @@ export const Html: RenderProvider<RenderContext> = {
61
80
  For: async ({ recurse, props }) => `{{#${props.attr}}}${await recurse()}{{/${props.attr}}}`,
62
81
  If: async ({ recurse, props }) => `{{#${props.attr}}}${await recurse()}{{/${props.attr}}}`,
63
82
  Unless: async ({ recurse, props }) => `{{^${props.attr}}}${await recurse()}{{/${props.attr}}}`,
64
- Value: async ({ props }) => props.raw ? `{{{${props.attr}}}}` : `{{${props.attr}}}`,
83
+ Value: async ({ props }) => (props.raw ? `{{{${props.attr}}}}` : `{{${props.attr}}}`),
65
84
 
66
85
  br: async () => '<br>\n',
67
- hr: async (node) => `<table ${propsToString(node.props)}><th></th></table>`,
68
- strong: standardInline, em: standardInline, p: standardFull,
69
- h1: standardFull, h2: standardFull, h3: standardFull, h4: standardFull,
70
- li: standard, ol: standardFull, ul: standardFull,
71
- table: standardFull, thead: standard, tr: standard, td: standard, th: standard, tbody: standard, center: standard, img: standardInline,
86
+ hr: async node => `<table ${propsToString(node.props)}><th></th></table>`,
87
+ strong: standardInline,
88
+ em: standardInline,
89
+ p: standardFull,
90
+ h1: standardFull,
91
+ h2: standardFull,
92
+ h3: standardFull,
93
+ h4: standardFull,
94
+ li: standard,
95
+ ol: standardFull,
96
+ ul: standardFull,
97
+ table: standardFull,
98
+ thead: standard,
99
+ tr: standard,
100
+ td: standard,
101
+ th: standard,
102
+ tbody: standard,
103
+ center: standard,
104
+ img: standardInline,
72
105
  title: standard,
73
- div: standard, span: standardInline, small: standardInline,
106
+ div: standard,
107
+ span: standardInline,
108
+ small: standardInline,
74
109
  a: async ({ recurse, props }) => `<a ${propsToString(props)}>${await recurse()}</a>`,
75
110
 
76
111
  InkyTemplate: ctx => ctx.recurse(),
@@ -78,7 +113,6 @@ export const Html: RenderProvider<RenderContext> = {
78
113
  Summary: async ({ recurse }) => `<span id="summary" style="${SUMMARY_STYLE}">${await recurse()}</span>`,
79
114
 
80
115
  Column: async ({ props, recurse, stack, node, context }): Promise<string> => {
81
-
82
116
  recurse();
83
117
 
84
118
  let expander = '';
@@ -88,7 +122,7 @@ export const Html: RenderProvider<RenderContext> = {
88
122
  const colCount = siblings.length || 1;
89
123
 
90
124
  if (parent) {
91
- const parentNode: (typeof parent) & { columnVisited?: boolean } = parent;
125
+ const parentNode: typeof parent & { columnVisited?: boolean } = parent;
92
126
  if (!parentNode.columnVisited) {
93
127
  parentNode.columnVisited = true;
94
128
  if (siblings.length) {
@@ -107,9 +141,9 @@ export const Html: RenderProvider<RenderContext> = {
107
141
  // If the column contains a nested row, the .expander class should not be used
108
142
  if (largeSize === context.columnCount && !props.noExpander) {
109
143
  let hasRow = false;
110
- visit(node, (child) => {
144
+ visit(node, child => {
111
145
  if (isOfType(child, 'Row')) {
112
- return hasRow = true;
146
+ return (hasRow = true);
113
147
  }
114
148
  });
115
149
  if (!hasRow) {
@@ -217,11 +251,11 @@ export const Html: RenderProvider<RenderContext> = {
217
251
 
218
252
  Menu: async ({ recurse, node, props }): Promise<string> => {
219
253
  let hasItem = false;
220
- visit(node, (child) => {
254
+ visit(node, child => {
221
255
  if (isOfType(child, 'Item')) {
222
- return hasItem = true;
256
+ return (hasItem = true);
223
257
  } else if ((child.type === 'td' || child.type === 'th') && child.props.className?.includes('menu-item')) {
224
- return hasItem = true;
258
+ return (hasItem = true);
225
259
  }
226
260
  });
227
261
 
@@ -280,7 +314,6 @@ export const Html: RenderProvider<RenderContext> = {
280
314
  },
281
315
 
282
316
  Callout: async ({ recurse, props }): Promise<string> => {
283
-
284
317
  const innerProps: JSXElement['props'] = { className: props.className };
285
318
  delete props.className;
286
319
 
@@ -337,4 +370,4 @@ export const Html: RenderProvider<RenderContext> = {
337
370
  </tr>
338
371
  </tbody>
339
372
  </table>`
340
- };
373
+ };
@@ -1,16 +1,16 @@
1
1
  import { castTo } from '@travetto/runtime';
2
2
 
3
+ import type { JSXElement } from '../../support/jsx-runtime.ts';
3
4
  import type { RenderProvider, RenderState } from '../types.ts';
4
5
  import type { RenderContext } from './context.ts';
5
- import type { JSXElement } from '../../support/jsx-runtime.ts';
6
6
 
7
7
  const visit = ({ recurse }: RenderState<JSXElement, RenderContext>): Promise<string> => recurse();
8
8
  const ignore = async (_: RenderState<JSXElement, RenderContext>): Promise<string> => '';
9
9
 
10
10
  export const Markdown: RenderProvider<RenderContext> = {
11
- finalize: (text) => {
11
+ finalize: text => {
12
12
  text = text
13
- .replace(/(\[[^\]]{1,100}\]\([^)]{1,1000}\))([A-Za-z0-9$]{1,100})/g, (all, link, value) => value === 's' ? all : `${link} ${value}`)
13
+ .replace(/(\[[^\]]{1,100}\]\([^)]{1,1000}\))([A-Za-z0-9$]{1,100})/g, (all, link, value) => (value === 's' ? all : `${link} ${value}`))
14
14
  .replace(/(\S)\n(#)/g, (_, left, right) => `${left}\n\n${right}`);
15
15
  return text;
16
16
  },
@@ -18,7 +18,7 @@ export const Markdown: RenderProvider<RenderContext> = {
18
18
  For: async ({ recurse, props }) => `{{#${props.attr}}}${await recurse()}{{/${props.attr}}}`,
19
19
  If: async ({ recurse, props }) => `{{#${props.attr}}}${await recurse()}{{/${props.attr}}}`,
20
20
  Unless: async ({ recurse, props }) => `{{^${props.attr}}}${await recurse()}{{/${props.attr}}}`,
21
- Value: async ({ props }) => props.raw ? `{{{${props.attr}}}}` : `{{${props.attr}}}`,
21
+ Value: async ({ props }) => (props.raw ? `{{{${props.attr}}}}` : `{{${props.attr}}}`),
22
22
 
23
23
  strong: async ({ recurse }) => `**${await recurse()}**`,
24
24
  hr: async () => '\n------------------\n',
@@ -31,7 +31,7 @@ export const Markdown: RenderProvider<RenderContext> = {
31
31
  li: async ({ recurse, stack }) => {
32
32
  const parent = stack.toReversed().find(node => node.type === 'ol' || node.type === 'ul');
33
33
  const depth = stack.filter(node => node.type === 'ol' || node.type === 'ul').length;
34
- return `${' '.repeat(depth)}${(parent && parent.type === 'ol') ? '1.' : '* '} ${await recurse()}\n`;
34
+ return `${' '.repeat(depth)}${parent && parent.type === 'ol' ? '1.' : '* '} ${await recurse()}\n`;
35
35
  },
36
36
  th: async ({ recurse }) => `|${await recurse()}`,
37
37
  td: async ({ recurse }) => `|${await recurse()}`,
@@ -44,12 +44,17 @@ export const Markdown: RenderProvider<RenderContext> = {
44
44
  h2: async ({ recurse }) => `\n## ${await recurse()}\n\n`,
45
45
  h3: async ({ recurse }) => `\n### ${await recurse()}\n\n`,
46
46
  h4: async ({ recurse }) => `\n#### ${await recurse()}\n\n`,
47
- a: async ({ recurse, props }) => `\n[${await recurse()}](${(castTo<{ href: string }>(props)).href})\n`,
47
+ a: async ({ recurse, props }) => `\n[${await recurse()}](${castTo<{ href: string }>(props).href})\n`,
48
48
  Button: async ({ recurse, props }) => `\n[${await recurse()}](${props.href})\n`,
49
49
 
50
50
  InkyTemplate: visit,
51
- Callout: visit, Center: visit, Container: visit,
52
- Column: visit, Wrapper: visit, Row: visit, BlockGrid: visit,
51
+ Callout: visit,
52
+ Center: visit,
53
+ Container: visit,
54
+ Column: visit,
55
+ Wrapper: visit,
56
+ Row: visit,
57
+ BlockGrid: visit,
53
58
 
54
59
  Menu: async ({ recurse }) => `\n${await recurse()}`,
55
60
  Item: async ({ recurse, stack, props }) => {
@@ -58,7 +63,14 @@ export const Markdown: RenderProvider<RenderContext> = {
58
63
  },
59
64
  Spacer: async () => '\n\n',
60
65
 
61
- Summary: ignore, Title: ignore,
66
+ Summary: ignore,
67
+ Title: ignore,
62
68
  img: ignore,
63
- div: visit, title: visit, span: visit, center: visit, table: visit, tbody: visit, small: visit
69
+ div: visit,
70
+ title: visit,
71
+ span: visit,
72
+ center: visit,
73
+ table: visit,
74
+ tbody: visit,
75
+ small: visit
64
76
  };
@@ -1,15 +1,14 @@
1
1
  import { castTo } from '@travetto/runtime';
2
2
 
3
- import { EMPTY_ELEMENT, getComponentName, type JSXElementByFn, type c } from '../components.ts';
3
+ import { createFragment, isJSXElement, type JSXChild, type JSXElement, JSXFragmentType } from '../../support/jsx-runtime.ts';
4
+ import { type c, EMPTY_ELEMENT, getComponentName, type JSXElementByFn } from '../components.ts';
4
5
  import type { RenderProvider, RenderState } from '../types.ts';
5
6
  import { RenderContext, type RenderContextInit } from './context.ts';
6
- import { isJSXElement, type JSXElement, createFragment, JSXFragmentType, type JSXChild } from '../../support/jsx-runtime.ts';
7
7
 
8
8
  /**
9
9
  * Inky Renderer
10
10
  */
11
11
  export class InkyRenderer {
12
-
13
12
  static async #render(
14
13
  ctx: RenderContext,
15
14
  renderer: RenderProvider<RenderContext>,
@@ -46,9 +45,12 @@ export class InkyRenderer {
46
45
  const recurse = (): Promise<string> => this.#render(ctx, renderer, final.props.children ?? [], [...stack, final]);
47
46
  // @ts-expect-error
48
47
  const state: RenderState<JSXElement, RenderContext> = {
49
- node: final, props: final.props, recurse, stack, context: ctx
48
+ node: final,
49
+ props: final.props,
50
+ recurse,
51
+ stack,
52
+ context: ctx
50
53
  };
51
- // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
52
54
  state.createState = (key, props) => this.createState(ctx, renderer, state, key, props);
53
55
  // @ts-expect-error
54
56
  return renderer[name](state);
@@ -66,7 +68,7 @@ export class InkyRenderer {
66
68
  renderer: RenderProvider<RenderContext>,
67
69
  state: RenderState<JSXElement, RenderContext>,
68
70
  key: K,
69
- props: JSXElementByFn<K>['props'],
71
+ props: JSXElementByFn<K>['props']
70
72
  // @ts-expect-error
71
73
  ): RenderState<JSXElementByFn<K>, RenderContext> {
72
74
  const node = ctx.createElement(key, props);
@@ -89,4 +91,4 @@ export class InkyRenderer {
89
91
  const text = await this.#render(ctx, provider, par, []);
90
92
  return provider.finalize(text, ctx, isRoot);
91
93
  }
92
- }
94
+ }
@@ -6,7 +6,7 @@ const empty = async (): Promise<string> => '';
6
6
  const visit = ({ recurse }: RenderState<JSXElement, RenderContext>): Promise<string> => recurse();
7
7
 
8
8
  export const Subject: RenderProvider<RenderContext> = {
9
- finalize: (text) => text,
9
+ finalize: text => text,
10
10
 
11
11
  For: async ({ recurse, props }) => `{{#${props.attr}}}${await recurse()}{{/${props.attr}}}`,
12
12
  If: async ({ recurse, props }) => `{{#${props.attr}}}${await recurse()}{{/${props.attr}}}`,
@@ -15,16 +15,44 @@ export const Subject: RenderProvider<RenderContext> = {
15
15
  Title: visit,
16
16
  InkyTemplate: visit,
17
17
 
18
- title: visit, span: visit, strong: visit, center: visit, em: visit, p: visit, small: visit,
18
+ title: visit,
19
+ span: visit,
20
+ strong: visit,
21
+ center: visit,
22
+ em: visit,
23
+ p: visit,
24
+ small: visit,
19
25
 
20
- Summary: empty, Button: empty,
21
- Callout: empty, Center: empty, HLine: empty,
22
- Menu: empty, Item: empty,
23
- Column: empty, Row: empty, BlockGrid: empty, Spacer: empty,
24
- Wrapper: empty, Container: empty,
26
+ Summary: empty,
27
+ Button: empty,
28
+ Callout: empty,
29
+ Center: empty,
30
+ HLine: empty,
31
+ Menu: empty,
32
+ Item: empty,
33
+ Column: empty,
34
+ Row: empty,
35
+ BlockGrid: empty,
36
+ Spacer: empty,
37
+ Wrapper: empty,
38
+ Container: empty,
25
39
 
26
- div: empty, hr: empty, br: empty, a: empty, img: empty,
27
- ul: empty, ol: empty, li: empty,
28
- table: empty, tbody: empty, td: empty, th: empty, tr: empty, thead: empty,
29
- h1: empty, h2: empty, h3: empty, h4: empty,
40
+ div: empty,
41
+ hr: empty,
42
+ br: empty,
43
+ a: empty,
44
+ img: empty,
45
+ ul: empty,
46
+ ol: empty,
47
+ li: empty,
48
+ table: empty,
49
+ tbody: empty,
50
+ td: empty,
51
+ th: empty,
52
+ tr: empty,
53
+ thead: empty,
54
+ h1: empty,
55
+ h2: empty,
56
+ h3: empty,
57
+ h4: empty
30
58
  };
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { JSXElement, ValidHtmlTags } from '../support/jsx-runtime.ts';
2
- import type { JSXElementByFn, c } from './components.ts';
2
+ import type { c, JSXElementByFn } from './components.ts';
3
3
 
4
4
  export type Wrapper = Record<string, (count: string) => string>;
5
5
 
@@ -16,8 +16,7 @@ export type RenderState<T extends JSXElement, C> = {
16
16
  /**
17
17
  * Renderer
18
18
  */
19
- export type RenderProvider<C> =
20
- { finalize: (text: string, ctx: C, isRoot?: boolean) => (string | Promise<string>) } &
21
- { [K in ValidHtmlTags]: (state: RenderState<JSXElement<K>, C>) => Promise<string>; } &
19
+ export type RenderProvider<C> = { finalize: (text: string, ctx: C, isRoot?: boolean) => string | Promise<string> } & {
20
+ [K in ValidHtmlTags]: (state: RenderState<JSXElement<K>, C>) => Promise<string>;
22
21
  // @ts-expect-error - This is a bit of a hack to allow for custom components,
23
- { [K in keyof typeof c]: (state: RenderState<JSXElementByFn<K>, C>) => Promise<string>; };
22
+ } & { [K in keyof typeof c]: (state: RenderState<JSXElementByFn<K>, C>) => Promise<string> };
package/src/wrapper.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import path from 'node:path';
2
2
 
3
- import { type EmailTemplateModule, type EmailTemplateLocation, EmailResourceLoader } from '@travetto/email';
3
+ import { EmailResourceLoader, type EmailTemplateLocation, type EmailTemplateModule } from '@travetto/email';
4
4
  import { PackageUtil } from '@travetto/manifest';
5
5
 
6
- import { InkyRenderer } from './render/renderer.ts';
6
+ import type { JSXElement } from '../support/jsx-runtime.ts';
7
7
  import { Html } from './render/html.ts';
8
8
  import { Markdown } from './render/markdown.ts';
9
+ import { InkyRenderer } from './render/renderer.ts';
9
10
  import { Subject } from './render/subject.ts';
10
- import type { JSXElement } from '../support/jsx-runtime.ts';
11
11
 
12
12
  export async function prepare(node: JSXElement, location: EmailTemplateLocation): Promise<EmailTemplateModule> {
13
13
  const ctx = {
@@ -23,6 +23,6 @@ export async function prepare(node: JSXElement, location: EmailTemplateLocation)
23
23
  `,
24
24
  html: () => InkyRenderer.render(node, Html, ctx),
25
25
  text: () => InkyRenderer.render(node, Markdown, ctx),
26
- subject: () => InkyRenderer.render(node, Subject, ctx),
26
+ subject: () => InkyRenderer.render(node, Subject, ctx)
27
27
  };
28
28
  }