Skip to content

Commit 74df796

Browse files
committed
chore: upgrade monaco editor bundle
1 parent c9386ec commit 74df796

10 files changed

Lines changed: 128 additions & 45 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ Two additional zero-config flows ship as separate modules: **Easy Cloud** for on
175175
- **Unsaved indicators** — Visual dot on tabs with pending changes
176176
- **5 editor themes** — Auto (system, follows OS dark/light preference), Dark, Light, Catppuccin Mocha, OLED
177177
- **Status bar** — Line count and cursor position display
178-
- **IntelliSense** — Autocomplete for GM API functions and `@metadata` directives
178+
- **IntelliSense** — Autocomplete for GM API functions and `@metadata` directives, backed by generated ScriptVault GM declarations in the Monaco sandbox
179179
- Code folding, bracket matching, bracket pair colorization, auto-close
180180
- Search & replace (`Ctrl+F` / `Ctrl+H`)
181181
- Real-time userscript metadata linting

ROADMAP.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -298,13 +298,6 @@ _(All Now-tier items are credential/compliance blocked — see `Roadmap_Blocked.
298298
- **Deliverable:** Partition ScriptStorage, ScriptValues, and BackupScheduler into separate storage buckets. Measure write throughput for libraries >500 scripts. Fall back to single-bucket on browsers that don't support the Storage Buckets API.
299299
- **Acceptance:** `navigator.storageBuckets` feature-detected. Backup restore and sync merge still work after partitioning. Large-library perf harness shows improvement.
300300

301-
### RD-11. Monaco v0.55+ Upgrade for LSP Namespace
302-
- **Tier:** Under Consideration | **Priority:** P3 | **Impact:** 3 | **Effort:** M
303-
- **Source:** Monaco ESM migration plan, Monaco v0.55 changelog (LSP namespace)
304-
- **Problem:** ScriptVault ships Monaco v0.52.2. v0.55+ adds a native LSP namespace that could improve userscript IntelliSense beyond the current autocomplete (GM API completions, @metadata directive completions). v0.55 also renamed `EditorAutoClosingOvertypeStrategy` to `EditorAutoClosingEditStrategy`.
305-
- **Deliverable:** Evaluate: does the LSP namespace enable richer userscript editing (e.g., type-aware GM API completions from `lib/scriptvault.d.ts`)? If yes, upgrade Monaco, update ESM build budget, and verify sandbox CSP compatibility. If no, defer until a compelling reason appears.
306-
- **Risk:** Breaking API changes in v0.55. ESM bundle size may grow. Requires separate dependency-budget and browser-proof cycle.
307-
308301
### Appendix: Research-Driven Sources
309302

310303
| ID | Source | URL |

docs/audit/monaco-esm-prototype-2026-06-06.json

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,58 @@
11
{
2-
"generatedAt": "2026-06-06T20:00:34.508Z",
2+
"generatedAt": "2026-06-28T02:34:13.452Z",
33
"root": "lib/monaco-esm",
44
"sizeBudgets": {
5-
"maxTotalBytes": 26000000,
5+
"maxTotalBytes": 28000000,
66
"maxTotalGzipBytes": 5000000,
77
"maxFileBytes": {
8-
"lib/monaco-esm/editor.js": 9000000,
9-
"lib/monaco-esm/workers/ts.worker.js": 13000000
8+
"lib/monaco-esm/editor.js": 9600000,
9+
"lib/monaco-esm/workers/ts.worker.js": 13500000
1010
}
1111
},
1212
"totals": {
13-
"totalBytes": 25186387,
14-
"totalGzipBytes": 4279263
13+
"totalBytes": 27717062,
14+
"totalGzipBytes": 4702243
1515
},
1616
"outputs": [
1717
{
18-
"path": "lib/monaco-esm/assets/codicon-37A3DWZT.ttf",
19-
"bytes": 80340,
20-
"gzipBytes": 44393
18+
"path": "lib/monaco-esm/assets/codicon-LN6W7LCM.ttf",
19+
"bytes": 121972,
20+
"gzipBytes": 59203
2121
},
2222
{
2323
"path": "lib/monaco-esm/editor.css",
24-
"bytes": 158378,
25-
"gzipBytes": 23224
24+
"bytes": 172300,
25+
"gzipBytes": 25407
2626
},
2727
{
2828
"path": "lib/monaco-esm/editor.js",
29-
"bytes": 8231464,
30-
"gzipBytes": 1433472
29+
"bytes": 9405086,
30+
"gzipBytes": 1618107
3131
},
3232
{
3333
"path": "lib/monaco-esm/workers/css.worker.js",
34-
"bytes": 1883186,
35-
"gzipBytes": 303112
34+
"bytes": 1919513,
35+
"gzipBytes": 308677
3636
},
3737
{
3838
"path": "lib/monaco-esm/workers/editor.worker.js",
39-
"bytes": 555624,
40-
"gzipBytes": 116865
39+
"bytes": 589593,
40+
"gzipBytes": 121849
4141
},
4242
{
4343
"path": "lib/monaco-esm/workers/html.worker.js",
44-
"bytes": 1264765,
45-
"gzipBytes": 242013
44+
"bytes": 1300500,
45+
"gzipBytes": 247008
4646
},
4747
{
4848
"path": "lib/monaco-esm/workers/json.worker.js",
49-
"bytes": 856164,
50-
"gzipBytes": 170016
49+
"bytes": 891272,
50+
"gzipBytes": 175214
5151
},
5252
{
5353
"path": "lib/monaco-esm/workers/ts.worker.js",
54-
"bytes": 12156466,
55-
"gzipBytes": 1946168
54+
"bytes": 13316826,
55+
"gzipBytes": 2146778
5656
}
5757
],
5858
"failures": []

package-lock.json

Lines changed: 40 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,14 @@
100100
"esbuild": "^0.28.0",
101101
"fake-indexeddb": "^6.2.5",
102102
"jsdom": "^29.0.1",
103-
"monaco-editor": "^0.52.0",
103+
"monaco-editor": "^0.55.1",
104104
"puppeteer-core": "^25.2.1",
105105
"typescript": "^6.0.2",
106106
"vitest": "^4.1.9",
107107
"web-ext": "^10.3.0"
108108
},
109109
"overrides": {
110+
"dompurify": "3.4.11",
110111
"web-ext": {
111112
"fx-runner": "1.5.0"
112113
}

pages/editor-sandbox.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
function initMonaco(monaco) {
8787
if (!monaco || !monaco.editor || !monaco.languages) throw new Error('Monaco ESM API unavailable');
8888
monacoApi = monaco;
89+
registerScriptVaultTypes(monaco);
8990
// ── Custom userscript language extension ──────────────────────────────
9091
// Register @metadata keywords in the completion provider
9192
monaco.languages.registerCompletionItemProvider('javascript', {
@@ -373,6 +374,18 @@
373374
}
374375

375376
// ── Message handler (from dashboard) ──────────────────────────────────────
377+
async function registerScriptVaultTypes(monaco) {
378+
const defaults = monaco.typescript?.javascriptDefaults || monaco.languages?.typescript?.javascriptDefaults;
379+
if (!defaults || typeof defaults.addExtraLib !== 'function' || typeof fetch !== 'function') return;
380+
381+
try {
382+
const response = await fetch('../lib/scriptvault.d.ts', { cache: 'force-cache' });
383+
if (!response || !response.ok) return;
384+
const source = await response.text();
385+
defaults.addExtraLib(source, 'file:///scriptvault/lib/scriptvault.d.ts');
386+
} catch (_) { /* local type hints are best-effort; editor loading must not fail */ }
387+
}
388+
376389
window.addEventListener('message', function (e) {
377390
const msg = e.data;
378391
if (!msg || typeof msg !== 'object') return;

scripts/check-monaco-esm-prototype.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ const REQUIRED_OUTPUTS = [
1818
];
1919

2020
export const DEFAULT_SIZE_BUDGETS = Object.freeze({
21-
maxTotalBytes: 26_000_000,
21+
maxTotalBytes: 28_000_000,
2222
maxTotalGzipBytes: 5_000_000,
2323
maxFileBytes: Object.freeze({
24-
'lib/monaco-esm/editor.js': 9_000_000,
25-
'lib/monaco-esm/workers/ts.worker.js': 13_000_000,
24+
'lib/monaco-esm/editor.js': 9_600_000,
25+
'lib/monaco-esm/workers/ts.worker.js': 13_500_000,
2626
}),
2727
});
2828

scripts/check-monaco-package-contract.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ function checkPlan(files, failures) {
195195
'npm run build:monaco',
196196
'npm run monaco:esm:check',
197197
'docs/audit/monaco-esm-prototype-2026-06-06.json',
198-
'maxTotalBytes: 26000000',
198+
'maxTotalBytes: 28000000',
199199
'maxTotalGzipBytes: 5000000',
200200
'lib/monaco-esm/editor.js',
201201
'lib/monaco-esm/workers/ts.worker.js',

tests/monaco-esm-plan.test.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,31 @@ describe('Monaco ESM migration plan', () => {
2727
expect(plan).toContain(heading);
2828
}
2929

30-
expect(plan).toContain('monaco-editor@^0.52.0');
30+
expect(plan).toContain('monaco-editor@^0.55.1');
3131
expect(plan).toContain('Monaco 0.53.0 deprecated AMD support');
32-
expect(plan).toContain('Monaco 0.55.0');
32+
expect(plan).toContain('Monaco 0.55.1');
3333
expect(plan).toContain('lib/monaco-esm/editor.js');
3434
expect(plan).toContain('lib/monaco-esm/workers/');
3535
});
3636

37+
it('pins the Monaco 0.55 namespace contract used by the sandbox', () => {
38+
const packageJson = JSON.parse(read('package.json'));
39+
const packageLock = JSON.parse(read('package-lock.json'));
40+
const editorTypes = read('node_modules/monaco-editor/esm/vs/editor/editor.main.d.ts');
41+
const apiTypes = read('node_modules/monaco-editor/esm/vs/editor/editor.api.d.ts');
42+
const sandbox = read('pages/editor-sandbox.html');
43+
44+
expect(packageJson.devDependencies['monaco-editor']).toBe('^0.55.1');
45+
expect(packageLock.packages['node_modules/monaco-editor'].version).toBe('0.55.1');
46+
expect(packageJson.overrides.dompurify).toBe('3.4.11');
47+
expect(editorTypes).toContain('as lsp');
48+
expect(editorTypes).toContain('as typescript');
49+
expect(apiTypes).toContain('EditorAutoClosingEditStrategy');
50+
expect(apiTypes).not.toContain('EditorAutoClosingOvertypeStrategy');
51+
expect(sandbox).toContain('monaco.typescript?.javascriptDefaults');
52+
expect(sandbox).not.toContain('monaco.languages.typescript');
53+
});
54+
3755
it('keeps extension packaging constraints explicit', () => {
3856
const plan = read('docs/monaco-esm-migration-plan.md');
3957

tests/monaco-esm-sandbox-loader.test.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ function sandboxScript() {
1515
}
1616

1717
function createFakeMonaco() {
18+
const javascriptDefaults = {
19+
addExtraLib: vi.fn(),
20+
};
1821
const editorInstance = {
1922
addCommand: vi.fn(),
2023
onDidChangeModelContent: vi.fn(),
@@ -43,6 +46,9 @@ function createFakeMonaco() {
4346
CompletionItemKind: { Keyword: 1, Value: 2 },
4447
registerCompletionItemProvider: vi.fn(),
4548
},
49+
typescript: {
50+
javascriptDefaults,
51+
},
4652
editor: {
4753
EditorOption: { wordWrap: 'wordWrap' },
4854
create: vi.fn(() => editorInstance),
@@ -53,7 +59,7 @@ function createFakeMonaco() {
5359
};
5460
}
5561

56-
function createHarness({ moduleFails = false, stylesheetFails = false } = {}) {
62+
function createHarness({ moduleFails = false, stylesheetFails = false, typeDefinitions = 'declare const GM_info: unknown;' } = {}) {
5763
const messages = [];
5864
const appendedLinks = [];
5965
const loading = { style: {}, innerHTML: '' };
@@ -90,6 +96,11 @@ function createHarness({ moduleFails = false, stylesheetFails = false } = {}) {
9096
Promise,
9197
queueMicrotask,
9298
location: { origin: 'https://scriptvault.local' },
99+
fetch: vi.fn(async (url) => ({
100+
ok: true,
101+
url,
102+
text: async () => typeDefinitions,
103+
})),
93104
parent: {
94105
postMessage(message) {
95106
messages.push(message);
@@ -109,7 +120,7 @@ function createHarness({ moduleFails = false, stylesheetFails = false } = {}) {
109120

110121
async function runSandbox(context) {
111122
vm.runInNewContext(sandboxScript(), context);
112-
for (let i = 0; i < 6; i += 1) await Promise.resolve();
123+
for (let i = 0; i < 12; i += 1) await Promise.resolve();
113124
}
114125

115126
describe('Monaco ESM sandbox loader', () => {
@@ -128,6 +139,18 @@ describe('Monaco ESM sandbox loader', () => {
128139
expect(harness.messages).toContainEqual({ type: 'ready' });
129140
});
130141

142+
it('loads ScriptVault GM declarations through the Monaco TypeScript namespace', async () => {
143+
const harness = createHarness({ typeDefinitions: 'declare function GM_getValue(name: string): unknown;' });
144+
145+
await runSandbox(harness.context);
146+
147+
expect(harness.context.fetch).toHaveBeenCalledWith('../lib/scriptvault.d.ts', { cache: 'force-cache' });
148+
expect(harness.monaco.typescript.javascriptDefaults.addExtraLib).toHaveBeenCalledWith(
149+
'declare function GM_getValue(name: string): unknown;',
150+
'file:///scriptvault/lib/scriptvault.d.ts',
151+
);
152+
});
153+
131154
it('posts the existing fallback message when the ESM bundle is missing', async () => {
132155
const harness = createHarness({ moduleFails: true });
133156

0 commit comments

Comments
 (0)