@adonisjs/require-ts
Version:
In memory typescript compiler
196 lines (195 loc) • 6.88 kB
JavaScript
"use strict";
/*
* @adonisjs/require-ts
*
* (c) Harminder Virk <virk@adonisjs.comharminder@cav.ai>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Compiler = void 0;
const utils_1 = require("@poppinss/utils");
const source_map_support_1 = __importDefault(require("source-map-support"));
const utils_2 = require("../utils");
const Cache_1 = require("../Cache");
const DiagnosticsReporter_1 = require("../DiagnosticsReporter");
/**
* Exposes the API compile source files using the tsc compiler. No
* type checking takes place.
*/
class Compiler {
constructor(appRoot, cacheRoot, ts, options, usesCache = true) {
this.appRoot = appRoot;
this.cacheRoot = cacheRoot;
this.ts = ts;
this.options = options;
this.usesCache = usesCache;
/**
* In-memory compiled files cache for source maps to work.
*/
this.memCache = new Map();
/**
* Disk cache
*/
this.cache = this.usesCache ? new Cache_1.Cache(this.appRoot, this.cacheRoot) : new Cache_1.FakeCache();
/**
* Dignostic reporter to print program errors
*/
this.diagnosticsReporter = new DiagnosticsReporter_1.DiagnosticsReporter(this.appRoot, this.ts, false);
this.transformers = {};
this.patchCompilerOptions();
this.setupSourceMaps();
this.resolveTransformers();
}
/**
* Patch compiler options to make source map work properly
*/
patchCompilerOptions() {
/**
* Force inline source maps. We need this to avoid manual
* lookups
*/
this.options.compilerOptions.inlineSourceMap = true;
/**
* Inline sources
*/
this.options.compilerOptions.inlineSources = true;
/**
* Remove "outDir" property, so that the source maps paths are generated
* relative from the cwd and not the outDir.
*
* ts-node manually patches the source maps to use absolute paths. We cannot
* do same, since we cache files on the disk and changing the folder name
* of project root will corrupt the absolute path names inside the
* source maps.
*/
delete this.options.compilerOptions.outDir;
/**
* Inline source maps and source map cannot be used together
*/
delete this.options.compilerOptions.sourceMap;
}
/**
* Resolves transformer relative from the app root
*/
resolverTransformer(transformer) {
try {
const value = (0, utils_1.esmRequire)(require.resolve(transformer, { paths: [this.appRoot] }));
if (typeof value !== 'function') {
throw new Error('Transformer module must export a function');
}
return value(this.ts, this.appRoot);
}
catch (error) {
if (error.code === 'ENOENT') {
throw new Error(`Unable to resolve transformer "${transformer}" specified in tsconfig.json file`);
}
throw error;
}
}
/**
* Resolve transformers
*/
resolveTransformers() {
if (!this.options.transformers) {
return;
}
if (this.options.transformers.before) {
this.transformers.before = this.options.transformers.before.map((transformer) => {
return this.resolverTransformer(transformer.transform);
});
}
if (this.options.transformers.after) {
this.transformers.after = this.options.transformers.after.map((transformer) => {
return this.resolverTransformer(transformer.transform);
});
}
if (this.options.transformers.afterDeclarations) {
this.transformers.afterDeclarations = this.options.transformers.afterDeclarations.map((transformer) => {
return this.resolverTransformer(transformer.transform);
});
}
}
/**
* Setup source maps support to read from in-memory cache
*/
setupSourceMaps() {
source_map_support_1.default.install({
environment: 'node',
retrieveFile: (pathOrUrl) => {
(0, utils_2.debug)('reading source for "%s"', pathOrUrl);
return this.memCache.get(pathOrUrl) || '';
},
});
}
/**
* Compiles the file using the typescript compiler
*/
compileFile(filePath, contents, virtualFile) {
(0, utils_2.debug)('compiling file using typescript "%s"', filePath);
let { outputText, diagnostics } = this.ts.transpileModule(contents, {
fileName: filePath,
compilerOptions: virtualFile
? {
...this.options.compilerOptions,
rootDir: undefined,
}
: this.options.compilerOptions,
reportDiagnostics: !virtualFile,
transformers: this.transformers,
});
/**
* Report diagnostics if any
*/
if (diagnostics) {
this.diagnosticsReporter.report(diagnostics);
}
/**
* Write to in-memory cache for sourcemaps to work
*/
this.memCache.set(filePath, outputText);
return outputText;
}
/**
* Compile typescript source code using the tsc compiler.
*/
compile(filePath, contents, virtualFile = false) {
/**
* Do not cache virtual files
*/
if (virtualFile) {
(0, utils_2.debug)('compiling virtual file "%s" (no cache)', filePath);
return this.compileFile(filePath, contents, true);
}
(0, utils_2.debug)('compiling file "%s"', filePath);
const cachePath = this.cache.makeCachePath(filePath, contents, '.js');
/**
* Return the file from cache when it exists
*/
const compiledContent = this.cache.get(cachePath);
if (compiledContent) {
/**
* Write to in-memory cache for sourcemaps to work
*/
this.memCache.set(filePath, compiledContent);
return compiledContent;
}
/**
* Compile file using the compiler
*/
const outputText = this.compileFile(filePath, contents, false);
/**
* Write to cache on disk
*/
this.cache.set(cachePath, outputText);
/**
* Return compiled text
*/
return outputText;
}
}
exports.Compiler = Compiler;