UNPKG

f2e-middle-esbuild

Version:
206 lines (205 loc) 8.76 kB
"use strict"; const esbuild = require("esbuild"); const path = require("path"); const fs = require("fs"); const utils_1 = require("./utils"); const creater = (conf, options = {}) => { const { root, build } = conf; const { esbuildrc = '.esbuildrc.js', watches = [/\.[jet]?sx?$/], cacheRoot = '.esbuild', externalName = (index, split_index = 0) => `external${index > 0 ? `.${index}` : ''}${split_index > 0 ? `.${split_index}` : ''}.ts`, moduleName = (index, split_index = 0) => `__LIBS_${index}__${split_index > 0 ? `${split_index}` : ''}`, options: runtimeOptions, externalOptions = {}, } = options; const get_cache_path = function () { const cache_root = path.join(root, cacheRoot); const cache_esbuild = path.join(cache_root, 'esbuild.json'); const cache_tsconfig = path.join(cache_root, 'tsconfig.json'); if (!fs.existsSync(cache_root)) { fs.mkdirSync(cache_root); } if (!fs.existsSync(cache_esbuild)) { fs.writeFileSync(cache_esbuild, JSON.stringify((0, utils_1.defaultOptions)(cacheRoot), null, 2)); } // 写入默认打包tsconfig if (!fs.existsSync(cache_tsconfig)) { fs.writeFileSync(cache_tsconfig, JSON.stringify((0, utils_1.defaultTsconfig)(cacheRoot), null, 2)); } return { cache_root, cache_esbuild, cache_tsconfig, }; }; const external_map = new Map(); const option_map = [].concat(require(path.join(root, esbuildrc))).reduce((all, op, index) => { const { entryPoints, ignore_external = false, external_splits = [], ...base_config } = Object.assign({}, op, runtimeOptions); const { external = [] } = base_config; const globalName = moduleName(index); const external_files = []; if (external.length > 0 && !ignore_external) { const libname = externalName(index); const { cache_root, cache_esbuild } = get_cache_path(); fs.writeFileSync(path.join(cache_root, libname), (0, utils_1.createExternal)(external)); const entry = `${cacheRoot}/${libname}`; external_files.push(entry); const _base_config = { target: base_config.target, plugins: base_config.plugins, loader: base_config.loader, }; all.set(entry, { ...require(cache_esbuild), ...externalOptions, ..._base_config, minify: build, entryPoints: [entry], globalName, footer: { js: `require = function (n) { var m = ${globalName}[n]; if (!m && '.' != n[0]) { console.error('module not found:', n); } if (m.default) { m = Object.assign(m.default, m) } return m; };\n` }, }); for (let i = 0; i < external_splits.length; i++) { const item = external_splits[i]; const _globalName = moduleName(index, i + 1); external_files.push(item.entry); all.set(item.entry, { ...require(cache_esbuild), ...externalOptions, ...base_config, minify: build, entryPoints: { [externalName(index, i + 1)]: item.entry }, globalName: _globalName, external, footer: { js: `Object.assign(${globalName}, {"${item.moduleName}": ${_globalName}});\n` }, }); external.push(item.moduleName); } } const entries = Object.entries(entryPoints); entries.forEach(([k, v]) => { if (typeof k === 'number') { all.set(v, { entryPoints: [v], ...base_config, }); } else { all.set(v, { entryPoints: { [k]: v }, ...base_config, }); } external_map.set(v, external_files); }); return all; }, new Map()); /** 缓存的编译配置,增量执行 */ const ctx_map = new Map(); /** 文件依赖,前面是entries */ const deps_map = new Map(); // 编译中的文件 const building_set = new Set(); // 等待编译的文件 const needbuilds = new Set(); return { onSet: async (pathname, data, store) => { if (/\.html?$/.test(pathname)) { return data.toString().replace(/<script(?:(?:\s|.)+?)src=[\"\'](.+?)[\"\'](?!\<)(?:(?:\s|.)*?)(?:(?:\/\>)|(?:\>\s*?\<\/script\>))/g, function (_, src) { const href = path.join(path.dirname(pathname), src); const item = external_map.get((0, utils_1.pathname_fixer)(href)); if (item) { return `${item.map(src => `<script src="${src}"></script>`).join('\n')}\n` + _; } else { return _; } }); } let result_js = { outputPath: pathname, data, }; const entry = option_map.get(pathname); if (!entry) { return result_js; } const options = { write: false, metafile: true, minify: build, ...entry, }; try { const result = await (async function (build) { if (build) { return esbuild.build(options); } let ctx = ctx_map.get(pathname); if (!ctx) { ctx = await esbuild.context(options); ctx_map.set(pathname, ctx); } return ctx.rebuild(); })(build); if (result) { const outputFiles = result.outputFiles; if (outputFiles && outputFiles.length) { for (let i = 0; i < outputFiles.length; i++) { const outputFile = outputFiles[i]; const outputPath = (0, utils_1.pathname_fixer)(path.relative(root, outputFile.path)); if (!build && /\.js$/.test(outputPath)) { const meta_json = JSON.stringify(result.metafile); store._set(outputPath + '.json', meta_json); store._set(outputPath + '.html', utils_1.html.analyze); } deps_map.set(pathname, Object.keys(result.metafile.inputs || {}).map(i => (0, utils_1.pathname_fixer)(i))); let data = Buffer.concat([outputFile.contents]); store._set(outputPath, data); if (/\.js$/.test(outputPath)) { result_js = { outputPath, data, end: true, }; } } } } return result_js; } catch (e) { console.log(e); return data; } }, buildWatcher: async (pathname, type, build) => { const find = watches.find(reg => reg.test(pathname)); if (find) { deps_map.forEach(async (deps, entry) => { if (deps.includes(pathname)) { if (!building_set.has(entry)) { building_set.add(entry); await build(entry); if (needbuilds.has(entry)) { needbuilds.delete(entry); await build(entry); } building_set.delete(entry); } else { needbuilds.add(entry); } } }); } } }; }; module.exports = creater;