ts-transformer-preval-macro
Version:
Pre-evaluate code at build-time
112 lines (110 loc) • 4.54 kB
JavaScript
;
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const shell = __importStar(require("child_process"));
const crypto_1 = __importDefault(require("crypto"));
const findCacheDir = require("find-cache-dir");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const deleteDir = __importStar(require("rimraf"));
const ts = __importStar(require("typescript"));
let OPTIONS = {
cacheActivated: false,
debug: false,
mode: 'development'
};
function preevaluationTransformer() {
return (context) => {
let tagName = 'preval';
const visit = (node) => {
if (ts.isImportDeclaration(node) &&
node.moduleSpecifier.getText().indexOf('prevalTransformer') > -1) {
if (node.importClause !== undefined) {
tagName = node.importClause.getText().slice(1, -1);
}
return undefined;
}
if (ts.isTaggedTemplateExpression(node) &&
node.tag.getText() === tagName) {
let code = node.template.getText().slice(1, -1);
const dir = path.dirname(node.getSourceFile().fileName);
const hash = crypto_1.default
.createHash('sha1')
.update(code)
.digest('hex');
// const evalFileDir = dir.split('/')[dir.split('/').length - 1];
const evalFilename = '_eval.' + hash;
const evalPath = path.join(dir, evalFilename + '.ts');
const evalDir = `${dir}/_temp_${hash}`;
const cacheDir = findCacheDir({
create: true,
name: `ts-transformer-preval-macro/${OPTIONS.mode}`
});
if (cacheDir === null) {
throw new Error('not able to create a cache dir');
}
const evalResult = path.join(cacheDir, `/${hash}.json`);
code = `${code}
run().then((result) => {
console.log(JSON.stringify(result));
});`;
if (fs.existsSync(evalResult) && OPTIONS.cacheActivated) {
const cachedRawResult = fs.readFileSync(evalResult);
return transformResult(cachedRawResult);
}
fs.writeFileSync(evalPath, code);
try {
shell.execSync(`tsc ${evalPath} --outDir ${evalDir}`);
}
catch (e) {
// TODO parse tsc errors and decide if something gone really wrong
if (!OPTIONS.debug) {
fs.unlinkSync(evalPath);
deleteDir.sync(evalDir);
throw e;
}
}
let rawResult = 'null';
try {
rawResult = shell
.execSync(`node ${evalDir}/${evalFilename}.js`)
.toString();
}
catch (e) {
if (!OPTIONS.debug) {
fs.unlinkSync(evalPath);
deleteDir.sync(evalDir);
throw e;
}
}
if (!OPTIONS.debug) {
fs.unlinkSync(evalPath);
deleteDir.sync(evalDir);
}
fs.writeFileSync(evalResult, rawResult);
return transformResult(rawResult);
}
return ts.visitEachChild(node, (child) => visit(child), context);
};
return (node) => {
return ts.visitNode(node, visit);
};
};
}
function transformResult(rawResult) {
const result = JSON.stringify(JSON.parse(rawResult));
return ts.createCall(ts.createPropertyAccess(ts.createIdentifier('JSON'), ts.createIdentifier('parse')), undefined, [ts.createStringLiteral(result)]);
}
exports.default = (options) => {
OPTIONS = Object.assign({}, OPTIONS, options);
return preevaluationTransformer;
};