@needle-tools/engine 5.1.0-alpha.1 → 5.1.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.needle/generated/needle-bindings.gen.d.ts +5 -0
- package/CHANGELOG.md +52 -0
- package/components.needle.json +1 -1
- package/dist/{gltf-progressive-DJBMx-zB.umd.cjs → gltf-progressive-BmblPzFj.umd.cjs} +4 -4
- package/dist/{gltf-progressive-BryRjllq.min.js → gltf-progressive-CN_mbb66.min.js} +2 -2
- package/dist/{gltf-progressive-Cl167Vjx.js → gltf-progressive-DUlhxdv4.js} +5 -2
- package/dist/needle-engine.bundle-C-ixARur.umd.cjs +1733 -0
- package/dist/needle-engine.bundle-CHmXdnE1.min.js +1733 -0
- package/dist/{needle-engine.bundle-BGyKqxBH.js → needle-engine.bundle-DF01sSGQ.js} +10841 -10434
- package/dist/needle-engine.d.ts +244 -54
- package/dist/needle-engine.js +541 -536
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-B_9sKVU7.min.js → postprocessing-B571qGWR.min.js} +34 -34
- package/dist/{postprocessing-WDc9WwI3.js → postprocessing-CfrLAbLX.js} +0 -1
- package/dist/{postprocessing-B2wb6pzI.umd.cjs → postprocessing-CiGkAeM9.umd.cjs} +17 -17
- package/dist/{vendor-CAcsI0eU.js → vendor-BFrMaK9q.js} +8983 -9136
- package/dist/vendor-CJmyOrCq.min.js +1116 -0
- package/dist/vendor-DkMW3WY4.umd.cjs +1116 -0
- package/lib/engine/api.d.ts +14 -0
- package/lib/engine/api.js +4 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/debug/debug_environment.js +1 -1
- package/lib/engine/debug/debug_environment.js.map +1 -1
- package/lib/engine/debug/debug_spatial_console.d.ts +2 -0
- package/lib/engine/debug/debug_spatial_console.js +10 -7
- package/lib/engine/debug/debug_spatial_console.js.map +1 -1
- package/lib/engine/engine_addressables.d.ts +2 -0
- package/lib/engine/engine_addressables.js +6 -3
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_application.js +8 -6
- package/lib/engine/engine_application.js.map +1 -1
- package/lib/engine/engine_audio.d.ts +68 -0
- package/lib/engine/engine_audio.js +172 -0
- package/lib/engine/engine_audio.js.map +1 -1
- package/lib/engine/engine_components.js +1 -1
- package/lib/engine/engine_components.js.map +1 -1
- package/lib/engine/engine_constants.js +6 -0
- package/lib/engine/engine_constants.js.map +1 -1
- package/lib/engine/engine_context.d.ts +33 -7
- package/lib/engine/engine_context.js +40 -2
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_context_registry.js +1 -1
- package/lib/engine/engine_context_registry.js.map +1 -1
- package/lib/engine/engine_gameobject.js +2 -2
- package/lib/engine/engine_gameobject.js.map +1 -1
- package/lib/engine/engine_init.js +16 -1
- package/lib/engine/engine_init.js.map +1 -1
- package/lib/engine/engine_input.d.ts +3 -2
- package/lib/engine/engine_input.js +3 -2
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_license.d.ts +2 -0
- package/lib/engine/engine_license.js +25 -15
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_lifecycle_functions_internal.js +5 -0
- package/lib/engine/engine_lifecycle_functions_internal.js.map +1 -1
- package/lib/engine/engine_mainloop_utils.js +5 -2
- package/lib/engine/engine_mainloop_utils.js.map +1 -1
- package/lib/engine/engine_networking_blob.d.ts +1 -1
- package/lib/engine/engine_networking_blob.js +5 -11
- package/lib/engine/engine_networking_blob.js.map +1 -1
- package/lib/engine/engine_physics_rapier.js +0 -1
- package/lib/engine/engine_physics_rapier.js.map +1 -1
- package/lib/engine/engine_pmrem.js +2 -2
- package/lib/engine/engine_pmrem.js.map +1 -1
- package/lib/engine/engine_scenedata.d.ts +32 -0
- package/lib/engine/engine_scenedata.js +138 -0
- package/lib/engine/engine_scenedata.js.map +1 -0
- package/lib/engine/engine_serialization_builtin_serializer.d.ts +10 -16
- package/lib/engine/engine_serialization_builtin_serializer.js +55 -41
- package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
- package/lib/engine/engine_ssr.d.ts +18 -0
- package/lib/engine/engine_ssr.js +40 -0
- package/lib/engine/engine_ssr.js.map +1 -0
- package/lib/engine/engine_three_utils.d.ts +14 -7
- package/lib/engine/engine_three_utils.js +14 -7
- package/lib/engine/engine_three_utils.js.map +1 -1
- package/lib/engine/engine_types.d.ts +2 -0
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/engine_utils.js +2 -0
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/engine_utils_hash.d.ts +9 -0
- package/lib/engine/engine_utils_hash.js +112 -0
- package/lib/engine/engine_utils_hash.js.map +1 -0
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
- package/lib/engine/webcomponents/jsx.d.ts +51 -0
- package/lib/engine/webcomponents/logo-element.d.ts +2 -1
- package/lib/engine/webcomponents/logo-element.js +2 -1
- package/lib/engine/webcomponents/logo-element.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +4 -4
- package/lib/engine/webcomponents/needle menu/needle-menu.js +2 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-button.d.ts +2 -1
- package/lib/engine/webcomponents/needle-button.js +2 -1
- package/lib/engine/webcomponents/needle-button.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +11 -4
- package/lib/engine/webcomponents/needle-engine.js +2 -1
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.d.ts +3 -2
- package/lib/engine/xr/NeedleXRSession.js +51 -15
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine/xr/events.d.ts +1 -1
- package/lib/engine/xr/events.js.map +1 -1
- package/lib/engine-components/Animation.js +17 -16
- package/lib/engine-components/Animation.js.map +1 -1
- package/lib/engine-components/AnimatorController.d.ts +2 -0
- package/lib/engine-components/AnimatorController.js +4 -1
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/AudioSource.d.ts +19 -3
- package/lib/engine-components/AudioSource.js +121 -68
- package/lib/engine-components/AudioSource.js.map +1 -1
- package/lib/engine-components/DragControls.d.ts +7 -0
- package/lib/engine-components/DragControls.js +19 -0
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/Light.d.ts +6 -8
- package/lib/engine-components/Light.js +40 -27
- package/lib/engine-components/Light.js.map +1 -1
- package/lib/engine-components/Networking.d.ts +1 -1
- package/lib/engine-components/Networking.js +1 -1
- package/lib/engine-components/OrbitControls.js +16 -11
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/ReflectionProbe.js +2 -0
- package/lib/engine-components/ReflectionProbe.js.map +1 -1
- package/lib/engine-components/RigidBody.js +3 -3
- package/lib/engine-components/RigidBody.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.js +2 -0
- package/lib/engine-components/SceneSwitcher.js.map +1 -1
- package/lib/engine-components/SeeThrough.js +2 -2
- package/lib/engine-components/SeeThrough.js.map +1 -1
- package/lib/engine-components/api.d.ts +1 -1
- package/lib/engine-components/api.js +1 -1
- package/lib/engine-components/api.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/BloomEffect.d.ts +1 -1
- package/lib/engine-components/postprocessing/Effects/Sharpening.js +1 -2
- package/lib/engine-components/postprocessing/Effects/Sharpening.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.js +5 -6
- package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
- package/lib/engine-components/postprocessing/VolumeParameter.d.ts +2 -0
- package/lib/engine-components/postprocessing/VolumeParameter.js +4 -1
- package/lib/engine-components/postprocessing/VolumeParameter.js.map +1 -1
- package/lib/engine-components/ui/Canvas.d.ts +1 -1
- package/lib/engine-components/ui/Canvas.js +2 -8
- package/lib/engine-components/ui/Canvas.js.map +1 -1
- package/lib/engine-components/ui/Text.d.ts +1 -0
- package/lib/engine-components/ui/Text.js +10 -7
- package/lib/engine-components/ui/Text.js.map +1 -1
- package/lib/engine-components/web/CursorFollow.js +21 -12
- package/lib/engine-components/web/CursorFollow.js.map +1 -1
- package/lib/engine-components/web/ScrollFollow.d.ts +0 -1
- package/lib/engine-components/web/ScrollFollow.js +3 -2
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/lib/engine-components/webxr/WebXRImageTracking.js +4 -0
- package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
- package/lib/needle-engine.d.ts +2 -0
- package/lib/needle-engine.js +2 -0
- package/lib/needle-engine.js.map +1 -1
- package/package.json +6 -4
- package/plugins/dts-generator/dts.codegen.js +334 -0
- package/plugins/dts-generator/dts.scan.js +99 -0
- package/plugins/dts-generator/dts.writer.js +59 -0
- package/plugins/dts-generator/glb.discovery.js +279 -0
- package/plugins/dts-generator/glb.extractor.js +215 -0
- package/plugins/dts-generator/glb.reader.js +167 -0
- package/plugins/dts-generator/index.js +36 -0
- package/plugins/dts-generator/manifest.types.js +174 -0
- package/plugins/types/index.d.ts +2 -1
- package/plugins/types/needle-bindings.d.ts +30 -0
- package/plugins/types/userconfig.d.ts +21 -2
- package/plugins/vite/asap.js +18 -9
- package/plugins/vite/dependencies.js +29 -0
- package/plugins/vite/dependency-watcher.d.ts +2 -2
- package/plugins/vite/dependency-watcher.js +3 -4
- package/plugins/vite/drop.d.ts +2 -2
- package/plugins/vite/drop.js +3 -4
- package/plugins/vite/dts-generator.d.ts +7 -0
- package/plugins/vite/dts-generator.js +191 -0
- package/plugins/vite/index.d.ts +10 -3
- package/plugins/vite/index.js +27 -10
- package/plugins/vite/local-files-core.js +3 -3
- package/plugins/vite/local-files-utils.d.ts +3 -1
- package/plugins/vite/local-files-utils.js +29 -5
- package/plugins/vite/logging.js +2 -2
- package/plugins/vite/meta.js +4 -2
- package/plugins/vite/poster.d.ts +2 -2
- package/plugins/vite/poster.js +3 -5
- package/plugins/vite/reload.d.ts +2 -2
- package/plugins/vite/reload.js +23 -22
- package/src/engine/api.ts +18 -1
- package/src/engine/debug/debug_environment.ts +1 -1
- package/src/engine/debug/debug_spatial_console.ts +10 -7
- package/src/engine/engine_addressables.ts +6 -3
- package/src/engine/engine_application.ts +8 -6
- package/src/engine/engine_audio.ts +184 -0
- package/src/engine/engine_components.ts +1 -1
- package/src/engine/engine_constants.ts +11 -6
- package/src/engine/engine_context.ts +50 -7
- package/src/engine/engine_context_registry.ts +1 -1
- package/src/engine/engine_gameobject.ts +2 -2
- package/src/engine/engine_init.ts +15 -1
- package/src/engine/engine_input.ts +3 -2
- package/src/engine/engine_license.ts +23 -19
- package/src/engine/engine_lifecycle_functions_internal.ts +7 -0
- package/src/engine/engine_mainloop_utils.ts +5 -2
- package/src/engine/engine_networking_blob.ts +5 -11
- package/src/engine/engine_physics_rapier.ts +0 -3
- package/src/engine/engine_pmrem.ts +3 -3
- package/src/engine/engine_scenedata.ts +136 -0
- package/src/engine/engine_serialization_builtin_serializer.ts +63 -46
- package/src/engine/engine_ssr.ts +48 -0
- package/src/engine/engine_three_utils.ts +15 -7
- package/src/engine/engine_types.ts +2 -0
- package/src/engine/engine_utils.ts +1 -0
- package/src/engine/engine_utils_hash.ts +65 -0
- package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
- package/src/engine/webcomponents/jsx.d.ts +51 -0
- package/src/engine/webcomponents/logo-element.ts +3 -1
- package/src/engine/webcomponents/needle menu/needle-menu.ts +4 -2
- package/src/engine/webcomponents/needle-button.ts +3 -1
- package/src/engine/webcomponents/needle-engine.ts +12 -4
- package/src/engine/xr/NeedleXRSession.ts +49 -14
- package/src/engine/xr/events.ts +1 -1
- package/src/engine-components/Animation.ts +19 -16
- package/src/engine-components/AnimatorController.ts +4 -1
- package/src/engine-components/AudioSource.ts +130 -79
- package/src/engine-components/DragControls.ts +18 -2
- package/src/engine-components/Light.ts +40 -26
- package/src/engine-components/Networking.ts +1 -1
- package/src/engine-components/OrbitControls.ts +18 -9
- package/src/engine-components/ReflectionProbe.ts +2 -0
- package/src/engine-components/RigidBody.ts +3 -3
- package/src/engine-components/SceneSwitcher.ts +1 -0
- package/src/engine-components/SeeThrough.ts +2 -2
- package/src/engine-components/api.ts +1 -1
- package/src/engine-components/postprocessing/Effects/BloomEffect.ts +1 -1
- package/src/engine-components/postprocessing/Effects/Sharpening.ts +1 -2
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +4 -8
- package/src/engine-components/postprocessing/VolumeParameter.ts +4 -1
- package/src/engine-components/ui/Canvas.ts +2 -8
- package/src/engine-components/ui/Text.ts +12 -8
- package/src/engine-components/web/CursorFollow.ts +21 -13
- package/src/engine-components/web/ScrollFollow.ts +2 -2
- package/src/engine-components/webxr/WebXRImageTracking.ts +2 -0
- package/src/needle-engine.ts +3 -0
- package/src/vite-env.d.ts +16 -0
- package/dist/needle-engine.bundle-CiYtOO2O.min.js +0 -1732
- package/dist/needle-engine.bundle-DzVx9Z8D.umd.cjs +0 -1732
- package/dist/vendor-CEM38hLE.umd.cjs +0 -1116
- package/dist/vendor-HRlxIBga.min.js +0 -1116
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* GLB / glTF JSON chunk readers — local files and remote URLs.
|
|
4
|
+
*
|
|
5
|
+
* All functions return the parsed glTF JSON object or null on failure.
|
|
6
|
+
* Remote fetches use HTTP Range requests and cache by Last-Modified header.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { openSync, readSync, closeSync, readFileSync } from 'fs';
|
|
10
|
+
import { needleLog } from '../vite/logging.js';
|
|
11
|
+
|
|
12
|
+
const PLUGIN = "needle:dts-generator";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Read the JSON chunk from a binary GLB file without loading the binary blob.
|
|
16
|
+
* @param {string} filePath
|
|
17
|
+
* @returns {Record<string, unknown> | null}
|
|
18
|
+
*/
|
|
19
|
+
export function readGlbJsonChunk(filePath) {
|
|
20
|
+
const fd = openSync(filePath, "r");
|
|
21
|
+
try {
|
|
22
|
+
const header = Buffer.allocUnsafe(20);
|
|
23
|
+
const bytesRead = readSync(fd, header, 0, header.length, 0);
|
|
24
|
+
if (bytesRead < 20) return null;
|
|
25
|
+
|
|
26
|
+
const magic = header.readUInt32LE(0);
|
|
27
|
+
const version = header.readUInt32LE(4);
|
|
28
|
+
const chunkLength = header.readUInt32LE(12);
|
|
29
|
+
const chunkType = header.readUInt32LE(16);
|
|
30
|
+
|
|
31
|
+
if (magic !== 0x46546c67 || version !== 2) return null;
|
|
32
|
+
if (chunkType !== 0x4E4F534A) return null; // not JSON chunk
|
|
33
|
+
|
|
34
|
+
const jsonBuffer = Buffer.allocUnsafe(chunkLength);
|
|
35
|
+
const jsonBytesRead = readSync(fd, jsonBuffer, 0, chunkLength, 20);
|
|
36
|
+
if (jsonBytesRead < chunkLength) return null;
|
|
37
|
+
|
|
38
|
+
return JSON.parse(jsonBuffer.toString("utf8").replace(/\u0000+$/g, ""));
|
|
39
|
+
} catch (_e) {
|
|
40
|
+
return null;
|
|
41
|
+
} finally {
|
|
42
|
+
closeSync(fd);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {string} filePath
|
|
48
|
+
* @returns {Record<string, unknown> | null}
|
|
49
|
+
*/
|
|
50
|
+
export function readGltfJsonFile(filePath) {
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
53
|
+
} catch (_e) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Parse the filename from a `Content-Disposition` header value.
|
|
60
|
+
* Handles `filename="foo.glb"` and `filename*=UTF-8''foo.glb` forms.
|
|
61
|
+
* Returns null if not present or not parseable.
|
|
62
|
+
* @param {string | null} header
|
|
63
|
+
* @returns {string | null}
|
|
64
|
+
*/
|
|
65
|
+
export function parseContentDispositionFilename(header) {
|
|
66
|
+
if (!header) return null;
|
|
67
|
+
// RFC 5987 extended form: filename*=UTF-8''foo.glb
|
|
68
|
+
const extMatch = header.match(/filename\*\s*=\s*(?:[^']*'')?([^;]+)/i);
|
|
69
|
+
if (extMatch) {
|
|
70
|
+
try { return decodeURIComponent(extMatch[1].trim()); } catch (_e) { /* fall through */ }
|
|
71
|
+
}
|
|
72
|
+
// Plain form: filename="foo.glb" or filename=foo.glb
|
|
73
|
+
const plainMatch = header.match(/filename\s*=\s*"?([^";]+)"?/i);
|
|
74
|
+
if (plainMatch) return plainMatch[1].trim();
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** @type {Map<string, { lastModified: string, json: Record<string, unknown>, filename: string | null }>} */
|
|
79
|
+
const _remoteGlbCache = new Map();
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Fetch the JSON chunk of a remote GLB.
|
|
83
|
+
* Tries an initial Range request for the header bytes; if the server already returned
|
|
84
|
+
* enough data (200 + full body, or 206 with sufficient bytes) the JSON chunk is
|
|
85
|
+
* extracted directly. Otherwise a second Range request fetches the JSON chunk.
|
|
86
|
+
* Uses `Last-Modified` header for caching — avoids re-parsing if unchanged.
|
|
87
|
+
* Also captures `Content-Disposition` filename for friendly key generation.
|
|
88
|
+
* Returns `null` on any network or parse error.
|
|
89
|
+
*
|
|
90
|
+
* @param {string} url
|
|
91
|
+
* @returns {Promise<{ json: Record<string, unknown>, filename: string | null } | null>}
|
|
92
|
+
*/
|
|
93
|
+
export async function readRemoteGlbJsonChunk(url) {
|
|
94
|
+
try {
|
|
95
|
+
const cached = _remoteGlbCache.get(url);
|
|
96
|
+
|
|
97
|
+
const headerRes = await fetch(url, { headers: { Range: "bytes=0-19" } });
|
|
98
|
+
if (!headerRes.ok) {
|
|
99
|
+
needleLog(PLUGIN, `Remote GLB fetch failed (${headerRes.status}): ${url}`, "warn");
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const lastModified = headerRes.headers.get("Last-Modified") ?? "";
|
|
104
|
+
const filename = parseContentDispositionFilename(headerRes.headers.get("Content-Disposition"));
|
|
105
|
+
|
|
106
|
+
if (cached && lastModified && cached.lastModified === lastModified) {
|
|
107
|
+
// Prefer the filename we resolved during the full fetch (may have come from the JSON chunk
|
|
108
|
+
// response). Only fall back to the fresh header value if the cache has nothing.
|
|
109
|
+
return { json: cached.json, filename: cached.filename ?? filename };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const firstBytes = Buffer.from(await headerRes.arrayBuffer());
|
|
113
|
+
if (firstBytes.length < 20) return null;
|
|
114
|
+
|
|
115
|
+
const magic = firstBytes.readUInt32LE(0);
|
|
116
|
+
const version = firstBytes.readUInt32LE(4);
|
|
117
|
+
const chunkLength = firstBytes.readUInt32LE(12);
|
|
118
|
+
const chunkType = firstBytes.readUInt32LE(16);
|
|
119
|
+
|
|
120
|
+
if (magic !== 0x46546c67 || version !== 2) return null; // not a GLB
|
|
121
|
+
if (chunkType !== 0x4E4F534A) return null; // chunk0 not JSON
|
|
122
|
+
|
|
123
|
+
let jsonBytes;
|
|
124
|
+
let resolvedFilename = filename;
|
|
125
|
+
|
|
126
|
+
if (firstBytes.length >= 20 + chunkLength) {
|
|
127
|
+
// The first response already contained the full JSON chunk (server ignored Range or returned 200).
|
|
128
|
+
jsonBytes = firstBytes.slice(20, 20 + chunkLength);
|
|
129
|
+
} else {
|
|
130
|
+
// Try a second Range request for just the JSON chunk bytes.
|
|
131
|
+
const jsonEnd = 20 + chunkLength - 1;
|
|
132
|
+
const jsonRes = await fetch(url, { headers: { Range: `bytes=20-${jsonEnd}` } });
|
|
133
|
+
if (!jsonRes.ok) {
|
|
134
|
+
needleLog(PLUGIN, `Remote GLB JSON chunk fetch failed (${jsonRes.status}): ${url}`, "warn");
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const jsonResBytes = Buffer.from(await jsonRes.arrayBuffer());
|
|
138
|
+
// Server may return 200 + full body even for a Range request — slice out our window.
|
|
139
|
+
if (jsonResBytes.length >= 20 + chunkLength) {
|
|
140
|
+
jsonBytes = jsonResBytes.slice(20, 20 + chunkLength);
|
|
141
|
+
} else if (jsonResBytes.length >= chunkLength) {
|
|
142
|
+
jsonBytes = jsonResBytes.slice(0, chunkLength);
|
|
143
|
+
} else {
|
|
144
|
+
needleLog(PLUGIN, `Remote GLB unexpected response (${jsonRes.status}, ${jsonResBytes.length} bytes, expected ${chunkLength}): ${url}`, "warn");
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const filenameFromJsonRes = parseContentDispositionFilename(jsonRes.headers.get("Content-Disposition"));
|
|
149
|
+
resolvedFilename = filename ?? filenameFromJsonRes;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (resolvedFilename === null) {
|
|
153
|
+
try {
|
|
154
|
+
const headRes = await fetch(url, { method: "HEAD" });
|
|
155
|
+
resolvedFilename = parseContentDispositionFilename(headRes.headers.get("Content-Disposition"));
|
|
156
|
+
} catch (_e) { /* ignore — fall back to URL-based name */ }
|
|
157
|
+
}
|
|
158
|
+
const json = /** @type {Record<string, unknown>} */ (
|
|
159
|
+
JSON.parse(jsonBytes.toString("utf8").replace(/\u0000+$/g, ""))
|
|
160
|
+
);
|
|
161
|
+
_remoteGlbCache.set(url, { lastModified, json, filename: resolvedFilename });
|
|
162
|
+
return { json, filename: resolvedFilename };
|
|
163
|
+
} catch (e) {
|
|
164
|
+
needleLog(PLUGIN, `Failed to fetch remote GLB: ${url} — ${/** @type {any} */ (e)?.message ?? e}`, "warn");
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* Needle Engine — HTML binding DTS generator
|
|
4
|
+
*
|
|
5
|
+
* Scans all GLB/glTF files in the project's assets directory, extracts
|
|
6
|
+
* NEEDLE_components data, and emits a `needle-bindings.d.ts` virtual-module
|
|
7
|
+
* declaration so that TypeScript can type-check HTML ↔ 3D component bindings.
|
|
8
|
+
*
|
|
9
|
+
* Typical generated output:
|
|
10
|
+
*
|
|
11
|
+
* declare module "needle-bindings" {
|
|
12
|
+
* interface SceneData {
|
|
13
|
+
* Sphere: {
|
|
14
|
+
* MyBall: { speed: number; label: string; };
|
|
15
|
+
* };
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* How component field types are resolved:
|
|
20
|
+
* - For built-in Needle Engine components, types are read from
|
|
21
|
+
* `components.needle.json` which lists only @serializable fields with
|
|
22
|
+
* their proper TypeScript types.
|
|
23
|
+
* - For user-defined components (not in the manifest), types are inferred
|
|
24
|
+
* from the JSON value in the GLB (number/string/boolean → typed, else → unknown).
|
|
25
|
+
* - Known Three.js types (Color, Vector3, Object3D, …) are emitted as
|
|
26
|
+
* `import("three").TypeName` and known Needle types (RGBAColor, AssetReference, …)
|
|
27
|
+
* as `import("@needle-tools/engine").TypeName`.
|
|
28
|
+
* - Truly unknown types fall back to `unknown`.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
export { resolveEntrypointGlbs, collectSceneFiles } from './glb.discovery.js';
|
|
32
|
+
export { readGlbJsonChunk, readGltfJsonFile, readRemoteGlbJsonChunk } from './glb.reader.js';
|
|
33
|
+
export { extractComponentBindings, sanitizeNodeName, inferNodeThreeType, inferTsType } from './glb.extractor.js';
|
|
34
|
+
export { scanBindings } from './dts.scan.js';
|
|
35
|
+
export { generateDts, generateHtmlCustomData } from './dts.codegen.js';
|
|
36
|
+
export { generateBindingsDts } from './dts.writer.js';
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* Type tables and components.needle.json manifest loader.
|
|
4
|
+
*
|
|
5
|
+
* Knows about primitive TS types, known Three.js types, known Needle Engine
|
|
6
|
+
* types, and how to resolve field types from the manifest.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync, readFileSync } from 'fs';
|
|
10
|
+
import { join, dirname } from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
/** Primitive TS type strings that can safely appear in an ambient declaration. */
|
|
17
|
+
export const PRIMITIVE_TYPES = new Set(["number", "string", "boolean"]);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Known Three.js types → import("three").TypeName
|
|
21
|
+
* @type {Record<string, string>}
|
|
22
|
+
*/
|
|
23
|
+
export const THREE_TYPES = {
|
|
24
|
+
Color: `import("three").Color`,
|
|
25
|
+
ColorRepresentation: `import("three").ColorRepresentation`,
|
|
26
|
+
Euler: `import("three").Euler`,
|
|
27
|
+
Texture: `import("three").Texture`,
|
|
28
|
+
// Materials
|
|
29
|
+
Material: `import("three").Material`,
|
|
30
|
+
MeshStandardMaterial: `import("three").MeshStandardMaterial`,
|
|
31
|
+
// Objects
|
|
32
|
+
Object3D: `import("three").Object3D`,
|
|
33
|
+
Mesh: `import("three").Mesh`,
|
|
34
|
+
SkinnedMesh: `import("three").SkinnedMesh`,
|
|
35
|
+
// Other
|
|
36
|
+
Vector2: `import("three").Vector2`,
|
|
37
|
+
Vector3: `import("three").Vector3`,
|
|
38
|
+
Vector4: `import("three").Vector4`,
|
|
39
|
+
Matrix3: `import("three").Matrix3`,
|
|
40
|
+
Matrix4: `import("three").Matrix4`,
|
|
41
|
+
Quaternion: `import("three").Quaternion`,
|
|
42
|
+
// Animation
|
|
43
|
+
AnimationClip: `import("three").AnimationClip`,
|
|
44
|
+
AnimationMixer: `import("three").AnimationMixer`,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Known Needle Engine types → import("@needle-tools/engine").TypeName
|
|
49
|
+
* @type {Record<string, string>}
|
|
50
|
+
*/
|
|
51
|
+
export const NEEDLE_TYPES = {
|
|
52
|
+
AssetReference: `import("@needle-tools/engine").AssetReference`,
|
|
53
|
+
EventList: `import("@needle-tools/engine").EventList`,
|
|
54
|
+
GameObject: `import("@needle-tools/engine").GameObject`,
|
|
55
|
+
LookAtConstraint: `import("@needle-tools/engine").LookAtConstraint`,
|
|
56
|
+
RGBAColor: `import("@needle-tools/engine").RGBAColor`,
|
|
57
|
+
RenderTexture: `import("@needle-tools/engine").RenderTexture`,
|
|
58
|
+
Renderer: `import("@needle-tools/engine").Renderer`,
|
|
59
|
+
Rigidbody: `import("@needle-tools/engine").Rigidbody`,
|
|
60
|
+
Sprite: `import("@needle-tools/engine").Sprite`,
|
|
61
|
+
Vec2: `import("@needle-tools/engine").Vec2`,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Map a single non-array, non-primitive type token to its TS representation.
|
|
66
|
+
* Returns null if unknown.
|
|
67
|
+
* @param {string} token
|
|
68
|
+
* @returns {string | null}
|
|
69
|
+
*/
|
|
70
|
+
function mapKnownType(token) {
|
|
71
|
+
if (token in THREE_TYPES) return THREE_TYPES[token];
|
|
72
|
+
if (token in NEEDLE_TYPES) return NEEDLE_TYPES[token];
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Convert a manifest type string to a safe ambient TS type.
|
|
78
|
+
* Primitives and known Three.js/Needle types are resolved precisely.
|
|
79
|
+
* For unknown types on a manifest component, falls back to
|
|
80
|
+
* `import("@needle-tools/engine").ComponentName["fieldName"]`.
|
|
81
|
+
* Truly unresolvable types → "unknown".
|
|
82
|
+
*
|
|
83
|
+
* @param {string} typeStr
|
|
84
|
+
* @param {string} [componentName] The manifest component class name (enables indexed-access fallback)
|
|
85
|
+
* @param {string} [fieldName] The field name on that component
|
|
86
|
+
* @returns {string}
|
|
87
|
+
*/
|
|
88
|
+
export function manifestTypeToTs(typeStr, componentName, fieldName) {
|
|
89
|
+
const parts = typeStr.split(" | ").map(p => p.trim());
|
|
90
|
+
const safeParts = parts.map(p => {
|
|
91
|
+
if (p === "undefined" || p === "null") return p;
|
|
92
|
+
const arrayMatch = p.match(/^(number|string|boolean)\[\]$/);
|
|
93
|
+
if (arrayMatch) return p;
|
|
94
|
+
if (PRIMITIVE_TYPES.has(p)) return p;
|
|
95
|
+
const arrayTypeMatch = p.match(/^(\w+)\[\]$/);
|
|
96
|
+
if (arrayTypeMatch) {
|
|
97
|
+
const base = arrayTypeMatch[1];
|
|
98
|
+
const mapped = mapKnownType(base);
|
|
99
|
+
if (mapped) return `${mapped}[]`;
|
|
100
|
+
}
|
|
101
|
+
const known = mapKnownType(p);
|
|
102
|
+
if (known) return known;
|
|
103
|
+
return null;
|
|
104
|
+
});
|
|
105
|
+
if (safeParts.every(p => p !== null)) {
|
|
106
|
+
return /** @type {string[]} */ (safeParts).join(" | ");
|
|
107
|
+
}
|
|
108
|
+
if (componentName && fieldName) {
|
|
109
|
+
return `import("@needle-tools/engine").${componentName}["${fieldName}"]`;
|
|
110
|
+
}
|
|
111
|
+
return "unknown";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Load components.needle.json and build a lookup:
|
|
116
|
+
* componentName → Map<fieldName, tsType>
|
|
117
|
+
* Inherited fields are flattened (inheritedFrom chain is resolved).
|
|
118
|
+
*
|
|
119
|
+
* @returns {Map<string, Map<string, string>>}
|
|
120
|
+
*/
|
|
121
|
+
export function loadComponentsManifest() {
|
|
122
|
+
/** @type {Map<string, Map<string, string>>} */
|
|
123
|
+
const manifest = new Map();
|
|
124
|
+
const manifestPath = join(__dirname, "../../components.needle.json");
|
|
125
|
+
if (!existsSync(manifestPath)) return manifest;
|
|
126
|
+
try {
|
|
127
|
+
/** @type {Array<{name: string, inheritedFrom?: string, children?: Array<{name: string, kind: string, type: string}>}>} */
|
|
128
|
+
const entries = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
129
|
+
|
|
130
|
+
/** @type {Map<string, Map<string, string>>} */
|
|
131
|
+
const ownFields = new Map();
|
|
132
|
+
/** @type {Map<string, string>} */
|
|
133
|
+
const inheritedFrom = new Map();
|
|
134
|
+
for (const entry of entries) {
|
|
135
|
+
if (!entry.name) continue;
|
|
136
|
+
inheritedFrom.set(entry.name, entry.inheritedFrom || "");
|
|
137
|
+
/** @type {Map<string, string>} */
|
|
138
|
+
const fields = new Map();
|
|
139
|
+
if (Array.isArray(entry.children)) {
|
|
140
|
+
for (const child of entry.children) {
|
|
141
|
+
if (child.kind === "property" && child.name && child.type) {
|
|
142
|
+
fields.set(child.name, manifestTypeToTs(child.type, entry.name, child.name));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
ownFields.set(entry.name, fields);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** @param {string} name @returns {Map<string, string>} */
|
|
150
|
+
function resolveFields(name) {
|
|
151
|
+
if (manifest.has(name)) return /** @type {Map<string, string>} */ (manifest.get(name));
|
|
152
|
+
const own = ownFields.get(name) ?? new Map();
|
|
153
|
+
const parent = inheritedFrom.get(name);
|
|
154
|
+
if (parent && ownFields.has(parent)) {
|
|
155
|
+
const parentFields = resolveFields(parent);
|
|
156
|
+
const merged = new Map([...parentFields, ...own]);
|
|
157
|
+
manifest.set(name, merged);
|
|
158
|
+
return merged;
|
|
159
|
+
}
|
|
160
|
+
manifest.set(name, own);
|
|
161
|
+
return own;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (const name of ownFields.keys()) {
|
|
165
|
+
resolveFields(name);
|
|
166
|
+
}
|
|
167
|
+
} catch (e) {
|
|
168
|
+
console.warn("[needle:dts-generator] Failed to load components.needle.json:", (/** @type {any} */ (e))?.message ?? e);
|
|
169
|
+
}
|
|
170
|
+
return manifest;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** @type {Map<string, Map<string, string>>} */
|
|
174
|
+
export const componentsManifest = loadComponentsManifest();
|
package/plugins/types/index.d.ts
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ambient module declaration for `needle-bindings`.
|
|
3
|
+
*
|
|
4
|
+
* `SceneData` is keyed by GLB friendly name, then by the node hierarchy:
|
|
5
|
+
* ctx.sceneData.Minimal.Camera.$object // → THREE.Camera
|
|
6
|
+
* ctx.sceneData.Minimal.Camera.$components.OrbitControls.autoRotate = true;
|
|
7
|
+
* ctx.sceneData.Minimal.UI.Button.$components.Button
|
|
8
|
+
*
|
|
9
|
+
* Each node entry has:
|
|
10
|
+
* $object — the Three.js Object3D (typed precisely, e.g. Mesh, Camera, Light)
|
|
11
|
+
* $components — Needle components attached to this node
|
|
12
|
+
* [childName] — child nodes, recursively typed
|
|
13
|
+
*
|
|
14
|
+
* When the `needle:dts-generator` Vite plugin is active it writes
|
|
15
|
+
* `.needle/generated/needle-bindings.gen.d.ts` next to the installed package,
|
|
16
|
+
* which augments this interface with the actual bindings extracted from
|
|
17
|
+
* the project's GLB files at build time.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// Pull in the project-local generated augmentation (written by needle:dts-generator).
|
|
21
|
+
/// <reference path="../../.needle/generated/needle-bindings.gen.d.ts" />
|
|
22
|
+
|
|
23
|
+
declare module "needle-bindings" {
|
|
24
|
+
/**
|
|
25
|
+
* Scene data keyed by GLB friendly name, then by node hierarchy.
|
|
26
|
+
* Fallback index signature allows unknown names without crashing.
|
|
27
|
+
*/
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
29
|
+
interface SceneData {}
|
|
30
|
+
}
|
|
@@ -26,10 +26,17 @@ export type userSettings = {
|
|
|
26
26
|
noCopy?: boolean;
|
|
27
27
|
/** When enabled the needle-engine include directory will be copied */
|
|
28
28
|
copyIncludesFromEngine?: boolean;
|
|
29
|
-
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/** Set to `false` to prevent the Rapier physics engine from loading.
|
|
32
|
+
* NOTE: by default Needle Engine uses dynamic loading of the Rapier physics engine. This means that the Rapier code is only loaded when a physics component is used in the scene.
|
|
33
|
+
*/
|
|
30
34
|
useRapier?: boolean;
|
|
31
|
-
/**
|
|
35
|
+
/** Set to `false` to prevent postprocessing effects from loading.
|
|
36
|
+
* NOTE: by default Needle Engine uses dynamic loading of postprocessing modules. This means that the postprocessing code is only loaded when a postprocessing effect is used in the scene.
|
|
37
|
+
*/
|
|
32
38
|
usePostprocessing?: boolean;
|
|
39
|
+
|
|
33
40
|
noDependencyWatcher?: boolean;
|
|
34
41
|
/** set to false to suppress editor-sync package installation and connection */
|
|
35
42
|
dontInstallEditor?: boolean;
|
|
@@ -169,4 +176,16 @@ export type userSettings = {
|
|
|
169
176
|
|
|
170
177
|
/** Set to true to disable the plugin that ensures VSCode workspace settings for custom-elements.json data */
|
|
171
178
|
noCustomElementData?: boolean;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Generate Typescript declaration files for references 3D assets in your project.
|
|
182
|
+
* These will be available via `context.sceneData` in your code.
|
|
183
|
+
* @default enabled
|
|
184
|
+
*/
|
|
185
|
+
dts?: {
|
|
186
|
+
/** When set to false, disables the generation of TypeScript declaration files.
|
|
187
|
+
* @default true
|
|
188
|
+
*/
|
|
189
|
+
enabled?: boolean;
|
|
190
|
+
}
|
|
172
191
|
}
|
package/plugins/vite/asap.js
CHANGED
|
@@ -22,7 +22,7 @@ export async function needleAsap(command, config, userSettings) {
|
|
|
22
22
|
|
|
23
23
|
fixMainTs();
|
|
24
24
|
|
|
25
|
-
if (command
|
|
25
|
+
if (command === "serve") {
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -50,7 +50,7 @@ export async function needleAsap(command, config, userSettings) {
|
|
|
50
50
|
const tags = [];
|
|
51
51
|
|
|
52
52
|
try {
|
|
53
|
-
generateGltfPreloadLinks(config, html, tags);
|
|
53
|
+
generateGltfPreloadLinks(config, html, tags, viteConfig?.base);
|
|
54
54
|
}
|
|
55
55
|
catch (err) {
|
|
56
56
|
console.error("Error generating gltf preload links", err);
|
|
@@ -173,18 +173,21 @@ function fixMainTs() {
|
|
|
173
173
|
* @param {import('vite').ResolvedConfig} _config
|
|
174
174
|
* @param {import('vite').HtmlTagDescriptor[]} tags
|
|
175
175
|
*/
|
|
176
|
-
function generateScriptPreloadLinks(
|
|
176
|
+
function generateScriptPreloadLinks(config, tags) {
|
|
177
177
|
try {
|
|
178
|
+
const base = config.base || '/';
|
|
178
179
|
const chunks = preloadScriptPaths;
|
|
179
180
|
// console.log("ASAP", chunks)
|
|
180
181
|
if (chunks.length > 0) {
|
|
181
182
|
for (const chunk of chunks) {
|
|
183
|
+
// Apply base path so preload hrefs resolve correctly under SPA routing
|
|
184
|
+
const href = chunk.startsWith('./') ? base + chunk.slice(2) : chunk;
|
|
182
185
|
tags.push({
|
|
183
186
|
tag: 'link',
|
|
184
187
|
attrs: {
|
|
185
188
|
rel: "modulepreload",
|
|
186
189
|
as: "script",
|
|
187
|
-
href:
|
|
190
|
+
href: href,
|
|
188
191
|
}
|
|
189
192
|
});
|
|
190
193
|
}
|
|
@@ -203,15 +206,16 @@ const codegenRegex = /\"(?<gltf>.+(.glb|.gltf)(\?.*)?)\"/gm;
|
|
|
203
206
|
/**
|
|
204
207
|
* @param {import('../types').needleConfig} config
|
|
205
208
|
* @param {string} html
|
|
206
|
-
* @param {import('vite').HtmlTagDescriptor[]} tags
|
|
209
|
+
* @param {import('vite').HtmlTagDescriptor[]} tags
|
|
210
|
+
* @param {string} [base]
|
|
207
211
|
**/
|
|
208
|
-
function generateGltfPreloadLinks(config, html, tags) {
|
|
212
|
+
function generateGltfPreloadLinks(config, html, tags, base) {
|
|
209
213
|
|
|
210
214
|
// TODO: try to get the <needle-engine src> element src attribute and preload that
|
|
211
215
|
const needleEngineMatches = tryParseNeedleEngineSrcAttributeFromHtml(html);
|
|
212
216
|
if (needleEngineMatches?.length) {
|
|
213
217
|
for (const item of needleEngineMatches) {
|
|
214
|
-
insertPreloadLink(tags, item, "model/gltf+json");
|
|
218
|
+
insertPreloadLink(tags, item, "model/gltf+json", base);
|
|
215
219
|
}
|
|
216
220
|
}
|
|
217
221
|
|
|
@@ -246,7 +250,7 @@ function generateGltfPreloadLinks(config, html, tags) {
|
|
|
246
250
|
}
|
|
247
251
|
}
|
|
248
252
|
needleLog("needle:asap", `Insert glTF preload link: ${value}`);
|
|
249
|
-
insertPreloadLink(tags, value, "model/gltf+json");
|
|
253
|
+
insertPreloadLink(tags, value, "model/gltf+json", base);
|
|
250
254
|
}
|
|
251
255
|
}
|
|
252
256
|
}
|
|
@@ -258,9 +262,14 @@ function generateGltfPreloadLinks(config, html, tags) {
|
|
|
258
262
|
* @param {import('vite').HtmlTagDescriptor[]} tags
|
|
259
263
|
* @param {string} href
|
|
260
264
|
* @param {string} type
|
|
265
|
+
* @param {string} [base]
|
|
261
266
|
*/
|
|
262
|
-
function insertPreloadLink(tags, href, type) {
|
|
267
|
+
function insertPreloadLink(tags, href, type, base) {
|
|
263
268
|
if (!href) return;
|
|
269
|
+
// Apply base path so preload hrefs resolve correctly under SPA routing
|
|
270
|
+
if (base && !href.startsWith('http') && !href.startsWith('/')) {
|
|
271
|
+
href = base + (href.startsWith('./') ? href.slice(2) : href);
|
|
272
|
+
}
|
|
264
273
|
tags.push({
|
|
265
274
|
tag: 'link',
|
|
266
275
|
attrs: {
|
|
@@ -61,6 +61,35 @@ export function needleDependencies(command, config, userSettings) {
|
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
63
|
},
|
|
64
|
+
// Vite 8's optimizer rebases `new URL(specifier, import.meta.url)` paths
|
|
65
|
+
// inside pre-bundled dependencies (PR #21434), but only handles truly relative
|
|
66
|
+
// paths (./ ../). Bare-specifier paths like `three-mesh-bvh/src/workers/...`
|
|
67
|
+
// are treated as relative to the *source file* that contained the `new URL()`
|
|
68
|
+
// call, producing a wrong URL such as:
|
|
69
|
+
// /node_modules/@needle-tools/engine/.../three-mesh-bvh/src/workers/generateMeshBVH.worker.js
|
|
70
|
+
// This middleware rewrites those broken URLs so the dev server finds the file at
|
|
71
|
+
// its real location in node_modules.
|
|
72
|
+
{
|
|
73
|
+
name: 'needle:worker-url-rewrite',
|
|
74
|
+
configureServer(server) {
|
|
75
|
+
const rewritePackages = ['three-mesh-bvh'];
|
|
76
|
+
server.middlewares.use((req, _res, next) => {
|
|
77
|
+
if (req.url) {
|
|
78
|
+
for (const pkg of rewritePackages) {
|
|
79
|
+
const marker = `/${pkg}/`;
|
|
80
|
+
if (req.url.includes(marker) && !req.url.startsWith(`/node_modules/${pkg}/`)) {
|
|
81
|
+
const idx = req.url.indexOf(marker);
|
|
82
|
+
const rewritten = '/node_modules' + req.url.slice(idx);
|
|
83
|
+
needleLog('needle-dependencies', `Rewriting worker URL → ${rewritten}`);
|
|
84
|
+
req.url = rewritten;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
next();
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
},
|
|
64
93
|
]
|
|
65
94
|
}
|
|
66
95
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @param {"build" | "serve"}
|
|
2
|
+
* @param {"build" | "serve" | undefined} _command
|
|
3
3
|
* @param {unknown} _config
|
|
4
4
|
* @param {import('../types').userSettings} userSettings
|
|
5
5
|
* @returns {import('vite').Plugin | null}
|
|
6
6
|
*/
|
|
7
|
-
export function needleDependencyWatcher(
|
|
7
|
+
export function needleDependencyWatcher(_command: "build" | "serve" | undefined, _config: unknown, userSettings: import("../types").userSettings): import("vite").Plugin | null;
|
|
8
8
|
export type PackageJson = {
|
|
9
9
|
dependencies?: Record<string, string>;
|
|
10
10
|
devDependencies?: Record<string, string>;
|
|
@@ -12,14 +12,12 @@ function log(...msg) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* @param {"build" | "serve"}
|
|
15
|
+
* @param {"build" | "serve" | undefined} _command
|
|
16
16
|
* @param {unknown} _config
|
|
17
17
|
* @param {import('../types').userSettings} userSettings
|
|
18
18
|
* @returns {import('vite').Plugin | null}
|
|
19
19
|
*/
|
|
20
|
-
export function needleDependencyWatcher(
|
|
21
|
-
if (command === "build") return null;
|
|
22
|
-
|
|
20
|
+
export function needleDependencyWatcher(_command, _config, userSettings) {
|
|
23
21
|
if (userSettings?.noDependencyWatcher === true) return null;
|
|
24
22
|
|
|
25
23
|
const dir = process.cwd();
|
|
@@ -28,6 +26,7 @@ export function needleDependencyWatcher(command, _config, userSettings) {
|
|
|
28
26
|
|
|
29
27
|
return /** @type {import('vite').Plugin} */ ({
|
|
30
28
|
name: 'needle-dependency-watcher',
|
|
29
|
+
apply: 'serve',
|
|
31
30
|
/** @param {import('vite').ViteDevServer} server */
|
|
32
31
|
configureServer(server) {
|
|
33
32
|
manageClients(server);
|
package/plugins/vite/drop.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Experimental, allow dropping files from Unity into the running scene.
|
|
2
|
-
* @param {"build" | "serve"}
|
|
2
|
+
* @param {"build" | "serve" | undefined} _command
|
|
3
3
|
* @param {import('../types/needleConfig').needleMeta | null | undefined} config
|
|
4
4
|
* @param {import('../types/userconfig.js').userSettings} userSettings
|
|
5
5
|
* @returns {import('vite').Plugin | null | undefined}
|
|
6
6
|
*/
|
|
7
|
-
export function needleDrop(
|
|
7
|
+
export function needleDrop(_command: "build" | "serve" | undefined, config: import("../types/needleConfig").needleMeta | null | undefined, userSettings: import("../types/userconfig.js").userSettings): import("vite").Plugin | null | undefined;
|
package/plugins/vite/drop.js
CHANGED
|
@@ -7,18 +7,17 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
7
7
|
const __dirname = path.dirname(__filename);
|
|
8
8
|
|
|
9
9
|
/** Experimental, allow dropping files from Unity into the running scene.
|
|
10
|
-
* @param {"build" | "serve"}
|
|
10
|
+
* @param {"build" | "serve" | undefined} _command
|
|
11
11
|
* @param {import('../types/needleConfig').needleMeta | null | undefined} config
|
|
12
12
|
* @param {import('../types/userconfig.js').userSettings} userSettings
|
|
13
13
|
* @returns {import('vite').Plugin | null | undefined}
|
|
14
14
|
*/
|
|
15
|
-
export function needleDrop(
|
|
16
|
-
if (command === "build") return;
|
|
17
|
-
|
|
15
|
+
export function needleDrop(_command, config, userSettings) {
|
|
18
16
|
if(userSettings.useDrop !== true) return null;
|
|
19
17
|
|
|
20
18
|
return {
|
|
21
19
|
name: "needle:drop",
|
|
20
|
+
apply: 'serve',
|
|
22
21
|
config(/** @type {{ server?: { hmr?: { port?: number } } }} */ viteConfig) {
|
|
23
22
|
if(userSettings)
|
|
24
23
|
if (!viteConfig.server) viteConfig.server = {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {"build" | "serve"} _command Vite command (unused — runs in both modes)
|
|
3
|
+
* @param {import('../types/needleConfig').needleMeta | null | undefined} _config
|
|
4
|
+
* @param {import('../types').userSettings} [_userSettings]
|
|
5
|
+
* @returns {import('vite').Plugin | null}
|
|
6
|
+
*/
|
|
7
|
+
export function needleDtsGenerator(_command: "build" | "serve", _config: import("../types/needleConfig").needleMeta | null | undefined, _userSettings?: import("../types").userSettings): import("vite").Plugin | null;
|