repl-sdk 0.0.0 → 1.0.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.
Files changed (61) hide show
  1. package/dist/assets/tar-worker-kdkltuRC.js +598 -0
  2. package/dist/assets/tar-worker-kdkltuRC.js.map +1 -0
  3. package/dist/codemirror-D4aIVflZ.js +110 -0
  4. package/dist/codemirror-D4aIVflZ.js.map +1 -0
  5. package/dist/gjs-CzFzkEFv.js +173 -0
  6. package/dist/gjs-CzFzkEFv.js.map +1 -0
  7. package/dist/gmd-D9OXs2v3.js +166 -0
  8. package/dist/gmd-D9OXs2v3.js.map +1 -0
  9. package/dist/hbs-CuhWjffM.js +62 -0
  10. package/dist/hbs-CuhWjffM.js.map +1 -0
  11. package/dist/index-CUWCqMoD.js +2133 -0
  12. package/dist/index-CUWCqMoD.js.map +1 -0
  13. package/dist/index.js +4 -104
  14. package/dist/index.js.map +1 -1
  15. package/dist/parse-aBKk9rfS.js +328 -0
  16. package/dist/parse-aBKk9rfS.js.map +1 -0
  17. package/dist/render-app-island-B-i8rvGi.js +61 -0
  18. package/dist/render-app-island-B-i8rvGi.js.map +1 -0
  19. package/package.json +82 -9
  20. package/src/cache.js +138 -0
  21. package/src/cdn.js +93 -0
  22. package/src/codemirror.js +161 -0
  23. package/src/compilers/ember/gjs.js +212 -0
  24. package/src/compilers/ember/gmd.js +190 -0
  25. package/src/compilers/ember/hbs.js +98 -0
  26. package/src/compilers/ember/render-app-island.js +83 -0
  27. package/src/compilers/ember.js +166 -0
  28. package/src/compilers/js.js +32 -0
  29. package/src/compilers/markdown/build-compiler.js +151 -0
  30. package/src/compilers/markdown/const.js +2 -0
  31. package/src/compilers/markdown/heading-id.js +75 -0
  32. package/src/compilers/markdown/live-code-extraction.js +198 -0
  33. package/src/compilers/markdown/parse.js +22 -0
  34. package/src/compilers/markdown/parse.test.ts +363 -0
  35. package/src/compilers/markdown/sanitize-for-glimmer.js +26 -0
  36. package/src/compilers/markdown/types.ts +21 -0
  37. package/src/compilers/markdown/utils.js +78 -0
  38. package/src/compilers/markdown.js +125 -0
  39. package/src/compilers/mermaid.js +35 -0
  40. package/src/compilers/react.js +47 -0
  41. package/src/compilers/svelte.js +116 -0
  42. package/src/compilers/vue.js +58 -0
  43. package/src/compilers.js +108 -0
  44. package/src/es-module-shim.js +53 -0
  45. package/src/index.d.ts +53 -4
  46. package/src/index.js +744 -89
  47. package/src/npm.js +58 -0
  48. package/src/request.Request.test.ts +59 -0
  49. package/src/request.js +140 -0
  50. package/src/resolve.fromImports.test.ts +35 -0
  51. package/src/resolve.fromInternalImport.test.ts +69 -0
  52. package/src/resolve.js +352 -0
  53. package/src/resolve.resolvePath.test.ts +24 -0
  54. package/src/resolve.test.ts +23 -0
  55. package/src/specifier.js +71 -0
  56. package/src/specifier.test.ts +90 -0
  57. package/src/tar-worker.js +61 -0
  58. package/src/tar.js +76 -0
  59. package/src/types.ts +335 -58
  60. package/src/utils.js +28 -1
  61. package/declarations/index.d.ts +0 -73
@@ -0,0 +1,190 @@
1
+ /**
2
+ * @typedef {import('unified').Plugin} Plugin
3
+ */
4
+ import { assert, isRecord } from '../../utils.js';
5
+ import { buildCodeFenceMetaUtils } from '../markdown/utils.js';
6
+ import { renderApp } from './render-app-island.js';
7
+
8
+ let elementId = 0;
9
+
10
+ /**
11
+ * @param {unknown} [ options ]
12
+ * @returns {{
13
+ * scope: Record<string, unknown>,
14
+ * remarkPlugins: Plugin[],
15
+ * rehypePlugins: Plugin[],
16
+ * ShadowComponent: string | undefined,
17
+ * CopyComponent: string | undefined
18
+ * }}
19
+ */
20
+ export function filterOptions(options) {
21
+ if (!isRecord(options)) {
22
+ return {
23
+ scope: {},
24
+ remarkPlugins: [],
25
+ rehypePlugins: [],
26
+ ShadowComponent: undefined,
27
+ CopyComponent: undefined,
28
+ };
29
+ }
30
+
31
+ return {
32
+ scope: /** @type {Record<string, unknown>}*/ (options?.scope || {}),
33
+ remarkPlugins: /** @type {Plugin[]}*/ (options?.remarkPlugins || []),
34
+ rehypePlugins: /** @type {Plugin[]}*/ (options?.rehypePlugins || []),
35
+ ShadowComponent: /** @type {string}*/ (options?.ShadowComponent),
36
+ CopyComponent: /** @type {string}*/ (options?.CopyComponent),
37
+ };
38
+ }
39
+
40
+ /**
41
+ * @type {import('../../types.ts').CompilerConfig['compiler']}
42
+ */
43
+ export async function compiler(config, api) {
44
+ const userOptions = filterOptions(
45
+ /** @type {Record<string, unknown>} */ (config.userOptions)?.gmd || config
46
+ );
47
+
48
+ const { isLive, isPreview, needsLive, allowedFormats, getFlavorFromMeta, isBelow } =
49
+ buildCodeFenceMetaUtils(api);
50
+
51
+ const { parseMarkdown } = await import(/* @vite-ignore */ '../markdown/parse.js');
52
+
53
+ /**
54
+ * @type {import('../../types.ts').Compiler}
55
+ */
56
+ const gmdCompiler = {
57
+ compile: async (text, options) => {
58
+ const compileOptions = filterOptions(options);
59
+ const result = await parseMarkdown(text, {
60
+ remarkPlugins: [...userOptions.remarkPlugins, ...compileOptions.remarkPlugins],
61
+ rehypePlugins: [...userOptions.rehypePlugins, ...compileOptions.rehypePlugins],
62
+ isLive,
63
+ isPreview,
64
+ isBelow,
65
+ needsLive,
66
+ ALLOWED_FORMATS: allowedFormats,
67
+ getFlavorFromMeta,
68
+ });
69
+
70
+ const { template } = await api.tryResolve('@ember/template-compiler/runtime');
71
+
72
+ const scope = {
73
+ ...filterOptions(userOptions).scope,
74
+ ...filterOptions(options).scope,
75
+ };
76
+
77
+ const component = template(result.text, {
78
+ scope: () => ({
79
+ ...scope,
80
+ // TODO: compile all the components from "result" and add them to scope here
81
+ // would this be better than the markdown style multiple islands
82
+ }),
83
+ });
84
+
85
+ return { compiled: component, ...result, scope };
86
+ },
87
+ render: async (element, compiled, extra, compiler) => {
88
+ /**
89
+ *
90
+ * TODO: These will make things easier:
91
+ * https://github.com/emberjs/rfcs/pull/1099
92
+ * https://github.com/ember-cli/ember-addon-blueprint/blob/main/files/tests/test-helper.js
93
+ */
94
+ const attribute = `data-repl-sdk-ember-gmd-${elementId++}`;
95
+
96
+ element.setAttribute(attribute, '');
97
+
98
+ const [application, destroyable, resolver, router, route, testWaiters, runloop] =
99
+ await compiler.tryResolveAll([
100
+ '@ember/application',
101
+ '@ember/destroyable',
102
+ 'ember-resolver',
103
+ '@ember/routing/router',
104
+ '@ember/routing/route',
105
+ '@ember/test-waiters',
106
+ '@ember/runloop',
107
+ ]);
108
+
109
+ // We don't want to await here, because we need to early
110
+ // return the element so that the app can render in to it.
111
+ // (Ember will only render in to an element if it's present in the DOM)
112
+ const destroy = await renderApp({
113
+ element,
114
+ selector: `[${attribute}]`,
115
+ component: compiled,
116
+ log: compiler.announce,
117
+ modules: {
118
+ application,
119
+ destroyable,
120
+ resolver,
121
+ router,
122
+ route,
123
+ testWaiters,
124
+ runloop,
125
+ },
126
+ });
127
+
128
+ /**
129
+ * @type {(() => void)[]}
130
+ */
131
+ const destroyables = [];
132
+
133
+ await Promise.all(
134
+ /** @type {unknown[]} */ (extra.codeBlocks).map(async (/** @type {unknown} */ info) => {
135
+ /** @type {Record<string, unknown>} */
136
+ const infoObj = /** @type {Record<string, unknown>} */ (info);
137
+
138
+ if (
139
+ !api.canCompile(
140
+ /** @type {string} */ (infoObj.format),
141
+ /** @type {string} */ (infoObj.flavor)
142
+ )
143
+ ) {
144
+ return;
145
+ }
146
+
147
+ const flavor = /** @type {string} */ (infoObj.flavor);
148
+ const hasScope =
149
+ flavor === 'ember' || infoObj.format === 'gjs' || infoObj.format === 'hbs';
150
+ const subRender = await compiler.compile(
151
+ /** @type {string} */ (infoObj.format),
152
+ /** @type {string} */ (infoObj.code),
153
+ {
154
+ ...compiler.optionsFor(/** @type {string} */ (infoObj.format), flavor),
155
+ flavor: flavor,
156
+ // @ts-ignore
157
+ ...(hasScope
158
+ ? {
159
+ scope: extra.scope,
160
+ }
161
+ : {}),
162
+ }
163
+ );
164
+
165
+ const selector = `#${/** @type {string} */ (infoObj.placeholderId)}`;
166
+ const target = element.querySelector(selector);
167
+
168
+ assert(
169
+ `Could not find placeholder / target element (using selector: \`${selector}\`). ` +
170
+ `Could not render ${/** @type {string} */ (infoObj.format)} block.`,
171
+ target
172
+ );
173
+
174
+ destroyables.push(subRender.destroy);
175
+ target.appendChild(subRender.element);
176
+ })
177
+ );
178
+
179
+ return () => {
180
+ for (const subDestroy of destroyables) {
181
+ subDestroy();
182
+ }
183
+
184
+ destroy();
185
+ };
186
+ },
187
+ };
188
+
189
+ return gmdCompiler;
190
+ }
@@ -0,0 +1,98 @@
1
+ import { isRecord } from '../../utils.js';
2
+ import { renderApp } from './render-app-island.js';
3
+
4
+ let elementId = 0;
5
+
6
+ /**
7
+ * @param {unknown} [ options ]
8
+ * @returns {{ scope: Record<string, unknown> }}
9
+ */
10
+ function filterOptions(options) {
11
+ if (!isRecord(options)) {
12
+ return { scope: {} };
13
+ }
14
+
15
+ return {
16
+ scope: /** @type {Record<string, unknown>}*/ (options?.scope || {}),
17
+ };
18
+ }
19
+
20
+ /**
21
+ * @type {import('../../types.ts').CompilerConfig['compiler']}
22
+ */
23
+ export async function compiler(config, api) {
24
+ /**
25
+ * @type {import('../../types.ts').Compiler}
26
+ */
27
+ const hbsCompiler = {
28
+ compile: async (text, options) => {
29
+ const { template } = await api.tryResolve('@ember/template-compiler/runtime');
30
+
31
+ const component = template(text, {
32
+ scope: () => ({
33
+ ...filterOptions(config).scope,
34
+ ...filterOptions(options).scope,
35
+ }),
36
+ });
37
+
38
+ /**
39
+ * Some versions of ember implement the runtime template compiler incorrectly (albeit, correct enough for the constraints at the time).
40
+ * So we need to wait longer than a microtask queue request could take.
41
+ *
42
+ * To make sure that the template is compiled, and "component"
43
+ * has a value.
44
+ *
45
+ * See:
46
+ * - https://github.com/emberjs/ember.js/issues/20913
47
+ * - https://github.com/emberjs/ember.js/issues/20914
48
+ */
49
+ await new Promise(requestAnimationFrame);
50
+
51
+ /**
52
+ * Is this allowed here? or do I just return text,
53
+ * and do the above in 'render'
54
+ */
55
+ return component;
56
+ },
57
+ render: async (element, compiled, extra, compiler) => {
58
+ /**
59
+ *
60
+ * TODO: These will make things easier:
61
+ * https://github.com/emberjs/rfcs/pull/1099
62
+ * https://github.com/ember-cli/ember-addon-blueprint/blob/main/files/tests/test-helper.js
63
+ */
64
+ const attribute = `data-repl-sdk-ember-hbs-${elementId++}`;
65
+
66
+ element.setAttribute(attribute, '');
67
+
68
+ const [application, destroyable, resolver, router, route, testWaiters, runloop] =
69
+ await compiler.tryResolveAll([
70
+ '@ember/application',
71
+ '@ember/destroyable',
72
+ 'ember-resolver',
73
+ '@ember/routing/router',
74
+ '@ember/routing/route',
75
+ '@ember/test-waiters',
76
+ '@ember/runloop',
77
+ ]);
78
+
79
+ return renderApp({
80
+ element,
81
+ selector: `[${attribute}]`,
82
+ component: compiled,
83
+ log: compiler.announce,
84
+ modules: {
85
+ application,
86
+ destroyable,
87
+ resolver,
88
+ router,
89
+ route,
90
+ testWaiters,
91
+ runloop,
92
+ },
93
+ });
94
+ },
95
+ };
96
+
97
+ return hbsCompiler;
98
+ }
@@ -0,0 +1,83 @@
1
+ /** @type {any} */
2
+ let bootWaiter;
3
+ /** @type {any} */
4
+ let createWaiter;
5
+
6
+ /**
7
+ * Wait to boot the app until the Element is in the DOM.
8
+ * because This old way of making a whole app requires that the element
9
+ * be present in the app.
10
+ *
11
+ * We really need renderComponent(...)
12
+ * https://github.com/emberjs/ember.js/pull/20781
13
+ *
14
+ * @param {{
15
+ * element: Element,
16
+ * modules: { [name: string]: any},
17
+ * selector: string,
18
+ * log: (type: 'error' | 'info', message: string) => void;
19
+ * component: unknown
20
+ * }} options
21
+ */
22
+ export async function renderApp({ element, modules, selector, component, log }) {
23
+ const App = modules.application.default;
24
+ const registerDestructor = modules.destroyable.registerDestructor;
25
+ const destroy = modules.destroyable.destroy;
26
+ const Resolver = modules.resolver.default;
27
+ const Router = modules.router.default;
28
+ const Route = modules.route.default;
29
+ const schedule = modules.runloop.schedule;
30
+
31
+ bootWaiter ||= modules.testWaiters.buildWaiter('repl-output:waiting-for-boot');
32
+ createWaiter ||= modules.testWaiters.buildWaiter('repl-output:waiting-for-creation');
33
+
34
+ const bootToken = bootWaiter.beginAsync();
35
+ const createToken = createWaiter.beginAsync();
36
+
37
+ class EphemeralApp extends App {
38
+ modulePrefix = 'ephemeral-render-output';
39
+ rootElement = element;
40
+ Resolver = Resolver.withModules({
41
+ 'ephemeral-render-output/templates/application': { default: component },
42
+ 'ephemeral-render-output/routes/application': {
43
+ default: class Application extends Route {
44
+ /**
45
+ * @param {unknown[]} args
46
+ */
47
+ constructor(...args) {
48
+ super(...args);
49
+
50
+ registerDestructor(() => {
51
+ bootWaiter.endAsync(bootToken);
52
+ });
53
+ }
54
+ afterModel() {
55
+ schedule('afterRender', () => {
56
+ requestAnimationFrame(() => {
57
+ log('info', 'Ember Island Rendered');
58
+ bootWaiter.endAsync(bootToken);
59
+ createWaiter.endAsync(createToken);
60
+ });
61
+ });
62
+ }
63
+ },
64
+ },
65
+ 'ephemeral-render-output/router': {
66
+ default: class BoilerplateRouter extends Router {
67
+ location = 'none';
68
+ rootURL = '/';
69
+ },
70
+ },
71
+ });
72
+ }
73
+
74
+ log('info', 'Booting Ember Island');
75
+
76
+ const app = EphemeralApp.create({
77
+ rootElement: element,
78
+ });
79
+
80
+ return () => {
81
+ destroy(app);
82
+ };
83
+ }
@@ -0,0 +1,166 @@
1
+ /**
2
+ * @typedef {import('../types.ts').CompilerConfig} CompilerConfig
3
+ */
4
+
5
+ /**
6
+ * Other `@ember` (and `@glimmer`) packages are bundled in ember-source,
7
+ * and typecilaly use a build plugin to resolve from `@ember/*` imports.
8
+ */
9
+ const externalPackages = [
10
+ '@ember/test-helpers',
11
+ '@ember/string',
12
+ '@ember/test-waiters',
13
+ '@ember/render-modifiers',
14
+ '@glimmer/component',
15
+ ];
16
+
17
+ /**
18
+ * @param {string} id
19
+ * @returns {string | undefined | (() => Record<string, unknown>)}
20
+ */
21
+ function resolve(id) {
22
+ if (id === '@ember/template-compiler/runtime') {
23
+ return `https://esm.sh/*ember-source/dist/packages/@ember/template-compiler/runtime.js`;
24
+ }
25
+
26
+ const isExternalEmber = externalPackages.some((name) => id.startsWith(name));
27
+
28
+ if (isExternalEmber) {
29
+ return `https://esm.sh/*${id}`;
30
+ }
31
+
32
+ if (id.startsWith('@ember')) {
33
+ return `https://esm.sh/*ember-source/dist/packages/${id}`;
34
+ }
35
+
36
+ if (id.startsWith('@glimmer')) {
37
+ return `https://esm.sh/*ember-source/dist/dependencies/${id}.js`;
38
+ }
39
+
40
+ if (id.startsWith('@embroider/macros')) {
41
+ return () => ({
42
+ // passthrough, we are not doing dead-code-elimination
43
+
44
+ /**
45
+ * @param {unknown} x
46
+ */
47
+ macroCondition: (x) => Boolean(x),
48
+ // I *could* actually implement this
49
+ dependencySatisfies: () => true,
50
+ isDevelopingApp: () => true,
51
+ getGlobalConfig: () => ({
52
+ WarpDrive: {
53
+ debug: false,
54
+ env: {
55
+ DEBUG: false,
56
+ TESTING: false,
57
+ PRODUCTION: true,
58
+ },
59
+ activeLogging: false,
60
+ compatWith: '99.0',
61
+ features: {},
62
+ deprecations: {},
63
+ polyfillUUID: false,
64
+ includeDataAdapter: false,
65
+ },
66
+ }),
67
+ });
68
+ }
69
+ }
70
+
71
+ /**
72
+ Example:
73
+
74
+ Uncaught (in promise) Error: Assertion Failed: You attempted to update `count` on `Demo`, but it had already been used previously in the same computation. Attempting to update a value after using it in a computation can cause logical errors, infinite revalidation bugs, and performance issues, and is not supported.
75
+
76
+ `count` was first used:
77
+
78
+ - While rendering:
79
+ {{outlet}} for -top-level
80
+ -top-level
81
+ {{outlet}} for application
82
+ Demo
83
+ this.foos.value
84
+ this.foos
85
+
86
+ Stack trace for the update:
87
+ *
88
+ * @param {PromiseRejectionEvent} e
89
+ * @param {(message: string) => void} handle
90
+ */
91
+ function onUnhandled(e, handle) {
92
+ if (!e.reason?.message) return;
93
+
94
+ let reason = e.reason.message;
95
+
96
+ if (reason.includes('Stack trace for the update:')) {
97
+ reason += ' (see console)';
98
+ }
99
+
100
+ handle(reason);
101
+ }
102
+
103
+ /**
104
+ * @type {CompilerConfig}
105
+ */
106
+ export const gjs = {
107
+ resolve,
108
+ onUnhandled,
109
+ codemirror: {
110
+ lang: async () => {
111
+ const { gjs } = await import('codemirror-lang-glimmer-js');
112
+
113
+ return gjs();
114
+ },
115
+ },
116
+ compiler: async (...args) => {
117
+ const gjs = await import('./ember/gjs.js');
118
+
119
+ return gjs.compiler(...args);
120
+ },
121
+ };
122
+
123
+ /**
124
+ * @type {CompilerConfig}
125
+ */
126
+ export const hbs = {
127
+ resolve,
128
+ onUnhandled,
129
+ codemirror: {
130
+ lang: async () => {
131
+ const { glimmer } = await import('codemirror-lang-glimmer');
132
+
133
+ return glimmer();
134
+ },
135
+ },
136
+ compiler: async (...args) => {
137
+ const hbs = await import('./ember/hbs.js');
138
+
139
+ return hbs.compiler(...args);
140
+ },
141
+ };
142
+
143
+ /**
144
+ * @type {CompilerConfig}
145
+ */
146
+ export const gmd = {
147
+ resolve,
148
+ onUnhandled,
149
+ codemirror: {
150
+ lang: async () => {
151
+ const { glimdown } = await import('codemirror-lang-glimdown');
152
+
153
+ return glimdown();
154
+ },
155
+ support: async () => {
156
+ const { gjs } = await import('codemirror-lang-glimmer-js');
157
+
158
+ return [gjs().support];
159
+ },
160
+ },
161
+ compiler: async (...args) => {
162
+ const hbs = await import('./ember/gmd.js');
163
+
164
+ return hbs.compiler(...args);
165
+ },
166
+ };
@@ -0,0 +1,32 @@
1
+ import { assert } from '../utils.js';
2
+
3
+ /**
4
+ * @type {import('../types.ts').CompilerConfig}
5
+ */
6
+ export const js = {
7
+ codemirror: {
8
+ lang: async () => {
9
+ const { javascript } = await import('@codemirror/lang-javascript');
10
+
11
+ return javascript();
12
+ },
13
+ },
14
+ compiler: async (config, api) => {
15
+ return {
16
+ compile: async (text, options) => {
17
+ // No compiling needed. Just JS
18
+ return text;
19
+ },
20
+ render: async (element, fun, extra, compiler) => {
21
+ assert(
22
+ `js document must have a function for a default export. Instead received: ${typeof fun}`,
23
+ typeof fun === 'function'
24
+ );
25
+
26
+ await fun(element);
27
+
28
+ compiler.announce('info', 'Done');
29
+ },
30
+ };
31
+ },
32
+ };