| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- /* eslint-disable no-eval */
- /* eslint-disable import/extensions */
- /* eslint-disable @typescript-eslint/no-var-requires */
- import { PageContext, SubPageMetaDatum, PageMetaDatum } from '@uni-helper/vite-plugin-uni-pages'
- import { Plugin } from 'vite'
- import enums from './src/enums/index'
- const common = require('./scripts/common.js')
- const { files, cl } = common
- const config = {
- /** 打包路径 */
- mpBuildPath: '',
- /** 原始路径 */
- rootPath: '',
- env: process.env,
- }
- const convert = {
- /** 已整体瘦身 */
- slimed: false,
- /** 瘦身过的文件信息 */
- slimeds: {},
- /** 引用接口所有文件信息 */
- alleics: [] as any[],
- /** 瘦身接口配置信息 */
- slimInterfaceConfig({ filePath = '' } = {}) {
- // 路径获取失败 跳出
- if (!(config.mpBuildPath && config.rootPath)) return
- /** 打包路径 */
- const mpBuildPath = config.mpBuildPath
- /** 原始路径 */
- const rootPath = config.rootPath
- // 接口配置目录
- const interfacePath = 'utils/config/interFaces'
- // const path = 'D:\\cl\\work\\pro\\mobile\\dist\\build\\mp-weixin'
- // console.log(path, pfs)
- // 所有接口配置信息文件路径
- const ifs = files.path.resolve(rootPath, interfacePath)
- const iffs = files.getAllFile(ifs) // .filter((f) => f.includes(`utils\\config\\interFaces`))
- // 接口信息内容
- const ifcontents = iffs
- .filter((f) => !['index.ts'].some((s) => f.includes(s)))
- .map((m) => {
- let fsc = files.read(m)
- fsc = fsc
- // 删除realUrl后面的配置
- // .replace(/(realUrl.+),(?:(?!realUrl)[\s\S])+},/g, '$1},')
- // 删除reqType、resType配置信息
- .replace(/reqType(?:(?!,\n)[\s\S])+,\n/g, '')
- .replace(/resType(?:(?!,\n)[\s\S])+,\n/g, '')
- .replace(/\n/g, '')
- .replaceAll(' as const', '')
- // const fscm = fsc.match(/^(.+\.)(.+)=(.+)(;+\s*)*$/)
- // 反序列化最外层的对象
- const fscm = fsc.match(/\{[\s\S]+\}/)
- const groupName = files.basename(m, false)
- let gifs = {}
- if (fscm && fscm.length === 1) {
- try {
- gifs = (0, eval)(`(${fscm[0]})`)
- } catch (e) {
- cl.loge(m, e)
- }
- }
- return {
- /** 原始文件路径 */
- filePath: m,
- /** 打包后的文件路径 */
- buildFilePath: m.replace(rootPath, mpBuildPath).replace('.ts', '.js'),
- /** 分组名称 */
- groupName,
- /** 分组内的接口信息 */
- gifs,
- }
- })
- // 排除接口配置信息之外的所有js文件内容
- /** 引用接口所有文件内容 */
- let alleiConts = ''
- // 指定某个文件变化,替换公共存储内的本文件内容
- if (filePath) {
- cl.logs(`变化来源:${filePath}`)
- const filePathEnd = filePath.replace(rootPath, '')
- const curEic = convert.alleics.find(
- (f) =>
- f.filePath.replace(mpBuildPath, '').replace(/\..*$/, '') ===
- filePathEnd.replace(/\..*$/, ''),
- )
- if (curEic) {
- curEic.content = files.read(filePath)
- }
- // 整体瘦身时
- } else {
- const pfs = files.getAllFile(mpBuildPath)
- convert.alleics = pfs
- .filter(
- (f) => f.endsWith('.js') && !f.includes('node-modules') && !f.includes(interfacePath),
- )
- .map((m) => {
- return {
- filePath: m,
- content: files.read(m),
- }
- })
- }
- alleiConts = convert.alleics.map((m) => m.content).join('')
- alleiConts = alleiConts.replaceAll(' ', '').replaceAll('\n', '').replaceAll('\r', '')
- // 删除未用到的接口配置信息
- ifcontents.forEach((f) => {
- const curGifs = f.gifs
- Object.keys(f.gifs).forEach((fk) => {
- // 使用痕迹
- const UsageTraces = [
- `${f.groupName}.${fk}.`,
- `${f.groupName}.${fk}(`,
- `${f.groupName}.${fk}<`,
- ]
- // 删除 未使用的接口信息
- if (!UsageTraces.some((s) => alleiConts.includes(s))) {
- delete curGifs[fk]
- } else {
- // 删除类型信息对象
- delete curGifs[fk].reqType
- delete curGifs[fk].resType
- }
- })
- const curSD = convert.slimeds[f.groupName]
- if (curSD) {
- if (JSON.stringify(curSD.gifs) !== JSON.stringify(f.gifs)) {
- curSD.gifs = f.gifs
- cl.logs('变化的文件:' + curSD.buildFilePath)
- }
- } else {
- // 记录瘦身信息
- convert.slimeds[f.groupName] = f
- }
- })
- // 瘦身目标目录的接口配置信息
- if (!convert.slimed) {
- cl.logw('整体瘦身中...')
- ifcontents.forEach((f) => {
- files.write(
- f.buildFilePath,
- `"use strict";exports.${f.groupName}=${JSON.stringify(f.gifs)};`,
- )
- cl.logs('瘦身文件:' + f.buildFilePath)
- })
- cl.logs('整体瘦身结束')
- }
- },
- /** 生成页面配置文件 */
- generateFileOper(type, callBack) {
- const pagesPath = files.getPath(`src/utils/config/${type === 0 ? 'pages' : 'subPages'}.ts`)
- let pagesContent = files.read(pagesPath)
- pagesContent = pagesContent.replace('export default', '')
- pagesContent = pagesContent.replaceAll(' as ayPage', '')
- /** 读取的旧的配置信息-可手动修改 */
- let pagesContentObj
- try {
- pagesContentObj = (0, eval)(`(${pagesContent})`)
- } catch (e) {
- pagesContentObj = {}
- }
- callBack(pagesContentObj)
- let pageMetaDataStr = JSON.stringify(pagesContentObj, null, 2)
- // 去掉"
- pageMetaDataStr = pageMetaDataStr.replace(/\"(.+)\":/g, '$1:').replaceAll('"', `'`)
- // pageMetaDataStr = pageMetaDataStr.replaceAll('},', `} as ayPage,`)
- // 追加 ayPage 类型
- // pageMetaDataStr = pageMetaDataStr.replace(/(isPager:.+\s+\})([\n,])/g, '$1 as ayPage$2')
- pageMetaDataStr = pageMetaDataStr.replace(/(\})(\n\}|,)/g, '$1 as ayPage$2')
- files.write(pagesPath, `export default ${pageMetaDataStr}`)
- },
- /** 生成页面配置信息 */
- generatePageConfig({
- ctx,
- spmd,
- pagesContentObj,
- }: {
- ctx?: PageContext
- /** 子包页面 */
- spmd?: SubPageMetaDatum
- /** 最终的配置信息-可手动修改,对应utils/config/pages.ts 或 utils/config/subPages.ts */
- pagesContentObj: AnyObject
- }) {
- /** 新生成的配置 */
- const pageMetaDataObj = {}
- const getPageType = (f) => {
- let rv = enums.PageType.page
- if (ctx?.pagesGlobConfig?.tabBar?.list.some((s) => s?.pagePath === f.path)) {
- rv = enums.PageType.tabPage
- }
- // if (ctx.options.homePage.includes(f.path)) {
- // rv = 'home'
- // }
- return rv
- }
- let cusPages = [] as PageMetaDatum[]
- if (spmd?.pages) {
- cusPages = spmd.pages.map<PageMetaDatum>((m) => {
- return {
- ...m,
- path: `${spmd.root}/${m.path}`,
- }
- })
- }
- const curPages = ctx?.pageMetaData || cusPages
- curPages.forEach((f) => {
- let key = f.path.replaceAll('/', '_')
- if (!/^[$A-Z_][0-9A-Z_$]*$/i.test(key)) {
- cl.loge(`${f.path}名称不合法,只能包含字母数字下划线`)
- return
- }
- // key值移除包名前缀
- // 主包页面
- if (ctx) {
- key = key.replace('pages_', '')
- }
- // 分包页面
- if (spmd?.root) {
- key = key.replace(spmd.root + '_', '')
- }
- pageMetaDataObj[key] = {
- // 为了方便小程序跳转,这里拼上/
- _url: '/' + f.path,
- _type: getPageType(f),
- title: key,
- // 登录不需要身份
- identity: !f.path.includes('login/index'),
- isPager: false,
- }
- // 手动设置了style,赋值给PageMetaDatum
- if (pagesContentObj[key]?.style) {
- f.style = pagesContentObj[key].style
- }
- })
- // 删除配置-因:删掉的目录
- Object.keys(pagesContentObj).forEach((k) => {
- if (!pageMetaDataObj[k]) {
- cl.logw(`删除的页面:${pagesContentObj[k]._url}`)
- delete pagesContentObj[k]
- }
- })
- /** 增量生成页面配置信息 */
- for (const k in pageMetaDataObj) {
- // 页面已存在
- if (pagesContentObj[k]) {
- const cur = pagesContentObj[k]
- // 重置只读属性(_开头的)值
- Object.keys(cur).forEach((f) => {
- if (f.startsWith('_')) delete cur[f]
- })
- pagesContentObj[k] = {
- ...pageMetaDataObj[k],
- ...pagesContentObj[k],
- }
- } else {
- // 页面不存在
- pagesContentObj[k] = pageMetaDataObj[k]
- cl.logs(`新增的页面:${pageMetaDataObj[k]._url}`)
- }
- }
- },
- }
- /** 对 uno.config.ts 功能的不充 */
- function UnoCssMPPlugin(): Plugin {
- return {
- name: 'UnoCssMPPlugin',
- /** 拦截app.wxss写入时修改 uno中的样式名称增加.container 前缀,提高样式权重 */
- writeBundle(options, bundle) {
- for (const filename in bundle) {
- if (filename.includes('app.wxss')) {
- const chunk = bundle[filename]
- if (chunk.type === 'asset') {
- const tempFilePath = `${options.dir}/${filename}`
- const code = chunk.source as string
- const allCss = code.split('/* stylelint-disable comment-empty-line-before */')
- let unoCss = allCss[0]
- unoCss = unoCss
- .replaceAll('}', '}\n')
- .replace(/(\.(?:(?!container).+))\{/g, '.container $1{')
- files.write(tempFilePath, unoCss + allCss[1])
- // bundle[filename] = {
- // fileName: 'app.wxss',
- // needsCodeReference: false,
- // source: '6666',
- // type: 'asset',
- // }
- }
- }
- }
- },
- }
- }
- /** 小程序瘦身插件 */
- function slimMPPlugin(): Plugin {
- if (config.env.UNI_PLATFORM === 'h5') {
- return {
- name: 'slimMP-plugin',
- }
- } else {
- return {
- name: 'slimMP-plugin',
- // apply: 'build',
- // renderStart(outputOptions, inputOptions) {
- // },
- // renderChunk() {
- // },
- writeBundle1(options, bundle) {
- for (const filename in bundle) {
- // 获取文件内容
- const chunk = bundle[filename]
- delete bundle[filename]
- // try {
- // // 内容未变化的文件,不进行磁盘写入
- // const fileContent = files.read(options.dir + '/' + filename)
- // let codekn = 'code'
- // if (chunk.type === 'asset') {
- // codekn = 'source'
- // }
- // if (chunk[codekn] === fileContent) {
- // console.log(filename)
- // delete bundle[filename]
- // }
- // } catch {}
- }
- },
- // 输出生成阶段的第一个钩子是 outputOptions,
- /** 输出生成阶段最后一个钩子 */
- generateBundle(options, bundle) {
- // 开发环境减少每次编译后的文件写入
- if (config.env.NODE_ENV !== 'development') return
- // files.write(files.getPath('temp/generateBundle.json'), JSON.stringify(arguments))
- // 遍历所有的打包文件
- for (const filename in bundle) {
- // 获取文件内容
- const chunk = bundle[filename]
- if (chunk.type === 'chunk') {
- // 写入瘦身后的 接口配置信息
- if (filename.includes('config/interFaces')) {
- const groupName = files.basename(filename, false)
- const curSD = convert.slimeds[groupName]
- if (curSD) {
- chunk.code = `"use strict";exports.${groupName}=${JSON.stringify(curSD.gifs)};`
- }
- }
- }
- try {
- // 内容未变化的文件,不进行磁盘写入
- const fileContent = files.read(options.dir + '/' + filename)
- let codekn = 'code'
- if (chunk.type === 'asset') {
- codekn = 'source'
- }
- if (chunk[codekn] === fileContent) {
- delete bundle[filename]
- }
- } catch {}
- }
- },
- /** 监听文件修改 */
- watchChange(filePath) {
- // h5环境 跳出
- // if (config.env.UNI_PLATFORM === 'h5') return
- // 已整体瘦身 & 有路径 & 非\utils\config目录下的 & filePath包含\\(小程序路径)
- if (
- convert.slimed &&
- filePath &&
- !filePath.includes('\\utils\\config') &&
- filePath.includes('\\')
- ) {
- convert.slimInterfaceConfig({ filePath })
- }
- },
- buildStart(options) {
- // files.write(files.getPath('temp/buildStart.json'), JSON.stringify(options))
- // return
- if (options.plugins) {
- const unimp: any = options.plugins.find((f) => ['uni:mp'].includes(f.name))
- if (unimp) {
- config.mpBuildPath = unimp.uni.copyOptions.targets[0].dest
- config.rootPath = unimp.uni.compilerOptions.root
- }
- }
- },
- closeBundle() {
- if (convert.slimed) {
- return
- }
- convert.slimInterfaceConfig()
- convert.slimed = true
- },
- }
- }
- }
- /** 生成页面配置信息 */
- function generatePageConfig(ctx: PageContext) {
- const pagesContentObj = {}
- convert.generateFileOper(0, (pagesContentObj) => {
- convert.generatePageConfig({ ctx, pagesContentObj })
- })
- if (ctx.subPageMetaData.length) {
- const pagesContentObj = {}
- convert.generateFileOper(1, (pagesContentObj) => {
- ctx.subPageMetaData.forEach((f) => {
- if (!pagesContentObj[f.root]) {
- pagesContentObj[f.root] = {}
- }
- convert.generatePageConfig({
- spmd: f,
- pagesContentObj: pagesContentObj[f.root],
- })
- })
- })
- }
- }
- export { slimMPPlugin, UnoCssMPPlugin, generatePageConfig }
|