普通视图

发现新文章,点击刷新页面。
昨天 — 2026年1月9日首页

解析ElementPlus打包源码(五、copyFiles)

作者 jqq666
2026年1月9日 15:12

还有最后一个copyFiles,虽然有点水,还是记录下

image.png

copyTypesDefinitions 我们之前打包类型已经写过。这里我们只看copyFiles

export const copyFiles = () =>
  Promise.all([
    copyFile(epPackage, path.join(epOutput, 'package.json')),
    copyFile(
      path.resolve(projRoot, 'README.md'),
      path.resolve(epOutput, 'README.md')
    ),
    copyFile(
      path.resolve(projRoot, 'typings', 'global.d.ts'),
      path.resolve(epOutput, 'global.d.ts')
    ),
  ])

这里复制了三个文件

packages/element-plus/package.json

packages/element-plus/package.json这个文件复制到打包后的项目下当作打包后的package.json

package.json里面涉及了一些package.json的基础信息,例如:

{
  "name": "element-plus",        // 包名,安装时使用 npm install element-plus
  "version": "0.0.0-dev.1",     // 版本号
  "description": "A Component Library for Vue 3", // 包的描述
  "keywords": ["element-plus", "vue", ...], // 关键词,方便 npm 搜索
  "homepage": "https://element-plus.org/", // 官网地址
  "bugs": { "url": "..." },      // 问题反馈地址
  "license": "MIT",              // 开源协议
  "repository": { ... }         // 代码仓库地址
  "publishConfig": { ... }    // 发布配置:`access: public` 表示该包是公开发布的(npm 私有包需付费,此配置确保包公开可访问)
}

还有一些模块导出规则的信息,main/module/types这三个是早期 npm 包的 “基础配置”,只能定义 “根路径” 的单一入口,无法精细化控制子路径,现在的exports优先级更高

"main": "lib/index.js",
"module": "es/index.mjs",
"types": "es/index.d.ts",
"exports": {
  // 1. 根路径引入:import 'element-plus' / require('element-plus')
  ".": {
    "types": "./es/index.d.ts",  // TS 类型文件(优先匹配)
    "import": "./es/index.mjs",  // ESM 引入(import 语法)加载 es 目录的 mjs 文件(ESM 模块)
    "require": "./lib/index.js"  // CommonJS 引入(require 语法)加载 lib 目录的 js 文件(CJS 模块)
  },
  // 2. 全局类型引入:import 'element-plus/global'
  "./global": {
    "types": "./global.d.ts"     // 仅导出全局类型(无 js 代码,用于全局类型声明)
  },
  // 3. 直接引入 es 目录:import 'element-plus/es'
  "./es": {
    "types": "./es/index.d.ts",
    "import": "./es/index.mjs"   // 仅支持 ESM 引入(es 目录只存 ESM 模块)
  },
  // 4. 直接引入 lib 目录:require('element-plus/lib')
  "./lib": {
    "types": "./lib/index.d.ts",
    "require": "./lib/index.js"  // 仅支持 CommonJS 引入(lib 目录只存 CJS 模块)
  },
  // 5. 按需引入 es 目录下的 mjs 文件:import 'element-plus/es/button.mjs'
  "./es/*.mjs": {
    "types": "./es/*.d.ts",      // 匹配对应 ts 类型文件(如 button.d.ts)
    "import": "./es/*.mjs"       // 加载对应 mjs 文件(ESM 按需引入)
  },
  // 6. 按需引入 es 目录下的模块:import 'element-plus/es/button'(无后缀)
  "./es/*": {
    "types": ["./es/*.d.ts", "./es/*/index.d.ts"], // 类型匹配优先级:先找 button.d.ts,再找 button/index.d.ts
    "import": "./es/*.mjs"       // 自动补全 mjs 后缀,适配开发者省略后缀的习惯
  },
  // 7. 按需引入 lib 目录下的 js 文件:require('element-plus/lib/button.js')
  "./lib/*.js": {
    "types": "./lib/*.d.ts",
    "require": "./lib/*.js"      // CJS 按需引入(带后缀)
  },
  // 8. 按需引入 lib 目录下的模块:require('element-plus/lib/button')(无后缀)
  "./lib/*": {
    "types": ["./lib/*.d.ts", "./lib/*/index.d.ts"], // 类型匹配规则同 es 目录
    "require": "./lib/*.js"      // 自动补全 js 后缀
  },
  // 9. 兜底规则:匹配所有未定义的路径(如 import 'element-plus/package.json'"./*": "./*"
}

package.json中还有相关的peerDependencies(要求项目中安装符合版本的依赖,否则会进行提醒)、dependencies、devDependencies

还有其他的相关配置可自行查看文档

README.md

根路径下的README文档,复制到打包后的包中

global.d.ts

这个文件就是Element Plus 的全局 TypeScript 类型声明文件

主要就是全局引入组件的时候,方便通过引入这个类型文件,在整个项目中都有提示

可见文档说明 ElementPlus快速开始

昨天以前首页

解析ElementPlus打包源码(三、打包类型)

作者 jqq666
2026年1月6日 15:52

runTask('generateTypesDefinitions')

我们定位到相关的代码

import path from 'path'
import { readFile, writeFile } from 'fs/promises'
import glob from 'fast-glob'
import { copy, remove } from 'fs-extra'
import { buildOutput } from '@element-plus/build-utils'
import { pathRewriter, run } from '../utils'

export const generateTypesDefinitions = async () => {
  await run(
    'npx vue-tsc -p tsconfig.web.json --declaration --emitDeclarationOnly --declarationDir dist/types'
  )
  const typesDir = path.join(buildOutput, 'types', 'packages')
  const filePaths = await glob(`**/*.d.ts`, {
    cwd: typesDir,
    absolute: true,
  })
  const rewriteTasks = filePaths.map(async (filePath) => {
    const content = await readFile(filePath, 'utf8')
    await writeFile(filePath, pathRewriter('esm')(content), 'utf8')
  })
  await Promise.all(rewriteTasks)
  const sourceDir = path.join(typesDir, 'element-plus')
  await copy(sourceDir, typesDir)
  await remove(sourceDir)
}

打包类型文件

image.png

image.png

对于npx vue-tsc -p tsconfig.web.json --declaration --emitDeclarationOnly --declarationDir dist/types-p tsconfig.web.json指定要使用的编译配置文件,--declaration 指定生成声明文件,--emitDeclarationOnly表示只生成声明文件不会进行代码编译,--declarationDir dist/types指定了输出目录

然后运行pnpm buid,可以看到生成了对应的类型文件

image.png

重写类型文件

image.png

image.png

主要就是针对路径进行处理,把开发环境的路径处理成了打包之后的路径

重写类型文件之前 image.png 重写类型文件之后 image.png

可以看到这里import的是es文件目录下的类型文件,之前打包的es下面并没有对应的类型啊???其实后面还有处理,会把types的相关类型移动到对应目录下,需要往后面看 copyTypesDefinitions

提升package/element-plus的类型文件到上层

image.png

我们之前打包的配置 preserveModules 值为 true 时,不会有element-plus的目录结构了,下面的文件会提升到上层。

这里就是为了把类型提到上层,使得其和之前的组件打包的结构一致

提升前:image.png

提升后:image.png

copyTypesDefinitions

image.png

export const copyTypesDefinitions: TaskFunction = (done) => {
  const src = path.resolve(buildOutput, 'types', 'packages')
  const copyTypes = (module: Module) =>
    withTaskName(`copyTypes:${module}`, () =>
      copy(src, buildConfig[module].output.path, { recursive: true })
    )

  return parallel(copyTypes('esm'), copyTypes('cjs'))(done)
}

这是要把types下面的各种文件,copy到es和lib的相关文件下

image.png

❌
❌