ts-patch
Version:
Patch typescript to support custom transformers in tsconfig.json
773 lines (772 loc) • 38.2 kB
JavaScript
var tsp = (function () {
"use strict";
var tsp;
(function (tsp) {
const os = require("os");
const path = require("path");
const fs = require("fs");
tsp.diagnosticMap = new WeakMap();
tsp.supportedExtensions = [".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"];
tsp.tsExtensions = [".ts", ".mts", ".cts"];
function diagnosticExtrasFactory(program) {
const diagnostics = tsp.diagnosticMap.get(program) || tsp.diagnosticMap.set(program, []).get(program);
const addDiagnostic = (diag) => diagnostics.push(diag);
const removeDiagnostic = (index) => { diagnostics.splice(index, 1); };
return { addDiagnostic, removeDiagnostic, diagnostics };
}
tsp.diagnosticExtrasFactory = diagnosticExtrasFactory;
function getTmpDir(subPath) {
const tmpDir = path.resolve(os.tmpdir(), "tsp", subPath);
if (!fs.existsSync(tmpDir))
fs.mkdirSync(tmpDir, { recursive: true });
return tmpDir;
}
tsp.getTmpDir = getTmpDir;
function getTsInstance() {
return (typeof ts !== "undefined" ? ts : module.exports);
}
tsp.getTsInstance = getTsInstance;
function getErrorMessage(e) {
return String(e?.message || "");
}
function extractFilePathFromError(e) {
if (typeof e?.fileName === "string")
return e.fileName;
const stack = String(e?.stack || "");
const fileUrlMatch = stack.match(/file:\/\/([^:\n)]+\.[cm]?[tj]sx?):\d+:\d+/);
if (fileUrlMatch)
return decodeURIComponent(fileUrlMatch[1]);
const pathMatch = stack.match(/(\/[^:\n)]+\.[cm]?[tj]sx?):\d+:\d+/);
return pathMatch?.[1];
}
function describeEsmInCjsError(e) {
const msg = getErrorMessage(e);
const filePath = extractFilePathFromError(e);
const sourceRef = filePath ? ` "${filePath}"` : "";
if (msg.includes("import.meta") && msg.includes("outside a module")) {
return (`Transformer source${sourceRef} uses "import.meta" but was loaded as CommonJS. ` +
`Rename the transformer or helper to ".mts", set the transformer tsConfig "module" to ESNext, ` +
`or use NodeNext with "type": "module" in the nearest package.json.`);
}
if (/await is only valid/i.test(msg) && /module/i.test(msg)) {
return (`Transformer source${sourceRef} uses top-level "await" but was loaded as CommonJS. ` +
`Rename the transformer or helper to ".mts", set the transformer tsConfig "module" to ESNext, ` +
`or use NodeNext with "type": "module" in the nearest package.json.`);
}
if (e?.code === "ERR_REQUIRE_ASYNC_MODULE") {
return (`Transformer source${sourceRef} contains top-level "await" in its ESM graph and cannot be loaded synchronously with require(). ` +
`Move async initialization out of the top level, or open an issue if the transformer requires async module initialization.`);
}
return undefined;
}
tsp.describeEsmInCjsError = describeEsmInCjsError;
class TsPatchError extends Error {
constructor(message, diagnostic) {
super(message);
this.diagnostic = diagnostic;
}
}
tsp.TsPatchError = TsPatchError;
})(tsp || (tsp = {}));
var tsp;
(function (tsp) {
const fs = require("fs");
const path = require("path");
const url = require("url");
const moduleApi = require("module");
function isTypeScriptPath(filePath) {
return tsp.tsExtensions.includes(path.extname(filePath));
}
function isBuiltinModule(specifier) {
const builtinModules = moduleApi.builtinModules;
const normalized = specifier.replace(/^node:/, "");
return builtinModules?.includes(specifier) || builtinModules?.includes(normalized);
}
function getParentDir(parentURL) {
if (!parentURL)
return process.cwd();
if (!parentURL.startsWith("file:"))
return process.cwd();
return path.dirname(url.fileURLToPath(parentURL));
}
function maybeFileURLToPath(specifier) {
return specifier.startsWith("file:") ? url.fileURLToPath(specifier) : undefined;
}
function tryFile(filePath) {
try {
const stat = fs.statSync(filePath);
return stat.isFile() ? filePath : undefined;
}
catch {
return undefined;
}
}
function resolveTypeScriptFile(basePath) {
const ext = path.extname(basePath);
if (tsp.tsExtensions.includes(ext))
return tryFile(basePath);
const extensionReplacements = {
".js": [".ts"],
".mjs": [".mts"],
".cjs": [".cts"]
};
for (const replacementExt of extensionReplacements[ext] || []) {
const candidate = basePath.slice(0, -ext.length) + replacementExt;
const res = tryFile(candidate);
if (res)
return res;
}
if (!ext) {
for (const candidateExt of tsp.tsExtensions) {
const res = tryFile(basePath + candidateExt);
if (res)
return res;
}
for (const candidateExt of tsp.tsExtensions) {
const res = tryFile(path.join(basePath, "index" + candidateExt));
if (res)
return res;
}
}
return undefined;
}
function getTypeScriptModulePath(specifier, parentURL, registerConfig) {
if (isBuiltinModule(specifier))
return undefined;
const fileURLPath = maybeFileURLToPath(specifier);
if (fileURLPath)
return resolveTypeScriptFile(fileURLPath);
if (path.isAbsolute(specifier))
return resolveTypeScriptFile(specifier);
if (specifier.startsWith(".")) {
return resolveTypeScriptFile(path.resolve(getParentDir(parentURL), specifier));
}
const { compilerOptions, pluginConfig } = registerConfig;
if (pluginConfig.resolvePathAliases && compilerOptions?.baseUrl && compilerOptions.paths) {
const matchPath = tsp.getTsConfigPaths().createMatchPath(compilerOptions.baseUrl, compilerOptions.paths);
const matchedPath = matchPath(specifier, undefined, fs.existsSync, tsp.supportedExtensions);
if (matchedPath)
return resolveTypeScriptFile(matchedPath);
}
return undefined;
}
function getCompilerOptions(registerConfig) {
const baseOptions = registerConfig.compilerOptions || {};
const compilerOptions = {
...baseOptions,
target: baseOptions.target ?? tsp.tsShim.ScriptTarget.ES2022,
jsx: baseOptions.jsx ?? tsp.tsShim.JsxEmit.React,
esModuleInterop: baseOptions.esModuleInterop ?? true,
module: baseOptions.module ?? tsp.tsShim.ModuleKind.NodeNext,
sourceMap: true,
inlineSourceMap: false,
inlineSources: true,
declaration: false,
declarationMap: false,
emitDeclarationOnly: false,
noEmit: false,
outDir: undefined,
outFile: undefined,
composite: undefined,
declarationDir: undefined
};
if (baseOptions.module === undefined) {
compilerOptions.moduleResolution = baseOptions.moduleResolution ?? tsp.tsShim.ModuleResolutionKind.NodeNext;
}
else if (baseOptions.moduleResolution !== undefined) {
compilerOptions.moduleResolution = baseOptions.moduleResolution;
}
return compilerOptions;
}
function getTranspileCompilerOptions(format, compilerOptions) {
const transpileOptions = {
...compilerOptions,
module: format === "module" ? tsp.tsShim.ModuleKind.ESNext : tsp.tsShim.ModuleKind.CommonJS
};
delete transpileOptions.moduleResolution;
return transpileOptions;
}
function formatDiagnostics(diagnostics) {
const diagnosticHost = {
getCurrentDirectory: () => process.cwd(),
getCanonicalFileName: (fileName) => fileName,
getNewLine: () => tsp.tsShim.sys.newLine
};
const formatter = tsp.tsShim.formatDiagnosticsWithColorAndContext || tsp.tsShim.formatDiagnostics;
return formatter(diagnostics, diagnosticHost);
}
function getTranspileDiagnostics(diagnostics) {
const ignoredDiagnostics = new Set([6059, 18002, 18003]);
return (diagnostics || []).filter(diagnostic => !ignoredDiagnostics.has(diagnostic.code));
}
function inlineSourceMap(outputText, sourceMapText) {
if (!sourceMapText)
return outputText;
const sourceMapComment = `//# sourceMappingURL=data:application/json;charset=utf-8;base64,${Buffer.from(sourceMapText, "utf8").toString("base64")}`;
const outputWithoutSourceMap = outputText.replace(/\r?\n?\/\/# sourceMappingURL=.*(?:\r?\n)?$/, "");
return `${outputWithoutSourceMap}\n${sourceMapComment}`;
}
function compileTransformer(filePath, registerConfig) {
const sourceText = fs.readFileSync(filePath, "utf8");
const compilerOptions = getCompilerOptions(registerConfig);
const format = tsp.getEmitFormat(filePath, sourceText, compilerOptions);
if (format === "unsupported") {
const moduleKind = compilerOptions.module === undefined ? "undefined" : tsp.tsShim.ModuleKind[compilerOptions.module];
throw new tsp.TsPatchError(`Transformer "${filePath}" uses tsConfig "module" setting "${moduleKind}", which is not loadable in Node. ` +
`Use CommonJS, ES2015 or later, ESNext, Node16, NodeNext, or a module-specific extension such as ".cts" or ".mts".`);
}
const result = tsp.tsShim.transpileModule(sourceText, {
compilerOptions: getTranspileCompilerOptions(format, compilerOptions),
fileName: filePath,
reportDiagnostics: true
});
const diagnostics = getTranspileDiagnostics(result.diagnostics);
if (diagnostics.length) {
throw new tsp.TsPatchError(`Unable to compile TypeScript transformer "${filePath}":\n` + formatDiagnostics(diagnostics));
}
return {
format,
source: inlineSourceMap(result.outputText, result.sourceMapText)
};
}
function registerCompilerLoader(registerConfig) {
if (typeof moduleApi.registerHooks !== "function") {
throw new tsp.TsPatchError("TypeScript transformer loading requires Node.js >=22.15.0 with module.registerHooks().");
}
const compiledFiles = new Map();
const hooks = moduleApi.registerHooks({
resolve(specifier, context, nextResolve) {
const parentURL = context?.parentURL;
const explicitTsPath = getTypeScriptModulePath(specifier, parentURL, registerConfig);
if (explicitTsPath) {
return {
url: url.pathToFileURL(explicitTsPath).href,
shortCircuit: true
};
}
try {
const resolved = nextResolve(specifier, context);
if (resolved?.url?.startsWith("file:")) {
const resolvedPath = url.fileURLToPath(resolved.url);
if (isTypeScriptPath(resolvedPath)) {
return {
url: resolved.url,
shortCircuit: true
};
}
}
return resolved;
}
catch (e) {
const fallbackPath = getTypeScriptModulePath(specifier, parentURL, registerConfig);
if (fallbackPath) {
return {
url: url.pathToFileURL(fallbackPath).href,
shortCircuit: true
};
}
throw e;
}
},
load(fileURL, context, nextLoad) {
if (!fileURL.startsWith("file:"))
return nextLoad(fileURL, context);
const filePath = url.fileURLToPath(fileURL);
if (!isTypeScriptPath(filePath))
return nextLoad(fileURL, context);
let compiledFile = compiledFiles.get(filePath);
if (!compiledFile) {
compiledFile = compileTransformer(filePath, registerConfig);
compiledFiles.set(filePath, compiledFile);
}
return {
format: compiledFile.format,
source: compiledFile.source,
shortCircuit: true
};
}
});
return () => {
compiledFiles.clear();
hooks.deregister();
};
}
tsp.registerCompilerLoader = registerCompilerLoader;
})(tsp || (tsp = {}));
var tsp;
(function (tsp) {
const crypto = require("crypto");
function createTransformersFromPattern(opt) {
const { factory, config, program, ls, registerConfig } = opt;
const { transform, after, afterDeclarations, name, type, transformProgram, ...cleanConfig } = config;
if (!transform)
throw new tsp.TsPatchError("Not a valid config entry: \"transform\" key not found");
const transformerKind = after ? "after" :
afterDeclarations ? "afterDeclarations" :
"before";
let pluginFactoryResult;
switch (config.type) {
case "ls":
if (!ls)
throw new tsp.TsPatchError(`Plugin ${transform} needs a LanguageService`);
pluginFactoryResult = factory(ls, cleanConfig);
break;
case "config":
pluginFactoryResult = factory(cleanConfig);
break;
case "compilerOptions":
pluginFactoryResult = factory(program.getCompilerOptions(), cleanConfig);
break;
case "checker":
pluginFactoryResult = factory(program.getTypeChecker(), cleanConfig);
break;
case undefined:
case "program":
const { addDiagnostic, removeDiagnostic, diagnostics } = tsp.diagnosticExtrasFactory(program);
pluginFactoryResult = factory(program, cleanConfig, {
ts: tsp.getTsInstance(),
addDiagnostic,
removeDiagnostic,
diagnostics,
library: tsp.currentLibrary
});
break;
case "raw":
pluginFactoryResult = (ctx) => factory(ctx, program, cleanConfig);
break;
default:
throw new tsp.TsPatchError(`Invalid plugin type found in tsconfig.json: '${config.type}'`);
}
let transformerFactories;
switch (typeof pluginFactoryResult) {
case "function":
transformerFactories = [pluginFactoryResult];
break;
case "object":
const factoryOrFactories = pluginFactoryResult[transformerKind];
if (typeof factoryOrFactories === "function") {
transformerFactories = [pluginFactoryResult[transformerKind]];
break;
}
else if (Array.isArray(factoryOrFactories)) {
transformerFactories = [...factoryOrFactories];
break;
}
default:
throw new tsp.TsPatchError(`Invalid plugin result: expected a function or an object with a '${transformerKind}' property`);
}
const wrappedFactories = [];
for (const transformerFactory of transformerFactories) {
if (!transformerFactory || typeof transformerFactory !== "function")
throw new tsp.TsPatchError(`Invalid plugin entry point! Expected a transformer factory function or an object with a '${transformerKind}' property`);
const wrapper = wrapTransformerFactory(transformerFactory, registerConfig, true);
wrappedFactories.push(wrapper);
}
const res = {
[transformerKind]: wrappedFactories
};
return res;
}
function wrapTransformerFactory(transformerFn, requireConfig, wrapInnerFunction) {
const wrapper = function tspWrappedFactory(...args) {
let res;
try {
tsp.registerPlugin(requireConfig);
if (!wrapInnerFunction) {
res = transformerFn(...args);
}
else {
const resFn = transformerFn(...args);
if (typeof resFn !== "function")
throw new tsp.TsPatchError("Invalid plugin: expected a function");
res = wrapTransformerFactory(resFn, requireConfig, false);
}
}
catch (e) {
const hint = tsp.describeEsmInCjsError(e);
if (hint)
throw new tsp.TsPatchError(hint);
throw e;
}
finally {
tsp.unregisterPlugin();
}
return res;
};
return wrapper;
}
class PluginCreator {
constructor(configs, options) {
this.plugins = [];
this.configs = configs;
this.options = options;
const { resolveBaseDir } = options;
this.plugins = configs
.filter(config => config.transform !== undefined)
.map(config => new tsp.TspPlugin(config, { resolveBaseDir }));
this.needsTscJsDocParsing = this.plugins.some(plugin => plugin.packageConfig?.tscOptions?.parseAllJsDoc === true);
}
mergeTransformers(into, source) {
const slice = (input) => (Array.isArray(input) ? input.slice() : [input]);
if (source.before)
into.before.push(...slice(source.before));
if (source.after)
into.after.push(...slice(source.after));
if (source.afterDeclarations)
into.afterDeclarations.push(...slice(source.afterDeclarations));
return this;
}
createSourceTransformers(params, customTransformers) {
const transformers = { before: [], after: [], afterDeclarations: [] };
const [ls, program] = ("ls" in params) ? [params.ls, params.ls.getProgram()] : [void 0, params.program];
for (const plugin of this.plugins) {
if (plugin.kind !== "SourceTransformer")
continue;
const { config } = plugin;
const createFactoryResult = plugin.createFactory();
if (!createFactoryResult)
continue;
const { factory, registerConfig } = createFactoryResult;
this.mergeTransformers(transformers, createTransformersFromPattern({
factory: factory,
registerConfig,
config,
program,
ls
}));
}
if (customTransformers)
this.mergeTransformers(transformers, customTransformers);
return transformers;
}
createProgramTransformers() {
const res = new Map();
for (const plugin of this.plugins) {
if (plugin.kind !== "ProgramTransformer")
continue;
const { config } = plugin;
const createFactoryResult = plugin.createFactory();
if (createFactoryResult === undefined)
continue;
const { registerConfig, factory: unwrappedFactory } = createFactoryResult;
const factory = wrapTransformerFactory(unwrappedFactory, registerConfig, false);
const transformerKey = crypto
.createHash("md5")
.update(JSON.stringify({ factory, config }))
.digest("hex");
res.set(transformerKey, [factory, config]);
}
return res;
}
}
tsp.PluginCreator = PluginCreator;
})(tsp || (tsp = {}));
var tsp;
(function (tsp) {
const path = require("path");
const fs = require("fs");
const requireStack = [];
function getPackagePath(entryFilePath) {
let currentDir = path.dirname(entryFilePath);
const seenPaths = new Set();
while (currentDir !== path.parse(currentDir).root) {
if (seenPaths.has(currentDir))
return undefined;
seenPaths.add(currentDir);
const potentialPkgPath = path.join(currentDir, "package.json");
if (fs.existsSync(potentialPkgPath))
return potentialPkgPath;
currentDir = path.resolve(currentDir, "..");
}
return undefined;
}
class TspPlugin {
constructor(config, createOptions) {
this.config = { ...config };
this.validateConfig();
this._createOptions = createOptions;
this.importKey = config.import || "default";
this.kind = config.transformProgram === true ? "ProgramTransformer" : "SourceTransformer";
const { resolveBaseDir } = createOptions;
const configTransformValue = config.transform;
this.tsConfigPath = config.tsConfig && path.resolve(resolveBaseDir, config.tsConfig);
const entryFilePath = require.resolve(configTransformValue, { paths: [resolveBaseDir] });
this.entryFilePath = entryFilePath;
let pluginPackageConfig;
const modulePackagePath = getPackagePath(entryFilePath);
if (modulePackagePath) {
const modulePkgJsonContent = fs.readFileSync(modulePackagePath, "utf8");
const modulePkgJson = JSON.parse(modulePkgJsonContent);
pluginPackageConfig = modulePkgJson.tsp;
if (pluginPackageConfig === null || typeof pluginPackageConfig !== "object")
pluginPackageConfig = undefined;
}
this.packageConfig = pluginPackageConfig;
}
validateConfig() {
const { config } = this;
const configTransformValue = config.transform;
if (!configTransformValue)
throw new tsp.TsPatchError(`Invalid plugin config: missing "transform" value`);
if (config.resolvePathAliases && !config.tsConfig) {
console.warn(`[ts-patch] Warning: resolvePathAliases needs a tsConfig value pointing to a tsconfig.json for transformer" ${configTransformValue}.`);
}
}
createFactory() {
const { entryFilePath, config, tsConfigPath, importKey } = this;
const configTransformValue = config.transform;
if (requireStack.includes(entryFilePath))
return;
requireStack.push(entryFilePath);
const isTs = configTransformValue.match(/\.[mc]?ts$/) != null;
const registerConfig = {
isTs,
tsConfig: tsConfigPath,
pluginConfig: config
};
tsp.registerPlugin(registerConfig);
try {
const commonjsModule = loadEntryFile();
const factoryModule = (typeof commonjsModule === "function") ? { default: commonjsModule } : commonjsModule;
const factory = factoryModule[importKey];
if (!factory)
throw new tsp.TsPatchError(`tsconfig.json > plugins: "${configTransformValue}" does not have an export "${importKey}": ` +
require("util").inspect(factoryModule));
if (typeof factory !== "function") {
throw new tsp.TsPatchError(`tsconfig.json > plugins: "${configTransformValue}" export "${importKey}" is not a plugin: ` +
require("util").inspect(factory));
}
return {
factory,
registerConfig: registerConfig
};
}
finally {
requireStack.pop();
tsp.unregisterPlugin();
}
function loadEntryFile() {
try {
return require(entryFilePath);
}
catch (e) {
const hint = tsp.describeEsmInCjsError(e);
if (hint)
throw new tsp.TsPatchError(hint);
throw e;
}
}
}
}
tsp.TspPlugin = TspPlugin;
})(tsp || (tsp = {}));
var tsp;
(function (tsp) {
const path = require("path");
let configStack = [];
function getTsConfigPaths() {
try {
return require("tsconfig-paths");
}
catch (e) {
if (e.code === "MODULE_NOT_FOUND")
throw new tsp.TsPatchError(`resolvePathAliases requires the library: tsconfig-paths. ` +
`Add tsconfig-paths as a (dev)-dependency or install globally.`);
else
throw e;
}
}
tsp.getTsConfigPaths = getTsConfigPaths;
function getCompilerOptions(tsConfig) {
const configFile = tsp.tsShim.readConfigFile(tsConfig, tsp.tsShim.sys.readFile);
const parsedConfig = configFile && tsp.tsShim.parseJsonConfigFileContent(configFile.config, tsp.tsShim.sys, path.dirname(tsConfig));
return parsedConfig.options;
}
function unregisterPlugin() {
const activeRegisterConfig = configStack.pop();
if (activeRegisterConfig.tsConfigPathsCleanup) {
activeRegisterConfig.tsConfigPathsCleanup();
delete activeRegisterConfig.tsConfigPathsCleanup;
}
if (activeRegisterConfig.compilerLoaderCleanup) {
activeRegisterConfig.compilerLoaderCleanup();
delete activeRegisterConfig.compilerLoaderCleanup;
}
}
tsp.unregisterPlugin = unregisterPlugin;
function registerPlugin(registerConfig) {
if (!registerConfig)
throw new tsp.TsPatchError("requireConfig is required");
configStack.push(registerConfig);
const { isTs, tsConfig, pluginConfig } = registerConfig;
if (tsConfig && pluginConfig.resolvePathAliases) {
registerConfig.compilerOptions ?? (registerConfig.compilerOptions = getCompilerOptions(tsConfig));
const { paths, baseUrl } = registerConfig.compilerOptions;
if (paths && baseUrl) {
registerConfig.tsConfigPathsCleanup = getTsConfigPaths().register({ baseUrl, paths });
}
}
if (isTs) {
if (tsConfig)
registerConfig.compilerOptions ?? (registerConfig.compilerOptions = getCompilerOptions(tsConfig));
registerConfig.compilerLoaderCleanup = tsp.registerCompilerLoader(registerConfig);
}
}
tsp.registerPlugin = registerPlugin;
})(tsp || (tsp = {}));
var tsp;
(function (tsp) {
function getCreateSourceFileOptions(filePath, compilerOptions) {
const implied = tsp.tsShim.getImpliedNodeFormatForFileWorker(filePath, undefined, tsp.tsShim.sys, compilerOptions);
return {
...(typeof implied === "object" ? implied : { impliedNodeFormat: implied }),
languageVersion: tsp.tsShim.getEmitScriptTarget(compilerOptions),
setExternalModuleIndicator: tsp.tsShim.getSetExternalModuleIndicator(compilerOptions)
};
}
function createFormatSourceFile(filePath, sourceText, compilerOptions) {
const sourceFileOptions = getCreateSourceFileOptions(filePath, compilerOptions);
const sourceFile = tsp.tsShim.createSourceFile(filePath, sourceText, sourceFileOptions, false);
const internalSourceFile = sourceFile;
internalSourceFile.packageJsonLocations = sourceFileOptions.packageJsonLocations;
internalSourceFile.packageJsonScope = sourceFileOptions.packageJsonScope;
return sourceFile;
}
function getEmitFormat(filePath, sourceText, compilerOptions) {
const sourceFile = createFormatSourceFile(filePath, sourceText, compilerOptions);
const emitKind = tsp.tsShim.getEmitModuleFormatOfFileWorker(sourceFile, compilerOptions);
switch (emitKind) {
case undefined:
case tsp.tsShim.ModuleKind.None:
case tsp.tsShim.ModuleKind.CommonJS:
return "commonjs";
case tsp.tsShim.ModuleKind.AMD:
case tsp.tsShim.ModuleKind.UMD:
case tsp.tsShim.ModuleKind.System:
case tsp.tsShim.ModuleKind.Preserve:
return "unsupported";
case tsp.tsShim.ModuleKind.ES2015:
case tsp.tsShim.ModuleKind.ES2020:
case tsp.tsShim.ModuleKind.ES2022:
case tsp.tsShim.ModuleKind.ESNext:
default:
return "module";
}
}
tsp.getEmitFormat = getEmitFormat;
})(tsp || (tsp = {}));
var tsp;
(function (tsp) {
const activeProgramTransformers = new Set();
const { dirname } = require("path");
function getProjectDir(compilerOptions) {
return compilerOptions.configFilePath && dirname(compilerOptions.configFilePath);
}
function getProjectConfig(compilerOptions, rootFileNames) {
let configFilePath = compilerOptions.configFilePath;
let projectDir = getProjectDir(compilerOptions);
if (configFilePath === undefined) {
const baseDir = (rootFileNames.length > 0) ? dirname(rootFileNames[0]) : projectDir ?? process.cwd();
configFilePath = tsp.tsShim.findConfigFile(baseDir, tsp.tsShim.sys.fileExists);
if (configFilePath) {
const config = readConfig(configFilePath);
compilerOptions = { ...config.options, ...compilerOptions };
projectDir = getProjectDir(compilerOptions);
}
}
return ({ projectDir, compilerOptions });
}
function readConfig(configFileNamePath) {
const projectDir = dirname(configFileNamePath);
const result = tsp.tsShim.readConfigFile(configFileNamePath, tsp.tsShim.sys.readFile);
if (result.error)
throw new tsp.TsPatchError("Error in tsconfig.json: " + result.error.messageText);
return tsp.tsShim.parseJsonConfigFileContent(result.config, tsp.tsShim.sys, projectDir, undefined, configFileNamePath);
}
function preparePluginsFromCompilerOptions(plugins) {
if (!plugins)
return [];
if ((plugins.length === 1) && plugins[0].customTransformers) {
const { before = [], after = [] } = plugins[0].customTransformers;
return [
...before.map((item) => ({ transform: item })),
...after.map((item) => ({ transform: item, after: true })),
];
}
return plugins;
}
function createProgram(rootNamesOrOptions, options, host, oldProgram, configFileParsingDiagnostics) {
let rootNames;
const createOpts = !Array.isArray(rootNamesOrOptions) ? rootNamesOrOptions : void 0;
if (createOpts) {
rootNames = createOpts.rootNames;
options = createOpts.options;
host = createOpts.host;
oldProgram = createOpts.oldProgram;
configFileParsingDiagnostics = createOpts.configFileParsingDiagnostics;
}
else {
options = options;
rootNames = rootNamesOrOptions;
}
const projectConfig = getProjectConfig(options, rootNames);
if (["tsc", "tsserver", "tsserverlibrary"].includes(tsp.currentLibrary)) {
options = projectConfig.compilerOptions;
if (createOpts)
createOpts.options = options;
}
const plugins = preparePluginsFromCompilerOptions(options.plugins);
const pluginCreator = new tsp.PluginCreator(plugins, { resolveBaseDir: projectConfig.projectDir ?? process.cwd() });
if (tsp.currentLibrary === "tsc" && tsp.tsShim.JSDocParsingMode && pluginCreator.needsTscJsDocParsing) {
host.jsDocParsingMode = tsp.tsShim.JSDocParsingMode.ParseAll;
}
let program = createOpts ?
tsp.tsShim.originalCreateProgram(createOpts) :
tsp.tsShim.originalCreateProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics);
const programTransformers = pluginCreator.createProgramTransformers();
for (const [transformerKey, [programTransformer, config]] of programTransformers) {
if (activeProgramTransformers.has(transformerKey))
continue;
activeProgramTransformers.add(transformerKey);
const newProgram = programTransformer(program, host, config, { ts: tsp.getTsInstance() });
if (typeof newProgram?.["emit"] === "function")
program = newProgram;
activeProgramTransformers.delete(transformerKey);
}
if (!program.originalEmit) {
program.originalEmit = program.emit;
program.emit = newEmit;
}
function newEmit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers, ...additionalArgs) {
const transformers = pluginCreator.createSourceTransformers({ program }, customTransformers);
const result = program.originalEmit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, transformers, ...additionalArgs);
for (const diagnostic of tsp.diagnosticMap.get(program) || [])
if (!result.diagnostics.includes(diagnostic))
result.diagnostics.push(diagnostic);
return result;
}
return program;
}
tsp.createProgram = createProgram;
})(tsp || (tsp = {}));
var tsp;
(function (tsp) {
tsp.tsShim = new Proxy({}, {
get(_, key) {
const target = tsp.getTsInstance();
if (target) {
return target[key];
}
else {
try {
return eval(key);
}
catch (e) {
throw new tsp.TsPatchError(`Failed to find "${key}" in TypeScript shim`, e);
}
}
},
});
})(tsp || (tsp = {}));
return tsp;
})();