niucloud/admin/scripts/build-shared.cjs
2026-07-14 17:02:06 +08:00

134 lines
5.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 构建浏览器 ESM 共享依赖 → dist/.shared/
*
* 产物经 assemble 复制到 dist/assets/shared/,由 Import Map 加载。
* admin-lang 将 src/lang 打成独立包,供 core 与 addon 共用 t()。
*
* 用法npm run build:shared
*/
const fs = require('fs')
const path = require('path')
const { spawnSync } = require('child_process')
const { ROOT } = require('./addon-utils.cjs')
const { SHARED_BUILD_ORDER } = require('./shared-external.cjs')
const { scanDirExports, resolveConflicts, buildReExportStatements } = require('./scan-exports.cjs')
/** 临时入口目录,每次构建前重写 */
const SHARED_SRC = path.join(ROOT, '.build', 'shared')
/** Vite shared 构建 staging 输出 */
const OUT_DIR = path.join(ROOT, 'dist', '.shared')
/** 各 shared 包的极简 re-export 入口(由 vite.config.shared.ts 打包) */
const ENTRY_CONTENT = {
vue: "import * as Vue from 'vue'\nexport * from 'vue'\nexport default Vue\n",
'vue-router': "export * from 'vue-router'\n",
pinia: "export * from 'pinia'\n",
'element-plus': "import * as ElementPlus from 'element-plus'\nexport * from 'element-plus'\nexport default ElementPlus\n",
'icons-vue': "export * from '@element-plus/icons-vue'\n",
axios: "export { default } from 'axios'\nexport * from 'axios'\n",
'vue-i18n': "export * from 'vue-i18n/dist/vue-i18n.esm-bundler.js'\n",
'admin-lang': "export { language, t, default } from '../../src/lang/index.ts'\n",
'core-shared': '' // 动态生成,从 API 目录扫描
}
function writeEntries() {
fs.mkdirSync(SHARED_SRC, { recursive: true })
// 通用冲突映射:{ "module:name" -> "module_name" }
const allConflicts = {}
// 1a. 扫描 app/api 目录(排除 addon.ts / module.ts
const apiDir = path.join(ROOT, 'src', 'app', 'api')
const apiFiles = scanDirExports(apiDir, { exclude: ['addon.ts', 'module.ts'] })
const { resolved: apiResolved, conflicts: apiConflicts } = resolveConflicts(apiFiles)
Object.assign(allConflicts, apiConflicts)
const apiExports = buildReExportStatements(apiResolved, '../../src/app/api')
// 1b. 扫描 utils 目录中被重导出的文件
const utilsDir = path.join(ROOT, 'src', 'utils')
const utilsExclude = ['common.ts', 'request.ts', 'storage.ts', 'config.ts', 'test.ts',
'directives.ts', 'addon-loader.ts', 'addon-lang.ts', 'tianditu.ts', 'qqmap.ts']
const utilsFiles = scanDirExports(utilsDir, { exclude: utilsExclude })
const { resolved: utilsResolved, conflicts: utilsConflicts } = resolveConflicts(utilsFiles)
Object.assign(allConflicts, utilsConflicts)
const utilsExports = buildReExportStatements(utilsResolved, '../../src/utils')
// 2. 生成 core-shared 入口
ENTRY_CONTENT['core-shared'] = [
"export * from '../../src/utils/common'",
"import _request from '../../src/utils/request'",
"import _storage from '../../src/utils/storage'",
"import _config from '../../src/utils/config'",
"import _test from '../../src/utils/test'",
"export { _request as request }",
"export { _storage as storage }",
"export { _config as config }",
"export { _test as test }",
"export default _request",
"export * from '../../src/utils/qqmap'",
...utilsExports,
...apiExports,
"export { default as UploadImage } from '../../src/components/upload-image/index.vue'",
"export { default as DiyPage } from '../../src/components/diy-page/index.vue'",
"export { default as MapSelector } from '../../src/components/map-selector/index.vue'",
"export { default as Editor } from '../../src/components/editor/index.vue'"
].join('\n')
for (const [key, content] of Object.entries(ENTRY_CONTENT)) {
fs.writeFileSync(path.join(SHARED_SRC, `${key}.ts`), content, 'utf-8')
}
// 3. 保存冲突映射
if (Object.keys(allConflicts).length) {
fs.writeFileSync(path.join(ROOT, '.build', 'export-conflicts.json'), JSON.stringify(allConflicts, null, 2), 'utf-8')
console.log(`[build-shared] conflicts: ${Object.keys(allConflicts).length} entries`)
}
if (Object.keys(apiConflicts).length) {
fs.writeFileSync(path.join(ROOT, '.build', 'api-conflicts.json'), JSON.stringify(apiConflicts, null, 2), 'utf-8')
}
}
/** 构建单个 shared 包;仅第一个包 emptyOutDir 清空 .shared */
function runBuild(pkgKey, emptyOutDir) {
const r = spawnSync('npx', ['vite', 'build', '--config', 'vite.config.shared.ts'], {
cwd: ROOT,
env: {
...process.env,
SHARED_PKG: pkgKey,
SHARED_EMPTY: emptyOutDir ? '1' : '0',
NODE_OPTIONS: '--max-old-space-size=4096'
},
stdio: 'inherit',
shell: process.platform === 'win32'
})
if ((r.status ?? 1) !== 0) {
console.error(`[build-shared] failed: ${pkgKey}`)
process.exit(r.status ?? 1)
}
console.log(`[build-shared] ok: ${pkgKey}.js`)
}
function main() {
writeEntries()
fs.mkdirSync(OUT_DIR, { recursive: true })
SHARED_BUILD_ORDER.forEach((pkgKey, i) => {
runBuild(pkgKey, i === 0)
})
console.log(`[build-shared] done -> ${path.relative(ROOT, OUT_DIR)}/`)
// 若 dist/assets 已存在(曾 assemble 过),顺便同步一份 shared 便于单独调试
const distAssets = path.join(ROOT, 'dist', 'assets', 'shared')
if (fs.existsSync(path.join(ROOT, 'dist', 'assets'))) {
fs.mkdirSync(distAssets, { recursive: true })
for (const name of fs.readdirSync(OUT_DIR)) {
if (!name.endsWith('.js')) continue
fs.copyFileSync(path.join(OUT_DIR, name), path.join(distAssets, name))
}
console.log(`[build-shared] synced -> dist/assets/shared/`)
}
}
main()