diff --git a/.gitignore b/.gitignore index 86f13c5..d560b2c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ package-lock.json yarn.lock dist/ -docs/typedocs +packages/core/docs/typedocs test-results/ *.tsbuildinfo .vite/ diff --git a/.prettierignore b/.prettierignore index c907ed1..e8be54f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,5 @@ dist/ node_modules/ test-results/ -docs/typedocs +packages/core/docs/typedocs pnpm-lock.yaml diff --git a/AGENTS.md b/AGENTS.md index d2eac67..5691312 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 @@ -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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f088fd2..f4863de 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 @@ -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. diff --git a/README.md b/README.md index 794406d..5625d40 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/eslint.config.mjs b/eslint.config.mjs index d4d91f2..4a9cbca 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -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, diff --git a/package.json b/package.json index 3b3a02a..59c066f 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/packages/cli/README.md b/packages/cli/README.md new file mode 100644 index 0000000..a6e5f75 --- /dev/null +++ b/packages/cli/README.md @@ -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 [operation...] +``` + +| 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 +``` diff --git a/packages/cli/bin/node-assets.cjs b/packages/cli/bin/node-assets.cjs new file mode 100755 index 0000000..5c4d1fe --- /dev/null +++ b/packages/cli/bin/node-assets.cjs @@ -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; + }); +} diff --git a/packages/cli/package.json b/packages/cli/package.json new file mode 100644 index 0000000..1813e5b --- /dev/null +++ b/packages/cli/package.json @@ -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:*" + } +} diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts new file mode 100644 index 0000000..812b5b7 --- /dev/null +++ b/packages/cli/src/cli.ts @@ -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 { + 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 [operations...] ", + "", + `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") + ); +} diff --git a/packages/cli/src/pipeline.ts b/packages/cli/src/pipeline.ts new file mode 100644 index 0000000..c4da63f --- /dev/null +++ b/packages/cli/src/pipeline.ts @@ -0,0 +1,76 @@ +import { extname } from "node:path"; + +import type * as NodeAssets from "@babylonjs/node-assets"; + +export function getPipelineDefinitions() { + return { + inputs: [ + { + extensions: [".gltf", ".glb"], + create: (library: typeof NodeAssets, path: string) => new library.GltfInputBlock({ input: path }), + }, + ], + outputs: [ + { + extensions: [".glb"], + create: (library: typeof NodeAssets) => new library.GltfOutputBlock(), + }, + ], + operations: [ + { + name: "draco", + description: "Compress geometry with Draco", + create: (library: typeof NodeAssets) => new library.EncodeDracoBlock(), + }, + { + name: "meshopt", + description: "Compress geometry with Meshopt", + create: (library: typeof NodeAssets) => new library.EncodeMeshoptBlock(), + }, + { + name: "ktx2", + description: "Encode textures as KTX2", + create: (library: typeof NodeAssets) => new library.EncodeKTX2Block(), + }, + ], + }; +} + +interface PipelineOptions { + readonly inputPath: string; + readonly outputPath: string; + readonly blockNames: readonly string[]; +} + +export async function createPipelineAsync({ inputPath, outputPath, blockNames }: PipelineOptions) { + const definitions = getPipelineDefinitions(); + const inputExtension = extname(inputPath).toLowerCase(); + const outputExtension = extname(outputPath).toLowerCase(); + const inputDefinition = definitions.inputs.find(({ extensions }) => extensions.includes(inputExtension)); + const outputDefinition = definitions.outputs.find(({ extensions }) => extensions.includes(outputExtension)); + if (inputDefinition === undefined) { + throw new Error(`Unsupported input extension "${inputExtension}".`); + } + if (outputDefinition === undefined) { + throw new Error(`Unsupported output extension "${outputExtension}".`); + } + const transforms = blockNames.map((name) => { + const definition = definitions.operations.find((block) => block.name === name); + if (definition === undefined) { + throw new Error(`Unknown operation "${name}".`); + } + return definition; + }); + + const library = await import("@babylonjs/node-assets"); + const source = inputDefinition.create(library, inputPath); + let previous = source.output; + for (const transform of transforms) { + const block = transform.create(library); + previous.connectTo(block.input); + previous = block.output; + } + const destination = outputDefinition.create(library); + previous.connectTo(destination.input); + return new library.NodeAsset({ name: "pipeline", outputBlock: destination }); +} diff --git a/packages/cli/vite.config.ts b/packages/cli/vite.config.ts new file mode 100644 index 0000000..1ac31fc --- /dev/null +++ b/packages/cli/vite.config.ts @@ -0,0 +1,21 @@ +import { isBuiltin } from "node:module"; +import { fileURLToPath } from "node:url"; + +import { defineConfig } from "vite"; + +export default defineConfig({ + root: fileURLToPath(new URL(".", import.meta.url)), + build: { + target: "node20", + minify: false, + sourcemap: true, + lib: { + entry: "src/cli.ts", + formats: ["es"], + fileName: () => "cli.js", + }, + rollupOptions: { + external: (id) => isBuiltin(id) || id === "@babylonjs/node-assets", + }, + }, +}); diff --git a/packages/core/README.md b/packages/core/README.md new file mode 100644 index 0000000..a651642 --- /dev/null +++ b/packages/core/README.md @@ -0,0 +1,17 @@ +# Node Assets + +A TypeScript library to help prepare 3D assets for the web, with converters & compressors included. + +> **⚠️ Notice:** This package is experimental. API is subject to change and not intended for production use. + +See the [usage guide](docs/usage.md) for examples. [Basics](docs/basics.md) and +[blocks](docs/blocks.md) describe the implementation. + +## Contributing + +See the +[contribution guide](https://github.com/BabylonJS/Node-Assets/blob/main/CONTRIBUTING.md). + +## License + +[Apache-2.0](https://github.com/BabylonJS/Node-Assets/blob/main/LICENSE) diff --git a/packages/core/docs/basics.md b/packages/core/docs/basics.md new file mode 100644 index 0000000..5dd7ada --- /dev/null +++ b/packages/core/docs/basics.md @@ -0,0 +1,57 @@ +# Graph Terminology + +Below is a rough translation guide for common graph terminology to Babylon node editor terminology. + +A graph is a **NodeAsset**. +Nodes are referred to as **blocks** in code contexts and **nodes** in UI contexts. +Sink nodes, or terminal nodes, are **output blocks**. +Edges are **connections**. +End points of an edge are **connection points** in code contexts and **ports** in UI contexts. +The payload carried along an edge is **runtime data**: what a block processes. +The type of payload accepted by an end points is defined by its **connection point type**. +Edges can only be drawn between compatible **connection point types**. +Inbound end points are block **inputs**. +Outbound end points are block **outputs**. + +# Connection Point Types + +Connection points types, in general, come in two forms. + +- File: for format- or byte-level operations. Examples: platform I/O (future) +- Content: for content-level operations. Examples: removing vertices, updating texture pixels + +## Content + +- `Babylon` (future) + - Runtime data: `Scene` (@babylonjs/core) (future) +- `glTF` + - Runtime data: `Document` (@gltf-transform/core) + +Runtime data is passed by reference. + +## File + +(Future) + +# Resources + +Resources are reusable values owned by a pipeline execution's resource scope, such as a shared `PlatformIO` instance. They are created on demand and shared by blocks within that execution. Blocks borrow resources; the scope retains them until execution completes or fails, then performs any required cleanup and releases its references. + +Worker-backed encoding is future work. + +# Blocks + +Blocks are broadly categorized as follows. + +1. Inputs: data. User supplies a value; then, graph-managed data flows out. +2. Mutators: functions. Data is managed by graph in both directions, in and out. +3. Outputs: data. Graph-managed data in, user-facing data out. + +Some other categories: + +- Files +- Selectors +- Transforms: N -> N +- Transcoders: N -> U + +Blocks have input and output connection points. Some might also have additional, optional input connection points. diff --git a/docs/architecture/index.md b/packages/core/docs/blocks.md similarity index 52% rename from docs/architecture/index.md rename to packages/core/docs/blocks.md index 2960dc8..5ba394f 100644 --- a/docs/architecture/index.md +++ b/packages/core/docs/blocks.md @@ -1,60 +1,4 @@ -# Graph Terminology - -Below is a rough translation guide for common graph terminology to Babylon node editor terminology. - -A graph is a **NodeAsset**. -Nodes are referred to as **blocks** in code contexts and **nodes** in UI contexts. -Sink nodes, or terminal nodes, are **output blocks**. -Edges are **connections**. -End points of an edge are **connection points** in code contexts and **ports** in UI contexts. -The payload carried along an edge is **runtime data**: what a block processes. -The type of payload accepted by an end points is defined by its **connection point type**. -Edges can only be drawn between compatible **connection point types**. -Inbound end points are block **inputs**. -Outbound end points are block **outputs**. - -# Connection Point Types - -Connection points types, in general, come in two forms. - -- File: for format- or byte-level operations. Examples: platform I/O (future) -- Content: for content-level operations. Examples: removing vertices, updating texture pixels - -## Content - -- `Babylon` (future) - - Runtime data: `Scene` (@babylonjs/core) -- `glTF` - - Runtime data: `Document` (@gltf-transform/core) - -Runtime data is passed by reference. - -## File - -(Future) - -# Resources - -Resources are reusable values owned by a pipeline execution's resource scope, such as a shared `PlatformIO` instance. They are created on demand and shared by blocks within that execution. Blocks borrow resources; the scope retains them until execution completes or fails, then performs any required cleanup and releases its references. - -Worker-backed encoding is future work. - -# Blocks - -Blocks are broadly categorized as follows. - -1. Inputs: data. User supplies a value; then, graph-managed data flows out. -2. Mutators: functions. Data is managed by graph in both directions, in and out. -3. Outputs: data. Graph-managed data in, user-facing data out. - -Some other categories: - -- Files -- Selectors -- Transforms: N -> N -- Transcoders: N -> U - -Blocks have input and output connection points. Some might also have additional, optional input connection points. +# List of Blocks > **Uses** lists a block's implementation dependencies: libraries, functions, modules, and instances. These may include execution-scoped resources. diff --git a/docs/usage.md b/packages/core/docs/usage.md similarity index 100% rename from docs/usage.md rename to packages/core/docs/usage.md diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000..8b3edb8 --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,60 @@ +{ + "name": "@babylonjs/node-assets", + "private": true, + "version": "0.0.0", + "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", + "directory": "packages/core" + }, + "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" + }, + "scripts": { + "build": "vite build", + "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" + } +} diff --git a/src/blocks/block.ts b/packages/core/src/blocks/block.ts similarity index 100% rename from src/blocks/block.ts rename to packages/core/src/blocks/block.ts diff --git a/src/blocks/blockDefinition.ts b/packages/core/src/blocks/blockDefinition.ts similarity index 100% rename from src/blocks/blockDefinition.ts rename to packages/core/src/blocks/blockDefinition.ts diff --git a/src/blocks/encodeDracoBlock.ts b/packages/core/src/blocks/encodeDracoBlock.ts similarity index 100% rename from src/blocks/encodeDracoBlock.ts rename to packages/core/src/blocks/encodeDracoBlock.ts diff --git a/src/blocks/encodeKtx2Block.ts b/packages/core/src/blocks/encodeKtx2Block.ts similarity index 100% rename from src/blocks/encodeKtx2Block.ts rename to packages/core/src/blocks/encodeKtx2Block.ts diff --git a/src/blocks/encodeMeshoptBlock.ts b/packages/core/src/blocks/encodeMeshoptBlock.ts similarity index 100% rename from src/blocks/encodeMeshoptBlock.ts rename to packages/core/src/blocks/encodeMeshoptBlock.ts diff --git a/src/blocks/fbxInputBlock.ts b/packages/core/src/blocks/fbxInputBlock.ts similarity index 100% rename from src/blocks/fbxInputBlock.ts rename to packages/core/src/blocks/fbxInputBlock.ts diff --git a/src/blocks/gltfInputBlock.ts b/packages/core/src/blocks/gltfInputBlock.ts similarity index 100% rename from src/blocks/gltfInputBlock.ts rename to packages/core/src/blocks/gltfInputBlock.ts diff --git a/src/blocks/gltfOutputBlock.ts b/packages/core/src/blocks/gltfOutputBlock.ts similarity index 100% rename from src/blocks/gltfOutputBlock.ts rename to packages/core/src/blocks/gltfOutputBlock.ts diff --git a/src/blocks/objInputBlock.ts b/packages/core/src/blocks/objInputBlock.ts similarity index 100% rename from src/blocks/objInputBlock.ts rename to packages/core/src/blocks/objInputBlock.ts diff --git a/src/blocks/stlInputBlock.ts b/packages/core/src/blocks/stlInputBlock.ts similarity index 100% rename from src/blocks/stlInputBlock.ts rename to packages/core/src/blocks/stlInputBlock.ts diff --git a/src/connectionPoints/connectionPoint.ts b/packages/core/src/connectionPoints/connectionPoint.ts similarity index 100% rename from src/connectionPoints/connectionPoint.ts rename to packages/core/src/connectionPoints/connectionPoint.ts diff --git a/src/connectionPoints/file.ts b/packages/core/src/connectionPoints/file.ts similarity index 100% rename from src/connectionPoints/file.ts rename to packages/core/src/connectionPoints/file.ts diff --git a/src/connectionPoints/gltfDocument.ts b/packages/core/src/connectionPoints/gltfDocument.ts similarity index 100% rename from src/connectionPoints/gltfDocument.ts rename to packages/core/src/connectionPoints/gltfDocument.ts diff --git a/src/connectionPoints/url.ts b/packages/core/src/connectionPoints/url.ts similarity index 100% rename from src/connectionPoints/url.ts rename to packages/core/src/connectionPoints/url.ts diff --git a/src/helpers/convertBabylonSceneToDocument.ts b/packages/core/src/helpers/convertBabylonSceneToDocument.ts similarity index 100% rename from src/helpers/convertBabylonSceneToDocument.ts rename to packages/core/src/helpers/convertBabylonSceneToDocument.ts diff --git a/src/helpers/isNodeRuntime.ts b/packages/core/src/helpers/isNodeRuntime.ts similarity index 100% rename from src/helpers/isNodeRuntime.ts rename to packages/core/src/helpers/isNodeRuntime.ts diff --git a/src/helpers/loadNodePackageFile.ts b/packages/core/src/helpers/loadNodePackageFile.ts similarity index 100% rename from src/helpers/loadNodePackageFile.ts rename to packages/core/src/helpers/loadNodePackageFile.ts diff --git a/src/helpers/loadSceneWithPlugin.ts b/packages/core/src/helpers/loadSceneWithPlugin.ts similarity index 100% rename from src/helpers/loadSceneWithPlugin.ts rename to packages/core/src/helpers/loadSceneWithPlugin.ts diff --git a/src/index.ts b/packages/core/src/index.ts similarity index 100% rename from src/index.ts rename to packages/core/src/index.ts diff --git a/src/nodeAsset.ts b/packages/core/src/nodeAsset.ts similarity index 100% rename from src/nodeAsset.ts rename to packages/core/src/nodeAsset.ts diff --git a/src/nodeAssetContext.ts b/packages/core/src/nodeAssetContext.ts similarity index 100% rename from src/nodeAssetContext.ts rename to packages/core/src/nodeAssetContext.ts diff --git a/src/resources/dracoEncoderResource.ts b/packages/core/src/resources/dracoEncoderResource.ts similarity index 100% rename from src/resources/dracoEncoderResource.ts rename to packages/core/src/resources/dracoEncoderResource.ts diff --git a/src/resources/gltfDecoderResource.ts b/packages/core/src/resources/gltfDecoderResource.ts similarity index 100% rename from src/resources/gltfDecoderResource.ts rename to packages/core/src/resources/gltfDecoderResource.ts diff --git a/src/resources/meshoptEncoderResource.ts b/packages/core/src/resources/meshoptEncoderResource.ts similarity index 100% rename from src/resources/meshoptEncoderResource.ts rename to packages/core/src/resources/meshoptEncoderResource.ts diff --git a/src/resources/nullEngineResource.ts b/packages/core/src/resources/nullEngineResource.ts similarity index 100% rename from src/resources/nullEngineResource.ts rename to packages/core/src/resources/nullEngineResource.ts diff --git a/src/resources/platformIOResource.ts b/packages/core/src/resources/platformIOResource.ts similarity index 100% rename from src/resources/platformIOResource.ts rename to packages/core/src/resources/platformIOResource.ts diff --git a/src/resources/resource.ts b/packages/core/src/resources/resource.ts similarity index 100% rename from src/resources/resource.ts rename to packages/core/src/resources/resource.ts diff --git a/src/resources/resourceScope.ts b/packages/core/src/resources/resourceScope.ts similarity index 100% rename from src/resources/resourceScope.ts rename to packages/core/src/resources/resourceScope.ts diff --git a/src/types/assets.d.ts b/packages/core/src/types/assets.d.ts similarity index 100% rename from src/types/assets.d.ts rename to packages/core/src/types/assets.d.ts diff --git a/tsconfig.build.json b/packages/core/tsconfig.build.json similarity index 78% rename from tsconfig.build.json rename to packages/core/tsconfig.build.json index 7fbdbf6..ef9ce4f 100644 --- a/tsconfig.build.json +++ b/packages/core/tsconfig.build.json @@ -1,5 +1,5 @@ { - "extends": "./tsconfig.json", + "extends": "../../tsconfig.json", "compilerOptions": { "noEmit": false, "outDir": "./dist", diff --git a/typedoc.json b/packages/core/typedoc.json similarity index 100% rename from typedoc.json rename to packages/core/typedoc.json diff --git a/vite.config.ts b/packages/core/vite.config.ts similarity index 95% rename from vite.config.ts rename to packages/core/vite.config.ts index 34efd10..ff3a332 100644 --- a/vite.config.ts +++ b/packages/core/vite.config.ts @@ -1,4 +1,5 @@ import { isBuiltin } from "node:module"; +import { fileURLToPath } from "node:url"; import { defineConfig, type Plugin } from "vite"; import dts from "vite-plugin-dts"; @@ -6,6 +7,7 @@ import dts from "vite-plugin-dts"; const EmptyNodeBuiltinModuleId = "\0node-assets-empty-node-builtin"; export default defineConfig({ + root: fileURLToPath(new URL(".", import.meta.url)), base: "./", build: { target: "es2022", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b108a24..5fb5133 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,44 +7,19 @@ settings: importers: .: - dependencies: - '@babylonjs/core': - specifier: ^9.21.2 - version: 9.21.2 - '@babylonjs/loaders': - specifier: ^9.21.2 - version: 9.21.2(@babylonjs/core@9.21.2)(babylonjs-gltf2interface@9.21.2) - '@babylonjs/serializers': - specifier: ^9.21.2 - version: 9.21.2(@babylonjs/core@9.21.2)(babylonjs-gltf2interface@9.21.2) + devDependencies: + '@babylonjs/node-assets': + specifier: workspace:* + version: link:packages/core + '@eslint/js': + specifier: ^10.0.1 + version: 10.0.1(eslint@10.8.1) '@gltf-transform/core': specifier: 4.5.0 version: 4.5.0 '@gltf-transform/extensions': specifier: 4.5.0 version: 4.5.0 - '@gltf-transform/functions': - specifier: 4.5.0 - version: 4.5.0(@types/node@26.2.0) - '@types/draco3dgltf': - specifier: 1.4.3 - version: 1.4.3 - babylonpress-ktx2-encoder: - specifier: 0.6.0 - version: 0.6.0 - draco3dgltf: - specifier: 1.5.7 - version: 1.5.7 - meshoptimizer: - specifier: 1.2.0 - version: 1.2.0 - sharp: - specifier: 0.35.4 - version: 0.35.4(@types/node@26.2.0) - devDependencies: - '@eslint/js': - specifier: ^10.0.1 - version: 10.0.1(eslint@10.8.1) '@types/node': specifier: ^26.1.1 version: 26.2.0 @@ -82,6 +57,48 @@ importers: specifier: ^4.1.10 version: 4.1.10(@types/node@26.2.0)(vite@6.4.3(@types/node@26.2.0)(yaml@2.9.0)) + packages/cli: + dependencies: + '@babylonjs/node-assets': + specifier: workspace:* + version: link:../core + + packages/core: + dependencies: + '@babylonjs/core': + specifier: ^9.21.2 + version: 9.21.2 + '@babylonjs/loaders': + specifier: ^9.21.2 + version: 9.21.2(@babylonjs/core@9.21.2)(babylonjs-gltf2interface@9.21.2) + '@babylonjs/serializers': + specifier: ^9.21.2 + version: 9.21.2(@babylonjs/core@9.21.2)(babylonjs-gltf2interface@9.21.2) + '@gltf-transform/core': + specifier: 4.5.0 + version: 4.5.0 + '@gltf-transform/extensions': + specifier: 4.5.0 + version: 4.5.0 + '@gltf-transform/functions': + specifier: 4.5.0 + version: 4.5.0(@types/node@26.2.0) + '@types/draco3dgltf': + specifier: 1.4.3 + version: 1.4.3 + babylonpress-ktx2-encoder: + specifier: 0.6.0 + version: 0.6.0 + draco3dgltf: + specifier: 1.5.7 + version: 1.5.7 + meshoptimizer: + specifier: 1.2.0 + version: 1.2.0 + sharp: + specifier: 0.35.4 + version: 0.35.4(@types/node@26.2.0) + packages: '@babel/helper-string-parser@7.29.7': diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0c7f1e7..ca3ae88 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,5 +6,8 @@ # a platform binary. pnpm blocks dependency build scripts by default and treats # an unapproved script as an install failure. The allowBuilds entry below permits # this script. +packages: + - packages/* + allowBuilds: esbuild: true diff --git a/tests/bundle/browserConsumerBundle.test.ts b/tests/bundle/browserConsumerBundle.test.ts index 7e26a03..6bd29d1 100644 --- a/tests/bundle/browserConsumerBundle.test.ts +++ b/tests/bundle/browserConsumerBundle.test.ts @@ -4,13 +4,13 @@ import { pathToFileURL } from "node:url"; import { build, type Plugin } from "vite"; import { beforeAll, describe, expect, it, vi } from "vitest"; -import type * as NodeAssets from "../../src/index"; +import type * as NodeAssets from "../../packages/core/src/index"; import { parseGlbAsync } from "../helpers/glb"; import { generateGltfJson } from "../helpers/gltf"; const ConsumerModuleId = "\0node-assets-browser-consumer"; const PublishedPackageName = "@babylonjs/node-assets"; -const PublishedEntryPath = fileURLToPath(new URL("../../dist/index.js", import.meta.url)); +const PublishedEntryPath = fileURLToPath(new URL("../../packages/core/dist/index.js", import.meta.url)); describe("browser consumer bundle", () => { beforeAll(buildLibrary, 120_000); @@ -66,7 +66,7 @@ describe("browser consumer bundle", () => { async function buildLibrary(): Promise { await build({ - configFile: fileURLToPath(new URL("../../vite.config.ts", import.meta.url)), + configFile: fileURLToPath(new URL("../../packages/core/vite.config.ts", import.meta.url)), logLevel: "silent", }); } diff --git a/tests/e2e/cli.test.ts b/tests/e2e/cli.test.ts new file mode 100644 index 0000000..694c9e3 --- /dev/null +++ b/tests/e2e/cli.test.ts @@ -0,0 +1,238 @@ +import { cp, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises"; +import { join, relative } from "node:path"; +import { fileURLToPath } from "node:url"; + +import { NodeIO } from "@gltf-transform/core"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; + +import cliPackage from "../../packages/cli/package.json"; +import { EncodeDracoBlock, EncodeKTX2Block, EncodeMeshoptBlock, GltfInputBlock, GltfOutputBlock, NodeAsset } from "../../packages/core/src/index"; +import { buildCliFixtureAsync, runNodeAsync } from "../helpers/cli"; +import { expectKtx2Image, parseGlbAsync } from "../helpers/glb"; +import { generateGlbDataUri, generateGltfJson, generateTexturedGltfJson } from "../helpers/gltf"; + +describe("Node Assets CLI", () => { + let directory: string; + let launcher: string; + let input: string; + let texturedInput: string; + + beforeAll(async () => { + directory = await mkdtemp(fileURLToPath(new URL("../../packages/core/node_modules/.node-assets-cli-", import.meta.url))); + launcher = await buildCliFixtureAsync(directory); + input = join(directory, "input.gltf"); + texturedInput = join(directory, "textured.gltf"); + await writeFile(input, generateGltfJson()); + await writeFile(texturedInput, generateTexturedGltfJson()); + const glbDataUri = generateGlbDataUri(); + await writeFile(join(directory, "input.glb"), Buffer.from(glbDataUri.slice(glbDataUri.indexOf(",") + 1), "base64")); + }, 120_000); + + afterAll(async () => { + if (directory !== undefined) { + await rm(directory, { recursive: true, force: true }); + } + }); + + it.each([[], ["--help"], ["-h"], ["pipeline", "--help"]].map((args) => ({ args })))("shows help for $args", async ({ args }) => { + const result = await runNodeAsync([launcher, ...args], directory); + expect(result.code).toBe(0); + expect(result.stderr).toBe(""); + for (const term of ["pipeline", ".gltf", ".glb", "draco", "meshopt", "ktx2"]) { + expect(result.stdout).toContain(term); + } + }); + + it.each(["--version", "-v"])("reports the package version with %s", async (flag) => { + const result = await runNodeAsync([launcher, flag], directory); + expect(result.code).toBe(0); + expect(result.stdout.trim()).toBe(cliPackage.version); + expect(result.stderr).toBe(""); + }); + + it.each( + [ + ["unknown"], + ["pipeline"], + ["pipeline", "input.gltf"], + ["pipeline", "input.gltf", "unknown", "output.glb"], + ["pipeline", "input.gltf", "DRACO", "output.glb"], + ["pipeline", "--unknown", "input.gltf", "output.glb"], + ["pipeline", "input.gltf", "--force", "output.glb"], + ["pipeline", "input.obj", "output.glb"], + ["pipeline", "input", "output.glb"], + ["pipeline", "input.gltf", "output.gltf"], + ["pipeline", "input.gltf", "output"], + ].map((args) => ({ args })) + )("rejects invalid arguments $args", async ({ args }) => { + const cwd = await mkdtemp(join(directory, "invalid-")); + await cp(input, join(cwd, "input.gltf")); + await cp(input, join(cwd, "input.obj")); + await cp(input, join(cwd, "input")); + const before = await readdir(cwd); + const result = await runNodeAsync([launcher, ...args], cwd); + expect(result.code).toBe(1); + expect(result.stderr.trim()).not.toBe(""); + expect(await readdir(cwd)).toEqual(before); + }); + + it.each(["gltf", "glb"])("round trips a local %s without transforms", async (extension) => { + const output = join(directory, `roundtrip-${extension}.glb`); + const result = await runNodeAsync([launcher, "pipeline", `input.${extension}`, output], directory); + expect(result.code).toBe(0); + const document = await new NodeIO().read(output); + expect(document.getRoot().listMeshes()[0]?.listPrimitives()[0]?.getAttribute("POSITION")?.getArray()).toEqual(new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0])); + const parsed = await readGlbAsync(output); + expect(parsed.json.extensionsUsed ?? []).not.toContain("KHR_draco_mesh_compression"); + expect(parsed.json.extensionsUsed ?? []).not.toContain("EXT_meshopt_compression"); + }); + + it("resolves sibling glTF resources from the input path", async () => { + const sourceDirectory = join(directory, "external"); + await mkdir(sourceDirectory); + const io = new NodeIO(); + await io.write(join(sourceDirectory, "model.gltf"), await io.read(input)); + expect(await readdir(sourceDirectory)).toContain("model.bin"); + + const output = join(directory, "external.glb"); + const result = await runNodeAsync([launcher, "pipeline", "external/model.gltf", output], directory); + expect(result.code).toBe(0); + expect((await io.read(output)).getRoot().listMeshes()).toHaveLength(1); + }); + + it("accepts uppercase extensions and relative paths with spaces from another directory", async () => { + const cwd = join(directory, "working directory"); + await mkdir(cwd); + await cp(input, join(cwd, "source file.GLTF")); + const result = await runNodeAsync([launcher, "pipeline", "source file.GLTF", "output file.GLB"], cwd); + expect(result.code).toBe(0); + expect((await new NodeIO().read(join(cwd, "output file.GLB"))).getRoot().listMeshes()).toHaveLength(1); + }); + + it("accepts hyphen-prefixed paths after the option terminator", async () => { + const cwd = await mkdtemp(join(directory, "hyphens-")); + await cp(input, join(cwd, "-input.gltf")); + const result = await runNodeAsync([launcher, "--", "pipeline", "-input.gltf", "-output.glb"], cwd); + expect(result.code).toBe(0); + expect((await new NodeIO().read(join(cwd, "-output.glb"))).getRoot().listMeshes()).toHaveLength(1); + }); + + it.each([ + { blocks: ["draco"], geometry: "KHR_draco_mesh_compression", ktx2: false }, + { blocks: ["meshopt"], geometry: "EXT_meshopt_compression", ktx2: false }, + { blocks: ["ktx2"], geometry: undefined, ktx2: true }, + { blocks: ["ktx2", "draco"], geometry: "KHR_draco_mesh_compression", ktx2: true }, + { blocks: ["ktx2", "meshopt"], geometry: "EXT_meshopt_compression", ktx2: true }, + ])( + "encodes $blocks through the built package", + async ({ blocks, geometry, ktx2 }) => { + const output = join(directory, `${blocks.join("-")}.glb`); + const result = await runNodeAsync([launcher, "pipeline", texturedInput, ...blocks, output], directory); + expect(result.code).toBe(0); + const parsed = await readGlbAsync(output); + if (geometry !== undefined) { + expect(parsed.json.extensionsUsed).toContain(geometry); + if (geometry === "KHR_draco_mesh_compression") { + expect(parsed.json.meshes?.[0]?.primitives[0]?.extensions?.KHR_draco_mesh_compression).toBeDefined(); + } else { + expect(parsed.json.bufferViews?.some(({ extensions }) => extensions?.EXT_meshopt_compression !== undefined)).toBe(true); + } + } + if (ktx2) { + expect(parsed.json.extensionsUsed).toContain("KHR_texture_basisu"); + expectKtx2Image(parsed); + } + }, + 60_000 + ); + + it.each([ + { blocks: ["draco", "draco"], first: EncodeDracoBlock, second: EncodeDracoBlock }, + { blocks: ["ktx2", "ktx2"], first: EncodeKTX2Block, second: EncodeKTX2Block }, + { blocks: ["draco", "meshopt"], first: EncodeDracoBlock, second: EncodeMeshoptBlock }, + { blocks: ["meshopt", "draco"], first: EncodeMeshoptBlock, second: EncodeDracoBlock }, + ])( + "matches library behavior for $blocks without imposing restrictions", + async ({ blocks, first: FirstBlock, second: SecondBlock }) => { + const source = new GltfInputBlock({ input: texturedInput }); + const first = new FirstBlock(); + const second = new SecondBlock(); + const destination = new GltfOutputBlock(); + source.output.connectTo(first.input); + first.output.connectTo(second.input); + second.output.connectTo(destination.input); + const asset = new NodeAsset({ name: "reference", outputBlock: destination }); + const output = join(directory, `sequence-${blocks.join("-")}.glb`); + + try { + const [reference] = await Promise.allSettled([asset.executeAsync()]); + const result = await runNodeAsync([launcher, "pipeline", texturedInput, ...blocks, output], directory); + if (reference?.status === "fulfilled") { + expect(result.code).toBe(0); + expect((await readGlbAsync(output)).json).toEqual((await parseGlbAsync(reference.value)).json); + } else { + expect(result.code).toBe(1); + expect(result.stderr.trim()).not.toBe(""); + await expect(readFile(output)).rejects.toThrow(); + } + } finally { + asset.dispose(); + } + }, + 60_000 + ); + + it("does not overwrite an existing destination", async () => { + const output = join(directory, "existing.glb"); + const original = Buffer.from("keep this file"); + await writeFile(output, original); + const result = await runNodeAsync([launcher, "pipeline", input, output], directory); + expect(result.code).toBe(1); + expect(result.stderr.trim()).not.toBe(""); + expect(await readFile(output)).toEqual(original); + }); + + it("does not overwrite the input through an equivalent relative output path", async () => { + const source = join(directory, "input.glb"); + const original = await readFile(source); + const result = await runNodeAsync([launcher, "pipeline", source, relative(directory, source)], directory); + expect(result.code).toBe(1); + expect(await readFile(source)).toEqual(original); + }); + + it.each(["missing", "malformed", "directory"])("surfaces a %s input without creating output", async (kind) => { + const source = join(directory, `${kind}.gltf`); + const output = join(directory, `${kind}.glb`); + if (kind === "malformed") { + await writeFile(source, "not glTF"); + } else if (kind === "directory") { + await mkdir(source); + } + const result = await runNodeAsync([launcher, "pipeline", source, output], directory); + expect(result.code).toBe(1); + expect(result.stderr.trim()).not.toBe(""); + await expect(readFile(output)).rejects.toThrow(); + }); + + it("surfaces a missing output parent without creating directories", async () => { + const parent = join(directory, "missing-parent"); + const result = await runNodeAsync([launcher, "pipeline", input, join(parent, "output.glb")], directory); + expect(result.code).toBe(1); + expect(result.stderr.trim()).not.toBe(""); + await expect(readdir(parent)).rejects.toThrow(); + }); + + it("refuses a directory destination", async () => { + const output = join(directory, "destination.glb"); + await mkdir(output); + const result = await runNodeAsync([launcher, "pipeline", input, output], directory); + expect(result.code).toBe(1); + expect(result.stderr.trim()).not.toBe(""); + expect(await readdir(output)).toEqual([]); + }); +}); + +async function readGlbAsync(path: string) { + const bytes = await readFile(path); + return parseGlbAsync(new File([bytes], "scene.glb", { type: "model/gltf-binary" })); +} diff --git a/tests/helpers/cli.ts b/tests/helpers/cli.ts new file mode 100644 index 0000000..53875a5 --- /dev/null +++ b/tests/helpers/cli.ts @@ -0,0 +1,54 @@ +import { execFile } from "node:child_process"; +import { cp, mkdir } from "node:fs/promises"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; + +import { build } from "vite"; + +import cliPackage from "../../packages/cli/package.json"; +import libraryConfig from "../../packages/core/vite.config"; + +export async function buildCliFixtureAsync(directory: string): Promise { + const libraryDirectory = join(directory, "node_modules", "@babylonjs", "node-assets"); + const cliDirectory = join(directory, "node_modules", "@babylonjs", "node-assets-cli"); + await mkdir(libraryDirectory, { recursive: true }); + await mkdir(cliDirectory, { recursive: true }); + await cp(new URL("../../packages/core/package.json", import.meta.url), join(libraryDirectory, "package.json")); + await cp(new URL("../../packages/cli/package.json", import.meta.url), join(cliDirectory, "package.json")); + await cp(new URL("../../packages/cli/bin", import.meta.url), join(cliDirectory, "bin"), { recursive: true }); + + await build({ + ...libraryConfig, + configFile: false, + logLevel: "silent", + // Declaration bundling targets the real package's dist; this isolated fixture only executes JavaScript. + plugins: (libraryConfig.plugins ?? []).filter((plugin) => !plugin || !("name" in plugin) || plugin.name !== "vite:dts"), + build: { ...libraryConfig.build, outDir: join(libraryDirectory, "dist") }, + }); + await build({ + configFile: fileURLToPath(new URL("../../packages/cli/vite.config.ts", import.meta.url)), + logLevel: "silent", + build: { outDir: join(cliDirectory, "dist") }, + }); + + return join(cliDirectory, cliPackage.bin["node-assets"]); +} + +interface CommandResult { + readonly code: number; + readonly stdout: string; + readonly stderr: string; +} + +export function runNodeAsync(args: readonly string[], cwd: string): Promise { + return new Promise((resolve, reject) => { + execFile(process.execPath, args, { cwd, timeout: 30_000 }, (error, stdout, stderr) => { + const code = error?.code ?? 0; + if (typeof code !== "number" || error?.killed) { + reject(error); + return; + } + resolve({ code, stdout, stderr }); + }); + }); +} diff --git a/tests/helpers/numberBlocks.ts b/tests/helpers/numberBlocks.ts index 1240c68..d1c8e71 100644 --- a/tests/helpers/numberBlocks.ts +++ b/tests/helpers/numberBlocks.ts @@ -1,5 +1,5 @@ -import { defineBlock, enumValue } from "../../src/blocks/blockDefinition"; -import { defineConnectionPointType } from "../../src/connectionPoints/connectionPoint"; +import { defineBlock, enumValue } from "../../packages/core/src/blocks/blockDefinition"; +import { defineConnectionPointType } from "../../packages/core/src/connectionPoints/connectionPoint"; export const NumberType = defineConnectionPointType("number", (value): value is number => typeof value === "number"); export const OtherNumberType = defineConnectionPointType("number", (value): value is number => typeof value === "number"); diff --git a/tests/integration/compressedGlbPipeline.test.ts b/tests/integration/compressedGlbPipeline.test.ts index c97155d..c24e34a 100644 --- a/tests/integration/compressedGlbPipeline.test.ts +++ b/tests/integration/compressedGlbPipeline.test.ts @@ -1,6 +1,6 @@ import { describe, expect, expectTypeOf, it, vi } from "vitest"; -import { EncodeDracoBlock, EncodeKTX2Block, EncodeMeshoptBlock, GltfInputBlock, GltfOutputBlock, NodeAsset } from "../../src/index"; +import { EncodeDracoBlock, EncodeKTX2Block, EncodeMeshoptBlock, GltfInputBlock, GltfOutputBlock, NodeAsset } from "../../packages/core/src/index"; import { expectKtx2Image, parseGlbAsync } from "../helpers/glb"; import { generateTexturedGltfJson } from "../helpers/gltf"; diff --git a/tests/integration/dracoCompression.test.ts b/tests/integration/dracoCompression.test.ts index 8fc3142..70807f1 100644 --- a/tests/integration/dracoCompression.test.ts +++ b/tests/integration/dracoCompression.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from "vitest"; -import { EncodeDracoBlock, GltfInputBlock, GltfOutputBlock, NodeAsset } from "../../src/index"; +import { EncodeDracoBlock, GltfInputBlock, GltfOutputBlock, NodeAsset } from "../../packages/core/src/index"; import { parseGlbAsync } from "../helpers/glb"; import { generateGltfJson } from "../helpers/gltf"; diff --git a/tests/integration/encodeKtx2.test.ts b/tests/integration/encodeKtx2.test.ts index 7898fb1..c451c9f 100644 --- a/tests/integration/encodeKtx2.test.ts +++ b/tests/integration/encodeKtx2.test.ts @@ -2,7 +2,7 @@ import { Document } from "@gltf-transform/core"; import { EXTTextureWebP } from "@gltf-transform/extensions"; import { describe, expect, it, vi } from "vitest"; -import { EncodeKTX2Block, FbxInputBlock, GltfInputBlock, GltfOutputBlock, NodeAsset, ObjInputBlock } from "../../src/index"; +import { EncodeKTX2Block, FbxInputBlock, GltfInputBlock, GltfOutputBlock, NodeAsset, ObjInputBlock } from "../../packages/core/src/index"; import { generateTexturedFbxDataWithUvs } from "../helpers/fbx"; import { expectKtx2Image, getTextureImageIndex, parseGlbAsync } from "../helpers/glb"; import { generateTexturedGltfJson } from "../helpers/gltf"; diff --git a/tests/integration/fbxInput.test.ts b/tests/integration/fbxInput.test.ts index b5a1c55..249211a 100644 --- a/tests/integration/fbxInput.test.ts +++ b/tests/integration/fbxInput.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from "vitest"; -import { FbxInputBlock, GltfOutputBlock, NodeAsset, NodeAssetContext } from "../../src/index"; +import { FbxInputBlock, GltfOutputBlock, NodeAsset, NodeAssetContext } from "../../packages/core/src/index"; import { generateBinaryFbxData, generateFbxData, generateFbxDataUri, generateTexturedFbxDataWithUvs, generateTgaTextureData } from "../helpers/fbx"; import { parseGlbAsync } from "../helpers/glb"; diff --git a/tests/integration/gltfInput.test.ts b/tests/integration/gltfInput.test.ts index 5f2b738..c2fd962 100644 --- a/tests/integration/gltfInput.test.ts +++ b/tests/integration/gltfInput.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from "vitest"; -import { GltfInputBlock, NodeAsset, NodeAssetContext } from "../../src/index"; +import { GltfInputBlock, NodeAsset, NodeAssetContext } from "../../packages/core/src/index"; import { decodeDataUri, generateGlbDataUri, generateGltfJson } from "../helpers/gltf"; describe("glTF input", () => { diff --git a/tests/integration/gltfOutput.test.ts b/tests/integration/gltfOutput.test.ts index aac2134..baa504f 100644 --- a/tests/integration/gltfOutput.test.ts +++ b/tests/integration/gltfOutput.test.ts @@ -1,6 +1,6 @@ import { describe, it, vi } from "vitest"; -import { GltfInputBlock, GltfOutputBlock, NodeAsset } from "../../src/index"; +import { GltfInputBlock, GltfOutputBlock, NodeAsset } from "../../packages/core/src/index"; import { parseGlbAsync } from "../helpers/glb"; import { generateGltfJson } from "../helpers/gltf"; diff --git a/tests/integration/meshoptCompression.test.ts b/tests/integration/meshoptCompression.test.ts index 4b85e71..5686e75 100644 --- a/tests/integration/meshoptCompression.test.ts +++ b/tests/integration/meshoptCompression.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from "vitest"; -import { EncodeMeshoptBlock, GltfInputBlock, GltfOutputBlock, NodeAsset } from "../../src/index"; +import { EncodeMeshoptBlock, GltfInputBlock, GltfOutputBlock, NodeAsset } from "../../packages/core/src/index"; import { parseGlbAsync } from "../helpers/glb"; import { generateGltfJson } from "../helpers/gltf"; diff --git a/tests/integration/objInput.test.ts b/tests/integration/objInput.test.ts index 87deee6..03a54c6 100644 --- a/tests/integration/objInput.test.ts +++ b/tests/integration/objInput.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from "vitest"; -import { GltfOutputBlock, NodeAsset, ObjInputBlock } from "../../src/index"; +import { GltfOutputBlock, NodeAsset, ObjInputBlock } from "../../packages/core/src/index"; import { parseGlbAsync } from "../helpers/glb"; import { generateMtlData, generateObjDataUri, generateObjData, generateTexturedObjData, generateTextureData } from "../helpers/obj"; diff --git a/tests/integration/stlInput.test.ts b/tests/integration/stlInput.test.ts index f3f1a96..9deee11 100644 --- a/tests/integration/stlInput.test.ts +++ b/tests/integration/stlInput.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from "vitest"; -import { GltfOutputBlock, NodeAsset, NodeAssetContext, StlInputBlock } from "../../src/index"; +import { GltfOutputBlock, NodeAsset, NodeAssetContext, StlInputBlock } from "../../packages/core/src/index"; import { parseGlbAsync } from "../helpers/glb"; import { generateBinaryStlData, generateStlData, generateStlDataUri } from "../helpers/stl"; diff --git a/tests/unit/block.test.ts b/tests/unit/block.test.ts index c554b97..d96915d 100644 --- a/tests/unit/block.test.ts +++ b/tests/unit/block.test.ts @@ -1,8 +1,8 @@ import { describe, expect, it } from "vitest"; -import { Block } from "../../src/blocks/block"; -import { defineBlock, value } from "../../src/blocks/blockDefinition"; -import { NodeAsset } from "../../src/nodeAsset"; +import { Block } from "../../packages/core/src/blocks/block"; +import { defineBlock, value } from "../../packages/core/src/blocks/blockDefinition"; +import { NodeAsset } from "../../packages/core/src/nodeAsset"; import { ScaleDefinition, NumberDefinition, OtherNumberDefinition, NumberType } from "../helpers/numberBlocks"; describe("Block class", () => { diff --git a/tests/unit/nodeAsset.test.ts b/tests/unit/nodeAsset.test.ts index bb1af25..6743805 100644 --- a/tests/unit/nodeAsset.test.ts +++ b/tests/unit/nodeAsset.test.ts @@ -1,9 +1,9 @@ import { describe, expect, expectTypeOf, it } from "vitest"; -import { Block } from "../../src/blocks/block"; -import { defineBlock, defineSourceBlock } from "../../src/blocks/blockDefinition"; -import { NodeAsset, NodeAssetContext } from "../../src/index"; -import type { Resource } from "../../src/resources/resource"; +import { Block } from "../../packages/core/src/blocks/block"; +import { defineBlock, defineSourceBlock } from "../../packages/core/src/blocks/blockDefinition"; +import { NodeAsset, NodeAssetContext } from "../../packages/core/src/index"; +import type { Resource } from "../../packages/core/src/resources/resource"; import { ScaleDefinition, NumberDefinition } from "../helpers/numberBlocks"; describe("NodeAsset", () => { diff --git a/tsconfig.json b/tsconfig.json index 24342b8..3e9c206 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,11 +18,14 @@ "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "skipLibCheck": true, + "paths": { + "@babylonjs/node-assets": ["./packages/core/src/index.ts"] + }, "declaration": true, "declarationMap": true, "sourceMap": true, "noEmit": true }, - "include": ["src", "tests", "*.config.ts", "*.config.mjs", "examples/blockDefinition.ts"] + "include": ["packages/**/*.ts", "tests", "*.config.ts", "*.config.mjs", "examples/blockDefinition.ts"] }