// Force inline const enum. // We don't use terser >= 4.2.1 to do this because it obsoleted `reduce_funcs` option. // // Replace: // /*@__DROP_PURE_EXPORT__*/ // var aa = { // bb: 0, // cc: 1, // }; // const UPPER_CASE = 2; // console.log(aa.bb, aa.cc, UPPER_CASE); // // As: // /*@__DROP_PURE_EXPORT__*/ // var aa = { // bb: 0, // cc: 1, // }; // const UPPER_CASE = 2; // console.log(0, 1, 2); const es = require('event-stream'); const stream = require('stream'); const COMMENT_ANNOTATION = `/*@__DROP_PURE_EXPORT__*/`; const CONST_OBJ_REG = /^[ \t]*\/\*@__DROP_PURE_EXPORT__\*\/[\w\s]*?\s+([A-Za-z_$][0-9A-Za-z_$]*)\s*=\s*({[\n\s\S]*?})/gm; const CONST_NUM_REG = /^[ \t]*const[ \t]+([A-Z_$][0-9A-Z_$]*)[ \t]*=[ \t]*(\d{1,2});?$/gm; function matchAll (str, reg, callback) { // the easiest matchAll polyfill ; ) if (!reg.global) { throw new Error('Can not matchAll if the global property is false.'); } str.replace(reg, function (m) { callback.apply(null, arguments); return m; }); } exports.inlineEnum = function (filename) { if (filename.endsWith('.js') || filename.endsWith('.ts')) { return es.map(function (data, callback) { var content = data.toString(); if (content.includes(COMMENT_ANNOTATION)) { // fast test let error = null; matchAll(content, CONST_OBJ_REG, function (match, obj, body) { try { body = Function(`return ${body}`)(); } catch (e) { error = new Error(`Failed to inline property "${obj}" in "${filename}", ${e}`); return; } for (const key in body) { let reg = new RegExp(`\\b${obj}\\.${key}\\b`, 'g'); let replaced = content.replace(reg, body[key]); if (replaced !== content) { content = replaced; console.log(`[inline-prop] '${obj}.${key}' is inlined with ${body[key]}`); } } }); if (error) { return callback(error); } data = new Buffer(content); } callback(null, data); }); } else { return stream.PassThrough(); } }; exports.inlineConst = function (filename) { if (filename.endsWith('.js') || filename.endsWith('.ts')) { return es.map(function (data, callback) { var content = data.toString(); if (content.includes(COMMENT_ANNOTATION)) { // fast test let error = null; matchAll(content, CONST_NUM_REG, function (match, name, value) { let reg = new RegExp(`([{,]\\s*|\\btypeof[ \\t]+)?(\\b${name}\\b)(?![ \\t]*=)`, 'g'); // not support negative lookbehind... let replaced = content.replace(reg, function (m, g1, g2) { if (m === g2) { return value; } else { return m; } }); if (replaced !== content) { content = replaced; console.log(`[inline-prop] '${name}' is inlined with ${value}`); } }); if (error) { return callback(error); } data = new Buffer(content); } callback(null, data); }); } else { return stream.PassThrough(); } };