Files
2026-09-08 20:28:54 +08:00

390 lines
11 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}