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,69 @@
const uniI18n = require('@dcloudio/uni-cli-i18n')
function addImportsMap (metadata, name, source) {
if (!metadata.modules) {
metadata.modules = {}
}
metadata.modules[name] = source
}
module.exports = function babelPluginGlobalComponent ({
types: t
}) {
return {
visitor: {
Program: {
exit (path) {
path.traverse({
CallExpression (path) {
if (path.hub) {
const {
callee,
arguments: args
} = path.node
const {
metadata
} = path.hub.file
if (!callee.object || !callee.property) {
return
}
if (callee.object.name === 'Vue' && callee.property.name === 'component') {
if (!args[0] || args[0].type !== 'StringLiteral') {
throw new Error(uniI18n.__('mpLoader.firstParameterNeedStaticString', { 0: 'Vue.component()' }))
}
if (!args[1]) {
throw new Error(uniI18n.__('mpLoader.requireTwoParameter', { 0: 'Vue.component()' }))
}
if (!metadata.globalComponents) {
metadata.globalComponents = {}
}
metadata.globalComponents[args[0].value] = metadata.modules[args[1].name]
}
}
}
})
}
},
ImportDeclaration (path) {
if (path.hub) {
const {
specifiers,
source: {
value
}
} = path.node
const {
metadata
} = path.hub.file
specifiers.forEach(specifier => {
addImportsMap(metadata, specifier.local.name, value)
})
}
}
}
}
}
@@ -0,0 +1,91 @@
const uniI18n = require('@dcloudio/uni-cli-i18n')
const hyphenateRE = /\B([A-Z])/g
function cached (fn) {
const cache = Object.create(null)
return function cachedFn (str) {
const hit = cache[str]
return hit || (cache[str] = fn(str))
}
}
const hyphenate = cached((str) => {
return str.replace(hyphenateRE, '-$1').toLowerCase()
})
module.exports = function ({
types: t
}) {
return {
visitor: {
ExportDefaultDeclaration (path) {
const declaration = path.node.declaration
// export default {components:{}}
if (t.isObjectExpression(declaration)) {
handleObjectExpression(declaration, path)
}
// export default Vue.extend({components:{}})
if (t.isCallExpression(declaration) && t.isMemberExpression(declaration.callee) && declaration.arguments
.length === 1) {
if (declaration.callee.object.name === 'Vue' && declaration.callee.property.name === 'extend') {
handleObjectExpression(declaration.arguments[0], path)
}
}
// export default @Component({components:{}}) class MyComponent extend Vue
if (t.isClassDeclaration(declaration) && declaration.decorators && declaration.decorators.length) {
const componentDecorator = declaration.decorators[0]
if (t.isCallExpression(componentDecorator.expression)) {
const args = componentDecorator.expression.arguments
if (args && args.length && t.isObjectExpression(args[0])) {
handleObjectExpression(args[0], path)
}
}
}
}
}
}
function handleObjectExpression (declaration, path) {
const componentsProperty = declaration.properties.filter(prop => {
return t.isObjectProperty(prop) && t.isIdentifier(prop.key) &&
prop.key.name === 'components'
})[0]
if (componentsProperty && t.isObjectExpression(componentsProperty.value)) {
const properties = componentsProperty.value.properties
.filter(prop => t.isObjectProperty(prop) && t.isIdentifier(prop.value))
const components = {}
properties.forEach(prop => {
// prop.key maybe Identifier or StringLiteral
// Identifier use name, StringLiteral use value
const key = prop.key.name || prop.key.value
const value = prop.value.name
const source = findSource(value, path.scope.bindings)
if (!source) {
throw new Error(uniI18n.__('mpLoader.componentReferenceError', { 0: key }))
}
if (process.UNI_LIBRARIES.includes(source)) {
const componentName = hyphenate(key)
components[key] = source + '/lib/' + componentName + '/' + componentName
} else {
components[key] = source
}
})
path.hub.file.metadata.components = components
}
}
function findSource (identifierName, bindings) {
const binding = bindings[identifierName]
if (!binding) {
return
}
if (t.isImportDeclaration(binding.path.parent)) {
return binding.path.parent.source.value
}
}
}
@@ -0,0 +1,66 @@
const path = require('path')
const t = require('@babel/types')
const babelTraverse = require('@babel/traverse').default
const {
parseComponents
} = require('./util')
const uniI18n = require('@dcloudio/uni-cli-i18n')
module.exports = function (ast, state = {}) {
const imports = []
let nodePath = false
try {
babelTraverse(ast, {
CallExpression (path) {
const callee = path.node.callee
if (!callee.object || !callee.property) {
return
}
const objectName = callee.object.name
const propertyName = callee.property.name
if (
propertyName === 'component' &&
(objectName === 'Vue' || objectName === 'app')
) {
const args = path.node.arguments
const nameNode = args[0]
const valueNode = args[1]
nodePath = path
if (!t.isStringLiteral(nameNode)) {
throw new Error(
uniI18n.__('mpLoader.firstParameterNeedStaticString', {
0: objectName + '.component()'
})
)
}
if (!t.isIdentifier(valueNode)) {
throw new Error(
uniI18n.__('mpLoader.requireTwoParameter', {
0: objectName + '.component()'
})
)
}
imports.push({
name: nameNode.value,
value: valueNode.name
})
}
}
})
if (imports.length) {
state.components = parseComponents(imports, nodePath)
} else {
state.components = []
}
} catch (e) {
if (state.filename) {
console.error('at ' + require('@dcloudio/uni-cli-shared').normalizePath(path.relative(process.env.UNI_INPUT_DIR, state.filename)) + ':1')
}
throw e
}
return {
ast,
state
}
}
@@ -0,0 +1,25 @@
module.exports = function ({
types: t
}) {
return {
visitor: {
MemberExpression (path, state) {
if (
t.isIdentifier(path.node.property) &&
path.node.property.name === '$mount' &&
!path.node.$createApp
) {
path.node.$createApp = true
path.get('object').replaceWith(
t.callExpression(
t.identifier('createApp'),
[
path.node.object
]
)
)
}
}
}
}
}
@@ -0,0 +1,57 @@
const t = require('@babel/types')
const babelTemplate = require('@babel/template').default
// const buildDynamicImport = babelTemplate(`var IMPORT_NAME = ()=>import(IMPORT_SOURCE)`, {
// preserveComments: true,
// plugins: [
// 'dynamicImport'
// ]
// })
// 已废弃,@vue/cli-plugin-babel@4 增加了 dynamic import 转换
// var test = ()=>import(/* webpackChunkName: "components/test" */'../../components/test')
// function getDynamicImport (name, source, chunkName) {
// const stringLiteral = t.stringLiteral(source)
// const dynamicImportComment = {
// type: 'CommentBlock',
// value: `webpackChunkName: "${chunkName}"`
// }
// stringLiteral.leadingComments = [dynamicImportComment]
// return buildDynamicImport({
// IMPORT_NAME: t.identifier(name),
// IMPORT_SOURCE: stringLiteral
// })
// }
// var test = function(resolve) {require.ensure([], () => resolve(require('../../components/test')),'components/test')}
const buildRequireEnsure = babelTemplate(
'var IMPORT_NAME = function(){require.ensure([],()=>resolve(require(IMPORT_SOURCE)),CHUNK_NAME)}'
)
function getRequireEnsure (name, source, chunkName) {
return buildRequireEnsure({
IMPORT_NAME: t.identifier(name),
IMPORT_SOURCE: t.stringLiteral(source),
CHUNK_NAME: t.stringLiteral(chunkName)
})
}
module.exports = function ({
types: t
}) {
return {
visitor: {
ImportDeclaration (path, state) {
const dynamicImport = state.opts.dynamicImports[path.node.source.value]
if (dynamicImport) {
path.insertBefore(
getRequireEnsure(
path.node.specifiers[0].local.name,
dynamicImport.source,
dynamicImport.chunkName
)
)
path.remove()
}
}
}
}
}
@@ -0,0 +1,198 @@
const path = require('path')
const t = require('@babel/types')
const babelTraverse = require('@babel/traverse').default
const {
parseComponents
} = require('./util')
function handleObjectExpression (declaration, path, state) {
if (state.options) { // name,inheritAttrs,props
Object.keys(state.options).forEach(name => {
const optionProperty = declaration.properties.filter(prop => {
return t.isObjectProperty(prop) &&
t.isIdentifier(prop.key) &&
prop.key.name === name
})[0]
if (optionProperty) {
if (name === 'props') {
if (t.isArrayExpression(optionProperty.value)) {
state.options[name] = JSON.stringify(optionProperty.value.elements.filter(element => t.isStringLiteral(
element)).map(({
value
}) => value))
} else if (t.isObjectExpression(optionProperty.value)) {
const props = []
optionProperty.value.properties.forEach(({
key
}) => {
if (t.isIdentifier(key)) {
props.push(key.name)
} else if (t.isStringLiteral(key)) {
props.push(key.value)
}
})
state.options[name] = JSON.stringify(props)
}
} else if (t.isStringLiteral(optionProperty.value)) {
state.options[name] = JSON.stringify(optionProperty.value.value)
} else {
state.options[name] = optionProperty.value.value
}
}
})
}
const componentsProperty = declaration.properties.filter(prop => {
return t.isObjectProperty(prop) &&
t.isIdentifier(prop.key) &&
prop.key.name === 'components'
})[0]
if (componentsProperty && t.isObjectExpression(componentsProperty.value)) {
handleComponentsObjectExpression(componentsProperty.value, path, state)
}
}
function handleComponentsObjectExpression (componentsObjExpr, path, state, prepend) {
const properties = componentsObjExpr.properties
.filter(prop => t.isObjectProperty(prop) && t.isIdentifier(prop.value))
const components = parseComponents(properties.map(prop => {
return {
name: prop.key.name || prop.key.value,
value: prop.value.name
}
}), path)
state.components = prepend ? components.concat(state.components) : components
}
function handleIdentifier ({
name
}, path, state) {
// 仅做有限查找
for (let i = path.container.length; i > 0; i--) {
const node = path.container[i - 1]
let declarations = []
if (t.isExpressionStatement(node)) {
declarations = [node]
} else if (t.isVariableDeclaration(node)) {
declarations = node.declarations
}
for (let i = declarations.length; i > 0; i--) {
let declaration = declarations[i - 1]
let identifier
if (t.isVariableDeclarator(declaration)) {
identifier = declaration.id
declaration = declaration.init
} else if (t.isExpressionStatement(declaration) && t.isAssignmentExpression(declaration.expression)) {
identifier = declaration.expression.left
declaration = declaration.expression.right
}
// __sfc_main.components = Object.assign({CustomButton}, __sfc_main.components);
if (t.isMemberExpression(identifier) && identifier.object.name === name && identifier.property.name === 'components' && t.isCallExpression(declaration) && declaration.arguments.length === 2 && t.isObjectExpression(declaration.arguments[0])) {
handleComponentsObjectExpression(declaration.arguments[0], path, state, true)
return
}
if (identifier.name === name) {
if (t.isCallExpression(declaration) &&
t.isMemberExpression(declaration.callee) &&
declaration.arguments.length === 1) {
declaration = declaration.arguments[0]
}
if (t.isObjectExpression(declaration)) {
handleObjectExpression(declaration, path, state)
}
return
}
}
}
}
module.exports = function (ast, state = {
type: 'Component',
components: [],
options: {}
}) {
try {
babelTraverse(ast, {
CallExpression (path) {
const callee = path.node.callee
const args = path.node.arguments
const objExpr = args[0]
if (
t.isIdentifier(callee) &&
callee.name === 'defineComponent' &&
args.length === 1 &&
t.isObjectExpression(objExpr)
) {
handleObjectExpression(objExpr, path, state)
}
},
AssignmentExpression (path) {
const leftExpression = path.node.left
const rightExpression = path.node.right
if ( // global['__wxVueOptions'] = {'van-button':VanButton}
t.isMemberExpression(leftExpression) &&
t.isObjectExpression(rightExpression) &&
leftExpression.object.name === 'global' &&
leftExpression.property.value === '__wxVueOptions'
) {
handleObjectExpression(rightExpression, path, state)
}
if ( // exports.default.components = Object.assign({'van-button': VanButton}, exports.default.components || {})
t.isMemberExpression(leftExpression) &&
t.isCallExpression(rightExpression) &&
leftExpression.property.name === 'components' &&
t.isMemberExpression(leftExpression.object) &&
leftExpression.object.object.name === 'exports' &&
leftExpression.object.property.name === 'default' &&
rightExpression.arguments.length === 2 &&
t.isObjectExpression(rightExpression.arguments[0])
) {
handleComponentsObjectExpression(rightExpression.arguments[0], path, state, true)
}
},
ExportDefaultDeclaration (path) {
const declaration = path.node.declaration
if (t.isObjectExpression(declaration)) { // export default {components:{}}
handleObjectExpression(declaration, path, state)
} else if (t.isIdentifier(declaration)) {
handleIdentifier(declaration, path, state)
} else if (t.isCallExpression(declaration) &&
t.isMemberExpression(declaration.callee) &&
declaration.arguments.length === 1) { // export default Vue.extend({components:{}})
if (declaration.callee.object.name === 'Vue' && declaration.callee.property.name ===
'extend') {
const argument = declaration.arguments[0]
if (t.isObjectExpression(argument)) {
handleObjectExpression(argument, path, state)
} else if (t.isIdentifier(argument)) {
handleIdentifier(argument, path, state)
}
}
} else if (t.isClassDeclaration(declaration) &&
declaration.decorators &&
declaration.decorators.length
) { // export default @Component({components:{}}) class MyComponent extend Vue
const componentDecorator = declaration.decorators[0]
if (t.isCallExpression(componentDecorator.expression)) {
const args = componentDecorator.expression.arguments
if (args && args.length && t.isObjectExpression(args[0])) {
handleObjectExpression(args[0], path, state)
}
}
}
}
})
} catch (e) {
if (state.filename) {
console.error('at ' + require('@dcloudio/uni-cli-shared').normalizePath(path.relative(process.env.UNI_INPUT_DIR, state.filename)) + ':1')
}
throw e
}
return {
ast,
state
}
}
+88
View File
@@ -0,0 +1,88 @@
const t = require('@babel/types')
const uniI18n = require('@dcloudio/uni-cli-i18n')
const hyphenateRE = /\B([A-Z])/g
function cached (fn) {
const cache = Object.create(null)
return function cachedFn (str) {
const hit = cache[str]
return hit || (cache[str] = fn(str))
}
}
const hyphenate = cached((str) => {
return str.replace(hyphenateRE, '-$1').toLowerCase()
})
function findImportDeclaration (identifierName, path) {
const binding = path.scope.getBinding(identifierName)
if (!binding) {
return
}
if (t.isImportDeclaration(binding.path.parent)) {
return binding.path.parentPath
}
}
function parseComponents (names, path) {
const components = []
const dynamicImportMap = new Map()
names.forEach(({
name,
value
}) => {
const importDeclaration = findImportDeclaration(value, path)
if (!importDeclaration) {
throw new Error(uniI18n.__('mpLoader.componentReferenceErrorOnlySupportImport', {
0: name
}))
}
let source = importDeclaration.node.source.value
if (process.UNI_LIBRARIES && process.UNI_LIBRARIES.includes(source)) {
const componentName = hyphenate(name)
source = source + '/lib/' + componentName + '/' + componentName
}
const dynamicImportArray = dynamicImportMap.get(importDeclaration) || []
dynamicImportArray.push({
name,
value,
source
})
dynamicImportMap.set(importDeclaration, dynamicImportArray)
})
const importDeclarations = dynamicImportMap.keys()
for (const importDeclaration of importDeclarations) {
const dynamicImportArray = dynamicImportMap.get(importDeclaration)
dynamicImportArray.forEach((dynamicImport) => {
components.push(dynamicImport)
})
importDeclaration.remove()
}
return components
}
function findBabelLoader (loaders) {
return loaders.find(loader => loader.path.indexOf('babel-loader') !== -1)
}
const babelPluginDynamicImport = require.resolve('./plugin-dynamic-import')
function addDynamicImport (babelLoader, resourcePath, dynamicImports) {
babelLoader.options = babelLoader.options || {}
if (!babelLoader.options.plugins) {
babelLoader.options.plugins = []
}
babelLoader.options.plugins.push([babelPluginDynamicImport, {
resourcePath,
dynamicImports
}])
}
module.exports = {
addDynamicImport,
findBabelLoader,
parseComponents
}
+159
View File
@@ -0,0 +1,159 @@
const fs = require('fs')
const path = require('path')
const loaderUtils = require('loader-utils')
const parser = require('@babel/parser')
const {
removeExt,
hyphenate,
normalizePath,
getComponentName,
jsPreprocessOptions
} = require('@dcloudio/uni-cli-shared')
const {
getBabelParserOptions
} = require('@dcloudio/uni-cli-shared/lib/platform')
const {
updateUsingComponents
} = require('@dcloudio/uni-cli-shared/lib/cache')
const preprocessor = require('@dcloudio/vue-cli-plugin-uni/packages/webpack-preprocess-loader/preprocess')
const {
resolve,
normalizeNodeModules
} = require('./shared')
const {
findBabelLoader,
addDynamicImport
} = require('./babel/util')
const traverse = require('./babel/global-component-traverse')
const babelPluginCreateApp = require.resolve('./babel/plugin-create-app')
const uniI18n = require('@dcloudio/uni-cli-i18n')
function addCreateApp (babelLoader) {
babelLoader.options = babelLoader.options || {}
if (!babelLoader.options.plugins) {
babelLoader.options.plugins = []
}
babelLoader.options.plugins.push([babelPluginCreateApp])
}
module.exports = function (content, map) {
this.cacheable && this.cacheable()
if (this.resourceQuery) {
const params = loaderUtils.parseQuery(this.resourceQuery)
if (params && params.page) {
params.page = decodeURIComponent(params.page)
// import Vue from 'vue'是为了触发 vendor 合并
let ext = '.vue'
// nvue 跨平台编译,理论上不需要这么麻烦,直接不指定后缀即可,但可能开发者有同名 js 文件,导致引用错误
if (process.env.UNI_USING_NVUE_COMPILER) {
const vuePagePath = path.resolve(process.env.UNI_INPUT_DIR, normalizePath(params.page) + '.vue')
if (!fs.existsSync(vuePagePath)) {
const nvuePagePath = path.resolve(process.env.UNI_INPUT_DIR, normalizePath(params.page) +
'.nvue')
if (fs.existsSync(nvuePagePath)) {
ext = '.nvue'
}
}
}
return this.callback(null,
`
import Vue from 'vue'
import Page from './${normalizePath(params.page)}${ext}'
createPage(Page)
`, map)
}
} else {
content = preprocessor.preprocess(content, jsPreprocessOptions.context, {
type: jsPreprocessOptions.type
})
if (process.env.UNI_USING_VUE3) {
if (content.indexOf('createSSRApp') !== -1) {
content = content + ';createApp().app.mount(\'#app\');'
}
}
const resourcePath = 'app'
const {
state: {
components
}
} = traverse(parser.parse(content, getBabelParserOptions()), {
filename: this.resourcePath,
components: []
})
let babelLoader = findBabelLoader(this.loaders)
if (!babelLoader) {
throw new Error(uniI18n.__('mpLoader.findFail', {
0: 'babel-loader'
}))
} else {
const webpack = require('webpack')
if (webpack.version[0] > 4) {
// clone babelLoader and options
const index = this.loaders.indexOf(babelLoader)
const newBabelLoader = Object.assign({}, babelLoader)
Object.assign(newBabelLoader, { options: Object.assign({}, babelLoader.options) })
this.loaders.splice(index, 1, newBabelLoader)
babelLoader = newBabelLoader
}
addCreateApp(babelLoader)
}
if (!components.length) {
// 防止组件从有到无
updateUsingComponents(resourcePath, Object.create(null), 'App')
return this.callback(null, content, map)
}
const callback = this.async()
const dynamicImports = Object.create(null)
Promise.all(components.map(component => {
return resolve.call(this, component.source).then(resolved => {
component.name = getComponentName(hyphenate(component.name))
const source = component.source
component.source = normalizeNodeModules(removeExt(path.relative(process.env.UNI_INPUT_DIR,
resolved)))
// 非页面组件才需要 dynamic import
if (!process.UNI_ENTRY[component.source]) {
dynamicImports[source] = {
identifier: component.value,
chunkName: component.source,
source
}
}
})
})).then(() => {
const usingComponents = Object.create(null)
components.forEach(({
name,
source
}) => {
usingComponents[name] = `/${source}`
})
addDynamicImport(babelLoader, resourcePath, dynamicImports)
updateUsingComponents(resourcePath, usingComponents, 'App')
callback(null, content, map)
}, err => {
callback(err, content, map)
})
}
}
+94
View File
@@ -0,0 +1,94 @@
const path = require('path')
const babel = require('@babel/core')
const loaderUtils = require('loader-utils')
const {
hashify,
hasModule,
removeExt,
normalizePath
} = require('@dcloudio/uni-cli-shared')
const {
resolve,
getPlatformExts,
cacheGlobalComponents,
normalizeNodeModules
} = require('./shared')
const babelPluginGlobalComponent = require('./babel-plugin-global-component')
const templateExt = getPlatformExts().template
function getNormalMainJsCode (params) {
return `import App from './${normalizePath(params.page)}.vue'
import Vue from 'vue'
App.mpType='page'
const app = new Vue(App)
app.$mount()`
}
function getMPVuePageFactoryMainJsCode (params) {
return `import pageFactory from 'mpvue-page-factory'
import App from './${normalizePath(params.page)}.vue'
Page(pageFactory(App))`
}
module.exports = function (content, map) {
if (process.env.UNI_USING_COMPONENTS) {
return require('./main-new').call(this, content, map)
}
this.cacheable && this.cacheable()
if (this.resourceQuery) {
const params = loaderUtils.parseQuery(this.resourceQuery)
if (params && params.page) {
params.page = decodeURIComponent(params.page)
return (process.env.UNI_PLATFORM === 'mp-weixin' || process.env.UNI_PLATFORM === 'app-plus')
? getMPVuePageFactoryMainJsCode(params) : getNormalMainJsCode(params)
}
} else {
// 解析全局组件
const plugins = []
if (hasModule('@babel/plugin-syntax-typescript')) {
plugins.push('@babel/plugin-syntax-typescript')
plugins.push([
'@babel/plugin-proposal-decorators',
{
legacy: true
}
])
}
plugins.push(babelPluginGlobalComponent)
const ast = babel.transform(content, {
root: process.env.UNI_CLI_CONTEXT,
plugins
})
const globalComponents = {}
const callback = this.async()
if (!ast.metadata.globalComponents) {
ast.metadata.globalComponents = {}
}
Promise.all(Object.keys(ast.metadata.globalComponents).map(name => {
return resolve.call(this, ast.metadata.globalComponents[name]).then(resolved => {
resolved = path.relative(process.env.UNI_INPUT_DIR, resolved)
const hashed = hashify(resolved)
globalComponents[name] = {
name: hashed,
src: '/' + normalizeNodeModules(removeExt(resolved)) + '.vue' +
templateExt
}
})
})).then(() => {
cacheGlobalComponents(globalComponents)
callback(null, content)
}, err => {
callback(err, content)
})
}
}
@@ -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
}
+157
View File
@@ -0,0 +1,157 @@
const path = require('path')
const parser = require('@babel/parser')
const {
removeExt,
hyphenate,
normalizePath,
getComponentName,
jsPreprocessOptions
} = require('@dcloudio/uni-cli-shared')
const {
getBabelParserOptions
} = require('@dcloudio/uni-cli-shared/lib/platform')
const {
isBuiltInComponentPath
} = require('@dcloudio/uni-cli-shared/lib/pages')
const {
updateUsingComponents
} = require('@dcloudio/uni-cli-shared/lib/cache')
const preprocessor = require('@dcloudio/vue-cli-plugin-uni/packages/webpack-preprocess-loader/preprocess')
const traverse = require('./babel/scoped-component-traverse')
const {
resolve,
normalizeNodeModules,
getIssuer
} = require('./shared')
const {
findBabelLoader,
addDynamicImport
} = require('./babel/util')
const uniI18n = require('@dcloudio/uni-cli-i18n')
module.exports = function (content, map) {
this.cacheable && this.cacheable()
content = preprocessor.preprocess(content, jsPreprocessOptions.context, {
type: jsPreprocessOptions.type
})
let resourcePath = normalizeNodeModules(removeExt(normalizePath(path.relative(process.env.UNI_INPUT_DIR, this
.resourcePath))))
let type = ''
if (resourcePath === 'App') {
type = 'App'
} else if (process.UNI_ENTRY[resourcePath]) {
type = 'Page'
}
// <script src=""/>
if (!type) {
const moduleIssuer = getIssuer(this._compilation, this._module)
if (moduleIssuer) {
const moduleIssuerIssuer = getIssuer(this._compilation, moduleIssuer)
if (moduleIssuerIssuer) {
resourcePath = normalizeNodeModules(removeExt(normalizePath(path.relative(process.env.UNI_INPUT_DIR, moduleIssuerIssuer.resource))))
if (resourcePath === 'App') {
type = 'App'
} else if (process.UNI_ENTRY[resourcePath]) {
type = 'Page'
}
}
}
}
if ( // windows 上 page-meta, navigation-bar 可能在不同盘上
/^win/.test(process.platform) &&
path.isAbsolute(resourcePath) &&
isBuiltInComponentPath(resourcePath)
) {
resourcePath = normalizePath(path.relative(process.env.UNI_CLI_CONTEXT, resourcePath))
}
if (!type) {
type = 'Component'
}
const {
state: {
components
}
} = traverse(parser.parse(content, getBabelParserOptions()), {
type,
components: [],
filename: this.resourcePath
})
const callback = this.async()
if (!components.length) {
if (type === 'App') {
callback(null, content, map)
return
}
// 防止组件从有到无,App.vue 中不支持使用组件
updateUsingComponents(resourcePath, Object.create(null), type, content)
callback(null, content, map)
return
}
const dynamicImports = Object.create(null)
Promise.all(components.map(component => {
return resolve.call(this, component.source).then(resolved => {
component.name = getComponentName(hyphenate(component.name))
const source = component.source
component.source = normalizeNodeModules(removeExt(path.relative(process.env.UNI_INPUT_DIR,
resolved)))
// 非页面组件才需要 dynamic import
if (!process.UNI_ENTRY[component.source]) {
dynamicImports[source] = {
identifier: component.value,
chunkName: component.source,
source: source
}
}
})
})).then(() => {
const usingComponents = Object.create(null)
components.forEach(({
name,
source
}) => {
usingComponents[name] = `/${source}`
})
let babelLoader = findBabelLoader(this.loaders)
if (!babelLoader) {
callback(new Error(uniI18n.__('mpLoader.findFail', {
0: 'babel-loader'
})), content)
} else {
const webpack = require('webpack')
if (webpack.version[0] > 4) {
// clone babelLoader and options
const index = this.loaders.indexOf(babelLoader)
const newBabelLoader = Object.assign({}, babelLoader)
Object.assign(newBabelLoader, { options: Object.assign({}, babelLoader.options) })
this.loaders.splice(index, 1, newBabelLoader)
babelLoader = newBabelLoader
}
addDynamicImport(babelLoader, resourcePath, dynamicImports)
updateUsingComponents(resourcePath, usingComponents, type, content)
callback(null, content, map)
}
}, err => {
callback(err, content, map)
})
}
+69
View File
@@ -0,0 +1,69 @@
const path = require('path')
const babel = require('@babel/core')
const {
hashify,
removeExt,
hasModule
} = require('@dcloudio/uni-cli-shared')
const {
resolve,
cacheCompilerOptions
} = require('./shared')
const babelPluginScopedComponent = require('./babel-plugin-scoped-component')
module.exports = function (content, map) {
if (process.env.UNI_USING_COMPONENTS) {
if (process.env.UNI_PLATFORM === 'app-plus') {
return require('./script-new').call(this, content, map)
}
return require('./script-new').call(this, content, map)
}
this.cacheable && this.cacheable()
// 单页面 解析 component 依赖
const plugins = []
if (hasModule('@babel/plugin-syntax-typescript')) {
plugins.push('@babel/plugin-syntax-typescript')
plugins.push([
'@babel/plugin-proposal-decorators',
{
legacy: true
}
])
}
plugins.push(babelPluginScopedComponent)
const ast = babel.transform(content, {
configFile: false,
plugins
})
const components = ast.metadata.components || {}
const imports = {}
const callback = this.async()
Promise.all(Object.keys(components).map(name => {
return resolve.call(this, components[name]).then(resolved => {
resolved = path.relative(process.env.UNI_INPUT_DIR, resolved)
const hashed = hashify(resolved)
imports[name] = {
name: hashed,
src: removeExt(resolved)
}
})
})).then(() => {
const realResourcePath = path.relative(process.env.UNI_INPUT_DIR, this.resourcePath)
const compilerOptions = {
name: hashify(realResourcePath),
imports
}
cacheCompilerOptions(realResourcePath, compilerOptions)
callback(null, content, map)
}, err => {
callback(err, content, map)
})
}
+119
View File
@@ -0,0 +1,119 @@
const webpack = require('webpack')
const {
removeExt,
getPlatformExts,
getPlatformTarget,
createSource,
deleteAsset
} = require('@dcloudio/uni-cli-shared')
const {
normalizeNodeModules
} = require('@dcloudio/uni-cli-shared/lib/platform')
const templates = {}
const compilerOptions = {}
const compiledComponentTemplates = {}
let globalComponents = {}
const components = new Set()
const usingComponents = {}
function resolve (source) {
return new Promise((resolve, reject) => {
this.resolve(this.context, source, (err, filepath) => {
if (err) {
reject(err)
return
}
resolve(filepath)
})
})
}
function restoreNodeModules (str) {
if (process.env.UNI_PLATFORM === 'mp-alipay') {
str = str.replace('node-modules/npm-scope-', 'node-modules/@')
}
str = str.replace('node-modules', 'node_modules')
return str
}
function getIssuer (compilation, module) {
return webpack.version[0] > 4 ? compilation.moduleGraph.getIssuer(module) : module.issuer
}
function getModuleId (compilation, module) {
return webpack.version[0] > '4' ? compilation.chunkGraph.getModuleId(module) : module.id
}
module.exports = {
resolve,
restoreNodeModules,
normalizeNodeModules,
getComponents () {
return components
},
getUsingComponents () {
return usingComponents
},
cacheUsingComponents (name, scopedComponents) {
usingComponents[name] = scopedComponents // 方便写入 json usingComponents
scopedComponents.forEach(scopedComponent => {
components.add(scopedComponent.source + '.js')
})
},
cacheGlobalComponents (newGlobalComponents) {
globalComponents = newGlobalComponents
},
cacheTemplate (name, content) {
templates[removeExt(name)] = content
},
cacheCompilerOptions (name, options = {}) {
name = removeExt(name)
compilerOptions[name] = Object.assign(compilerOptions[name] || {}, options)
},
cacheCompiledComponentTemplates (name, options) {
compiledComponentTemplates[name] = options
},
getCompiledComponentTemplate (name) {
return compiledComponentTemplates[name] || {}
},
getTemplates () {
return templates
},
getCompilerOptions () {
return compilerOptions
},
getGlobalComponents () {
return globalComponents
},
getPages () {
const pages = {}
Object.keys(process.UNI_ENTRY).forEach(page => {
if (compilerOptions[page]) {
pages[page] = compilerOptions[page].name
}
})
return pages
},
getSubPages () {
return process.UNI_SUB_PACKAGES_ROOT
},
getRoot (filePath, subPackages) {
const subPackage = subPackages.find(subPackage => filePath.indexOf(subPackage + '/') === 0)
if (subPackage) {
return subPackage + '/'
}
return ''
},
getPlatformExts,
getPlatformTarget,
createSource,
deleteAsset,
getIssuer,
getModuleId
}
+72
View File
@@ -0,0 +1,72 @@
const fs = require('fs')
const path = require('path')
const {
removeExt,
normalizePath,
getFlexDirection,
parseManifestJson
} = require('@dcloudio/uni-cli-shared')
const {
normalizeNodeModules
} = require('./shared')
module.exports = function (content, map) {
this.cacheable && this.cacheable()
if (!process.env.UNI_USING_NVUE_COMPILER) {
return this.callback(null, content, map)
}
if (path.extname(this.resourcePath) !== '.nvue') {
return this.callback(null, content, map)
}
const resourcePath = normalizeNodeModules(
removeExt(
normalizePath(path.relative(process.env.UNI_INPUT_DIR, this.resourcePath))
)
)
if (!process.UNI_ENTRY[resourcePath]) {
return this.callback(null, content, map)
}
const manifestJsonPath = path.resolve(process.env.UNI_INPUT_DIR, 'manifest.json')
const manifestJson = parseManifestJson(fs.readFileSync(manifestJsonPath, 'utf8'))
this.callback(null,
`<style>
view,
label,
swiper-item,
scroll-view {
display:flex;
flex-direction:${getFlexDirection(manifestJson['app-plus'])};
flex-shrink: 0;
flex-grow: 0;
flex-basis: auto;
align-items: stretch;
align-content: flex-start;
}
view,
image,
input,
scroll-view,
swiper,
swiper-item,
text,
textarea,
video {
position: relative;
border: 0px solid #000000;
box-sizing: border-box;
}
swiper-item {
position: absolute;
}
button {
margin: 0;
}
</style>
${content}`,
map)
}
+69
View File
@@ -0,0 +1,69 @@
const path = require('path')
const qs = require('querystring')
const {
md5,
removeExt,
getPlatformExts
} = require('@dcloudio/uni-cli-shared')
const {
cacheTemplate,
cacheCompilerOptions,
getPlatformTarget
} = require('./shared')
const templateExt = getPlatformExts().template
module.exports = function (content) {
if (process.env.UNI_USING_COMPONENTS) {
return require('./template-new').call(this, content)
}
this.cacheable && this.cacheable()
const realResourcePath = path.relative(process.env.UNI_INPUT_DIR, this.resourcePath)
if (process.env.UNI_USING_COMPONENTS) {
// 向 uni-template-compier 传递 emitFile
const vueLoaderOptions = this.loaders.find(loader => loader.ident === 'vue-loader-options')
if (vueLoaderOptions) {
Object.assign(vueLoaderOptions.options.compilerOptions, {
resourcePath: removeExt(realResourcePath) + templateExt,
emitFile: this.emitFile
})
} else {
throw new Error('vue-loader-options parse error')
}
} else {
if (!content.trim()) {
content = '<view></view>'
}
cacheTemplate(realResourcePath, content)
const query = qs.parse(this.resourceQuery.slice(1))
const {
id
} = query
const compilerOptions = {
scopeId: query.scoped ? `data-v-${id}` : null,
target: getPlatformTarget(),
md5: md5(content.trim() + process.env.UNI_PLATFORM),
realResourcePath
}
cacheCompilerOptions(realResourcePath, compilerOptions)
// 向 vue-loader templateLoader 传递 compilerOptions
const vueLoaderOptions = this.loaders.find(loader => loader.ident === 'vue-loader-options')
if (vueLoaderOptions) {
Object.assign(vueLoaderOptions.options.compilerOptions, compilerOptions)
} else {
throw new Error('vue-loader-options parse error')
}
}
return content
}
+90
View File
@@ -0,0 +1,90 @@
const path = require('path')
const loaderUtils = require('loader-utils')
const {
removeExt,
normalizePath,
getPlatformExts,
getShadowTemplate
} = require('@dcloudio/uni-cli-shared')
const {
getJsonFile,
getWXComponents,
updateSpecialMethods,
getGlobalUsingComponents,
updateGenericComponents, // resolve
updateComponentGenerics, // define
updateUsingGlobalComponents
} = require('@dcloudio/uni-cli-shared/lib/cache')
const {
isBuiltInComponentPath
} = require('@dcloudio/uni-cli-shared/lib/pages')
const {
getPlatformFilterTag
} = require('@dcloudio/uni-cli-shared/lib/platform')
const {
normalizeNodeModules
} = require('./shared')
const templateExt = getPlatformExts().template
const filterTagName = getPlatformFilterTag() || ''
function parseFilterModules (filterModules) {
if (filterModules) {
return JSON.parse(Buffer.from(filterModules, 'base64').toString('utf8'))
}
return {}
}
module.exports = function (content, map) {
this.cacheable && this.cacheable()
const vueLoaderOptions = this.loaders.find(loader => loader.ident === 'vue-loader-options')
if (vueLoaderOptions) {
const globalUsingComponents = getGlobalUsingComponents()
const realResourcePath = path.relative(process.env.UNI_INPUT_DIR, this.resourcePath)
let resourcePath = normalizeNodeModules(removeExt(realResourcePath) + templateExt)
if ( // windows 上 page-meta, navigation-bar 可能在不同盘上
/^win/.test(process.platform) &&
path.isAbsolute(resourcePath) &&
isBuiltInComponentPath(resourcePath)
) {
resourcePath = normalizePath(path.relative(process.env.UNI_CLI_CONTEXT, resourcePath))
}
const wxComponents = getWXComponents(resourcePath.replace(path.extname(resourcePath), ''))
const params = loaderUtils.parseQuery(this.resourceQuery)
/* eslint-disable no-mixed-operators */
const filterModules = parseFilterModules(params && params['filter-modules'])
Object.assign(vueLoaderOptions.options.compilerOptions, {
mp: {
platform: process.env.UNI_PLATFORM,
scopedSlotsCompiler: process.env.SCOPED_SLOTS_COMPILER,
slotMultipleInstance: process.env.SLOT_MULTIPLE_INSTANCE === 'true',
mergeVirtualHostAttributes: process.env.MERGE_VIRTUAL_HOST_ATTRIBUTES === 'true'
},
filterModules,
filterTagName,
resourcePath,
emitFile: this.emitFile,
wxComponents,
getJsonFile,
getShadowTemplate,
updateSpecialMethods,
globalUsingComponents,
updateGenericComponents,
updateComponentGenerics,
updateUsingGlobalComponents
})
} else {
throw new Error('vue-loader-options parse error')
}
this.callback(null, content, map)
}