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
package/src/types.ts CHANGED
@@ -1,62 +1,339 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import type { FileDescription } from 'tarparser';
3
+
4
+ export interface RequestAnswer {
5
+ inTarFile: string;
6
+ ext: string;
7
+ from: string;
8
+ }
9
+
10
+ export interface PublicMethods {
11
+ announce: (type: 'error' | 'info', message: string) => void;
12
+ getCompiler: (format: string, flavor?: string) => Promise<Compiler>;
13
+ getAllowedFormats: () => string[];
14
+ getFlavorsFor: (format: string) => string[];
15
+
16
+ /**
17
+ * Will try to use a shimmed import, or will return null.
18
+ */
19
+ tryResolve: (
20
+ moduleName: string,
21
+ fallback?: (moduleName?: string) => Promise<object | undefined>
22
+ ) => Promise<any>;
23
+
24
+ /**
25
+ * Import many modules in parallel.
26
+ * For each array entry,
27
+ * Will try to use a shimmed import, or will return null.
28
+ *
29
+ * Optionally accepts a fallback function for proxying to a CDN, if desired.
30
+ *
31
+ */
32
+ tryResolveAll: (
33
+ moduleNames: string[],
34
+ fallback?: (moduleName?: string) => Promise<unknown>
35
+ ) => Promise<any[]>;
36
+ compile: (
37
+ format: string,
38
+ text: string,
39
+ options?: {
40
+ flavor?: string;
41
+ fileName?: string;
42
+ }
43
+ ) => Promise<{ element: HTMLElement; destroy: () => void }>;
44
+
45
+ optionsFor: (
46
+ format: string,
47
+ flavor?: string
48
+ ) => {
49
+ resolve?: (id: string) => string | undefined;
50
+ versions?: { [packageName: string]: string };
51
+ needsLiveMeta?: boolean;
52
+ [option: string]: unknown;
53
+ };
54
+
55
+ /**
56
+ * Does this compiler support the given format?
57
+ */
58
+ canCompile: (
59
+ format: string,
60
+ flavor?: string
61
+ ) =>
62
+ | {
63
+ result: true;
64
+ }
65
+ | { result: false; reason: string };
66
+ }
67
+ export interface ResolvedCompilerOptions {
68
+ resolve: { [importPath: string]: unknown };
69
+ needsLiveMeta?: boolean;
70
+ versions?: { [packageName: string]: string };
71
+ userOptions?: Options['options'];
72
+ }
73
+
74
+ type CompileResult =
75
+ | string
76
+ | {
77
+ compiled: string;
78
+ [option: string]: unknown;
79
+ };
80
+
81
+ export interface Compiler {
82
+ /**
83
+ * Convert a string from "fileExtension" to standard JavaScript.
84
+ * This will be loaded as a module and then passed to the render method.
85
+ *
86
+ * You may return either just a string, or an object with a `compiled` property that is a string -- any additional properties will be passde through to the render function -- which may be useful if there is accompanying CSS.
87
+ */
88
+ compile: (text: string, options: Record<string, unknown>) => Promise<CompileResult>;
89
+
90
+ /**
91
+ * For the root of a node rendered for this compiler,
92
+ * how will this particular library / framework
93
+ * render in to the given element?
94
+ *
95
+ * Example:
96
+ * ```js
97
+ * {
98
+ * async compiler() {
99
+ * const { createRoot } = await import('react-dom/client');
100
+ *
101
+ * return {
102
+ * render(element, defaultExport) {
103
+ * const root = createRoot(element);
104
+ *
105
+ * root.render(defaultExport);
106
+ * },
107
+ * // ...
108
+ * }
109
+ * }
110
+ * }
111
+ * ```
112
+ *
113
+ * @param {HTMLElement} the element to render in to, this is provided by repl-sdk.
114
+ * @param {any} defaultExport the default export from the compiled module.
115
+ * @param {{ compiled: string } & Record<string, unknown>} extras the compiled string (for reference), as well as any extra information that may have been returned from the compile function.
116
+ */
117
+ render: (
118
+ element: HTMLElement,
119
+ defaultExport: unknown,
120
+ extras: { compiled: string } & Record<string, unknown>,
121
+ compiler: PublicMethods
122
+ ) => Promise<void | (() => void)>;
123
+
124
+ /**
125
+ * Sometimes libraries do not publish browser-compatible modules,
126
+ * and require additional transpilation.
127
+ * Usually this happens by the consuming application's build process -- but in this REPL,
128
+ * we are kind of not exactly a consuming application, but still need to handle the further
129
+ * build concerns.
130
+ *
131
+ * For example, `import.meta.env.DEV` is not a platform native thing that and requires
132
+ * a build plugin. Vite has one built in, but all other tools need to manually specify
133
+ * what to do with `import.meta.env.DEV`.
134
+ *
135
+ * Another example, some component frameworks may use templates, which can only be compiled
136
+ * by the host application, as the details of how a template is compiled are private API,
137
+ * and can vary in minor releases of the template compiler.
138
+ *
139
+ * This should be a map of file extensions to async functions that must return either the
140
+ * original file text or additionally transformed text.
141
+ *
142
+ * ```js
143
+ * handlers: {
144
+ * // When resolving a .js file from a CDN or NPM, replace `import.meta.env.DEV` with `true`
145
+ * js: async(text) => {
146
+ * return text.replaceAll('import.meta.env.DEV', 'true');
147
+ * }
148
+ * }
149
+ * ```
150
+ *
151
+ * This could also be used to run the same build step on fetched files as
152
+ * the files provided to the REPL.
153
+ *
154
+ * ```js
155
+ * let compiler = {
156
+ * compile: async (text) => { ... },
157
+ * render: async (element, compiled, ...rest) => { ... },
158
+ * handlers: {
159
+ * js: async(text) => {
160
+ * return compiler.compile(text);
161
+ * }
162
+ * }
163
+ * }
164
+ * ```
165
+ */
166
+ handlers?: {
167
+ [fileExtension: string]: (text: string) => Promise<CompileResult>;
168
+ };
169
+ }
170
+
171
+ export interface CompilerConfig {
172
+ /**
173
+ * Extensions for codemirror, providing syntax highlighting and other editor features (completions, etc)
174
+ */
175
+ codemirror: {
176
+ lang: () => Promise<unknown>;
177
+ support?: () => Promise<unknown>;
178
+ };
179
+
180
+ /**
181
+ * When using this file extension in markdown documents,
182
+ * should we only evaluate the code block if the "live"
183
+ * meta is attached to the codefence?
184
+ *
185
+ * If you don't use markdown-embedded rendering,
186
+ * you can ignore this option. Default behavior is "false",
187
+ * but it doesn't matter if you don't render markdown anyway.
188
+ *
189
+ * For example, with `needsLiveMeta: false`:
190
+ * \`\`\`js
191
+ * console.log('hello');
192
+ * \`\`\`
193
+ * will be evaluated and log to the console.
194
+ * However, with `needsLiveMeta: true`, the above snippet would not
195
+ * be evaluated. To evaluate a snippet with `needsLiveMeta: true`:
196
+ * \`\`\`js live
197
+ * console.log('hello');
198
+ * \`\`\`
199
+ */
200
+ needsLiveMeta?: boolean;
201
+
202
+ /**
203
+ * Synchronously extend the way resolution works
204
+ * This is "import map as a function", which allows for more flexibility than just the static import map.
205
+ *
206
+ * This may not return a promise.
207
+ * But we can return functions that return promises.
208
+ *
209
+ */
210
+ resolve?: (
211
+ id: string
212
+ ) =>
213
+ | string
214
+ | (() => Record<string, unknown>)
215
+ | (() => Promise<Record<string, unknown>>)
216
+ | undefined;
217
+
218
+ /**
219
+ * This optional method provides the opportunity to reformat messages that would be displayed to the REPL.
220
+ */
221
+ onUnhandled?: (e: PromiseRejectionEvent, handle: (msg: string) => void) => void;
222
+
223
+ compiler: (
224
+ /**
225
+ * The config for the compiler may be passed by the caller.
226
+ * Common use case for this object is specifying what versions
227
+ * of the compiler/library/framework dependencies to use.
228
+ */
229
+ config: { versions: { [packageName: string]: string }; [option: string]: unknown },
230
+ /**
231
+ * The public methods provided from the compiler
232
+ */
233
+ api: PublicMethods
234
+ ) => Promise<Compiler>;
235
+ }
236
+
1
237
  export interface Options {
238
+ /**
239
+ * Events will be emitted on this object.
240
+ * Not required, but could be useful for providing lots of feedback to the user.
241
+ */
242
+ on?: {
243
+ /**
244
+ * Public-log messages will be emitted here.
245
+ * These include:
246
+ * - errors
247
+ * - what is being fetched / compiled
248
+ */
249
+ log?: (type: 'error' | 'info', message: string) => void;
250
+ };
251
+
252
+ /**
253
+ * Map of pre-resolved JS values to use as the import map
254
+ * These could assume the role of runtime virtual modules.
255
+ *
256
+ * These will take precedence over the importMap, and implicit CDN fallback.
257
+ */
258
+ resolve?: { [importPath: string]: unknown };
259
+
260
+ /**
261
+ * Specifies which vesions of dependencies to when pulling from a CDN.
262
+ * Defaults to latest.
263
+ */
264
+ versions?: { [packageName: string]: string };
265
+
266
+ /**
267
+ * Map of file extensions to compiler configurations
268
+ */
2
269
  formats: {
3
- [fileExtension: string]: {
4
- /**
5
- * When using this file extension in markdown documents,
6
- * should we only evaluate the code block if the "live"
7
- * meta is attached to the codefence?
8
- *
9
- * If you don't use markdown-embedded rendering,
10
- * you can ignore this option. Default behavior is "false",
11
- * but it doesn't matter if you don't render markdown anyway.
12
- *
13
- * For example, with `needsLiveMeta: false`:
14
- * \`\`\`js
15
- * console.log('hello');
16
- * \`\`\`
17
- * will be evaluated and log to the console.
18
- * However, with `needsLiveMeta: true`, the above snippet would not
19
- * be evaluated. To evaluate a snippet with `needsLiveMeta: true`:
20
- * \`\`\`js live
21
- * console.log('hello');
22
- * \`\`\`
23
- */
24
- needsLiveMeta?: boolean;
25
- compiler: () => Promise<{
26
- /**
27
- * Convert a string from "fileExtension" to standard JavaScript.
28
- * This will be loaded as a module and then passed to the render method.
29
- */
30
- compile: (text: string) => Promise<string>;
31
- /**
32
- * For the root of a node rendered for this compiler,
33
- * how will this particular library / framework
34
- * render in to the given element?
35
- *
36
- * Example:
37
- * ```js
38
- * {
39
- * async compiler() {
40
- * const { createRoot } = await import('react-dom/client');
41
- *
42
- * return {
43
- * render(element, defaultExport) {
44
- * const root = createRoot(element);
45
- *
46
- * root.render(defaultExport);
47
- * },
48
- * // ...
49
- * }
50
- * }
51
- * }
52
- * ```
53
- *
54
- * @param {HTMLElement} the element to render in to, this is provided by repl-sdk.
55
- * @param {any} the default export from the compiled module.
56
- */
57
- render: (element: HTMLElement, defaultExport: any) => void;
58
- }>;
59
- }
60
- }
270
+ [fileExtension: string]:
271
+ | CompilerConfig
272
+ | {
273
+ [flavor: string]: CompilerConfig;
274
+ };
275
+ };
276
+
277
+ options?: {
278
+ [format: string]:
279
+ | KnownDefaultOptions
280
+ | {
281
+ [flavor: string]: { [option: string]: unknown };
282
+ }
283
+ | {
284
+ [option: string]: unknown;
285
+ };
286
+ };
287
+
288
+ /**
289
+ * Show extra debug logging or not
290
+ */
291
+ logging?: boolean | undefined;
292
+ }
293
+
294
+ interface KnownDefaultOptions {
295
+ md?: {
296
+ rehypePlugins?: unknown[];
297
+ remarkPlugins?: unknown[];
298
+ CopyComponent?: string;
299
+ ShadowComponent?: string;
300
+ };
301
+ }
302
+
303
+ import type { ImportMap as ManifestImports } from 'resolve.imports';
304
+
305
+ export interface UntarredPackage {
306
+ /**
307
+ * the package.json
308
+ */
309
+ manifest: {
310
+ name: string;
311
+ version: string;
312
+ exports?: ManifestExports;
313
+ imports?: Record<string, ManifestImports>;
314
+ main?: string;
315
+ module?: string;
316
+ browser?: string;
317
+ };
318
+ contents: {
319
+ [path: string]: FileDescription;
320
+ };
321
+ }
322
+
323
+ type ManifestExport = string | string[] | { [condition: string]: ManifestExport };
324
+
325
+ export interface ManifestExports {
326
+ [importPath: string]: ManifestExport;
327
+ }
328
+
329
+ export interface InfoMessage {
330
+ type: 'info';
331
+ message: string;
332
+ }
333
+
334
+ export interface ErrorMessage {
335
+ type: 'error';
336
+ message: string;
61
337
  }
62
338
 
339
+ export type Message = InfoMessage | ErrorMessage;
package/src/utils.js CHANGED
@@ -1,10 +1,37 @@
1
1
  /**
2
2
  * @param {string} message
3
3
  * @param {unknown} test
4
- * @asserts test
4
+ * @returns {asserts test}
5
5
  */
6
6
  export function assert(message, test) {
7
7
  if (!test) {
8
8
  throw new Error(message);
9
9
  }
10
10
  }
11
+
12
+ let i = 0;
13
+
14
+ export function nextId() {
15
+ i += 1;
16
+
17
+ return `repl_${i}`;
18
+ }
19
+
20
+ export const fakeDomain = 'repl.sdk';
21
+ export const tgzPrefix = 'file:///tgz.repl.sdk/';
22
+ export const unzippedPrefix = 'file:///tgz.repl.sdk/unzipped';
23
+
24
+ /**
25
+ * @param {string} url
26
+ */
27
+ export function prefix_tgz(url) {
28
+ return `${tgzPrefix}${url}`;
29
+ }
30
+
31
+ /**
32
+ * @param {unknown} x
33
+ * @returns {x is Record<string, unknown>}
34
+ */
35
+ export function isRecord(x) {
36
+ return typeof x === 'object' && x !== null && !Array.isArray(x);
37
+ }
@@ -1,73 +0,0 @@
1
- export declare class Compiler {
2
- constructor(options?: Options);
3
-
4
- compile(format: string, text: string): Promise<HTMLElement>;
5
- }
6
-
7
- export declare const defaultFormats: Options['formats'];
8
-
9
- export declare const defaults: Options;
10
-
11
- declare interface Options {
12
- formats: {
13
- [fileExtension: string]: {
14
- /**
15
- * When using this file extension in markdown documents,
16
- * should we only evaluate the code block if the "live"
17
- * meta is attached to the codefence?
18
- *
19
- * If you don't use markdown-embedded rendering,
20
- * you can ignore this option. Default behavior is "false",
21
- * but it doesn't matter if you don't render markdown anyway.
22
- *
23
- * For example, with `needsLiveMeta: false`:
24
- * \`\`\`js
25
- * console.log('hello');
26
- * \`\`\`
27
- * will be evaluated and log to the console.
28
- * However, with `needsLiveMeta: true`, the above snippet would not
29
- * be evaluated. To evaluate a snippet with `needsLiveMeta: true`:
30
- * \`\`\`js live
31
- * console.log('hello');
32
- * \`\`\`
33
- */
34
- needsLiveMeta?: boolean;
35
- compiler: () => Promise<{
36
- /**
37
- * Convert a string from "fileExtension" to standard JavaScript.
38
- * This will be loaded as a module and then passed to the render method.
39
- */
40
- compile: (text: string) => Promise<string>;
41
- /**
42
- * For the root of a node rendered for this compiler,
43
- * how will this particular library / framework
44
- * render in to the given element?
45
- *
46
- * Example:
47
- * ```js
48
- * {
49
- * async compiler() {
50
- * const { createRoot } = await import('react-dom/client');
51
- *
52
- * return {
53
- * render(element, defaultExport) {
54
- * const root = createRoot(element);
55
- *
56
- * root.render(defaultExport);
57
- * },
58
- * // ...
59
- * }
60
- * }
61
- * }
62
- * ```
63
- *
64
- * @param {HTMLElement} the element to render in to, this is provided by repl-sdk.
65
- * @param {any} the default export from the compiled module.
66
- */
67
- render: (element: HTMLElement, defaultExport: any) => void;
68
- }>;
69
- };
70
- };
71
- }
72
-
73
- export { }