Skip to content

Commit 97f698f

Browse files
johnsoncodehksxzz
andauthored
feat: download TS dynamically (vuejs#125)
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
1 parent f9fedcd commit 97f698f

File tree

3 files changed

+92
-39
lines changed

3 files changed

+92
-39
lines changed

src/monaco/env.ts

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ export function initMonaco(store: Store) {
4343
// Support for go to definition
4444
monaco.editor.registerEditorOpener({
4545
openCodeEditor(_, resource) {
46-
4746
if (resource.scheme === 'https') {
4847
// ignore cdn files
4948
return true
@@ -75,44 +74,29 @@ export class WorkerHost {
7574
}
7675
}
7776

78-
async function fetchJson<T>(url: string) {
79-
try {
80-
const res = await fetch(url);
81-
if (res.status === 200) {
82-
return await res.json();
83-
}
84-
} catch {
85-
// ignore
86-
}
87-
}
88-
8977
let disposeVue: undefined | (() => void)
9078
export async function reloadVue(store: Store) {
9179
disposeVue?.()
9280

93-
const locale = navigator.language.toLowerCase()
94-
const tsLocalized = await fetchJson(`https://cdn.jsdelivr.net/npm/typescript/lib/${locale}/diagnosticMessages.generated.json`)
9581
const worker = editor.createWebWorker<any>({
9682
moduleId: 'vs/language/vue/vueWorker',
9783
label: 'vue',
9884
host: new WorkerHost(),
9985
createData: {
100-
locale: locale,
101-
tsLocalized: tsLocalized,
10286
tsconfig: store.getTsConfig?.() || {},
10387
dependencies: !store.vueVersion
10488
? {}
10589
: {
106-
vue: store.vueVersion,
107-
'@vue/compiler-core': store.vueVersion,
108-
'@vue/compiler-dom': store.vueVersion,
109-
'@vue/compiler-sfc': store.vueVersion,
110-
'@vue/compiler-ssr': store.vueVersion,
111-
'@vue/reactivity': store.vueVersion,
112-
'@vue/runtime-core': store.vueVersion,
113-
'@vue/runtime-dom': store.vueVersion,
114-
'@vue/shared': store.vueVersion,
115-
}
90+
vue: store.vueVersion,
91+
'@vue/compiler-core': store.vueVersion,
92+
'@vue/compiler-dom': store.vueVersion,
93+
'@vue/compiler-sfc': store.vueVersion,
94+
'@vue/compiler-ssr': store.vueVersion,
95+
'@vue/reactivity': store.vueVersion,
96+
'@vue/runtime-core': store.vueVersion,
97+
'@vue/runtime-dom': store.vueVersion,
98+
'@vue/shared': store.vueVersion,
99+
},
116100
} satisfies CreateData,
117101
})
118102
const languageId = ['vue', 'javascript', 'typescript']
@@ -151,7 +135,17 @@ export function loadMonacoEnv(store: Store) {
151135
;(self as any).MonacoEnvironment = {
152136
async getWorker(_: any, label: string) {
153137
if (label === 'vue') {
154-
return new vueWorker()
138+
const worker = new vueWorker()
139+
const init = new Promise<void>((resolve) => {
140+
worker.addEventListener('message', (data) => {
141+
if (data.data === 'inited') {
142+
resolve()
143+
}
144+
})
145+
worker.postMessage({ event: 'init', tsVersion: store.state.typescriptVersion })
146+
})
147+
await init
148+
return worker
155149
}
156150
return new editorWorker()
157151
},

src/monaco/vue.worker.ts

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,64 @@
11
// @ts-ignore
22
import * as worker from 'monaco-editor-core/esm/vs/editor/editor.worker'
33
import type * as monaco from 'monaco-editor-core'
4-
import * as ts from 'typescript'
5-
import { createJsDelivrFs, createJsDelivrUriResolver, decorateServiceEnvironment } from '@volar/cdn'
4+
import {
5+
createJsDelivrFs,
6+
createJsDelivrUriResolver,
7+
decorateServiceEnvironment,
8+
} from '@volar/cdn'
69
import { VueCompilerOptions, resolveConfig } from '@vue/language-service'
7-
import { createLanguageService, createLanguageHost, createServiceEnvironment } from '@volar/monaco/worker'
10+
import {
11+
createLanguageService,
12+
createLanguageHost,
13+
createServiceEnvironment,
14+
} from '@volar/monaco/worker'
815
import type { WorkerHost } from './env'
916

1017
export interface CreateData {
11-
locale: string
12-
tsLocalized: any
1318
tsconfig: {
14-
compilerOptions?: ts.CompilerOptions
19+
compilerOptions?: import('typescript').CompilerOptions
1520
vueCompilerOptions?: Partial<VueCompilerOptions>
1621
}
1722
dependencies: {}
1823
}
1924

20-
self.onmessage = () => {
25+
const locale = navigator.language.toLowerCase()
26+
27+
let ts: typeof import('typescript')
28+
let tsLocalized: any
29+
30+
self.onmessage = async (msg) => {
31+
if (msg.data?.event === 'init') {
32+
;[ts, tsLocalized] = await Promise.all([
33+
importTsFromCdn(msg.data.tsVersion),
34+
fetchJson(
35+
`https://cdn.jsdelivr.net/npm/typescript@${msg.data.tsVersion}/lib/${locale}/diagnosticMessages.generated.json`
36+
),
37+
])
38+
self.postMessage('inited')
39+
return
40+
}
2141
worker.initialize(
2242
(
2343
ctx: monaco.worker.IWorkerContext<WorkerHost>,
24-
{ tsconfig, dependencies, locale, tsLocalized }: CreateData
44+
{ tsconfig, dependencies }: CreateData
2545
) => {
2646
const { options: compilerOptions } = ts.convertCompilerOptionsFromJson(
2747
tsconfig?.compilerOptions || {},
2848
''
2949
)
30-
const env = createServiceEnvironment();
31-
const host = createLanguageHost(ctx.getMirrorModels, env, '/src', compilerOptions)
50+
const env = createServiceEnvironment()
51+
const host = createLanguageHost(
52+
ctx.getMirrorModels,
53+
env,
54+
'/src',
55+
compilerOptions
56+
)
3257
const jsDelivrFs = createJsDelivrFs(ctx.host.onFetchCdnFile)
33-
const jsDelivrUriResolver = createJsDelivrUriResolver('/node_modules', dependencies)
58+
const jsDelivrUriResolver = createJsDelivrUriResolver(
59+
'/node_modules',
60+
dependencies
61+
)
3462

3563
if (locale) {
3664
env.locale = locale
@@ -55,3 +83,24 @@ self.onmessage = () => {
5583
}
5684
)
5785
}
86+
87+
async function importTsFromCdn(tsVersion: string) {
88+
const _module = globalThis.module
89+
;(globalThis as any).module = { exports: {} }
90+
const tsUrl = `https://cdn.jsdelivr.net/npm/typescript@${tsVersion}/lib/typescript.js`
91+
await import(/* @vite-ignore */ tsUrl)
92+
const ts = module.exports
93+
globalThis.module = _module
94+
return ts as typeof import('typescript')
95+
}
96+
97+
async function fetchJson<T>(url: string) {
98+
try {
99+
const res = await fetch(url)
100+
if (res.status === 200) {
101+
return await res.json()
102+
}
103+
} catch {
104+
// ignore
105+
}
106+
}

src/store.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export interface StoreState {
8585
errors: (string | Error)[]
8686
vueRuntimeURL: string
8787
vueServerRendererURL: string
88+
typescriptVersion: string
8889
// used to force reset the sandbox
8990
resetFlip: boolean
9091
}
@@ -166,6 +167,7 @@ export class ReplStore implements Store {
166167
errors: [],
167168
vueRuntimeURL: this.defaultVueRuntimeURL,
168169
vueServerRendererURL: this.defaultVueServerRendererURL,
170+
typescriptVersion: 'latest',
169171
resetFlip: true,
170172
})
171173

@@ -182,7 +184,10 @@ export class ReplStore implements Store {
182184
)
183185

184186
watch(
185-
() => this.state.files[tsconfigFile]?.code,
187+
() => [
188+
this.state.files[tsconfigFile]?.code,
189+
this.state.typescriptVersion,
190+
],
186191
() => reloadVue(this)
187192
)
188193

@@ -384,6 +389,11 @@ export class ReplStore implements Store {
384389
this.state.files[importMapFile]!.code = JSON.stringify(map, null, 2)
385390
}
386391

392+
setTypeScriptVersion(version: string) {
393+
this.state.typescriptVersion = version
394+
console.info(`[@vue/repl] Now using TypeScript version: ${version}`)
395+
}
396+
387397
async setVueVersion(version: string) {
388398
this.vueVersion = version
389399
const compilerUrl = `https://cdn.jsdelivr.net/npm/@vue/compiler-sfc@${version}/dist/compiler-sfc.esm-browser.js`

0 commit comments

Comments
 (0)