UNPKG

waw

Version:

**waw** is a minimal Node.js (≥ 24) platform that loads **modules** and either:

316 lines (211 loc) 8.75 kB
# waw Framework **waw** is a minimal Node.js (≥ 24) platform that loads **modules** and either: - executes a **CLI command** exposed by modules (`cli.js`), or - boots the **runtime** by sequentially loading each module’s `index.js`. The core is intentionally small. It does not implement application frameworks, routing, databases, generators, or business logic — modules do. --- ## Philosophy - **Tiny core** — only module discovery, dependency install, ordering, CLI dispatch, runtime bootstrap - **Modules first** — all behavior comes from modules - **Explicit over magic** — behavior is defined by files (`module.json`, `cli.js`, `index.js`) - **Sync by default** — dependency installation and module load preparation are deterministic If a feature is not implemented by a module, it does not exist. --- ## Requirements - **Node.js ≥ 24** - `npm` available in `PATH` --- ## Installation ```sh npm install -g waw ```` --- ## Project structure (minimal) ```txt project/ ├── config.json ├── server.json (optional) ├── server/ (default local modules folder; configurable) │ ├── my-module/ │ │ ├── module.json │ │ ├── index.js (optional runtime entry) │ │ └── cli.js (optional CLI commands) ``` --- ## How waw works ### 1) Configuration & module discovery On startup, waw reads config from (merged, later wins): 1. Global `waw/config.json` 2. Global `waw/server.json` 3. Project `./config.json` 4. Project `./server.json` Then it discovers modules: * **Local modules**: directories under `/<project>/<config.server>` * If `config.server` is not set and a `server/` folder exists, it uses `"server"`. * **Global modules**: declared in `config.modules` and fetched into the globally installed waw package under `waw/server/<name>`. Global module fetch behavior: * `config.modules` is interpreted as a map of `{ <moduleName>: <orgKey> }`. * Supported `orgKey` values are: * `waw``https://github.com/WebArtWork/waw-{NAME}.git` * `itkp``git@github.com:IT-Kamianets/waw-{NAME}.git` * If a required global module directory is missing, waw performs a **destructive force sync** of that repo into `waw/server/<name>`. Fallback behavior: * If no modules are found at all, waw force-syncs and loads a global module named **`core`** from the `waw` org pattern. Project-type auto modules: * If the project contains one of these marker files: * `angular.json` → loads global module `angular` * `react.json` → loads global module `react` * `vue.json` → loads global module `vue` * `wjst.json` → loads global module `wjst` * Each is fetched from the `waw` org pattern if missing. > Note: In this repository, the loader contains only these mechanics. Any application/framework behavior must be implemented by modules. --- ### 2) Module definition (`module.json`) Each module is a folder containing `module.json`. Supported fields used by this loader include: * `dependencies`: an object of npm dependencies to install into the module directory * `before` / `after`: ordering constraints (string or array; supports `"*"`) * `priority`: numeric priority used only when ordering constraints form a cycle The loader normalizes and augments module metadata at load time: * `m.__root` / `m.rootPath`: absolute path to the module folder * `m.__name`: directory name (module name) * `m.__global`: boolean whether module is global * `m.files`: snapshot list of files under the module directory (excluding `node_modules` and `.git`) --- ### 3) Dependencies Modules may declare npm dependencies inside `module.json`: ```json { "dependencies": { "express": "^4.19.0" } } ``` Behavior: * Dependencies are installed **synchronously** during module loading. * Installation is performed into `<moduleRoot>/node_modules`. * waw installs all declared deps in a single `npm i` call using: * `--no-save --no-package-lock --no-audit --no-fund --loglevel=error` * `--legacy-peer-deps` * The loader checks installed versions with a small semver matcher supporting: * exact `x.y.z` * `^x.y.z`, `~x.y.z` * `>=x.y.z` * `*` / `latest` No `package.json` is created inside modules by the loader. --- ### 4) Module ordering (`before` / `after`) Modules may define ordering rules in `module.json`: ```json { "before": "*" } ``` ```json { "after": "core", "before": "*" } ``` Rules: * `before` / `after` accept a string or an array * `"*"` means “all modules except those explicitly constrained in the opposite direction” * Ordering is a **stable topological sort** based on discovery order * If constraints produce a cycle, waw falls back to sorting by `priority` (descending) --- ### 5) CLI commands (`cli.js`) A module may provide CLI commands by including a file named **exactly** `cli.js`. Example: ```js // cli.js module.exports.love = function (waw) { console.log("waw loves you :)"); process.exit(0); }; ``` Run: ```sh waw love ``` Dispatch rules: * waw reads the raw command from `process.argv[2]`. * CLI files are scanned **from the last module to the first** (later modules override earlier ones). * Only files with base name **`cli.js`** are considered (not `*.cli.js`). * Matching is attempted in this order: 1. exact key match with raw command (`ex[cmdRaw]`) 2. exact key match with lowercased command (`ex[cmd]`) 3. case-insensitive scan of exported keys * `cli.js` may export: * an object of command functions, and/or * a function `(cmdRaw, waw) => boolean|void` * If it returns `false`, it is treated as “not handled” and waw continues scanning. * Any other return value (or no return) is treated as handled. When a command is executed, the command receives a context object (see below) and the process is expected to exit if appropriate. --- ### 6) Runtime (`index.js`) If no CLI command is handled, waw starts the runtime using **nodemon**. Nodemon runs `util.runtime.js` and watches: * Project: * `<project>/server` * `<project>/angular.json` * `<project>/react.json` * `<project>/vue.json` * `<project>/config.json` * Global waw install: * `<waw>/server` * `<waw>/config.json` When runtime starts, waw sequentially loads each module’s `index.js`: * If `<moduleRoot>/index.js` exists and exports a function, it is called as `await func(waw)`. * Errors are caught and printed; loading proceeds to the next module. --- ## The `waw` context Every CLI command and runtime module receives a shared `waw` object containing: ### Core properties * `argv` — CLI args after `waw` (`process.argv.slice(2)`) * `projectPath` — current working directory * `modulesPath` — resolved modules directory in the project (defaults to `<project>/server`) * `wawPath` — directory of the globally installed `waw` package * `wawModulesPath``<wawPath>/server` * `configPath` / `configServerPath` — project config file paths * `wawConfigPath` / `wawConfigServerPath` — global config file paths * `config` — merged config snapshot (global + project) * `projectType` — one of: `angular | react | vue | wjst | waw | ""` ### FS helpers * `exists(...parts)` * `isFile(...parts)` * `isDir(...parts)` * `ensureDir(...parts)` * `rm(...parts)` * `readText(path, fallback?)` * `writeText(path, data)` * `readJson(path, fallback?)` * `writeJson(path, obj, pretty = true)` * `readWrite(fromPath, toPath, replaceMap)` ### Utilities * `terminal` — interactive CLI helper (ask/confirm/choose + spinner) * `git` — git workflows utility (init, attach, forceSync, publish, etc.) ### Events * `emit(event, ...args)` * `on(event, fn)` * `once(event, fn)` * `off(event, fn)` ### Extra fields injected by the loader When running CLI commands, waw injects these additional fields: * `modules` — ordered list of loaded modules * `module` — current module object * `module_root` — current module root path * `module_config` — current module config object (`module.json` content + loader metadata) --- ## What waw is not * Not a backend framework * Not tied to Express/MongoDB/etc. * Not a generator by default * Not async-heavy * Not opinionated Those belong in modules. --- ## Contributing Contributions are welcome: * Improve the loader (keep it small) * Improve documentation * Create modules * Report bugs See `CONTRIBUTING.md`. --- ## License MIT © Web Art Work