diff --git a/README.md b/README.md index c250547..6f841e7 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ Those feature can be used in the workbench feature is enabled: - `@codingame/monaco-editor-wrapper/features/extensionGallery` enables the extension gallery panel and the possibility to install extensions from the marketplace - `@codingame/monaco-editor-wrapper/features/terminal` enables the terminal panel - `@codingame/monaco-editor-wrapper/features/testing` enables the testing panels +- `@codingame/monaco-editor-wrapper/features/typescriptStandalone` enables the monaco standalone typescript language feature worker ### Embed language IntelliSense diff --git a/package-lock.json b/package-lock.json index 2b4d34d..f4631bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,6 +67,7 @@ "@codingame/monaco-vscode-shellscript-default-extension": "^6.0.1", "@codingame/monaco-vscode-snippets-service-override": "^6.0.1", "@codingame/monaco-vscode-sql-default-extension": "^6.0.1", + "@codingame/monaco-vscode-standalone-typescript-language-features": "^6.0.3", "@codingame/monaco-vscode-storage-service-override": "^6.0.1", "@codingame/monaco-vscode-swift-default-extension": "^6.0.1", "@codingame/monaco-vscode-terminal-service-override": "^6.0.1", @@ -86,6 +87,7 @@ "@codingame/monaco-vscode-xml-default-extension": "^6.0.1", "@codingame/monaco-vscode-yaml-default-extension": "^6.0.1", "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@^6.0.1", + "typescript-worker-node-types": "npm:@types/node@^16.11.7", "vscode": "npm:@codingame/monaco-vscode-api@^6.0.1" }, "devDependencies": { @@ -1100,6 +1102,14 @@ "vscode": "npm:@codingame/monaco-vscode-api@6.0.1" } }, + "node_modules/@codingame/monaco-vscode-standalone-typescript-language-features": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-standalone-typescript-language-features/-/monaco-vscode-standalone-typescript-language-features-6.0.3.tgz", + "integrity": "sha512-zXf5eJFezoUvzMTG19ipw09czOuRY71ZA4DBjrwZbzQCQECMWs3A5B4+jhN/gS4a3tWKJpZADcycavHmQdlBTQ==", + "dependencies": { + "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@6.0.3" + } + }, "node_modules/@codingame/monaco-vscode-storage-service-override": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-storage-service-override/-/monaco-vscode-storage-service-override-6.0.1.tgz", @@ -12781,6 +12791,12 @@ "node": ">=14.17" } }, + "node_modules/typescript-worker-node-types": { + "name": "@types/node", + "version": "16.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", + "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==" + }, "node_modules/uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", diff --git a/package.json b/package.json index 23a4f20..43883f7 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,10 @@ "./features/profile": { "types": "./dist/features/profile.d.ts", "default": "./dist/features/profile.js" + }, + "./features/typescriptStandalone": { + "types": "./dist/features/typescriptStandalone.d.ts", + "default": "./dist/features/typescriptStandalone.js" } }, "typesVersions": { @@ -100,6 +104,9 @@ ], "features/profile": [ "./dist/features/profile.d.ts" + ], + "features/typescriptStandalone": [ + "./dist/features/typescriptStandalone.d.ts" ] } }, @@ -167,6 +174,7 @@ "@codingame/monaco-vscode-shellscript-default-extension": "^6.0.1", "@codingame/monaco-vscode-snippets-service-override": "^6.0.1", "@codingame/monaco-vscode-sql-default-extension": "^6.0.1", + "@codingame/monaco-vscode-standalone-typescript-language-features": "^6.0.3", "@codingame/monaco-vscode-storage-service-override": "^6.0.1", "@codingame/monaco-vscode-swift-default-extension": "^6.0.1", "@codingame/monaco-vscode-terminal-service-override": "^6.0.1", @@ -177,6 +185,7 @@ "@codingame/monaco-vscode-theme-seti-default-extension": "^6.0.1", "@codingame/monaco-vscode-timeline-service-override": "^6.0.1", "@codingame/monaco-vscode-typescript-basics-default-extension": "^6.0.1", + "@codingame/monaco-vscode-user-data-profile-service-override": "^6.0.1", "@codingame/monaco-vscode-vb-default-extension": "^6.0.1", "@codingame/monaco-vscode-view-status-bar-service-override": "^6.0.1", "@codingame/monaco-vscode-views-service-override": "^6.0.1", @@ -184,8 +193,8 @@ "@codingame/monaco-vscode-working-copy-service-override": "^6.0.1", "@codingame/monaco-vscode-xml-default-extension": "^6.0.1", "@codingame/monaco-vscode-yaml-default-extension": "^6.0.1", - "@codingame/monaco-vscode-user-data-profile-service-override": "^6.0.1", "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@^6.0.1", + "typescript-worker-node-types": "npm:@types/node@^16.11.7", "vscode": "npm:@codingame/monaco-vscode-api@^6.0.1" }, "devDependencies": { diff --git a/rollup.config.ts b/rollup.config.ts index 9fda901..991d23b 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -6,9 +6,10 @@ import typescript from '@rollup/plugin-typescript' import * as rollup from 'rollup' import vsixPlugin, { IExtensionManifest } from '@codingame/monaco-vscode-rollup-vsix-plugin' import glob from 'fast-glob' -import { addExtension } from '@rollup/pluginutils' +import { addExtension, dataToEsm } from '@rollup/pluginutils' import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets' import path from 'path' +import fs from 'fs' import pkg from './package.json' assert { type: 'json' } const externals = Object.keys(pkg.dependencies) @@ -29,7 +30,8 @@ export default rollup.defineConfig({ 'features/notifications': 'src/features/notifications.ts', 'features/extensionGallery': 'src/features/extensionGallery.ts', 'features/workbench': 'src/features/workbench.ts', - 'features/profile': 'src/features/profile.ts' + 'features/profile': 'src/features/profile.ts', + 'features/typescriptStandalone': 'src/features/typescriptStandalone.ts' }, output: [{ dir: 'dist', @@ -74,17 +76,44 @@ export default rollup.defineConfig({ return undefined } }, + { + name: 'd-ts-glob-import', + async resolveId (source, importer) { + if (source.startsWith('types:')) { + return `types:${path.resolve(path.dirname(importer!), source.slice(6))}` + } + return undefined + }, + async load (importee) { + if (importee.startsWith('types:')) { + const cwd = importee.slice(6) + const files = await glob('**/*.d.ts', { + cwd + }) + + const data = Object.fromEntries(await Promise.all(files.map(async f => { + const data = await fs.promises.readFile(path.resolve(cwd, f)) + return [ + f, + data.toString('utf-8') + ] + }))) + return dataToEsm(data) + } + return undefined + } + }, { name: 'glob-vsix-import', async resolveId (source, importer) { if (source.endsWith('*.vsix')) { - return `glob:${path.resolve(path.dirname(importer!), source)}` + return `vsixGlob:${path.resolve(path.dirname(importer!), source)}` } return undefined }, async load (importee) { - if (importee.startsWith('glob:')) { - const files = await glob(importee.slice(5)) + if (importee.startsWith('vsixGlob:')) { + const files = await glob(importee.slice(9)) return ` ${files.map((file, index) => `import { whenReady as whenReady${index} } from '${file}'`).join('\n')} diff --git a/src/features/typescriptStandalone.ts b/src/features/typescriptStandalone.ts new file mode 100644 index 0000000..da7722e --- /dev/null +++ b/src/features/typescriptStandalone.ts @@ -0,0 +1,120 @@ +import '@codingame/monaco-vscode-standalone-typescript-language-features' +import * as monaco from 'monaco-editor' +import { FileSystemProviderCapabilities, FileSystemProviderError, FileSystemProviderErrorCode, FileType, IFileSystemProviderWithFileReadWriteCapability, IStat, registerFileSystemOverlay } from '@codingame/monaco-vscode-files-service-override' +import * as vscode from 'vscode' +import { registerWorkerLoader } from '../worker.js' + +const global = ` +declare global { + /** + * Read a line from stdin + */ + function readline(): string; + /** + * Print a string to stdout + * @deprecated Use console.log instead + */ + function print(word: string): void; +} +// It needs to be a module +export {} +` + +const compilerOptions: Parameters[0] = { + target: monaco.languages.typescript.ScriptTarget.ES2016, + allowNonTsExtensions: true, + moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs, + module: monaco.languages.typescript.ModuleKind.CommonJS, + noEmit: true, + lib: ['es2020'] +} + +class TypescriptWorkerTypeFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability { + capabilities = FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.PathCaseSensitive | FileSystemProviderCapabilities.Readonly + + constructor (private getWorker: () => Promise) { + } + + private async getExtraLib (resource: monaco.Uri) { + const content = await (await this.getWorker()).getScriptText(resource.path.slice(1)) + + return content + } + + async readFile (resource: monaco.Uri): Promise { + const file = await this.getExtraLib(resource) + if (file != null) { + return new TextEncoder().encode(file) + } + throw FileSystemProviderError.create('file not found', FileSystemProviderErrorCode.FileNotFound) + } + + async stat (resource: monaco.Uri): Promise { + const file = await this.getExtraLib(resource) + if (file != null) { + return { + type: FileType.File, + size: file.length, + mtime: 0, + ctime: 0 + } + } + throw FileSystemProviderError.create('file not found', FileSystemProviderErrorCode.FileNotFound) + } + + async writeFile (): Promise { + throw FileSystemProviderError.create('not allowed', FileSystemProviderErrorCode.NoPermissions) + } + + onDidChangeCapabilities = new vscode.EventEmitter().event + onDidChangeFile = new vscode.EventEmitter().event + watch (): monaco.IDisposable { + return { + dispose () {} + } + } + + async mkdir (): Promise { + throw FileSystemProviderError.create('Not allowed', FileSystemProviderErrorCode.NoPermissions) + } + + async readdir () { + return [] + } + + async delete (): Promise { + throw FileSystemProviderError.create('Not allowed', FileSystemProviderErrorCode.NoPermissions) + } + + async rename (): Promise { + throw FileSystemProviderError.create('Not allowed', FileSystemProviderErrorCode.NoPermissions) + } +} + +monaco.languages.typescript.typescriptDefaults.setCompilerOptions(compilerOptions) +monaco.languages.typescript.javascriptDefaults.setCompilerOptions(compilerOptions) +monaco.languages.typescript.typescriptDefaults.addExtraLib(global, 'global.d.ts') +monaco.languages.typescript.javascriptDefaults.addExtraLib(global, 'global.d.ts') + +monaco.languages.onLanguage('typescript', async () => { + registerFileSystemOverlay(-1, new TypescriptWorkerTypeFileSystemProvider(async () => (await monaco.languages.typescript.getTypeScriptWorker())())) + + const types = (await import('types:../../node_modules/typescript-worker-node-types')).default + + for (const [file, content] of Object.entries(types)) { + monaco.languages.typescript.typescriptDefaults.addExtraLib(content, `node/${file}`) + } +}) +monaco.languages.onLanguage('javascript', async () => { + registerFileSystemOverlay(-1, new TypescriptWorkerTypeFileSystemProvider(async () => (await monaco.languages.typescript.getJavaScriptWorker())())) + + const types = (await import('types:../../node_modules/typescript-worker-node-types')).default + + for (const [file, content] of Object.entries(types)) { + monaco.languages.typescript.javascriptDefaults.addExtraLib(content, `node/${file}`) + } +}) + +const workerLoader = () => new Worker(new URL('@codingame/monaco-vscode-standalone-typescript-language-features/worker', import.meta.url)) +registerWorkerLoader('typescript', workerLoader) +registerWorkerLoader('javascript', workerLoader) diff --git a/src/types/types.d.ts b/src/types/types.d.ts new file mode 100644 index 0000000..c60baca --- /dev/null +++ b/src/types/types.d.ts @@ -0,0 +1,4 @@ +declare module 'types:*' { + const types: Record + export default types +}