From 40a379530ac7c75c9b268417f0386cafd903d779 Mon Sep 17 00:00:00 2001 From: Exelo Date: Sat, 6 Jun 2026 11:57:56 +0900 Subject: [PATCH 1/7] feat: add better readme in application schematics --- e2e/application.test.ts | 20 +++ src/libs/application/application.factory.ts | 1 + src/libs/application/application.options.d.ts | 5 + src/libs/application/application.schema.d.ts | 5 + src/libs/application/files/js/README.md | 165 ++++++++++++++++++ src/libs/application/files/ts/README.md | 165 ++++++++++++++++++ src/libs/application/schema.json | 5 + 7 files changed, 366 insertions(+) diff --git a/e2e/application.test.ts b/e2e/application.test.ts index d98d4cc..e60202c 100644 --- a/e2e/application.test.ts +++ b/e2e/application.test.ts @@ -35,6 +35,12 @@ describe("application schematic", () => { it("should not include pnpm config by default", () => { expect(packageJson).not.toHaveProperty("pnpm"); }); + + it("should not include init functions docs by default", () => { + expect(tree.readContent("/my-app/README.md")).not.toContain( + "│ └── init/ # Lifecycle hooks (before-init, after-run, …)", + ); + }); }); describe("with JavaScript", () => { @@ -137,4 +143,18 @@ describe("application schematic", () => { }); }); }); + + describe("with init functions", () => { + it("should generate specifications of init functions", async () => { + const tree = await runner.runSchematic("application", { + name: "init-functions-app", + initFunctions: true, + }); + expect(tree.files).toContain("/init-functions-app/README.md"); + const content = tree.readContent("/init-functions-app/README.md"); + expect(content).toContain( + "│ └── init/ # Lifecycle hooks (before-init, after-run, …)", + ); + }); + }); }); diff --git a/src/libs/application/application.factory.ts b/src/libs/application/application.factory.ts index 69972a7..4c80c93 100644 --- a/src/libs/application/application.factory.ts +++ b/src/libs/application/application.factory.ts @@ -37,6 +37,7 @@ const transform = (schema: ApplicationSchema): ApplicationOptions => { packageManager: schema.packageManager ?? DEFAULT_PACKAGE_MANAGER, server: schema.server ?? false, editor: schema.editor ?? false, + initFunctions: schema.initFunctions ?? false, }; }; diff --git a/src/libs/application/application.options.d.ts b/src/libs/application/application.options.d.ts index 81cadf7..71201d8 100644 --- a/src/libs/application/application.options.d.ts +++ b/src/libs/application/application.options.d.ts @@ -48,4 +48,9 @@ export interface ApplicationOptions { * Add editor dependencies */ editor: boolean; + + /** + * Add init functions to the application + */ + initFunctions: boolean; } diff --git a/src/libs/application/application.schema.d.ts b/src/libs/application/application.schema.d.ts index 61d5c61..60579c3 100644 --- a/src/libs/application/application.schema.d.ts +++ b/src/libs/application/application.schema.d.ts @@ -53,4 +53,9 @@ export interface ApplicationSchema { * Add editor dependencies */ editor: boolean; + + /** + * Add init functions to the application + */ + initFunctions: boolean; } diff --git a/src/libs/application/files/js/README.md b/src/libs/application/files/js/README.md index a916173..94b63de 100644 --- a/src/libs/application/files/js/README.md +++ b/src/libs/application/files/js/README.md @@ -1 +1,166 @@ # <%= name %> + +--- + +## Prerequisites + +- [Node.js](https://nodejs.org/) 18+ +- Package manager: **<%= packageManager %>** +- [NanoForge CLI](https://www.npmjs.com/package/@nanoforge-dev/cli): `<%= packageManager %> install -g @nanoforge-dev/cli` + +--- + +## Installation + +```bash +<%= packageManager %> install +``` + +--- + +## Development + +Start the game in watch mode with hot reload: + +```bash +nf dev +``` + +<% if (editor) { %>Open the visual editor (optional — lets you edit entities, components and systems without touching code): +```bash +nf editor +``` + +<% } %>--- + +## Project structure + +``` +<%= name %>/ +├── .nanoforge/ # NanoForge internal save files (do not edit manually) +├── client/ # Client-side code (runs in the browser) +│ ├── static/ # Static assets +│ ├── components/ # ECS components +│ ├── systems/ # ECS systems<% if (initFunctions) { %> +│ └── init/ # Lifecycle hooks (before-init, after-run, …)<% } %><% if (server) { %> +├── server/ # Server-side code +│ ├── static/ +│ ├── components/ +│ ├── systems/<% if (initFunctions) { %> +│ └── init/<% } %><% } %> +├── package.json +├── jsconfig.json +└── nanoforge.config.json # Game configuration +``` + +--- + +## Modifying the game + +### Add a component + +```bash +nf generate component --part client +``` + +A component is a plain class that holds data for an entity. Edit the generated file in `client/components/`: + +```js +export class MyComponent { + constructor(speed) { + this.speed = speed; + } +} + +export default MyComponent.name; +``` + +### Add a system + +```bash +nf generate system --part client +``` + +A system is a function that runs every tick and operates on entities that have specific components. Edit the generated file in `client/systems/`: + +```js +import { MyComponent } from "../components/my.component.js"; + +export const mySystem = (registry, ctx) => { + const entities = registry.getZipper([MyComponent]); + + entities.forEach((entity) => { + entity.MyComponent.speed += 1; + }); +}; + +export default mySystem.name; +``` + +<% if (server) { %> +### Server-side code + +Server components and systems live in `server/` and are imported from `@nanoforge-dev/ecs-server`. They follow the same pattern as the client side. + +Configure the server connection in `.env`: + +| Variable | Default | Description | +|---------------------------------------|-------------|---------------------------------| +| `NANOFORGE_CLIENT_SERVER_ADDRESS` | `127.0.0.1` | Server address (client-side) | +| `NANOFORGE_CLIENT_SERVER_TCP_PORT` | `4444` | TCP port the client connects to | +| `NANOFORGE_CLIENT_SERVER_UDP_PORT` | `4445` | UDP port the client connects to | +| `NANOFORGE_SERVER_LISTENING_TCP_PORT` | `4444` | TCP port the server listens on | +| `NANOFORGE_SERVER_LISTENING_UDP_PORT` | `4445` | UDP port the server listens on | + +<% } %>--- + +## Build + +Compile the game for production: + +```bash +<%= packageManager %> run build +# or +nf build +``` + +Output is written to the `.nanoforge/` directory. + +--- + +## Run + +Start the game server and open it in a browser: + +```bash +<%= packageManager %> run start +# or +nf start +``` + +<% if (lint) { %>--- + +## Code quality + +```bash +# Check formatting and linting +<%= packageManager %> run lint + +# Auto-fix formatting and linting +<%= packageManager %> run format +``` + +<% } %>--- + +## CLI reference + +| Command | Description | +|--------------------------------|-------------------------------------------| +| `nf dev` | Start in development mode with hot reload | +| `nf build` | Build for production | +| `nf start` | Run the built game | +| `nf generate component ` | Scaffold a new component | +| `nf generate system ` | Scaffold a new system |<% if (editor) { %> +| `nf editor` | Open the visual editor |<% } %> + +Full CLI documentation: diff --git a/src/libs/application/files/ts/README.md b/src/libs/application/files/ts/README.md index a916173..44878e2 100644 --- a/src/libs/application/files/ts/README.md +++ b/src/libs/application/files/ts/README.md @@ -1 +1,166 @@ # <%= name %> + +--- + +## Prerequisites + +- [Node.js](https://nodejs.org/) 18+ +- Package manager: **<%= packageManager %>** +- [NanoForge CLI](https://www.npmjs.com/package/@nanoforge-dev/cli): `<%= packageManager %> install -g @nanoforge-dev/cli` + +--- + +## Installation + +```bash +<%= packageManager %> install +``` + +--- + +## Development + +Start the game in watch mode with hot reload: + +```bash +nf dev +``` + +<% if (editor) { %>Open the visual editor (optional — lets you edit entities, components and systems without touching code): +```bash +nf editor +``` + +<% } %>--- + +## Project structure + +``` +<%= name %>/ +├── .nanoforge/ # NanoForge internal save files (do not edit manually) +├── client/ # Client-side code (runs in the browser) +│ ├── static/ # Static assets +│ ├── components/ # ECS components +│ ├── systems/ # ECS systems<% if (initFunctions) { %> +│ └── init/ # Lifecycle hooks (before-init, after-run, …)<% } %><% if (server) { %> +├── server/ # Server-side code +│ ├── static/ +│ ├── components/ +│ ├── systems/<% if (initFunctions) { %> +│ └── init/<% } %><% } %> +├── package.json +├── tsconfig.json +└── nanoforge.config.json # Game configuration +``` + +--- + +## Modifying the game + +### Add a component + +```bash +nf generate component --part client +``` + +A component is a plain class that holds data for an entity. Edit the generated file in `client/components/`: + +```ts +export class MyComponent { + constructor(public speed: number) {} +} + +export default MyComponent.name; +``` + +### Add a system + +```bash +nf generate system --part client +``` + +A system is a function that runs every tick and operates on entities that have specific components. Edit the generated file in `client/systems/`: + +```ts +import { type Context } from "@nanoforge-dev/common"; +import { type Registry } from "@nanoforge-dev/ecs-client"; +import { MyComponent } from "../components/my.component"; + +export const mySystem = (registry: Registry, ctx: Context) => { + const entities = registry.getZipper([MyComponent]); + + entities.forEach((entity) => { + entity.MyComponent.speed += 1; + }); +}; + +export default mySystem.name; +``` + +<% if (server) { %> +### Server-side code + +Server components and systems live in `server/` and are imported from `@nanoforge-dev/ecs-server`. They follow the same pattern as the client side. + +Configure the server connection in `.env`: + +| Variable | Default | Description | +|---------------------------------------|-------------|---------------------------------| +| `NANOFORGE_CLIENT_SERVER_ADDRESS` | `127.0.0.1` | Server address (client-side) | +| `NANOFORGE_CLIENT_SERVER_TCP_PORT` | `4444` | TCP port the client connects to | +| `NANOFORGE_CLIENT_SERVER_UDP_PORT` | `4445` | UDP port the client connects to | +| `NANOFORGE_SERVER_LISTENING_TCP_PORT` | `4444` | TCP port the server listens on | +| `NANOFORGE_SERVER_LISTENING_UDP_PORT` | `4445` | UDP port the server listens on | + +<% } %>--- + +## Build + +Compile the game for production: + +```bash +<%= packageManager %> run build +# or +nf build +``` + +Output is written to the `.nanoforge/` directory. + +--- + +## Run + +Start the game server and open it in a browser: + +```bash +<%= packageManager %> run start +# or +nf start +``` + +<% if (lint) { %>--- + +## Code quality + +```bash +# Check formatting and linting +<%= packageManager %> run lint + +# Auto-fix formatting and linting +<%= packageManager %> run format +``` + +<% } %>--- + +## CLI reference + +| Command | Description | +|--------------------------------|-------------------------------------------| +| `nf dev` | Start in development mode with hot reload | +| `nf build` | Build for production | +| `nf start` | Run the built game | +| `nf generate component ` | Scaffold a new component | +| `nf generate system ` | Scaffold a new system |<% if (editor) { %> +| `nf editor` | Open the visual editor |<% } %> + +Full CLI documentation: diff --git a/src/libs/application/schema.json b/src/libs/application/schema.json index a96522c..d570149 100644 --- a/src/libs/application/schema.json +++ b/src/libs/application/schema.json @@ -59,6 +59,11 @@ "type": "boolean", "description": "Add editor dependencies", "default": false + }, + "initFunctions": { + "type": "boolean", + "description": "Add init functions to the application", + "default": false } }, "required": ["name"] From 8bccf8f34c0426dbfeba71b358fafce33e7ee9a4 Mon Sep 17 00:00:00 2001 From: Exelo Date: Sat, 6 Jun 2026 11:58:22 +0900 Subject: [PATCH 2/7] fix: change components manifest in component schematic --- .../files/js/__fileName__.component.js | 48 ++++++++----------- .../files/ts/__fileName__.component.ts | 48 ++++++++----------- 2 files changed, 42 insertions(+), 54 deletions(-) diff --git a/src/libs/component/files/js/__fileName__.component.js b/src/libs/component/files/js/__fileName__.component.js index 212ce6c..10ad6b3 100644 --- a/src/libs/component/files/js/__fileName__.component.js +++ b/src/libs/component/files/js/__fileName__.component.js @@ -47,32 +47,26 @@ export const EDITOR_COMPONENT_MANIFEST = { name: "<%= className %>", description: "<%= className %> component description", params: [ - [ - { - type: "string", - name: "paramA", - description: "Param A description", - example: "Example value", - }, - ], - [ - { - type: "number", - name: "paramB", - description: "Param B description", - example: 3, - }, - ], - [ - { - type: "boolean", - name: "paramC", - description: "Param C description", - example: true, - default: false, - // Not required because it has a default value - optional: true, - }, - ], + { + type: "string", + name: "paramA", + description: "Param A description", + example: "Example value", + }, + { + type: "number", + name: "paramB", + description: "Param B description", + example: 3, + }, + { + type: "boolean", + name: "paramC", + description: "Param C description", + example: true, + default: false, + // Not required because it has a default value + optional: true, + }, ], }; diff --git a/src/libs/component/files/ts/__fileName__.component.ts b/src/libs/component/files/ts/__fileName__.component.ts index 2b35828..2be836f 100644 --- a/src/libs/component/files/ts/__fileName__.component.ts +++ b/src/libs/component/files/ts/__fileName__.component.ts @@ -30,32 +30,26 @@ export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { name: "<%= className %>", description: "<%= className %> component description", params: [ - [ - { - type: "string", - name: "paramA", - description: "Param A description", - example: "Example value", - }, - ], - [ - { - type: "number", - name: "paramB", - description: "Param B description", - example: 3, - }, - ], - [ - { - type: "boolean", - name: "paramC", - description: "Param C description", - example: true, - default: false, - // Not required because it has a default value - optional: true, - }, - ], + { + type: "string", + name: "paramA", + description: "Param A description", + example: "Example value", + }, + { + type: "number", + name: "paramB", + description: "Param B description", + example: 3, + }, + { + type: "boolean", + name: "paramC", + description: "Param C description", + example: true, + default: false, + // Not required because it has a default value + optional: true, + }, ], }; From de7acb2707c93d78bab7c4ad7bef30a7fa8dacb0 Mon Sep 17 00:00:00 2001 From: Exelo Date: Sat, 6 Jun 2026 11:59:27 +0900 Subject: [PATCH 3/7] fix: include .gitignore in pnpm pack by changing his name --- e2e/application.test.ts | 1 + src/libs/application/application.factory.ts | 1 + src/libs/application/files/js/{.gitignore => __dot__gitignore} | 0 src/libs/application/files/ts/{.gitignore => __dot__gitignore} | 0 4 files changed, 2 insertions(+) rename src/libs/application/files/js/{.gitignore => __dot__gitignore} (100%) rename src/libs/application/files/ts/{.gitignore => __dot__gitignore} (100%) diff --git a/e2e/application.test.ts b/e2e/application.test.ts index e60202c..542e536 100644 --- a/e2e/application.test.ts +++ b/e2e/application.test.ts @@ -22,6 +22,7 @@ describe("application schematic", () => { expect(tree.files).toContain("/my-app/eslint.config.js"); expect(tree.files).toContain("/my-app/prettier.config.js"); expect(tree.files).toContain("/my-app/README.md"); + expect(tree.files).toContain("/my-app/.gitignore"); }); it("should set the project name in package.json", () => { diff --git a/src/libs/application/application.factory.ts b/src/libs/application/application.factory.ts index 4c80c93..6a5db8b 100644 --- a/src/libs/application/application.factory.ts +++ b/src/libs/application/application.factory.ts @@ -44,6 +44,7 @@ const transform = (schema: ApplicationSchema): ApplicationOptions => { const generate = (options: ApplicationOptions, path: string): Source => { return apply(url(join("./files" as Path, options.language)), [ template({ + dot: ".", ...strings, ...options, }), diff --git a/src/libs/application/files/js/.gitignore b/src/libs/application/files/js/__dot__gitignore similarity index 100% rename from src/libs/application/files/js/.gitignore rename to src/libs/application/files/js/__dot__gitignore diff --git a/src/libs/application/files/ts/.gitignore b/src/libs/application/files/ts/__dot__gitignore similarity index 100% rename from src/libs/application/files/ts/.gitignore rename to src/libs/application/files/ts/__dot__gitignore From 0aaff3ac953c7aa4dfc8c5f2e4f4e8f2cf965b39 Mon Sep 17 00:00:00 2001 From: Exelo Date: Sat, 6 Jun 2026 11:59:56 +0900 Subject: [PATCH 4/7] feat: add .env file on server application schematic --- e2e/application.test.ts | 6 ++++++ src/libs/application/application.factory.ts | 7 ++++++- src/libs/application/files/js/__dot__env | 6 ++++++ src/libs/application/files/ts/__dot__env | 6 ++++++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/libs/application/files/js/__dot__env create mode 100644 src/libs/application/files/ts/__dot__env diff --git a/e2e/application.test.ts b/e2e/application.test.ts index 542e536..2cdccd9 100644 --- a/e2e/application.test.ts +++ b/e2e/application.test.ts @@ -23,6 +23,7 @@ describe("application schematic", () => { expect(tree.files).toContain("/my-app/prettier.config.js"); expect(tree.files).toContain("/my-app/README.md"); expect(tree.files).toContain("/my-app/.gitignore"); + expect(tree.files).not.toContain("/my-app/.env"); }); it("should set the project name in package.json", () => { @@ -58,6 +59,7 @@ describe("application schematic", () => { expect(tree.files).toContain("/js-app/package.json"); expect(tree.files).toContain("/js-app/jsconfig.json"); expect(tree.files).not.toContain("/js-app/tsconfig.json"); + expect(tree.files).not.toContain("/js-app/.env"); }); }); @@ -92,6 +94,10 @@ describe("application schematic", () => { }); }); + it("should include env file", () => { + expect(tree.files).toContain("/server-app/.env"); + }); + it("should include ecs-server dependency", () => { const packageJson = JSON.parse(tree.readContent("/server-app/package.json")); expect(packageJson.devDependencies["@nanoforge-dev/ecs-server"]).toBeDefined(); diff --git a/src/libs/application/application.factory.ts b/src/libs/application/application.factory.ts index 6a5db8b..bea05aa 100644 --- a/src/libs/application/application.factory.ts +++ b/src/libs/application/application.factory.ts @@ -65,14 +65,19 @@ export const main = (schema: ApplicationSchema): Rule => { tree = await firstValueFrom(tree); } + const basePath = join("/" as Path, path); + if (!options.lint) { - const basePath = join("/" as Path, path); tree.delete(join(basePath, "eslint.config.js")); tree.delete(join(basePath, "prettier.config.js")); tree.delete(join(basePath, ".prettierignore")); if (options.language === "js") tree.delete(join(basePath, "jsconfig.json")); } + if (!options.server) { + tree.delete(join(basePath, ".env")); + } + return tree; }; }; diff --git a/src/libs/application/files/js/__dot__env b/src/libs/application/files/js/__dot__env new file mode 100644 index 0000000..75b4595 --- /dev/null +++ b/src/libs/application/files/js/__dot__env @@ -0,0 +1,6 @@ +NANOFORGE_CLIENT_SERVER_TCP_PORT=4444 +NANOFORGE_CLIENT_SERVER_UDP_PORT=4445 +NANOFORGE_CLIENT_SERVER_ADDRESS=127.0.0.1 + +NANOFORGE_SERVER_LISTENING_TCP_PORT=4444 +NANOFORGE_SERVER_LISTENING_UDP_PORT=4445 diff --git a/src/libs/application/files/ts/__dot__env b/src/libs/application/files/ts/__dot__env new file mode 100644 index 0000000..75b4595 --- /dev/null +++ b/src/libs/application/files/ts/__dot__env @@ -0,0 +1,6 @@ +NANOFORGE_CLIENT_SERVER_TCP_PORT=4444 +NANOFORGE_CLIENT_SERVER_UDP_PORT=4445 +NANOFORGE_CLIENT_SERVER_ADDRESS=127.0.0.1 + +NANOFORGE_SERVER_LISTENING_TCP_PORT=4444 +NANOFORGE_SERVER_LISTENING_UDP_PORT=4445 From e35663caa6826177086c4ee84377171322e12b29 Mon Sep 17 00:00:00 2001 From: Exelo Date: Sat, 6 Jun 2026 12:00:18 +0900 Subject: [PATCH 5/7] fix: remove init functions field in config on false --- e2e/configuration.test.ts | 2 +- src/libs/configuration/configuration.factory.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/e2e/configuration.test.ts b/e2e/configuration.test.ts index 4753008..474e9cc 100644 --- a/e2e/configuration.test.ts +++ b/e2e/configuration.test.ts @@ -29,7 +29,7 @@ describe("configuration schematic", () => { it("should include default client build config", () => { expect(config.name).toBe("my-name"); expect(config.language).toBe("ts"); - expect(config.initFunctions).toBe(false); + expect(config.initFunctions).not.toBeDefined(); }); it("should include default client build config", () => { diff --git a/src/libs/configuration/configuration.factory.ts b/src/libs/configuration/configuration.factory.ts index 842330c..a4a0a17 100644 --- a/src/libs/configuration/configuration.factory.ts +++ b/src/libs/configuration/configuration.factory.ts @@ -55,7 +55,6 @@ const getConfig = (schema: ConfigurationSchema): DeepPartial => { const res: DeepPartial = { name: schema.name, language: schema.language, - initFunctions: schema.initFunctions, client: { enable: true, }, @@ -64,6 +63,10 @@ const getConfig = (schema: ConfigurationSchema): DeepPartial => { }, }; + if (schema.initFunctions) { + res["initFunctions"] = true; + } + if (schema.language === "js") { if ("client" in res && res.client) { res.client["build"] = { entry: "client/main.js" }; From c06a8c16ce0276fcda12c7ee07e97ddc5369c960 Mon Sep 17 00:00:00 2001 From: Exelo Date: Sat, 6 Jun 2026 12:00:50 +0900 Subject: [PATCH 6/7] fix: remove extensions for components and systems in part main schematic --- e2e/part-main.test.ts | 4 ++-- src/utils/main/main.generator.ts | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/e2e/part-main.test.ts b/e2e/part-main.test.ts index bb6052b..14c8913 100644 --- a/e2e/part-main.test.ts +++ b/e2e/part-main.test.ts @@ -26,11 +26,11 @@ const clientSave: Save = { components: [ { name: "ExampleComponent", - path: "./components/example.component", + path: "./components/example.component.ts", paramsNames: ["exampleValueName", "exampleNum"], }, ], - systems: [{ name: "exampleSystem", path: "./systems/example.system" }], + systems: [{ name: "exampleSystem", path: "./systems/example.system.js" }], entities: [ { id: "player", diff --git a/src/utils/main/main.generator.ts b/src/utils/main/main.generator.ts index b45636c..5dc3329 100644 --- a/src/utils/main/main.generator.ts +++ b/src/utils/main/main.generator.ts @@ -150,11 +150,12 @@ export class MainGenerator { private generateImports(els: { name: string; path: string }[], relative: boolean): MainGenerator { els .sort((a, b) => a.path.localeCompare(b.path)) - .forEach(({ name, path }) => + .forEach(({ name, path }) => { + path = path.replace(/\.(?:ts|js)$/, ""); this.writeLine( `import { ${name} } from "${relative ? joinRelative(this.pathToRoot, path) : path}";`, - ), - ); + ); + }); this.endSection(); return this; } From 44ab6099edae02d00b084a905aefab7e688076e4 Mon Sep 17 00:00:00 2001 From: Exelo Date: Sat, 6 Jun 2026 12:24:03 +0900 Subject: [PATCH 7/7] docs: add full docs --- README.md | 3 + docs/config.json | 21 -- docs/docs/api.mdx | 120 --------- docs/docs/architecture.mdx | 132 ---------- docs/docs/index.mdx | 12 - docs/docs/schematics.mdx | 244 ------------------ docs/docs/schematics/1-overview.mdx | 62 +++++ .../schematics/2-schematics/1-application.mdx | 80 ++++++ .../2-schematics/2-configuration.mdx | 50 ++++ .../schematics/2-schematics/3-part-base.mdx | 115 +++++++++ .../schematics/2-schematics/4-part-main.mdx | 77 ++++++ .../docs/schematics/2-schematics/5-docker.mdx | 70 +++++ .../schematics/2-schematics/6-component.mdx | 120 +++++++++ .../docs/schematics/2-schematics/6-system.mdx | 105 ++++++++ docs/guides/contributing.mdx | 119 --------- docs/guides/creating-schematics.mdx | 222 ---------------- docs/guides/getting-started.mdx | 159 ------------ docs/guides/index.mdx | 13 - docs/guides/testing.mdx | 75 ------ docs/index.mdx | 25 -- 20 files changed, 682 insertions(+), 1142 deletions(-) delete mode 100644 docs/config.json delete mode 100644 docs/docs/api.mdx delete mode 100644 docs/docs/architecture.mdx delete mode 100644 docs/docs/index.mdx delete mode 100644 docs/docs/schematics.mdx create mode 100644 docs/docs/schematics/1-overview.mdx create mode 100644 docs/docs/schematics/2-schematics/1-application.mdx create mode 100644 docs/docs/schematics/2-schematics/2-configuration.mdx create mode 100644 docs/docs/schematics/2-schematics/3-part-base.mdx create mode 100644 docs/docs/schematics/2-schematics/4-part-main.mdx create mode 100644 docs/docs/schematics/2-schematics/5-docker.mdx create mode 100644 docs/docs/schematics/2-schematics/6-component.mdx create mode 100644 docs/docs/schematics/2-schematics/6-system.mdx delete mode 100644 docs/guides/contributing.mdx delete mode 100644 docs/guides/creating-schematics.mdx delete mode 100644 docs/guides/getting-started.mdx delete mode 100644 docs/guides/index.mdx delete mode 100644 docs/guides/testing.mdx delete mode 100644 docs/index.mdx diff --git a/README.md b/README.md index 5e7a07d..3f30e7c 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,9 @@ This repository provide multiples schematics, usable with Angular Devkit schemat - `configuration` : Template of `nanoforge.config.json` with premade fields - `part-base` : Base of Nanoforge client or server - `part-main` : Client or server `main.ts` from a config +- `component` : ECS component class for a client or server part +- `system` : ECS system function for a client or server part +- `docker` : Dockerfile and `.dockerignore` tailored to your package manager ## Contributing diff --git a/docs/config.json b/docs/config.json deleted file mode 100644 index ba98b3f..0000000 --- a/docs/config.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "documentation": { - "group": "Schematics", - "pages": [ - "docs/schematics/index", - "docs/schematics/api", - "docs/schematics/architecture", - "docs/schematics/schematics" - ] - }, - "guides": { - "group": "Schematics", - "pages": [ - "guides/schematics/index", - "guides/schematics/contributing", - "guides/schematics/creating-schematics", - "guides/schematics/testing", - "guides/schematics/getting-started" - ] - } -} diff --git a/docs/docs/api.mdx b/docs/docs/api.mdx deleted file mode 100644 index 421741b..0000000 --- a/docs/docs/api.mdx +++ /dev/null @@ -1,120 +0,0 @@ ---- -title: API Reference -description: Internal utility modules and shared helpers used by the schematics. ---- - -# API Reference - -This section documents the internal utility modules and classes under `src/utils/`. - -## MainGenerator - -**Module:** `src/utils/main/main.generator.ts` - -A builder-pattern class that generates the contents of a NanoForge `main.ts` entry point file. It is used by the -`part-main` schematic. - -### Methods - -- `generateBaseImports(hasTypes: boolean)` adds the base NanoForge imports and conditionally emits type imports. -- `generateLibsImports(libs)` generates import statements for all libraries listed in the save file. -- `generateComponentsImports(components)` generates import statements for all components. -- `generateSystemsImports(systems)` generates import statements for all systems. -- `generateMainFunction(hasTypes, cb)` wraps generated output in an `export async function main(...)` block. -- `generateApp(isServer)` emits `NanoforgeFactory.createClient()` or `NanoforgeFactory.createServer()`. -- `generateAppInit()` emits `await app.init(options);`. -- `generateAppRun()` emits `await app.run();`. -- `generateLibsInstances(libs)` emits `const = new ();` for each library. -- `generateLibsInit(libs)` emits the matching `app.use*()` call for each library type. -- `generateRegistry(libs)` emits `const registry = .registry;`. -- `generateEntities(entities)` emits entity creation and component attachment calls. -- `generateSystems(systems)` emits `registry.addSystem();` for each system. -- `generateInitFunctionIfNeeded(needed, func)` conditionally emits lifecycle hook calls. -- `generateInitFunctionsImportsIfNeeded(needed)` conditionally emits lifecycle hook imports. -- `toString()` returns the accumulated generated code. - -## ConfigFinder - -**Module:** `src/utils/config/config.finder.ts` - -Searches the virtual file tree for an existing `nanoforge.config.json` and returns the parsed config if found. - -- `find(tree, path)` recursively searches upward from the given path. - -## ConfigDeclarator - -**Module:** `src/utils/config/config.declarator.ts` - -Updates a configuration tree by merging server options. - -- `declare(tree, path, server)` reads the config, deep-merges server configuration when requested, and writes the result back. - -## Formatting Utilities - -**Module:** `src/utils/formatting.ts` - -- `toKebabCase(str)` converts a string to kebab-case. -- `toCamelCase(str)` converts a string to camelCase. -- `toPascalCase(str)` converts a string to PascalCase. - -## Name Utilities - -**Module:** `src/utils/name.ts` - -- `resolvePackageName(path)` extracts the package name from a path string and handles scoped packages. - -## Object Utilities - -**Module:** `src/utils/object.ts` - -- `deepMerge(...objects)` recursively merges multiple objects together. -- `isObject(item)` returns `true` when the item is a plain object. - -## Types and Enums - -**Module:** `src/utils/main/enums.ts` - -The init function enum defines the six lifecycle hook names: - -```ts -enum InitFunctionEnum { - BEFORE_INIT = "beforeInit", - AFTER_INIT = "afterInit", - BEFORE_REGISTRY_INIT = "beforeRegistryInit", - AFTER_REGISTRY_INIT = "afterRegistryInit", - BEFORE_RUN = "beforeRun", - AFTER_RUN = "afterRun", -} -``` - -**Module:** `src/utils/main/save.type.ts` - -The save types describe the metadata consumed by `part-main`: - -```ts -enum SaveLibraryTypeEnum { - COMPONENT_SYSTEM = "component-system", - GRAPHICS = "graphics", - ASSET_MANAGER = "asset-manager", - NETWORK = "network", - INPUT = "input", - SOUND = "sound", -} -``` - -## Constants - -**Module:** `src/utils/main/const.ts` - -Maps library types to the corresponding `app.use*()` method name. - -```ts -const LIBS_FUNCTIONS_NAME = { - "component-system": "useComponentSystem", - graphics: "useGraphics", - "asset-manager": "useAssetManager", - network: "useNetwork", - input: "useInput", - sound: "useSound", -}; -``` diff --git a/docs/docs/architecture.mdx b/docs/docs/architecture.mdx deleted file mode 100644 index 833c2b6..0000000 --- a/docs/docs/architecture.mdx +++ /dev/null @@ -1,132 +0,0 @@ ---- -title: Architecture -description: How the schematics package is organized and built. ---- - -# Architecture - -## Overview - -NanoForge Schematics is built on top of the Angular DevKit Schematics framework. It provides code generation -templates that scaffold NanoForge projects, including project structure, configuration, client and server base -code, Docker support, and dynamically generated main entry points. - -The package is published to npm as `@nanoforge-dev/schematics` and ships both ESM and CommonJS bundles. - -## Technology Stack - -| Component | Technology | -| ------------------- | ------------------------------ | -| Language | TypeScript (strict mode) | -| Schematic framework | Angular DevKit Schematics 21.x | -| Build tool | tsdown | -| Module formats | ESM + CJS | -| Target | ESNext | -| Package manager | pnpm 10.x | -| Node version | 25 | -| Linter | ESLint 9.x | -| Formatter | Prettier 3.x | -| CI/CD | GitHub Actions | - -## Project Structure - -````text -src/ -+-- index.ts # Public API exports -+-- defaults.ts # Default option values -+-- collection.json # Schematics collection manifest -+-- libs/ # Schematic implementations -+ +-- application/ # Application scaffolding -+ +-- configuration/ # Config file generation -+ +-- part-base/ # Client/server base structure -+ +-- part-main/ # Main file code generation -+ +-- docker/ # Dockerfile generation -+ +-- component/ # Component generation -+ +-- system/ # System generation -+-- utils/ # Shared utilities -+ +-- formatting.ts # String formatting helpers -+ +-- name.ts # Package name resolution -+ +-- object.ts # Deep merge utilities -+ +-- type.ts # Shared type definitions -+ +-- config/ # Configuration helpers -+ +-- main/ # Main file generation -+``` - -## Factory Pattern - -Each schematic follows a three-phase factory pattern: - -1. **Transform** - Convert raw schema input into validated internal options with defaults applied. -2. **Generate** - Load template files from `files/`, interpolate options into the templates, and produce a virtual file tree. -3. **Merge** - Combine the generated file tree into the target project directory using Angular DevKit merge strategies. - -Each factory module exports a `main` function that receives the schema and returns an Angular DevKit `Rule`. - -## Collection Manifest - -The `collection.json` file registers all available schematics. Each entry specifies the factory function, a -description, and the JSON Schema used for input validation. - -```json -{ - "schematics": { - "application": { - "factory": "./libs/application/application.factory#main", - "description": "Create a NanoForge Base application.", - "schema": "./libs/application/schema.json" - }, - "configuration": { - "factory": "./libs/configuration/configuration.factory#main", - "description": "Create a NanoForge Configuration.", - "schema": "./libs/configuration/schema.json" - }, - "part-base": { - "factory": "./libs/part-base/part-base.factory#main", - "description": "Create a NanoForge Base for client or server.", - "schema": "./libs/part-base/schema.json" - }, - "part-main": { - "factory": "./libs/part-main/part-main.factory#main", - "description": "Create a Main file for client or server.", - "schema": "./libs/part-main/schema.json" - }, - "docker": { - "factory": "./libs/docker/docker.factory#main", - "description": "Create a Dockerfile for the application.", - "schema": "./libs/docker/schema.json" - }, - "component": { - "factory": "./libs/component/component.factory#main", - "description": "Create a NanoForge component.", - "schema": "./libs/component/schema.json" - }, - "system": { - "factory": "./libs/system/system.factory#main", - "description": "Create a NanoForge system.", - "schema": "./libs/system/schema.json" - } - } -} -```` - -## Build Pipeline - -The project uses tsdown for bundling. The build produces: - -- A main bundle from `src/index.ts` in both ESM and CJS formats -- Per-schematic ESM bundles from each `*.factory.ts` -- Type declarations (`.d.ts` and `.d.cts`) -- Source maps - -The `postbuild` step copies non-TypeScript assets into `dist/` using `cpx2`. - -```bash -# Full build -pnpm run build - -# Internally this runs: -# 1. tsc --noEmit -# 2. tsdown -# 3. copy:collection -# 4. copy:lib -``` diff --git a/docs/docs/index.mdx b/docs/docs/index.mdx deleted file mode 100644 index 875d753..0000000 --- a/docs/docs/index.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Technical Documentation -description: Reference pages for schematics, architecture, and internal utilities. ---- - -# Technical Documentation - -This section covers the implementation details behind `@nanoforge-dev/schematics`. - -- [Architecture](architecture) -- [Schematics Reference](schematics) -- [API Reference](api) diff --git a/docs/docs/schematics.mdx b/docs/docs/schematics.mdx deleted file mode 100644 index 55bfa00..0000000 --- a/docs/docs/schematics.mdx +++ /dev/null @@ -1,244 +0,0 @@ ---- -title: Schematics Reference -description: Detailed reference for every schematic shipped by the package. ---- - -# Schematics Reference - -This page documents each schematic provided by `@nanoforge-dev/schematics`. - -## application - -Scaffolds a complete NanoForge project with tooling configuration and optional client or server structure. - -**Factory:** `src/libs/application/application.factory.ts` - -| Option | Type | Required | Default | Description | -| ---------------- | ---------------------------------- | -------- | ----------------- | ---------------------------- | -| `name` | string | Yes | `nanoforge-app` | Project name | -| `version` | string | No | `0.0.0` | Application version | -| `author` | string | No | `""` | Application author | -| `description` | string | No | `""` | Application description | -| `directory` | string | No | Derived from name | Output directory | -| `language` | `ts` \| `js` | No | `ts` | Language for generated files | -| `strict` | boolean | No | `true` | Enable strict mode | -| `lint` | boolean | No | `true` | Generate lint files | -| `packageManager` | `npm` \| `yarn` \| `pnpm` \| `bun` | No | `npm` | Package manager to configure | -| `server` | boolean | No | `false` | Include server configuration | -| `editor` | boolean | No | `false` | Add editor dependencies | - -Generated output typically includes `package.json`, `eslint.config.js`, `prettier.config.js`, `.prettierignore`, and `README.md`. -TypeScript projects also receive `tsconfig.json`. - -## configuration - -Generates or updates `nanoforge.config.json`. When an existing config is found in the directory tree, the new values -are deep-merged into it. - -**Factory:** `src/libs/configuration/configuration.factory.ts` - -| Option | Type | Required | Default | Description | -| --------------- | ------------ | -------- | --------------- | ------------------------------------------ | -| `name` | string | No | `nanoforge-app` | Project name | -| `directory` | string | No | `.` | Destination directory | -| `server` | boolean | No | `false` | Enable server configuration | -| `language` | `ts` \| `js` | No | `ts` | Application language | -| `initFunctions` | boolean | No | `false` | Add init functions to the main application | - -Example output: - -```json -{ - "client": { - "build": { - "entryFile": "client/main.ts", - "outDir": ".nanoforge/client" - }, - "runtime": { - "dir": ".nanoforge/client" - } - }, - "server": { - "enable": true, - "build": { - "entryFile": "server/main.ts", - "outDir": ".nanoforge/server" - }, - "runtime": { - "dir": ".nanoforge/server" - } - } -} -``` - -## part-base - -Generates the base directory structure for a client or server part. This includes example components, example systems, -optional lifecycle init functions, and a `.nanoforge/.save.json` metadata file used by `part-main`. - -**Factory:** `src/libs/part-base/part-base.factory.ts` - -| Option | Type | Required | Default | Description | -| --------------- | -------------------- | -------- | ------- | -------------------------------------- | -| `directory` | string | No | `.` | Destination directory | -| `part` | `client` \| `server` | Yes | - | Which part to generate | -| `language` | `ts` \| `js` | No | `ts` | Language for generated files | -| `initFunctions` | boolean | No | `false` | Generate lifecycle init function files | -| `server` | boolean | No | `false` | Indicate whether the app has a server | - -Generated output resembles: - -```text -/ -+-- / -| +-- components/ -| | +-- example.component.ts -| +-- systems/ -| | +-- example.system.ts -| +-- init/ (only if initFunctions=true) -| +-- before-init.ts -| +-- after-init.ts -| +-- before-registry-init.ts -| +-- after-registry-init.ts -| +-- before-run.ts -| +-- after-run.ts -+-- .nanoforge/ - +-- .save.json -``` - -When `initFunctions` is enabled, six lifecycle hooks are generated: - -| Function | Parameters | When Called | -| -------------------- | ----------------- | ---------------------------------- | -| `beforeInit` | `app` | Before application initialization | -| `afterInit` | `app` | After application initialization | -| `beforeRegistryInit` | `app`, `registry` | Before ECS registry initialization | -| `afterRegistryInit` | `app`, `registry` | After ECS registry initialization | -| `beforeRun` | `app` | Before application run loop starts | -| `afterRun` | `app` | After application run loop ends | - -## part-main - -Dynamically generates the `main.ts` or `main.js` entry point for a client or server part. The generated code is driven -by the `.nanoforge/.save.json` metadata file, which lists libraries, components, systems, and entities. - -**Factory:** `src/libs/part-main/part-main.factory.ts` - -| Option | Type | Required | Default | Description | -| --------------- | -------------------- | -------- | ---------------------------------- | ------------------------------------- | -| `directory` | string | No | `.` | Working directory | -| `part` | `client` \| `server` | Yes | - | Which part to generate | -| `language` | `ts` \| `js` | No | `ts` | Language for the generated file | -| `initFunctions` | boolean | No | `false` | Include lifecycle init function calls | -| `saveFile` | string | No | `.nanoforge/.save.json` | Path to the metadata save file | -| `outFile` | string | No | Derived from `part` and `language` | Output file path override | -| `editor` | boolean | No | `false` | Build the main editor variant | - -Save file format: - -```ts -interface Save { - libraries: { - id: string; - type: - | "component-system" - | "graphics" - | "asset-manager" - | "network" - | "input" - | "sound" - | string; - name: string; - path: string; - }[]; - components: { - name: string; - path: string; - paramsNames: string[]; - }[]; - systems: { - name: string; - path: string; - }[]; - entities: { - id: string; - components: Record>; - }[]; -} -``` - -The generated code follows this pattern: - -```ts -import { type IRunOptions } from "@nanoforge-dev/common"; -import { NanoforgeFactory } from "@nanoforge-dev/core"; - -// library, component, and system imports - -export async function main(options: IRunOptions) { - const app = NanoforgeFactory.createClient(); - - const registry = ecsLibrary.registry; - - await app.init(options); - - const entity = registry.spawnEntity(); - registry.addComponent(entity, new ExampleComponent("a", 1)); - registry.addSystem(exampleSystem); - - await app.run(); -} -``` - -Library type mapping: - -| Library Type | Method Called | -| ------------------ | -------------------------------- | -| `component-system` | `app.useComponentSystem()` | -| `graphics` | `app.useGraphics()` | -| `asset-manager` | `app.useAssetManager()` | -| `network` | `app.useNetwork()` | -| `input` | `app.useInput()` | -| `sound` | `app.useSound()` | -| Custom string | `app.use(Symbol(""), ...)` | - -## docker - -Creates a Dockerfile for the application. - -**Factory:** `src/libs/docker/docker.factory.ts` - -| Option | Type | Required | Default | Description | -| ---------------- | ---------------------------------- | -------- | ------- | ----------------------------------- | -| `directory` | string | No | `.` | Destination directory | -| `packageManager` | `npm` \| `yarn` \| `pnpm` \| `bun` | No | `npm` | Package manager to use in the image | - -## component - -Creates a NanoForge component for a client or server part. - -**Factory:** `src/libs/component/component.factory.ts` - -| Option | Type | Required | Default | Description | -| ----------- | -------------------- | -------- | --------- | ---------------------------- | -| `name` | string | No | `example` | Component name | -| `directory` | string | No | `.` | Destination directory | -| `part` | `client` \| `server` | Yes | - | Which part to generate | -| `language` | `ts` \| `js` | No | `ts` | Language for generated files | - -The schematic normalizes the component name into a PascalCase class name and a kebab-case file name. - -## system - -Creates a NanoForge system for a client or server part. - -**Factory:** `src/libs/system/system.factory.ts` - -| Option | Type | Required | Default | Description | -| ----------- | -------------------- | -------- | --------- | ---------------------------- | -| `name` | string | No | `example` | System name | -| `directory` | string | No | `.` | Destination directory | -| `part` | `client` \| `server` | Yes | - | Which part to generate | -| `language` | `ts` \| `js` | No | `ts` | Language for generated files | - -The schematic normalizes the system name into a camelCase function name and a kebab-case file name. diff --git a/docs/docs/schematics/1-overview.mdx b/docs/docs/schematics/1-overview.mdx new file mode 100644 index 0000000..b2012e5 --- /dev/null +++ b/docs/docs/schematics/1-overview.mdx @@ -0,0 +1,62 @@ +--- +title: Schematics +description: Overview of the @nanoforge-dev/schematics package +--- + +import { Card, CardGroup } from "mintlify/components"; + +## What are schematics? + +NanoForge schematics are code generators built on [Angular DevKit](https://github.com/angular/angular-cli/tree/main/packages/angular_devkit/schematics). They produce the boilerplate files that every NanoForge project needs — from the initial application scaffold down to individual ECS components and systems. + +The NanoForge CLI (`nf`) calls these schematics internally whenever you run commands like `nf new` or `nf generate`. You can also invoke them directly via the `schematics` CLI for advanced use cases. + +## Installation + +```bash +npm install -g @nanoforge-dev/cli +``` + +The schematics package is a dependency of the CLI and does not need to be installed separately. If you want to run schematics without the CLI: + +```bash +npm install -g @angular-devkit/schematics-cli +npm install @nanoforge-dev/schematics +``` + +Then invoke directly: + +```bash +schematics @nanoforge-dev/schematics: [options] +``` + +## Available schematics + + + + Scaffold a complete NanoForge project with `package.json`, TypeScript or JavaScript config, + linting, and optional server setup. + + + Generate a `nanoforge.config.json` configuration file consumed by the NanoForge CLI. + + + Create the base folder structure for a client or server part, including example component, + system, save file, and optional lifecycle hooks. + + + Generate the `main.ts` entry point for a client or server part by reading the `.nanoforge` save + file. + + + Scaffold an ECS component class with an editor manifest for use in the NanoForge editor. + + + Scaffold an ECS system function with an editor manifest for use in the NanoForge editor. + + + Generate a production-ready `Dockerfile` and `.dockerignore` tailored to your package manager. + + + +see the [changelog](https://github.com/NanoForge-dev/schematics/blob/main/CHANGELOG.md) for the full release history. diff --git a/docs/docs/schematics/2-schematics/1-application.mdx b/docs/docs/schematics/2-schematics/1-application.mdx new file mode 100644 index 0000000..9554bc3 --- /dev/null +++ b/docs/docs/schematics/2-schematics/1-application.mdx @@ -0,0 +1,80 @@ +--- +title: Application +description: Generate a complete NanoForge application scaffold +--- + +import { ToolTip } from "mintlify/components"; + +## Overview + +The `application` schematic creates a fully structured NanoForge project with all the base files needed to start development — `package.json`, linting configuration, TypeScript or JavaScript setup, and optionally `.env` for server-enabled apps. + +## Usage + +```bash +nf new +``` + +Or via Angular DevKit schematics directly: + +```bash +schematics @nanoforge-dev/schematics:application --name=my-app +``` + +## Options + +| Option | Type | Default | Description | +| ---------------- | ------------------------------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | `string` | `nanoforge-app` | The name of the application | +| `version` | `string` | `0.0.0` | Initial version in `package.json` | +| `author` | `string` | `""` | Author field in `package.json` | +| `description` | `string` | `""` | Description field in `package.json` | +| `directory` | `string` | `name` | Destination directory for the generated files | +| `language` | `"ts" \| "js"` | `ts` | Language to use for generated source files | +| `strict` | `boolean` | `true` | Enable strict mode | +| `lint` | `boolean` | `true` | Generate linting configuration files | +| `packageManager` | `"npm" \| "yarn" \| "pnpm" \| "bun"` | `npm` | Package manager to target in scripts and lockfile references | +| `server` | `boolean` | `false` | Configure a server for the application | +| `editor` | `boolean` | `false` | Include editor dependencies | +| `initFunctions` | `boolean` | `false` | Add init function lifecycle hooks | + +## Generated structure + +``` +my-app/ +├── package.json +├── tsconfig.json # TypeScript only +├── jsconfig.json # JavaScript only +├── eslint.config.js # if lint: true +├── prettier.config.js # if lint: true +├── .prettierignore # if lint: true +├── .env # if server: true +├── .gitignore +└── README.md +``` + +## Examples + +**Minimal TypeScript app:** + +```bash +schematics @nanoforge-dev/schematics:application --name=my-game +``` + +**JavaScript app with pnpm and server:** + +```bash +schematics @nanoforge-dev/schematics:application \ + --name=my-game \ + --language=js \ + --packageManager=pnpm \ + --server=true +``` + +**TypeScript app without linting:** + +```bash +schematics @nanoforge-dev/schematics:application \ + --name=my-game \ + --lint=false +``` diff --git a/docs/docs/schematics/2-schematics/2-configuration.mdx b/docs/docs/schematics/2-schematics/2-configuration.mdx new file mode 100644 index 0000000..8514f09 --- /dev/null +++ b/docs/docs/schematics/2-schematics/2-configuration.mdx @@ -0,0 +1,50 @@ +--- +title: Configuration +description: Generate a NanoForge configuration file +--- + +import { Tooltip } from "mintlify/components"; + +## Overview + +The `configuration` schematic creates a `nanoforge.config.json` file at the root of your project. This file is the central configuration consumed by the NanoForge CLI for builds, starts, and other operations. + +## Usage + +```bash +schematics @nanoforge-dev/schematics:configuration +``` + +## Options + +| Option | Type | Default | Description | +| --------------- | -------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | `string` | `nanoforge-app` | The name of the application | +| `directory` | `string` | `.` | Destination directory | +| `server` | `boolean` | `false` | Include server configuration | +| `language` | `"ts" \| "js"` | `ts` | Language used in the project | +| `initFunctions` | `boolean` | `false` | Reference init functions in configuration | + +## Generated file + +``` +nanoforge.config.json +``` + +The generated file is an empty JSON object `{}` that you populate with the fields the NanoForge CLI expects. Refer to the [CLI documentation](https://github.com/NanoForge-dev/CLI) for the full list of supported fields. + +## Examples + +**Generate configuration at the project root:** + +```bash +schematics @nanoforge-dev/schematics:configuration --name=my-game +``` + +**Generate configuration for a server-enabled project:** + +```bash +schematics @nanoforge-dev/schematics:configuration \ + --name=my-game \ + --server=true +``` diff --git a/docs/docs/schematics/2-schematics/3-part-base.mdx b/docs/docs/schematics/2-schematics/3-part-base.mdx new file mode 100644 index 0000000..eec16aa --- /dev/null +++ b/docs/docs/schematics/2-schematics/3-part-base.mdx @@ -0,0 +1,115 @@ +--- +title: Part Base +description: Generate the base structure for a NanoForge client or server part +--- + +import { Tooltip } from "mintlify/components"; + +## Overview + +The `part-base` schematic generates the foundational folder structure for a NanoForge **client** or **server** part. It creates example components and systems, the `.nanoforge` save file used to track ECS state, and optionally lifecycle init functions. + +## Usage + +```bash +schematics @nanoforge-dev/schematics:part-base --part=client +``` + +## Options + +| Option | Type | Default | Description | +| --------------- | ---------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `part` | `"client" \| "server"` | _(required)_ | The part of the application to generate | +| `directory` | `string` | `.` | Destination directory for the generated files | +| `language` | `"ts" \| "js"` | `ts` | Language to use for generated source files | +| `server` | `boolean` | `false` | Whether the application has a server | +| `initFunctions` | `boolean` | `false` | Add lifecycle init function hooks | + +## Generated structure + +``` +{directory}/ +├── {part}/ +│ ├── components/ +│ │ └── example.component.ts +│ ├── systems/ +│ │ └── example.system.ts +│ └── init/ # if initFunctions: true +│ ├── before-init.ts +│ ├── after-init.ts +│ ├── before-registry-init.ts +│ ├── after-registry-init.ts +│ ├── before-run.ts +│ └── after-run.ts +└── .nanoforge/ + └── {part}.save.json +``` + +## Save file + +The `.nanoforge/{part}.save.json` file is the source of truth for ECS state. It lists the libraries, components, systems, and entities registered for this part. The `part-main` schematic reads this file to generate the `main.ts` entry point. + +```json +{ + "libraries": [ ... ], + "components": [ + { + "name": "ExampleComponent", + "path": "./components/example.component.ts", + "paramsNames": ["paramA", "paramB", "paramC"] + } + ], + "systems": [ + { + "name": "exampleSystem", + "path": "./systems/example.system.ts" + } + ], + "entities": [ + { + "id": "exampleEntity", + "components": { + "ExampleComponent": { "paramA": "example", "paramB": 10 } + } + } + ] +} +``` + +## Init functions + +When `initFunctions` is `true`, the following lifecycle hooks are scaffolded: + +| File | Hook | Called | +| ------------------------- | -------------------- | ----------------------------------- | +| `before-init.ts` | `beforeInit` | Before the app initializes | +| `after-init.ts` | `afterInit` | After the app initializes | +| `before-registry-init.ts` | `beforeRegistryInit` | Before the ECS registry initializes | +| `after-registry-init.ts` | `afterRegistryInit` | After the ECS registry initializes | +| `before-run.ts` | `beforeRun` | Before the game loop starts | +| `after-run.ts` | `afterRun` | After the game loop ends | + +## Examples + +**Generate a TypeScript client part:** + +```bash +schematics @nanoforge-dev/schematics:part-base --part=client +``` + +**Generate a server part with init hooks:** + +```bash +schematics @nanoforge-dev/schematics:part-base \ + --part=server \ + --initFunctions=true +``` + +**Generate a JavaScript client in a specific directory:** + +```bash +schematics @nanoforge-dev/schematics:part-base \ + --part=client \ + --language=js \ + --directory=./my-game +``` diff --git a/docs/docs/schematics/2-schematics/4-part-main.mdx b/docs/docs/schematics/2-schematics/4-part-main.mdx new file mode 100644 index 0000000..60a88dc --- /dev/null +++ b/docs/docs/schematics/2-schematics/4-part-main.mdx @@ -0,0 +1,77 @@ +--- +title: Part Main +description: Generate the main entry point for a NanoForge client or server part +--- + +import { Tooltip } from "mintlify/components"; + +## Overview + +The `part-main` schematic reads the `.nanoforge/{part}.save.json` file produced by `part-base` and generates a fully wired `main.ts` (or `main.js`) entry point. It registers all libraries, components, systems, and entities declared in the save file. + + + This schematic must be run after `part-base`. + + +## Usage + +```bash +schematics @nanoforge-dev/schematics:part-main --part=client +``` + +## Options + +| Option | Type | Default | Description | +| --------------- | ---------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `part` | `"client" \| "server"` | _(required)_ | The part of the application to generate | +| `directory` | `string` | `.` | Project root directory | +| `language` | `"ts" \| "js"` | `ts` | Language of the generated entry point | +| `initFunctions` | `boolean` | `false` | Wire init lifecycle functions into the main file | +| `saveFile` | `string` | — | Custom path to the save JSON file | +| `outFile` | `string` | — | Custom output path for the generated main file | +| `editor` | `boolean` | `false` | Generate the editor variant of the main file | + +## Output path resolution + +The generated file is placed at one of the following paths, in priority order: + +1. `outFile` — if provided explicitly +2. `.nanoforge/editor/{part}/main.{language}` — if `editor: true` +3. `{part}/main.{language}` — default + +## How it works + +`part-main` reads `{directory}/.nanoforge/{part}.save.json`, builds the full application bootstrap code, then writes the result as the entry point. Every time you add a component, system, or entity through the NanoForge CLI or editor, regenerating `part-main` keeps the entry point in sync with the save file. + +## Examples + +**Generate a client main file:** + +```bash +schematics @nanoforge-dev/schematics:part-main --part=client +``` + +**Generate a server main file with init hooks:** + +```bash +schematics @nanoforge-dev/schematics:part-main \ + --part=server \ + --initFunctions=true +``` + +**Generate with a custom save file and output path:** + +```bash +schematics @nanoforge-dev/schematics:part-main \ + --part=client \ + --saveFile=./custom.save.json \ + --outFile=./src/client/main.ts +``` + +**Regenerate the editor entry point:** + +```bash +schematics @nanoforge-dev/schematics:part-main \ + --part=client \ + --editor=true +``` diff --git a/docs/docs/schematics/2-schematics/5-docker.mdx b/docs/docs/schematics/2-schematics/5-docker.mdx new file mode 100644 index 0000000..ce32b7d --- /dev/null +++ b/docs/docs/schematics/2-schematics/5-docker.mdx @@ -0,0 +1,70 @@ +--- +title: Docker +description: Generate a Dockerfile for a NanoForge application +--- + +import { Tooltip } from "mintlify/components"; + +## Overview + +The `docker` schematic generates a production-ready `Dockerfile` and a `.dockerignore` at the specified directory. The Dockerfile is tailored to the package manager you use — selecting the correct base image, lockfile, and install command automatically. + +## Usage + +```bash +schematics @nanoforge-dev/schematics:docker +``` + +## Options + +| Option | Type | Default | Description | +| ---------------- | ------------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `directory` | `string` | `.` | Destination directory for the generated files | +| `packageManager` | `"npm" \| "yarn" \| "pnpm" \| "bun"` | `npm` | Package manager used by the application | + +## Generated files + +``` +{directory}/ +├── Dockerfile +└── .dockerignore +``` + +## Dockerfile behaviour by package manager + +| Package manager | Base image | Install command | +| --------------- | -------------- | -------------------------------------------------- | +| `npm` | `node:25-slim` | `npm install --no-lockfile` | +| `yarn` | `node:25-slim` | `yarn install --frozen-lockfile` | +| `pnpm` | `node:25-slim` | `pnpm install --frozen-lockfile --allow-build=bun` | +| `bun` | `oven/bun:1.3` | `bun install --frozen-lockfile` | + +The Dockerfile follows a standard multi-step approach: + +1. Copy `package.json` and the lockfile. +2. Install dependencies with frozen lockfile for reproducible builds. +3. Copy the rest of the source. +4. Run `build`. +5. Start with `start`. + +## Examples + +**Generate a Dockerfile for an npm project:** + +```bash +schematics @nanoforge-dev/schematics:docker +``` + +**Generate a Dockerfile for a pnpm project:** + +```bash +schematics @nanoforge-dev/schematics:docker --packageManager=pnpm +``` + +**Generate in a specific directory:** + +```bash +schematics @nanoforge-dev/schematics:docker \ + --packageManager=bun \ + --directory=./my-game +``` diff --git a/docs/docs/schematics/2-schematics/6-component.mdx b/docs/docs/schematics/2-schematics/6-component.mdx new file mode 100644 index 0000000..9b14080 --- /dev/null +++ b/docs/docs/schematics/2-schematics/6-component.mdx @@ -0,0 +1,120 @@ +--- +title: Component +description: Generate a NanoForge ECS component +--- + +import Tooltip from "mint/Tooltip"; + +## Overview + +The `component` schematic scaffolds a new ECS component class for a NanoForge client or server part. The generated file includes the component class, a default export required for code generation, and an `EDITOR_COMPONENT_MANIFEST` required for the NanoForge editor to display and configure the component. + +## Usage + +```bash +schematics @nanoforge-dev/schematics:component --part=client --name=health +``` + +## Options + +| Option | Type | Default | Description | +| ----------- | ---------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | `string` | `example` | Component name | +| `part` | `"client" \| "server"` | _(required)_ | The part of the application this component belongs to | +| `directory` | `string` | `.` | Destination directory for the generated file | +| `language` | `"ts" \| "js"` | `ts` | Language to use for the generated file | + +## Generated file + +`{directory}/{name}.component.ts` + +```ts +import { type EditorComponentManifest } from "@nanoforge-dev/ecs-client"; + +export class HealthComponent { + name = this.constructor.name; + + constructor( + public paramA: string, + public paramB: number, + public paramC: boolean = false, + ) {} + + get foo() { + return "bar"; + } + + get paramAOrDefault() { + return this.paramC ? this.paramA : "default"; + } + + addOne() { + this.paramB += 1; + } +} + +// Required to generate code +export default HealthComponent.name; + +// Required for the editor to display the component and generate code +export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { + name: "Health", + description: "Health component description", + params: [ + { + type: "string", + name: "paramA", + description: "Param A description", + example: "Example value", + }, + { + type: "number", + name: "paramB", + description: "Param B description", + example: 3, + }, + { + type: "boolean", + name: "paramC", + description: "Param C description", + example: true, + default: false, + optional: true, + }, + ], +}; +``` + +## Editor manifest + +The `EDITOR_COMPONENT_MANIFEST` export is required for the NanoForge editor to discover the component, render its configuration panel, and generate code. Each `params` entry describes one constructor parameter: + +| Field | Required | Description | +| ------------- | -------- | ------------------------------------------------------------ | +| `type` | Yes | Parameter type: `"string"`, `"number"`, or `"boolean"` | +| `name` | Yes | Constructor parameter name | +| `description` | Yes | Human-readable description shown in the editor | +| `example` | Yes | Example value used in code generation | +| `default` | No | Default value when the parameter is optional | +| `optional` | No | When `true`, the editor treats the parameter as non-required | + +## Examples + +**Generate a `PlayerComponent` for the client:** + +```bash +schematics @nanoforge-dev/schematics:component \ + --part=client \ + --name=player \ + --directory=client/components +``` + +**Generate a JavaScript server component:** + +```bash +schematics @nanoforge-dev/schematics:component \ + --part=server \ + --name=network-player \ + --language=js \ + --directory=server/components +``` diff --git a/docs/docs/schematics/2-schematics/6-system.mdx b/docs/docs/schematics/2-schematics/6-system.mdx new file mode 100644 index 0000000..51ecf8d --- /dev/null +++ b/docs/docs/schematics/2-schematics/6-system.mdx @@ -0,0 +1,105 @@ +--- +title: System +description: Generate a NanoForge ECS system +--- + +import { Tooltip } from "mintlify/components"; + +## Overview + +The `system` schematic scaffolds a new ECS system function for a NanoForge client or server part. The generated file includes the system function, a default export required for code generation, and an `EDITOR_SYSTEM_MANIFEST` required for the NanoForge editor to display and configure the system. + +## Usage + +```bash +schematics @nanoforge-dev/schematics:system --part=client --name=movement +``` + +## Options + +| Option | Type | Default | Description | +| ----------- | ---------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `name` | `string` | `example` | System name | +| `part` | `"client" \| "server"` | _(required)_ | The part of the application this system belongs to | +| `directory` | `string` | `.` | Destination directory for the generated file | +| `language` | `"ts" \| "js"` | `ts` | Language to use for the generated file | + +## Generated file + +`{directory}/{name}.system.ts` + +```ts +import { type Context } from "@nanoforge-dev/common"; +import { type EditorSystemManifest, type Registry } from "@nanoforge-dev/ecs-client"; + +import { ExampleComponent } from "../components/example.component"; + +export const movementSystem = (registry: Registry, ctx: Context) => { + const entities = registry.getZipper([ExampleComponent]); + + entities.forEach((entity) => { + if (entity.ExampleComponent.paramA === "end") { + ctx.app.setIsRunning(false); + return; + } + + if (entity.ExampleComponent.paramB === 0) entity.ExampleComponent.paramA = "end"; + + if (entity.ExampleComponent.paramB >= 0) + entity.ExampleComponent.paramB = entity.ExampleComponent.paramB - 1; + }); +}; + +// Required to generate code +export default movementSystem.name; + +// Required for the editor to display the system and generate code +export const EDITOR_SYSTEM_MANIFEST: EditorSystemManifest = { + name: "movement", + description: + "This system end the game when paramB reaches 0 for any entity with ExampleComponent", + dependencies: ["ExampleComponent"], +}; +``` + +## Editor manifest + +The `EDITOR_SYSTEM_MANIFEST` export is required for the NanoForge editor to discover the system and wire it into the game loop. + +| Field | Required | Description | +| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | Yes | System name as displayed in the editor | +| `description` | Yes | Human-readable description of what the system does | +| `dependencies` | Yes | Component names this system depends on | + +## System signature + +Every NanoForge system follows the same function signature: + +```ts +(registry: Registry, ctx: Context) => void +``` + +- **`registry`** — the ECS registry; use `registry.getZipper([...])` to query entities with specific components. +- **`ctx`** — the application context; provides access to `ctx.app`, `ctx.assets`, and other engine services. + +## Examples + +**Generate a `PhysicsSystem` for the client:** + +```bash +schematics @nanoforge-dev/schematics:system \ + --part=client \ + --name=physics \ + --directory=client/systems +``` + +**Generate a JavaScript server system:** + +```bash +schematics @nanoforge-dev/schematics:system \ + --part=server \ + --name=sync-state \ + --language=js \ + --directory=server/systems +``` diff --git a/docs/guides/contributing.mdx b/docs/guides/contributing.mdx deleted file mode 100644 index 756d70e..0000000 --- a/docs/guides/contributing.mdx +++ /dev/null @@ -1,119 +0,0 @@ ---- -title: Contributing -description: Conventions and workflow for contributing to NanoForge Schematics. ---- - -# Contributing - -This guide explains how to contribute to the NanoForge Schematics project. - -## Prerequisites - -- Node.js 25 -- pnpm 10.x - -## Setup - -1. Fork and clone the repository. - - ```bash - git clone https://github.com//schematics.git - cd schematics - ``` - -2. Ensure you are on the main branch. - - ```bash - git checkout main - ``` - -3. Install dependencies. - - ```bash - pnpm install --frozen-lockfile - ``` - - This also sets up Husky git hooks via the `prepare` script. - -4. Create a feature branch. - - ```bash - git checkout -b feat/my-feature - ``` - -## Development Workflow - -1. Make your changes in `src/`. -2. Run formatting and lint fixes with `pnpm format`. -3. Build the project with `pnpm build`. -4. Run the full lint check with `pnpm lint`. - -## Commit Convention - -This project uses Conventional Commits. - -```text -(): - -[optional body] - -[optional footer(s)] -``` - -Valid types include `feat`, `fix`, `docs`, `chore`, `refactor`, `perf`, `test`, and `style`. - -Examples: - -```text -feat(application): add support for custom templates -fix(part-main): correct import ordering for systems -docs: update contributing guide -chore(deps): update angular-devkit to v22 -``` - -Commit messages are validated by `commitlint` via a git hook. - -## Pull Request Process - -1. Push your branch to your fork. -2. Open a pull request against `main`. -3. Ensure the CI pipeline passes. -4. Request a review from a maintainer. -5. Merge once approved. - -## Code Style - -The project enforces consistent code style through ESLint and Prettier. - -Naming conventions: - -- Files: kebab-case -- Classes: PascalCase -- Functions: camelCase -- Constants: SCREAMING_SNAKE_CASE -- Enums: PascalCase enum name with SCREAMING_SNAKE_CASE values - -Import ordering is enforced by the Prettier plugin. - -Template files under `src/libs/**/files/` are excluded from linting and TypeScript compilation. - -## Project Structure - -When adding code, follow the existing structure: - -- Schematics live in `src/libs//` -- Utilities live in `src/utils/` -- Types live in `.d.ts` files alongside implementations or in `src/utils/` - -## Dependencies - -Dependencies are managed through pnpm workspace version catalogs defined in `pnpm-workspace.yaml`. -When adding or updating a dependency, use the catalog reference rather than a direct version. - -## Reporting Issues - -Report bugs and request features on the GitHub Issues page. - -## Security - -For security vulnerabilities, refer to `SECURITY.md` in the repository root. diff --git a/docs/guides/creating-schematics.mdx b/docs/guides/creating-schematics.mdx deleted file mode 100644 index 8cdfc96..0000000 --- a/docs/guides/creating-schematics.mdx +++ /dev/null @@ -1,222 +0,0 @@ ---- -title: Creating a New Schematic -description: Build and register a new schematic in the NanoForge schematics package. ---- - -# Creating a New Schematic - -This guide walks through creating a new schematic for the NanoForge Schematics package. - -## Concepts - -A schematic is a code generator built on the Angular DevKit Schematics framework. Each schematic consists of: - -- A JSON Schema file (`schema.json`) that defines the input options -- Type definitions (`.d.ts`) for the schema and validated options -- A factory function (`.factory.ts`) that transforms input, generates files from templates, and merges them into the target project -- Template files (`files/`) in EJS format that produce the output - -## Step 1: Create the Directory Structure - -Create a new directory under `src/libs/` with the schematic name: - -```text -src/libs/my-schematic/ -+-- my-schematic.factory.ts -+-- my-schematic.options.d.ts -+-- my-schematic.schema.d.ts -+-- schema.json -+-- files/ - +-- ts/ - | +-- ... - +-- js/ - +-- ... -``` - -## Step 2: Define the JSON Schema - -Create `schema.json` to define the input options: - -```json -{ - "$schema": "http://json-schema.org/draft-07/schema", - "$id": "SchematicsNanoForgeMySchematic", - "title": "NanoForge My Schematic Options Schema", - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The name of the application", - "$default": { - "$source": "argv", - "index": 0 - }, - "x-prompt": "What name would you like to use?" - }, - "directory": { - "type": "string", - "description": "Output directory" - }, - "language": { - "type": "string", - "enum": ["ts", "js"], - "description": "Language for generated files", - "default": "ts" - } - }, - "required": ["name"] -} -``` - -Key schema features: - -- `$default.$source: "argv"` reads the value from positional CLI arguments -- `x-prompt` prompts the user interactively if the value is not provided -- `required` lists mandatory fields -- `default` sets fallback values - -## Step 3: Define Type Interfaces - -Create the schema type (`my-schematic.schema.d.ts`): - -```ts -export interface MySchematicSchema { - name: string; - directory?: string; - language?: string; -} -``` - -Create the options type (`my-schematic.options.d.ts`): - -```ts -export interface MySchematicOptions { - name: string; - language: string; -} -``` - -The schema type represents raw user input, while the options type represents the validated and defaulted values used -internally. - -## Step 4: Implement the Factory - -Create `my-schematic.factory.ts` following the transform-generate-merge pattern. - -```ts -import { type Path, join, strings } from "@angular-devkit/core"; -import { - type Rule, - type Source, - apply, - mergeWith, - move, - template, - url, -} from "@angular-devkit/schematics"; - -import { toKebabCase } from "@utils/formatting"; -import { resolvePackageName } from "@utils/name"; - -import { DEFAULT_APP_NAME, DEFAULT_LANGUAGE } from "~/defaults"; - -import { type MySchematicOptions } from "./my-schematic.options"; -import { type MySchematicSchema } from "./my-schematic.schema"; - -const transform = (schema: MySchematicSchema): MySchematicOptions => { - const name = resolvePackageName(toKebabCase(schema.name?.toString() ?? DEFAULT_APP_NAME)); - - return { - name, - language: schema.language ?? DEFAULT_LANGUAGE, - }; -}; - -const generate = (options: MySchematicOptions, path: string): Source => { - return apply(url(join("./files" as Path, options.language)), [ - template({ - ...strings, - ...options, - }), - move(path), - ]); -}; - -export const main = (schema: MySchematicSchema): Rule => { - const options = transform(schema); - return mergeWith(generate(options, schema.directory ?? options.name)); -}; -``` - -## Step 5: Create Template Files - -Template files use EJS syntax for interpolation. Place them under `files/ts/` and `files/js/`. - -Example template file (`files/ts/example.ts`): - -```ts -// Generated for <%= name %> - -export class <%= classify(name) %>Manager { - constructor() { - console.log("<%= name %> initialized"); - } -} -``` - -Available template helpers from `@angular-devkit/core/strings`: - -| Helper | Description | Example | -| ------------ | ------------------------------ | ------------------- | -| `dasherize` | Convert to kebab-case | `MyApp` -> `my-app` | -| `classify` | Convert to PascalCase | `my-app` -> `MyApp` | -| `camelize` | Convert to camelCase | `my-app` -> `MyApp` | -| `underscore` | Convert to snake_case | `MyApp` -> `my_app` | -| `capitalize` | Capitalize first letter | `hello` -> `Hello` | -| `decamelize` | Split camelCase with separator | `myApp` -> `my_app` | - -Dynamic file and directory names use `__variable__` syntax. - -```text -files/ts/__name__/ -> my-project/ -files/ts/__name__.config.ts -> my-project.config.ts -``` - -## Step 6: Register the Schematic - -Add the new schematic to `src/collection.json`. - -```json -{ - "schematics": { - "my-schematic": { - "factory": "./libs/my-schematic/my-schematic.factory#main", - "description": "Create a NanoForge my-schematic.", - "schema": "./libs/my-schematic/schema.json" - } - } -} -``` - -## Step 7: Add the Build Entry - -Add a build entry in `tsdown.config.ts`. - -```ts -export default [ - createTsdownConfig(), - createLibTsdownConfig("application"), - createLibTsdownConfig("configuration"), - createLibTsdownConfig("part-base"), - createLibTsdownConfig("part-main"), - createLibTsdownConfig("my-schematic"), -]; -``` - -## Step 8: Build and Verify - -Build the project and verify everything compiles: - -```bash -pnpm build -``` diff --git a/docs/guides/getting-started.mdx b/docs/guides/getting-started.mdx deleted file mode 100644 index 23a9f80..0000000 --- a/docs/guides/getting-started.mdx +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: Getting Started -description: Install and use @nanoforge-dev/schematics to scaffold NanoForge projects. ---- - -# Getting Started - -This guide explains how to install and use `@nanoforge-dev/schematics` to scaffold NanoForge game engine projects. - -## Prerequisites - -- Node.js 25 or later -- A package manager: npm, yarn, pnpm, or bun - -## Installation - -Install the schematics package globally or as a project dependency: - -```bash -# npm -npm install -g @nanoforge-dev/schematics - -# pnpm -pnpm add -g @nanoforge-dev/schematics - -# yarn -yarn global add @nanoforge-dev/schematics - -# bun -bun add -g @nanoforge-dev/schematics -``` - -## Create a New Application - -Use the `application` schematic to scaffold a complete NanoForge project: - -```bash -schematics @nanoforge-dev/schematics:application my-game -``` - -This creates a `my-game/` directory with the project structure, package metadata, and base configuration files. - -### Options - -```bash -schematics @nanoforge-dev/schematics:application my-game \ - --language=ts \ - --packageManager=pnpm \ - --server=true \ - --author="Your Name" \ - --version="1.0.0" \ - --description="My NanoForge game" -``` - -| Flag | Description | -| ------------------ | --------------------------------------------- | -| `--language` | `ts` (default) or `js` | -| `--packageManager` | `npm` (default), `yarn`, `pnpm`, or `bun` | -| `--server` | Set to `true` to include server configuration | -| `--strict` | Set to `false` to disable strict mode | -| `--author` | Author name for `package.json` | -| `--version` | Initial version | -| `--description` | Project description for `package.json` | -| `--directory` | Custom output directory | - -## Generate Configuration - -Create or update the `nanoforge.config.json` file: - -```bash -schematics @nanoforge-dev/schematics:configuration my-game -``` - -With server support: - -```bash -schematics @nanoforge-dev/schematics:configuration my-game --server=true -``` - -If a `nanoforge.config.json` already exists in the directory tree, the schematic deep-merges new values into the -existing configuration rather than overwriting it. - -## Generate Client or Server Base Code - -Use the `part-base` schematic to scaffold the directory structure for a client or server part: - -```bash -# Generate client base -schematics @nanoforge-dev/schematics:part-base my-game --part=client - -# Generate server base -schematics @nanoforge-dev/schematics:part-base my-game --part=server -``` - -With lifecycle init functions: - -```bash -schematics @nanoforge-dev/schematics:part-base my-game \ - --part=client \ - --initFunctions=true -``` - -This generates example components, example systems, and optionally six lifecycle hook functions in the `init/` -directory. - -## Generate the Main Entry Point - -Use the `part-main` schematic to generate a `main.ts` file from a `.nanoforge/.save.json` metadata file: - -```bash -# Generate client main file -schematics @nanoforge-dev/schematics:part-main my-game --part=client - -# Generate server main file -schematics @nanoforge-dev/schematics:part-main my-game --part=server - -# With init functions -schematics @nanoforge-dev/schematics:part-main my-game \ - --part=client \ - --initFunctions=true - -# Custom save file location -schematics @nanoforge-dev/schematics:part-main my-game \ - --part=client \ - --saveFile=custom/path/save.json -``` - -The save file defines which libraries, components, systems, and entities should be wired into the main entry point. - -## Typical Workflow - -1. Scaffold the project. - - ```bash - schematics @nanoforge-dev/schematics:application my-game --server=true - ``` - -2. Install dependencies. - - ```bash - cd my-game - npm install - ``` - -3. Generate the client base. - - ```bash - schematics @nanoforge-dev/schematics:part-base my-game --part=client --initFunctions=true - ``` - -4. Generate the server base if you are using server support. - - ```bash - schematics @nanoforge-dev/schematics:part-base my-game --part=server --initFunctions=true - ``` - -5. Edit the save files in `.nanoforge/client.save.json` and `.nanoforge/server.save.json`. -6. Generate the main entry points. -7. Start developing your game logic in the generated components, systems, and init functions. diff --git a/docs/guides/index.mdx b/docs/guides/index.mdx deleted file mode 100644 index a0a0f5e..0000000 --- a/docs/guides/index.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: Guides -description: Practical guides for using and contributing to the schematics package. ---- - -# Guides - -Start here if you want to install, use, test, or extend the schematics package. - -- [Getting Started](getting-started) -- [Testing](testing) -- [Contributing](contributing) -- [Creating a New Schematic](creating-schematics) diff --git a/docs/guides/testing.mdx b/docs/guides/testing.mdx deleted file mode 100644 index 30528b4..0000000 --- a/docs/guides/testing.mdx +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: Testing -description: Run lint, type checking, and build verification for the schematics package. ---- - -# Testing - -This guide covers how to run quality checks on the NanoForge Schematics codebase. - -## Running Lint Checks - -The project uses ESLint for code quality and Prettier for formatting. - -```bash -pnpm lint -``` - -This runs: - -1. `prettier --check .` to verify formatting -2. `eslint --format=pretty src` to check TypeScript source files for lint errors - -## Auto-Fixing Issues - -```bash -pnpm format -``` - -This runs: - -1. `prettier --write .` to reformat files -2. `eslint --fix --format=pretty src` to auto-fix lint issues where possible - -## Type Checking - -TypeScript type checking is part of the build process. - -```bash -# Type check only -npx tsc --noEmit - -# Full build -pnpm build -``` - -## Building - -Verify that the project builds without errors: - -```bash -pnpm build -``` - -This runs type checking, bundles the code with tsdown, and copies template files and schemas to `dist/`. - -## Pre-Commit Hooks - -The project has pre-commit hooks configured via Husky and lint-staged. When you commit, the following checks run -automatically on staged files: - -- Prettier formats all staged files -- ESLint fixes lint issues in staged `src/**/*.ts` files - -## CI Pipeline - -The GitHub Actions CI pipeline runs on every pull request targeting `main`, every push to `main`, and manual dispatch. -It runs `pnpm lint` and blocks merging if it fails. - -## Verifying a Full Check - -Before submitting a pull request, run the full check locally: - -```bash -pnpm format && pnpm build -``` diff --git a/docs/index.mdx b/docs/index.mdx deleted file mode 100644 index 17e2a53..0000000 --- a/docs/index.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: NanoForge Schematics -description: Overview of the schematics and documentation for @nanoforge-dev/schematics. ---- - -# NanoForge Schematics - -`@nanoforge-dev/schematics` provides Angular DevKit schematics for scaffolding NanoForge game engine projects. -It generates project structures, configuration files, client and server base code, Dockerfiles, and main entry -points from project metadata. - -The package currently ships these schematics: - -- `application` -- `configuration` -- `part-base` -- `part-main` -- `docker` -- `component` -- `system` - -## Documentation - -- [Technical Documentation](docs/index) -- [Guides](guides/index)