UNPKG

neo-builder

Version:

the fastest tiny script packager written in javascript and supporting iife dynamic chaining w/o extra runtime

244 lines (178 loc) 8.67 kB
//@ts-check const path = require('path') const fs = require('fs') const performance = require('perf_hooks').performance; const { execSync } = require('child_process') // const buble = require('buble'); // const babel = require('babel-standalone'); // const jsxTransform = require('babel-plugin-transform-react-jsx'); const build = require('./main').buildFile; const { mergeFlatMaps, extractEmbedMap } = require('./utils'); const TS_MAP_Token = '//# sourceMappingURL=data:application/json;base64,'; const cache = {} if (~process.argv.indexOf('-h')) { console.log(` -s - source file name (could be passed as first arg without the flag -s) -t - target file name (required) -m - generate sourcemap file (optional) --time - verbose build time (optional) `); process.exit(0) }; function getArgv(argk) { let index = process.argv.indexOf(argk) + 1 if (index) { return process.argv[index] } else { return null; } } const helpers = { s: 'source file', t: 'target file' } let source = resolveFile('s', 1); let target = resolveFile('t', false); const sourcemapInline = ~process.argv.indexOf('--inline-m'); const sourcemap = sourcemapInline || ~process.argv.indexOf('-m'); const minify = sourcemapInline || ~process.argv.indexOf('--minify'); const jsx__converter = sourcemapInline || ~process.argv.indexOf('--jsx-converter'); const release = ~process.argv.indexOf('-r'); if (release && sourcemap) { console.log(`\x1B[34m >> using the -k option in conjunction with - is not recommended, since these options have not been tested together.\x1B[0m`); } console.time('built in') let result = build(source, target, { release: !!release == true, sourceMaps: sourcemap ? (() => { // also look at cjs-to-es6 ? // let encode = null; const packageName = 'sourcemap-codec' try { var { encode } = require(packageName); } catch (err) { console.log('\x1B[33mThe package needed to generate the source map has not been found and will be installed automatically\x1B[0m'); console.log(execSync('npm i sourcemap-codec').toString()); // -D? var { encode } = require(packageName); } return { encode, external: !!sourcemapInline == true } })() : null, advanced: source.endsWith('.ts') ? { ts: (/** @type {string} */ code) => { const ts = importPackage({ packageName: 'typescript' }) const decode = importPackage({ packageName: 'sourcemap-codec', funcName: 'decode' }) var [originMapping, mapInfo, code] = extractEmbedMap(code, { decode }); const builtCode = ts.transpile(code, { sourceMap: true, inlineSourceMap: true, inlineSources: true, jsx: true, allowJs: true }) if (!mapInfo) { return builtCode } var [code, mergedMap] = mergeFlatMaps(builtCode, originMapping, {mapStartToken: TS_MAP_Token, decode}) /** @type {(source: import('sourcemap-codec').SourceMapMappings) => string} */ const encode = importPackage({ packageName: 'sourcemap-codec', funcName: 'encode' }) mapInfo.mappings = encode(mergedMap); mapInfo.file = '' return code + '\n' + TS_MAP_Token + Buffer.from(JSON.stringify(mapInfo)).toString('base64') } } : null, plugins: [].concat(minify ? [{ name: 'neo-minify-plugin', bundle: (/** @type {string} */ code, { maps, rawMap }) => { const uglifier = importPackage({ packageName: 'uglify-js' }) const result = uglifier.minify({ target: code }, { sourceMap: sourcemap ? { content: JSON.stringify(maps), url: sourcemapInline ? "inline" : (target + ".map") } : undefined }); if (sourcemap && !sourcemapInline) { fs.writeFileSync(target + '.map', result.map) // fs.writeFileSync(target + '.map', JSON.stringify(result.map)) } return result.code } }] : []).concat(jsx__converter ? [ { name: 'neo-jsx-convert-plugin', bundle: (/** @type {string} */ code, { maps, rawMap }) => { // const buble = importPackage({ packageName: 'buble' }) // const builtResult = buble.transform(code, {}) // 1)- input sourcemap has no 1)+ size // const { encode, decode } = importPackage({ packageName: 'sourcemap-codec' }) // const [, mergedMap] = mergeFlatMaps(builtResult.code, decode(maps.mappings), { pluginMapping: decode(builtResult.map.mappings) },); // maps.mappings = encode(mergedMap); // code = builtResult.code + '\n' + TS_MAP_Token + Buffer.from(JSON.stringify(maps)).toString('base64'); const babel = importPackage({ packageName: 'babel-standalone' }) const jsxTransform = importPackage({ packageName: 'babel-plugin-transform-react-jsx' }) const builtResult = babel.transform(code, { // 1)+ input sourcemap has 2)+? possible babel-polyfill inputSourceMap: maps, sourceMaps: true, // sourceMaps: 'inline', plugins: [ jsxTransform // "@babel/plugin-transform-react-jsx" ] }) code = builtResult.code + '\n' + TS_MAP_Token + Buffer.from(JSON.stringify(builtResult.map)).toString('base64'); return code } } ] : []) }) if (result) { const relativeSourcePath = path.relative(process.cwd(), source); const relativeTargetPath = path.relative(process.cwd(), target); console.log(`\x1B[34m${relativeSourcePath} => ${relativeTargetPath}\x1B[0m`); if (sourcemap && !!sourcemapInline == false) { console.log(`\x1B[34m${'.'.repeat(relativeSourcePath.length)} => ${relativeTargetPath}.map\x1B[0m`) } if (~process.argv.indexOf('--time')) { console.timeEnd('built in') } } /** * @param {{ * packageName: 'typescript'|'sourcemap-codec'|'uglify-js'|'buble'|'babel-plugin-transform-react-jsx'|'babel-standalone', * funcName?: string, * destDesc?: string // to generate the source map * }} packInfo */ function importPackage({ packageName, funcName, destDesc }) { const cacheName = packageName + '.' + (funcName || 'default'); if (cache[cacheName]) { return cache[cacheName]; } try { var encode = funcName ? require(packageName)[funcName] : require(packageName); } catch (err) { console.log(`\x1B[33mThe package ${packageName} needed ${destDesc} has not been found and will be tried to install automatically\x1B[0m`); console.log(execSync('npm i ' + packageName).toString()); // -D? var encode = funcName ? require(packageName)[funcName] : require(packageName); } cache[cacheName] = encode; return encode; } /** * @param {keyof typeof helpers} flag * @param {boolean|1} [check=undefined] * @returns {string} */ function resolveFile(flag, check) { let target = getArgv('-' + flag) || (check === 1 ? process.argv[check + 1] : null) if (!target) { const errMessage = `the path is not specified (use the -${flag} <filename> option for specify ${helpers[flag]})`; console.warn('\x1B[31m' + errMessage + '\x1B[0m') process.exit(1) } if (!path.isAbsolute(target)) { target = path.resolve(process.cwd(), target); } if (check && check !== undefined && !fs.existsSync(target)) { console.log(process.cwd); console.warn('\x1B[31m' + `${target} file not found` + '\x1B[0m') // throw new Error(`${target} file not found`); process.exit(1) } return target; }