From c90f7286f48162d3296c3839c0da0440369ede22 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 14 Aug 2019 11:47:50 -0700 Subject: [PATCH] Add 'preParse' plugin hook --- Gulpfile.js | 15 +-- src/compiler/plugin.ts | 190 ++++++++++++++++++++++++++---------- src/compiler/program.ts | 35 +++++-- src/compiler/sys.ts | 2 +- src/compiler/types.ts | 185 ++++++++++++++++++++--------------- src/pluginApi/pluginApi.ts | 0 src/pluginApi/tsconfig.json | 14 +++ 7 files changed, 288 insertions(+), 153 deletions(-) create mode 100644 src/pluginApi/pluginApi.ts create mode 100644 src/pluginApi/tsconfig.json diff --git a/Gulpfile.js b/Gulpfile.js index df9b13e971b..10df3a7be9a 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -108,18 +108,7 @@ task("watch-tsc", series(lkgPreBuild, parallel(watchLib, watchDiagnostics, watch task("watch-tsc").description = "Watch for changes and rebuild the command-line compiler only."; const buildApi = (() => { - const flattenCompiler = async () => flatten("src/compiler/tsconfig.json", "built/local/api.tsconfig.json", { - compilerOptions: { - removeComments: false, - stripInternal: true, - emitDeclarationOnly: true, - declaration: true, - declarationMap: false, - outFile: "api.out.js" - } - }); - - const buildApiOut = () => buildProject("built/local/api.tsconfig.json"); + const buildApiOut = () => buildProject("src/pluginApi/tsconfig.json", cmdLineOptions); const generateApiDts = () => src("built/local/api.out.d.ts", { base: "built/local" }) .pipe(newer("built/local/api.d.ts")) @@ -136,7 +125,7 @@ const buildApi = (() => { .pipe(rename("api.js")) .pipe(dest("built/local")); - return series(flattenCompiler, buildApiOut, generateApiDts, generateApiJs); + return series(buildApiOut, generateApiDts, generateApiJs); })(); task("api", series(lkgPreBuild, buildApi)); task("api").description = "Build the compiler plugin API"; diff --git a/src/compiler/plugin.ts b/src/compiler/plugin.ts index 78469239aa8..b213d19e451 100644 --- a/src/compiler/plugin.ts +++ b/src/compiler/plugin.ts @@ -1,13 +1,16 @@ +/*@internal*/ namespace ts { - type Hook = "activate" | "deactivate" | "preEmit"; + type MatchingKeys = K extends (T[K] extends TMatch ? K : never) ? K : never; + type Hook = MatchingKeys, (context: CompilerPluginContext, ...args: any[]) => CompilerPluginResult | void>; type HookFunction = NonNullable; + type HookReturnType = Exclude>, void>; type ExecuteUserCodeResult = | { error: Diagnostic, result: undefined } | { error: undefined, result: T | undefined }; interface PluginEntry { - active: boolean; // Tracks whether the plugin has been activated. + state: "inactive" | "active" | "failed"; // Tracks whether the plugin has been activated. compilerPlugin: CompilerPlugin; } @@ -31,7 +34,6 @@ namespace ts { */ /*@internal*/ export function getPlugins(host: ModuleLoaderHost, initialDir: string, plugins: ReadonlyArray) { - debugger; interface ResolvedModule { module: {}; moduleName: string; @@ -195,92 +197,180 @@ namespace ts { return moduleName.replace(/^(@[^\\/]+[\\/])?/, "$1typescript-plugin-"); } + /** + * Gets a wrapped view of a Program for use with a plugin. + */ + function getOrCreatePluginProgram(program: Program) { + if (program.pluginProgram) { + return program.pluginProgram; + } + + let disposed = false; + program.pluginProgram = { + getCompilerOptions: () => (checkDisposed(), program.getCompilerOptions()), + getSourceFile: fileName => (checkDisposed(), program.getSourceFile(fileName)), + getSourceFileByPath: path => (checkDisposed(), program.getSourceFileByPath(path)), + getCurrentDirectory: () => (checkDisposed(), program.getCurrentDirectory()), + getRootFileNames: () => (checkDisposed(), program.getRootFileNames()), + getSourceFiles: () => (checkDisposed(), program.getSourceFiles()), + emit: (targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers) => (checkDisposed(), program.emit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers)), + getOptionsDiagnostics: cancellationToken => (checkDisposed(), program.getOptionsDiagnostics(cancellationToken)), + getGlobalDiagnostics: cancellationToken => (checkDisposed(), program.getGlobalDiagnostics(cancellationToken)), + getSyntacticDiagnostics: (sourceFile, cancellationToken) => (checkDisposed(), program.getSyntacticDiagnostics(sourceFile, cancellationToken)), + getSemanticDiagnostics: (sourceFile, cancellationToken) => (checkDisposed(), program.getSemanticDiagnostics(sourceFile, cancellationToken)), + getDeclarationDiagnostics: (sourceFile, cancellationToken) => (checkDisposed(), program.getDeclarationDiagnostics(sourceFile, cancellationToken)), + getConfigFileParsingDiagnostics: () => (checkDisposed(), program.getConfigFileParsingDiagnostics()), + getTypeChecker: () => (checkDisposed(), program.getTypeChecker()), + isSourceFileFromExternalLibrary: file => (checkDisposed(), program.isSourceFileFromExternalLibrary(file)), + isSourceFileDefaultLibrary: file => (checkDisposed(), program.isSourceFileDefaultLibrary(file)), + getProjectReferences: () => (checkDisposed(), program.getProjectReferences()), + getResolvedProjectReferences: () => (checkDisposed(), program.getResolvedProjectReferences()), + pluginDispose: () => { disposed = true }, + // internal members are not exposed. + getMissingFilePaths: notImplemented, + getSuggestionDiagnostics: notImplemented, + getCommonSourceDirectory: notImplemented, + getDiagnosticsProducingTypeChecker: notImplemented, + dropDiagnosticsProducingTypeChecker: notImplemented, + getClassifiableNames: notImplemented, + getNodeCount: notImplemented, + getIdentifierCount: notImplemented, + getSymbolCount: notImplemented, + getTypeCount: notImplemented, + getFileProcessingDiagnostics: notImplemented, + getResolvedTypeReferenceDirectives: notImplemented, + getSourceFileFromReference: notImplemented, + getLibFileFromReference: notImplemented, + sourceFileToPackageName: undefined!, + redirectTargetsMap: undefined!, + isEmittedFile: notImplemented, + getResolvedModuleWithFailedLookupLocationsFromCache: notImplemented, + getProjectReferenceRedirect: notImplemented, + getResolvedProjectReferenceToRedirect: notImplemented, + forEachResolvedProjectReference: notImplemented, + getResolvedProjectReferenceByPath: notImplemented, + getRefFileMap: notImplemented, + emitBuildInfo: notImplemented, + getRelationCacheSizes: notImplemented, + dispose: notImplemented, + }; + program.pluginProgram.pluginProgram = program.pluginProgram; + return program.pluginProgram; + + function checkDisposed() { + if (disposed) throw new TypeError("Object is disposed."); + disposed = true; + program = undefined!; + } + } + /** * Creates a `CompilerPluginHost` used to manage plugin lifetime. */ - /*@internal*/ - export function createPluginHost(plugins: ReadonlyArray): CompilerPluginHost { - const entries: PluginEntry[] = plugins.map(compilerPlugin => ({ compilerPlugin, active: false })); + export function createPluginHost(plugins: ReadonlyArray, compilerOptions: CompilerOptions): CompilerPluginHost { + const entries: PluginEntry[] = plugins.map(compilerPlugin => ({ compilerPlugin, state: "inactive" })); + return { + preParse, preEmit, deactivate }; - function executeUserCode(compilerPlugin: CompilerPlugin, hook: K, ...args: Parameters>): ExecuteUserCodeResult>> { - const hookFunction: HookFunction | undefined = compilerPlugin.plugin[hook]!; - if (typeof hookFunction === "function") { + function selectPlugins({ eventName, state }: { eventName?: Hook, state?: "inactive" | "active" | "failed" }) { + const result: PluginEntry[] = []; + for (const entry of entries) { + if (state !== undefined && entry.state !== state) continue; + if (eventName !== undefined && !contains(entry.compilerPlugin.activationEvents, eventName)) continue; + result.push(entry); + } + return result; + } + + function executeUserCode(plugin: PluginEntry, hook: K, ...args: Parameters>): ExecuteUserCodeResult> { + const hookAction = plugin.compilerPlugin.plugin[hook]; + if (typeof hookAction === "function") { try { - const result = hookFunction.apply(compilerPlugin.plugin, args); - return { result, error: undefined }; + const result: HookReturnType | undefined = hookAction.apply(plugin.compilerPlugin.plugin, args); + return { error: undefined, result }; } catch (error) { if (error instanceof OperationCanceledException) { throw error; } - return { result: undefined, error: createUserCodeDiagnostic(compilerPlugin, hook, error) }; + plugin.state = "failed"; + return { error: createUserCodeDiagnostic(plugin.compilerPlugin, hook, error), result: undefined }; } } - return { result: undefined, error: undefined }; + return { error: undefined, result: undefined }; } - function createContext(compilerHost: CompilerHost, { options }: CompilerPlugin): CompilerPluginContext { - return { ts, compilerHost, options }; + function createContext(compilerHost: CompilerHost, { compilerPlugin: { options } }: PluginEntry): CompilerPluginContext { + return { ts, compilerHost, compilerOptions, options }; } function createUserCodeDiagnostic(compilerPlugin: CompilerPlugin, hook: Hook, error: { message?: string, stack?: string }) { return createCompilerDiagnostic(Diagnostics.Plugin_0_failed_while_executing_the_1_hook_Colon_2, compilerPlugin.name, hook, error.stack || error.message || error.toString()); } - function activate(host: CompilerHost, activationEventName: string) { - const activeCompilerPlugins: CompilerPlugin[] = []; + function activate(host: CompilerHost, eventName: Hook): ReadonlyArray | undefined { let diagnostics: ReadonlyArray | undefined; - for (const entry of entries) { - if (!entry.active && contains(entry.compilerPlugin.activationEvents, activationEventName)) { - const { error, result: activationResult } = executeUserCode(entry.compilerPlugin, "activate", createContext(host, entry.compilerPlugin)); - if (error) { - diagnostics = concatenate(diagnostics, [error]); - } - else { - if (activationResult) diagnostics = concatenate(diagnostics, activationResult.diagnostics); - entry.active = true; - } + for (const plugin of selectPlugins({ eventName, state: "inactive" })) { + const { error, result } = executeUserCode(plugin, "activate", createContext(host, plugin), { }); + if (error) { + // TODO(rbuckton): Determine better mechanism to handle plugin activation failure. + diagnostics = concatenate(diagnostics, [error]); } - if (entry.active) { - activeCompilerPlugins.push(entry.compilerPlugin); + else { + plugin.state = "active"; + if (result) diagnostics = concatenate(diagnostics, result.diagnostics); } } - return { activeCompilerPlugins, diagnostics }; + return diagnostics; } - function preEmit(host: CompilerHost, program: Program, targetSourceFile?: SourceFile, cancellationToken?: CancellationToken): CompilerPluginPreEmitResult { - debugger; - const activationResult = activate(host, /*activationEventName*/ "preEmit"); - const activeCompilerPlugins = activationResult.activeCompilerPlugins; - let diagnostics = activationResult.diagnostics; - let customTransformers: CustomTransformers | undefined; - for (const plugin of activeCompilerPlugins) { - const { error, result: preEmitResult } = executeUserCode(plugin, "preEmit", createContext(host, plugin), program, targetSourceFile, cancellationToken); + function preParse(host: CompilerHost, { rootNames, projectReferences }: CompilerPluginPreParseArgs): CompilerPluginPreParseResult { + let diagnostics = activate(host, "preParse"); + for (const plugin of selectPlugins({ eventName: "preParse", state: "active" })) { + const { error, result } = executeUserCode(plugin, "preParse", createContext(host, plugin), { rootNames, projectReferences }); if (error) { diagnostics = concatenate(diagnostics, [error]); } - else if (preEmitResult) { - diagnostics = concatenate(diagnostics, preEmitResult.diagnostics); - customTransformers = combineCustomTransformers(customTransformers, preEmitResult.customTransformers); + else if (result) { + diagnostics = concatenate(diagnostics, result.diagnostics); + host = result.compilerHost || host; + rootNames = result.rootNames || rootNames; + projectReferences = result.projectReferences || projectReferences; + } + } + return { compilerHost: host, rootNames, projectReferences }; + } + + function preEmit(host: CompilerHost, { program, targetSourceFile, cancellationToken }: CompilerPluginPreEmitArgs): CompilerPluginPreEmitResult { + program = getOrCreatePluginProgram(program); + let diagnostics = activate(host, "preEmit"); + let customTransformers: CustomTransformers | undefined; + for (const plugin of selectPlugins({ eventName: "preEmit", state: "active" })) { + const { error, result } = executeUserCode(plugin, "preEmit", createContext(host, plugin), { program, targetSourceFile, cancellationToken }); + if (error) { + diagnostics = concatenate(diagnostics, [error]); + } + else if (result) { + diagnostics = concatenate(diagnostics, result.diagnostics); + customTransformers = combineCustomTransformers(customTransformers, result.customTransformers); } } return { diagnostics, customTransformers }; } function deactivate(host: CompilerHost): CompilerPluginDeactivationResult { - debugger; - let diagnostics: Diagnostic[] | undefined; - for (const entry of entries) { - if (entry.active) { - entry.active = false; - const { error } = executeUserCode(entry.compilerPlugin, "deactivate", createContext(host, entry.compilerPlugin)); - if (error) { - diagnostics = append(diagnostics, error); - } + let diagnostics: ReadonlyArray | undefined; + for (const plugin of selectPlugins({ state: "active" })) { + const { error } = executeUserCode(plugin, "deactivate", createContext(host, plugin)); + if (error) { + diagnostics = concatenate(diagnostics, [error]); + } + else { + plugin.state = "inactive"; } } return { diagnostics }; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 3ffb94fce91..3cb97fffef2 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -711,8 +711,8 @@ namespace ts { export function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: ReadonlyArray): Program; export function createProgram(rootNamesOrOptions: ReadonlyArray | CreateProgramOptions, _options?: CompilerOptions, _host?: CompilerHost, _oldProgram?: Program, _configFileParsingDiagnostics?: ReadonlyArray): Program { const createProgramOptions = isArray(rootNamesOrOptions) ? createCreateProgramOptions(rootNamesOrOptions, _options!, _host, _oldProgram, _configFileParsingDiagnostics) : rootNamesOrOptions; // TODO: GH#18217 - const { rootNames, options, configFileParsingDiagnostics, projectReferences } = createProgramOptions; - let { oldProgram } = createProgramOptions; + const { options, configFileParsingDiagnostics } = createProgramOptions; + let { rootNames, projectReferences, oldProgram } = createProgramOptions; let program: Program; let processingDefaultLibFiles: SourceFile[] | undefined; @@ -725,7 +725,7 @@ namespace ts { const ambientModuleNameToUnmodifiedFileName = createMap(); // Todo:: Use this to report why file was included in --extendedDiagnostics let refFileMap: MultiMap | undefined; - const pluginHost = createProgramOptions.plugins && createPluginHost(createProgramOptions.plugins); + const pluginHost = createProgramOptions.plugins && createPluginHost(createProgramOptions.plugins, options); const cachedSemanticDiagnosticsForFile: DiagnosticCache = {}; const cachedDeclarationDiagnosticsForFile: DiagnosticCache = {}; @@ -733,6 +733,21 @@ namespace ts { let resolvedTypeReferenceDirectives = createMap(); let fileProcessingDiagnostics = createDiagnosticCollection(); + performance.mark("beforeProgram"); + + let host = createProgramOptions.host || createCompilerHost(options); + const preParseResult = pluginHost && pluginHost.preParse(host, { rootNames, projectReferences }); + if (preParseResult) { + if (preParseResult.diagnostics) { + for (const diagnostic of preParseResult.diagnostics) { + fileProcessingDiagnostics.add(diagnostic); + } + } + if (preParseResult.compilerHost) host = preParseResult.compilerHost; + if (preParseResult.rootNames) rootNames = preParseResult.rootNames; + if (preParseResult.projectReferences) projectReferences = preParseResult.projectReferences; + } + // The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules. // This works as imported modules are discovered recursively in a depth first manner, specifically: // - For each root file, findSourceFile is called. @@ -750,9 +765,6 @@ namespace ts { // Track source files that are source files found by searching under node_modules, as these shouldn't be compiled. const sourceFilesFoundSearchingNodeModules = createMap(); - performance.mark("beforeProgram"); - - const host = createProgramOptions.host || createCompilerHost(options); const configParsingHost = parseConfigHostFromCompilerHostLike(host); let skipDefaultLib = options.noLib; @@ -975,6 +987,9 @@ namespace ts { if (pluginHost) { pluginHost.deactivate(host); } + if (program.pluginProgram) { + program.pluginProgram.pluginDispose(); + } } function compareDefaultLibFiles(a: SourceFile, b: SourceFile) { @@ -1560,9 +1575,11 @@ namespace ts { let pluginDiagnostics: ReadonlyArray | undefined; if (pluginHost) { - const result = pluginHost.preEmit(host, program, sourceFile, cancellationToken); - pluginDiagnostics = result.diagnostics; - customTransformers = combineCustomTransformers(customTransformers, result.customTransformers); + const result = pluginHost.preEmit(host, { program, targetSourceFile: sourceFile, cancellationToken }); + if (result) { + pluginDiagnostics = result.diagnostics; + customTransformers = combineCustomTransformers(customTransformers, result.customTransformers); + } } if (!emitOnlyDtsFiles) { diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 16e2604abeb..255efe2fcf8 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -1,4 +1,4 @@ -declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any; +declare function setTimeout(handler: (...args: A) => void, timeout: number, ...args: A): any; declare function clearTimeout(handle: any): void; namespace ts { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 3561861c7f3..2e529e8b24e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2947,27 +2947,6 @@ namespace ts { file: Path; } - /* @internal */ - export interface CompilerPluginDeactivationResult { - diagnostics?: ReadonlyArray; - } - - /** - * The CompilerPluginHost provides an interface for interacting with the compiler plugin model. - */ - /*@internal*/ - export interface CompilerPluginHost { - /** - * Trigger the `preEmit` hooks for plugins. - */ - preEmit(host: CompilerHost, program: Program, targetSourceFile?: SourceFile, cancellationToken?: CancellationToken): CompilerPluginPreEmitResult; - - /** - * Deactivate any active plugins. - */ - deactivate(host: CompilerHost): CompilerPluginDeactivationResult; - } - // TODO: This should implement TypeCheckerHost but that's an internal type. export interface Program extends ScriptReferenceHost { @@ -3063,6 +3042,14 @@ namespace ts { * Dispose of any resources held by the program and deactivate any active plugins. */ /*@internal*/ dispose(): void; + + /** A view of the Program that is provided to plugins. */ + /*@internal*/ pluginProgram?: PluginProgram; + } + + /* @internal */ + export interface PluginProgram extends Program { + pluginDispose(): void; } /* @internal */ @@ -4927,65 +4914,6 @@ namespace ts { /* @internal */ spec: ConfigFileSpecs; } - /** - * A context object passed to a plugin during activation. - */ - export interface CompilerPluginContext { - /** - * The running instance of the TypeScript compiler. - */ - readonly ts: typeof ts; - - /** - * The current CompilerHost. - */ - readonly compilerHost: CompilerHost; - - /** - * Configuration options for the plugin. - */ - readonly options: MapLike; - } - - /** - * An optional result that can be returned from the `CompilerPluginModule.activate` hook. - */ - export interface CompilerPluginActivationResult { - diagnostics?: ReadonlyArray; - } - - /** - * An optional result that can be returned from the `CompilerPluginModule.preEmit` hook. - */ - export interface CompilerPluginPreEmitResult { - diagnostics?: ReadonlyArray; - customTransformers?: CustomTransformers; - } - - /** - * Describes the supported shape of the main module for a compiler plugin. - */ - export interface CompilerPluginModule { - /** - * The `activate` hook is invoked when a plugin is activated for the first time within a `Program`. - * @param context The current plugin context. - */ - activate?(context: CompilerPluginContext): CompilerPluginActivationResult | void; - /** - * The `preEmit` hook is invoked after type check has completed and immediately before emit. - * @param context The current plugin context. - * @param program The current `Program`. - * @param targetSourceFile The `SourceFile` that is about to be emitted, or `undefined` when emitting all outputs. - * @param cancellationToken A `CancellationToken` that can be used to abort an operation when running in the language service. - */ - preEmit?(context: CompilerPluginContext, program: Program, targetSourceFile?: SourceFile, cancellationToken?: CancellationToken): CompilerPluginPreEmitResult | void; - /** - * The `deactivate` hook is invoked when a plugin should be deactivated so that it can free up any shared resources. - * @param context The current plugin context. - */ - deactivate?(context: CompilerPluginContext): void; - } - export interface CompilerPlugin { /** The rsolved package name for the plugin. */ name: string; @@ -5003,6 +4931,103 @@ namespace ts { plugin: CompilerPluginModule; } + /** + * A context object passed to a plugin during activation. + */ + export interface CompilerPluginContext { + /** The running instance of the TypeScript compiler. */ + readonly ts: typeof ts; + /** The current CompilerHost. */ + readonly compilerHost: CompilerHost; + /** The current CompilerOptions. */ + readonly compilerOptions: CompilerOptions; + /** Configuration options for the plugin. */ + readonly options: MapLike; + } + + /** + * The CompilerPluginHost provides an interface for interacting with the compiler plugin model. + */ + /*@internal*/ + export interface CompilerPluginHost { + preParse(host: CompilerHost, args: CompilerPluginPreParseArgs): CompilerPluginPreParseResult | void; + preEmit(host: CompilerHost, args: CompilerPluginPreEmitArgs): CompilerPluginPreEmitResult | void; + deactivate(host: CompilerHost): CompilerPluginDeactivationResult; + } + + /** + * Describes the supported shape of the main module for a compiler plugin. + */ + export interface CompilerPluginModule { + /** + * The `activate` hook is invoked when a plugin is activated for the first time within a `Program`. + */ + activate?(context: CompilerPluginContext, args: CompilerPluginActivationArgs): CompilerPluginActivationResult | void; + /** + * The `preParse` hook is invoked when a new `Program` is about to be created before any files are parsed. + */ + preParse?(context: CompilerPluginContext, args: CompilerPluginPreParseArgs): CompilerPluginPreParseResult | void; + /** + * The `preEmit` hook is invoked after type check has completed and immediately before emit. + */ + preEmit?(context: CompilerPluginContext, args: CompilerPluginPreEmitArgs): CompilerPluginPreEmitResult | void; + /** + * The `deactivate` hook is invoked when a plugin should be deactivated so that it can free up any shared resources. + */ + deactivate?(context: CompilerPluginContext): void; + } + + export interface CompilerPluginResult { + diagnostics?: ReadonlyArray; + } + + export interface CompilerPluginActivationArgs { + } + + /** + * An optional result that can be returned from the `CompilerPluginModule.activate` hook. + */ + export interface CompilerPluginActivationResult extends CompilerPluginResult { + } + + export interface CompilerPluginPreParseArgs { + readonly rootNames: ReadonlyArray; + readonly projectReferences: ReadonlyArray | undefined; + } + + export interface CompilerPluginPreParseResult extends CompilerPluginResult { + compilerHost?: CompilerHost; + rootNames?: ReadonlyArray; + projectReferences?: ReadonlyArray; + } + + export interface CompilerPluginPostCreateProgramArgs { + readonly program: Program; + } + + export interface CompilerPluginPostCreateProgramResult extends CompilerPluginResult { + } + + export interface CompilerPluginPreEmitArgs { + /** The current `Program`. */ + readonly program: Program; + /** The `SourceFile` that is about to be emitted, or `undefined` when emitting all outputs. */ + readonly targetSourceFile: SourceFile | undefined; + /** A `CancellationToken` that can be used to abort an operation when running in the language service. */ + readonly cancellationToken: CancellationToken | undefined; + } + + /** + * An optional result that can be returned from the `CompilerPluginModule.preEmit` hook. + */ + export interface CompilerPluginPreEmitResult extends CompilerPluginResult { + customTransformers?: CustomTransformers; + } + + /* @internal */ + export interface CompilerPluginDeactivationResult extends CompilerPluginResult { + } + /* @internal */ export interface ModuleLoaderHost extends ModuleResolutionHost { require(initialDir: string, moduleName: string): RequireResult; diff --git a/src/pluginApi/pluginApi.ts b/src/pluginApi/pluginApi.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/pluginApi/tsconfig.json b/src/pluginApi/tsconfig.json new file mode 100644 index 00000000000..2d5d836ed1e --- /dev/null +++ b/src/pluginApi/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig-library-base", + "compilerOptions": { + "removeComments": false, + "emitDeclarationOnly": true, + "outFile": "../../built/local/api.out.js" + }, + "files": [ + "pluginApi.ts" + ], + "references": [ + { "path": "../compiler", "prepend": true } + ] +}