如何从Monaco编辑器获取转译后的代码?

3
如何从 Monaco 编辑器获取转译后的 TypeScript 代码?同时,是否可以访问 TypeScript 语言服务?我尝试了以下代码:
monaco.languages.typescript.getTypeScriptWorker();

但是它返回了Promise<any>,我不知道该怎么办!

3个回答

9
经过调查研究, TypeScript 的 LanguageService 接口似乎部分通过 worker proxy 对象暴露出来。这可能是因为该服务在另一个线程中,且需要使用 promise 来推送消息。为了简化操作,我审查了返回对象上的公开函数并创建了此定义,希望对某些人有所帮助。
namespace ts {
    export interface IMonacoTypeScriptServiceProxy {
        _getModel(uri: string): Promise<{ _eol: string, _lineStarts: any, _Lines: string[], length: number, _uri: Uri, _versionId: number }>;
        findRenameLocations(uri: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): Promise<readonly RenameLocation[] | undefined>;
        getCodeFixesAtPosition(uri: string, start: number, end: number, errorCodes: readonly number[], formatOptions: FormatCodeSettings, preferences: UserPreferences): Promise<readonly CodeFixAction[]>;
        getCompilationSettings(): Promise<CompilerOptions>;
        getCompilerOptionsDiagnostics(): Promise<Diagnostic[]>;
        getCompletionEntryDetails(uri: string, position: number, name: string, formatOptions: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences | undefined): Promise<CompletionEntryDetails | undefined>;
        getCompletionsAtPosition(uri: string, position: number, options: GetCompletionsAtPositionOptions | undefined): Promise<WithMetadata<CompletionInfo> | undefined>;
        getCurrentDirectory(): Promise<string>;
        getDefaultLibFileName(options: CompilerOptions): Promise<string>;
        getDefinitionAtPosition(uri: string, position: number): Promise<ReadonlyArray<DefinitionInfo> | undefined>;
        getEmitOutput(uri: string, emitOnlyDtsFiles?: boolean): Promise<EmitOutput>;
        getFormattingEditsAfterKeystroke(uri: string, position: number, key: string, options: FormatCodeOptions | FormatCodeSettings): Promise<TextChange[]>;
        getFormattingEditsForDocument(uri: string, options: FormatCodeOptions | FormatCodeSettings): Promise<TextChange[]>;
        getFormattingEditsForRange(uri: string, start: number, end: number, options: FormatCodeOptions | FormatCodeSettings): Promise<TextChange[]>;
        getNavigationBarItems(uri: string): Promise<NavigationBarItem[]>;
        getOccurrencesAtPosition(uri: string, position: number): Promise<ReadonlyArray<ReferenceEntry> | undefined>;
        getQuickInfoAtPosition(uri: string, position: number): Promise<QuickInfo | undefined>;
        getReferencesAtPosition(uri: string, position: number): Promise<ReferenceEntry[] | undefined>;
        getRenameInfo(uri: string, position: number, options?: RenameInfoOptions): Promise<RenameInfo>;
        getScriptFileNames(): Promise<string[]>;
        getScriptKind(uri: string): Promise<ScriptKind>;
        getScriptSnapshot(uri: string): Promise<IScriptSnapshot | undefined>;
        getScriptVersion(uri: string): Promise<string>;
        getSemanticDiagnostics(uri: string): Promise<Diagnostic[]>;
        getSignatureHelpItems(uri: string, position: number, options: SignatureHelpItemsOptions | undefined): Promise<SignatureHelpItems | undefined>;
        getSuggestionDiagnostics(uri: string): Promise<DiagnosticWithLocation[]>;
        getSyntacticDiagnostics(uri: string): Promise<DiagnosticWithLocation[]>;
        isDefaultLibFileName(uri: string): Promise<boolean>;
    }
}

使用方法如下:

var editor = monaco.editor.create(...etc...);
var tsProxy: ts.IMonacoTypeScriptServiceProxy;

monaco.languages.typescript.getTypeScriptWorker()
            .then(function(worker: (v: monaco.Uri) => Promise<ts.IMonacoTypeScriptServiceProxy>) {
                worker(editor.getModel().uri)
                      .then(function(proxy) {
                            tsProxy = proxy;
                      });
            });

现在代理函数被称为getEmitOutput(uri: string, emitOnlyDtsFiles?: boolean)。其中uri是文件名,它是一个monaco.Uri转换成的字符串(在monaco术语中,model是编辑器中的单个文件),所以:
tsProxy.getEmitOutput(editor.getModel().uri.toString())
                                    .then((r) => { console.log(r.outputFiles[0].text); });

返回经过转换的JavaScript,以及所有其他文件(如果有的话)!不客气。 :)

3
我更新了方法列表,现在增加了几个。 - Mike Lischke
1
这太棒了!感谢您抽出时间记录这个。 - LOAS

5
自 0.20.0 版本以来,情况已经发生了变化。现在方法 getTypeScriptWorkergetJavaScriptWorker 返回有效的类型:
    export const getTypeScriptWorker: () => Promise<(...uris: Uri[]) => Promise<TypeScriptWorker>>;
    export const getJavaScriptWorker: () => Promise<(...uris: Uri[]) => Promise<TypeScriptWorker>>;

同时,Promise参数也已经发生了变化。

随着这一变化,不再需要维护自己的工作线程(代理)类型。


3
到目前为止,讨论的重点是如何获取JS / TS工作程序代理,但对于最初的问题——如何获取转译代码——有一个更简单的解决方案:
    import ts from "typescript";

    this.worker.postMessage({ code: ts.transpile(context.code) });

这是我将TS代码从编辑器发送到Web Worker以进行隔离执行的方法。

也许区别在于您的变体在主线程上运行转译,而其他答案则将其卸载到工作线程上? - cbreezier
我不确定你指的是哪个差异,顺便说一句,我建议不要在工作线程中访问 Monaco,至少在使用像 webpack 或 rollup 这样的打包工具时不要这样做,因为它会将整个 Monaco 代码包含在该工作线程捆绑包中,这会使其变得非常庞大(>4MB)。 - Mike Lischke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接