ts-jest
Version:
A Jest transformer with source map support that lets you use Jest to test projects written in TypeScript
171 lines (170 loc) • 7.49 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.tsTranspileModule = exports.isModernNodeModuleKind = void 0;
const node_path_1 = __importDefault(require("node:path"));
const typescript_1 = __importDefault(require("typescript"));
const messages_1 = require("../../utils/messages");
const barebonesLibContent = `/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
interface CallableFunction {}
interface NewableFunction {}
interface IArguments {}
interface Number {}
interface Object {}
interface RegExp {}
interface String {}
interface Array<T> { length: number; [n: number]: T; }
interface SymbolConstructor {
(desc?: string | number): symbol;
for(name: string): symbol;
readonly toStringTag: symbol;
}
declare var Symbol: SymbolConstructor;
interface Symbol {
readonly [Symbol.toStringTag]: string;
}`;
const barebonesLibName = 'lib.d.ts';
let barebonesLibSourceFile;
const carriageReturnLineFeed = '\r\n';
const lineFeed = '\n';
function getNewLineCharacter(options) {
switch (options.newLine) {
case typescript_1.default.NewLineKind.CarriageReturnLineFeed:
return carriageReturnLineFeed;
case typescript_1.default.NewLineKind.LineFeed:
default:
return lineFeed;
}
}
const isModernNodeModuleKind = (module) => {
return module ? [typescript_1.default.ModuleKind.Node16, /* ModuleKind.Node18 */ 101, typescript_1.default.ModuleKind.NodeNext].includes(module) : false;
};
exports.isModernNodeModuleKind = isModernNodeModuleKind;
const shouldCheckProjectPkgJsonContent = (fileName, moduleKind) => {
return fileName.endsWith('package.json') && (0, exports.isModernNodeModuleKind)(moduleKind);
};
/**
* Copy source code of {@link ts.transpileModule} from {@link https://github.com/microsoft/TypeScript/blob/main/src/services/transpile.ts}
* with extra modifications:
* - Remove generation of declaration files
* - Allow using custom AST transformers with the internal created {@link Program}
*/
const transpileWorker = (input, transpileOptions) => {
barebonesLibSourceFile ??= typescript_1.default.createSourceFile(barebonesLibName, barebonesLibContent, {
languageVersion: typescript_1.default.ScriptTarget.Latest,
});
const diagnostics = [];
const options = transpileOptions.compilerOptions
? // @ts-expect-error internal TypeScript API
typescript_1.default.fixupCompilerOptions(transpileOptions.compilerOptions, diagnostics)
: {};
// mix in default options
const defaultOptions = typescript_1.default.getDefaultCompilerOptions();
for (const key in defaultOptions) {
if (Object.hasOwn(defaultOptions, key) && options[key] === undefined) {
options[key] = defaultOptions[key];
}
}
// @ts-expect-error internal TypeScript API
for (const option of typescript_1.default.transpileOptionValueCompilerOptions) {
// Do not set redundant config options if `verbatimModuleSyntax` was supplied.
if (options.verbatimModuleSyntax && new Set(['isolatedModules']).has(option.name)) {
continue;
}
options[option.name] = option.transpileOptionValue;
}
// transpileModule does not write anything to disk so there is no need to verify that there are no conflicts between input and output paths.
options.suppressOutputPathCheck = true;
// Filename can be non-ts file.
options.allowNonTsExtensions = true;
options.declaration = false;
options.declarationMap = false;
const newLine = getNewLineCharacter(options);
// if jsx is specified then treat file as .tsx
const inputFileName = transpileOptions.fileName ?? (transpileOptions.compilerOptions?.jsx ? 'module.tsx' : 'module.ts');
// Create a compilerHost object to allow the compiler to read and write files
const compilerHost = {
getSourceFile: (fileName) => {
// @ts-expect-error internal TypeScript API
if (fileName === typescript_1.default.normalizePath(inputFileName)) {
return sourceFile;
}
// @ts-expect-error internal TypeScript API
return fileName === typescript_1.default.normalizePath(barebonesLibName) ? barebonesLibSourceFile : undefined;
},
writeFile: (name, text) => {
if (node_path_1.default.extname(name) === '.map') {
sourceMapText = text;
}
else {
outputText = text;
}
},
getDefaultLibFileName: () => barebonesLibName,
useCaseSensitiveFileNames: () => false,
getCanonicalFileName: (fileName) => fileName,
getCurrentDirectory: () => '',
getNewLine: () => newLine,
fileExists: (fileName) => {
if (shouldCheckProjectPkgJsonContent(fileName, options.module)) {
return typescript_1.default.sys.fileExists(fileName);
}
return fileName === inputFileName;
},
readFile: (fileName) => {
if (shouldCheckProjectPkgJsonContent(fileName, options.module)) {
return typescript_1.default.sys.readFile(fileName);
}
return '';
},
directoryExists: () => true,
getDirectories: () => [],
};
const sourceFile = typescript_1.default.createSourceFile(inputFileName, input, {
languageVersion: options.target ?? typescript_1.default.ScriptTarget.ESNext,
impliedNodeFormat: typescript_1.default.getImpliedNodeFormatForFile(inputFileName,
/*packageJsonInfoCache*/ undefined, compilerHost, options),
// @ts-expect-error internal TypeScript API
setExternalModuleIndicator: typescript_1.default.getSetExternalModuleIndicator(options),
jsDocParsingMode: transpileOptions.jsDocParsingMode ?? typescript_1.default.JSDocParsingMode.ParseAll,
});
if (transpileOptions.moduleName) {
sourceFile.moduleName = transpileOptions.moduleName;
}
if (transpileOptions.renamedDependencies) {
// @ts-expect-error internal TypeScript API
sourceFile.renamedDependencies = new Map(Object.entries(transpileOptions.renamedDependencies));
}
// Output
let outputText;
let sourceMapText;
const inputs = [inputFileName];
const program = typescript_1.default.createProgram(inputs, options, compilerHost);
if (transpileOptions.reportDiagnostics) {
diagnostics.push(...program.getSyntacticDiagnostics(sourceFile));
}
diagnostics.push(...program.getOptionsDiagnostics());
// Emit
const result = program.emit(
/*targetSourceFile*/ undefined,
/*writeFile*/ undefined,
/*cancellationToken*/ undefined,
/*emitOnlyDtsFiles*/ undefined, transpileOptions.transformers?.(program));
diagnostics.push(...result.diagnostics);
if (outputText === undefined) {
diagnostics.push({
category: typescript_1.default.DiagnosticCategory.Error,
code: messages_1.TsJestDiagnosticCodes.Generic,
messageText: 'No output generated',
file: sourceFile,
start: 0,
length: 0,
});
}
return { outputText: outputText ?? '', diagnostics, sourceMapText };
};
exports.tsTranspileModule = transpileWorker;
;