- CMS
- 后端
- Web框架
【免费下载链接】OrchardCore
Orchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.
本篇指南围绕 OrchardCore 仓库中负责前端资源编译的核心工具——Asset Manager(资源管理器)展开,系统讲解其依赖环境要求、构建/监听/清理命令、三级 package.json 结构,以及Assets.json中 8 种动作(vite、sass、min、copy、concat、parcel、webpack、run)的完整配置与实战示例,并结合.scripts/assets-manager/下的实际源码揭示底层执行原理。读完本文,你将能够独立为任意模块或主题新增、构建、调试前端资源,并掌握yarn build/yarn watch/yarn host/yarn clean/yarn check/yarn lint/yarn dry-run等命令的用法与适用场景。
一、Asset Manager 是什么:模块与主题前端资源的统一编译入口
OrchardCore 是一个基于 ASP.NET Core 的模块化、多租户应用框架与 CMS。除了 C# 后端,大量模块(如 OrchardCore.Media、OrchardCore.Resources)和主题(如 TheAdmin、TheBlogTheme)还携带 SCSS、JavaScript、TypeScript、Vue 等前端资源。Asset Manager 正是负责编译这些资源的统一工具链:它以Assets.json文件为清单,在src/{OrchardCore.Modules,OrchardCore.Themes}/*/下自动发现各模块/主题的资源定义,再驱动 Vite、Parcel、Webpack、Sass 等打包器完成构建。
从源码结构看,整套工具链实现位于 .scripts/assets-manager 目录,其 package.json 声明为@orchardcore/assets-manager,入口为./build.mjs;根目录 package.json 通过"build": "assets-manager build"、"watch": "assets-manager watch"、"dry-run": "assets-manager dry-run"、"clean": "assets-manager clean"等脚本将其桥接为仓库级yarn命令。
资源发现机制
资产清单的定位逻辑在 assetGroups.mjs 中实现:
getAllAssetGroups()通过buildConfig("assetsLookupGlob")获取 glob,默认值为src/{Modules,Themes}/*/Assets.json(见 config.mjs),遍历仓库下所有模块/主题的Assets.json;- 每个清单用 JSON5 解析(支持注释与尾逗号),逐条展开为 "asset group"(资源组);
resolveAssetGroupPaths()会把source与dest解析为绝对路径,其中以node_modules开头的路径统一从**仓库根目录(workspace root)**解析,其余路径则相对于该Assets.json所在目录解析——这正是后面copy/concat/min动作中node_modules路径语义的由来。
默认 glob 与
parcelBundleOutput等关键配置都可以通过仓库根目录的build.config.mjs覆写(该文件未提交到仓库,由各开发者按需创建),见 config.mjs 的实现。
二、环境准备:Node.js 24.x 与版本检测机制
Node.js 版本要求
仓库通过根目录的 .node-version 文件将 Node.js 版本固定为24.x(当前仓库内容为24.20.0)。构建脚本会在启动时读取该文件并与当前process.versions.node比对,版本不匹配时弹出交互式选择:
1) Continue anyway 2) Abort 3) Install via fnm, Node.js 24.x and build 4) Install via Volta, Node.js 24.x and build推荐始终选择选项 1(继续)或选项 3(通过 fnm 安装)。该提示逻辑、fnm/Volta 的安装与 PATH 处理、以及 CI 环境(检测到CI/TF_BUILD/GITHUB_ACTIONS环境变量时直接报错退出)的完整实现见 build.mjs 开头的版本检测段落。构建脚本甚至会在磁盘上查找~/.local/share/fnm、~/.fnm、~/.volta/bin等已知安装目录并动态扩展 PATH,避免"装了但当前终端找不到"的经典问题。
在自动化/无交互环境下,可以用管道输入预置答案:
echo "1" | yarn build # 使用当前 Node.js 继续构建 echo "3" | yarn build # 通过 fnm 安装 Node 24 并构建安装依赖
若包缺失(如首次克隆仓库、或修改了任一package.json),使用 fnm 指定版本执行安装:
fnm exec --using 24.14.1 -- corepack yarn install注意:修改任何package.json(根目录、.scripts/assets-manager/、或某个模块的Assets/目录)之后都必须重新执行 install,否则新增依赖不会被解析。
三、构建命令速查:build / watch / host / clean
所有命令均从仓库根目录执行。CLI 的核心参数为:
-n/--name/--names:按Assets.json中的"name"字段精确筛选要构建的资源(支持逗号分隔多个);-t/--tag/--tags:按"tags"数组筛选(支持逗号分隔多个);-b/--bundle:仅构建带 bundle 入口的资源(内部会聚合为orchardcore-bundle)。
# 构建全部资源 echo "1" | yarn build # 按名称构建单个资源 echo "1" | yarn build -n media-app # 按名称构建多个资源 echo "1" | yarn build -n media-app,media-field # 按标签构建(例如所有带 admin 标签的资源) echo "1" | yarn build -t admin # 监听模式:保存即自动重新构建(开发模式) echo "1" | yarn watch -n media-app # 以打包器开发服务器托管(支持 HMR 热更新) echo "1" | yarn host -n media-app # 清理全部构建产物 echo "1" | yarn clean从 build.mjs 的源码可以看到这些参数的解析方式:minimist解析后,parseFilterValues会把-n/-t的值按空白与逗号拆分成数组,再分别对资源组做name精确匹配或tags交集匹配;watch与host任务强制要求通过-n指定资源(不允许按标签监听)。随后各组按action分派到vite.mjs/parcel.mjs/webpack.mjs/sass.mjs/copy.mjs/min.mjs/concat.mjs等子进程,组配置以 Base64 编码后通过ASSETS_MANAGER_ENCODED_GROUP环境变量传入,最后用concurrently并行执行并按order字段排序。
构建产物与提交约定
构建输出写入模块/主题自身的wwwroot/目录,并且必须提交到仓库。修改任何资源后,需要同时提交源文件与生成的wwwroot/文件——这是 OrchardCore 资源发布机制的一部分:运行中的站点直接静态托管wwwroot下的产物,无需在部署时重新构建前端。例如 OrchardCore.Media 的产物可见于 src/OrchardCore.Modules/OrchardCore.Media/wwwroot/Scripts(media2.js、media2.min.js、media-picker2.js等)。
四、三级 package.json 结构与依赖管理
整个仓库采用 Yarn workspaces 组织依赖,分为三个层级(见根目录 package.json 的workspaces字段与.scripts/assets-manager/package.json):
| 位置 | 作用 |
|---|---|
根目录package.json | Workspace 编排、版本解析、顶层脚本(build/watch/clean/lint/check) |
.scripts/assets-manager/package.json | 构建工具链本体(Vite、Parcel、Sass、Webpack、esbuild、lightningcss 等) |
src/.../Assets/package.json | 被捆绑进该模块/主题的运行时依赖 |
- 为模块添加运行时依赖:在模块的
Assets/目录下执行yarn add some-library@1.2.3,然后重新构建对应资源(echo "1" | yarn build -n asset-name)。运行时依赖会被打包进该模块产出的 bundle 中。 - 添加构建工具(影响所有资源):编辑 .scripts/assets-manager/package.json,然后运行
fnm exec --using 24.14.1 -- corepack yarn install。该文件目前内置的构建依赖包括vite@^8.1.5、parcel@2.13.3、sass@^1.85.1、webpack@5.94.0、esbuild@^0.28.1、lightningcss@1.29.1、postcss-rtlcss@5.6.0、@vitejs/plugin-vue@^6.0.8等,是理解各 action 能力边界的直接依据。
workspaces 还包含了src/Frontend/与.scripts/bloom/(Media 模块 Vue 应用共享的组件库,见 .scripts/bloom),说明部分前端工程是跨模块共享的。
五、Assets.json 清单格式与 8 种动作详解
每个带资源的模块/主题在其根目录都有一个Assets.json(例如 src/OrchardCore.Modules/OrchardCore.Media/Assets.json、src/OrchardCore.Themes/TheAdmin/Assets.json)。它是一个 JSON 数组,每个元素描述一个资源组:
action:构建动作类型(必填);name:唯一标识,也是-n命令行筛选用的名称(必填);source:源文件/目录/glob(字符串或字符串数组);dest:输出目录(可选,各动作有默认值);tags:字符串数组,供-t筛选(可选);order:构建顺序(可选,越小越先执行,见 build.mjs 中的排序逻辑);generateRTL:是否额外生成 RTL 样式(部分动作支持);options:透传给具体打包器的附加选项(如 parcel 的shouldContentHash)。
一个最简示例:
[ { "action": "vite", "name": "media-app", "source": "Assets/media-app/", "tags": ["admin", "js"] }, { "action": "sass", "name": "media-styles", "source": "Assets/scss/media.scss", "tags": ["admin", "css"] } ]以下按 .agents/skills/orchardcore-asset-manager/references/actions.md 与源码逐一展开 8 种动作。
5.1 vite —— Vue 应用与 TypeScript 项目
使用 Vite 打包,读取source目录下的vite.config.ts。适用于 Vue 应用与 TS 工程。
{ "action": "vite", "name": "media-app", "source": "Assets/media-app/", "tags": ["admin", "js"] }关键约束与行为:
source为包含vite.config.ts的根目录;输出位置取决于配置中的build.outDir;- Asset Manager 会自动注入
orchard-minify插件(见 vite.mjs 中plugins: [minifyPlugin()]),因此不要在vite.config.ts中设置build.minify; - 不要在 Vite 应用目录内放 workspace 级
package.json,应使用模块的Assets/package.json。
真实示例可见 media-picker/vite.config.ts:它设置了build.outDir指向模块wwwroot、build.minify: false(注释明确说明"Minification is handled by the asset-manager pipeline"),并通过lib模式输出Scripts/media-picker2.js。同目录的 media-gallery/vite.config.ts 展示了 Vue + Tailwind + RTL 的完整配置形态。
orchard-minify 插件的产物约定(见 plugins/vite-plugin-minify.mjs):对每个 JS chunk 用 esbuild、对 CSS 用 lightningcss 压缩,生成三件套:
file.js—— 压缩且带//# sourceMappingURL引用;file.min.js—— 压缩且不带sourcemap 引用;file.map—— source map。
资源清单(Resource Manifest)衔接:构建产物需要在 C# 侧注册为资源。以 ResourceManifestOptionsConfiguration.cs 为范本:
s_manifest .DefineScript("media-picker") .SetUrl("~/OrchardCore.Media/Scripts/media-picker2.min.js", "~/OrchardCore.Media/Scripts/media-picker2.js") .SetVersion("2.0.0") .SetAttribute("type", "module");SetUrl的第一参数为生产环境使用的压缩版(.min.js),第二参数为调试/开发使用的非压缩版(.js),OrchardCore 资源管理系统会按环境自动选择——这也解释了为何构建必须同时产出.js与.min.js两个文件。
5.2 sass —— SCSS 转译
将 SCSS 转译为 CSS,默认输出到wwwroot/Styles/,可用dest覆盖。
{ "action": "sass", "name": "admin-dashboard", "source": "Assets/scss/dashboard.scss", "tags": ["admin", "css"] }source:单个.scss文件或 glob(如Assets/trumbowyg/plugins/*/ui/sass/*.scss,见 OrchardCore.Resources 的 Assets.json);dest:可选输出目录(默认wwwroot/Styles/);- 支持
generateRTL: true额外生成 RTL 版本。
真实案例:TheAdmin 主题在 src/OrchardCore.Themes/TheAdmin/Assets.json 中定义了theme-theadmin、theme-theadmin-layout、theme-theadmin-login三个 sass 组,均设置generateRTL: true并输出到wwwroot/css/。
5.3 min —— JS/CSS 压缩
对 JS 或 CSS 文件做压缩,产出.min.js/.min.css与.map文件。
{ "action": "min", "name": "media-field", "source": "Assets/js/media-field.js", "dest": "wwwroot/Scripts/", "tags": ["admin", "js"] }source:文件或 glob;dest:可选输出目录。
这是 OrchardCore.Resources 中使用最频繁的动作之一(src/OrchardCore.Modules/OrchardCore.Resources/Assets.json),例如对node_modules/jquery/dist/jquery.js、node_modules/codemirror/...等数十个第三方库做压缩并输出到wwwroot/Scripts/codemirror/等路径。注意这里node_modules/从 workspace 根解析,与copy动作一致。
5.4 copy —— 原样拷贝(常用于 node_modules 依赖)
将文件(常来自node_modules)复制到wwwroot/。不监听文件变化。
{ "action": "copy", "name": "bootstrap-5", "source": [ "node_modules/bootstrap/dist/css/bootstrap.min.css", "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" ], "dest": "wwwroot/Vendor/bootstrap-5/", "tags": ["resources"] }source:字符串或字符串数组/glob;dest:输出目录;若省略,则根据tags与第一个源文件扩展名推断;node_modules/路径从workspace 根解析(实现见 assetGroups.mjs 对src.startsWith("node_modules")的处理)。
真实案例:OrchardCore.SignalR 通过 src/OrchardCore.Modules/OrchardCore.SignalR/Assets.json 将../../../node_modules/@microsoft/signalr/dist/browser/*.js拷入wwwroot/Scripts/;OrchardCore.Resources 则用 copy 把 Bootstrap、FontAwesome、jQuery 各历史版本、Monaco 编辑器等资源分发到wwwroot/Vendor/下的独立版本目录。
5.5 concat —— 多文件拼接
将多个文件按顺序物理拼接为一个文件,不经过打包器。
{ "action": "concat", "name": "media", "source": [ "node_modules/blueimp-file-upload/js/jquery.fileupload.js", "Assets/js/app/Shared/uploadComponent.js" ], "dest": "wwwroot/Scripts" }source:文件数组(不支持 glob);- 所有
node_modules/路径从 workspace 根解析——因此所有模块必须就同一依赖版本达成一致,或者使用NPM 别名(aliasing),例如"bootstrap-4.6.1": "npm:bootstrap@4.6.1"(仓库中正是这样为不同模块并存的旧版本依赖命名的)。
真实案例:OrchardCore.Resources 用 concat 将 trumbowyg 的 20 余个插件 JS 文件拼接为单个wwwroot/Scripts/trumbowyg/产物。
5.6 parcel —— 零配置打包
运行 Parcel 打包器,零配置,从Assets/package.json读取入口。
{ "action": "parcel", "name": "datasource-wrapper", "source": "Assets/Scripts/datasource-wrapper.js", "dest": "wwwroot/datasource-wrapper", "tags": ["js"] }source:入口文件;dest:输出目录(必填,Parcel 会产出多个文件);- 若删除输出后缓存过期,先运行
yarn clean再构建。
真实案例:OrchardCore.Media 的 Assets.json 中media-editors、media-profiles-index、media-profile-editor均为 parcel 组,输出到wwwroot/Scripts/media-editors/等目录;OrchardCore.Resources 则用 parcel 构建 Monaco 编辑器的 ESM 版本及其 5 个 worker 入口(monaco-esm),并通过options.shouldContentHash: false关闭内容哈希。
5.7 webpack —— Webpack 打包
使用配置文件运行 Webpack。
{ "action": "webpack", "name": "graphiql", "config": "/Assets/webpack.config.js", "tags": ["admin", "js"] }config:webpack.config.js的路径(相对于模块根目录)。
该动作是保留给仍依赖 Webpack 的既有前端工程的过渡通道,当前仓库中 GraphQL 相关前端即采用此方式。
5.8 run —— 运行任意命令
通过 Asset Manager 调度任意 shell 命令,用于自定义构建脚本。
{ "action": "run", "name": "custom-app", "source": "Assets/custom-app", "scripts": { "build": "yarn build", "watch": "yarn start" } }source:命令的工作目录;scripts:把管道命令(build/watch/host)映射为要执行的 shell 命令。
在 build.mjs 中,run 动作的实现是cd ${group.source} && ${script},且只在scripts中恰好存在当前任务名对应的脚本时才执行,否则跳过并给出提示。
六、各动作与任务的兼容矩阵
从 build.mjs 的分派逻辑可以整理出动作与任务的兼容关系(构建时并行执行、按order排序,见代码中concurrently部分):
| 动作 | build | watch | host | dry-run | copy | clean |
|---|---|---|---|---|---|---|
| vite | ✅ | ✅(build.watch模式) | ✅(dev server + HMR) | — | — | ✅ |
| parcel | ✅ | ✅ | ✅ | — | — | ✅ |
| webpack | ✅ | ✅ | ✅ | — | — | ✅ |
| sass | ✅ | ✅ | — | — | — | ✅ |
| copy | ✅ | —(提示使用 copy/build) | — | ✅ | ✅ | ✅ |
| min | ✅ | — | — | ✅ | — | ✅ |
| concat | ✅ | — | — | ✅ | — | ✅ |
| run | 视scripts[task]是否存在 | 视scripts.watch | 视scripts.host | — | — | — |
七、代码质量命令:check / lint / dry-run
这三个命令不经过 Node.js 版本提示,可直接执行:
# 对所有 Vue/TS 文件做类型检查(vue-tsc --noEmit) yarn check # 对整个仓库执行 ESLint yarn lint # 只对某个目录执行 ESLint yarn lint src/OrchardCore.Modules/OrchardCore.Media/Assets/ # 预览将要构建/拷贝的文件清单(不写入任何文件) echo "1" | yarn dry-run echo "1" | yarn dry-run -n media-app # 限定单个资源yarn check:底层是vue-tsc --noEmit(根 package.json 中定义为yarn workspaces foreach -A -v -v run check),用于在提交 TS/Vue 改动前捕获类型错误。不支持-n;若要检查特定模块,直接指向该模块的 tsconfig:yarn vue-tsc --noEmit -p src/OrchardCore.Modules/OrchardCore.Media/Assets/media-app/tsconfig.json。yarn lint:仓库级 ESLint(根目录 eslint.config.mjs),可传文件/目录参数限定范围。yarn dry-run:仅打印构建计划(拷贝目标、输出路径)而不触碰文件,非常适合在新增Assets.json条目后先验证路径正确性再执行首次真实构建。
八、常见问题排查
| 问题 | 解决方案 |
|---|---|
Cannot find package '@tailwindcss/vite' | 运行fnm exec --using 24.14.1 -- corepack yarn install重新安装依赖 |
| Parcel 缓存过期(删除输出后仍报错) | 先echo "1" | yarn clean,再重新构建 |
| 改动未生效 | 确认生成的wwwroot/文件确实更新;未更新则重新构建 |
误跑了全仓库构建(-n值写错,例如用了 package.json 的 name 而不是Assets.json的"name") | 立即执行git status --short检查;用git checkout --还原目标模块/主题之外的所有改动路径,再提交 |
| 改了源码但运行中的应用仍复现旧 bug | 忘记重新构建了。对比时间戳:stat -c '%y' <source>.ts与stat -c '%y' wwwroot/Scripts/<bundle>.js,若 bundle 更旧,运行yarn build -n <asset-name> |
关于-n的常见误解
-n匹配的是Assets.json中的"name"字段(如media-gallery、theme-theadmin),不是package.json 中的包名。以 OrchardCore.Media 为例,其 Assets.json 中的合法-n取值是media-gallery、media-picker、media-editors、media-profiles-index、media-profile-editor。若误用了 package.json 的 name,筛选结果为空,会导致意外构建全仓库资源,务必留意。
九、为模块新增一个前端资源的完整流程
综合以上内容,给模块(或主题)新增前端资源的标准步骤为:
- 编写源码:在模块的
Assets/目录下创建源文件(如Assets/js/my-widget.js或Assets/scss/my-widget.scss)。若需要运行时依赖,先在Assets/下yarn add <package>@<version>; - 登记
Assets.json:在模块根目录的Assets.json中追加一个条目,按需选择action(Vue 应用用vite、TS 入口用parcel、样式用sass、第三方库分发用copy/min等),并填写唯一的name与tags; - 验证路径:
echo "1" | yarn dry-run -n my-widget预览构建计划,确认 source/dest 无误; - 构建:
echo "1" | yarn build -n my-widget,检查wwwroot/下的产物(.js/.min.js/.map或.css/.min.css); - 在 C# 侧注册资源:参照 ResourceManifestOptionsConfiguration.cs,用
DefineScript/DefineStyle+SetUrl("...min.js", "...js")注册,供 Razor/Liquid 视图按环境引用压缩版或调试版; - 质量检查:
yarn check(类型)与yarn lint <assets-dir>(代码规范); - 提交:将源码与生成的
wwwroot/文件一并提交到仓库。
十、总结:Asset Manager 的定位与边界
Asset Manager 将 OrchardCore 的前端构建收敛为"一份Assets.json清单 + 一组 yarn 命令"的简单模型:开发者不需要逐模块配置 Vite/Parcel/Webpack,也不需要维护各自的构建脚本;构建工具链集中在 .scripts/assets-manager 一处(vite.mjs、parcel.mjs、webpack.mjs、sass.mjs、copy.mjs、min.mjs、concat.mjs、clean.mjs、output.mjs、assetGroups.mjs、config.mjs),产物统一落入各模块/主题的wwwroot/并随仓库提交。需要强调的是,本文描述的版本、命令与配置均以当前仓库内容为准:Node.js 版本由 .node-version 固定,构建依赖版本见 .scripts/assets-manager/package.json,各动作的权威行为则分别体现在 build.mjs 与动作参考文档 actions.md 中——当两者冲突时,以源码为准。
- CMS
- 后端
- Web框架
【免费下载链接】OrchardCore
Orchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.
相关推荐
OrchardCore Assets.json Action 全解析:Vite / Sass / Parcel / Webpack 等 8 种前端资源构建动作详解
OrchardCore Assets.json Action 全解析:Vite / Sass / Parcel / Webpack 等 8 种前端资源构建动作详
CMS后端Web框架LTE-Cell-Scanner进阶技巧:OpenCL平台配置与性能优化实用指南
LTE Cell Scanner进阶技巧:OpenCL平台配置与性能优化实用指南 LTE Cell Scanner是一款功能强大的开源LTE基站扫描工具,它支持
通信智能硬件ComfyUI-WanVideoWrapper实战指南:从零构建专业级AI视频生成工作流
ComfyUI WanVideoWrapper实战指南:从零构建专业级AI视频生成工作流 你是否曾经想过,能否用AI将一张普通的照片变成会说话的虚拟主播?或者让
人工智能大模型媒体生成
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考