Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package-lock.json
yarn.lock

dist/
docs/typedocs
packages/core/docs/typedocs
test-results/
*.tsbuildinfo
.vite/
Expand Down
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
dist/
node_modules/
test-results/
docs/typedocs
packages/core/docs/typedocs
pnpm-lock.yaml
10 changes: 7 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ Guidance for coding agents working in this repository.

## What this is

This is a monorepo containing multiple packages related to 3D asset processing.

`@babylonjs/node-assets` is an experimental TypeScript library for reading many 3D source formats and producing web-ready formats. The package targets Node and browser environments and bundles all required converters and compressors.

`@babylonjs/node-assets-cli` is a command-line interface for building and running pipelines with `@babylonjs/node-assets`.

## Getting started

- Read [[CONTRIBUTING.md]] for setup and scripts.
- Read [[docs/usage.md]] for user-facing behavior contracts.
- Read [[docs/architecture/index.md]] for intended implementation details and more behavior contracts.
- Read [[packages/core/docs/usage.md]] for user-facing behavior contracts.
- Read [[packages/core/docs/basics.md]] and [[packages/core/docs/blocks.md]] for intended implementation details and more behavior contracts.

## Guidelines

Expand All @@ -21,7 +25,7 @@ Guidance for coding agents working in this repository.

## Planning

- To propose a feature, first update or add the smallest task-focused guide, section, or note in [[docs/usage.md]].
- To propose a feature, first update or add the smallest task-focused guide, section, or note in [[packages/core/docs/usage.md]].

## Style

Expand Down
20 changes: 19 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ pnpm install
pnpm build
```

The `packages/core` workspace contains the `@babylonjs/node-assets` library.
`packages/cli` contains the command-line package and depends on the core package.
Build both before running the local CLI:

```sh
pnpm cli pipeline input.gltf ktx2 draco output.glb
pnpm cli --help
```

## Scripts

```sh
Expand All @@ -22,8 +31,17 @@ pnpm lint:fix # ESLint autofix
pnpm format # Write Prettier formatting
pnpm test # Run Vitest
pnpm test:watch # Run Vitest in watch mode
pnpm build # Build with Vite and emit dist/
pnpm build # Build the library, then the CLI
pnpm build:core # Build only the library into packages/core/dist/
pnpm cli # Run the built CLI (append pipeline arguments)
pnpm typedocs # Generate the TypeDoc API reference
```

The shared lint, format, and typecheck commands cover both packages. CLI tests
live in `tests/e2e/cli.test.ts` and build isolated package fixtures:

```sh
pnpm test tests/e2e/cli.test.ts
```

Make sure you've run `pnpm lint`, `pnpm test`, and `pnpm build` before opening a pull request.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Node Assets

A graph-based system for preparing 3D assets for the web.
A TypeScript library to help prepare 3D assets for the web.

> **⚠️ Notice:** This package is experimental. API is subject to change and not intended for production use.

Expand Down
2 changes: 1 addition & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const internalNamingPlugin = {

export default tseslint.config(
{
ignores: ["dist/**", "node_modules/**", "test-results/**", "docs/**", "**/*.md"],
ignores: ["**/dist/**", "**/node_modules/**", "test-results/**", "docs/**", "packages/core/docs/typedocs/**", "**/*.md"],
},

js.configs.recommended,
Expand Down
66 changes: 11 additions & 55 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,73 +1,29 @@
{
"name": "@babylonjs/node-assets",
"version": "0.1.0",
"name": "node-assets-workspace",
"private": true,
"license": "Apache-2.0",
"type": "module",
"sideEffects": false,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./package.json": "./package.json"
},
"files": [
"dist"
],
"keywords": [
"babylon",
"babylonjs",
"3d",
"gltf",
"glb",
"asset-pipeline",
"draco",
"meshopt",
"ktx2"
],
"repository": {
"type": "git",
"url": "git+https://github.com/BabylonJS/Node-Assets.git"
},
"bugs": {
"url": "https://github.com/BabylonJS/Node-Assets/issues"
},
"homepage": "https://github.com/BabylonJS/Node-Assets#readme",
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
},
"packageManager": "pnpm@11.9.0",
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "vite build",
"build": "pnpm --filter @babylonjs/node-assets build && pnpm --filter @babylonjs/node-assets-cli build",
"build:core": "pnpm --filter @babylonjs/node-assets build",
"cli": "node packages/cli/bin/node-assets.cjs",
"typecheck": "tsc -p tsconfig.json --noEmit",
"lint": "eslint . && pnpm typecheck",
"lint:fix": "eslint . --fix",
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\" \"*.config.ts\" \"*.config.mjs\"",
"format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\" \"*.config.ts\" \"*.config.mjs\"",
"format": "prettier --write \"packages/**/*.{ts,cjs}\" \"tests/**/*.ts\" \"*.config.ts\" \"*.config.mjs\"",
"format:check": "prettier --check \"packages/**/*.{ts,cjs}\" \"tests/**/*.ts\" \"*.config.ts\" \"*.config.mjs\"",
"test": "vitest run",
"test:watch": "vitest",
"typedocs": "typedoc"
},
"dependencies": {
"@babylonjs/core": "^9.21.2",
"@babylonjs/loaders": "^9.21.2",
"@babylonjs/serializers": "^9.21.2",
"@gltf-transform/core": "4.5.0",
"@gltf-transform/extensions": "4.5.0",
"@gltf-transform/functions": "4.5.0",
"@types/draco3dgltf": "1.4.3",
"babylonpress-ktx2-encoder": "0.6.0",
"draco3dgltf": "1.5.7",
"meshoptimizer": "1.2.0",
"sharp": "0.35.4"
"typedocs": "pnpm --filter @babylonjs/node-assets typedocs"
},
"devDependencies": {
"@babylonjs/node-assets": "workspace:*",
"@eslint/js": "^10.0.1",
"@gltf-transform/core": "4.5.0",
"@gltf-transform/extensions": "4.5.0",
"@types/node": "^26.1.1",
"eslint": "^10.7.0",
"eslint-config-prettier": "^10.1.8",
Expand Down
43 changes: 43 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Node Assets CLI

A command-line interface for building and running pipelines with `@babylonjs/node-assets`.

> **⚠️ Notice:** This package is experimental. API is subject to change and not intended for production use.

## Usage

### Pipelines

A pipeline is a sequence of operations applied to a 3D asset. The CLI allows you to define and run pipelines using the `node-assets pipeline` command.

```sh
node-assets pipeline input.gltf output.glb
node-assets pipeline input.glb ktx2 draco output.glb
```

The command syntax is:

```text
node-assets pipeline <input> [operation...] <output>
```

| Element | Supported values |
| --------- | -------------------------- |
| Input | `.gltf`, `.glb` |
| Output | `.glb` |
| Operation | `draco`, `meshopt`, `ktx2` |

Without specifying operations, the CLI reads the input and writes it back out as the target output format.

Run `node-assets --help` for command help or `node-assets --version` for the
installed version.

## Develop locally

From the repository root:

```sh
pnpm install
pnpm build
pnpm cli pipeline input.gltf ktx2 draco output.glb
```
10 changes: 10 additions & 0 deletions packages/cli/bin/node-assets.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env node

if (require.main === module) {
import("../dist/cli.js")
.then(({ runCliAsync }) => runCliAsync(process.argv.slice(2)))
.catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exitCode = 1;
});
}
33 changes: 33 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@babylonjs/node-assets-cli",
"private": true,
"version": "0.0.0",
"description": "Build and run Node Assets pipelines from the command line.",
"license": "Apache-2.0",
"type": "module",
"sideEffects": false,
"bin": {
"node-assets": "./bin/node-assets.cjs"
},
"files": [
"bin",
"dist"
],
"repository": {
"type": "git",
"url": "git+https://github.com/BabylonJS/Node-Assets.git",
"directory": "packages/cli"
},
"bugs": {
"url": "https://github.com/BabylonJS/Node-Assets/issues"
},
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
},
"scripts": {
"build": "vite build"
},
"dependencies": {
"@babylonjs/node-assets": "workspace:*"
}
}
75 changes: 75 additions & 0 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { stat, writeFile } from "node:fs/promises";
import { resolve } from "node:path";
import { parseArgs } from "node:util";

import { version } from "../package.json";
import { createPipelineAsync, getPipelineDefinitions } from "./pipeline";

export async function runCliAsync(args: string[]): Promise<void> {
const { values, positionals } = parseArgs({
args,
allowPositionals: true,
strict: true,
options: {
help: { type: "boolean", short: "h" },
version: { type: "boolean", short: "v" },
},
});

if (values.help || args.length === 0) {
printHelp();
return;
}
if (values.version) {
console.log(version);
return;
}

const [command, input, ...remaining] = positionals;
if (command !== "pipeline") {
throw new Error(`Unknown command "${command ?? ""}". Run node-assets --help for usage.`);
}
const output = remaining.pop();
if (input === undefined || output === undefined) {
throw new Error("A pipeline requires an input file and an output file.");
}

const inputPath = resolve(input);
const outputPath = resolve(output);
if (!(await stat(inputPath)).isFile()) {
throw new Error(`Input is not a regular file: ${inputPath}`);
}

const asset = await createPipelineAsync({ inputPath, outputPath, blockNames: remaining });
try {
const file = await asset.executeAsync();
await writeFile(outputPath, new Uint8Array(await file.arrayBuffer()), { flag: "wx" });
console.log(`Wrote ${outputPath}`);
} finally {
asset.dispose();
}
}

function printHelp(): void {
const { inputs, outputs, operations } = getPipelineDefinitions();
console.log(
[
"Usage: node-assets pipeline <input> [operations...] <output>",
"",
`Supported file types:`,
`Input: ${inputs.flatMap(({ extensions }) => extensions).join(", ")}`,
`Output: ${outputs.flatMap(({ extensions }) => extensions).join(", ")}`,
"Paths are local and relative to the working directory.",
"",
"Operations:",
...operations.map(({ name, description }) => ` ${name.padEnd(9)}${description}`),
"",
"Options:",
" -h, --help Show this help",
" -v, --version Show the CLI version",
" -- End options before hyphen-prefixed paths",
"",
"Example: node-assets pipeline input.gltf ktx2 draco output.glb",
].join("\n")
);
}
Loading
Loading