Skip to content
AI & Agents
Skill

/frappe-ops-frontend-build

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+),

From plugin
frappe-claude-skill-package
17961 skills
Install
$ npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-ops-frontend-build --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/frappe-ops-frontend-build

Context preview

The summary Claude sees to decide when to auto-load this skill.

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+),

SKILL.md

frappe-ops-frontend-build.SKILL.md
name: frappe-ops-frontend-build
description: >
  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..
license: MIT
compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16."
metadata:
  author: OpenAEC-Foundation
  version: "2.0"

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:**

1. `bench build` scans each app's `public/` directory recursively 2. Files matching `*.bundle.*` are treated as entry points 3. esbuild compiles, bundles, and optionally minifies each entry point 4. Output goes to `assets/dist/[app]/js/` or `assets/dist/[app]/css/` 5. 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

Read more
Ships withfrappe-claude-skill-package

60 deterministic Claude AI skills for Frappe Framework & ERPNext v14-v16 development and operations

Get the whole plugin
Stats
178
Stars
53
Forks
Maintained
Maintenance
Python
Language
2mo ago
Last commit
8mo ago
Created
14d ago
Added

Repo: Impertio-Studio/Frappe_Claude_Skill_Package