modern-monaco 0.1.4 → 0.1.5
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 +125 -112
- package/dist/core.js +7 -2
- package/dist/editor-core.js +5 -0
- package/dist/lsp/typescript/worker.js +183 -182
- package/dist/shiki.js +10 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,15 +20,12 @@ Meet the modern version of [Monaco Editor](https://www.npmjs.com/package/monaco-
|
|
|
20
20
|
|
|
21
21
|
## Installation
|
|
22
22
|
|
|
23
|
-
You can install
|
|
23
|
+
You can install modern-monaco from NPM:
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
npm i modern-monaco
|
|
26
|
+
npm i modern-monaco
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
> [!Note]
|
|
30
|
-
> The `typescript` package is required by the JavaScript/TypeScript LSP worker. We recommend `typescript@5.5.x` or later.
|
|
31
|
-
|
|
32
29
|
Or import it from [esm.sh](https://esm.sh/) CDN in the browser without a build step:
|
|
33
30
|
|
|
34
31
|
```js
|
|
@@ -37,7 +34,7 @@ import * from "https://esm.sh/modern-monaco"
|
|
|
37
34
|
|
|
38
35
|
## Usage
|
|
39
36
|
|
|
40
|
-
|
|
37
|
+
modern-monaco provides three modes to create a browser-based code editor:
|
|
41
38
|
|
|
42
39
|
- **Lazy**: pre-highlight code with Shiki while loading the `editor-core.js` in the background.
|
|
43
40
|
- **SSR**: render a mock editor on the server side and hydrates it on the client side.
|
|
@@ -45,11 +42,11 @@ import * from "https://esm.sh/modern-monaco"
|
|
|
45
42
|
|
|
46
43
|
### Lazy Mode
|
|
47
44
|
|
|
48
|
-
[monaco-editor](https://www.npmjs.com/package/monaco-editor) is a large package with additional CSS/Worker modules that requires `MonacoEnvironment` setup for language service support.
|
|
45
|
+
[monaco-editor](https://www.npmjs.com/package/monaco-editor) is a large package with additional CSS/Worker modules that requires `MonacoEnvironment` setup for language service support. modern-monaco provides a simple yet smart way to load editor modules on demand.
|
|
49
46
|
|
|
50
|
-
By pre-highlighting code with Shiki while loading editor modules in the background,
|
|
47
|
+
By pre-highlighting code with Shiki while loading editor modules in the background, modern-monaco can significantly reduce loading screen time.
|
|
51
48
|
|
|
52
|
-
To create a Monaco editor lazily, you need to add a `<monaco-editor>` custom element in your app
|
|
49
|
+
To create a Monaco editor lazily, you need to add a `<monaco-editor>` custom element in the HTML of your app, then call the `lazy` function provided by modern-monaco. You may also need a `Workspace` object to manage editor models without calling the native Monaco APIs.
|
|
53
50
|
|
|
54
51
|
```html
|
|
55
52
|
<!-- index.html -->
|
|
@@ -64,8 +61,8 @@ import { lazy, Workspace } from "modern-monaco";
|
|
|
64
61
|
// create a workspace with initial files
|
|
65
62
|
const workspace = new Workspace({
|
|
66
63
|
initialFiles: {
|
|
67
|
-
"index.html": `<html>...</body></html>`,
|
|
68
|
-
"main.js": `console.log("Hello, world!")
|
|
64
|
+
"index.html": `<html><body>...</body></html>`,
|
|
65
|
+
"main.js": `console.log("Hello, world!")`,
|
|
69
66
|
},
|
|
70
67
|
entryFile: "index.html",
|
|
71
68
|
});
|
|
@@ -73,8 +70,9 @@ const workspace = new Workspace({
|
|
|
73
70
|
// initialize the editor lazily
|
|
74
71
|
await lazy({ workspace });
|
|
75
72
|
|
|
76
|
-
//
|
|
77
|
-
workspace.
|
|
73
|
+
// write a file and open it in the editor
|
|
74
|
+
workspace.fs.writeFile("util.js", "export function add(a, b) { return a + b; }");
|
|
75
|
+
workspace.openTextDocument("util.js");
|
|
78
76
|
```
|
|
79
77
|
|
|
80
78
|
### SSR Mode
|
|
@@ -86,29 +84,31 @@ import { renderToWebComponent } from "modern-monaco/ssr";
|
|
|
86
84
|
|
|
87
85
|
export default {
|
|
88
86
|
async fetch(req) {
|
|
89
|
-
const
|
|
87
|
+
const editorHTML = await renderToWebComponent(
|
|
90
88
|
`console.log("Hello, world!")`,
|
|
91
89
|
{
|
|
92
|
-
theme: "OneDark-Pro",
|
|
93
90
|
language: "javascript",
|
|
91
|
+
theme: "OneDark-Pro",
|
|
94
92
|
userAgent: req.headers.get("user-agent"), // detect default font for different platforms
|
|
95
93
|
},
|
|
96
94
|
);
|
|
97
95
|
return new Response(
|
|
98
96
|
html`
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
97
|
+
${editorHTML}
|
|
98
|
+
<script type="module">
|
|
99
|
+
import { hydrate } from "https://esm.sh/modern-monaco";
|
|
100
|
+
// hydrate the editor
|
|
101
|
+
hydrate();
|
|
102
|
+
</script>
|
|
103
|
+
`,
|
|
106
104
|
{ headers: { "Content-Type": "text/html" } },
|
|
107
105
|
);
|
|
108
106
|
},
|
|
109
107
|
};
|
|
110
108
|
```
|
|
111
109
|
|
|
110
|
+
SSR Demo: https://modern-monaco-demo.vercel.app ([Source](https://github.com/pi0/modern-monaco-demo) by [@pi0](https://github.com/pi0))
|
|
111
|
+
|
|
112
112
|
### Manual Mode
|
|
113
113
|
|
|
114
114
|
You can also create a [Monaco editor](https://microsoft.github.io/monaco-editor/docs.html) instance manually. It loads themes and language grammars automatically.
|
|
@@ -132,12 +132,12 @@ You can also create a [Monaco editor](https://microsoft.github.io/monaco-editor/
|
|
|
132
132
|
|
|
133
133
|
## Using Workspace
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
modern-monaco provides VSCode-like workspace features, such as edit history, file system provider, and more.
|
|
136
136
|
|
|
137
137
|
```js
|
|
138
138
|
import { lazy, Workspace } from "modern-monaco";
|
|
139
139
|
|
|
140
|
-
//
|
|
140
|
+
// create a workspace with initial files
|
|
141
141
|
const workspace = new Workspace({
|
|
142
142
|
/** The name of the workspace, used for project isolation. Default is "default". */
|
|
143
143
|
name: "project-name",
|
|
@@ -150,94 +150,19 @@ const workspace = new Workspace({
|
|
|
150
150
|
entryFile: "index.html",
|
|
151
151
|
});
|
|
152
152
|
|
|
153
|
-
//
|
|
153
|
+
// use the workspace in lazy mode
|
|
154
154
|
lazy({ workspace });
|
|
155
155
|
|
|
156
|
-
//
|
|
156
|
+
// open a file in the workspace
|
|
157
157
|
workspace.openTextDocument("main.js");
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
-
###
|
|
161
|
-
|
|
162
|
-
You can add a `tsconfig.json` file to configure the TypeScript compiler options for the TypeScript language service.
|
|
163
|
-
|
|
164
|
-
```js
|
|
165
|
-
const tsconfig = {
|
|
166
|
-
"compilerOptions": {
|
|
167
|
-
"strict": true,
|
|
168
|
-
"noUnusedLocals": true,
|
|
169
|
-
"noUnusedParameters": true,
|
|
170
|
-
"noFallthroughCasesInSwitch": true,
|
|
171
|
-
},
|
|
172
|
-
};
|
|
173
|
-
const workspace = new Workspace({
|
|
174
|
-
initialFiles: {
|
|
175
|
-
"tsconfig.json": JSON.stringify(tsconfig, null, 2),
|
|
176
|
-
},
|
|
177
|
-
});
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### Using Import Maps
|
|
160
|
+
### Custom Workspace FileSystem
|
|
181
161
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
```js
|
|
185
|
-
const indexHtml = html`<!DOCTYPE html>
|
|
186
|
-
<html>
|
|
187
|
-
<head>
|
|
188
|
-
<script type="importmap">
|
|
189
|
-
{
|
|
190
|
-
"imports": {
|
|
191
|
-
"react": "https://esm.sh/react@18",
|
|
192
|
-
"react-dom/": "https://esm.sh/react-dom@18/"
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
</script>
|
|
196
|
-
</head>
|
|
197
|
-
<body>
|
|
198
|
-
<div id="root"></div>
|
|
199
|
-
<script type="module" src="app.tsx"></script>
|
|
200
|
-
</body>
|
|
201
|
-
</html>
|
|
202
|
-
`;
|
|
203
|
-
const appTsx = `import { createRoot } from "react-dom/client";
|
|
204
|
-
|
|
205
|
-
createRoot(document.getElementById("root")).render(<div>Hello, world!</div>);
|
|
206
|
-
`;
|
|
207
|
-
|
|
208
|
-
const workspace = new Workspace({
|
|
209
|
-
initialFiles: {
|
|
210
|
-
"index.html": indexHtml,
|
|
211
|
-
"app.tsx": appTsx,
|
|
212
|
-
},
|
|
213
|
-
});
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
You can also provide an import map object as the `lsp.typescript.importMap` option in the `lazy`, `init`, or `hydrate` functions.
|
|
217
|
-
|
|
218
|
-
```js
|
|
219
|
-
lazy({
|
|
220
|
-
lsp: {
|
|
221
|
-
typescript: {
|
|
222
|
-
importMap: {
|
|
223
|
-
"react": "https://esm.sh/react@18",
|
|
224
|
-
"react-dom/": "https://esm.sh/react-dom@18/",
|
|
225
|
-
},
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
});
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
> [!Note]
|
|
232
|
-
> By default, `modern-monaco` uses `react` or `preact` in the `importmap` script as the `jsxImportSource` option for the TypeScript worker.
|
|
233
|
-
> To use a custom `jsxImportSource` option, add the `@jsxRuntime` specifier in the `importmap` script.
|
|
234
|
-
|
|
235
|
-
### Using Custom FileSystem
|
|
236
|
-
|
|
237
|
-
You can provide a custom filesystem implementation to override the default IndexedDB filesystem.
|
|
162
|
+
By default, modern-monaco uses `IndexedDB` as the workspace filesystem to persist the editor changes. With a custom filesystem, you can implement your own persistence logic.
|
|
238
163
|
|
|
239
164
|
```ts
|
|
240
|
-
import {
|
|
165
|
+
import { type FileSystem, lazy, Workspace } from "modern-monaco";
|
|
241
166
|
|
|
242
167
|
class CustomFileSystem implements FileSystem {
|
|
243
168
|
// Custom FileSystem implementation
|
|
@@ -250,11 +175,15 @@ const workspace = new Workspace({
|
|
|
250
175
|
},
|
|
251
176
|
customFS: new CustomFileSystem(),
|
|
252
177
|
});
|
|
178
|
+
|
|
179
|
+
lazy({ workspace });
|
|
253
180
|
```
|
|
254
181
|
|
|
182
|
+
Please refer to the [FileSystem](./types/workspace.d.ts#L54) interface for more details.
|
|
183
|
+
|
|
255
184
|
## Editor Theme & Language Grammars
|
|
256
185
|
|
|
257
|
-
|
|
186
|
+
modern-monaco uses [Shiki](https://shiki.style) for syntax highlighting with extensive grammars and themes. By default, it loads themes and grammars from esm.sh on demand.
|
|
258
187
|
|
|
259
188
|
### Setting the Editor Theme
|
|
260
189
|
|
|
@@ -275,7 +204,7 @@ lazy({
|
|
|
275
204
|
> [!Note]
|
|
276
205
|
> The theme ID should be one of the [Shiki Themes](https://shiki.style/themes).
|
|
277
206
|
|
|
278
|
-
|
|
207
|
+
modern-monaco loads the theme data from the CDN when a theme ID is provided. You can also use a theme from the `tm-themes` package:
|
|
279
208
|
|
|
280
209
|
```js
|
|
281
210
|
import OneDark from "tm-themes/themes/OneDark-Pro.json" with { type: "json" };
|
|
@@ -287,14 +216,14 @@ lazy({
|
|
|
287
216
|
|
|
288
217
|
### Pre-loading Language Grammars
|
|
289
218
|
|
|
290
|
-
By default,
|
|
219
|
+
By default, modern-monaco loads language grammars when a specific language mode is attached to the editor. You can also pre-load language grammars by adding the `langs` option to the `lazy`, `init`, or `hydrate` functions. The `langs` option is an array of language grammars, which can be a language grammar object, a language ID, or a URL to the language grammar.
|
|
291
220
|
|
|
292
221
|
```js
|
|
293
222
|
import markdown from "tm-grammars/markdown.json" with { type: "json" };
|
|
294
223
|
|
|
295
224
|
lazy({
|
|
296
225
|
langs: [
|
|
297
|
-
// load language grammars from CDN
|
|
226
|
+
// load language grammars from CDN, these language ids must be defined in the `tm-grammars` package
|
|
298
227
|
"html",
|
|
299
228
|
"css",
|
|
300
229
|
"javascript",
|
|
@@ -356,14 +285,14 @@ For manual mode, check [here](https://microsoft.github.io/monaco-editor/docs.htm
|
|
|
356
285
|
|
|
357
286
|
## Language Server Protocol (LSP)
|
|
358
287
|
|
|
359
|
-
|
|
288
|
+
modern-monaco by default supports full LSP features for the following languages:
|
|
360
289
|
|
|
361
290
|
- HTML
|
|
362
291
|
- CSS/SCSS/LESS
|
|
363
292
|
- JavaScript/TypeScript
|
|
364
293
|
- JSON
|
|
365
294
|
|
|
366
|
-
Additionally,
|
|
295
|
+
Additionally, modern-monaco supports features like:
|
|
367
296
|
|
|
368
297
|
- File System Provider for import completions
|
|
369
298
|
- Embedded languages in HTML
|
|
@@ -372,7 +301,7 @@ Additionally, `modern-monaco` supports features like:
|
|
|
372
301
|
|
|
373
302
|
> [!Note]
|
|
374
303
|
> You don't need to set `MonacoEnvironment.getWorker` for LSP support.
|
|
375
|
-
>
|
|
304
|
+
> modern-monaco automatically loads the required LSP workers.
|
|
376
305
|
|
|
377
306
|
### LSP Language Configuration
|
|
378
307
|
|
|
@@ -414,9 +343,93 @@ export interface LSPLanguageConfig {
|
|
|
414
343
|
}
|
|
415
344
|
```
|
|
416
345
|
|
|
346
|
+
### Import Maps
|
|
347
|
+
|
|
348
|
+
modern-monaco uses [import maps](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) to resolve **bare specifier** imports in JavaScript/TypeScript. By default, modern-monaco detects the `importmap` from the root `index.html` in the workspace.
|
|
349
|
+
|
|
350
|
+
```js
|
|
351
|
+
const indexHtml = html`<!DOCTYPE html>
|
|
352
|
+
<html>
|
|
353
|
+
<head>
|
|
354
|
+
<script type="importmap">
|
|
355
|
+
{
|
|
356
|
+
"imports": {
|
|
357
|
+
"react": "https://esm.sh/react@18",
|
|
358
|
+
"react-dom/": "https://esm.sh/react-dom@18/"
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
</script>
|
|
362
|
+
</head>
|
|
363
|
+
<body>
|
|
364
|
+
<div id="root"></div>
|
|
365
|
+
<script type="module" src="app.tsx"></script>
|
|
366
|
+
</body>
|
|
367
|
+
</html>
|
|
368
|
+
`;
|
|
369
|
+
const appTsx = `import { createRoot } from "react-dom/client";
|
|
370
|
+
|
|
371
|
+
createRoot(document.getElementById("root")).render(<div>Hello, world!</div>);
|
|
372
|
+
`;
|
|
373
|
+
|
|
374
|
+
const workspace = new Workspace({
|
|
375
|
+
initialFiles: {
|
|
376
|
+
"index.html": indexHtml,
|
|
377
|
+
"app.tsx": appTsx,
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
You can also provide an import map object as the `lsp.typescript.importMap` option in the `lazy`, `init`, or `hydrate` functions.
|
|
383
|
+
|
|
384
|
+
```js
|
|
385
|
+
lazy({
|
|
386
|
+
lsp: {
|
|
387
|
+
typescript: {
|
|
388
|
+
importMap: {
|
|
389
|
+
"react": "https://esm.sh/react@18",
|
|
390
|
+
"react-dom/": "https://esm.sh/react-dom@18/",
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Adding `tsconfig.json`
|
|
398
|
+
|
|
399
|
+
You can add a `tsconfig.json` file to configure the TypeScript compiler options for the TypeScript language service.
|
|
400
|
+
|
|
401
|
+
```js
|
|
402
|
+
const tsconfig = {
|
|
403
|
+
"compilerOptions": {
|
|
404
|
+
"target": "ES2022",
|
|
405
|
+
"strict": true,
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
const workspace = new Workspace({
|
|
409
|
+
initialFiles: {
|
|
410
|
+
"tsconfig.json": JSON.stringify(tsconfig, null, 2),
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
You can also manually add the TypeScript compiler options as the `lsp.typescript.compilerOptions` option in the `lazy`, `init`, or `hydrate` functions.
|
|
416
|
+
|
|
417
|
+
```js
|
|
418
|
+
lazy({
|
|
419
|
+
lsp: {
|
|
420
|
+
typescript: {
|
|
421
|
+
compilerOptions: {
|
|
422
|
+
target: "ES2022",
|
|
423
|
+
strict: true,
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
```
|
|
429
|
+
|
|
417
430
|
## Using the `core` Module
|
|
418
431
|
|
|
419
|
-
|
|
432
|
+
modern-monaco includes built-in grammars and LSP providers for HTML, CSS, JavaScript/TypeScript, and JSON. If you don't need these features, you can use the `modern-monaco/core` sub-module to reduce the bundle size.
|
|
420
433
|
|
|
421
434
|
```js
|
|
422
435
|
import { lazy } from "modern-monaco/core";
|
package/dist/core.js
CHANGED
|
@@ -11,7 +11,7 @@ function createWebWorker(url, name) {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// src/core.ts
|
|
14
|
-
import { getLanguageIdFromPath, initShiki, setDefaultWasmLoader, tmGrammars, tmThemes } from "./shiki.js";
|
|
14
|
+
import { getExtnameFromLanguageId, getLanguageIdFromPath, initShiki, setDefaultWasmLoader, tmGrammars, tmThemes } from "./shiki.js";
|
|
15
15
|
import { initShikiMonacoTokenizer, registerShikiMonacoTokenizer } from "./shiki.js";
|
|
16
16
|
import { render } from "./shiki.js";
|
|
17
17
|
import { getWasmInstance } from "./shiki-wasm.js";
|
|
@@ -281,6 +281,10 @@ async function lazy(options, hydrate2) {
|
|
|
281
281
|
} catch (error) {
|
|
282
282
|
if (error instanceof ErrorNotFound) {
|
|
283
283
|
if (code) {
|
|
284
|
+
const dirname = filename.split("/").slice(0, -1).join("/");
|
|
285
|
+
if (dirname) {
|
|
286
|
+
await workspace.fs.createDirectory(dirname);
|
|
287
|
+
}
|
|
284
288
|
await workspace.fs.writeFile(filename, code);
|
|
285
289
|
workspace._openTextDocument(filename, editor);
|
|
286
290
|
} else {
|
|
@@ -355,7 +359,8 @@ async function loadMonaco(highlighter, workspace, lsp, onDidEditorWorkerResolve)
|
|
|
355
359
|
}
|
|
356
360
|
return worker;
|
|
357
361
|
},
|
|
358
|
-
getLanguageIdFromUri: (uri) => getLanguageIdFromPath(uri.path)
|
|
362
|
+
getLanguageIdFromUri: (uri) => getLanguageIdFromPath(uri.path),
|
|
363
|
+
getExtnameFromLanguageId
|
|
359
364
|
});
|
|
360
365
|
monaco.editor.registerLinkOpener({
|
|
361
366
|
async open(link) {
|
package/dist/editor-core.js
CHANGED
|
@@ -177729,6 +177729,11 @@ Object.assign(editor, {
|
|
|
177729
177729
|
if (!language2 && uri) {
|
|
177730
177730
|
language2 = MonacoEnvironment.getLanguageIdFromUri?.(uri);
|
|
177731
177731
|
}
|
|
177732
|
+
if (!uri) {
|
|
177733
|
+
const extname3 = MonacoEnvironment.getExtnameFromLanguageId?.(language2) ?? "txt";
|
|
177734
|
+
const uuid = Math.round((Date.now() + Math.random()) * 1e3).toString(36);
|
|
177735
|
+
uri = "file:///.inmemory/" + uuid + "." + extname3;
|
|
177736
|
+
}
|
|
177732
177737
|
return createModel2(value, language2, uri);
|
|
177733
177738
|
},
|
|
177734
177739
|
getModel: (uri) => {
|