diff --git a/src/monaco/env.ts b/src/monaco/env.ts index 8074bba9..2fc02630 100644 --- a/src/monaco/env.ts +++ b/src/monaco/env.ts @@ -122,6 +122,10 @@ export interface WorkerMessage { event: 'init' tsVersion: string tsLocale?: string + pkgDirUrl?: string + pkgFileTextUrl?: string + pkgLatestVersionUrl?: string + typescriptLib?: string } export function loadMonacoEnv(store: Store) { @@ -135,11 +139,27 @@ export function loadMonacoEnv(store: Store) { resolve() } }) - worker.postMessage({ + + const { + pkgDirUrl, + pkgFileTextUrl, + pkgLatestVersionUrl, + typescriptLib, + } = store.resourceLinks || {} + + const message: WorkerMessage = { event: 'init', tsVersion: store.typescriptVersion, tsLocale: store.locale, - } satisfies WorkerMessage) + pkgDirUrl: pkgDirUrl ? String(pkgDirUrl) : undefined, + pkgFileTextUrl: pkgFileTextUrl ? String(pkgFileTextUrl) : undefined, + pkgLatestVersionUrl: pkgLatestVersionUrl + ? String(pkgLatestVersionUrl) + : undefined, + typescriptLib: typescriptLib ? String(typescriptLib) : undefined, + } + + worker.postMessage(message) }) await init return worker diff --git a/src/monaco/resource.ts b/src/monaco/resource.ts index 8f78d279..da94d290 100644 --- a/src/monaco/resource.ts +++ b/src/monaco/resource.ts @@ -16,7 +16,6 @@ export type CreateNpmFileSystemOptions = { pkgPath: string, ) => string getPackageFileTextUrl?: ( - path: string, pkgName: string, pkgVersion: string | undefined, pkgPath: string, @@ -24,14 +23,12 @@ export type CreateNpmFileSystemOptions = { } const defaultUnpkgOptions: Required = { - getPackageLatestVersionUrl: (pkgName: string) => + getPackageLatestVersionUrl: (pkgName) => `https://unpkg.com/${pkgName}@latest/package.json`, - getPackageDirectoryUrl: ( - pkgName: string, - pkgVersion: string, - pkgPath: string, - ) => `https://unpkg.com/${pkgName}@${pkgVersion}/${pkgPath}/?meta`, - getPackageFileTextUrl: (path: string) => `https://unpkg.com/${path}`, + getPackageDirectoryUrl: (pkgName, pkgVersion, pkgPath) => + `https://unpkg.com/${pkgName}@${pkgVersion}/${pkgPath}/?meta`, + getPackageFileTextUrl: (pkgName, pkgVersion, pkgPath) => + `https://unpkg.com/${pkgName}@${pkgVersion || 'latest'}/${pkgPath}`, } export function createNpmFileSystem( @@ -235,7 +232,7 @@ export function createNpmFileSystem( return } const text = await fetchText( - getPackageFileTextUrl(path, pkgName, _version, pkgFilePath), + getPackageFileTextUrl(pkgName, _version, pkgFilePath), ) if (text !== undefined) { onFetch?.(path, text) @@ -317,7 +314,7 @@ export function createNpmFileSystem( version = modName.substring(modName.lastIndexOf('@') + 1) } if (!version && getPackageVersion) { - getPackageVersion?.(pkgName) + version = getPackageVersion?.(pkgName) } return [modName, pkgName, version, path] } diff --git a/src/monaco/vue.worker.ts b/src/monaco/vue.worker.ts index d61870ca..61867043 100644 --- a/src/monaco/vue.worker.ts +++ b/src/monaco/vue.worker.ts @@ -23,13 +23,35 @@ export interface CreateData { dependencies: Record } +function createFunc(func?: string) { + if (func && typeof func === 'string') { + return Function(`return ${func}`)() + } + return undefined +} + let ts: typeof import('typescript') let locale: string | undefined +let resourceLinks: Record< + keyof Pick< + WorkerMessage, + 'pkgDirUrl' | 'pkgFileTextUrl' | 'pkgLatestVersionUrl' + >, + ((...args: any[]) => string) | undefined +> self.onmessage = async (msg: MessageEvent) => { if (msg.data?.event === 'init') { locale = msg.data.tsLocale - ts = await importTsFromCdn(msg.data.tsVersion) + ts = await importTsFromCdn( + msg.data.tsVersion, + createFunc(msg.data.typescriptLib), + ) + resourceLinks = { + pkgDirUrl: createFunc(msg.data.pkgDirUrl), + pkgFileTextUrl: createFunc(msg.data.pkgFileTextUrl), + pkgLatestVersionUrl: createFunc(msg.data.pkgLatestVersionUrl), + } self.postMessage('inited') return } @@ -61,6 +83,11 @@ self.onmessage = async (msg: MessageEvent) => { content, ) }, + { + getPackageDirectoryUrl: resourceLinks.pkgDirUrl, + getPackageFileTextUrl: resourceLinks.pkgFileTextUrl, + getPackageLatestVersionUrl: resourceLinks.pkgLatestVersionUrl, + }, ), } @@ -98,10 +125,15 @@ self.onmessage = async (msg: MessageEvent) => { ) } -async function importTsFromCdn(tsVersion: string) { +async function importTsFromCdn( + tsVersion: string, + getTsCdn?: (version?: string) => string, +) { const _module = globalThis.module ;(globalThis as any).module = { exports: {} } - const tsUrl = `https://cdn.jsdelivr.net/npm/typescript@${tsVersion}/lib/typescript.js` + const tsUrl = + getTsCdn?.(tsVersion) || + `https://cdn.jsdelivr.net/npm/typescript@${tsVersion}/lib/typescript.js` await import(/* @vite-ignore */ tsUrl) const ts = globalThis.module.exports globalThis.module = _module diff --git a/src/output/Sandbox.vue b/src/output/Sandbox.vue index eba9c688..43977c99 100644 --- a/src/output/Sandbox.vue +++ b/src/output/Sandbox.vue @@ -128,6 +128,11 @@ function createSandbox() { //, previewOptions.value?.placeholderHTML || '', ) + .replace( + //, + store.value.resourceLinks?.esModuleShims || + 'https://cdn.jsdelivr.net/npm/es-module-shims@1.5.18/dist/es-module-shims.wasm.js', + ) sandbox.srcdoc = sandboxSrc containerRef.value?.appendChild(sandbox) diff --git a/src/output/srcdoc.html b/src/output/srcdoc.html index 189c19ca..407bbd91 100644 --- a/src/output/srcdoc.html +++ b/src/output/srcdoc.html @@ -6,8 +6,9 @@ color-scheme: dark; } body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, - Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + font-family: + -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, + Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; } @@ -361,10 +362,7 @@ - + diff --git a/src/store.ts b/src/store.ts index 2508b679..550cee99 100644 --- a/src/store.ts +++ b/src/store.ts @@ -48,6 +48,7 @@ export function useStore( typescriptVersion = ref('latest'), dependencyVersion = ref(Object.create(null)), reloadLanguageTools = ref(), + resourceLinks = undefined, }: Partial = {}, serializedState?: string, ): ReplStore { @@ -92,7 +93,9 @@ export function useStore( vueVersion, async (version) => { if (version) { - const compilerUrl = `https://cdn.jsdelivr.net/npm/@vue/compiler-sfc@${version}/dist/compiler-sfc.esm-browser.js` + const compilerUrl = + resourceLinks?.value?.vueCompilerUrl?.(version) || + `https://cdn.jsdelivr.net/npm/@vue/compiler-sfc@${version}/dist/compiler-sfc.esm-browser.js` loading.value = true compiler.value = await import(/* @vite-ignore */ compilerUrl).finally( () => (loading.value = false), @@ -389,6 +392,8 @@ export function useStore( deserialize, getFiles, setFiles, + + resourceLinks, }) return store } @@ -414,6 +419,20 @@ export interface SFCOptions { template?: Partial } +export type ResourceLinkConfigs = { + esModuleShims?: string + vueCompilerUrl?: (version: string) => string + typescriptLib?: (version: string) => string + // for monaco + pkgLatestVersionUrl?: (pkgName: string) => string + pkgDirUrl?: (pkgName: string, pkgVersion: string, pkgPath: string) => string + pkgFileTextUrl?: ( + pkgName: string, + pkgVersion: string | undefined, + pkgPath: string, + ) => string +} + export type StoreState = ToRefs<{ files: Record activeFilename: string @@ -440,6 +459,9 @@ export type StoreState = ToRefs<{ /** \{ dependencyName: version \} */ dependencyVersion: Record reloadLanguageTools?: (() => void) | undefined + + /** Custom online resources */ + resourceLinks?: ResourceLinkConfigs }> export interface ReplStore extends UnwrapRef { @@ -463,6 +485,8 @@ export interface ReplStore extends UnwrapRef { deserialize(serializedState: string, checkBuiltinImportMap?: boolean): void getFiles(): Record setFiles(newFiles: Record, mainFile?: string): Promise + /** Custom online resources */ + resourceLinks?: ResourceLinkConfigs } export type Store = Pick< @@ -487,6 +511,7 @@ export type Store = Pick< | 'renameFile' | 'getImportMap' | 'getTsConfig' + | 'resourceLinks' > export class File {