UNPKG

@ui5/linter

Version:

A static code analysis tool for UI5

125 lines (122 loc) 5.11 kB
import ts from "typescript"; import path from "node:path/posix"; import { getLogger } from "@ui5/logger"; import { taskStart } from "../../../utils/perf.js"; import { createTransformer } from "./tsTransformer.js"; import { UnsupportedModuleError } from "./util.js"; const log = getLogger("linter:ui5Types:amdTranspiler:transpiler"); function createCompilerHost(sourceFiles, writtenFiles) { return { getSourceFile: (fileName) => sourceFiles.get(fileName), writeFile: (name, text) => { writtenFiles.set(name, text); }, getDefaultLibFileName: () => "lib.d.ts", useCaseSensitiveFileNames: () => false, getCanonicalFileName: (fileName) => fileName, getCurrentDirectory: () => "", getNewLine: () => "\n", fileExists: (fileName) => sourceFiles.has(fileName), readFile: () => "", directoryExists: () => true, getDirectories: () => [], }; } const compilerOptions = { checkJs: false, allowJs: true, skipLibCheck: true, target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ES2022, isolatedModules: true, sourceMap: true, suppressOutputPathCheck: true, noLib: true, noResolve: true, allowNonTsExtensions: true, }; function createProgram(inputFileNames, host) { return ts.createProgram(inputFileNames, compilerOptions, host); } export default function transpileAmdToEsm(resourcePath, content, context, strict) { // Do not transpile UI5 bundles if (content.startsWith("//@ui5-bundle ")) { log.verbose(`Skipping transformation of UI5 bundle ${resourcePath}.`); return { source: content, map: "" }; } // This is heavily inspired by the TypesScript "transpileModule" API const fileName = path.basename(resourcePath); const taskDone = taskStart("Transpiling AMD to ESM", fileName, true); const sourceFile = ts.createSourceFile(fileName, content, { languageVersion: ts.ScriptTarget.ES2022, jsDocParsingMode: ts.JSDocParsingMode.ParseNone, } // /*setParentNodes*/ false, // ts.ScriptKind.JS ); const sourceFiles = new Map(); sourceFiles.set(fileName, sourceFile); const writtenResources = new Map(); const compilerHost = createCompilerHost(sourceFiles, writtenResources); const program = createProgram([fileName], compilerHost); const transformers = { before: [createTransformer(program, resourcePath, context)], }; try { // ts.setEmitFlags(sourceFile, ts.EmitFlags.NoTrailingSourceMap); // TODO: Investigate whether we can retrieve a source file that can be fed directly into the typeChecker program.emit( /* targetSourceFile */ undefined, /* writeFile */ undefined, /* cancellationToken */ undefined, /* emitOnlyDtsFiles */ undefined, transformers); /* tsc currently does not provide an API to emit TypeScript *with* a source map (see https://github.com/microsoft/TypeScript/issues/51329) The below can be used to emit TypeScript without a source map: const result = ts.transform( sourceFile, [createTransformer(resourcePath, program)], compilerOptions); const printer = ts.createPrinter(); const printed = printer.printNode(ts.EmitHint.SourceFile, result.transformed[0], sourceFile); const printed = printer.printFile(result.transformed[0]); outputText = printed; */ } catch (err) { if (strict) { throw err; } // Usually the UnsupportedModuleError is already caught inside and not thrown, but this is a safety net // in case it is thrown anyway. if (err instanceof UnsupportedModuleError) { log.verbose(`Failed to transform module ${fileName}: ${err.message}`); if (err.stack) { log.verbose(`Stack trace:`); log.verbose(err.stack); } return { source: content, map: "" }; } else if (err instanceof Error && err.message.startsWith("Debug Failure")) { // We probably failed to create a valid AST. The error is thrown by TypeScript itself. log.verbose(`AST transformation failed for module ${fileName}: ${err.message}`); if (err.stack) { log.verbose(`Stack trace:`); log.verbose(err.stack); } return { source: content, map: "" }; } throw err; } const source = writtenResources.get(fileName); if (!source) { throw new Error(`Transpiling yielded no result for ${fileName}`); } const map = writtenResources.get(`${fileName}.map`); if (!map) { throw new Error(`Transpiling yielded no source map for ${fileName}`); } // Convert sourceMappingURL ending with ".js" to ".ts" // map = map // .replace(`//# sourceMappingURL=${fileName}.map`, `//# sourceMappingURL=${fileName}.map`); taskDone(); return { source, map }; } //# sourceMappingURL=transpiler.js.map