Frappe ops frontend build
Skill Impertio-Studio/Frappe_Claude_Skill_Package/skills/source/ops/frappe-ops-frontend-build
60 deterministic Claude AI skills for Frappe Framework & ERPNext v14-v16 development and operations
npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-ops-frontend-buildAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
What its author says it does
Copied from the file, not written here
Use when configuring frontend asset bundling, migrating from build.json (v14) to esbuild (v15+), or troubleshooting SCSS/CSS compilation. Prevents build failures from mixing v14 and v15 build systems and misconfigured asset pipelines. Covers esbuild configuration (v15+), build.json (v14), asset bundling, SCSS compilation, bundle.js setup, bench build flags. Keywords: esbuild, build.json, frontend build, SCSS, CSS, asset bundling, bench build, bundle.js, webpack, build error, assets not loading, CSS not updating, JS not compiling, bench build fails..
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
10.7 KB, as published. Nobody here has run it
Frontend Build System
Complete reference for Frappe's frontend asset bundling pipeline, from build configuration to production optimization.
Versions: v14 (build.json) / v15+ (esbuild)
Quick Reference: Build Commands
| Task | Command |
|---|---|
| Build all apps | bench build |
| Build specific app | bench build --app myapp |
| Build multiple apps | bench build --apps frappe,erpnext |
| Production build (minified) | bench build --production |
| Force rebuild | bench build --force |
| Watch mode (auto-rebuild) | bench watch |
| Hard link assets | bench build --hard-link |
Decision Tree: Build System Selection
Which build system?
├── Frappe v14?
│ └── build.json — Concatenation-based bundling
├── Frappe v15+?
│ └── esbuild — ES module bundling with *.bundle.* convention
└── Migrating v14 → v15?
└── Replace build.json with *.bundle.* files in public/
Build Pipeline Overview
v15+ (esbuild): Current System
The v15+ build system uses esbuild for fast ES module bundling. It automatically discovers bundle entry points by scanning the public/ directory for files matching *.bundle.{js|ts|css|scss|sass|less|styl}.
How it works:
bench buildscans each app'spublic/directory recursively- Files matching
*.bundle.*are treated as entry points - esbuild compiles, bundles, and optionally minifies each entry point
- Output goes to
assets/dist/[app]/js/orassets/dist/[app]/css/ - Filenames include content hashes for cache-busting:
main.bundle.HASH.js
Supported file types:
.js— ES6 modules with import/export.ts— TypeScript.vue— Vue single-file components.css— Standard CSS.scss/.sass— SASS/SCSS stylesheets.less— Less stylesheets.styl— Stylus stylesheets
v14 (build.json): Legacy System
The v14 system uses build.json in the app root to define concatenation rules.
{
"js/myapp.min.js": [
"public/js/file1.js",
"public/js/file2.js"
],
"css/myapp.min.css": [
"public/css/style1.css",
"public/css/style2.css"
]
}
NEVER use build.json in v15+ — it is ignored by the esbuild pipeline.
Bundle Entry Points [v15+]
Creating a Bundle
Place files in your app's public/ directory with the .bundle. naming convention:
myapp/
└── public/
├── js/
│ └── myapp.bundle.js # → dist/myapp/js/myapp.bundle.HASH.js
├── css/
│ └── myapp.bundle.scss # → dist/myapp/css/myapp.bundle.HASH.css
└── components/
└── widget.bundle.js # → dist/myapp/js/widget.bundle.HASH.js
Bundle File Content
// myapp/public/js/myapp.bundle.js
import { createApp } from "vue";
import MyComponent from "./components/MyComponent.vue";
// ES6 imports are resolved by esbuild
import "../css/myapp.bundle.scss";
// npm packages (installed via yarn) can be imported directly
import dayjs from "dayjs";
createApp(MyComponent).mount("#myapp-root");
Output Mapping
| Input | Output |
|---|---|
public/js/main.bundle.js | assets/dist/[app]/js/main.bundle.[hash].js |
public/css/style.bundle.scss | assets/dist/[app]/css/style.bundle.[hash].css |
public/deep/nested/file.bundle.ts | assets/dist/[app]/js/file.bundle.[hash].js |
hooks.py Asset Inclusion
Desk Assets (Backend Interface)
# hooks.py — loads in /app (Desk)
app_include_js = "myapp.bundle.js"
app_include_css = "myapp.bundle.css"
# Multiple files
app_include_js = ["myapp.bundle.js", "extra.bundle.js"]
app_include_css = ["myapp.bundle.css", "extra.bundle.css"]
Portal Assets (Public Website)
# hooks.py — loads on web pages (portal)
web_include_js = "myapp-web.bundle.js"
web_include_css = "myapp-web.bundle.css"
Page-Specific Assets
# hooks.py — loads on specific Desk pages
page_js = {"page_name": "public/js/custom_page.js"}
Web Form Assets (Standard Web Forms Only)
# hooks.py — loads on specific Web Forms
webform_include_js = {"ToDo": "public/js/custom_todo.js"}
webform_include_css = {"ToDo": "public/css/custom_todo.css"}
Critical Rules
- ALWAYS use the bundle filename (not the full path) in hooks.py for v15+
- NEVER include the hash in hooks.py — Frappe resolves the hashed filename automatically
- ALWAYS rebuild after changing hooks.py:
bench build --app myapp - Multiple apps can define the same hooks — assets accumulate across all installed apps
Including Assets in Templates
Jinja Helpers
<!-- Include script with correct hash -->
{{ include_script("myapp.bundle.js") }}
<!-- Include stylesheet with correct hash -->
{{ include_style("myapp.bundle.css") }}
<!-- Get path string only (no HTML tag) -->
<script src="{{ bundled_asset('myapp.bundle.js') }}"></script>
Lazy Loading in Desk
// Load asset on demand (returns Promise)
frappe.require("myapp.bundle.js", () => {
// Asset loaded, initialize component
myapp.init();
});
// Multiple assets
frappe.require(["widget.bundle.js", "widget.bundle.css"], () => {
// Both loaded
});
SCSS/CSS Compilation
SCSS Bundle Example
// myapp/public/css/myapp.bundle.scss
// Import Frappe variables (available in all apps)
@import "frappe/public/scss/variables";
// Import partials (NOT bundles — no .bundle. in name)
@import "./components/header";
@import "./components/sidebar";
.myapp-container {
padding: var(--padding-lg);
background: var(--bg-color);
}
Partial Files
Partials (files starting with _ or without .bundle. in the name) are NOT compiled as entry points. They are only included via @import:
public/css/
├── myapp.bundle.scss # Entry point — compiled
├── _variables.scss # Partial — imported only
└── components/
├── _header.scss # Partial — imported only
└── _sidebar.scss # Partial — imported only
Development Workflow
Watch Mode [v15+]
# Auto-rebuild on file changes
bench watch
- Watches all apps'
public/directories for changes - Rebuilds only affected bundles (incremental)
- Desk auto-reloads when assets change (if
live_reloadis enabled)
Enabling Live Reload
# Via config
bench set-config -g live_reload true
# Via environment variable
export LIVE_RELOAD=1
Development vs Production Build
| Feature | Development (bench build) | Production (bench build --production) |
|---|---|---|
| Minification | No | Yes |
| Source maps | Yes | No |
| Bundle size | Larger | Optimized |
| Build speed | Fast | Slower |
Frappe UI (Vue.js) Custom Pages [v15+]
Setting Up a Vue Page
// myapp/public/js/mypage.bundle.js
import { createApp } from "vue";
import { FrappeUI } from "frappe-ui";
import App from "./App.vue";
const app = createApp(App);
app.use(FrappeUI);
app.mount("#myapp-page");
Registering the Page
# Create a Page DocType or use www/ for web pages
# The bundle loads via hooks.py or include_script()
npm Dependencies
# Install from app directory
cd apps/myapp
yarn add vue frappe-ui dayjs
Dependencies are resolved by esbuild from node_modules/ during build.
Common Build Errors and Fixes
Error: "Could not resolve module"
ERROR: Could not resolve "some-package"
Fix: Install the missing npm package:
cd apps/myapp && yarn add some-package
Error: "No bundle entry points found"
Fix: Ensure files use the *.bundle.* naming convention and are in the public/ directory.
Error: Stale Assets After Deployment
Fix: Force rebuild with cache clear:
bench build --force
bench clear-cache
Error: CSS Not Updating
Fix: Check that SCSS files import correctly and the entry point has .bundle. in the name:
bench build --app myapp --force
Error: "build.json" Ignored in v15
Fix: Migrate to *.bundle.* entry points. build.json is a v14-only feature.
Asset Optimization for Production
Pre-Deployment Checklist
- Build with production flag:
bench build --production - Verify bundle sizes: Check
assets/dist/for unexpectedly large files - Use lazy loading: Split rarely-used features into separate bundles loaded via
frappe.require() - Minimize hook includes: Only include essential assets in
app_include_js/css - Use CSS variables: Leverage Frappe's built-in CSS custom properties instead of duplicating styles
Bundle Splitting Strategy
public/
├── js/
│ ├── myapp.bundle.js # Core — loaded on every page via hooks
│ ├── report-widget.bundle.js # Lazy — loaded only on report pages
│ └── chart-tools.bundle.js # Lazy — loaded only when charts needed
└── css/
├── myapp.bundle.scss # Core — loaded on every page via hooks
└── print.bundle.scss # Lazy — loaded only for print views
Version Differences
| Feature | v14 | v15+ |
|---|---|---|
| Build system | build.json | esbuild |
| Entry point convention | Defined in JSON | *.bundle.* auto-discovery |
| TypeScript support | No | Yes |
| Vue SFC support | No | Yes |
| SCSS compilation | Via build pipeline | Via esbuild |
| Watch mode | bench watch | bench watch (faster) |
| Live reload | Manual | Automatic (configurable) |
| Source maps | Limited | Full support |
| Tree shaking | No | Yes |
| npm imports | Requires manual bundling | Direct ES6 imports |
Reference Files
| File | Contents |
|---|---|
| examples.md | Complete build configuration examples |
| anti-patterns.md | Common build mistakes and fixes |