Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ projects/e-svga*
@mf-types
.output
doc_build

.rslib
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"@commitlint/cli": "^17.8.0",
"@commitlint/config-conventional": "^17.8.0",
"@empjs/biome-config": "workspace:^",
"@rslib/core": "^0.13.2",
"@rslib/core": "^0.14.0",
"cross-env": "^7.0.3",
"serve": "14.2.5",
"tsup": "8.5.0",
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
"scripts": {
"dev": "tsup --watch --env.ENV dev",
"build": "tsup --env.ENV prod",
"dev-rs": "rslib build --watch --env-mode development",
"build-rs": "rslib build --env-mode production",
"ph": "pnpm publish --no-git-checks"
},
"engines": {
Expand All @@ -83,6 +85,7 @@
"@types/webpack": "^5.28.4",
"@types/webpack-bundle-analyzer": "^4.6.2",
"@types/webpack-sources": "^3.2.2",
"chokidar": "^4.0.3",
"vue": "^3.5.21"
},
"dependencies": {
Expand Down
48 changes: 48 additions & 0 deletions packages/cli/rslib.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {defineConfig, type RslibConfig} from '@rslib/core'

export default defineConfig(({envMode}): RslibConfig => {
const isDev = envMode === 'development'
// console.log('isDev', isDev, envMode)
return {
lib: [
{
bundle: true,
format: 'esm',
shims: {
esm: {
__filename: true,
__dirname: true,
require: true,
},
},
dts: true,
},
{
bundle: true,
format: 'cjs',
shims: {
cjs: {
'import.meta.url': true,
},
},
},
],
source: {
tsconfigPath: './tsconfig.json',
define: {
'process.env.ENV': JSON.stringify(isDev ? 'dev' : 'prod'),
},
entry: {
index: 'src/index.ts',
},
},
output: {
target: 'node',
sourceMap: isDev,
minify: !isDev,
externals: {
'@rsdoctor/rspack-plugin': 'commonjs @rsdoctor/rspack-plugin',
},
},
}
})
75 changes: 72 additions & 3 deletions packages/cli/src/helper/buildPrint.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fs from 'node:fs'
import path from 'node:path'
import type {Stats, StatsAsset} from '@rspack/core'
import chalk from 'chalk'
import {gzipSizeSync} from 'gzip-size'
import {blue, gray, green, magenta, red, white, yellow} from 'src/helper/color'
import store from 'src/store'
Expand Down Expand Up @@ -130,8 +131,76 @@ export async function printFileSizes(stats: Stats) {
const totalSizeLabel = `${blue.bold('Total size:')} ${calcFileSize(totalSize)}`
const gzippedSizeLabel = `${blue.bold('Gzipped size:')} ${calcFileSize(totalGzipSize)}`
logger.info(`\n ${totalSizeLabel}\n ${gzippedSizeLabel}\n`)
logger.info(`${green('')} Ready in ${timeFormat(Number(origin.time))}\n`)
logger.info(`${chalk.green('ready ')}Built in ${timeFormat(Number(origin.time))}\n`)
}
export function timeDone(mis: number | string = 0) {
logger.info(`${green('✓')} Ready in ${timeFormat(Number(mis))}`)
/**
* 统一的打印函数,用于显示不同类型的构建消息
* @param type 消息类型: 'start' | 'ready'
* @param message 要显示的消息
*/
function printMessage(type: 'start' | 'ready', message: string) {
const prefix = type === 'start' ? chalk.cyan('start ') : chalk.green('ready ')
console.log(prefix + chalk.white(message))
}

export function timeDone(mis: number | string = 0, tips = 'Ready') {
logger.info(`${chalk.green('ready ')}${tips} in ${timeFormat(Number(mis))}`)
}

/**
* 显示构建开始提示
*/
export function printBuildStart() {
printMessage('start', 'build started...')
}

/**
* 显示构建完成提示
* @param stats 构建统计信息
*/
export function printBuildDone(stats: any) {
const data = stats?.toJson({all: false, colors: false, assets: false, chunks: false, timings: true})
printMessage('ready', `built in ${chalk.bold((data.time / 1000).toFixed(2))} s`)
}

/**
* 显示模块构建开始提示
* @param moduleId 模块ID
* @param moduleCache 可选的模块缓存
*/
export function printModuleBuildStart(moduleId: string, moduleCache?: Map<string, number>) {
if (moduleId.includes('/src/') && !moduleId.includes('node_modules')) {
const relativePath = moduleId.split('/src/').pop() || ''
if (relativePath) {
printMessage('start', 'building ' + chalk.dim('src/' + relativePath))
}
}
}

/**
* 记录构建开始
* @param message 可选的自定义消息
*/
export function logStart(message = 'build started...') {
printMessage('start', message)
}

/**
* 记录构建完成
* @param stats 构建统计信息或构建时间(毫秒)
* @param message 可选的自定义消息前缀
*/
export function logDone(stats: any | number, message = 'built') {
let timeInSeconds: string

if (typeof stats === 'number') {
// 如果传入的是毫秒数
timeInSeconds = (stats / 1000).toFixed(2)
} else {
// 如果传入的是stats对象
const data = stats?.toJson({all: false, colors: false, assets: false, chunks: false, timings: true})
timeInSeconds = (data.time / 1000).toFixed(2)
}

printMessage('ready', `${message} in ${chalk.bold(timeInSeconds)} s`)
}
205 changes: 205 additions & 0 deletions packages/cli/src/helper/compilerWatcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import type {Compiler} from '@rspack/core'
import chalk from 'chalk'
import path from 'path'
import type {DevServer} from '../server/connect/dev'
import {logDone, logStart} from './buildPrint'

// 源文件过滤条件
const SRC_FILE_REGEX = /\/src\//
const NODE_MODULES_REGEX = /node_modules/

/**
* 格式化文件路径,使其更易读
*/
function formatFileList(files: string[], context: string): string[] {
return files.map(file => {
// 将绝对路径转换为相对于项目根目录的路径
const relativePath = path.relative(context, file)
return chalk.dim(path.dirname(relativePath) + path.sep) + chalk.dim(path.basename(relativePath))
})
}

export function setupCompilerWatcher(compiler: Compiler, devServer: DevServer) {
const HOOK_NAME = 'emp:compiler'

// 用于跟踪当前构建周期中已处理的模块
const processedModules = new Set<string>()

// 用于存储文件的最后修改时间
const fileTimestamps = new Map<string, number>()

// 是否有文件被修改的标志
let hasModifiedFiles = false

// 用于存储懒加载的模块
const lazyModules = new Set<string>()

// 编译状态
let isCompiling = false

// 项目根目录
const context = compiler.options.context || process.cwd()

/**
* 检测文件是否为源代码文件
*/
const isSourceFile = (filePath: string): boolean => {
return SRC_FILE_REGEX.test(filePath) && !NODE_MODULES_REGEX.test(filePath)
}

/**
* 获取变更的文件列表,避免重复处理同一个文件
*/
const getChangedFiles = (compiler: any): {changed: Set<string>; removed: Set<string>} => {
const changed = new Set<string>()
const removed = new Set<string>()

// 使用Rspack/Webpack 5的modifiedFiles和removedFiles API
if (compiler.modifiedFiles || compiler.removedFiles) {
// 处理修改的文件
if (compiler.modifiedFiles) {
for (const file of compiler.modifiedFiles) {
// 只处理源代码文件
if (isSourceFile(file)) {
// 检查文件是否已经在当前编译周期中处理过
const lastProcessedTime = fileTimestamps.get(file) || 0
const currentTime = Date.now()

// 如果文件在短时间内被多次修改,只处理一次
// 使用300ms作为防抖时间
if (currentTime - lastProcessedTime > 300) {
changed.add(file)
fileTimestamps.set(file, currentTime)
}
}
}
}

// 处理删除的文件
if (compiler.removedFiles) {
for (const file of compiler.removedFiles) {
if (isSourceFile(file)) {
removed.add(file)
// 从时间戳记录中删除
fileTimestamps.delete(file)
}
}
}

return {changed, removed}
}

// 兼容旧版本
if (compiler.watchFileSystem?.watcher) {
const watcher = compiler.watchFileSystem.watcher
const mtimes = watcher.mtimes || {}

for (const filePath of Object.keys(mtimes)) {
if (!isSourceFile(filePath)) continue

const mtime = mtimes[filePath]
const oldTime = fileTimestamps.get(filePath) || 0
const currentTime = Date.now()

// 防抖处理
if (oldTime < mtime && currentTime - oldTime > 300) {
changed.add(filePath)
fileTimestamps.set(filePath, currentTime)
}
}
}

return {changed, removed}
}

/**
* 打印构建日志
*/
function printBuildLog(changedFiles: Set<string>, removedFiles: Set<string>) {
if (!devServer.isServerStarted) return

const changedList = Array.from(changedFiles)
const removedList = Array.from(removedFiles)
const lazyList = Array.from(lazyModules)

// 如果没有文件变更,不打印日志
if (changedList.length === 0 && removedList.length === 0 && lazyList.length === 0) {
return
}

let message = ''

// 格式化并添加修改的文件
if (changedList.length > 0) {
const formattedFiles = formatFileList(changedList, context)
message += `modified ${formattedFiles.join(', ')}`
}

// 格式化并添加懒加载的模块
if (lazyList.length > 0) {
const formattedLazy = formatFileList(lazyList, context)
message += `lazyLoaded ${formattedLazy.join(', ')}`
}

// 格式化并添加删除的文件
if (removedList.length > 0) {
const formattedRemoved = formatFileList(removedList, context)
message += `removed ${formattedRemoved.join(', ')}`
}

// 打印构建开始信息
logStart(message)
}

// 监听编译开始,重置状态
compiler.hooks.watchRun.tap(HOOK_NAME, (compiler: any) => {
// 避免重复触发
if (isCompiling) return
isCompiling = true

// 清空当前构建周期的处理记录
processedModules.clear()
lazyModules.clear()

// 获取变更的文件
const {changed, removed} = getChangedFiles(compiler)
hasModifiedFiles = changed.size > 0 || removed.size > 0

// 打印构建日志
if (hasModifiedFiles) {
printBuildLog(changed, removed)
}

// 将变更的文件添加到处理列表
changed.forEach(filePath => processedModules.add(filePath))
})

// 监听构建完成
compiler.hooks.done.tap(HOOK_NAME, stats => {
// 只有在服务器启动完成且有文件被修改时才处理
if (!devServer.isServerStarted || !hasModifiedFiles) {
isCompiling = false
return
}

// 确保每次构建只打印一次完成信息
if (processedModules.size > 0 || lazyModules.size > 0) {
logDone(stats)
// 重置标志
hasModifiedFiles = false
}

// 重置编译状态
isCompiling = false
})

return {
cleanup: () => {
processedModules.clear()
fileTimestamps.clear()
lazyModules.clear()
hasModifiedFiles = false
isCompiling = false
},
}
}
6 changes: 4 additions & 2 deletions packages/cli/src/helper/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import color from 'src/helper/color'
export type LoggerType = 'debug' | 'info' | 'warn' | 'error'
export {color}
//
const isDev = process.env.ENV === 'dev'
// console.log('process.env.ENV', process.env.ENV)
export class Logger {
public brandName = ''
public fullName = ''
public logLevel = 'debug'
public isLogTime = process.env.ENV === 'dev'
public logLevel = isDev ? 'debug' : 'info'
public isLogTime = isDev
setup({brandName, logLevel, fullName}: any) {
//` EMP v${store.empPkg.version} `
this.brandName = ` [${brandName}] `
Expand Down
Loading