@antmove/wx-alipay
Version:
transform wechat miniprogram to alipay miniprogram tool.
679 lines (653 loc) • 21.5 kB
JavaScript
const path = require('path')
const chalk = require('chalk')
const fs = require('fs-extra')
const {
prettierCode,
isTypeFile,
record,
reportMethods,
runJs,
cjsToes,
emptyFiles,
setAppName,
setCompileType,
setAppFromId,
reportError,
getAppName,
recordOptions,
transformEnvConfig,
processErrMassage,
} = require('@antmove/utils')
const transformNpmComponents = require('../useCore/npmComponentsTransform')
const { monitorFiles } = require('../useCore/watcher')
const wxmlParser = require('../parse/parse.js')
// const upDataTool = require("../utils/updataTool");
const appJsonProcess = require('../component/appJson')
const pageJsonProcess = require('../component/pageJson')
const checkCoverView = require('../utils/checkCoverView') // cover-view 检测
const getPackageJson = require('../utils/getpackageData')
const saveComponentJs = require('../utils/saveComponentJs')
const generateBundleComponent = require('../generate/generateWrapComponents')
const generateMiniProjectJson = require('../generate/generateMiniProjectJson')
const generateAntmoveReadme = require('../generate/generateAntmoveReadme')
// const generateWxsDeps = require('../generate/generateWxsDep')
// 制作日志
const recordConfig = require('../utils/record/config')
const isWechatApp = require('../utils/isWechatApp')
const { processAppJson } = require('../generate/generateRuntimeLogPage')
const Config = require('../config.js')
const compileJs = require('./compile/compileJs')
const compileWxss = require('./compile/compileWxss')
const compileWxml = require('./compile/compileWxml')
const project = {
name: '',
path: '',
distPath: '',
fileNum: 0,
pageNum: 0,
componentNum: 0,
usetime: '',
}
const { report, reportTable, reportSpeed, reportDist } = reportMethods
// 默认报告不显示具体文件
const showCompile = true
// 默认查看报告
let showReport = false
const statFileNameArr = []
let readtimes = 0
let finishFile = 0
let projectParents = ''
const beginTime = Number(new Date())
// 输出日志数据
let repData = {}
// let isUpdata = true;
// let baseurl = 'http://cache.amap.com/ecology/tool/antmove/wechat-alipay/';
const exportObj = {
defaultOptions: {
exclude: [
'project.config.json',
'node_modules',
'antmove.config.js',
'miniprogram_npm',
],
env: 'production',
remote: false,
},
beforeParse(next) {
process.on('warning', () => {
if (process.env.transFile) {
console.log()
console.log(
chalk.yellow(
`(${process.env.transFile}) 转换失败,请通过Github: https://github.com/ant-move/Antmove 或加钉群(21977588)联系我们`,
),
)
}
})
const ver = fs.readJSONSync(path.join(__dirname, '../package.json')).version
setCompileType(`wx-alipay@${ver}`)
setAppFromId(this.$options.fromId)
let ifComponent = false
if (this.$options.component === 'component') {
ifComponent = true
}
try {
if (!isWechatApp(this.$options.entry, ifComponent)) {
const errStr = `[Ops] ${this.$options.entry} 不是一个微信小程序目录, 请检查目录或请通过Github: https://github.com/ant-move/Antmove 加钉群(21977588)联系我们.`
if (this.$options.error) {
next(errStr)
throw new Error(errStr)
} else {
console.log(chalk.red(errStr))
}
return false
}
} catch (err) {
console.log(err)
return false
}
this.$options.empty
&& fs.existsSync(this.$options.dist)
&& emptyFiles(this.$options.dist, [
'miniprogram_npm',
'node_modules',
'.tea',
'mini.project.json',
])
if (this.$options.scope && /true/.test(this.$options.scope)) {
Config.options.scopeStyle = true
}
Config.env
= process.env.NODE_ENV === 'development' ? 'development' : 'production'
showReport = Config.env === 'development'
Config.component2
= typeof this.$options.component2 === 'boolean'
? this.$options.component2
: true
Config.useRuntimeLog
= typeof this.$options.useRuntimeLog === 'boolean'
? this.$options.useRuntimeLog
: false
Config.aliAppType = this.$options.platform || 'alipay'
if (this.$options.component === 'component') {
Config.min = true
}
if (!this.$options.isNpmComponent) {
// isUpdata = this.$options.remote; // 是否从远程拉取 polyfill 代码
const date = ''
report(date, { type: 'title', showReport })
const { getSurrounding, getToolVs, resDataInit } = record(recordConfig)
repData = resDataInit()
repData.surroundings = getSurrounding()
const versionData = {}
versionData.version = this.$options.version
repData.toolVs = getToolVs(versionData)
const pathArr = this.$options.entry.split(path.sep)
projectParents = pathArr[pathArr.length - 3]
}
// const toolPath = path.join(__dirname, '../package.json');
// const toolVsData = JSON.parse(fs.readFileSync(toolPath)).version;
// baseurl = baseurl + toolVsData;
// try {
// await upDataTool({ baseurl, isUpdata, showReport });
// } catch (err) {}
next()
},
onParsing(fileInfo) {
if (fileInfo.basename.includes('@')) {
console.log(
'支付宝小程序文件或文件夹名中不允许出现 @ 符号,真机调试和预览会有构建失败的风险',
fileInfo.path,
)
}
fileInfo.output = this.$options.dist
fileInfo.entry = this.$options.entry
if (fileInfo.type === 'file') {
project.fileNum++
if (fileInfo.filename === 'app.json') {
project.path = fileInfo.dirname
const distPath = fileInfo.dist.split('app.json')[0]
project.distPath = path.join(distPath.substr(0, distPath.length - 1))
report('', {
type: 'project',
path: project.path,
showReport,
showCompile,
})
}
}
if (isTypeFile('.wxml', fileInfo.path)) {
fileInfo.ast = wxmlParser.parseFile(fileInfo.path)
}
},
onParsed() {
const { packageData, antmovePackageData } = getPackageJson()
if (!this.$options.isWx2Baidu) {
try {
reportDist(`${antmovePackageData.version}`, this.$options.dist, {
tool: '@antmove/wx-alipay',
version: packageData.version,
})
} catch (err) {
return false
}
} else {
console.log('\n ')
}
},
beforeCompile() {
/**
*
*/
},
onCompiling(fileInfo, ctx) {
ctx.$options.output = ctx.$options.output || ctx.$options.dist
const {
getTemplateData,
getStyleData,
getCustomScript,
getScriptData,
getJsonData,
getOthersFile,
} = record(recordConfig)
if (fileInfo.type !== 'file') {
fs.ensureDirSync(fileInfo.dist)
return false
}
process.env.transFile = fileInfo.path
let date = new Date()
const reportData = {
info: fileInfo.dirname,
type: 'parse',
showReport,
length: project.fileNum,
nums: finishFile,
}
let isComponentPage = false
if (!fileInfo.parent) {
readtimes = 0
const pathArr = fileInfo.path.split(path.sep)
projectParents = pathArr[pathArr.length - 3] || ''
reportData.info = fileInfo.path.split(projectParents)[1].substr(1)
report(date, reportData)
} else if (statFileNameArr.indexOf(fileInfo.dirname) === -1) {
readtimes = 0
reportData.info = fileInfo.dirname.split(projectParents)[1].substr(1)
report(date, reportData)
statFileNameArr.push(fileInfo.dirname)
}
readtimes++
if (ctx.$options && ctx.$options.componentPages) {
Object.keys(ctx.$options.componentPages).forEach((p) => {
if (
path.join(fileInfo.entry, p)
=== fileInfo.path.replace(fileInfo.extname, '')
) {
fileInfo.dist = fileInfo.dist.replace(
p,
ctx.$options.componentPages[p].path,
)
isComponentPage = {
originPath: p,
...ctx.$options.componentPages[p],
}
}
})
}
if (isTypeFile('.wxml', fileInfo.path)) {
compileWxss(fileInfo, this.$options, true, isComponentPage)
const projectname = fileInfo.entry
const reptempData = getTemplateData(fileInfo, projectname)
checkCoverView(fileInfo.ast, reptempData)
compileWxml(fileInfo, ctx)
const _reportData = {
info: fileInfo.path.split(projectParents)[1].substr(1),
type: 'compile',
showCompile,
showReport,
length: project.fileNum,
nums: finishFile,
}
date = report(date, _reportData)
repData.transforms = Object.assign(repData.transforms, reptempData)
} else if (isTypeFile('.wxss', fileInfo.path)) {
compileWxss(fileInfo, this.$options)
const reptempData = getStyleData(
fileInfo.path.split(projectParents)[1].substr(1),
)
const _reportData = {
info: fileInfo.path.split(projectParents)[1].substr(1),
type: 'compile',
showCompile,
showReport,
length: project.fileNum,
nums: finishFile,
}
date = report(date, _reportData)
repData.transforms = Object.assign(repData.transforms, reptempData)
} else if (isTypeFile('.js', fileInfo.path)) {
const pathinfo = fileInfo.path.split(projectParents)[1].substr(1)
const originCode = fs.readFileSync(fileInfo.path, 'utf8')
const wxoriginCode = originCode
const apis = {}
if (this.$options.isWx2Baidu) {
saveComponentJs(fileInfo, originCode, this.$options)
}
try {
compileJs(fileInfo, ctx, originCode, apis)
} catch (error) {
processErrMassage(error, fileInfo.path.replace(fileInfo.entry, ''))
}
const _reportData = {
info: pathinfo,
type: 'compile',
showCompile,
showReport,
length: project.fileNum,
nums: finishFile,
}
date = report(date, _reportData)
const reptempData = getScriptData(pathinfo, apis, wxoriginCode)
repData.transforms = Object.assign(repData.transforms, reptempData)
} else if (isTypeFile('.wxs', fileInfo.path)) {
const pathinfo = fileInfo.path.split(projectParents)[1].substr(1)
const reptempData = getCustomScript(pathinfo)
repData.transforms = Object.assign(repData.transforms, reptempData)
let content = fs.readFileSync(fileInfo.path, 'utf8') || ''
const _reportData = {
info: pathinfo,
type: 'compile',
showCompile,
showReport,
length: project.fileNum,
nums: finishFile,
}
date = report(date, _reportData)
/**
* 不支持 sjs 兼容处理
*/
if (!Config.hasWxs) {
content = content.replace(/\.wxs/g, '.wxs.js')
if (content.match(/\s*getRegExp/g)) {
const preCode = `
function getRegExp (p1, p2) {
return new RegExp(p1, p2);
}
\n
`
content = preCode + content
}
fs.outputFileSync(fileInfo.dist.replace(/\.wxs$/, '.wxs.js'), content)
} else {
content = cjsToes(content)
content = content.replace(/\.wxs/g, '.sjs')
fs.outputFileSync(fileInfo.dist.replace(/\.wxs$/, '.sjs'), content)
}
} else {
let content
if (fileInfo.deep === 0 && fileInfo.filename === 'app.json') {
content = fs.readFileSync(fileInfo.path, 'utf8')
if (
this.$options.hooks
&& typeof this.$options.hooks.appJson === 'function'
) {
content = this.$options.hooks.appJson(content)
}
content = transformEnvConfig('alipay', content)
const appData = JSON.parse(content)
const json = appData
if (json.window && json.window.navigationBarTitleText) {
setAppName(json.window.navigationBarTitleText)
} else {
const appName = getAppName(
json.pages,
fileInfo.entry,
'navigationBarTitleText',
)
setAppName(appName)
}
try {
project.pageNum = appData.pages.length
const subpages = appData.subPackages || appData.subpackages
subpages && Array.isArray(subpages) && subpages.forEach((subp) => {
project.pageNum += subp.pages.length
})
} catch (err) {
project.pageNum = 0
}
const pathInfo = fileInfo.path.split(projectParents)[1].substr(1)
const jsonData = getJsonData(pathInfo, content)
repData.transforms = Object.assign(repData.transforms, jsonData)
if (Config.useRuntimeLog) {
content = processAppJson(content)
}
const app = JSON.parse(content)
this.$options.appPages = app.pages || []
let dirnameArr = fileInfo.dirname.split('/')
if (dirnameArr.length <= 1) {
dirnameArr = dirnameArr[0].split('\\')
}
try {
project.name
= app.window.navigationBarTitleText
|| dirnameArr[dirnameArr.length - 1]
} catch (err) {
project.name = dirnameArr[dirnameArr.length - 1]
}
content = appJsonProcess(content, this.$options)
content = prettierCode(content, 'json', {
useTabs: true,
tabWidth: 4,
})
const _reportData = {
info: pathInfo,
type: 'compile',
showCompile,
showReport,
length: project.fileNum,
nums: finishFile,
}
date = report(date, _reportData)
} else if (fileInfo.deep > 0 && fileInfo.extname === '.json') {
const pathInfo = fileInfo.path.split(projectParents)[1].substr(1)
const parent = fileInfo.parent
let bool = false
let wxmlFileInfo = null
parent
&& parent.children
&& parent.children.forEach((el) => {
if (`${fileInfo.basename}.wxml` === el.filename) {
bool = true
wxmlFileInfo = el
}
})
content = fs.readFileSync(fileInfo.path, 'utf8')
content = transformEnvConfig('alipay', content)
if (bool) {
content = pageJsonProcess.call(ctx, content, wxmlFileInfo)
}
const jsonData = getJsonData(pathInfo, content)
repData.transforms = Object.assign(repData.transforms, jsonData)
content = prettierCode(content, 'json', {
useTabs: true,
tabWidth: 4,
})
const pageJson = JSON.parse(content)
if (pageJson.component) {
project.componentNum++
}
const _reportData = {
info: pathInfo,
type: 'compile',
showCompile,
showReport,
length: project.fileNum,
nums: finishFile,
}
date = report(date, _reportData)
} else {
content = fs.readFileSync(fileInfo.path)
if (fileInfo.deep === 0 && fileInfo.filename === 'package.json') {
const { transformPackage } = require('@antmove/utils')
content = transformPackage(fileInfo, ctx)
content = prettierCode(content, 'json', {
useTabs: true,
tabWidth: 4,
})
}
const _reportData = {
info: fileInfo.path.split(projectParents)[1].substr(1),
type: 'compile',
showCompile,
showReport,
length: project.fileNum,
nums: finishFile,
}
date = report(date, _reportData)
const otherData = getOthersFile(
fileInfo.path.split(projectParents)[1].substr(1),
)
repData.transforms = Object.assign(repData.transforms, otherData)
}
fs.outputFileSync(fileInfo.dist, content)
}
// 记录当前处理完成的文件数目
finishFile++
const generateData = {
info: fileInfo.path.split(projectParents)[1].substr(1),
type: 'generate',
showReport,
length: project.fileNum,
nums: finishFile,
}
if (!fileInfo.parent) {
report(date, generateData)
} else if (readtimes === fileInfo.parent.children.length) {
generateData.info = fileInfo.dirname.split(projectParents)[1].substr(1)
report(date, generateData)
}
// console.log(new Date() - date)
if (
new Date() - date > 30
|| finishFile % 3 === 0
|| finishFile === project.fileNum
) {
reportSpeed({
showReport,
length: project.fileNum,
nums: finishFile,
})
}
return fileInfo
},
async compiled(ctx, cb = () => {}) {
ctx.$options = ctx.$options || {}
const _options = ctx.$options
if (typeof _options.useCompileLog === 'undefined') {
if (_options.env === 'development') {
ctx.$options.useCompileLog = true
} else {
ctx.$options.useCompileLog = false
}
} else {
ctx.$options.useCompileLog = _options.useCompileLog
}
if (typeof _options.autoOpenReport === 'undefined') {
if (_options.env === 'development') {
ctx.$options.autoOpenReport = true
} else {
ctx.$options.autoOpenReport = false
}
} else {
ctx.$options.autoOpenReport = _options.autoOpenReport
}
let isReport = this.$options.isReport
isReport = typeof isReport === 'boolean' ? isReport : true
reportError(null, null, 'log', null, isReport)
// 上报所有缺失属性 || 上报缺失的组件
for (const key in shortCompsInfo) {
if (shortCompsInfo.hasOwnProperty(key)) {
reportError(
null,
null,
'log',
13,
isReport,
true,
key,
shortCompsInfo[key].join(','),
)
}
}
// 上报缺失api
global.shortApiInfo.length
&& reportError(
null,
null,
'log',
13,
isReport,
true,
'api',
global.shortApiInfo.join(','),
)
const { findOpenAbility, statistics, writeReportPage } = record(
recordConfig,
)
if (!this.$plugin.isMonitorFiles) {
!this.$options.isNpmComponent && recordOptions(this.$options)
this.$options.watch && monitorFiles(this.$plugin)
}
const notTransformNpmComponents = Config.notTransformNpmComponents
if (!this.$options.isNpmComponent) {
if (
this.$options.ignoreNpm === false
&& !this.$options.isNpmComponent
&& notTransformNpmComponents
) {
const npmDirPath = path.join(this.$options.entry, 'miniprogram_npm')
const npmDir = fs.readdirSync(npmDirPath)
let npmComponentsExclude = []
npmComponentsExclude = npmDir.filter((n) => {
return !notTransformNpmComponents[n]
})
const transNpmOpts = {}
transNpmOpts.entry = npmDirPath
transNpmOpts.input = transNpmOpts.entry
transNpmOpts.dist = path.join(
this.$options.dist,
'__antmove_miniprogram_npm',
)
transNpmOpts.output = transNpmOpts.dist
transNpmOpts.exclude = npmComponentsExclude
transformNpmComponents(this.$plugin, transNpmOpts)
}
/**
* 如果是转换npm中组件,无需再引入__antmove
*/
if (Config.component2) {
generateMiniProjectJson(ctx.output, ctx.npm || [])
}
generateBundleComponent(ctx.output, Config)
generateAntmoveReadme(ctx.output)
await runGenerateBundleApi(ctx.output)
generateNodeTrees(ctx.output, Config)
if (ctx.$options.useCompileLog) {
const tableInfo = {
项目名称: project.name,
项目路径: project.path,
输出路径: project.distPath,
文件数: String(project.fileNum),
页面数: String(project.pageNum),
组件数: String(project.componentNum),
}
repData.tableInfo = tableInfo
const nowTime = report(beginTime, {
showReport,
type: 'computedTime',
})
tableInfo['总耗时'] = `${nowTime}ms`
reportTable({ tableInfo, showReport })
repData.opening = findOpenAbility(repData)
const statisticsData = statistics(repData.transforms)
repData.concept = statisticsData
const targetPath = path.join(
ctx.output,
`${Config.library.customComponentPrefix}/.config.json`,
)
writeReportPage(repData, targetPath, ctx.$options.autoOpenReport)
}
cb()
}
},
}
module.exports = exportObj
/**
* Run generateBundleApi in child_process
*/
function runGenerateBundleApi(output) {
const filename = path.join(__dirname, '../generate/generateBundleApi.js')
return new Promise((resolve, reject) => {
try {
runJs(
filename,
{
output,
Config,
},
(code) => {
resolve(code)
},
)
} catch (error) {
reject(error)
}
})
}
function generateNodeTrees(output, config) {
const str = `${global.appNodesTreeStr}}`
fs.outputFileSync(
path.join(output, config.library.customComponentPrefix, 'api/relations.js'),
str,
)
}