const { md5 } = require('@dcloudio/uni-cli-shared') const { getSlotsPath } = require('./util') const { getRoot, getPlatformExts, getGlobalComponents, normalizeNodeModules, getCompiledComponentTemplate, cacheCompiledComponentTemplates } = require('../shared') const compileToTemplate = require('./compile-to-template') const templateExt = getPlatformExts().template function normalizeImports (imports = {}, componentPath, subPackages) { const res = {} Object.keys(imports).forEach(key => { const { name, src } = imports[key] res[key] = { name, src: '/' + normalizeNodeModules(src) + '.vue' + templateExt } }) return res } function generateSlotsWxml ({ imports, contents }) { let slotsOutput = '' imports.forEach(im => { slotsOutput = slotsOutput + `\n` }) slotsOutput = slotsOutput + `\n` contents.forEach(b => { slotsOutput = slotsOutput + b + `\n\n` }) return slotsOutput } module.exports = function generateComponentsWxml (templates, allCompilerOptions, subPackages) { const mainSlots = { imports: new Set(), contents: new Set() } const subPackageSlots = {} const files = [] const allDeps = new Set() // only for mpvue const globalComponents = getGlobalComponents() let isMPVue = false Object.keys(templates).forEach(filePath => { const source = templates[filePath] const compilerOptions = allCompilerOptions[filePath] if (!compilerOptions) { throw new Error(filePath + ' error ') } const root = getRoot(filePath, subPackages) const imports = normalizeImports(compilerOptions.imports, filePath, subPackages) // add slots imports['_slots_'] = { name: '', src: getSlotsPath(root) } const emitFilePath = filePath + '.vue' + templateExt // 全局组件 Object.keys(globalComponents).forEach(name => { imports[name] = globalComponents[name] }) const componentMD5 = md5(source + compilerOptions.scopeId + JSON.stringify(imports)) const compiledComponentTemplate = getCompiledComponentTemplate(filePath) let currentSlots = [] if (!subPackageSlots[root] && root) { subPackageSlots[root] = { imports: new Set(), contents: new Set() } } if (compiledComponentTemplate.md5 !== componentMD5) { const result = compileToTemplate(source, Object.assign({}, compilerOptions, { imports })) let body = result.body const { slots, deps = [], mpvue, needHtmlParse } = result if (mpvue) { isMPVue = true deps.forEach(dep => { allDeps.add(dep) }) } if (needHtmlParse) { body = ` ${body}` } cacheCompiledComponentTemplates(filePath, { md5: componentMD5, body, slots }) currentSlots = slots || [] files.push({ file: emitFilePath, source: body }) } else { currentSlots = compiledComponentTemplate.slots || [] } let collector if (root) { collector = subPackageSlots[root] } else { collector = mainSlots } currentSlots.forEach(slot => { const dependencies = slot.dependencies || [] const body = slot.body dependencies.forEach(d => collector.imports.add(d)) collector.contents.add(body) if (collector !== mainSlots) { // TODO 待优化,把分包内容全部写入主包 slots 中 dependencies.forEach(d => mainSlots.imports.add(d)) mainSlots.contents.add(body) } }) }) if (!isMPVue) { // subPackage slots Object.keys(subPackageSlots) .forEach(root => { const { imports, contents } = subPackageSlots[root] || {} // subpackage slots files.push({ file: getSlotsPath(root), source: generateSlotsWxml({ imports, contents }) }) }) } else { // merge Object.keys(subPackageSlots) .forEach(root => { const { imports, contents } = subPackageSlots[root] || {} if (imports && imports.size) { mainSlots.imports = new Set([...mainSlots.imports, ...imports]) } if (contents && contents.size) { mainSlots.contents = new Set([...mainSlots.contents, ...contents]) } }) // mpvue slots add all imports allDeps.forEach(dep => { mainSlots.imports.add(dep) }) } // main slots files.push({ file: getSlotsPath(''), source: generateSlotsWxml({ imports: mainSlots.imports, contents: mainSlots.contents }) }) // TODO 遗留问题:当subPackage 引用 main 中的组件时,slots 被放在 subPackage 的 slots 中,导致 main 中的组件访问不到该 slots // 格式化node_modules,在微信小程序中,node_modules目录会被过滤掉,cli时 node_modules 在外层,也要转移到根目录 files.forEach(file => { file.file = normalizeNodeModules(file.file) }) return files }