@player-ui/markdown-plugin 0.3.1--canary.193.5900

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/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@player-ui/markdown-plugin",
3
+ "version": "0.3.1--canary.193.5900",
4
+ "private": false,
5
+ "publishConfig": {
6
+ "registry": "https://registry.npmjs.org"
7
+ },
8
+ "peerDependencies": {
9
+ "@player-ui/player": "0.3.1--canary.193.5900",
10
+ "@player-ui/types": "0.3.1--canary.193.5900"
11
+ },
12
+ "dependencies": {
13
+ "tapable-ts": "^0.2.3",
14
+ "mdast-util-from-markdown": "^2.0.0",
15
+ "@babel/runtime": "7.15.4"
16
+ },
17
+ "main": "dist/index.cjs.js",
18
+ "module": "dist/index.esm.js",
19
+ "typings": "dist/index.d.ts",
20
+ "sideEffects": false,
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/player-ui/player-ui"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/player-ui/player-ui/issues"
28
+ },
29
+ "homepage": "https://player-ui.github.io",
30
+ "contributors": [
31
+ {
32
+ "name": "Adam Dierkens",
33
+ "url": "https://github.com/adierkens"
34
+ },
35
+ {
36
+ "name": "Spencer Hamm",
37
+ "url": "https://github.com/spentacular"
38
+ },
39
+ {
40
+ "name": "Harris Borawski",
41
+ "url": "https://github.com/hborawski"
42
+ },
43
+ {
44
+ "name": "Jeremiah Zucker",
45
+ "url": "https://github.com/sugarmanz"
46
+ },
47
+ {
48
+ "name": "Ketan Reddy",
49
+ "url": "https://github.com/KetanReddy"
50
+ },
51
+ {
52
+ "name": "Brocollie08",
53
+ "url": "https://github.com/brocollie08"
54
+ },
55
+ {
56
+ "name": "Kelly Harrop",
57
+ "url": "https://github.com/kharrop"
58
+ },
59
+ {
60
+ "name": "Alejandro Fimbres",
61
+ "url": "https://github.com/lexfm"
62
+ },
63
+ {
64
+ "name": "Rafael Campos",
65
+ "url": "https://github.com/rafbcampos"
66
+ }
67
+ ],
68
+ "bundle": "./dist/markdown-plugin.prod.js"
69
+ }
package/src/index.ts ADDED
@@ -0,0 +1,50 @@
1
+ import type { Player, PlayerPlugin } from '@player-ui/player';
2
+ import { resolveDataRefsInString, NodeType } from '@player-ui/player';
3
+ import type { Mappers } from './types';
4
+ import { parseAssetMarkdownContent } from './utils';
5
+
6
+ export * from './types';
7
+
8
+ /**
9
+ * A plugin that parses markdown written into text assets using the given converters for markdown features into existing assets.
10
+ */
11
+ export class MarkdownPlugin implements PlayerPlugin {
12
+ name = 'MarkdownPlugin';
13
+
14
+ private mappers: Mappers;
15
+
16
+ constructor(mappers: Mappers) {
17
+ this.mappers = mappers;
18
+ }
19
+
20
+ apply(player: Player) {
21
+ player.hooks.view.tap(this.name, (view) => {
22
+ view.hooks.resolver.tap(this.name, (resolver) => {
23
+ resolver.hooks.beforeResolve.tap(this.name, (node, options) => {
24
+ if (node?.type === NodeType.Asset && node.value.type === 'markdown') {
25
+ const resolvedContent = resolveDataRefsInString(
26
+ node.value.value as string,
27
+ {
28
+ evaluate: options.evaluate,
29
+ model: options.data.model,
30
+ }
31
+ );
32
+
33
+ const parsed = parseAssetMarkdownContent({
34
+ asset: {
35
+ ...node.value,
36
+ value: resolvedContent,
37
+ },
38
+ mappers: this.mappers,
39
+ parser: options.parseNode,
40
+ });
41
+
42
+ return parsed;
43
+ }
44
+
45
+ return node;
46
+ });
47
+ });
48
+ });
49
+ }
50
+ }
package/src/types.ts ADDED
@@ -0,0 +1,144 @@
1
+ import type { Asset } from '@player-ui/types';
2
+
3
+ export interface BaseArgs {
4
+ /**
5
+ * Unparsed Asset
6
+ */
7
+ originalAsset: Asset;
8
+ }
9
+
10
+ export type LiteralMapper<T extends object = object> = (
11
+ args: {
12
+ /**
13
+ * markdown element value
14
+ */
15
+ value: string;
16
+ } & BaseArgs &
17
+ T
18
+ ) => Asset;
19
+
20
+ export type CompositeMapper<T extends object = object> = (
21
+ args: {
22
+ /**
23
+ * array of assets resulted from the recursion over the AST node children
24
+ */
25
+ value: Asset[];
26
+ } & BaseArgs &
27
+ T
28
+ ) => Asset;
29
+
30
+ export type FallbackMapper = (
31
+ args: {
32
+ /**
33
+ * markdown element value
34
+ */
35
+ value: string | Asset[];
36
+ } & BaseArgs
37
+ ) => Asset;
38
+
39
+ export interface Mappers {
40
+ /**
41
+ * required text Asset
42
+ */
43
+ text: LiteralMapper;
44
+ /**
45
+ * required paragraph (composite) Asset
46
+ */
47
+ paragraph: CompositeMapper;
48
+ /**
49
+ * required collection Asset to wrap arrays of assets
50
+ */
51
+ collection: CompositeMapper;
52
+ /**
53
+ * strong markdown (e.g. **bold**)
54
+ */
55
+ strong?: CompositeMapper;
56
+ /**
57
+ * emphasis markdown (e.g. *italic*)
58
+ */
59
+ emphasis?: CompositeMapper;
60
+ /**
61
+ * blockquote markdown (e.g. > blockquote)
62
+ */
63
+ blockquote?: CompositeMapper;
64
+ /**
65
+ * ordered or unordered list markdown (e.g. 1. item\n2. item, - item\n- item)
66
+ */
67
+ list?: CompositeMapper<{
68
+ /**
69
+ * Whether the list is ordered or not.
70
+ */
71
+ ordered: boolean;
72
+ /**
73
+ * The starting list number.
74
+ */
75
+ start?: number;
76
+ }>;
77
+ /**
78
+ * horizontalRule markdown (e.g. ---)
79
+ */
80
+ horizontalRule?: LiteralMapper;
81
+ /**
82
+ * link markdown (e.g. `[text](url)`)
83
+ */
84
+ link?: CompositeMapper<{
85
+ /**
86
+ * Link URL
87
+ */
88
+ href: string;
89
+ }>;
90
+ /**
91
+ * image markdown (e.g. `![alt](url)`)
92
+ */
93
+ image?: LiteralMapper<{
94
+ /**
95
+ * Image source URL
96
+ */
97
+ src: string;
98
+ }>;
99
+ /**
100
+ * code block markdown (e.g. ```code```)
101
+ */
102
+ code?: LiteralMapper<{
103
+ /**
104
+ * The language of the code block.
105
+ */
106
+ lang?: string;
107
+ }>;
108
+ /**
109
+ * heading markdown (e.g. # heading)
110
+ */
111
+ heading?: CompositeMapper<{
112
+ /**
113
+ * The heading depth.
114
+ */
115
+ depth: number;
116
+ }>;
117
+ /**
118
+ * inline code markdown (e.g. `code`)
119
+ */
120
+ inlineCode?: LiteralMapper;
121
+ /**
122
+ * list item markdown (e.g. - item)
123
+ */
124
+ listItem?: CompositeMapper;
125
+ }
126
+
127
+ export type Transformer<T = any> = (args: {
128
+ /**
129
+ * AST Node (e.g. Link)
130
+ */
131
+ astNode: T;
132
+ /**
133
+ * Player Asset
134
+ */
135
+ asset: Asset;
136
+ /**
137
+ * Record off mappers (markdown element -> Asset)
138
+ */
139
+ mappers: Mappers;
140
+ /**
141
+ * Record of parsers (e.g., { link :linkParser })
142
+ */
143
+ transformers: Record<string, Transformer>;
144
+ }) => Asset;
@@ -0,0 +1,2 @@
1
+ export * from './markdownParser';
2
+ export * from './transformers';
@@ -0,0 +1,67 @@
1
+ import type { Node, ParseObjectOptions } from '@player-ui/player';
2
+ import { NodeType } from '@player-ui/player';
3
+ import type { Asset } from '@player-ui/types';
4
+ import { fromMarkdown } from 'mdast-util-from-markdown';
5
+ import type { Mappers } from '../types';
6
+ import { transformers } from './transformers';
7
+
8
+ /**
9
+ * Parses markdown content using a provided mappers record.
10
+ */
11
+ export function parseAssetMarkdownContent({
12
+ asset,
13
+ mappers,
14
+ parser,
15
+ }: {
16
+ /**
17
+ * Asset to be parsed
18
+ */
19
+ asset: Asset;
20
+ /**
21
+ * Mappers record of AST Node to Player Content
22
+ *
23
+ * @see {@link Mappers}
24
+ */
25
+ mappers: Mappers;
26
+ /**
27
+ * Parser object to AST
28
+ */
29
+ parser?: (
30
+ obj: object,
31
+ type?: Node.ChildrenTypes,
32
+ options?: ParseObjectOptions
33
+ ) => Node.Node | null;
34
+ }): Node.Node | null {
35
+ const { children } = fromMarkdown(asset.value as string);
36
+ const isMultiParagraph = children.length > 1;
37
+
38
+ if (isMultiParagraph) {
39
+ const value = children.map((node) => {
40
+ const transformer = transformers[node.type as keyof typeof transformers];
41
+ return transformer({
42
+ astNode: node as any,
43
+ asset,
44
+ mappers,
45
+ transformers,
46
+ });
47
+ });
48
+
49
+ const collection = mappers.collection({
50
+ originalAsset: asset,
51
+ value,
52
+ });
53
+
54
+ return parser?.(collection, NodeType.Asset) || null;
55
+ }
56
+
57
+ const transformer =
58
+ transformers[children[0].type as keyof typeof transformers];
59
+ const content = transformer({
60
+ astNode: children[0] as any,
61
+ asset,
62
+ mappers,
63
+ transformers,
64
+ });
65
+
66
+ return parser?.(content, NodeType.Asset) || null;
67
+ }
@@ -0,0 +1,327 @@
1
+ import type { Asset } from '@player-ui/types';
2
+ import type {
3
+ Blockquote,
4
+ Code,
5
+ Emphasis,
6
+ Heading,
7
+ Image,
8
+ InlineCode,
9
+ Link,
10
+ List,
11
+ ListItem,
12
+ Paragraph,
13
+ Strong,
14
+ Text,
15
+ ThematicBreak,
16
+ } from 'mdast-util-from-markdown/lib';
17
+ import type { Transformer } from '../types';
18
+
19
+ /**
20
+ * Swap markdown type to text
21
+ */
22
+ function swapMarkdownType(asset: Asset): Asset {
23
+ return { ...asset, type: 'text' };
24
+ }
25
+
26
+ /**
27
+ * Transforms Text AST Node into Player Content
28
+ */
29
+ const textTransformer: Transformer<Text> = ({ astNode, asset, mappers }) => {
30
+ const { value } = astNode;
31
+
32
+ return mappers.text({
33
+ originalAsset: asset,
34
+ value,
35
+ });
36
+ };
37
+
38
+ /**
39
+ * Transforms Emphasis AST Node into Player Content
40
+ */
41
+ const emphasisTransformer: Transformer<Emphasis> = ({
42
+ astNode,
43
+ asset,
44
+ mappers,
45
+ transformers,
46
+ }) => {
47
+ if (mappers.emphasis) {
48
+ const { children } = astNode;
49
+
50
+ const value = children.map((node) =>
51
+ transformers[node.type]({ astNode: node, asset, mappers, transformers })
52
+ );
53
+
54
+ return mappers.emphasis({
55
+ originalAsset: asset,
56
+ value,
57
+ });
58
+ }
59
+
60
+ return swapMarkdownType(asset);
61
+ };
62
+
63
+ /**
64
+ * Transforms Strong AST Node into Player Content
65
+ */
66
+ const strongTransformer: Transformer<Strong> = ({
67
+ astNode,
68
+ asset,
69
+ mappers,
70
+ transformers,
71
+ }) => {
72
+ if (mappers.strong) {
73
+ const { children } = astNode;
74
+
75
+ const value = children.map((node) =>
76
+ transformers[node.type]({ astNode: node, asset, mappers, transformers })
77
+ );
78
+
79
+ return mappers.strong({
80
+ originalAsset: asset,
81
+ value,
82
+ });
83
+ }
84
+
85
+ return swapMarkdownType(asset);
86
+ };
87
+
88
+ /**
89
+ * Transforms Paragraph AST Node into Player Content
90
+ */
91
+ const paragraphTransformer: Transformer<Paragraph> = ({
92
+ astNode,
93
+ asset,
94
+ mappers,
95
+ transformers,
96
+ }) => {
97
+ const { children } = astNode;
98
+
99
+ if (
100
+ children.every(({ type }) => Boolean(mappers[type as keyof typeof mappers]))
101
+ ) {
102
+ const value = children.map((node) =>
103
+ transformers[node.type]({ astNode: node, asset, mappers, transformers })
104
+ );
105
+
106
+ return mappers.paragraph({
107
+ originalAsset: asset,
108
+ value,
109
+ });
110
+ }
111
+
112
+ return swapMarkdownType(asset);
113
+ };
114
+
115
+ /**
116
+ * Transforms List AST Node into Player Content
117
+ */
118
+ const listTransformer: Transformer<List> = ({
119
+ astNode,
120
+ asset,
121
+ mappers,
122
+ transformers,
123
+ }) => {
124
+ if (mappers.list) {
125
+ const { children, ordered, start } = astNode;
126
+
127
+ const value = children.map((node) =>
128
+ transformers[node.type]({ astNode: node, asset, mappers, transformers })
129
+ );
130
+
131
+ return mappers.list({
132
+ originalAsset: asset,
133
+ value,
134
+ ordered: Boolean(ordered),
135
+ start: start || undefined,
136
+ });
137
+ }
138
+
139
+ return swapMarkdownType(asset);
140
+ };
141
+
142
+ /**
143
+ * Transforms ListItem AST Node into Player Content
144
+ */
145
+ const listItemTransformer: Transformer<ListItem> = ({
146
+ astNode,
147
+ asset,
148
+ mappers,
149
+ transformers,
150
+ }) => {
151
+ const { children } = astNode;
152
+
153
+ const value = children.map((node) =>
154
+ transformers[node.type]({ astNode: node, asset, mappers, transformers })
155
+ );
156
+
157
+ const mapper = mappers.listItem || mappers.paragraph;
158
+
159
+ return mapper({
160
+ originalAsset: asset,
161
+ value,
162
+ });
163
+ };
164
+
165
+ /**
166
+ * Transforms Link AST Node into Player Content
167
+ */
168
+ const linkTransformer: Transformer<Link> = ({
169
+ astNode,
170
+ asset,
171
+ mappers,
172
+ transformers,
173
+ }) => {
174
+ if (mappers.link) {
175
+ const { children, url } = astNode;
176
+
177
+ const value = children.map((node) =>
178
+ transformers[node.type]({ astNode: node, asset, mappers, transformers })
179
+ );
180
+
181
+ return mappers.link({
182
+ originalAsset: asset,
183
+ href: url,
184
+ value,
185
+ });
186
+ }
187
+
188
+ return swapMarkdownType(asset);
189
+ };
190
+
191
+ /**
192
+ * Transforms Image AST Node into Player Content
193
+ */
194
+ const imageTransformer: Transformer<Image> = ({ astNode, asset, mappers }) => {
195
+ if (mappers.image) {
196
+ const { title, url, alt } = astNode;
197
+
198
+ return mappers.image({
199
+ originalAsset: asset,
200
+ src: url,
201
+ value: title || alt || '',
202
+ });
203
+ }
204
+
205
+ return swapMarkdownType(asset);
206
+ };
207
+
208
+ /**
209
+ * Transforms Blockquote AST Node into Player Content
210
+ */
211
+ const blockquoteTransformer: Transformer<Blockquote> = ({
212
+ astNode,
213
+ asset,
214
+ mappers,
215
+ transformers,
216
+ }) => {
217
+ if (mappers.blockquote) {
218
+ const { children } = astNode;
219
+
220
+ const value = children.map((node) =>
221
+ transformers[node.type]({ astNode: node, asset, mappers, transformers })
222
+ );
223
+
224
+ return mappers.blockquote({
225
+ originalAsset: asset,
226
+ value,
227
+ });
228
+ }
229
+
230
+ return swapMarkdownType(asset);
231
+ };
232
+
233
+ /**
234
+ * Transforms InlineCode AST Node into Player Content
235
+ */
236
+ const inlineCodeTransformer: Transformer<InlineCode> = ({
237
+ astNode,
238
+ asset,
239
+ mappers,
240
+ }) => {
241
+ if (mappers.inlineCode) {
242
+ const { value } = astNode;
243
+
244
+ return mappers.inlineCode({
245
+ originalAsset: asset,
246
+ value,
247
+ });
248
+ }
249
+
250
+ return swapMarkdownType(asset);
251
+ };
252
+
253
+ /**
254
+ * Transforms Code block AST Node into Player Content
255
+ */
256
+ const codeTransformer: Transformer<Code> = ({ astNode, asset, mappers }) => {
257
+ if (mappers.code) {
258
+ const { value, lang } = astNode;
259
+
260
+ return mappers.code({
261
+ originalAsset: asset,
262
+ value,
263
+ lang: lang || undefined,
264
+ });
265
+ }
266
+
267
+ return swapMarkdownType(asset);
268
+ };
269
+
270
+ /**
271
+ * Transforms Horizontal Rule (Thematic Break) AST Node into Player Content
272
+ */
273
+ const horizontalRuleTransformer: Transformer<ThematicBreak> = ({
274
+ asset,
275
+ mappers,
276
+ }) => {
277
+ if (mappers.horizontalRule) {
278
+ return mappers.horizontalRule({
279
+ originalAsset: asset,
280
+ value: '---',
281
+ });
282
+ }
283
+
284
+ return swapMarkdownType(asset);
285
+ };
286
+
287
+ /**
288
+ * Transforms Heading AST Node into Player Content
289
+ */
290
+ const headingTransformer: Transformer<Heading> = ({
291
+ astNode,
292
+ asset,
293
+ mappers,
294
+ transformers,
295
+ }) => {
296
+ if (mappers.heading) {
297
+ const { children, depth } = astNode;
298
+
299
+ const value = children.map((node) =>
300
+ transformers[node.type]({ astNode: node, asset, mappers, transformers })
301
+ );
302
+
303
+ return mappers.heading({
304
+ originalAsset: asset,
305
+ value,
306
+ depth,
307
+ });
308
+ }
309
+
310
+ return swapMarkdownType(asset);
311
+ };
312
+
313
+ export const transformers: Record<string, Transformer> = {
314
+ horizontalRule: horizontalRuleTransformer,
315
+ text: textTransformer,
316
+ emphasis: emphasisTransformer,
317
+ strong: strongTransformer,
318
+ blockquote: blockquoteTransformer,
319
+ list: listTransformer,
320
+ listItem: listItemTransformer,
321
+ link: linkTransformer,
322
+ image: imageTransformer,
323
+ paragraph: paragraphTransformer,
324
+ code: codeTransformer,
325
+ heading: headingTransformer,
326
+ inlineCode: inlineCodeTransformer,
327
+ };