Skip to content

Commit 44a46ff

Browse files
felixtrzmeta-codesync[bot]
authored andcommitted
fix(core): keep Havok physics out of bundles when features.physics is disabled
Summary: \`create-iwsdk\` defaults to \`--no-physics\` and most scaffolded apps never enable physics, but every IWSDK build was shipping the full Havok physics engine: a separate Havok JS chunk **plus the ~2 MB \`HavokPhysics.wasm\` binary**, plus a stream of physics symbols (\`PhysicsBody\`/\`PhysicsSystem\`/\`HP_World\`/\`MotionType\`) in the main bundle. Verified on \`examples/poke\` (\`features.physics: false\` set explicitly), pre-fix: main bundle 3.0 MB / 791 KB gzip + 2.0 MB WASM + 36 KB Havok-es chunk — all downloaded on first load. Two static pins were keeping \`babylonjs/havok\` and the physics module in the static module graph regardless of \`features.physics\`: 1. **\`packages/core/src/init/world-initializer.ts\`** statically imported \`{ PhysicsBody, PhysicsManipulation, PhysicsShape, PhysicsSystem }\` from \`../physics/index.js\` and only *used* them inside \`if (physicsEnabled)\`. The import alone was enough to pull the physics graph in. 2. **\`packages/core/src/physics/physics-system.ts\`** statically imported \`{ MotionType, ... }\` from \`babylonjs/havok\`. \`HP_ShapeId\`/\`HP_WorldId\`/\`MassProperties\`/\`HavokPhysicsWithBindings\` are TS type-only (erased), but \`MotionType\` is a runtime \`enum\` — so \`babylonjs/havok\` was a real runtime dep on this code path. The actual engine load on line 87 (\`await import('babylonjs/havok')\`) was already dynamic and would have given proper code-splitting on its own. And one barrel re-export was blocking tree-shaking: \`iwsdk/core/package.json\` had no \`sideEffects\` declaration, so every module was assumed to have side effects and Rollup couldn't drop unused exports re-exported via \`export * from './physics/index.js'\` in the core barrel. This change: - Converts the world-initializer physics imports to \`await import('../physics/index.js')\` inside the \`physicsEnabled\` branch. \`registerFeatureSystems\` and \`initializeWorld\` become \`async\` accordingly; \`initializeWorld\` already returned \`Promise<World>\` so the externally-visible signature is unchanged. - Switches \`physics-system.ts\`'s havok imports to \`import type { ... }\`. \`MotionType\` is the only one that was a runtime symbol, and the file already accesses it at runtime through the loaded instance (\`this.havok.MotionType.STATIC\` etc.) — the static import was only providing the type annotation on \`let motionType: MotionType\` (line 307), which is compile-erased. - Adds \`"sideEffects": ["dist/index.js"]\` to \`iwsdk/core/package.json\`. The barrel's banner \`console.log\` (the IWSDK ASCII art) is the only intentional top-level side effect; every other module is \`createComponent\`/\`createSystem\` const-binding + class definitions with no module-load side effects. Marking just the barrel as having side effects lets Rollup tree-shake unused exports — including everything physics if no consumer references a physics symbol. After the fix on \`examples/poke\`: - Entry HTML loads only the main bundle (2.9 MB) — no physics code, no Havok strings. - The physics module becomes a separate 16 KB chunk that is *generated* but never *fetched* unless \`features.physics\` is true. - Havok-es chunk (36 KB) and \`HavokPhysics.wasm\` (2 MB) are still emitted as cold assets the physics chunk would import on-demand, but no consumer fetches them. - Browser-downloaded bytes for a \`physics: false\` page drop from ~5 MB to 2.9 MB (~2.1 MB saved on first load). \`examples/physics\` (\`features.physics: true\`): main bundle still 2.9 MB; physics chunk loads on demand; Havok WASM still fetched as before. No regression. Reviewed By: zjm-meta Differential Revision: D105972540 fbshipit-source-id: 7c532b19469cc0344d859bda1fe258df8192d58b
1 parent aad5441 commit 44a46ff

3 files changed

Lines changed: 46 additions & 12 deletions

File tree

packages/core/package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,29 @@
88
"files": [
99
"dist"
1010
],
11+
"sideEffects": [
12+
"dist/index.js",
13+
"dist/asset/**",
14+
"dist/audio/**",
15+
"dist/camera/**",
16+
"dist/depth/**",
17+
"dist/ecs/**",
18+
"dist/environment/**",
19+
"dist/environment-raycast/**",
20+
"dist/grab/**",
21+
"dist/init/**",
22+
"dist/input/**",
23+
"dist/layers/**",
24+
"dist/level/**",
25+
"dist/locomotion/**",
26+
"dist/mcp/**",
27+
"dist/runtime/**",
28+
"dist/scene-understanding/**",
29+
"dist/transform/**",
30+
"dist/ui/**",
31+
"dist/vendor/**",
32+
"dist/visibility/**"
33+
],
1134
"scripts": {
1235
"prebuild": "node -p \"'export const VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > src/version.ts",
1336
"build": "rollup -c",

packages/core/src/init/world-initializer.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,10 @@ import {
4747
TurningMethod,
4848
} from '../locomotion/index.js';
4949
import { MCPRuntime } from '../mcp/index.js';
50-
import {
51-
PhysicsBody,
52-
PhysicsManipulation,
53-
PhysicsShape,
54-
PhysicsSystem,
55-
} from '../physics/index.js';
50+
// The physics module pulls in `@babylonjs/havok` (~2 MB WASM + engine JS) at
51+
// the static-graph level. Load it lazily in `registerFeatureSystems` only
52+
// when `features.physics` is enabled so non-physics projects don't pay the
53+
// bundle-size tax.
5654
import {
5755
Clock,
5856
PerspectiveCamera,
@@ -200,7 +198,7 @@ type CameraPoseOptions = NonNullable<
200198
* @remarks
201199
* This function powers {@link World.create}. Prefer using that static helper.
202200
*/
203-
export function initializeWorld(
201+
export async function initializeWorld(
204202
container: HTMLDivElement,
205203
options: WorldOptions = {},
206204
): Promise<World> {
@@ -234,8 +232,10 @@ export function initializeWorld(
234232
// Register additional systems (UI + Audio on by default)
235233
registerAdditionalSystems(world);
236234

237-
// Register input and feature systems with explicit priorities
238-
registerFeatureSystems(world, config);
235+
// Register input and feature systems with explicit priorities.
236+
// Awaited because the physics branch dynamically imports its module so
237+
// `@babylonjs/havok` stays out of the static graph for non-physics builds.
238+
await registerFeatureSystems(world, config);
239239

240240
// Setup render loop
241241
setupRenderLoop(world, renderer);
@@ -594,7 +594,7 @@ function registerAdditionalSystems(world: World) {
594594
world.registerComponent(AudioSource).registerSystem(AudioSystem);
595595
}
596596

597-
function registerFeatureSystems(
597+
async function registerFeatureSystems(
598598
world: World,
599599
config: ReturnType<typeof extractConfiguration>,
600600
) {
@@ -673,8 +673,12 @@ function registerFeatureSystems(
673673
world.registerSystem(GrabSystem, { priority: -3, configData: grabOpts });
674674
}
675675

676-
// Physics runs after Grab so it can respect Pressed overrides
676+
// Physics runs after Grab so it can respect Pressed overrides.
677+
// Dynamically imported so the physics module (and its ~2 MB Havok WASM)
678+
// stays out of the static module graph for non-physics projects.
677679
if (physicsEnabled) {
680+
const { PhysicsBody, PhysicsManipulation, PhysicsShape, PhysicsSystem } =
681+
await import('../physics/index.js');
678682
world
679683
.registerComponent(PhysicsBody)
680684
.registerComponent(PhysicsShape)

packages/core/src/physics/physics-system.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import {
8+
// All symbols from `@babylonjs/havok` are type-only here. The runtime engine
9+
// (which ships ~2 MB of WASM) is loaded lazily via `await import(...)` later
10+
// in `init()`, and runtime access to enums like `MotionType` is done via the
11+
// loaded instance (e.g. `this.havok.MotionType.STATIC`). Keeping these as
12+
// `import type` ensures `@babylonjs/havok` stays out of the static module
13+
// graph when consumers don't enable `features.physics`, so the WASM and JS
14+
// engine chunks aren't bundled into non-physics projects.
15+
import type {
916
HavokPhysicsWithBindings,
1017
HP_ShapeId,
1118
HP_WorldId,

0 commit comments

Comments
 (0)