UNPKG

@lcap/builder

Version:
329 lines (328 loc) 13.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildModules = exports.getModuleEntries = exports.LcapBuildModulesCSS = void 0; const vite_1 = require("vite"); const lodash_1 = require("lodash"); const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); const rollup_1 = __importDefault(require("rollup")); const rollup_plugin_dts_1 = require("rollup-plugin-dts"); const virtual_file_names_1 = require("../constants/virtual-file-names"); const logger_1 = __importDefault(require("../utils/logger")); const lcap_1 = require("../utils/lcap"); const build_utils_1 = require("../utils/build-utils"); const exec_1 = require("../utils/exec"); function getTagName(name, framework) { let key = name; if (framework.startsWith('vue')) { key = (0, lodash_1.kebabCase)(name); } return key; } const genCompEntryName = (name, framework) => `components/${getTagName(name, framework)}/index`; function LcapBuildModulesCSS() { const CSS_LANGS_RE = /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/; const cssModuleRE = new RegExp(`\\.module${CSS_LANGS_RE.source}`); const isModuleCSSRequest = (request) => cssModuleRE.test(request); const cssInjectorText = ` export function injectStyle(css, insertAt = 'top') { if (!css || typeof document === 'undefined') return const head = document.head || document.querySelector('head') const firstChild = head.querySelector(':first-child') const style = document.createElement('style') style.appendChild(document.createTextNode(css)) if (insertAt === 'top' && firstChild) { head.insertBefore(style, firstChild) } else { head.appendChild(style) } } `; const stylesMap = new Map(); return [{ name: 'vite-lcap:build-modules-css-pre', resolveId(source) { if (source === virtual_file_names_1.virtualInjectCSSFileId) { return virtual_file_names_1.virtualInjectCSSFileId; } if (source === virtual_file_names_1.virtualThemeCSSFileId) { return virtual_file_names_1.virtualThemeCSSFileId; } return undefined; }, load(id) { if (id === virtual_file_names_1.virtualInjectCSSFileId) { return cssInjectorText; } if (id === virtual_file_names_1.virtualThemeCSSFileId) { return ''; } return undefined; }, transform(code, id) { if (isModuleCSSRequest(id)) { stylesMap.set(id, code); } }, }, { name: 'vite-lcap:build-modules-css-post', enforce: 'post', transform(code, id) { if (!isModuleCSSRequest(id) || !stylesMap.has(id)) { return null; } const cssCode = stylesMap.get(id) || ''; const cssString = cssCode.replace(/ *\\9/g, '').replace(/\\(\d+)/g, '0o$1'); const injections = `\nimport { injectStyle } from '${virtual_file_names_1.virtualInjectCSSFileId}';\ninjectStyle(${JSON.stringify(cssString)}, 'bottom');`; return { code: code + injections, map: null, }; }, generateBundle(_, bundle) { Object.keys(bundle).forEach((file) => { if (bundle[file].type === 'asset') { delete bundle[file]; } }); }, }]; } exports.LcapBuildModulesCSS = LcapBuildModulesCSS; function getModuleEntries(options, components) { const entries = Object.assign({}, (options.entries ? options.entries : {})); components.forEach(({ name, tsPath }) => { const entryName = genCompEntryName(name, options.framework); const basename = path_1.default.basename(tsPath); if (basename !== 'api.ts') { return; } entries[entryName] = (0, vite_1.normalizePath)(path_1.default.relative(options.rootPath, path_1.default.resolve(tsPath, '../index'))); }); const logicEntry = 'src/logics/index.ts'; if (fs_extra_1.default.existsSync(logicEntry)) { entries['logics/index'] = 'src/logics/index.ts'; } return entries; } exports.getModuleEntries = getModuleEntries; function viteBuildModules(options, components) { return __awaiter(this, void 0, void 0, function* () { const loadResult = yield (0, vite_1.loadConfigFromFile)({ command: 'build', mode: 'production' }); if (!loadResult || !loadResult.config) { throw new Error('未找到 vite 配置文件'); } const { external } = (0, build_utils_1.getBuildOutputConifg)(options); if (options.external && options.external.length > 0) { external.push(...options.external); } const entries = getModuleEntries(options, components); const exportsMap = {}; Object.keys(entries).forEach((name) => { exportsMap[name] = []; }); let buildConfig = { define: { 'process.env': { NODE_ENV: 'production', }, }, build: { emptyOutDir: true, copyPublicDir: false, minify: false, sourcemap: false, lib: { entry: entries, formats: ['es'], }, rollupOptions: { external, treeshake: { moduleSideEffects: options.moduleSideEffects || ((id) => id.includes('node_modules')), }, output: { format: 'es', hoistTransitiveImports: false, chunkFileNames: '_chunks/dep-[hash].mjs', }, }, outDir: options.outDir, }, }; const { config } = loadResult; if (config.build && config.build.lib) { delete config.build.lib; } buildConfig = (0, vite_1.mergeConfig)(config, buildConfig); if (!buildConfig.plugins) { buildConfig.plugins = []; } else { buildConfig.plugins = buildConfig.plugins.flat().filter((p) => (p && (!p.name || !p.name.startsWith('vite:lcap-')))) || []; } buildConfig.plugins.push(LcapBuildModulesCSS()); buildConfig.plugins.push({ name: 'vite:lcap-collect-export', renderChunk(_, chunk) { if (!Array.isArray(exportsMap[chunk.name]) || chunk.fileName.startsWith('_chunks')) { return; } exportsMap[chunk.name].push(...chunk.exports); }, }); yield (0, vite_1.build)(Object.assign({ configFile: false, envFile: false }, buildConfig)); return exportsMap; }); } function generateModulesJSON(options, exportsMap, components) { return __awaiter(this, void 0, void 0, function* () { const filePath = path_1.default.resolve(options.rootPath, options.outDir, 'modules.json'); const exportNameMap = {}; const apiPathMap = {}; components.forEach((metaInfo) => { const apiPath = path_1.default.relative(options.rootPath, metaInfo.tsPath); apiPathMap[metaInfo.name] = apiPath; if (Array.isArray(metaInfo.children)) { metaInfo.children.forEach((m) => { apiPathMap[m.name] = apiPath; }); } }); Object.keys(exportsMap).forEach((entry) => { let exportNames = exportsMap[entry]; if (!exportNames || exportNames.length === 0) { return; } exportNames = exportNames.filter((name) => name !== 'default'); if (exportNames.length === 0) { const comp = components.find((cp) => (genCompEntryName(cp.name, options.framework) === entry)); if (comp) { exportNameMap[comp.name] = { src: entry, isDefault: true, }; } return; } exportNames.forEach((name) => { exportNameMap[name] = { src: entry, isDefault: false, }; }); }); const moduleInfo = { exports: exportNameMap, api: apiPathMap }; fs_extra_1.default.writeJSONSync(filePath, moduleInfo, { spaces: 2 }); return moduleInfo; }); } function generateIndex(options, exportNameMap) { return __awaiter(this, void 0, void 0, function* () { const codes = Object.keys(exportNameMap).map((key) => { const { src, isDefault } = exportNameMap[key]; return `export { ${isDefault ? `default as ${key}` : key} } from './${src}';`; }); codes.push(''); fs_extra_1.default.writeFileSync(path_1.default.resolve(options.rootPath, options.outDir, 'index.mjs'), codes.join('\n')); }); } function generateIndexDts(options, exportNameMap) { return __awaiter(this, void 0, void 0, function* () { const codes = Object.keys(exportNameMap).map((key) => { return `export declare const ${key}: any;`; }); codes.push(''); fs_extra_1.default.writeFileSync(path_1.default.resolve(options.rootPath, options.outDir, 'index.d.ts'), codes.join('\n')); }); } function buildDts(options) { return __awaiter(this, void 0, void 0, function* () { const typesPath = '_temp/types'; const command = options.framework === 'vue3' ? 'vue-tsc' : 'tsc'; try { yield (0, exec_1.exec)(`npx ${command} -d --emitDeclarationOnly -p ${options.tsconfigPath} --outDir ${typesPath}`); } catch (e) { logger_1.default.error(e); } let entry = `${typesPath}/index.d.ts`; if (!fs_extra_1.default.existsSync(entry)) { entry = `${typesPath}/src/index.d.ts`; } try { const bundle = yield rollup_1.default.rollup({ input: entry, plugins: [ (0, rollup_plugin_dts_1.dts)(), { name: 'rollup-temp-dts', resolveId(source) { if (source.endsWith('.css') || source.endsWith('.less') || source.endsWith('.scss') || source.endsWith('.vue')) { return 'vite__temp.d.ts'; } return undefined; }, load(id) { if (id === 'vite__temp.d.ts') { return { code: 'declare const _temp: any; export default _temp;', }; } return undefined; }, }, ], }); yield Promise.all([{ file: `${options.outDir}/index.d.ts`, format: 'es' }].map(bundle.write)); } catch (e) { logger_1.default.error('构建 d.ts 失败'); throw e; } finally { fs_extra_1.default.rmSync('_temp', { recursive: true }); } }); } function buildModules(options) { return __awaiter(this, void 0, void 0, function* () { if (!options.modules) { return; } const buildModulesOptions = { rootPath: options.rootPath, type: options.type, framework: options.framework, outDir: 'es', }; if (typeof options.modules === 'object') { Object.assign(buildModulesOptions, options.modules); } logger_1.default.start('开始模块构建....'); const components = (0, lcap_1.getComponentMetaInfos)(options.rootPath, true); const exportsMap = yield viteBuildModules(buildModulesOptions, components); const moduleInfo = yield generateModulesJSON(buildModulesOptions, exportsMap, components); yield generateIndex(buildModulesOptions, moduleInfo.exports); if (buildModulesOptions.tsconfigPath && fs_extra_1.default.existsSync(buildModulesOptions.tsconfigPath)) { yield buildDts(buildModulesOptions); } else { yield generateIndexDts(buildModulesOptions, moduleInfo.exports); } logger_1.default.success('已完成模块构建....'); }); } exports.buildModules = buildModules;