import * as path from 'path'; import path__default, { resolve as resolve$1, dirname, relative } from 'path'; import { createFilter } from '@rollup/pluginutils'; import typescript$1 from 'typescript'; import { fileURLToPath } from 'url'; import resolve from 'resolve'; import fs, { readFileSync, promises } from 'fs'; /** * Create a format diagnostics host to use with the Typescript type checking APIs. * Typescript hosts are used to represent the user's system, * with an API for checking case sensitivity etc. * @param compilerOptions Typescript compiler options. Affects functions such as `getNewLine`. * @see https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API */ function createFormattingHost(ts, compilerOptions) { return { /** Returns the compiler options for the project. */ getCompilationSettings: () => compilerOptions, /** Returns the current working directory. */ getCurrentDirectory: () => process.cwd(), /** Returns the string that corresponds with the selected `NewLineKind`. */ getNewLine() { switch (compilerOptions.newLine) { case ts.NewLineKind.CarriageReturnLineFeed: return '\r\n'; case ts.NewLineKind.LineFeed: return '\n'; default: return ts.sys.newLine; } }, /** Returns a lower case name on case insensitive systems, otherwise the original name. */ getCanonicalFileName: (fileName) => ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase() }; } /** * Create a helper for resolving modules using Typescript. * @param ts custom typescript implementation * @param host Typescript host that extends {@link ModuleResolutionHost} * @param filter * with methods for sanitizing filenames and getting compiler options. */ function createModuleResolver(ts, host, filter) { const compilerOptions = host.getCompilationSettings(); const cache = ts.createModuleResolutionCache(process.cwd(), host.getCanonicalFileName, compilerOptions); const moduleHost = { ...ts.sys, ...host }; return (moduleName, containingFile, redirectedReference, mode) => { const { resolvedModule } = ts.resolveModuleName(moduleName, containingFile, compilerOptions, moduleHost, cache, redirectedReference, mode); /** * If the module's path contains 'node_modules', ts considers it an external library and refuses to compile it, * so we have to change the value of `isExternalLibraryImport` to false if it's true * */ if ((resolvedModule === null || resolvedModule === void 0 ? void 0 : resolvedModule.isExternalLibraryImport) && filter(resolvedModule === null || resolvedModule === void 0 ? void 0 : resolvedModule.resolvedFileName)) { resolvedModule.isExternalLibraryImport = false; } return resolvedModule; }; } // const resolveIdAsync = (file: string, opts: AsyncOpts) => // new Promise((fulfil, reject) => // resolveId(file, opts, (err, contents) => // err || typeof contents === 'undefined' ? reject(err) : fulfil(contents) // ) // ); const resolveId = (file, opts) => resolve.sync(file, opts); /** * Returns code asynchronously for the tslib helper library. */ const getTsLibPath = () => { // Note: This isn't preferable, but we've no other way to test this bit. Removing the tslib devDep // during the test run doesn't work due to the nature of the pnpm flat node_modules, and // other workspace dependencies that depenend upon tslib. try { // eslint-disable-next-line no-underscore-dangle return resolveId(process.env.__TSLIB_TEST_PATH__ || 'tslib/tslib.es6.js', { // @ts-ignore import.meta.url is allowed because the Rollup plugin injects the correct module format basedir: fileURLToPath(new URL('.', import.meta.url)) }); } catch (_) { return null; } }; /** * Separate the Rollup plugin options from the Typescript compiler options, * and normalize the Rollup options. * @returns Object with normalized options: * - `filter`: Checks if a file should be included. * - `tsconfig`: Path to a tsconfig, or directive to ignore tsconfig. * - `compilerOptions`: Custom Typescript compiler options that override tsconfig. * - `typescript`: Instance of Typescript library (possibly custom). * - `tslib`: ESM code from the tslib helper library (possibly custom). */ const getPluginOptions = (options) => { const { cacheDir, exclude, include, filterRoot, noForceEmit, transformers, tsconfig, tslib, typescript, outputToFilesystem, compilerOptions, // previously was compilerOptions ...extra } = options; return { cacheDir, include, exclude, filterRoot, noForceEmit: noForceEmit || false, tsconfig, compilerOptions: { ...extra, ...compilerOptions }, typescript: typescript || typescript$1, tslib: tslib || getTsLibPath(), transformers, outputToFilesystem }; }; /** * Converts a Typescript type error into an equivalent Rollup warning object. */ function diagnosticToWarning(ts, host, diagnostic) { const pluginCode = `TS${diagnostic.code}`; const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); // Build a Rollup warning object from the diagnostics object. const warning = { pluginCode, message: `@rollup/plugin-typescript ${pluginCode}: ${message}` }; if (diagnostic.file) { // Add information about the file location const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); warning.loc = { column: character + 1, line: line + 1, file: diagnostic.file.fileName }; if (host) { // Extract a code frame from Typescript const formatted = ts.formatDiagnosticsWithColorAndContext([diagnostic], host); // Typescript only exposes this formatter as a string prefixed with the flattened message. // We need to remove it here since Rollup treats the properties as separate parts. let frame = formatted.slice(formatted.indexOf(message) + message.length); const newLine = host.getNewLine(); if (frame.startsWith(newLine)) { frame = frame.slice(frame.indexOf(newLine) + newLine.length); } warning.frame = frame; } } return warning; } const DEFAULT_COMPILER_OPTIONS = { module: 'esnext', skipLibCheck: true }; const OVERRIDABLE_EMIT_COMPILER_OPTIONS = { noEmit: false, emitDeclarationOnly: false }; const FORCED_COMPILER_OPTIONS = { // Always use tslib noEmitHelpers: true, importHelpers: true, // Preventing Typescript from resolving code may break compilation noResolve: false }; /* eslint-disable no-param-reassign */ const DIRECTORY_PROPS = ['outDir', 'declarationDir']; /** * Mutates the compiler options to convert paths from relative to absolute. * This should be used with compiler options passed through the Rollup plugin options, * not those found from loading a tsconfig.json file. * @param compilerOptions Compiler options to _mutate_. * @param relativeTo Paths are resolved relative to this path. */ function makePathsAbsolute(compilerOptions, relativeTo) { for (const pathProp of DIRECTORY_PROPS) { if (compilerOptions[pathProp]) { compilerOptions[pathProp] = resolve$1(relativeTo, compilerOptions[pathProp]); } } } /** * Mutates the compiler options to normalize some values for Rollup. * @param compilerOptions Compiler options to _mutate_. * @returns True if the source map compiler option was not initially set. */ function normalizeCompilerOptions(ts, compilerOptions) { let autoSetSourceMap = false; if (compilerOptions.inlineSourceMap) { // Force separate source map files for Rollup to work with. compilerOptions.sourceMap = true; compilerOptions.inlineSourceMap = false; } else if (typeof compilerOptions.sourceMap !== 'boolean') { // Default to using source maps. // If the plugin user sets sourceMap to false we keep that option. compilerOptions.sourceMap = true; // Using inlineSources to make sure typescript generate source content // instead of source path. compilerOptions.inlineSources = true; autoSetSourceMap = true; } switch (compilerOptions.module) { case ts.ModuleKind.ES2015: case ts.ModuleKind.ESNext: case ts.ModuleKind.Node16: case ts.ModuleKind.NodeNext: case ts.ModuleKind.CommonJS: // OK module type return autoSetSourceMap; case ts.ModuleKind.None: case ts.ModuleKind.AMD: case ts.ModuleKind.UMD: case ts.ModuleKind.System: { // Invalid module type const moduleType = ts.ModuleKind[compilerOptions.module]; throw new Error(`@rollup/plugin-typescript: The module kind should be 'ES2015', 'ESNext', 'node16' or 'nodenext', found: '${moduleType}'`); } default: // Unknown or unspecified module type, force ESNext compilerOptions.module = ts.ModuleKind.ESNext; } return autoSetSourceMap; } const { ModuleKind: ModuleKind$1, ModuleResolutionKind } = typescript$1; function makeForcedCompilerOptions(noForceEmit) { return { ...FORCED_COMPILER_OPTIONS, ...(noForceEmit ? {} : OVERRIDABLE_EMIT_COMPILER_OPTIONS) }; } /** * Finds the path to the tsconfig file relative to the current working directory. * @param ts Custom typescript implementation * @param relativePath Relative tsconfig path given by the user. * If `false` is passed, then a null path is returned. * @returns The absolute path, or null if the file does not exist. */ function getTsConfigPath(ts, relativePath) { if (relativePath === false) return null; // Resolve path to file. `tsConfigOption` defaults to 'tsconfig.json'. const tsConfigPath = resolve$1(process.cwd(), relativePath || 'tsconfig.json'); if (!ts.sys.fileExists(tsConfigPath)) { if (relativePath) { // If an explicit path was provided but no file was found, throw throw new Error(`Could not find specified tsconfig.json at ${tsConfigPath}`); } else { return null; } } return tsConfigPath; } /** * Tries to read the tsconfig file at `tsConfigPath`. * @param ts Custom typescript implementation * @param tsConfigPath Absolute path to tsconfig JSON file. */ function readTsConfigFile(ts, tsConfigPath) { const { config, error } = ts.readConfigFile(tsConfigPath, (path) => readFileSync(path, 'utf8')); if (error) { throw Object.assign(Error(), diagnosticToWarning(ts, null, error)); } return config || {}; } /** * Returns true if any of the `compilerOptions` contain an enum value (i.e.: ts.ScriptKind) rather than a string. * This indicates that the internal CompilerOptions type is used rather than the JsonCompilerOptions. */ function containsEnumOptions(compilerOptions) { const enums = [ 'module', 'target', 'jsx', 'moduleResolution', 'newLine' ]; return enums.some((prop) => prop in compilerOptions && typeof compilerOptions[prop] === 'number'); } /** * The module resolution kind is a function of the resolved `compilerOptions.module`. * This needs to be set explicitly for `resolveModuleName` to select the correct resolution method */ function setModuleResolutionKind(parsedConfig) { const moduleKind = parsedConfig.options.module; // Fallback if `parsedConfig.options.moduleResolution` is not set const moduleResolution = moduleKind === ModuleKind$1.Node16 ? ModuleResolutionKind.Node16 : moduleKind === ModuleKind$1.NodeNext ? ModuleResolutionKind.NodeNext : ModuleResolutionKind.NodeJs; return { ...parsedConfig, options: { moduleResolution, ...parsedConfig.options } }; } const configCache = new Map(); /** * Parse the Typescript config to use with the plugin. * @param ts Typescript library instance. * @param tsconfig Path to the tsconfig file, or `false` to ignore the file. * @param compilerOptions Options passed to the plugin directly for Typescript. * @param noForceEmit Whether to respect emit options from {@link tsconfig} * * @returns Parsed tsconfig.json file with some important properties: * - `options`: Parsed compiler options. * - `fileNames` Type definition files that should be included in the build. * - `errors`: Any errors from parsing the config file. */ function parseTypescriptConfig(ts, tsconfig, compilerOptions, noForceEmit) { /* eslint-disable no-undefined */ const cwd = process.cwd(); makePathsAbsolute(compilerOptions, cwd); let parsedConfig; // Resolve path to file. If file is not found, pass undefined path to `parseJsonConfigFileContent`. // eslint-disable-next-line no-undefined const tsConfigPath = getTsConfigPath(ts, tsconfig) || undefined; const tsConfigFile = tsConfigPath ? readTsConfigFile(ts, tsConfigPath) : {}; const basePath = tsConfigPath ? dirname(tsConfigPath) : cwd; // If compilerOptions has enums, it represents an CompilerOptions object instead of parsed JSON. // This determines where the data is passed to the parser. if (containsEnumOptions(compilerOptions)) { parsedConfig = setModuleResolutionKind(ts.parseJsonConfigFileContent({ ...tsConfigFile, compilerOptions: { ...DEFAULT_COMPILER_OPTIONS, ...tsConfigFile.compilerOptions } }, ts.sys, basePath, { ...compilerOptions, ...makeForcedCompilerOptions(noForceEmit) }, tsConfigPath, undefined, undefined, configCache)); } else { parsedConfig = setModuleResolutionKind(ts.parseJsonConfigFileContent({ ...tsConfigFile, compilerOptions: { ...DEFAULT_COMPILER_OPTIONS, ...tsConfigFile.compilerOptions, ...compilerOptions } }, ts.sys, basePath, makeForcedCompilerOptions(noForceEmit), tsConfigPath, undefined, undefined, configCache)); } const autoSetSourceMap = normalizeCompilerOptions(ts, parsedConfig.options); return { ...parsedConfig, autoSetSourceMap }; } /** * If errors are detected in the parsed options, * display all of them as warnings then emit an error. */ function emitParsedOptionsErrors(ts, context, parsedOptions) { if (parsedOptions.errors.length > 0) { parsedOptions.errors.forEach((error) => context.warn(diagnosticToWarning(ts, null, error))); context.error(`@rollup/plugin-typescript: Couldn't process compiler options`); } } /** * Validate that the `compilerOptions.sourceMap` option matches `outputOptions.sourcemap`. * @param context Rollup plugin context used to emit warnings. * @param compilerOptions Typescript compiler options. * @param outputOptions Rollup output options. * @param autoSetSourceMap True if the `compilerOptions.sourceMap` property was set to `true` * by the plugin, not the user. */ function validateSourceMap(context, compilerOptions, outputOptions, autoSetSourceMap) { if (compilerOptions.sourceMap && !outputOptions.sourcemap && !autoSetSourceMap) { context.warn(`@rollup/plugin-typescript: Rollup 'sourcemap' option must be set to generate source maps.`); } else if (!compilerOptions.sourceMap && outputOptions.sourcemap) { context.warn(`@rollup/plugin-typescript: Typescript 'sourceMap' compiler option must be set to generate source maps.`); } } /** * Validate that the out directory used by Typescript can be controlled by Rollup. * @param context Rollup plugin context used to emit errors. * @param compilerOptions Typescript compiler options. * @param outputOptions Rollup output options. */ function validatePaths(context, compilerOptions, outputOptions) { if (compilerOptions.out) { context.error(`@rollup/plugin-typescript: Deprecated Typescript compiler option 'out' is not supported. Use 'outDir' instead.`); } else if (compilerOptions.outFile) { context.error(`@rollup/plugin-typescript: Typescript compiler option 'outFile' is not supported. Use 'outDir' instead.`); } let outputDir = outputOptions.dir; if (outputOptions.file) { outputDir = dirname(outputOptions.file); } for (const dirProperty of DIRECTORY_PROPS) { if (compilerOptions[dirProperty] && outputDir) { // Checks if the given path lies within Rollup output dir const fromRollupDirToTs = relative(outputDir, compilerOptions[dirProperty]); if (fromRollupDirToTs.startsWith('..')) { if (outputOptions.dir) { context.error(`@rollup/plugin-typescript: Path of Typescript compiler option '${dirProperty}' must be located inside Rollup 'dir' option.`); } else { context.error(`@rollup/plugin-typescript: Path of Typescript compiler option '${dirProperty}' must be located inside the same directory as the Rollup 'file' option.`); } } } } if (compilerOptions.declaration || compilerOptions.declarationMap || compilerOptions.composite) { if (DIRECTORY_PROPS.every((dirProperty) => !compilerOptions[dirProperty])) { context.error(`@rollup/plugin-typescript: You are using one of Typescript's compiler options 'declaration', 'declarationMap' or 'composite'. ` + `In this case 'outDir' or 'declarationDir' must be specified to generate declaration files.`); } } } /** * Checks if the given OutputFile represents some code */ function isCodeOutputFile(name) { return !isMapOutputFile(name) && !isDeclarationOutputFile(name); } /** * Checks if the given OutputFile represents some source map */ function isMapOutputFile(name) { return name.endsWith('.map'); } /** * Checks if the given OutputFile represents some TypeScript source map */ function isTypeScriptMapOutputFile(name) { return name.endsWith('ts.map'); } /** * Checks if the given OutputFile represents some declaration */ function isDeclarationOutputFile(name) { return /\.d\.[cm]?ts$/.test(name); } /** * Returns the content of a filename either from the current * typescript compiler instance or from the cached content. * @param fileName The filename for the contents to retrieve * @param emittedFiles The files emitted in the current typescript instance * @param tsCache A cache to files cached by Typescript */ function getEmittedFile(fileName, emittedFiles, tsCache) { let code; if (fileName) { if (emittedFiles.has(fileName)) { code = emittedFiles.get(fileName); } else { code = tsCache.getCached(fileName); } } return code; } /** * Finds the corresponding emitted Javascript files for a given Typescript file. * @param id Path to the Typescript file. * @param emittedFiles Map of file names to source code, * containing files emitted by the Typescript compiler. */ function findTypescriptOutput(ts, parsedOptions, id, emittedFiles, tsCache) { const emittedFileNames = ts.getOutputFileNames(parsedOptions, id, !ts.sys.useCaseSensitiveFileNames); const codeFile = emittedFileNames.find(isCodeOutputFile); const mapFile = emittedFileNames.find(isMapOutputFile); return { code: getEmittedFile(codeFile, emittedFiles, tsCache), map: getEmittedFile(mapFile, emittedFiles, tsCache), declarations: emittedFileNames.filter((name) => name !== codeFile && name !== mapFile) }; } function normalizePath(fileName) { return fileName.split(path.win32.sep).join(path.posix.sep); } async function emitFile({ dir }, outputToFilesystem, context, filePath, fileSource) { const normalizedFilePath = normalizePath(filePath); // const normalizedPath = normalizePath(filePath); // Note: `dir` can be a value like `dist` in which case, `path.relative` could result in a value // of something like `'../.tsbuildinfo'. Our else-case below needs to mimic `path.relative` // returning a dot-notated relative path, so the first if-then branch is entered into const relativePath = dir ? path.relative(dir, normalizedFilePath) : '..'; // legal paths do not start with . nor .. : https://github.com/rollup/rollup/issues/3507#issuecomment-616495912 if (relativePath.startsWith('..')) { if (outputToFilesystem == null) { context.warn(`@rollup/plugin-typescript: outputToFilesystem option is defaulting to true.`); } if (outputToFilesystem !== false) { await promises.mkdir(path.dirname(normalizedFilePath), { recursive: true }); await promises.writeFile(normalizedFilePath, fileSource); } } else { context.emitFile({ type: 'asset', fileName: relativePath, source: fileSource }); } } // import { resolveIdAsync } from './tslib'; const { ModuleKind } = typescript$1; const pluginName = '@rollup/plugin-typescript'; const moduleErrorMessage = ` ${pluginName}: Rollup requires that TypeScript produces ES Modules. Unfortunately your configuration specifies a "module" other than "esnext". Unless you know what you're doing, please change "module" to "esnext" in the target tsconfig.json file or plugin options.`.replace(/\n/g, ''); const tsLibErrorMessage = `${pluginName}: Could not find module 'tslib', which is required by this plugin. Is it installed?`; let undef; const validModules = [ ModuleKind.ES2015, ModuleKind.ES2020, ModuleKind.ESNext, ModuleKind.Node16, ModuleKind.NodeNext, undef ]; // eslint-disable-next-line import/prefer-default-export const preflight = ({ config, context, inputPreserveModules, tslib }) => { if (!validModules.includes(config.options.module)) { context.warn(moduleErrorMessage); } if (!inputPreserveModules && tslib === null) { context.error(tsLibErrorMessage); } }; // `Cannot compile modules into 'es6' when targeting 'ES5' or lower.` const CANNOT_COMPILE_ESM = 1204; /** * Emit a Rollup warning or error for a Typescript type error. */ function emitDiagnostic(ts, context, host, diagnostic) { if (diagnostic.code === CANNOT_COMPILE_ESM) return; const { noEmitOnError } = host.getCompilationSettings(); // Build a Rollup warning object from the diagnostics object. const warning = diagnosticToWarning(ts, host, diagnostic); // Errors are fatal. Otherwise emit warnings. if (noEmitOnError && diagnostic.category === ts.DiagnosticCategory.Error) { context.error(warning); } else { context.warn(warning); } } function buildDiagnosticReporter(ts, context, host) { return function reportDiagnostics(diagnostic) { emitDiagnostic(ts, context, host, diagnostic); }; } /** * Merges all received custom transformer definitions into a single CustomTransformers object */ function mergeTransformers(builder, ...input) { // List of all transformer stages const transformerTypes = ['after', 'afterDeclarations', 'before']; const accumulator = { after: [], afterDeclarations: [], before: [] }; let program; let typeChecker; input.forEach((transformers) => { if (!transformers) { // Skip empty arguments lists return; } transformerTypes.forEach((stage) => { getTransformers(transformers[stage]).forEach((transformer) => { if (!transformer) { // Skip empty return; } if ('type' in transformer) { if (typeof transformer.factory === 'function') { // Allow custom factories to grab the extra information required program = program || builder.getProgram(); typeChecker = typeChecker || program.getTypeChecker(); let factory; if (transformer.type === 'program') { program = program || builder.getProgram(); factory = transformer.factory(program); } else { program = program || builder.getProgram(); typeChecker = typeChecker || program.getTypeChecker(); factory = transformer.factory(typeChecker); } // Forward the requested reference to the custom transformer factory if (factory) { accumulator[stage].push(factory); } } } else { // Add normal transformer factories as is accumulator[stage].push(transformer); } }); }); }); return accumulator; } function getTransformers(transformers) { return transformers || []; } const { DiagnosticCategory } = typescript$1; // @see https://github.com/microsoft/TypeScript/blob/master/src/compiler/diagnosticMessages.json // eslint-disable-next-line no-shadow var DiagnosticCode; (function (DiagnosticCode) { DiagnosticCode[DiagnosticCode["FILE_CHANGE_DETECTED"] = 6032] = "FILE_CHANGE_DETECTED"; DiagnosticCode[DiagnosticCode["FOUND_1_ERROR_WATCHING_FOR_FILE_CHANGES"] = 6193] = "FOUND_1_ERROR_WATCHING_FOR_FILE_CHANGES"; DiagnosticCode[DiagnosticCode["FOUND_N_ERRORS_WATCHING_FOR_FILE_CHANGES"] = 6194] = "FOUND_N_ERRORS_WATCHING_FOR_FILE_CHANGES"; })(DiagnosticCode || (DiagnosticCode = {})); function createDeferred(timeout) { let promise; let resolve = () => { }; if (timeout) { promise = Promise.race([ new Promise((r) => setTimeout(r, timeout, true)), new Promise((r) => (resolve = r)) ]); } else { promise = new Promise((r) => (resolve = r)); } return { promise, resolve }; } /** * Typescript watch program helper to sync Typescript watch status with Rollup hooks. */ class WatchProgramHelper { constructor() { this._startDeferred = null; this._finishDeferred = null; } watch(timeout = 1000) { // Race watcher start promise against a timeout in case Typescript and Rollup change detection is not in sync. this._startDeferred = createDeferred(timeout); this._finishDeferred = createDeferred(); } handleStatus(diagnostic) { // Fullfil deferred promises by Typescript diagnostic message codes. if (diagnostic.category === DiagnosticCategory.Message) { switch (diagnostic.code) { case DiagnosticCode.FILE_CHANGE_DETECTED: this.resolveStart(); break; case DiagnosticCode.FOUND_1_ERROR_WATCHING_FOR_FILE_CHANGES: case DiagnosticCode.FOUND_N_ERRORS_WATCHING_FOR_FILE_CHANGES: this.resolveFinish(); break; } } } resolveStart() { if (this._startDeferred) { this._startDeferred.resolve(false); this._startDeferred = null; } } resolveFinish() { if (this._finishDeferred) { this._finishDeferred.resolve(false); this._finishDeferred = null; } } async wait() { var _a; if (this._startDeferred) { const timeout = await this._startDeferred.promise; // If there is no file change detected by Typescript skip deferred promises. if (timeout) { this._startDeferred = null; this._finishDeferred = null; } await ((_a = this._finishDeferred) === null || _a === void 0 ? void 0 : _a.promise); } } } /** * Create a language service host to use with the Typescript compiler & type checking APIs. * Typescript hosts are used to represent the user's system, * with an API for reading files, checking directories and case sensitivity etc. * @see https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API */ function createWatchHost(ts, context, { formatHost, parsedOptions, writeFile, status, resolveModule, transformers }) { const createProgram = ts.createEmitAndSemanticDiagnosticsBuilderProgram; const baseHost = ts.createWatchCompilerHost(parsedOptions.fileNames, parsedOptions.options, ts.sys, createProgram, buildDiagnosticReporter(ts, context, formatHost), status, parsedOptions.projectReferences); let createdTransformers; return { ...baseHost, /** Override the created program so an in-memory emit is used */ afterProgramCreate(program) { const origEmit = program.emit; // eslint-disable-next-line no-param-reassign program.emit = (targetSourceFile, _, cancellationToken, emitOnlyDtsFiles, customTransformers) => { createdTransformers !== null && createdTransformers !== void 0 ? createdTransformers : (createdTransformers = typeof transformers === 'function' ? transformers(program.getProgram()) : mergeTransformers(program, transformers, customTransformers)); return origEmit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, createdTransformers); }; return baseHost.afterProgramCreate(program); }, /** Add helper to deal with module resolution */ resolveModuleNames(moduleNames, containingFile, _reusedNames, redirectedReference, _optionsOnlyWithNewerTsVersions, containingSourceFile) { return moduleNames.map((moduleName, i) => { var _a; const mode = containingSourceFile ? (_a = ts.getModeForResolutionAtIndex) === null || _a === void 0 ? void 0 : _a.call(ts, containingSourceFile, i) : undefined; // eslint-disable-line no-undefined return resolveModule(moduleName, containingFile, redirectedReference, mode); }); } }; } function createWatchProgram(ts, context, options) { return ts.createWatchProgram(createWatchHost(ts, context, options)); } /** Creates the folders needed given a path to a file to be saved*/ const createFileFolder = (filePath) => { const folderPath = path__default.dirname(filePath); fs.mkdirSync(folderPath, { recursive: true }); }; class TSCache { constructor(cacheFolder = '.rollup.cache') { this._cacheFolder = cacheFolder; } /** Returns the path to the cached file */ cachedFilename(fileName) { return path__default.join(this._cacheFolder, fileName.replace(/^([a-zA-Z]+):/, '$1')); } /** Emits a file in the cache folder */ cacheCode(fileName, code) { const cachedPath = this.cachedFilename(fileName); createFileFolder(cachedPath); fs.writeFileSync(cachedPath, code); } /** Checks if a file is in the cache */ isCached(fileName) { return fs.existsSync(this.cachedFilename(fileName)); } /** Read a file from the cache given the output name*/ getCached(fileName) { let code; if (this.isCached(fileName)) { code = fs.readFileSync(this.cachedFilename(fileName), { encoding: 'utf-8' }); } return code; } } function typescript(options = {}) { const { cacheDir, compilerOptions, exclude, filterRoot, include, outputToFilesystem, noForceEmit, transformers, tsconfig, tslib, typescript: ts } = getPluginOptions(options); const tsCache = new TSCache(cacheDir); const emittedFiles = new Map(); const watchProgramHelper = new WatchProgramHelper(); const parsedOptions = parseTypescriptConfig(ts, tsconfig, compilerOptions, noForceEmit); const filter = createFilter(include || '{,**/}*.(cts|mts|ts|tsx)', exclude, { resolve: filterRoot !== null && filterRoot !== void 0 ? filterRoot : parsedOptions.options.rootDir }); parsedOptions.fileNames = parsedOptions.fileNames.filter(filter); const formatHost = createFormattingHost(ts, parsedOptions.options); const resolveModule = createModuleResolver(ts, formatHost, filter); let program = null; return { name: 'typescript', buildStart(rollupOptions) { emitParsedOptionsErrors(ts, this, parsedOptions); preflight({ config: parsedOptions, context: this, // TODO drop rollup@3 support and remove inputPreserveModules: rollupOptions .preserveModules, tslib }); // Fixes a memory leak https://github.com/rollup/plugins/issues/322 if (this.meta.watchMode !== true) { // eslint-disable-next-line program === null || program === void 0 ? void 0 : program.close(); program = null; } if (!program) { program = createWatchProgram(ts, this, { formatHost, resolveModule, parsedOptions, writeFile(fileName, data) { if (parsedOptions.options.composite || parsedOptions.options.incremental) { tsCache.cacheCode(fileName, data); } emittedFiles.set(fileName, data); }, status(diagnostic) { watchProgramHelper.handleStatus(diagnostic); }, transformers }); } }, watchChange(id) { if (!filter(id)) return; watchProgramHelper.watch(); }, buildEnd() { if (this.meta.watchMode !== true) { // ESLint doesn't understand optional chaining // eslint-disable-next-line program === null || program === void 0 ? void 0 : program.close(); } }, renderStart(outputOptions) { validateSourceMap(this, parsedOptions.options, outputOptions, parsedOptions.autoSetSourceMap); validatePaths(this, parsedOptions.options, outputOptions); }, resolveId(importee, importer) { if (importee === 'tslib') { return tslib; } if (!importer) return null; // Convert path from windows separators to posix separators const containingFile = normalizePath(importer); // when using node16 or nodenext module resolution, we need to tell ts if // we are resolving to a commonjs or esnext module const mode = typeof ts.getImpliedNodeFormatForFile === 'function' ? ts.getImpliedNodeFormatForFile( // @ts-expect-error containingFile, undefined, // eslint-disable-line no-undefined { ...ts.sys, ...formatHost }, parsedOptions.options) : undefined; // eslint-disable-line no-undefined // eslint-disable-next-line no-undefined const resolved = resolveModule(importee, containingFile, undefined, mode); if (resolved) { if (/\.d\.[cm]?ts/.test(resolved.extension)) return null; if (!filter(resolved.resolvedFileName)) return null; return path.normalize(resolved.resolvedFileName); } return null; }, async load(id) { if (!filter(id)) return null; this.addWatchFile(id); await watchProgramHelper.wait(); const fileName = normalizePath(id); if (!parsedOptions.fileNames.includes(fileName)) { // Discovered new file that was not known when originally parsing the TypeScript config parsedOptions.fileNames.push(fileName); } const output = findTypescriptOutput(ts, parsedOptions, id, emittedFiles, tsCache); return output.code != null ? output : null; }, async generateBundle(outputOptions) { const declarationAndTypeScriptMapFiles = [...emittedFiles.keys()].filter((fileName) => isDeclarationOutputFile(fileName) || isTypeScriptMapOutputFile(fileName)); declarationAndTypeScriptMapFiles.forEach((id) => { const code = getEmittedFile(id, emittedFiles, tsCache); if (!code || !parsedOptions.options.declaration) { return; } let baseDir; if (outputOptions.dir) { baseDir = outputOptions.dir; } else if (outputOptions.file) { // the bundle output directory used by rollup when outputOptions.file is used instead of outputOptions.dir baseDir = path.dirname(outputOptions.file); } if (!baseDir) return; this.emitFile({ type: 'asset', fileName: normalizePath(path.relative(baseDir, id)), source: code }); }); const tsBuildInfoPath = ts.getTsBuildInfoEmitOutputFilePath(parsedOptions.options); if (tsBuildInfoPath) { const tsBuildInfoSource = emittedFiles.get(tsBuildInfoPath); // https://github.com/rollup/plugins/issues/681 if (tsBuildInfoSource) { await emitFile(outputOptions, outputToFilesystem, this, tsBuildInfoPath, tsBuildInfoSource); } } } }; } export { typescript as default }; //# sourceMappingURL=index.js.map