Skip to content

Commit 2f07e6a

Browse files
committed
refactor: optimize localCDN plugin implementation.
提取独立的插件 cdnCopyPlugin、createEnvReplacementPlugin 到 vite-plugin 文件夹中。
1 parent 4c1e5c1 commit 2f07e6a

3 files changed

Lines changed: 188 additions & 183 deletions

File tree

packages/build/vite-config/src/localCdnFile/localCdnPlugin.js

Lines changed: 4 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import path from 'node:path'
22
import fs from 'fs-extra'
3-
import fg from 'fast-glob'
4-
import { dedupeCopyFiles } from './locateCdnNpmInfo.js'
53
import { installPackageTemporary } from '../vite-plugins/installPackageTemporary.js'
6-
import { babelReplaceImportPathWithCertainFileName } from './replaceImportPath.mjs'
4+
import { createEnvReplacementPlugin } from '../vite-plugins/createEnvReplacementPlugin.js'
5+
import { copyPlugin } from '../vite-plugins/cdnCopyPlugin.js'
6+
import { dedupeCopyFiles } from './locateCdnNpmInfo.js'
77

88
const logger = console
99

10+
// 默认的复制配置,这几个 package 需要复制整个目录,所以需要有默认配置进行复制整个目录
1011
const defaultCopyConfig = {
1112
'@opentiny/vue-theme': {
1213
filePathInPackage: '/'
@@ -22,20 +23,6 @@ const defaultCopyConfig = {
2223
}
2324
}
2425

25-
/**
26-
* 对文件内容进行转换处理
27-
* @param {string} content - 文件内容
28-
* @param {string} filename - 文件名
29-
* @returns {string} - 处理后的内容
30-
*/
31-
function replaceJsImportPaths(content, filename) {
32-
if (filename.endsWith('.js')) {
33-
const result = babelReplaceImportPathWithCertainFileName(content, filename, console)
34-
return result.code || content
35-
}
36-
return content
37-
}
38-
3926
/**
4027
* 从importMapUrl字符串中提取包名、版本和文件路径
4128
* @param {string} str - 导入字符串
@@ -60,172 +47,6 @@ function extractInfo(str) {
6047
}
6148
}
6249

63-
/**
64-
* 创建环境变量替换插件
65-
* @param {string} cdnDir - 本地CDN目录名
66-
* @returns {Object} - Vite插件对象
67-
*/
68-
function createEnvReplacementPlugin(cdnDir, base) {
69-
return {
70-
name: 'vite-replace-cdn-env',
71-
config(config) {
72-
// 在构建时替换环境变量,将CDN域名替换为本地路径
73-
if (!config.define) {
74-
config.define = {}
75-
}
76-
77-
config.define['import.meta.env.VITE_CDN_DOMAIN'] = JSON.stringify(
78-
`${base.endsWith('/') ? base : base + '/'}${cdnDir}`
79-
)
80-
// 使用本地 CDN 时,强制设置CDN类型为 local
81-
config.define['import.meta.env.VITE_CDN_TYPE'] = JSON.stringify('local')
82-
}
83-
}
84-
}
85-
86-
/**
87-
* 复制文件或目录到目标路径
88-
* @param {string} srcPath - 源文件/目录路径
89-
* @param {string[]} destPaths - 目标路径数组
90-
* @param {Set} copiedFiles - 已复制文件集合
91-
* @param {string} outDir - 输出目录
92-
*/
93-
async function copyFileOrDirectory(srcPath, destPaths, copiedFiles, outDir) {
94-
// 生成一个唯一标识,避免重复复制相同文件
95-
const copyId = `${srcPath}:${destPaths.join(',')}`
96-
97-
if (copiedFiles.has(copyId)) {
98-
logger.log(`[vite-cdn-copy-plugin]: Skipping already copied file: ${srcPath}`)
99-
return
100-
}
101-
102-
copiedFiles.add(copyId)
103-
104-
// 检查源文件是否存在
105-
if (!fs.existsSync(srcPath)) {
106-
logger.warn(`[vite-cdn-copy-plugin]: Source does not exist: ${srcPath}`)
107-
return
108-
}
109-
110-
const isDirectory = fs.statSync(srcPath).isDirectory()
111-
112-
// 为每个目标路径执行复制
113-
for (const destPath of destPaths) {
114-
const fullDestPath = path.resolve(outDir, destPath)
115-
116-
try {
117-
// 确保目标目录存在
118-
await fs.ensureDir(path.dirname(fullDestPath))
119-
120-
logger.log(`[vite-cdn-copy-plugin]: Copying from ${srcPath} to ${fullDestPath}`)
121-
122-
if (isDirectory) {
123-
// 如果是目录,使用 fast-glob 遍历所有文件并处理
124-
logger.log(`[vite-cdn-copy-plugin]: Copying directory recursively: ${srcPath} -> ${fullDestPath}`)
125-
126-
// 确保目标路径存在
127-
await fs.ensureDir(fullDestPath)
128-
129-
// 使用绝对路径
130-
const absoluteSrcPath = path.resolve(process.cwd(), srcPath)
131-
132-
// 使用 fast-glob 查找所有文件
133-
const files = fg.sync(`${absoluteSrcPath}/**/*`, { onlyFiles: true })
134-
135-
// 处理每个文件
136-
for (const file of files) {
137-
const relativePath = path.relative(absoluteSrcPath, file)
138-
const destFilePath = path.join(fullDestPath, relativePath)
139-
140-
// 确保目标文件的目录存在
141-
await fs.ensureDir(path.dirname(destFilePath))
142-
143-
// 读取文件内容
144-
const content = await fs.readFile(file, 'utf-8')
145-
146-
// 应用转换
147-
const transformedContent = replaceJsImportPaths(content, file)
148-
149-
// 写入转换后的内容
150-
await fs.writeFile(destFilePath, transformedContent)
151-
}
152-
} else {
153-
// 如果是单个文件
154-
logger.log(`[vite-cdn-copy-plugin]: Copying file: ${srcPath} -> ${fullDestPath}`)
155-
156-
let finalDestPath = path.join(fullDestPath, path.basename(srcPath))
157-
158-
// 确保目标文件的目录存在
159-
await fs.ensureDir(path.dirname(finalDestPath))
160-
161-
// 读取文件内容
162-
const content = await fs.readFile(srcPath, 'utf-8')
163-
164-
// 应用转换
165-
const transformedContent = replaceJsImportPaths(content, srcPath)
166-
167-
// 写入转换后的内容
168-
await fs.writeFile(finalDestPath, transformedContent)
169-
}
170-
171-
logger.log(`[vite-cdn-copy-plugin]: Successfully copied: ${srcPath} -> ${fullDestPath}`)
172-
} catch (err) {
173-
logger.error(`[vite-cdn-copy-plugin]: Failed to copy ${srcPath} to ${fullDestPath}`, err)
174-
}
175-
}
176-
}
177-
178-
/**
179-
* 创建复制插件
180-
* @param {Array<Object>} targets - 复制目标配置数组
181-
* @param {string|Array<string>} targets[].src - 源文件路径或路径数组
182-
* @param {string|Array<string>} targets[].dest - 目标文件路径或路径数组
183-
* @returns {Object} Vite插件对象
184-
*/
185-
186-
function copyPlugin(targets) {
187-
let resolvedConfig = null
188-
let copiedFiles = new Set()
189-
190-
return {
191-
name: 'vite-cdn-copy-plugin',
192-
configResolved(getResolvedConfig) {
193-
resolvedConfig = getResolvedConfig
194-
},
195-
async writeBundle() {
196-
if (!targets || !targets.length) {
197-
return
198-
}
199-
200-
const outDir = resolvedConfig.build.outDir || 'dist'
201-
202-
logger.log('[vite-cdn-copy-plugin]: Start copying files to dist directory')
203-
204-
// 遍历所有复制目标
205-
for (const target of targets) {
206-
const { src, dest } = target
207-
208-
if (!src || !dest) {
209-
logger.warn('[vite-cdn-copy-plugin]: Skipping target with missing src or dest', target)
210-
continue
211-
}
212-
213-
// 处理源路径,支持数组形式
214-
// const srcPaths = (Array.isArray(src) ? src : [src]).map(item => path.resolve(process.cwd(), item))
215-
const srcPaths = Array.isArray(src) ? src : [src]
216-
// 处理目标路径,支持数组形式
217-
const destPaths = Array.isArray(dest) ? dest : [dest]
218-
219-
for (const srcPath of srcPaths) {
220-
await copyFileOrDirectory(srcPath, destPaths, copiedFiles, outDir)
221-
}
222-
}
223-
224-
logger.log('[vite-cdn-copy-plugin]: Finished copying files')
225-
}
226-
}
227-
}
228-
22950
/**
23051
* 比较两个版本号是否相同
23152
* @param {string} versionOrigin - 源版本号, 可能包含 ^ 或 ~ 开头
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import path from 'node:path'
2+
import fs from 'fs-extra'
3+
import fg from 'fast-glob'
4+
import { babelReplaceImportPathWithCertainFileName } from '../localCdnFile/replaceImportPath.mjs'
5+
6+
/**
7+
* 对文件内容进行转换处理
8+
* @param {string} content - 文件内容
9+
* @param {string} filename - 文件名
10+
* @returns {string} - 处理后的内容
11+
*/
12+
function replaceJsImportPaths(content, filename) {
13+
if (filename.endsWith('.js')) {
14+
const result = babelReplaceImportPathWithCertainFileName(content, filename, console)
15+
return result.code || content
16+
}
17+
return content
18+
}
19+
20+
const logger = console
21+
22+
/**
23+
* 复制文件或目录到目标路径
24+
* @param {string} srcPath - 源文件/目录路径
25+
* @param {string[]} destPaths - 目标路径数组
26+
* @param {Set} copiedFiles - 已复制文件集合
27+
* @param {string} outDir - 输出目录
28+
*/
29+
async function copyFileOrDirectory(srcPath, destPaths, copiedFiles, outDir) {
30+
// 生成一个唯一标识,避免重复复制相同文件
31+
const copyId = `${srcPath}:${destPaths.join(',')}`
32+
33+
if (copiedFiles.has(copyId)) {
34+
logger.log(`[vite-cdn-copy-plugin]: Skipping already copied file: ${srcPath}`)
35+
return
36+
}
37+
38+
copiedFiles.add(copyId)
39+
40+
// 检查源文件是否存在
41+
if (!fs.existsSync(srcPath)) {
42+
logger.warn(`[vite-cdn-copy-plugin]: Source does not exist: ${srcPath}`)
43+
return
44+
}
45+
46+
const isDirectory = fs.statSync(srcPath).isDirectory()
47+
48+
// 为每个目标路径执行复制
49+
for (const destPath of destPaths) {
50+
const fullDestPath = path.resolve(outDir, destPath)
51+
52+
try {
53+
// 确保目标目录存在
54+
await fs.ensureDir(path.dirname(fullDestPath))
55+
56+
logger.log(`[vite-cdn-copy-plugin]: Copying from ${srcPath} to ${fullDestPath}`)
57+
58+
if (isDirectory) {
59+
// 如果是目录,使用 fast-glob 遍历所有文件并处理
60+
logger.log(`[vite-cdn-copy-plugin]: Copying directory recursively: ${srcPath} -> ${fullDestPath}`)
61+
62+
// 确保目标路径存在
63+
await fs.ensureDir(fullDestPath)
64+
65+
// 使用绝对路径
66+
const absoluteSrcPath = path.resolve(process.cwd(), srcPath)
67+
68+
// 使用 fast-glob 查找所有文件
69+
const files = fg.sync(`${absoluteSrcPath}/**/*`, { onlyFiles: true })
70+
71+
// 处理每个文件
72+
for (const file of files) {
73+
const relativePath = path.relative(absoluteSrcPath, file)
74+
const destFilePath = path.join(fullDestPath, relativePath)
75+
76+
// 确保目标文件的目录存在
77+
await fs.ensureDir(path.dirname(destFilePath))
78+
79+
// 读取文件内容
80+
const content = await fs.readFile(file, 'utf-8')
81+
82+
// 应用转换
83+
const transformedContent = replaceJsImportPaths(content, file)
84+
85+
// 写入转换后的内容
86+
await fs.writeFile(destFilePath, transformedContent)
87+
}
88+
} else {
89+
// 如果是单个文件
90+
logger.log(`[vite-cdn-copy-plugin]: Copying file: ${srcPath} -> ${fullDestPath}`)
91+
92+
let finalDestPath = path.join(fullDestPath, path.basename(srcPath))
93+
94+
// 确保目标文件的目录存在
95+
await fs.ensureDir(path.dirname(finalDestPath))
96+
97+
// 读取文件内容
98+
const content = await fs.readFile(srcPath, 'utf-8')
99+
100+
// 应用转换
101+
const transformedContent = replaceJsImportPaths(content, srcPath)
102+
103+
// 写入转换后的内容
104+
await fs.writeFile(finalDestPath, transformedContent)
105+
}
106+
107+
logger.log(`[vite-cdn-copy-plugin]: Successfully copied: ${srcPath} -> ${fullDestPath}`)
108+
} catch (err) {
109+
logger.error(`[vite-cdn-copy-plugin]: Failed to copy ${srcPath} to ${fullDestPath}`, err)
110+
}
111+
}
112+
}
113+
114+
/**
115+
* 创建复制插件
116+
* @param {Array<Object>} targets - 复制目标配置数组
117+
* @param {string|Array<string>} targets[].src - 源文件路径或路径数组
118+
* @param {string|Array<string>} targets[].dest - 目标文件路径或路径数组
119+
* @returns {Object} Vite插件对象
120+
*/
121+
122+
export function copyPlugin(targets) {
123+
let resolvedConfig = null
124+
let copiedFiles = new Set()
125+
126+
return {
127+
name: 'vite-cdn-copy-plugin',
128+
configResolved(getResolvedConfig) {
129+
resolvedConfig = getResolvedConfig
130+
},
131+
async writeBundle() {
132+
if (!targets || !targets.length) {
133+
return
134+
}
135+
136+
const outDir = resolvedConfig.build.outDir || 'dist'
137+
138+
logger.log('[vite-cdn-copy-plugin]: Start copying files to dist directory')
139+
140+
// 遍历所有复制目标
141+
for (const target of targets) {
142+
const { src, dest } = target
143+
144+
if (!src || !dest) {
145+
logger.warn('[vite-cdn-copy-plugin]: Skipping target with missing src or dest', target)
146+
continue
147+
}
148+
149+
// 处理源路径,支持数组形式
150+
const srcPaths = Array.isArray(src) ? src : [src]
151+
// 处理目标路径,支持数组形式
152+
const destPaths = Array.isArray(dest) ? dest : [dest]
153+
154+
for (const srcPath of srcPaths) {
155+
await copyFileOrDirectory(srcPath, destPaths, copiedFiles, outDir)
156+
}
157+
}
158+
159+
logger.log('[vite-cdn-copy-plugin]: Finished copying files')
160+
}
161+
}
162+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* 创建环境变量替换插件
3+
* @param {string} cdnDir - 本地CDN目录名
4+
* @returns {Object} - Vite插件对象
5+
*/
6+
export function createEnvReplacementPlugin(cdnDir, base) {
7+
return {
8+
name: 'vite-replace-cdn-env',
9+
config(config) {
10+
// 在构建时替换环境变量,将CDN域名替换为本地路径
11+
if (!config.define) {
12+
config.define = {}
13+
}
14+
15+
config.define['import.meta.env.VITE_CDN_DOMAIN'] = JSON.stringify(
16+
`${base.endsWith('/') ? base : base + '/'}${cdnDir}`
17+
)
18+
// 使用本地 CDN 时,强制设置CDN类型为 local
19+
config.define['import.meta.env.VITE_CDN_TYPE'] = JSON.stringify('local')
20+
}
21+
}
22+
}

0 commit comments

Comments
 (0)