390 lines
11 KiB
JavaScript
390 lines
11 KiB
JavaScript
const fs = require('fs')
|
||
const path = require('path')
|
||
const crypto = require('crypto')
|
||
const {
|
||
isNormalPage
|
||
} = require('./util')
|
||
/**
|
||
* 1.page-loader 缓存基础的 app.json page.json project.config.json
|
||
* 2.main-loader 缓存 app.json 中的 usingComponents 节点
|
||
* 3.script-loader 修改缓存 usingComponents 节点
|
||
* 5.webpack plugin 中获取被修改的 page.json,component.json 并 emitFile
|
||
*/
|
||
let jsonFileMap = new Map()
|
||
const changedJsonFileSet = new Set()
|
||
let componentSet = new Set()
|
||
|
||
let pageSet = new Set()
|
||
|
||
let globalUsingComponents = Object.create(null)
|
||
let appJsonUsingComponents = Object.create(null)
|
||
let componentSpecialMethods = Object.create(null)
|
||
|
||
function getPagesJson () {
|
||
if (process.env.UNI_PLATFORM === 'h5') {
|
||
return process.UNI_H5_PAGES_JSON
|
||
}
|
||
const pagesJson = {
|
||
pages: {}
|
||
}
|
||
for (const name of pageSet.values()) {
|
||
const style = JSON.parse(getJsonFile(name) || '{}')
|
||
delete style.customUsingComponents
|
||
pagesJson.pages[name] = style
|
||
}
|
||
const appJson = JSON.parse(getJsonFile('app') || '{}')
|
||
pagesJson.globalStyle = appJson.window || {}
|
||
return pagesJson
|
||
}
|
||
|
||
function updateJsonFile (name, jsonStr) {
|
||
changedJsonFileSet.add(name)
|
||
if (typeof jsonStr !== 'string') {
|
||
jsonStr = JSON.stringify(jsonStr, null, 2)
|
||
}
|
||
jsonFileMap.set(name, jsonStr)
|
||
}
|
||
|
||
function getJsonFile (name) {
|
||
return jsonFileMap.get(name)
|
||
}
|
||
|
||
function getChangedJsonFileMap (clear = true) {
|
||
const changedJsonFileMap = new Map()
|
||
for (const name of changedJsonFileSet.values()) {
|
||
if (isNormalPage(name)) {
|
||
changedJsonFileMap.set(name + '.json', jsonFileMap.get(name))
|
||
}
|
||
}
|
||
clear && changedJsonFileSet.clear()
|
||
return changedJsonFileMap
|
||
}
|
||
|
||
function updateAppJson (name, jsonObj) {
|
||
updateComponentJson(name, jsonObj, true, 'App')
|
||
}
|
||
|
||
function updatePageJson (name, jsonObj) {
|
||
pageSet.add(name)
|
||
updateComponentJson(name, jsonObj, true, 'Page')
|
||
}
|
||
|
||
function updateProjectJson (name, jsonObj) {
|
||
updateComponentJson(name, jsonObj, false, 'Project')
|
||
}
|
||
|
||
const supportGlobalUsingComponents = process.env.UNI_PLATFORM === 'mp-weixin' || process.env.UNI_PLATFORM === 'mp-qq'
|
||
|
||
function updateComponentJson (name, jsonObj, usingComponents = true, type = 'Component') {
|
||
if (type === 'Component') {
|
||
jsonObj.component = true
|
||
if (process.env.UNI_PLATFORM === 'mp-alipay') {
|
||
const manifestConfig = process.UNI_MANIFEST
|
||
const alipayConfig = manifestConfig['mp-alipay'] || {}
|
||
jsonObj.styleIsolation = alipayConfig.styleIsolation || 'apply-shared'
|
||
}
|
||
} else if (type === 'Page') {
|
||
if (process.env.UNI_PLATFORM === 'mp-baidu') {
|
||
jsonObj.component = true
|
||
}
|
||
}
|
||
|
||
const oldJsonStr = getJsonFile(name)
|
||
if (oldJsonStr) { // update
|
||
if (usingComponents) { // merge usingComponents
|
||
// 其实直接拿新的 merge 到旧的应该就行
|
||
const oldJsonObj = JSON.parse(oldJsonStr)
|
||
jsonObj.usingComponents = oldJsonObj.usingComponents || {}
|
||
jsonObj.usingAutoImportComponents = oldJsonObj.usingAutoImportComponents || {}
|
||
if (oldJsonObj.genericComponents) {
|
||
jsonObj.genericComponents = oldJsonObj.genericComponents
|
||
}
|
||
if (oldJsonObj.usingGlobalComponents) { // 复制 global components(针对不支持全局 usingComponents 的平台)
|
||
jsonObj.usingGlobalComponents = oldJsonObj.usingGlobalComponents
|
||
}
|
||
}
|
||
const newJsonStr = JSON.stringify(jsonObj, null, 2)
|
||
if (newJsonStr !== oldJsonStr) {
|
||
updateJsonFile(name, newJsonStr)
|
||
}
|
||
} else { // add
|
||
updateJsonFile(name, jsonObj)
|
||
}
|
||
}
|
||
|
||
function updateUsingGlobalComponents (name, usingGlobalComponents) {
|
||
const manifestConfig = process.UNI_MANIFEST
|
||
const weixinConfig = manifestConfig['mp-weixin']
|
||
const independentSwitch = !!weixinConfig.independent
|
||
|
||
if (!independentSwitch && supportGlobalUsingComponents) {
|
||
return
|
||
}
|
||
|
||
const oldJsonStr = getJsonFile(name)
|
||
if (oldJsonStr) { // update
|
||
const jsonObj = JSON.parse(oldJsonStr)
|
||
jsonObj.usingGlobalComponents = usingGlobalComponents
|
||
const newJsonStr = JSON.stringify(jsonObj, null, 2)
|
||
if (newJsonStr !== oldJsonStr) {
|
||
updateJsonFile(name, newJsonStr)
|
||
}
|
||
} else { // add
|
||
const jsonObj = {
|
||
usingGlobalComponents
|
||
}
|
||
updateJsonFile(name, jsonObj)
|
||
}
|
||
}
|
||
|
||
function updateUsingAutoImportComponents (name, usingAutoImportComponents) {
|
||
const oldJsonStr = getJsonFile(name)
|
||
if (oldJsonStr) { // update
|
||
const jsonObj = JSON.parse(oldJsonStr)
|
||
jsonObj.usingAutoImportComponents = usingAutoImportComponents
|
||
const newJsonStr = JSON.stringify(jsonObj, null, 2)
|
||
if (newJsonStr !== oldJsonStr) {
|
||
updateJsonFile(name, newJsonStr)
|
||
}
|
||
} else { // add
|
||
updateJsonFile(name, {
|
||
usingAutoImportComponents
|
||
})
|
||
}
|
||
}
|
||
|
||
const styleIsolationRE =
|
||
/export\s+default\s+[\s\S]*?styleIsolation\s*:\s*['|"](isolated|apply-shared|shared)['|"]/
|
||
function parseComponentStyleIsolation (content) {
|
||
const matches = content.match(styleIsolationRE)
|
||
if (matches) {
|
||
return matches[1]
|
||
}
|
||
}
|
||
|
||
function updateUsingComponents (name, usingComponents, type, content = '') {
|
||
if (type === 'Component') {
|
||
componentSet.add(name)
|
||
}
|
||
if (type === 'App') { // 记录全局组件
|
||
globalUsingComponents = usingComponents
|
||
}
|
||
|
||
const oldJsonStr = getJsonFile(name)
|
||
const jsonObj = oldJsonStr ? JSON.parse(oldJsonStr) : {
|
||
usingComponents
|
||
}
|
||
if (type === 'Component') {
|
||
jsonObj.component = true
|
||
if (process.env.UNI_PLATFORM === 'mp-alipay') {
|
||
const alipayConfig = process.UNI_MANIFEST['mp-alipay'] || {}
|
||
jsonObj.styleIsolation =
|
||
parseComponentStyleIsolation(content) ||
|
||
alipayConfig.styleIsolation ||
|
||
'apply-shared'
|
||
}
|
||
|
||
// 微信小程序json文件中的styleIsolation优先级比options中的高,为了兼容旧版本,不能设置默认值,并且只有在manifest.json中配置styleIsolation才会静态分析组件的styleIsolation
|
||
if (process.env.UNI_PLATFORM === 'mp-weixin') {
|
||
const weixinConfig = process.UNI_MANIFEST['mp-weixin'] || {}
|
||
if (weixinConfig.styleIsolation) {
|
||
jsonObj.styleIsolation =
|
||
parseComponentStyleIsolation(content) ||
|
||
weixinConfig.styleIsolation
|
||
}
|
||
}
|
||
} else if (type === 'Page') {
|
||
if (process.env.UNI_PLATFORM === 'mp-baidu') {
|
||
jsonObj.component = true
|
||
}
|
||
}
|
||
if (oldJsonStr) { // update
|
||
jsonObj.usingComponents = usingComponents
|
||
const newJsonStr = JSON.stringify(jsonObj, null, 2)
|
||
if (newJsonStr !== oldJsonStr) {
|
||
updateJsonFile(name, newJsonStr)
|
||
}
|
||
} else { // add
|
||
updateJsonFile(name, jsonObj)
|
||
}
|
||
}
|
||
|
||
function updateComponentGenerics (name, componentGenerics) {
|
||
const oldJsonStr = getJsonFile(name)
|
||
if (oldJsonStr) { // update
|
||
const jsonObj = JSON.parse(oldJsonStr)
|
||
jsonObj.componentGenerics = componentGenerics
|
||
const newJsonStr = JSON.stringify(jsonObj, null, 2)
|
||
if (newJsonStr !== oldJsonStr) {
|
||
updateJsonFile(name, newJsonStr)
|
||
}
|
||
} else { // add
|
||
const jsonObj = {
|
||
componentGenerics
|
||
}
|
||
updateJsonFile(name, jsonObj)
|
||
}
|
||
}
|
||
|
||
function updateGenericComponents (name, genericComponents) {
|
||
const oldJsonStr = getJsonFile(name)
|
||
if (oldJsonStr) { // update
|
||
const jsonObj = JSON.parse(oldJsonStr)
|
||
jsonObj.genericComponents = genericComponents
|
||
const newJsonStr = JSON.stringify(jsonObj, null, 2)
|
||
if (newJsonStr !== oldJsonStr) {
|
||
updateJsonFile(name, newJsonStr)
|
||
}
|
||
} else { // add
|
||
const jsonObj = {
|
||
genericComponents
|
||
}
|
||
updateJsonFile(name, jsonObj)
|
||
}
|
||
}
|
||
|
||
function updateAppJsonUsingComponents (usingComponents) {
|
||
appJsonUsingComponents = usingComponents
|
||
}
|
||
|
||
function getComponentSet () {
|
||
return componentSet
|
||
}
|
||
|
||
function getGlobalUsingComponents () {
|
||
// 合并 app.json ,main.js 全局组件
|
||
return Object.assign({}, appJsonUsingComponents, globalUsingComponents)
|
||
}
|
||
|
||
function getWXComponents (name) {
|
||
const oldJsonStr = getJsonFile(name)
|
||
if (oldJsonStr) {
|
||
const jsonObj = JSON.parse(oldJsonStr)
|
||
if (jsonObj.customUsingComponents) {
|
||
return Object.assign({}, appJsonUsingComponents, jsonObj.customUsingComponents)
|
||
}
|
||
}
|
||
return Object.assign({}, appJsonUsingComponents)
|
||
}
|
||
|
||
function updateSpecialMethods (name, specialMethods) {
|
||
if (specialMethods.length) {
|
||
componentSpecialMethods[name] = specialMethods
|
||
} else {
|
||
delete componentSpecialMethods[name]
|
||
}
|
||
}
|
||
|
||
function getSpecialMethods (name) {
|
||
if (!name) {
|
||
return componentSpecialMethods
|
||
}
|
||
return componentSpecialMethods[name] || []
|
||
}
|
||
|
||
const cacheTypes = ['babel-loader', 'css-loader', 'uni-template-compiler', 'vue-loader']
|
||
|
||
function clearCache () {
|
||
const fsExtra = require('fs-extra')
|
||
cacheTypes.forEach(cacheType => {
|
||
fsExtra.emptyDirSync(path.resolve(
|
||
process.env.UNI_CLI_CONTEXT,
|
||
'node_modules/.cache/' + cacheType + '/' + process.env.UNI_PLATFORM
|
||
))
|
||
})
|
||
}
|
||
|
||
function digest (str) {
|
||
return crypto
|
||
.createHash('md5')
|
||
.update(str)
|
||
.digest('hex')
|
||
}
|
||
|
||
module.exports = {
|
||
getPageSet () {
|
||
return pageSet
|
||
},
|
||
getJsonFileMap () {
|
||
return jsonFileMap
|
||
},
|
||
// 先简单处理,该方案不好,
|
||
// 后续为 pages-loader 增加 cache-loader,
|
||
// 然后其他修改 json 的地方也要定制 cache-loader
|
||
store () {
|
||
const pagesJsonPath = path.resolve(process.env.UNI_INPUT_DIR, 'pages.json')
|
||
const filepath = path.resolve(
|
||
process.env.UNI_CLI_CONTEXT,
|
||
'node_modules/.cache/uni-pages-loader/' + process.env.UNI_PLATFORM,
|
||
digest(process.env.UNI_INPUT_DIR) + '.json'
|
||
)
|
||
|
||
const files = Array.from(jsonFileMap.entries())
|
||
const pages = Array.from(pageSet)
|
||
const components = Array.from(componentSet)
|
||
const methods = componentSpecialMethods
|
||
fs.writeFileSync(filepath, JSON.stringify({
|
||
mtimeMs: fs.statSync(pagesJsonPath).mtimeMs,
|
||
files,
|
||
pages,
|
||
components,
|
||
methods,
|
||
globalUsingComponents,
|
||
appJsonUsingComponents
|
||
}))
|
||
},
|
||
restore () {
|
||
const filepath = path.resolve(
|
||
process.env.UNI_CLI_CONTEXT,
|
||
'node_modules/.cache/uni-pages-loader/' + process.env.UNI_PLATFORM,
|
||
digest(process.env.UNI_INPUT_DIR) + '.json'
|
||
)
|
||
if (!fs.existsSync(filepath)) {
|
||
try {
|
||
clearCache()
|
||
} catch (e) { }
|
||
return
|
||
}
|
||
const pagesJsonPath = path.resolve(process.env.UNI_INPUT_DIR, 'pages.json')
|
||
const mtimeMs = fs.statSync(pagesJsonPath).mtimeMs
|
||
const jsonCache = require(filepath)
|
||
if (jsonCache.mtimeMs !== mtimeMs) {
|
||
try {
|
||
clearCache()
|
||
} catch (e) { }
|
||
return
|
||
}
|
||
jsonFileMap = new Map(jsonCache.files)
|
||
pageSet = new Set(jsonCache.pages)
|
||
componentSet = new Set(jsonCache.components)
|
||
componentSpecialMethods = jsonCache.methods
|
||
globalUsingComponents = jsonCache.globalUsingComponents
|
||
appJsonUsingComponents = jsonCache.appJsonUsingComponents
|
||
// restore 时,所有 file 均触发 change
|
||
for (const name of jsonFileMap.keys()) {
|
||
changedJsonFileSet.add(name)
|
||
}
|
||
return true
|
||
},
|
||
getJsonFile,
|
||
getPagesJson,
|
||
getComponentSet,
|
||
getWXComponents,
|
||
getGlobalUsingComponents,
|
||
updateAppJson,
|
||
updateJsonFile,
|
||
updatePageJson,
|
||
updateProjectJson,
|
||
updateComponentJson,
|
||
updateSpecialMethods,
|
||
updateUsingComponents,
|
||
updateUsingGlobalComponents,
|
||
updateAppJsonUsingComponents,
|
||
updateUsingAutoImportComponents,
|
||
updateComponentGenerics,
|
||
updateGenericComponents,
|
||
getChangedJsonFileMap,
|
||
getSpecialMethods,
|
||
supportGlobalUsingComponents
|
||
}
|