import { join, dirname, parse } from 'node:path' import fs from 'iofs' import Es from 'esbuild' import { compileScss, parseJs, compileVue, parseHtml } from './compile-vue.js' import { isCustomElement } from './utils.js' function readFile(file) { return (file && fs.cat(file)?.toString()) || '' } export default function compile(root = '', dist = '', conf = {}) { // const SOURCE_DIR = join(root, 'src') const PUBLIC_DIR = join(root, 'public') const DEPLOY_PATH = conf.base || '' // 部署目录, 默认是根目录部署 const IS_MPA = Object.keys(conf.pages).length > 1 const PAGES_PREFIX = Object.keys(conf.pages).map(it => `pages/${it}/`) const INJECT_SCSS = readFile(conf.inject?.scss) const LEGACY_MODE = !!conf.legacy conf.inject = conf.inject || { scss: '' } let timeStart = Date.now() let template = fs.cat(join(process.cwd(), 'index.html')).toString() let list = {} fs.ls(SOURCE_DIR, true).forEach(path => { if (fs.isdir(path)) { return } let name = path.slice(SOURCE_DIR.length + 1) let it = { path, name, ext: parse(name).ext } if (it.ext !== '') { if (IS_MPA && it.name.startsWith('pages/')) { if (PAGES_PREFIX.some(it => it.startsWith(it.name))) { return (list[path] = it) } else { return } } if (it.path === conf.inject.scss) { return } list[path] = it } }) let compileFiles = function (currentPage, page, files) { let options = { IS_MPA, currentPage, SOURCE_DIR, DEPLOY_PATH, INJECT_SCSS, LEGACY_MODE, isCustomElement: conf.isCustomElement || isCustomElement } for (let k in files) { let it = files[k] // 入口文件, 特殊处理 if (page && it.path === page.entry) { let entry = fs.cat(page.entry).toString() entry = parseJs(entry, conf.imports, { ...options, IS_ENTRY: true }) let code = parseHtml(template, { page, imports: conf.imports, entry, LEGACY_MODE }) fs.echo(code, join(dist, `${currentPage}.html`)) continue } page === null && console.log(' 解析 %s ...', it.name) let pageDir = IS_MPA && currentPage ? `pages/${currentPage}` : '' switch (it.ext) { case '.vue': { let code = compileVue(it.path, conf.imports, options) Es.transform(code, { minify: true }).then(r => { fs.echo( r.code, join(dist, 'assets/', pageDir, it.name.replace(/\.vue$/, '.js')) ) }) } break case '.js': { let code = fs.cat(it.path) code = parseJs(code + '', conf.imports, options) Es.transform(code, { minify: true }).then(r => { fs.echo(r.code, join(dist, 'assets/', pageDir, it.name)) }) } break case '.scss': case '.css': { let target = join( dist, 'assets/', it.name.replace(/\.scss$/, '.css') ) if (it.ext === '.css') { fs.cp(it.path, target) } else { let code = compileScss(it.path) fs.echo(code, target) } } break default: fs.cp(it.path, join(dist, it.name)) break } } } // 优先处理静态目录, 之后的源码目录中, 以便如果有产生相同的文件名, 则覆盖静态目录中的文件 if (fs.isdir(PUBLIC_DIR)) { console.log('\n正在处理静态资源 ...') fs.ls(PUBLIC_DIR, true).forEach(it => { let ext = parse(it).ext if (ext === '') { return } if (fs.isfile(it)) { let name = it.slice(PUBLIC_DIR.length + 1) console.log(' 复制 %s ...', name) fs.cp(it, join(dist, name)) } }) } for (let currentPage in conf.pages) { let page = conf.pages[currentPage] let dir = dirname(page.entry) let files = list if (IS_MPA) { files = {} fs.ls(dir, true).forEach(path => { if (fs.isdir(path)) { return } let name = path.slice(dir.length + 1) let ext = parse(name).ext if (ext === '') { return } delete list[path] files[path] = { name, path, ext } }) } console.log('正在生成 %s ...', `${currentPage}.html`) compileFiles(currentPage, page, files) } if (IS_MPA) { console.log('\n正在解析公共依赖 ...') compileFiles('', null, list) } process.on('exit', _ => { console.log('\n页面处理完成, 耗时 %ss\n', (Date.now() - timeStart) / 1000) }) }