bzd-nodejs
How to write, build, test, and contribute Node.js/JavaScript/Vue code in the bzd monorepo
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
How to write, build, test, and contribute Node.js/JavaScript/Vue code in the bzd monorepo
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
How to run, test, build, type-check, format, and manage Python dependencies in the bzd monorepo
Core software design principles — ALWAYS load this before planning and before writing any implementation code
How to build, test, run, and maintain Bazel targets in the bzd monorepo — always load this before invoking any ./tools/bazel command
How to read, write, and wire BDL (Bzd Description Language) files and their Bazel rules in the bzd monorepo — load this before touching any .bdl file or bdl_* Bazel rule
How to write, build, test, and contribute C++ code in the bzd monorepo
How to build/test esp32/esp32s3/etc targets in the bzd monorepo.
| name | bzd-nodejs |
| description | How to write, build, test, and contribute Node.js/JavaScript/Vue code in the bzd monorepo |
| compatibility | opencode |
.js) — no .mjs, no .cjs for new code./tools/bazel — use @bzd_rules_nodejs//nodejs:defs.bzl custom macros, never raw nodejs_binarybzd_rules_nodejs) — never invoke system node or npm directlypnpm install or npm install manually)bzd_nodejs_web_binary)#bzd/ resolves to the monorepo root — always use this for cross-package importscamelCase for functions and variables (repo-wide convention)| Path | Purpose |
|---|---|
nodejs/core/ | Core runtime libraries: exception, log, format, cache, router, http, rest, websocket, etc. |
nodejs/core/tests/ | Unit tests for core libraries (*_test.js) |
nodejs/db/ | Database abstractions: key-value store, storage, timeseries |
nodejs/email/ | Email integration (SendGrid, etc.) |
nodejs/payment/ | Payment integration (Stripe) |
nodejs/styles/ | SCSS design tokens and themes |
nodejs/utils/ | Utility functions: array, pathlib, regexpr, object, query, etc. |
nodejs/vue/ | Vue 3 layer: apps, components, directives, router, plugins |
nodejs/vue/apps/ | Base frontend/backend app scaffolding (frontend.js, backend.js) |
nodejs/vue/apps/example/ | Minimal reference app (start here for a new app) |
nodejs/vue/components/ | Reusable Vue components: graph, layout, logger, menu, modal, terminal, etc. |
nodejs/vue/directives/ | Custom Vue directives: loading, resize, tooltip |
tools/nodejs/ | npm dependency lockfiles: requirements.in, requirements.json |
apps/ | Full application targets (each app has a frontend/ and/or backend/) |
# Test all Node.js targets
./tools/bazel test //nodejs/...
# Test a specific target
./tools/bazel test //nodejs/core/tests:format
./tools/bazel test //nodejs/core/tests:router
# Show full test output even on success
./tools/bazel test --test_output=all //nodejs/core/tests:format
# Build a backend binary
./tools/bazel build //nodejs/vue/apps/example:backend
# Build a frontend bundle (produces a .bundle directory)
./tools/bazel build //nodejs/vue/apps/example:frontend
# Run a backend server
./tools/bazel run //nodejs/vue/apps/example:backend
# Test all app targets
./tools/bazel test //apps/...
Any frontend-related change should be tested visually, using screenshot to ensure the change has been made correctly.
All Node.js targets use custom rules from @bzd_rules_nodejs//nodejs:defs.bzl.
load("@bzd_rules_nodejs//nodejs:defs.bzl",
"bzd_nodejs_library",
"bzd_nodejs_binary",
"bzd_nodejs_test",
"bzd_nodejs_web_binary",
"bzd_nodejs_web_library",
"bzd_nodejs_requirements_compile",
)
| Rule | Purpose |
|---|---|
bzd_nodejs_library | Reusable library (srcs, deps, packages) |
bzd_nodejs_binary | Runnable Node.js server binary |
bzd_nodejs_test | Node.js test — runs under Mocha automatically |
bzd_nodejs_web_binary | Vite-bundled frontend app (produces a .bundle directory) |
bzd_nodejs_web_library | Vite-compiled UMD/CJS library |
bzd_nodejs_requirements_compile | Converts requirements.in → requirements.json lockfile |
bzd_nodejs_static | Generates static files from a Node.js script |
Key design: bzd_nodejs_binary and bzd_nodejs_test are macros that create a <name>.install target first (runs pnpm offline to populate node_modules), then the actual binary/test target.
#bzd/ PrefixThe #bzd/ import prefix resolves to the monorepo root. Use it for all cross-package imports (any import referencing a file outside the current package/directory).
// Cross-package import — ALWAYS use #bzd/
import ExceptionFactory from "#bzd/nodejs/core/exception.js";
import LogFactory from "#bzd/nodejs/core/log.js";
import { HttpClient } from "#bzd/nodejs/core/http/client.js";
import config from "#bzd/nodejs/vue/apps/config.json" with { type: "json" };
// Relative import — OK within the same package/directory
import Format from "./format.js";
import Router from "../router.js";
// npm package import — bare package name, no prefix
import { Command } from "commander/esm.mjs";
Rule of thumb:
./, ../)#bzd/.js (ES Modules) for all new code") — no single quotes(x) => x)camelCasePascalCasevalue_, helperMethod_())console.log: use LogFactory instead (see Logging section)tools/nodejs/.prettierrc.json){
"arrowParens": "always",
"bracketSpacing": true,
"printWidth": 120,
"semi": true,
"singleQuote": false,
"useTabs": true,
"vueIndentScriptAndStyle": true
}
All modules create their own typed exception class using ExceptionFactory. This is the standard error-handling mechanism — never throw raw Error objects.
import ExceptionFactory from "#bzd/nodejs/core/exception.js";
const Exception = ExceptionFactory("mymodule", "submodule");
// Throw an error
Exception.error("Something went wrong: {}", detail);
// Assert a condition
Exception.assert(value > 0, "Value must be positive, got {}", value);
// Assert equality (deep comparison — works on objects, arrays, sets)
Exception.assertEqual(result, expected);
// Assert a precondition (user input validation)
Exception.assertPrecondition(input !== null, "Input must not be null");
// Assert that an async block throws with a matching message
await Exception.assertThrowsWithMatch(async () => {
await riskyOperation();
}, "expected error substring");
// Wrap a caught Error with context
const e = Exception.fromError(caughtError, "Context: {}", contextInfo);
import LogFactory from "#bzd/nodejs/core/log.js";
const Log = LogFactory("mymodule");
Log.info("Server started on port {}", port);
Log.warning("Deprecated feature used: {}", feature);
Log.error("Operation failed: {}", error);
Log.debug("Processing item {}", item);
nodejs/<module>/my_feature.js:import ExceptionFactory from "#bzd/nodejs/core/exception.js";
const Exception = ExceptionFactory("mymodule", "myfeature");
export class MyFeature {
constructor(config) {
this.config_ = config;
}
doSomething(value) {
Exception.assert(value !== null, "Value must not be null");
return value * 2;
}
}
export default MyFeature;
nodejs/<module>/BUILD.bazel:load("@bzd_rules_nodejs//nodejs:defs.bzl", "bzd_nodejs_library")
bzd_nodejs_library(
name = "my_feature",
srcs = ["my_feature.js"],
visibility = ["//visibility:public"],
deps = [
"//nodejs/core:exception",
"//nodejs/core:log",
],
)
"//nodejs/<module>:my_feature"Tests use Mocha and are named <name>_test.js. Mocha globals (describe, it) are available automatically — no import needed.
import ExceptionFactory from "#bzd/nodejs/core/exception.js";
import MyFeature from "#bzd/nodejs/mymodule/my_feature.js";
const Exception = ExceptionFactory("test", "myfeature");
describe("MyFeature", () => {
describe("doSomething", () => {
it("doubles a positive number", () => {
const f = new MyFeature({});
Exception.assertEqual(f.doSomething(3), 6);
});
it("throws on null input", () => {
const f = new MyFeature({});
Exception.assertThrowsWithMatch(() => {
f.doSomething(null);
}, "Value must not be null");
});
});
describe("async behavior", () => {
it("resolves correctly", async () => {
const result = await f.asyncMethod();
Exception.assertEqual(result, expectedValue);
});
});
});
load("@bzd_rules_nodejs//nodejs:defs.bzl", "bzd_nodejs_test")
bzd_nodejs_test(
name = "my_feature",
srcs = ["my_feature_test.js"],
main = "my_feature_test.js",
deps = [
"//nodejs/mymodule:my_feature",
"//nodejs/core:exception",
],
)
*_test.js files (preferred pattern)load("@bzd_rules_nodejs//nodejs:defs.bzl", "bzd_nodejs_test")
test_srcs = glob(["*_test.js"])
[bzd_nodejs_test(
name = src.replace("_test.js", ""),
srcs = [src],
main = src,
deps = [
"//nodejs/mymodule:my_feature",
"//nodejs/core:exception",
],
) for src in test_srcs]
bzd_nodejs_test(
name = "my_test",
srcs = ["my_test.js"],
main = "my_test.js",
packages = ["@nodejs_deps//:some_package"],
deps = ["//nodejs/core:exception"],
)
apps/my_app/
├── BUILD.bazel
├── api.json # REST/WebSocket API schema
├── backend.js # Node.js HTTP server entry point
└── frontend/
├── BUILD.bazel
├── app.js # Frontend entry point
└── app.vue # Root Vue component
BUILD.bazel (backend)load("@bzd_rules_nodejs//nodejs:defs.bzl", "bzd_nodejs_binary")
bzd_nodejs_binary(
name = "backend",
srcs = ["backend.js", "api.json"],
args = [
"--static",
"$(rootpath //apps/my_app/frontend:frontend).bundle",
],
data = ["//apps/my_app/frontend:frontend"],
main = "backend.js",
deps = ["//nodejs/vue/apps:backend"],
)
BUILD.bazel (frontend)load("@bzd_rules_nodejs//nodejs:defs.bzl", "bzd_nodejs_web_binary")
bzd_nodejs_web_binary(
name = "frontend",
srcs = ["app.js", "app.vue"],
config_scss = "//nodejs/styles:default.scss",
main = "app.js",
deps = ["//nodejs/vue/apps:frontend"],
)
backend.js skeletonimport APIv1 from "#bzd/api.json" with { type: "json" };
import Backend from "#bzd/nodejs/vue/apps/backend.js";
const backend = await Backend.make(APIv1).useAuthentication().useLogger().setup();
await backend.start();
frontend/app.mjs skeletonimport APIv1 from "#bzd/api.json" with { type: "json" };
import Frontend from "#bzd/nodejs/vue/apps/frontend.js";
import App from "./app.vue";
const frontend = Frontend.make(App).useRest(APIv1.rest).useAuthentication().useLogger().setup();
frontend.mount("#app");
Vue components use .vue single-file component (SFC) format with <template>, <script>, and optionally <style>.
<template>
<div class="bzd-my-component">
<slot name="header"></slot>
<div class="bzd-my-component-content">{{ message }}</div>
</div>
</template>
<script>
import ExceptionFactory from "#bzd/nodejs/core/exception.js";
const Exception = ExceptionFactory("components", "mycomponent");
export default {
props: {
message: { type: String, required: true },
},
data() {
return {
value_: null,
};
},
methods: {
handleClick() {
Exception.assert(this.message, "Message must not be empty");
},
},
};
</script>
<style lang="scss">
.bzd-my-component {
display: flex;
}
</style>
load("@bzd_rules_nodejs//nodejs:defs.bzl", "bzd_nodejs_library")
bzd_nodejs_library(
name = "my_component",
srcs = ["my_component.vue"],
visibility = ["//visibility:public"],
deps = [
"//nodejs:vue",
"//nodejs/core:exception",
],
)
All npm packages are declared in tools/nodejs/requirements.in and locked in tools/nodejs/requirements.json.
Add the package name to tools/nodejs/requirements.in:
my-package
my-package=1.2.3
Regenerate the lockfile:
./tools/bazel run //tools/nodejs:requirements
Use the package in a BUILD file via the packages attribute:
bzd_nodejs_library(
name = "my_lib",
srcs = ["my_lib.js"],
packages = ["@nodejs_deps//:my_package"],
visibility = ["//visibility:public"],
)
Import in code using the bare package name (no #bzd/ prefix):
import { something } from "my-package";
import { Command } from "commander/esm.mjs";
# Run the sanitizer (Prettier + linting) on changed files
./tools/bazel run //:sanitizer
# Run on ALL files in the repo
./tools/bazel run //:sanitizer -- --all
# Check-only mode (report issues, no writes)
./tools/bazel run //:sanitizer -- --check --all
Prettier is applied to .mjs, .cjs, .js, .vue, .css, and .scss files.
To exclude a directory from sanitizer checks, place a .sanitizerignore file in that directory.
# Full quality gate (tests + sanitizer)
./quality_gate.sh
# Test all Node.js targets
./tools/bazel test //nodejs/...
# Test all app targets
./tools/bazel test //apps/...
# Sanitizer check only
./tools/bazel run //:sanitizer -- --check --all
Always run ./tools/bazel run //:sanitizer before committing.