Three-panel editor page (editor.html + src/editor/): slot list, the actual greybox course built from the game's own builders, and fit controls. Drop a .glb on a slot, nudge offset/rotation/scale, save to IndexedDB so the live game picks it up with no deploy, export manifest.json to commit it. Design notes worth keeping: - A custom asset never replaces the procedural node; it goes into a new sibling 'fit' node and the original is hidden. Reset is instant, references other systems captured stay alive, and the manifest transform lives on a node nobody else writes to (feel.ts, telegraph.ts and the machine parts all own transforms of their own). - The stage creates physics but never steps it: no systems, no world.start(), just renderOnce() on a rAF. Nothing here affects gameplay. - A blob body whose UVs paint cannot stick to is refused outright rather than swapped in — silently broken paint is worse than a missing model. Headless verification: 148 assertions across three node test files (manifest round-trip, GLB paintability against real parsed glTF, and the whole drop/save/restore path over an in-memory IndexedDB). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
34 lines
1.1 KiB
JavaScript
34 lines
1.1 KiB
JavaScript
/**
|
|
* Lane J — lets node run the repo's extension-less TS imports directly.
|
|
*
|
|
* The house test style (see paint/coverage-math.test.ts) imports './thing' with
|
|
* no extension, which is what tsc's "bundler" resolution and vite both want but
|
|
* which node's ESM resolver rejects outright. This registers a resolve hook that
|
|
* retries a bare relative specifier as `<specifier>.ts`, so:
|
|
*
|
|
* node --experimental-strip-types --import ./src/editor/node-ts-resolve.mjs \
|
|
* src/editor/manifest-io.test.ts
|
|
*
|
|
* Plain .mjs on purpose: tsconfig only compiles .ts, and nothing imports this
|
|
* file, so it never reaches tsc or the vite bundle.
|
|
*/
|
|
import { registerHooks } from 'node:module'
|
|
|
|
registerHooks({
|
|
resolve(specifier, context, next) {
|
|
try {
|
|
return next(specifier, context)
|
|
} catch (err) {
|
|
if (specifier.startsWith('.') && !/\.[a-z]+$/i.test(specifier)) {
|
|
try {
|
|
return next(specifier + '.ts', context)
|
|
} catch {
|
|
// '../machine' style directory imports resolve to their index.ts
|
|
return next(specifier + '/index.ts', context)
|
|
}
|
|
}
|
|
throw err
|
|
}
|
|
},
|
|
})
|