mirror of
https://gitee.com/niucloud-team/niucloud.git
synced 2026-08-01 20:15:49 +00:00
109 lines
3.4 KiB
JavaScript
109 lines
3.4 KiB
JavaScript
/**
|
|
* 扫描小程序分包 JS 中的 require / require.async 依赖
|
|
*/
|
|
const fs = require('fs')
|
|
const path = require('path')
|
|
|
|
const REQUIRE_RE = /\brequire(?:\.async)?\(\s*['"]([^'"]+)['"]\s*\)/g
|
|
|
|
/**
|
|
* @param {string} mpRoot dist/build/mp-weixin 根目录
|
|
* @param {string[]} subPackageRoots 如 ['addon/shop']
|
|
*/
|
|
function scanSubPackageRequires(mpRoot, subPackageRoots, addonKey) {
|
|
const requirePaths = new Set()
|
|
const forbiddenPaths = new Set()
|
|
const scannedFiles = []
|
|
|
|
for (const root of subPackageRoots) {
|
|
const absRoot = path.join(mpRoot, root)
|
|
if (!fs.existsSync(absRoot)) continue
|
|
|
|
for (const rel of walkJs(absRoot)) {
|
|
const absFile = path.join(absRoot, rel)
|
|
const relFromMp = path.posix.join(root, rel.replace(/\\/g, '/'))
|
|
scannedFiles.push(relFromMp)
|
|
const content = fs.readFileSync(absFile, 'utf-8')
|
|
let m
|
|
REQUIRE_RE.lastIndex = 0
|
|
while ((m = REQUIRE_RE.exec(content)) !== null) {
|
|
const spec = m[1]
|
|
const resolved = resolveRequire(mpRoot, absFile, spec)
|
|
if (!resolved) continue
|
|
|
|
if (isOtherAddonPath(resolved, subPackageRoots, addonKey)) {
|
|
forbiddenPaths.add(resolved)
|
|
continue
|
|
}
|
|
requirePaths.add(resolved)
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
requirePaths: [...requirePaths].sort(),
|
|
forbiddenPaths: [...forbiddenPaths].sort(),
|
|
scannedFiles: scannedFiles.sort()
|
|
}
|
|
}
|
|
|
|
function walkJs(dir, base = dir) {
|
|
const out = []
|
|
for (const name of fs.readdirSync(dir)) {
|
|
const full = path.join(dir, name)
|
|
if (fs.statSync(full).isDirectory()) {
|
|
out.push(...walkJs(full, base))
|
|
} else if (name.endsWith('.js')) {
|
|
out.push(path.relative(base, full))
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
function resolveRequire(mpRoot, fromFile, spec) {
|
|
if (spec.startsWith('http://') || spec.startsWith('https://')) return null
|
|
if (!spec.startsWith('.')) {
|
|
return spec.replace(/\\/g, '/')
|
|
}
|
|
const dir = path.dirname(fromFile)
|
|
let resolved = path.normalize(path.join(dir, spec))
|
|
if (!resolved.endsWith('.js') && !resolved.endsWith('.json')) {
|
|
if (fs.existsSync(resolved + '.js')) resolved += '.js'
|
|
else if (fs.existsSync(resolved + '.json')) resolved += '.json'
|
|
}
|
|
if (!resolved.startsWith(mpRoot)) return null
|
|
return path.relative(mpRoot, resolved).replace(/\\/g, '/')
|
|
}
|
|
|
|
function isOtherAddonPath(resolvedPath, allowedRoots, addonKey) {
|
|
if (!resolvedPath.startsWith('addon/')) return false
|
|
// 同插件目录下的文件(如 api/、hooks/ 等不属于子包的公共目录)不视为跨插件
|
|
if (addonKey && resolvedPath.startsWith(`addon/${addonKey}/`)) return false
|
|
for (const root of allowedRoots) {
|
|
if (resolvedPath === root || resolvedPath.startsWith(`${root}/`)) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
function validateRequires(mpRoot, requirePaths, forbiddenPaths) {
|
|
const missing = []
|
|
for (const p of requirePaths) {
|
|
const abs = path.join(mpRoot, p)
|
|
if (!fs.existsSync(abs)) {
|
|
missing.push(p)
|
|
}
|
|
}
|
|
return {
|
|
ok: missing.length === 0 && forbiddenPaths.length === 0,
|
|
missing,
|
|
forbiddenPaths
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
scanSubPackageRequires,
|
|
validateRequires
|
|
}
|