repl-sdk 1.5.1 → 1.6.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/package.json +9 -7
- package/src/compilers/ember/gjs.js +12 -1
- package/src/compilers/ember/gmd.js +7 -0
- package/src/compilers/ember/hbs.js +12 -1
- package/src/compilers/markdown/parse.js +52 -20
- package/src/compilers/markdown/parse.test.ts +1 -1
- package/src/index.js +6 -2
- package/src/remark-escape-components.js +39 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "repl-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -45,11 +45,12 @@
|
|
|
45
45
|
"@types/hast": "^3.0.4",
|
|
46
46
|
"@types/mdast": "^4.0.4",
|
|
47
47
|
"common-tags": "^1.8.2",
|
|
48
|
+
"decorator-transforms": "2.3.1",
|
|
48
49
|
"eslint": "^9.39.1",
|
|
49
50
|
"prettier": "^3.7.4",
|
|
50
51
|
"publint": "^0.3.16",
|
|
51
52
|
"typescript": "^5.9.3",
|
|
52
|
-
"vite": "^
|
|
53
|
+
"vite": "^8.0.8",
|
|
53
54
|
"vite-plugin-dts": "4.5.4",
|
|
54
55
|
"vitest": "^4.0.18"
|
|
55
56
|
},
|
|
@@ -95,21 +96,22 @@
|
|
|
95
96
|
"unified": "^11.0.5",
|
|
96
97
|
"unist-util-visit": "^5.0.0",
|
|
97
98
|
"vfile": "^6.0.3",
|
|
98
|
-
"codemirror-lang-glimdown": "^2.0.
|
|
99
|
-
"codemirror-lang-glimmer": "^2.0.
|
|
100
|
-
"codemirror-lang-glimmer-js": "^2.0.
|
|
99
|
+
"codemirror-lang-glimdown": "^2.0.4",
|
|
100
|
+
"codemirror-lang-glimmer": "^2.0.4",
|
|
101
|
+
"codemirror-lang-glimmer-js": "^2.0.4"
|
|
101
102
|
},
|
|
102
103
|
"volta": {
|
|
103
104
|
"extends": "../../package.json"
|
|
104
105
|
},
|
|
105
106
|
"scripts": {
|
|
106
|
-
"lint
|
|
107
|
+
"lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\" --prefixColors auto",
|
|
108
|
+
"lint:fix": "concurrently \"pnpm:lint:*:fix\" --names \"fix:\" --prefixColors auto && pnpm run format",
|
|
107
109
|
"example": "cd example && vite",
|
|
108
110
|
"lint:package": "pnpm publint",
|
|
109
111
|
"lint:js": "pnpm -w exec lint js",
|
|
110
112
|
"lint:types": "tsc --noEmit",
|
|
111
113
|
"lint:js:fix": "pnpm -w exec lint js:fix",
|
|
112
|
-
"
|
|
114
|
+
"format": "pnpm -w exec lint prettier:fix",
|
|
113
115
|
"lint:prettier": "pnpm -w exec lint prettier",
|
|
114
116
|
"test:node": "vitest"
|
|
115
117
|
}
|
|
@@ -170,7 +170,18 @@ export async function compiler(config, api) {
|
|
|
170
170
|
const { renderComponent } = await compiler.tryResolve('@ember/renderer');
|
|
171
171
|
|
|
172
172
|
const owner = makeOwner(config.owner);
|
|
173
|
-
const
|
|
173
|
+
const args = /** @type {Record<string, unknown> | undefined} */ (
|
|
174
|
+
extra && typeof extra === 'object' && 'args' in extra
|
|
175
|
+
? /** @type {Record<string, unknown>} */ (extra).args
|
|
176
|
+
: undefined
|
|
177
|
+
);
|
|
178
|
+
const result = renderComponent(compiled, {
|
|
179
|
+
into: element,
|
|
180
|
+
owner,
|
|
181
|
+
...(args ? { args } : {}),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
compiler.announce('info', 'Ember Island Rendered');
|
|
174
185
|
|
|
175
186
|
return () => result.destroy();
|
|
176
187
|
},
|
|
@@ -98,9 +98,16 @@ export async function compiler(config, api) {
|
|
|
98
98
|
|
|
99
99
|
const { renderComponent } = await compiler.tryResolve('@ember/renderer');
|
|
100
100
|
|
|
101
|
+
const args = /** @type {Record<string, unknown> | undefined} */ (
|
|
102
|
+
extra && typeof extra === 'object' && 'args' in extra
|
|
103
|
+
? /** @type {Record<string, unknown>} */ (extra).args
|
|
104
|
+
: undefined
|
|
105
|
+
);
|
|
106
|
+
|
|
101
107
|
const result = renderComponent(compiled, {
|
|
102
108
|
into: element,
|
|
103
109
|
owner: userOptions.owner,
|
|
110
|
+
...(args ? { args } : {}),
|
|
104
111
|
});
|
|
105
112
|
|
|
106
113
|
const destroy = () => result.destroy();
|
|
@@ -67,7 +67,18 @@ export async function compiler(config, api) {
|
|
|
67
67
|
|
|
68
68
|
const { renderComponent } = await compiler.tryResolve('@ember/renderer');
|
|
69
69
|
const owner = makeOwner(config.owner);
|
|
70
|
-
const
|
|
70
|
+
const args = /** @type {Record<string, unknown> | undefined} */ (
|
|
71
|
+
extra && typeof extra === 'object' && 'args' in extra
|
|
72
|
+
? /** @type {Record<string, unknown>} */ (extra).args
|
|
73
|
+
: undefined
|
|
74
|
+
);
|
|
75
|
+
const result = renderComponent(compiled, {
|
|
76
|
+
into: element,
|
|
77
|
+
owner,
|
|
78
|
+
...(args ? { args } : {}),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
compiler.announce('info', 'Ember Island Rendered');
|
|
71
82
|
|
|
72
83
|
return () => result.destroy();
|
|
73
84
|
},
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* @property {CodeBlock[]} codeBlocks
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
+
import remarkEscapeComponents, { REPL_LT } from '../../remark-escape-components.js';
|
|
15
16
|
import { buildCompiler } from './build-compiler.js';
|
|
16
17
|
|
|
17
18
|
export { buildCompiler } from './build-compiler.js';
|
|
@@ -22,36 +23,67 @@ export { buildCompiler } from './build-compiler.js';
|
|
|
22
23
|
* @returns {Promise<ParseResult>}
|
|
23
24
|
*/
|
|
24
25
|
export async function parseMarkdown(input, options) {
|
|
25
|
-
const markdownCompiler =
|
|
26
|
+
const markdownCompiler =
|
|
27
|
+
options?.compiler ??
|
|
28
|
+
buildCompiler({
|
|
29
|
+
...options,
|
|
30
|
+
remarkPlugins: [...(options?.remarkPlugins || []), remarkEscapeComponents],
|
|
31
|
+
});
|
|
26
32
|
const processed = await markdownCompiler.process(input);
|
|
27
33
|
const liveCode = /** @type {CodeBlock[]} */ (processed.data.liveCode || []);
|
|
28
34
|
// @ts-ignore - processed is typed as unknown due to unified processor complexity
|
|
29
35
|
let templateOnly = processed.toString();
|
|
30
36
|
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
// 1. Convert the placeholder written by the remark plugin to <
|
|
38
|
+
// This placeholder survives the entire unified pipeline without being
|
|
39
|
+
// entity-encoded, so no double-escaping can occur.
|
|
40
|
+
if (REPL_LT) {
|
|
41
|
+
templateOnly = templateOnly.replaceAll(REPL_LT, '<');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 2. The pipeline may HTML-escape `<` for PascalCase component invocations
|
|
45
|
+
// that appear in regular markdown (outside code/backticks). Undo that so
|
|
46
|
+
// Glimmer can still invoke them. We only unescape outside <code> elements
|
|
47
|
+
// (and outside <pre> blocks) to preserve escaping in code.
|
|
48
|
+
templateOnly = unescapeComponentsOutsideCode(templateOnly);
|
|
49
|
+
|
|
50
|
+
return { text: templateOnly, codeBlocks: liveCode };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Undo HTML-escaping of PascalCase component tags that appear outside
|
|
55
|
+
* `<code>` and `<pre>` blocks so Glimmer can invoke them.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} html
|
|
58
|
+
* @returns {string}
|
|
59
|
+
*/
|
|
60
|
+
function unescapeComponentsOutsideCode(html) {
|
|
61
|
+
// Split by <pre>…</pre> blocks first – never touch code fences.
|
|
62
|
+
const parts = html.split(/(<pre[\s\S]*?<\/pre>)/gi);
|
|
36
63
|
|
|
37
64
|
for (let i = 0; i < parts.length; i++) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
65
|
+
// Only touch content outside <pre>
|
|
66
|
+
if (i % 2 === 0) {
|
|
67
|
+
// Split by <code>…</code> so we skip inline code too
|
|
68
|
+
const part = parts[i] ?? '';
|
|
69
|
+
const codeParts = part.split(/(<code[^>]*>[\s\S]*?<\/code>)/gi);
|
|
70
|
+
|
|
71
|
+
for (let j = 0; j < codeParts.length; j++) {
|
|
72
|
+
const segment = codeParts[j];
|
|
73
|
+
|
|
74
|
+
// Even indices are outside <code> – unescape PascalCase there
|
|
75
|
+
if (j % 2 === 0 && segment) {
|
|
76
|
+
codeParts[j] = segment
|
|
77
|
+
.replace(/<([A-Z][a-zA-Z0-9]*\s[^<]*?)>/g, (_m, content) =>
|
|
78
|
+
content.includes('@') ? `<${content}>` : _m
|
|
79
|
+
)
|
|
80
|
+
.replace(/<\/([A-Z][a-zA-Z0-9]*)>/g, '</$1>');
|
|
47
81
|
}
|
|
82
|
+
}
|
|
48
83
|
|
|
49
|
-
|
|
50
|
-
});
|
|
84
|
+
parts[i] = codeParts.join('');
|
|
51
85
|
}
|
|
52
86
|
}
|
|
53
87
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return { text: templateOnly, codeBlocks: liveCode };
|
|
88
|
+
return parts.join('');
|
|
57
89
|
}
|
|
@@ -230,7 +230,7 @@ describe('default features', () => {
|
|
|
230
230
|
expect(result).toMatchInlineSnapshot(`
|
|
231
231
|
{
|
|
232
232
|
"codeBlocks": [],
|
|
233
|
-
"text": "<h2 id="hello-foo-two"><code
|
|
233
|
+
"text": "<h2 id="hello-foo-two"><code><Hello @foo="two" /></code></h2>",
|
|
234
234
|
}
|
|
235
235
|
`);
|
|
236
236
|
});
|
package/src/index.js
CHANGED
|
@@ -345,7 +345,7 @@ export class Compiler {
|
|
|
345
345
|
/**
|
|
346
346
|
* @param {string} format
|
|
347
347
|
* @param {string} text
|
|
348
|
-
* @param {{ fileName?: string, flavor?: string, [key: string]: unknown }} [ options ]
|
|
348
|
+
* @param {{ fileName?: string, flavor?: string, args?: Record<string, unknown>, [key: string]: unknown }} [ options ]
|
|
349
349
|
* @returns {Promise<{ element: HTMLElement, destroy: () => void }>}
|
|
350
350
|
*/
|
|
351
351
|
async compile(format, text, options = {}) {
|
|
@@ -411,6 +411,7 @@ export class Compiler {
|
|
|
411
411
|
return this.#render(compiler, value, {
|
|
412
412
|
...extras,
|
|
413
413
|
compiled: value,
|
|
414
|
+
...(opts.args ? { args: opts.args } : {}),
|
|
414
415
|
});
|
|
415
416
|
}
|
|
416
417
|
|
|
@@ -421,7 +422,10 @@ export class Compiler {
|
|
|
421
422
|
|
|
422
423
|
this.#log('[compile] preparing to render', defaultExport, extras);
|
|
423
424
|
|
|
424
|
-
return this.#render(compiler, defaultExport,
|
|
425
|
+
return this.#render(compiler, defaultExport, {
|
|
426
|
+
...extras,
|
|
427
|
+
...(opts.args ? { args: opts.args } : {}),
|
|
428
|
+
});
|
|
425
429
|
}
|
|
426
430
|
|
|
427
431
|
#compilerCache = new WeakMap();
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { visit } from 'unist-util-visit';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A unique placeholder that replaces `<` in PascalCase component tags
|
|
5
|
+
* inside inline code and non-live code fences. It survives the unified
|
|
6
|
+
* pipeline without being entity-encoded, and is converted to `<`
|
|
7
|
+
* in the final post-processing step inside `parseMarkdown()`.
|
|
8
|
+
*/
|
|
9
|
+
export const REPL_LT = '__REPL_LT__';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Remark plugin: escape PascalCase component tags in `inlineCode` and
|
|
13
|
+
* non-live `code` (code fence) nodes by replacing `<` with a placeholder.
|
|
14
|
+
*/
|
|
15
|
+
function remarkEscapeComponents() {
|
|
16
|
+
/** @param {import('mdast').Root} tree */
|
|
17
|
+
return (tree) => {
|
|
18
|
+
visit(tree, (node) => {
|
|
19
|
+
// Inline code (backticks)
|
|
20
|
+
if (node.type === 'inlineCode') {
|
|
21
|
+
node.value = node.value
|
|
22
|
+
.replace(/<([A-Z][a-zA-Z0-9]*(?:\s[^<]*)?)>/g, REPL_LT + '$1>')
|
|
23
|
+
.replace(/<\/([A-Z][a-zA-Z0-9]*)>/g, REPL_LT + '/$1>');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Code fences (``` blocks)
|
|
27
|
+
if (node.type === 'code') {
|
|
28
|
+
// Only escape if not live
|
|
29
|
+
if (!/\blive\b/.test(node.meta || '')) {
|
|
30
|
+
node.value = node.value
|
|
31
|
+
.replace(/<([A-Z][a-zA-Z0-9]*(?:\s[^<]*)?)>/g, REPL_LT + '$1>')
|
|
32
|
+
.replace(/<\/([A-Z][a-zA-Z0-9]*)>/g, REPL_LT + '/$1>');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default remarkEscapeComponents;
|