@wrench/roll-typescript
Version:
plugin for bundling TypeScript with support of modular output and declaration bundle
205 lines (183 loc) • 7.32 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const colors_1 = require("colors");
const path_1 = tslib_1.__importDefault(require("path"));
const bundle_dts_1 = require("./bundle-dts");
const dts_1 = require("./dts");
const host_1 = require("./host");
const project_1 = require("./project");
const util_1 = require("./util");
const NAME = "@wrench/typescript";
const VIRTUAL_NAME = `_virtual/${NAME}`;
const EXPORT_NULL = "exports = null;";
function typescript(options) {
options = Object.assign({}, options);
if (options.types)
options.types = path_1.default.resolve(options.types);
let isExternal;
let cache;
let exclude;
let modular;
let project;
let dtsPretty;
let compilerOptions;
let inputs;
let pending;
let shouldBundleDts;
return {
name: NAME,
buildStart(input) {
cache = new Map();
exclude = new Set([NAME, VIRTUAL_NAME]);
modular = input.preserveModules;
[project, inputs] = util_1.lazy(this, "project", project_1.createProject, options, input);
const { rootDir, rootDirs } = project.options;
const roots = rootDirs ? rootDirs : rootDir ? [rootDir] : null;
isExternal = util_1.createIsExternal(roots, options.external);
const dependencies = host_1.collectDependencies(project, inputs, true);
const localDependencies = dependencies.filter(x => isExternal(x) === false);
pending = new Set(localDependencies);
project.setFileNames(localDependencies);
project.reportDiagnostic = host_1.createReportDiagnosticByPlugin(this);
shouldBundleDts = !!(!modular && project.options.declarationDir && options.types);
compilerOptions = project.options;
dtsPretty = util_1.lazy(this, "dts-pretty", dtsPrettyFactory);
if (modular)
for (const id of pending)
this.emitFile({ id, type: "chunk" });
},
resolveId(specifier, importer) {
if (host_1.isTsOrTsx(project, importer)) {
const id = host_1.resolve(project, importer, specifier);
const external = isExternal(specifier, importer, id);
return { id, external, moduleSideEffects: true };
}
},
load(id) {
if (id === NAME)
return EXPORT_NULL;
},
transform(code, fileName) {
pending.delete(fileName);
if (fileName === NAME)
return;
if (!host_1.isTsOrTsx(project, fileName))
return;
project.updateScript(fileName, code);
const program = project.getProgram();
const out = host_1.emitByProgram(program, project, fileName);
const { js, jsmap, dts, dtsmap } = out;
if (!modular) {
if (dts)
host_1.writeOutputFile(project, dts);
if (dtsmap)
host_1.writeOutputFile(project, dtsmap);
}
if (isEmptyCode(js.text))
js.text = EXPORT_NULL;
return {
code: js.text,
map: jsmap && jsmap.text,
moduleSideEffects: true,
};
},
renderStart() {
if (!modular) {
flush(project, pending, true);
pending.clear();
}
},
renderChunk(code, chunk, output) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (chunk.fileName === VIRTUAL_NAME)
return;
if (modular) {
const host = util_1.lazy2(cache, output, checkProgramDirty, this, outputHostKey, project_1.forkHostByOutput, output, project, compilerOptions);
const program = host.getProgram();
const out = host_1.emitByProgram(program, host, chunk.facadeModuleId);
const { js, jsmap, dts, dtsmap } = out;
chunk.fileName = js.name;
if (output.banner && chunk.isEntry)
js.text = [(yield output.banner), js.text].join(host.getNewLine());
if (dts) {
dts.text = dtsPretty.format(dts.text, dts.name);
host_1.writeOutputFile(project, dts);
}
if (dtsmap)
host_1.writeOutputFile(project, dtsmap);
return {
code: js.text,
map: jsmap && jsmap.text,
};
}
else {
}
});
},
generateBundle(output, bundle) {
const host = util_1.lazy2(cache, output, checkProgramDirty, this, outputHostKey, project_1.forkHostByOutput, output, project, compilerOptions);
for (const key of Object.keys(bundle))
if (isExclude(bundle[key], exclude))
delete bundle[key];
if (shouldBundleDts) {
const outDir = path_1.default.resolve(path_1.default.dirname(output.file));
const rel = path_1.default.relative(outDir, options.types);
if (rel && !rel.startsWith("..")) {
shouldBundleDts = false;
const { declarationDir } = project.options;
const entry = path_1.default.join(declarationDir, rel);
const relTypes = path_1.default.relative(process.cwd(), options.types);
console.log(colors_1.cyan(`${entry} → ${relTypes}`));
host_1.addFileNames(host, host_1.collectDependencies(host, entry));
bundle_dts_1.bundleDts({
entry,
program: host.getProgram(),
output: options.types,
});
}
}
},
};
}
exports.typescript = typescript;
function isExclude(chunk, ...excludes) {
if (util_1.isOutputChunk(chunk))
for (const exclude of excludes)
if (exclude.has(chunk.facadeModuleId))
return true;
}
function outputHostKey(output) {
return `output.host.${output.file || output.dir}`;
}
function forkESNext(host) {
const { ts } = host;
return host.fork({
options: {
target: ts.ScriptTarget.ESNext,
},
});
}
function flush(host, fileNames, dtsOnly) {
const program = host.getProgram();
for (const fileName of fileNames)
host_1.writeEmit(host, host_1.emitByProgram(program, host, fileName, dtsOnly));
}
function showBundle(output, bundle) {
console.group(colors_1.cyan(output.file || output.dir));
for (const key of Object.keys(bundle))
console.log(bundle[key].fileName);
console.groupEnd();
}
function isEmptyCode(code) {
if (code) {
code = code.replace(/\/\/.*/g, "");
code = code.trim();
}
return !code;
}
function checkProgramDirty() {
this.checkProgramDirty();
}
function dtsPrettyFactory() {
return new dts_1.DtsPretty();
}