first commit

This commit is contained in:
2026-09-08 20:28:54 +08:00
commit 2ada8d3d5a
37380 changed files with 4886169 additions and 0 deletions
@@ -0,0 +1,38 @@
const { createSource } = require('../shared')
module.exports = function (compilation) {
if (process.env.UNI_PLATFORM !== 'mp-qq') {
return
}
// fix mp-qq https://github.com/dcloudio/uni-app/issues/2648
const appJsonName = 'app.json'
const appJsonFile = compilation.getAsset(appJsonName)
if (appJsonFile) {
const componentName = 'fix-2648'
const obj = JSON.parse(appJsonFile.source.source())
obj.usingComponents = obj.usingComponents || {}
if (!(componentName in obj.usingComponents)) {
obj.usingComponents[componentName] = `/${componentName}`
const source = JSON.stringify(obj, null, 2)
const newSource = createSource(source)
compilation.updateAsset(appJsonName, newSource)
const files = [
{
ext: 'qml',
source: '<!-- https://github.com/dcloudio/uni-app/issues/2648 -->'
},
{
ext: 'js',
source: 'Component({})'
},
{
ext: 'json',
source: '{"component":true}'
}
]
files.forEach(({ ext, source }) => {
compilation.emitAsset(`${componentName}.${ext}`, createSource(source))
})
}
}
}
@@ -0,0 +1,38 @@
const { createSource } = require('../shared')
module.exports = function (compilation) {
if (process.env.UNI_PLATFORM !== 'mp-alipay') {
return
}
// fix mp-alipay plugin
const appJsonName = 'app.json'
const appJsonFile = compilation.getAsset(appJsonName)
if (appJsonFile) {
const componentName = 'plugin-wrapper'
const obj = JSON.parse(appJsonFile.source.source())
obj.usingComponents = obj.usingComponents || {}
if (!(componentName in obj.usingComponents)) {
obj.usingComponents[componentName] = `/${componentName}`
const source = JSON.stringify(obj, null, 2)
const newSource = createSource(source)
compilation.updateAsset(appJsonName, newSource)
const files = [
{
ext: 'axml',
source: '<slot></slot>'
},
{
ext: 'js',
source: 'Component({onInit(){this.props.onPluginWrap(this)},didUnmount(){this.props.onPluginWrap(this,true)}})'
},
{
ext: 'json',
source: '{"component":true}'
}
]
files.forEach(({ ext, source }) => {
compilation.emitAsset(`${componentName}.${ext}`, createSource(source))
})
}
}
}
@@ -0,0 +1,33 @@
const path = require('path')
const {
normalizePath,
getPlatformExts
} = require('@dcloudio/uni-cli-shared')
const { deleteAsset } = require('../shared')
module.exports = function (compilation) {
// 移除部分含有错误引用的 wxss 文件
const styleImports = {}
compilation.getAssets().forEach((asset) => {
const name = asset.name
const styleExtname = getPlatformExts().style
if (name.endsWith(styleExtname)) {
let origSource = asset.source.source()
origSource = origSource.trim ? origSource.trim() : ''
const result = origSource.match(/^@import ["'](.+?)["']$/)
if (result) {
const stylePath = normalizePath(path.join(path.dirname(name), result[1]))
if (compilation.getAsset(stylePath)) {
styleImports[stylePath] = styleImports[stylePath] || []
styleImports[stylePath].push(name)
} else {
if (styleImports[name]) {
styleImports[name].forEach(name => deleteAsset(compilation, name))
delete styleImports[name]
}
deleteAsset(compilation, name)
}
}
}
})
}
@@ -0,0 +1,71 @@
const {
hyphenate,
getPlatformCompiler
} = require('@dcloudio/uni-cli-shared')
const SRC_REGEX = /src="([^"]+)"/g
module.exports = function compile (source, options) {
const {
compileToWxml,
compileToTemplate
} = getPlatformCompiler()
if (typeof compileToWxml === 'function') {
const components = {
'slots': {
src: '/common/slots.wxml',
name: 'slots'
}
}
Object.keys(options.imports).forEach(name => {
if (name !== '_slots_') {
components[hyphenate(name)] = options.imports[name]
}
})
const compiled = options.compiled
const {
code,
// compiled,
slots: mpvueSlots,
importCode
} = compileToWxml(compiled, {
name: options.name,
components,
moduleId: options.scopeId || ('M' + options.name)
})
const deps = []
if (importCode) {
let match
/* eslint-disable no-cond-assign */
while (match = SRC_REGEX.exec(importCode)) {
deps.push(match[1])
}
}
const slots = Object.keys(mpvueSlots).map(slotName => {
const slot = mpvueSlots[slotName]
return {
name: slot.name,
slotName,
body: slot.code,
dependencies: deps
}
})
return {
body: code,
slots,
deps,
mpvue: true
}
}
return compileToTemplate(source, Object.assign(options, {
htmlParse: {
templateName: 'octoParse'
}
}))
}
@@ -0,0 +1,86 @@
const {
getPlatformExts,
createSource
} = require('../shared')
const {
getShadowCss,
getPlatformGlobal
} = require('@dcloudio/uni-cli-shared')
const {
getSpecialMethods
} = require('@dcloudio/uni-cli-shared/lib/cache')
module.exports = function generateApp (compilation) {
const ext = getPlatformExts().style
let importMainCss = ''
let importVendorCss = ''
if (
process.env.NODE_ENV === 'production' &&
process.env.UNI_PLATFORM !== 'app-plus'
) {
const targetCssName = `common/main${ext}`
const asset = compilation.getAsset(targetCssName)
if (!asset) {
compilation.emitAsset(targetCssName, createSource(getShadowCss()))
} else {
const source = asset.source.source() + getShadowCss()
compilation.updateAsset(targetCssName, createSource(source))
}
}
// 框架预设样式 用于隐藏自定义组件
// TODO 分平台 import 不同 css
const platforms = ['mp-weixin', 'mp-qq', 'mp-jd', 'mp-xhs', 'mp-toutiao', 'mp-lark']
const presetStyle = platforms.includes(process.env.UNI_PLATFORM) ? '[data-custom-hidden="true"],[bind-data-custom-hidden="true"]{display: none !important;}' : ''
if (compilation.getAsset(`common/main${ext}`)) { // 是否存在 main.css
importMainCss = `@import './common/main${ext}';`
}
if (compilation.getAsset(`common/vendor${ext}`)) { // 是否存在 vendor.css
importVendorCss += `@import './common/vendor${ext}';`
}
const runtimeJsPath = 'common/runtime.js'
const asset = compilation.getAsset(runtimeJsPath)
if ( // app 和 baidu 不需要
process.env.UNI_PLATFORM !== 'app-plus' &&
process.env.UNI_PLATFORM !== 'mp-baidu' &&
asset &&
!asset.source.__$wrappered
) {
const source =
`
!function(){try{var a=Function("return this")();a&&!a.Math&&(Object.assign(a,{isFinite:isFinite,Array:Array,Date:Date,Error:Error,Function:Function,Math:Math,Object:Object,RegExp:RegExp,String:String,TypeError:TypeError,setTimeout:setTimeout,clearTimeout:clearTimeout,setInterval:setInterval,clearInterval:clearInterval}),"undefined"!=typeof Reflect&&(a.Reflect=Reflect))}catch(a){}}();
${asset.source.source()}
`
const newSource = createSource(source)
newSource.__$wrappered = true
compilation.updateAsset(runtimeJsPath, newSource)
}
const specialMethods = getSpecialMethods()
let beforeCode = ''
if (Object.keys(specialMethods).length) {
beforeCode = `${getPlatformGlobal()}.specialMethods = ${JSON.stringify(specialMethods)}`
}
return [{
file: 'app.js',
source: `${beforeCode}
require('./common/runtime.js')
require('./common/vendor.js')
require('./common/main.js')`
}, {
file: 'app' + ext,
source: `${importMainCss}
${importVendorCss}
${presetStyle}`
}]
}
@@ -0,0 +1,188 @@
const fs = require('fs')
const path = require('path')
const webpack = require('webpack')
const {
removeExt,
normalizePath
} = require('@dcloudio/uni-cli-shared')
const {
getComponentSet
} = require('@dcloudio/uni-cli-shared/lib/cache')
const {
isBuiltInComponentPath
} = require('@dcloudio/uni-cli-shared/lib/pages')
const {
restoreNodeModules,
createSource,
getModuleId
} = require('../shared')
const EMPTY_COMPONENT_LEN = 'Component({})'.length
const uniPath = normalizePath(require('@dcloudio/uni-cli-shared/lib/platform').getMPRuntimePath())
function findModule (modules, resource, altResource) {
return modules.find(
module => {
let moduleResource = module.resource
if (
!moduleResource ||
(
moduleResource.indexOf('.vue') === -1 &&
moduleResource.indexOf('.nvue') === -1
)
) {
return
}
moduleResource = removeExt(module.resource)
return moduleResource === resource || moduleResource === altResource
}
)
}
function findModuleId (compilation, modules, resource, altResource) {
const module = findModule(modules, resource, altResource)
return module && getModuleId(compilation, module)
}
function findModuleIdFromConcatenatedModules (compilation, modules, resource, altResource) {
const module = modules.find(module => {
return findModule(module.modules, resource, altResource)
})
return module && getModuleId(compilation, module)
}
function findComponentModuleId (compilation, modules, concatenatedModules, resource, altResource) {
return findModuleId(compilation, modules, resource, altResource) ||
findModuleIdFromConcatenatedModules(compilation, concatenatedModules, resource, altResource) ||
resource
}
let lastComponents = []
// TODO 解决方案不太理想
module.exports = function generateComponent (compilation, jsonpFunction = 'webpackJsonp') {
const curComponents = []
const componentChunkNameMap = {}
const components = getComponentSet()
if (components.size) {
const modules = Array.from(compilation.modules)
const concatenatedModules = modules.filter(module => module.modules)
let uniModule = modules.find(module => module.resource && normalizePath(module.resource) === uniPath)
if (!uniModule && webpack.version[0] > 4) {
uniModule = modules.find(module => module.rootModule && module.rootModule.resource && normalizePath(module.rootModule.resource) === uniPath)
}
const uniModuleId = getModuleId(compilation, uniModule)
const vueOuterComponentSting = 'vueOuterComponents'
compilation.getAssets().forEach(asset => {
const name = asset.name
// 判断是不是vue
const isVueComponent = components.has(name.replace('.js', ''))
// 独立分包外面的组件,复制到独立分包内,在components中看不到,所以需要单独处理
const isVueOuterComponent = Boolean(name.endsWith('.js') && name.indexOf(vueOuterComponentSting) >= 0)
if (isVueComponent || isVueOuterComponent) {
curComponents.push(name.replace('.js', ''))
if (asset.source.__$wrappered) {
return
}
const chunkName = name.replace('.js', '-create-component')
let moduleId = ''
if (name.indexOf('node-modules') === 0) {
const modulePath = removeExt(restoreNodeModules(name))
let resource = normalizePath(path.resolve(process.env.UNI_INPUT_DIR, '..', modulePath))
const altResource = normalizePath(path.resolve(process.env.UNI_INPUT_DIR, modulePath))
if (modulePath.includes('@dcloudio') && isBuiltInComponentPath(modulePath)) {
resource = normalizePath(path.resolve(process.env.UNI_CLI_CONTEXT, modulePath))
}
moduleId = findComponentModuleId(compilation, modules, concatenatedModules, resource, altResource)
} else {
const resource = removeExt(path.resolve(process.env.UNI_INPUT_DIR, name))
moduleId = findComponentModuleId(compilation, modules, concatenatedModules, resource)
}
const origSource = asset.source.source()
if (isVueComponent) {
componentChunkNameMap[name] = moduleId
} else if (isVueOuterComponent) {
const startIndex = name.indexOf(vueOuterComponentSting) + vueOuterComponentSting.length + 1
const rightOriginalComponentName = name.substring(startIndex)
moduleId = componentChunkNameMap[rightOriginalComponentName]
}
if (origSource.length !== EMPTY_COMPONENT_LEN) { // 不是空组件
const globalVar = process.env.UNI_PLATFORM === 'mp-alipay' ? 'my' : 'global'
// 主要是为了解决支付宝旧版本, Component 方法只在组件 js 里有,需要挂在 my.defineComponent
let beforeCode = ''
if (process.env.UNI_PLATFORM === 'mp-alipay') {
beforeCode = ';my.defineComponent || (my.defineComponent = Component);'
}
const source = beforeCode + origSource + (webpack.version[0] > 4
? `
;(${globalVar}["${jsonpFunction}"] = ${globalVar}["${jsonpFunction}"] || []).push([
['${chunkName}'],
{},
function(__webpack_require__){
__webpack_require__('${uniModuleId}')['createComponent'](__webpack_require__(${JSON.stringify(moduleId)}))
}
]);
`
: `
;(${globalVar}["${jsonpFunction}"] = ${globalVar}["${jsonpFunction}"] || []).push([
'${chunkName}',
{
'${chunkName}':(function(module, exports, __webpack_require__){
__webpack_require__('${uniModuleId}')['createComponent'](__webpack_require__(${JSON.stringify(moduleId)}))
})
},
[['${chunkName}']]
]);
`
)
const newSource = createSource(source)
newSource.__$wrappered = true
compilation.updateAsset(name, newSource)
}
}
})
}
if (process.env.UNI_FEATURE_OBSOLETE !== 'false') {
if (lastComponents.length) {
for (const name of lastComponents) {
if (!curComponents.includes(name)) {
removeUnusedComponent(name) // 组件被移除
}
}
}
for (const name of curComponents) {
if (!lastComponents.includes(name)) {
addComponent(name) // 新增组件
}
}
lastComponents = curComponents
}
}
function addComponent (name) {
const bakJson = path.join(process.env.UNI_OUTPUT_DIR, name + '.bak.json')
if (fs.existsSync(bakJson)) {
try {
fs.renameSync(bakJson, path.join(process.env.UNI_OUTPUT_DIR, name + '.json'))
} catch (e) { }
}
}
function removeUnusedComponent (name) {
try {
fs.renameSync(path.join(process.env.UNI_OUTPUT_DIR, name + '.json'), path.join(process.env.UNI_OUTPUT_DIR, name +
'.bak.json'))
} catch (e) { }
}
@@ -0,0 +1,221 @@
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 + `<import src="${normalizeNodeModules(im)}" />\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 = `<import src="/htmlparse/index${templateExt}" />
${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
}
@@ -0,0 +1,265 @@
const path = require('path')
const {
normalizePath
} = require('@dcloudio/uni-cli-shared')
const {
getPageSet,
getJsonFileMap,
getChangedJsonFileMap,
supportGlobalUsingComponents
} = require('@dcloudio/uni-cli-shared/lib/cache')
const { createSource } = require('../shared')
// 主要解决 extends 且未实际引用的组件
const EMPTY_COMPONENT = 'Component({})'
const usingComponentsMap = {}
// 百度小程序动态组件库 usingSwanComponents 引用组件
const mpBaiduDynamicLibs = [
'dynamicLib://editorLib/editor',
'dynamicLib://echartsLib/chart',
'dynamicLib://myModelviewer/modelviewer',
'dynamicLib://myDynamicLib/panoviewer',
'dynamicLib://myDynamicLib/spintileviewer',
'dynamicLib://myDynamicLib/vrvideo'
]
const AnalyzeDependency = require('@dcloudio/uni-mp-weixin/lib/independent-plugins/optimize-components-position/index')
function analyzeUsingComponents () {
if (!process.env.UNI_OPT_SUBPACKAGES) {
return
}
const pageSet = getPageSet()
const jsonFileMap = getJsonFileMap()
// 生成所有组件引用关系
for (const name of jsonFileMap.keys()) {
const jsonObj = JSON.parse(jsonFileMap.get(name))
const usingComponents = jsonObj.usingComponents
if (!usingComponents || !pageSet.has(name)) {
continue
}
// usingComponentsMap[name] = {}
Object.keys(usingComponents).forEach(componentName => {
const componentPath = usingComponents[componentName].slice(1)
if (!usingComponentsMap[componentPath]) {
usingComponentsMap[componentPath] = new Set()
}
usingComponentsMap[componentPath].add(name)
})
}
const subPackageRoots = Object.keys(process.UNI_SUBPACKAGES)
const findSubPackage = function (pages) {
const pkgs = new Set()
for (let i = 0; i < pages.length; i++) {
const pagePath = pages[i]
const pkgRoot = subPackageRoots.find(root => pagePath.indexOf(root) === 0)
if (!pkgRoot) { // 被非分包引用
return false
}
pkgs.add(pkgRoot)
if (pkgs.size > 1) { // 被多个分包引用
return false
}
}
return [...pkgs][0]
}
Object.keys(usingComponentsMap).forEach(componentName => {
const subPackage = findSubPackage([...usingComponentsMap[componentName]])
if (subPackage && componentName.indexOf(subPackage) !== 0) { // 仅存在一个子包引用且未在该子包
console.warn(`自定义组件 ${componentName} 建议移动到子包 ${subPackage}`)
}
})
// 生成所有组件递归引用关系
// Object.keys(usingComponentsMap).forEach(name => {
// Object.keys(usingComponentsMap[name]).forEach(componentName => {
// const usingComponents = usingComponentsMap[componentName.slice(1)]
// if (usingComponents) {
// usingComponentsMap[name][componentName] = usingComponents
// }
// })
// })
//
// // 生成页面组件引用关系
// const pageSet = getPageSet()
// const pagesUsingComponents = Object.keys(usingComponentsMap).reduce((pages, name) => {
// if (pageSet.has(name)) {
// pages[name] = usingComponentsMap[name]
// }
// return pages
// }, {})
}
const parseRequirePath = path => /^[A-z]/.test(path) ? `./${path}` : path
function normalizeUsingComponents (file, usingComponents) {
const names = Object.keys(usingComponents)
if (!names.length) {
return usingComponents
}
file = path.dirname('/' + file)
names.forEach(name => {
usingComponents[name] = normalizePath(parseRequirePath(path.relative(file, usingComponents[name])))
})
return usingComponents
}
const cacheFileMap = new Map()
module.exports = function generateJson (compilation) {
analyzeUsingComponents()
const emitFileMap = new Map([...cacheFileMap])
const jsonFileMap = getChangedJsonFileMap()
for (const name of jsonFileMap.keys()) {
const jsonObj = JSON.parse(jsonFileMap.get(name))
if (process.env.UNI_PLATFORM === 'app-plus') { // App平台默认增加usingComponents,激活__wxAppCode__
jsonObj.usingComponents = jsonObj.usingComponents || {}
}
// customUsingComponents
if (jsonObj.customUsingComponents && Object.keys(jsonObj.customUsingComponents).length) {
jsonObj.usingComponents = Object.assign(jsonObj.customUsingComponents, jsonObj.usingComponents)
}
delete jsonObj.customUsingComponents
// usingGlobalComponents
if (!supportGlobalUsingComponents && jsonObj.usingGlobalComponents && Object.keys(jsonObj.usingGlobalComponents).length) {
jsonObj.usingComponents = Object.assign(jsonObj.usingGlobalComponents, jsonObj.usingComponents)
}
// usingAutoImportComponents
if (jsonObj.usingAutoImportComponents && Object.keys(jsonObj.usingAutoImportComponents).length) {
jsonObj.usingComponents = Object.assign(jsonObj.usingAutoImportComponents, jsonObj.usingComponents)
}
delete jsonObj.usingAutoImportComponents
// 百度小程序插件内组件使用 usingSwanComponents
if (process.env.UNI_PLATFORM === 'mp-baidu') {
const usingComponents = jsonObj.usingComponents || {}
Object.keys(usingComponents).forEach(key => {
const value = usingComponents[key]
if (value.includes('://')) {
/**
* 部分动态库组件(如:editor)使用‘usingSwanComponents 引入
* 部分动态库组件(如:swan-sitemap-list)使用'usingComponents'引入
* 做白名单机制
*/
if (mpBaiduDynamicLibs.includes(value)) {
delete usingComponents[key]
jsonObj.usingSwanComponents = jsonObj.usingSwanComponents || {}
jsonObj.usingSwanComponents[key] = value
}
}
})
}
// fix mp-alipay plugin
if (process.env.UNI_PLATFORM === 'mp-alipay' && name !== 'app.json') {
const usingComponents = jsonObj.usingComponents || {}
if (Object.values(usingComponents).find(value => value.startsWith('plugin://'))) {
const componentName = 'plugin-wrapper'
usingComponents[componentName] = '/' + componentName
}
}
if (jsonObj.genericComponents && jsonObj.genericComponents.length) { // scoped slots
// 生成genericComponents json
const genericComponents = Object.create(null)
const scopedSlotComponents = []
jsonObj.genericComponents.forEach(genericComponentName => {
const genericComponentFile = normalizePath(
path.join(path.dirname(name), genericComponentName + '.json')
)
genericComponents[genericComponentName] = '/' +
genericComponentFile.replace(
path.extname(genericComponentFile), ''
)
scopedSlotComponents.push(genericComponentFile)
})
jsonObj.usingComponents = Object.assign(genericComponents, jsonObj.usingComponents)
const scopedSlotComponentJson = {
component: true,
usingComponents: jsonObj.usingComponents
}
const scopedSlotComponentJsonSource = JSON.stringify(scopedSlotComponentJson, null, 2)
scopedSlotComponents.forEach(scopedSlotComponent => {
compilation.emitAsset(scopedSlotComponent, createSource(scopedSlotComponentJsonSource))
})
}
delete jsonObj.genericComponents
if (process.env.UNI_PLATFORM !== 'app-plus' && process.env.UNI_PLATFORM !== 'h5') {
delete jsonObj.navigationBarShadow
}
if ((process.env.UNI_SUBPACKGE || process.env.UNI_MP_PLUGIN) && jsonObj.usingComponents) {
jsonObj.usingComponents = normalizeUsingComponents(name, jsonObj.usingComponents)
}
emitFileMap.set(name, jsonObj)
cacheFileMap.set(name, JSON.parse(JSON.stringify(jsonObj))) // 做一次拷贝,emitFileMap中内容在后面会被修改
}
// 组件依赖分析
(new AnalyzeDependency()).init(emitFileMap, compilation)
for (const [name, jsonObj] of emitFileMap) {
if (name === 'app.json') { // 删除manifest.json携带的配置项
delete jsonObj.insertAppCssToIndependent
delete jsonObj.independent
delete jsonObj.copyWxComponentsOnDemand
if (process.env.UNI_PLATFORM === 'mp-weixin') {
require('./mp-weixin-uniad-app.json')(jsonObj, process.env.USE_UNI_AD)
} else if (process.env.UNI_PLATFORM === 'mp-alipay') {
require('./mp-alipay-uniad-app.json')(jsonObj, process.env.USE_UNI_AD_ALIPAY)
}
} else { // 删除用于临时记录的属性
delete jsonObj.usingGlobalComponents
}
emit(name, jsonObj, compilation)
}
if (process.env.UNI_USING_CACHE && jsonFileMap.size) {
setTimeout(() => {
require('@dcloudio/uni-cli-shared/lib/cache').store()
}, 50)
}
}
function emit (name, jsonObj, compilation) {
if (jsonObj.usingComponents) {
jsonObj.usingComponents = Object.assign({}, jsonObj.usingComponents)
}
const source = JSON.stringify(jsonObj, null, 2)
const jsFile = name.replace('.json', '.js')
if (
![
'app.js',
'manifest.js',
'mini.project.js',
'ascf.config.js',
'quickapp.config.js',
'project.config.js',
'project.swan.js'
].includes(
jsFile) &&
!compilation.getAsset(jsFile)
) {
compilation.emitAsset(jsFile, createSource(EMPTY_COMPONENT))
}
compilation.emitAsset(name, createSource(source))
}
@@ -0,0 +1,28 @@
const path = require('path')
const {
getPlatformExts
} = require('../shared')
const ROOT_DATA_VAR = '$root'
function generatePageWxml (name, importee) {
if (process.env.UNI_PLATFORM === 'mp-baidu') {
return `<import src="${importee}" />
<template is="${name}" data="{{{ ...${ROOT_DATA_VAR}['0'], ${ROOT_DATA_VAR} }}}"/>`
} else if (process.env.UNI_PLATFORM === 'mp-alipay') {
return `<template is="${name}" data="{{ ...${ROOT_DATA_VAR}['0'], ${ROOT_DATA_VAR} }}"/>`
}
return `<import src="${importee}" />
<template is="${name}" data="{{ ...${ROOT_DATA_VAR}['0'], ${ROOT_DATA_VAR} }}"/>`
}
module.exports = function generatePagesWxml (pages, subPages) {
return Object.keys(pages).map(page => { // page wxml
const ext = getPlatformExts().template
return {
file: page + ext,
source: generatePageWxml(pages[page], `./${path.basename(page)}.vue${ext}`)
}
})
}
+147
View File
@@ -0,0 +1,147 @@
const path = require('path')
const webpack = require('webpack')
const {
parseEntry,
normalizePath
} = require('@dcloudio/uni-cli-shared')
const {
pagesJsonJsFileName
} = require('@dcloudio/uni-cli-shared/lib/pages')
const { createSource, getModuleId } = require('../shared')
const generateApp = require('./generate-app')
const generateJson = require('./generate-json')
const generateComponent = require('./generate-component')
const clearStyleFile = require('./clear-style-file')
const mockGenericComponent = require('./mock-generic-component')
const addEmptyComponent = require('./add-empty-component')
const addPluginWrapper = require('./add-plugin-wrapper')
function emitFile (filePath, source, compilation) {
compilation.emitAsset(filePath, createSource(source))
}
function addSubPackagesRequire (compilation) {
if (!process.env.UNI_OPT_SUBPACKAGES) {
return
}
const assetsKeys = Object.keys(compilation.assets)
Object.keys(process.UNI_SUBPACKAGES).forEach(root => {
const subPackageVendorPath = normalizePath(path.join(root, 'common/vendor.js'))
if (assetsKeys.indexOf(subPackageVendorPath) !== -1) {
// TODO 理论上仅需在分包第一个 js 中添加 require common vendor,但目前不同平台可能顺序不一致,
// 故 每个分包里的 js 里均添加一次 require
assetsKeys.forEach(name => {
if (
path.extname(name) === '.js' &&
name.indexOf(root + '/') === 0 &&
name !== subPackageVendorPath
) {
let relativePath = normalizePath(path.relative(path.dirname(name), subPackageVendorPath))
if (!relativePath.startsWith('.')) {
relativePath = './' + relativePath
}
const source = `require('${relativePath}');` + compilation.getAsset(name).source.source()
compilation.updateAsset(name, createSource(source))
}
})
}
})
}
function addMPPluginRequire (compilation) {
// 编译到小程序插件 特殊处理入口文件
const assetsKeys = Object.keys(compilation.assets)
const UNI_MP_PLUGIN_MAIN = process.env.UNI_MP_PLUGIN_MAIN
const UNI_MP_PLUGIN_EXPORT = JSON.parse(process.env.UNI_MP_PLUGIN_EXPORT)
assetsKeys.forEach(name => {
const needProcess = process.env.UNI_MP_PLUGIN ? name === UNI_MP_PLUGIN_MAIN : UNI_MP_PLUGIN_EXPORT.includes(name)
if (needProcess) {
const modules = Array.from(compilation.modules)
const orignalSource = compilation.getAsset(name).source.source()
const globalEnv = process.env.UNI_PLATFORM === 'mp-alipay' ? 'my' : 'wx'
const filePath = normalizePath(path.resolve(process.env.UNI_INPUT_DIR, name))
let uniModule = modules.find(module => module.resource && normalizePath(module.resource) === filePath)
if (!uniModule && webpack.version[0] > 4) {
uniModule = modules.find(module =>
module.rootModule && module.rootModule.resource && normalizePath(module.rootModule.resource) === filePath
)
}
const uniModuleId = getModuleId(compilation, uniModule)
const source = orignalSource + `\nmodule.exports = ${globalEnv}.__webpack_require_UNI_MP_PLUGIN__('${uniModuleId}');\n`
compilation.updateAsset(name, createSource(source))
}
})
}
function processAssets (compiler, compilation) {
addSubPackagesRequire(compilation)
addMPPluginRequire(compilation)
generateJson(compilation)
// app.js,app.wxss
generateApp(compilation)
.forEach(({
file,
source
}) => emitFile(file, source, compilation))
generateComponent(compilation, compiler.options.output[webpack.version[0] > 4 ? 'chunkLoadingGlobal' : 'jsonpFunction'])
clearStyleFile(compilation)
mockGenericComponent(compilation)
addEmptyComponent(compilation)
addPluginWrapper(compilation)
}
class WebpackUniMPPlugin {
apply (compiler) {
if (!process.env.UNI_USING_NATIVE && !process.env.UNI_USING_V3_NATIVE) {
if (webpack.version[0] > 4) {
compiler.hooks.compilation.tap('WebpackUniMPPlugin', compilation => {
compilation.hooks.processAssets.tap({
name: 'WebpackUniMPPlugin',
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
}, (_) => {
processAssets(compiler, compilation)
})
})
} else {
compiler.hooks.emit.tap('webpack-uni-mp-emit', (compilation) => processAssets(compiler, compilation))
}
}
compiler.hooks.invalid.tap('webpack-uni-mp-invalid', (fileName, changeTime) => {
if (
fileName &&
typeof fileName === 'string'
) { // 重新解析 entry
const basename = path.basename(fileName)
const deps = process.UNI_PAGES_DEPS || new Set()
if (
basename === 'pages.json' ||
basename === pagesJsonJsFileName ||
deps.has(normalizePath(fileName))
) {
try {
parseEntry()
} catch (e) {
console.error(e)
}
}
}
})
}
}
module.exports = WebpackUniMPPlugin
+123
View File
@@ -0,0 +1,123 @@
const path = require('path')
const {
md5,
parseEntry,
normalizePath
} = require('@dcloudio/uni-cli-shared')
const {
pagesJsonJsFileName
} = require('@dcloudio/uni-cli-shared/lib/pages')
const {
getPages,
getSubPages,
getTemplates,
getCompilerOptions
} = require('../shared')
const generateApp = require('./generate-app')
const generatePagesWxml = require('./generate-pages-wxml')
const generateComponentsWxml = require('./generate-components-wxml')
const emitFileCaches = {}
function emitFile (filePath, source, compilation) {
const emitFileMD5 = md5(filePath + source)
if (emitFileCaches[filePath] !== emitFileMD5) {
emitFileCaches[filePath] = emitFileMD5
compilation.assets[filePath] = {
size () {
return Buffer.byteLength(source, 'utf8')
},
source () {
return source
}
}
}
}
class WebpackUniMPPlugin {
apply (compiler) {
compiler.hooks.emit.tapPromise('webpack-uni-mp-emit', compilation => {
return new Promise((resolve, reject) => {
// app.js,app.wxss
generateApp(compilation)
.forEach(({
file,
source
}) => emitFile(file, source, compilation))
if (process.env.UNI_PLATFORM === 'mp-alipay') { // 支付宝页面 axml 仅生成一个(因 template 内不能使用自定义组件,比如 rich-text )
const pageAxmls = {}
generatePagesWxml(getPages(), getSubPages())
.forEach(({
file,
source
}) => {
pageAxmls[file] = source
})
// components wxml
generateComponentsWxml(getTemplates(), getCompilerOptions(), Array.from(new Set(
Object.values(process.UNI_SUB_PACKAGES_ROOT))))
.forEach(({
file,
source
}) => {
const pageAxmlPath = file.replace('.vue', '')
const pageAxmlSource = pageAxmls[pageAxmlPath]
if (pageAxmlSource) { // page.axml
emitFile(pageAxmlPath, source + '\n' + pageAxmlSource,
compilation)
} else {
emitFile(file, source, compilation)
}
})
} else {
// pages wxml
generatePagesWxml(getPages(), getSubPages())
.forEach(({
file,
source
}) => emitFile(file, source, compilation))
// components wxml
generateComponentsWxml(getTemplates(), getCompilerOptions(), Array.from(new Set(
Object.values(process.UNI_SUB_PACKAGES_ROOT))))
.forEach(({
file,
source
}) => emitFile(file, source, compilation))
}
resolve()
})
})
compiler.hooks.invalid.tap('webpack-uni-mp-invalid', (fileName, changeTime) => {
if (
fileName &&
typeof fileName === 'string'
) { // 重新解析 entry
const basename = path.basename(fileName)
const deps = process.UNI_PAGES_DEPS || new Set()
if (
basename === 'pages.json' ||
basename === pagesJsonJsFileName ||
deps.has(normalizePath(fileName))
) {
try {
parseEntry()
} catch (e) {
console.error(e)
}
}
}
})
}
}
module.exports = WebpackUniMPPlugin
@@ -0,0 +1,61 @@
const path = require('path')
const {
normalizePath,
getPlatformExts
} = require('@dcloudio/uni-cli-shared')
const { createSource, deleteAsset } = require('../shared')
module.exports = function (compilation) {
// 处理字节跳动|飞书小程序作用域插槽
const fixExtname = '.fix'
const fixSlots = {}
compilation.getAssets().forEach((asset) => {
const name = asset.name
if (name.endsWith(fixExtname)) {
const source = asset.source.source()
const [ownerName, parentName, componentName, slotName] = source.split(',')
const json = compilation.getAsset(ownerName + '.json')
const jsonSource = json && json.source.source()
if (jsonSource) {
const data = JSON.parse(jsonSource)
const usingComponents = data.usingComponents || {}
const componentPath = normalizePath(path.relative('/', usingComponents[parentName]))
const slots = fixSlots[componentPath] = fixSlots[componentPath] || {}
const slot = slots[slotName] = slots[slotName] || {}
slot[componentName] = '/' + name.replace(fixExtname, '')
deleteAsset(compilation, name)
const jsonName = `${componentPath}.json`
const jsonFile = compilation.getAsset(jsonName)
if (jsonFile) {
const oldSource = jsonFile.source.source()
const sourceObj = JSON.parse(oldSource)
Object.values(slots).forEach(components => {
const usingComponents = sourceObj.usingComponents = sourceObj.usingComponents || {}
Object.assign(usingComponents, components)
})
delete sourceObj.componentGenerics
const source = JSON.stringify(sourceObj, null, 2)
compilation.updateAsset(jsonName, createSource(source))
}
const templateName = `${componentPath}${getPlatformExts().template}`
const templateFile = compilation.getAsset(templateName)
if (templateFile) {
const oldSource = templateFile.source.source()
let templateSource = oldSource
Object.keys(slots).forEach(name => {
const reg = new RegExp(`<${name} (.+?)></${name}>`)
templateSource = oldSource.replace(reg, string => {
const props = string.match(reg)[1]
return Object.keys(slots[name]).map(key => {
return `<block tt:if="{{generic['${name.replace(/^scoped-slots-/, '')}']==='${key}'}}"><${key} ${props}></${key}></block>`
}).join('')
})
})
compilation.updateAsset(templateName, createSource(templateSource))
}
}
}
})
}
@@ -0,0 +1,38 @@
const UNI_PLUGINS = [{
name: 'uni-ad',
version: '*',
provider: '2021004169623603'
}
]
module.exports = function (appJson, useAD) {
if (!useAD) {
return
}
if (!appJson.plugins) {
appJson.plugins = {}
}
for (let i = 0; i < UNI_PLUGINS.length; i++) {
const { name, version, provider } = UNI_PLUGINS[i]
appJson.plugins[name] = {
version,
provider
}
}
if (!appJson.usingComponents) {
appJson.usingComponents = {}
}
if (!appJson.usingComponents['uniad-plugin']) {
appJson.usingComponents['uniad-plugin'] = 'plugin://uni-ad/ad'
}
if (!appJson.window) {
appJson.window = {}
}
// 信息流需要添加此配置
if (!appJson.window.enableInPageRender) {
appJson.window.enableInPageRender = 'YES'
}
}
@@ -0,0 +1,53 @@
const UNI_PLUGINS = [{
name: 'uni-ad',
version: '1.3.7',
provider: 'wxf72d316417b6767f'
},
{
name: 'coral-adv',
version: '1.0.27',
provider: 'wx0e203209e27b1e66'
}
]
const {
getManifestJson
} = require('@dcloudio/uni-cli-shared/lib/manifest.js')
module.exports = function (appJson, useAD) {
const manifestJson = getManifestJson()
const manifestJsonWxNode = manifestJson['mp-weixin']
if (manifestJsonWxNode) {
const plugins = manifestJsonWxNode.plugins || {}
for (const key in plugins) {
const provider = plugins[key].provider
if (provider && provider === 'wx0e203209e27b1e66') {
console.error('应用的uni-ad配置不正确,请直接在页面中引入uni-ad广告组件,无需单独引入插件。')
process.exit(-1)
}
}
}
if (!useAD) {
return
}
if (!appJson.plugins) {
appJson.plugins = {}
}
for (let i = 0; i < UNI_PLUGINS.length; i++) {
const { name, version, provider } = UNI_PLUGINS[i]
appJson.plugins[name] = {
version,
provider
}
}
if (!appJson.usingComponents) {
appJson.usingComponents = {}
}
if (!appJson.usingComponents['uniad-plugin']) {
appJson.usingComponents['uniad-plugin'] = 'plugin://uni-ad/ad'
}
}
+30
View File
@@ -0,0 +1,30 @@
const path = require('path')
const {
normalizePath
} = require('@dcloudio/uni-cli-shared')
const {
getPlatformExts
} = require('../shared')
const templateExt = getPlatformExts().template
const SLOTS_OUTPUT_PATH = '/[root]common/slots'
function getRelativePath (from, to) {
let relativePath = path.relative(from, to)
if (relativePath.indexOf('.') !== 0) {
relativePath = './' + relativePath
}
return normalizePath(relativePath)
}
function getSlotsPath (root) {
return SLOTS_OUTPUT_PATH.replace('[root]', root) + templateExt
}
module.exports = {
getSlotsPath,
getRelativePath
}