@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.
Files changed (250) hide show
  1. package/.needle/generated/needle-bindings.gen.d.ts +5 -0
  2. package/CHANGELOG.md +52 -0
  3. package/components.needle.json +1 -1
  4. package/dist/{gltf-progressive-DJBMx-zB.umd.cjs → gltf-progressive-BmblPzFj.umd.cjs} +4 -4
  5. package/dist/{gltf-progressive-BryRjllq.min.js → gltf-progressive-CN_mbb66.min.js} +2 -2
  6. package/dist/{gltf-progressive-Cl167Vjx.js → gltf-progressive-DUlhxdv4.js} +5 -2
  7. package/dist/needle-engine.bundle-C-ixARur.umd.cjs +1733 -0
  8. package/dist/needle-engine.bundle-CHmXdnE1.min.js +1733 -0
  9. package/dist/{needle-engine.bundle-BGyKqxBH.js → needle-engine.bundle-DF01sSGQ.js} +10841 -10434
  10. package/dist/needle-engine.d.ts +244 -54
  11. package/dist/needle-engine.js +541 -536
  12. package/dist/needle-engine.min.js +1 -1
  13. package/dist/needle-engine.umd.cjs +1 -1
  14. package/dist/{postprocessing-B_9sKVU7.min.js → postprocessing-B571qGWR.min.js} +34 -34
  15. package/dist/{postprocessing-WDc9WwI3.js → postprocessing-CfrLAbLX.js} +0 -1
  16. package/dist/{postprocessing-B2wb6pzI.umd.cjs → postprocessing-CiGkAeM9.umd.cjs} +17 -17
  17. package/dist/{vendor-CAcsI0eU.js → vendor-BFrMaK9q.js} +8983 -9136
  18. package/dist/vendor-CJmyOrCq.min.js +1116 -0
  19. package/dist/vendor-DkMW3WY4.umd.cjs +1116 -0
  20. package/lib/engine/api.d.ts +14 -0
  21. package/lib/engine/api.js +4 -0
  22. package/lib/engine/api.js.map +1 -1
  23. package/lib/engine/debug/debug_environment.js +1 -1
  24. package/lib/engine/debug/debug_environment.js.map +1 -1
  25. package/lib/engine/debug/debug_spatial_console.d.ts +2 -0
  26. package/lib/engine/debug/debug_spatial_console.js +10 -7
  27. package/lib/engine/debug/debug_spatial_console.js.map +1 -1
  28. package/lib/engine/engine_addressables.d.ts +2 -0
  29. package/lib/engine/engine_addressables.js +6 -3
  30. package/lib/engine/engine_addressables.js.map +1 -1
  31. package/lib/engine/engine_application.js +8 -6
  32. package/lib/engine/engine_application.js.map +1 -1
  33. package/lib/engine/engine_audio.d.ts +68 -0
  34. package/lib/engine/engine_audio.js +172 -0
  35. package/lib/engine/engine_audio.js.map +1 -1
  36. package/lib/engine/engine_components.js +1 -1
  37. package/lib/engine/engine_components.js.map +1 -1
  38. package/lib/engine/engine_constants.js +6 -0
  39. package/lib/engine/engine_constants.js.map +1 -1
  40. package/lib/engine/engine_context.d.ts +33 -7
  41. package/lib/engine/engine_context.js +40 -2
  42. package/lib/engine/engine_context.js.map +1 -1
  43. package/lib/engine/engine_context_registry.js +1 -1
  44. package/lib/engine/engine_context_registry.js.map +1 -1
  45. package/lib/engine/engine_gameobject.js +2 -2
  46. package/lib/engine/engine_gameobject.js.map +1 -1
  47. package/lib/engine/engine_init.js +16 -1
  48. package/lib/engine/engine_init.js.map +1 -1
  49. package/lib/engine/engine_input.d.ts +3 -2
  50. package/lib/engine/engine_input.js +3 -2
  51. package/lib/engine/engine_input.js.map +1 -1
  52. package/lib/engine/engine_license.d.ts +2 -0
  53. package/lib/engine/engine_license.js +25 -15
  54. package/lib/engine/engine_license.js.map +1 -1
  55. package/lib/engine/engine_lifecycle_functions_internal.js +5 -0
  56. package/lib/engine/engine_lifecycle_functions_internal.js.map +1 -1
  57. package/lib/engine/engine_mainloop_utils.js +5 -2
  58. package/lib/engine/engine_mainloop_utils.js.map +1 -1
  59. package/lib/engine/engine_networking_blob.d.ts +1 -1
  60. package/lib/engine/engine_networking_blob.js +5 -11
  61. package/lib/engine/engine_networking_blob.js.map +1 -1
  62. package/lib/engine/engine_physics_rapier.js +0 -1
  63. package/lib/engine/engine_physics_rapier.js.map +1 -1
  64. package/lib/engine/engine_pmrem.js +2 -2
  65. package/lib/engine/engine_pmrem.js.map +1 -1
  66. package/lib/engine/engine_scenedata.d.ts +32 -0
  67. package/lib/engine/engine_scenedata.js +138 -0
  68. package/lib/engine/engine_scenedata.js.map +1 -0
  69. package/lib/engine/engine_serialization_builtin_serializer.d.ts +10 -16
  70. package/lib/engine/engine_serialization_builtin_serializer.js +55 -41
  71. package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
  72. package/lib/engine/engine_ssr.d.ts +18 -0
  73. package/lib/engine/engine_ssr.js +40 -0
  74. package/lib/engine/engine_ssr.js.map +1 -0
  75. package/lib/engine/engine_three_utils.d.ts +14 -7
  76. package/lib/engine/engine_three_utils.js +14 -7
  77. package/lib/engine/engine_three_utils.js.map +1 -1
  78. package/lib/engine/engine_types.d.ts +2 -0
  79. package/lib/engine/engine_types.js.map +1 -1
  80. package/lib/engine/engine_utils.js +2 -0
  81. package/lib/engine/engine_utils.js.map +1 -1
  82. package/lib/engine/engine_utils_hash.d.ts +9 -0
  83. package/lib/engine/engine_utils_hash.js +112 -0
  84. package/lib/engine/engine_utils_hash.js.map +1 -0
  85. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
  86. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
  87. package/lib/engine/webcomponents/jsx.d.ts +51 -0
  88. package/lib/engine/webcomponents/logo-element.d.ts +2 -1
  89. package/lib/engine/webcomponents/logo-element.js +2 -1
  90. package/lib/engine/webcomponents/logo-element.js.map +1 -1
  91. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +4 -4
  92. package/lib/engine/webcomponents/needle menu/needle-menu.js +2 -1
  93. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  94. package/lib/engine/webcomponents/needle-button.d.ts +2 -1
  95. package/lib/engine/webcomponents/needle-button.js +2 -1
  96. package/lib/engine/webcomponents/needle-button.js.map +1 -1
  97. package/lib/engine/webcomponents/needle-engine.d.ts +11 -4
  98. package/lib/engine/webcomponents/needle-engine.js +2 -1
  99. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  100. package/lib/engine/xr/NeedleXRSession.d.ts +3 -2
  101. package/lib/engine/xr/NeedleXRSession.js +51 -15
  102. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  103. package/lib/engine/xr/events.d.ts +1 -1
  104. package/lib/engine/xr/events.js.map +1 -1
  105. package/lib/engine-components/Animation.js +17 -16
  106. package/lib/engine-components/Animation.js.map +1 -1
  107. package/lib/engine-components/AnimatorController.d.ts +2 -0
  108. package/lib/engine-components/AnimatorController.js +4 -1
  109. package/lib/engine-components/AnimatorController.js.map +1 -1
  110. package/lib/engine-components/AudioSource.d.ts +19 -3
  111. package/lib/engine-components/AudioSource.js +121 -68
  112. package/lib/engine-components/AudioSource.js.map +1 -1
  113. package/lib/engine-components/DragControls.d.ts +7 -0
  114. package/lib/engine-components/DragControls.js +19 -0
  115. package/lib/engine-components/DragControls.js.map +1 -1
  116. package/lib/engine-components/Light.d.ts +6 -8
  117. package/lib/engine-components/Light.js +40 -27
  118. package/lib/engine-components/Light.js.map +1 -1
  119. package/lib/engine-components/Networking.d.ts +1 -1
  120. package/lib/engine-components/Networking.js +1 -1
  121. package/lib/engine-components/OrbitControls.js +16 -11
  122. package/lib/engine-components/OrbitControls.js.map +1 -1
  123. package/lib/engine-components/ReflectionProbe.js +2 -0
  124. package/lib/engine-components/ReflectionProbe.js.map +1 -1
  125. package/lib/engine-components/RigidBody.js +3 -3
  126. package/lib/engine-components/RigidBody.js.map +1 -1
  127. package/lib/engine-components/SceneSwitcher.js +2 -0
  128. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  129. package/lib/engine-components/SeeThrough.js +2 -2
  130. package/lib/engine-components/SeeThrough.js.map +1 -1
  131. package/lib/engine-components/api.d.ts +1 -1
  132. package/lib/engine-components/api.js +1 -1
  133. package/lib/engine-components/api.js.map +1 -1
  134. package/lib/engine-components/postprocessing/Effects/BloomEffect.d.ts +1 -1
  135. package/lib/engine-components/postprocessing/Effects/Sharpening.js +1 -2
  136. package/lib/engine-components/postprocessing/Effects/Sharpening.js.map +1 -1
  137. package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
  138. package/lib/engine-components/postprocessing/PostProcessingHandler.js +5 -6
  139. package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
  140. package/lib/engine-components/postprocessing/VolumeParameter.d.ts +2 -0
  141. package/lib/engine-components/postprocessing/VolumeParameter.js +4 -1
  142. package/lib/engine-components/postprocessing/VolumeParameter.js.map +1 -1
  143. package/lib/engine-components/ui/Canvas.d.ts +1 -1
  144. package/lib/engine-components/ui/Canvas.js +2 -8
  145. package/lib/engine-components/ui/Canvas.js.map +1 -1
  146. package/lib/engine-components/ui/Text.d.ts +1 -0
  147. package/lib/engine-components/ui/Text.js +10 -7
  148. package/lib/engine-components/ui/Text.js.map +1 -1
  149. package/lib/engine-components/web/CursorFollow.js +21 -12
  150. package/lib/engine-components/web/CursorFollow.js.map +1 -1
  151. package/lib/engine-components/web/ScrollFollow.d.ts +0 -1
  152. package/lib/engine-components/web/ScrollFollow.js +3 -2
  153. package/lib/engine-components/web/ScrollFollow.js.map +1 -1
  154. package/lib/engine-components/webxr/WebXRImageTracking.js +4 -0
  155. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  156. package/lib/needle-engine.d.ts +2 -0
  157. package/lib/needle-engine.js +2 -0
  158. package/lib/needle-engine.js.map +1 -1
  159. package/package.json +6 -4
  160. package/plugins/dts-generator/dts.codegen.js +334 -0
  161. package/plugins/dts-generator/dts.scan.js +99 -0
  162. package/plugins/dts-generator/dts.writer.js +59 -0
  163. package/plugins/dts-generator/glb.discovery.js +279 -0
  164. package/plugins/dts-generator/glb.extractor.js +215 -0
  165. package/plugins/dts-generator/glb.reader.js +167 -0
  166. package/plugins/dts-generator/index.js +36 -0
  167. package/plugins/dts-generator/manifest.types.js +174 -0
  168. package/plugins/types/index.d.ts +2 -1
  169. package/plugins/types/needle-bindings.d.ts +30 -0
  170. package/plugins/types/userconfig.d.ts +21 -2
  171. package/plugins/vite/asap.js +18 -9
  172. package/plugins/vite/dependencies.js +29 -0
  173. package/plugins/vite/dependency-watcher.d.ts +2 -2
  174. package/plugins/vite/dependency-watcher.js +3 -4
  175. package/plugins/vite/drop.d.ts +2 -2
  176. package/plugins/vite/drop.js +3 -4
  177. package/plugins/vite/dts-generator.d.ts +7 -0
  178. package/plugins/vite/dts-generator.js +191 -0
  179. package/plugins/vite/index.d.ts +10 -3
  180. package/plugins/vite/index.js +27 -10
  181. package/plugins/vite/local-files-core.js +3 -3
  182. package/plugins/vite/local-files-utils.d.ts +3 -1
  183. package/plugins/vite/local-files-utils.js +29 -5
  184. package/plugins/vite/logging.js +2 -2
  185. package/plugins/vite/meta.js +4 -2
  186. package/plugins/vite/poster.d.ts +2 -2
  187. package/plugins/vite/poster.js +3 -5
  188. package/plugins/vite/reload.d.ts +2 -2
  189. package/plugins/vite/reload.js +23 -22
  190. package/src/engine/api.ts +18 -1
  191. package/src/engine/debug/debug_environment.ts +1 -1
  192. package/src/engine/debug/debug_spatial_console.ts +10 -7
  193. package/src/engine/engine_addressables.ts +6 -3
  194. package/src/engine/engine_application.ts +8 -6
  195. package/src/engine/engine_audio.ts +184 -0
  196. package/src/engine/engine_components.ts +1 -1
  197. package/src/engine/engine_constants.ts +11 -6
  198. package/src/engine/engine_context.ts +50 -7
  199. package/src/engine/engine_context_registry.ts +1 -1
  200. package/src/engine/engine_gameobject.ts +2 -2
  201. package/src/engine/engine_init.ts +15 -1
  202. package/src/engine/engine_input.ts +3 -2
  203. package/src/engine/engine_license.ts +23 -19
  204. package/src/engine/engine_lifecycle_functions_internal.ts +7 -0
  205. package/src/engine/engine_mainloop_utils.ts +5 -2
  206. package/src/engine/engine_networking_blob.ts +5 -11
  207. package/src/engine/engine_physics_rapier.ts +0 -3
  208. package/src/engine/engine_pmrem.ts +3 -3
  209. package/src/engine/engine_scenedata.ts +136 -0
  210. package/src/engine/engine_serialization_builtin_serializer.ts +63 -46
  211. package/src/engine/engine_ssr.ts +48 -0
  212. package/src/engine/engine_three_utils.ts +15 -7
  213. package/src/engine/engine_types.ts +2 -0
  214. package/src/engine/engine_utils.ts +1 -0
  215. package/src/engine/engine_utils_hash.ts +65 -0
  216. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +1 -1
  217. package/src/engine/webcomponents/jsx.d.ts +51 -0
  218. package/src/engine/webcomponents/logo-element.ts +3 -1
  219. package/src/engine/webcomponents/needle menu/needle-menu.ts +4 -2
  220. package/src/engine/webcomponents/needle-button.ts +3 -1
  221. package/src/engine/webcomponents/needle-engine.ts +12 -4
  222. package/src/engine/xr/NeedleXRSession.ts +49 -14
  223. package/src/engine/xr/events.ts +1 -1
  224. package/src/engine-components/Animation.ts +19 -16
  225. package/src/engine-components/AnimatorController.ts +4 -1
  226. package/src/engine-components/AudioSource.ts +130 -79
  227. package/src/engine-components/DragControls.ts +18 -2
  228. package/src/engine-components/Light.ts +40 -26
  229. package/src/engine-components/Networking.ts +1 -1
  230. package/src/engine-components/OrbitControls.ts +18 -9
  231. package/src/engine-components/ReflectionProbe.ts +2 -0
  232. package/src/engine-components/RigidBody.ts +3 -3
  233. package/src/engine-components/SceneSwitcher.ts +1 -0
  234. package/src/engine-components/SeeThrough.ts +2 -2
  235. package/src/engine-components/api.ts +1 -1
  236. package/src/engine-components/postprocessing/Effects/BloomEffect.ts +1 -1
  237. package/src/engine-components/postprocessing/Effects/Sharpening.ts +1 -2
  238. package/src/engine-components/postprocessing/PostProcessingHandler.ts +4 -8
  239. package/src/engine-components/postprocessing/VolumeParameter.ts +4 -1
  240. package/src/engine-components/ui/Canvas.ts +2 -8
  241. package/src/engine-components/ui/Text.ts +12 -8
  242. package/src/engine-components/web/CursorFollow.ts +21 -13
  243. package/src/engine-components/web/ScrollFollow.ts +2 -2
  244. package/src/engine-components/webxr/WebXRImageTracking.ts +2 -0
  245. package/src/needle-engine.ts +3 -0
  246. package/src/vite-env.d.ts +16 -0
  247. package/dist/needle-engine.bundle-CiYtOO2O.min.js +0 -1732
  248. package/dist/needle-engine.bundle-DzVx9Z8D.umd.cjs +0 -1732
  249. package/dist/vendor-CEM38hLE.umd.cjs +0 -1116
  250. package/dist/vendor-HRlxIBga.min.js +0 -1116
@@ -0,0 +1,191 @@
1
+ // @ts-check
2
+ /**
3
+ * Vite plugin: needle:dts-generator
4
+ *
5
+ * Thin wrapper around plugins/dts-generator/index.js.
6
+ * Regenerates `needle-bindings.d.ts` on startup and whenever a .glb / .gltf
7
+ * file changes in the assets directory.
8
+ *
9
+ * The generated file is written to `{codegenDirectory}/needle-bindings.d.ts`
10
+ * (falls back to `src/generated/needle-bindings.d.ts`).
11
+ *
12
+ * Usage — the plugin is already wired into needlePlugins(). To use standalone:
13
+ *
14
+ * import { needleDtsGenerator } from "@needle-tools/engine/plugins/vite/dts-generator.js";
15
+ * // in vite.config.js plugins array:
16
+ * needleDtsGenerator(command, needleConfig, userSettings)
17
+ */
18
+
19
+ import { join, resolve, dirname } from 'path';
20
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, realpathSync } from 'fs';
21
+ import { tryLoadProjectConfig } from './config.js';
22
+ import { generateBindingsDts } from '../dts-generator/index.js';
23
+ import { needleLog } from './logging.js';
24
+
25
+ // Two dirs up from plugins/vite/ → package root.
26
+ // Use fileURLToPath so %7E and other URL-encoded characters in the path are decoded correctly.
27
+ // realpathSync follows symlinks so the path is stable even in npm link / monorepo setups.
28
+ import { fileURLToPath } from 'url';
29
+ let _packageRoot = /** @type {string | null} */ (null);
30
+ try {
31
+ _packageRoot = realpathSync(join(dirname(fileURLToPath(import.meta.url)), '..', '..'));
32
+ } catch (_e) {
33
+ // If we can't resolve the package root (e.g. unusual install layout), the plugin
34
+ // will silently skip generation rather than crashing the dev server.
35
+ }
36
+
37
+ /**
38
+ * Ensure `.vscode/settings.json` references the generated `needle-html-data.json`
39
+ * so VS Code provides `data-bind-needle` completions in HTML files automatically.
40
+ * Only adds the entry if it isn't already present — never overwrites other settings.
41
+ *
42
+ * @param {string} projectRoot
43
+ * @param {string} htmlDataPath Absolute path to the generated needle-html-data.json
44
+ */
45
+ function ensureVscodeHtmlCustomData(projectRoot, htmlDataPath) {
46
+ const vscodeDir = join(projectRoot, ".vscode");
47
+ const settingsPath = join(vscodeDir, "settings.json");
48
+
49
+ // Relative path from project root for portability
50
+ const relPath = htmlDataPath.replace(projectRoot + "/", "").replace(projectRoot + "\\", "");
51
+
52
+ /** @type {Record<string, unknown>} */
53
+ let settings = {};
54
+ if (existsSync(settingsPath)) {
55
+ try {
56
+ settings = JSON.parse(readFileSync(settingsPath, "utf8"));
57
+ } catch (_e) { /* malformed JSON — leave settings empty, will add key */ }
58
+ }
59
+
60
+ const key = "html.customData";
61
+ const existing = Array.isArray(settings[key]) ? /** @type {string[]} */ (settings[key]) : [];
62
+ if (existing.includes(relPath)) return; // already registered
63
+
64
+ settings[key] = [...existing, relPath];
65
+
66
+ mkdirSync(vscodeDir, { recursive: true });
67
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
68
+ needleLog("needle:dts-generator", `registered HTML completions in .vscode/settings.json`);
69
+ }
70
+
71
+ /**
72
+ * @param {"build" | "serve"} _command Vite command (unused — runs in both modes)
73
+ * @param {import('../types/needleConfig').needleMeta | null | undefined} _config
74
+ * @param {import('../types').userSettings} [_userSettings]
75
+ * @returns {import('vite').Plugin | null}
76
+ */
77
+ export function needleDtsGenerator(_command, _config, _userSettings) {
78
+
79
+ if(_userSettings?.dts?.enabled === false) {
80
+ return null;
81
+ }
82
+
83
+ let projectRoot = process.cwd();
84
+
85
+ function resolveCodegenDir() {
86
+ const projectConfig = tryLoadProjectConfig();
87
+ return projectConfig?.codegenDirectory
88
+ ? resolve(projectRoot, projectConfig.codegenDirectory)
89
+ : join(projectRoot, "src", "generated");
90
+ }
91
+
92
+ function resolveOutputPath() {
93
+ // Write to a .needle dotfolder at the package root.
94
+ // Not in the `files` allowlist → never npm-published.
95
+ // ../../.needle/ from plugins/types/ resolves correctly for both
96
+ // symlinked (js/package~/) and published (node_modules/@needle-tools/engine/) layouts.
97
+ return join(/** @type {string} */ (_packageRoot), ".needle", "generated", "needle-bindings.gen.d.ts");
98
+ }
99
+
100
+ function resolveAssetsDir() {
101
+ const projectConfig = tryLoadProjectConfig();
102
+ return projectConfig?.assetsDirectory
103
+ ? resolve(projectRoot, projectConfig.assetsDirectory)
104
+ : join(projectRoot, "assets");
105
+ }
106
+
107
+ /** @type {import('vite').ViteDevServer | undefined} */
108
+ let devServer;
109
+
110
+ /** @type {Promise<void> | null} */
111
+ let _runInFlight = null;
112
+
113
+ function run() {
114
+ if (_runInFlight) return _runInFlight;
115
+ _runInFlight = _doRun().finally(() => { _runInFlight = null; });
116
+ return _runInFlight;
117
+ }
118
+
119
+ async function _doRun() {
120
+ try {
121
+ if (!_packageRoot) return;
122
+ const assetsDir = resolveAssetsDir();
123
+ const outputPath = resolveOutputPath();
124
+ const codegenDir = resolveCodegenDir();
125
+ const count = await generateBindingsDts({ assetsDir, outputPath, projectRoot, codegenDir });
126
+ // HTML custom data sits next to the generated dts in .needle/generated/
127
+ const htmlDataPath = join(/** @type {string} */ (_packageRoot), ".needle", "generated", "needle-html-data.json");
128
+ ensureVscodeHtmlCustomData(projectRoot, htmlDataPath);
129
+ if (count !== false) {
130
+ needleLog("needle:dts-generator", `${count} binding(s) → ${outputPath.replace(process.cwd(), ".")}`);
131
+ if (devServer) {
132
+ const hot = devServer.hot ?? devServer.ws;
133
+ hot.send({ type: "full-reload", path: "*" });
134
+ }
135
+ } else {
136
+ needleLog("needle:dts-generator", `up-to-date → ${outputPath.replace(process.cwd(), ".")}`);
137
+ }
138
+ } catch (err) {
139
+ needleLog("needle:dts-generator", "Failed: " + (/** @type {any} */ (err)?.message ?? err));
140
+ }
141
+ }
142
+
143
+ return {
144
+ name: "needle:dts-generator",
145
+
146
+ /** @param {import('vite').ResolvedConfig} config */
147
+ configResolved(config) {
148
+ projectRoot = config.root ?? process.cwd();
149
+ },
150
+
151
+ buildStart() {
152
+ // In serve mode, the configureServer post-hook runs instead.
153
+ // Only run here for actual builds (no devServer).
154
+ if (!devServer) return run();
155
+ return undefined;
156
+ },
157
+
158
+ /** @param {import('vite').ViteDevServer} server */
159
+ configureServer(server) {
160
+ devServer = server;
161
+
162
+ // Watch assets directory for GLB/glTF changes and regenerate
163
+ const assetsDir = resolveAssetsDir();
164
+ server.watcher.add(assetsDir);
165
+
166
+ // Also watch files that determine which GLBs are entrypoints
167
+ const indexHtmlPath = join(projectRoot, "index.html");
168
+ const genJsPath = join(resolveCodegenDir(), "gen.js");
169
+ server.watcher.add(indexHtmlPath);
170
+ server.watcher.add(genJsPath);
171
+
172
+ server.watcher.on("change", (file) => {
173
+ if (
174
+ (/\.(glb|gltf)$/i.test(file) && file.startsWith(assetsDir)) ||
175
+ file === indexHtmlPath ||
176
+ file === genJsPath
177
+ ) {
178
+ run();
179
+ }
180
+ });
181
+ server.watcher.on("add", (file) => {
182
+ if (/\.(glb|gltf)$/i.test(file) && file.startsWith(assetsDir)) {
183
+ run();
184
+ }
185
+ });
186
+
187
+ // Return post-hook so Vite awaits the initial run before printing "ready"
188
+ return () => run();
189
+ },
190
+ };
191
+ }
@@ -19,13 +19,20 @@
19
19
  * ],
20
20
  * }
21
21
  * ```
22
- * @param {"build" | "serve"} command
23
- * @param {import('../types/needleConfig').needleMeta | null | undefined} config
22
+ * When used without arguments (e.g. in a SvelteKit `defineConfig({})` plain object),
23
+ * the command is resolved automatically from Vite's `configResolved` hook.
24
+ * ```js
25
+ * // SvelteKit — no command needed:
26
+ * export default defineConfig({ plugins: [sveltekit(), needlePlugins()] });
27
+ * ```
28
+ * @param {"build" | "serve" | undefined} [command]
29
+ * @param {import('../types/needleConfig').needleMeta | null | undefined} [config]
24
30
  * @param {import('../types/index.js').userSettings} [userSettings]
25
31
  * @returns {Promise<import('vite').Plugin[]>}
26
32
  */
27
- export function needlePlugins(command: "build" | "serve", config?: import("../types/needleConfig").needleMeta | null | undefined, userSettings?: import("../types/index.js").userSettings): Promise<import("vite").Plugin[]>;
33
+ export function needlePlugins(command?: "build" | "serve" | undefined, config?: import("../types/needleConfig").needleMeta | null | undefined, userSettings?: import("../types/index.js").userSettings): Promise<import("vite").Plugin[]>;
28
34
  export { needleAI } from "./ai.js";
35
+ export { needleDtsGenerator } from "./dts-generator.js";
29
36
  export { needleAsap } from "./asap.js";
30
37
  export { needleDefines } from "./defines.js";
31
38
  export { needleBuildPipeline } from "./build-pipeline.js";
@@ -1,6 +1,9 @@
1
1
  import { needleAI } from "./ai.js";
2
2
  export { needleAI } from "./ai.js";
3
3
 
4
+ import { needleDtsGenerator } from "./dts-generator.js";
5
+ export { needleDtsGenerator } from "./dts-generator.js";
6
+
4
7
  import { needleAsap } from "./asap.js";
5
8
  export { needleAsap } from "./asap.js";
6
9
 
@@ -113,12 +116,18 @@ const defaultUserSettings = {
113
116
  * ],
114
117
  * }
115
118
  * ```
116
- * @param {"build" | "serve"} command
117
- * @param {import('../types/needleConfig').needleMeta | null | undefined} config
119
+ * When used without arguments (e.g. in a SvelteKit `defineConfig({})` plain object),
120
+ * the command is resolved automatically from Vite's `configResolved` hook.
121
+ * ```js
122
+ * // SvelteKit — no command needed:
123
+ * export default defineConfig({ plugins: [sveltekit(), needlePlugins()] });
124
+ * ```
125
+ * @param {"build" | "serve" | undefined} [command]
126
+ * @param {import('../types/needleConfig').needleMeta | null | undefined} [config]
118
127
  * @param {import('../types/index.js').userSettings} [userSettings]
119
128
  * @returns {Promise<import('vite').Plugin[]>}
120
129
  */
121
- export async function needlePlugins(command, config = undefined, userSettings = {}) {
130
+ export async function needlePlugins(command = undefined, config = undefined, userSettings = {}) {
122
131
 
123
132
  if (!config) config = {}
124
133
 
@@ -126,6 +135,7 @@ export async function needlePlugins(command, config = undefined, userSettings =
126
135
  userSettings = { ...defaultUserSettings, ...userSettings };
127
136
 
128
137
  const array = [
138
+ needleDtsGenerator(command, config, userSettings),
129
139
  needleAI(command, config, userSettings),
130
140
  needleLogger(command, config, userSettings),
131
141
  needleDefines(command, config, userSettings),
@@ -164,14 +174,21 @@ export async function needlePlugins(command, config = undefined, userSettings =
164
174
  array.push(await editorConnection(command, config, userSettings, array));
165
175
  array.push(needleDependencyWatcher(command, config, userSettings));
166
176
 
167
- // Ensure the process exits on SIGINT (Ctrl+C) since plugin timers/sockets can keep the event loop alive
168
- if (command === "serve") {
177
+ // Ensure the process exits on SIGINT (Ctrl+C) since plugin timers/sockets can keep the event loop alive.
178
+ // If command is unknown at call time (e.g. SvelteKit plain defineConfig), defer to configResolved.
179
+ const registerSigint = () => {
169
180
  process.on('SIGINT', () => {
170
- // Wait a moment to allow plugins to clean up if needed, then exit
171
- setTimeout(() => {
172
- console.debug('\nGoodbye!');
173
- process.exit();
174
- }, 1000);
181
+ setTimeout(() => { console.debug('\nGoodbye!'); process.exit(); }, 1000);
182
+ });
183
+ };
184
+ if (command === "serve") {
185
+ registerSigint();
186
+ } else if (command === undefined) {
187
+ array.push({
188
+ name: "needle:sigint",
189
+ configResolved(config) {
190
+ if (config.command === "serve") registerSigint();
191
+ }
175
192
  });
176
193
  }
177
194
 
@@ -291,7 +291,7 @@ export function needleMakeFilesLocal(command, _config, userSettings) {
291
291
  failedDownloads,
292
292
  localizationStats,
293
293
  }, activeHandlers);
294
- src = fixRelativeNewURL(src);
294
+ src = fixRelativeNewURL(src, viteConfig?.base);
295
295
  }
296
296
  catch (err) {
297
297
  needleLog("needle:local-files", "Error in transform: " + getErrMessage(err), "error");
@@ -303,7 +303,7 @@ export function needleMakeFilesLocal(command, _config, userSettings) {
303
303
  },
304
304
  renderChunk(code, chunk) {
305
305
  if (!chunk.fileName?.endsWith(".js")) return null;
306
- const fixed = fixRelativeNewURL(code);
306
+ const fixed = fixRelativeNewURL(code, viteConfig?.base);
307
307
  if (fixed === code) return null;
308
308
  return {
309
309
  code: fixed,
@@ -314,7 +314,7 @@ export function needleMakeFilesLocal(command, _config, userSettings) {
314
314
  for (const output of Object.values(bundle)) {
315
315
  if (output.type !== "chunk") continue;
316
316
  if (!output.fileName?.endsWith(".js")) continue;
317
- const fixed = fixRelativeNewURL(output.code);
317
+ const fixed = fixRelativeNewURL(output.code, viteConfig?.base);
318
318
  if (fixed !== output.code) output.code = fixed;
319
319
  }
320
320
  },
@@ -24,9 +24,11 @@ export function normalizeWebPath(path: string): string;
24
24
  export function ensureTrailingSlash(path: string): string;
25
25
  /**
26
26
  * @param {string} src
27
+ * @param {string} [base] - Vite base path (e.g. "/" or "/app/"). When provided,
28
+ * ext/ paths are made absolute so they resolve correctly under SPA routing.
27
29
  * @returns {string}
28
30
  */
29
- export function fixRelativeNewURL(src: string): string;
31
+ export function fixRelativeNewURL(src: string, base?: string): string;
30
32
  /**
31
33
  * @param {string} src
32
34
  * @returns {string}
@@ -113,25 +113,49 @@ export function ensureTrailingSlash(path) {
113
113
 
114
114
  /**
115
115
  * @param {string} src
116
+ * @param {string} [base] - Vite base path (e.g. "/" or "/app/"). When provided,
117
+ * ext/ paths are made absolute so they resolve correctly under SPA routing.
116
118
  * @returns {string}
117
119
  */
118
- export function fixRelativeNewURL(src) {
120
+ export function fixRelativeNewURL(src, base) {
121
+ /** @param {string} path */
122
+ function makeAbsolute(path) {
123
+ // Strip any leading ./ ../ or / prefix to get the clean ext/... path
124
+ const clean = path.replace(/^(?:\.\.?\/)+/, '').replace(/^\//, '');
125
+ if (base) return base.replace(/\/+$/, '') + '/' + clean;
126
+ return clean;
127
+ }
128
+
119
129
  src = src.replace(
120
130
  /(?<==\s*)(["'])((?:(?:\.{1,2}\/)|\/)?ext\/[^"']*\/)\1/g,
121
131
  (/** @type {string} */ _match, /** @type {string} */ quote, /** @type {string} */ path) => {
122
- const runtimePath = path.startsWith("/ext/") ? path.substring(1) : path;
123
- return `new URL(${quote}${runtimePath}${quote}, self.location?.href || ${quote}${quote}).href`;
132
+ const resolved = makeAbsolute(path);
133
+ return `new URL(${quote}${resolved}${quote}, self.location?.href || ${quote}${quote}).href`;
124
134
  }
125
135
  );
126
136
 
127
137
  src = src.replace(
128
138
  /new\s+URL\s*\(\s*(["'`])((?:(?:\.{1,2}\/)|\/)?ext\/[^"'`]+)\1\s*\)/g,
129
139
  (/** @type {string} */ _match, /** @type {string} */ quote, /** @type {string} */ path) => {
130
- const runtimePath = path.startsWith("/ext/") ? path.substring(1) : path;
131
- return `new URL(${quote}${runtimePath}${quote}, self.location?.href)`;
140
+ const resolved = makeAbsolute(path);
141
+ return `new URL(${quote}${resolved}${quote}, self.location?.href)`;
132
142
  }
133
143
  );
134
144
 
145
+ // Make remaining ext/ string literals absolute (e.g. font paths passed as
146
+ // function arguments like addVariant("normal","normal","ext/fonts/...")).
147
+ // The regexes above already consumed paths inside new URL() calls and
148
+ // assignment-based patterns, so this only catches leftover bare strings.
149
+ if (base) {
150
+ src = src.replace(
151
+ /(["'`])((?:\.{1,2}\/)?ext\/[^"'`]+)\1/g,
152
+ (/** @type {string} */ _match, /** @type {string} */ quote, /** @type {string} */ path) => {
153
+ const resolved = makeAbsolute(path);
154
+ return `${quote}${resolved}${quote}`;
155
+ }
156
+ );
157
+ }
158
+
135
159
  return src;
136
160
  }
137
161
 
@@ -97,8 +97,8 @@ export function needleLog(pluginName, message, level = 'log', options = undefine
97
97
  ? (dimBody ? leveledBody.split('\n').map(l => colors.dim(l)).join('\n') : leveledBody)
98
98
  : "";
99
99
  const payloadCore = emitHeader
100
- ? (body.length > 0 ? `${header}\n${body}\n` : `${header}\n`)
101
- : (body.length > 0 ? `${body}\n` : "");
100
+ ? (body.length > 0 ? `${header}\n${body}` : `${header}`)
101
+ : (body.length > 0 ? `${body}` : "");
102
102
  const payload = leadingNewline ? `\n${payloadCore}` : payloadCore;
103
103
  log(payload);
104
104
  return;
@@ -13,8 +13,7 @@ import { needleGreenBold, needleLog } from './logging.js';
13
13
  */
14
14
  export function needleMeta(command, config, userSettings) {
15
15
 
16
- // we can check if this is a build
17
- const isBuild = command === 'build';
16
+ let isBuild = command === 'build';
18
17
 
19
18
  async function updateConfig() {
20
19
  config = await loadConfig();
@@ -25,6 +24,9 @@ export function needleMeta(command, config, userSettings) {
25
24
  return {
26
25
  // replace meta tags
27
26
  name: 'needle:meta',
27
+ configResolved(resolvedConfig) {
28
+ isBuild = resolvedConfig.command === 'build';
29
+ },
28
30
  transformIndexHtml: {
29
31
  order: 'pre',
30
32
  handler(/** @type {string} */ html, /** @type {unknown} */ _ctx) {
@@ -1,8 +1,8 @@
1
1
  export function getPosterPath(): string;
2
2
  /**
3
- * @param {"build" | "serve"} command
3
+ * @param {"build" | "serve" | undefined} _command
4
4
  * @param {import('../types/needleConfig').needleMeta | null | undefined} config
5
5
  * @param {import('../types').userSettings} userSettings
6
6
  * @returns {import('vite').Plugin | undefined}
7
7
  */
8
- export function needlePoster(command: "build" | "serve", config: import("../types/needleConfig").needleMeta | null | undefined, userSettings: import("../types").userSettings): import("vite").Plugin | undefined;
8
+ export function needlePoster(_command: "build" | "serve" | undefined, config: import("../types/needleConfig").needleMeta | null | undefined, userSettings: import("../types").userSettings): import("vite").Plugin | undefined;
@@ -10,19 +10,17 @@ export function getPosterPath() {
10
10
  }
11
11
 
12
12
  /**
13
- * @param {"build" | "serve"} command
13
+ * @param {"build" | "serve" | undefined} _command
14
14
  * @param {import('../types/needleConfig').needleMeta | null | undefined} config
15
15
  * @param {import('../types').userSettings} userSettings
16
16
  * @returns {import('vite').Plugin | undefined}
17
17
  */
18
- export function needlePoster(command, config, userSettings) {
19
- // only relevant for local development
20
- if (command === 'build') return [];
21
-
18
+ export function needlePoster(_command, config, userSettings) {
22
19
  if (userSettings.noPoster) return;
23
20
 
24
21
  return {
25
22
  name: 'needle:poster',
23
+ apply: 'serve',
26
24
  configureServer(server) {
27
25
  const hot = server.hot ?? server.ws;
28
26
  hot.on('needle:screenshot', async (data, client) => {
@@ -1,7 +1,7 @@
1
1
  /**
2
- * @param {"build" | "serve"} command
2
+ * @param {"build" | "serve" | undefined} _command
3
3
  * @param {import('../types/needleConfig').needleMeta | null} config
4
4
  * @param {import('../types').userSettings} userSettings
5
5
  * @returns {import('vite').Plugin | undefined}
6
6
  */
7
- export function needleReload(command: "build" | "serve", config: import("../types/needleConfig").needleMeta | null, userSettings: import("../types").userSettings): import("vite").Plugin | undefined;
7
+ export function needleReload(_command: "build" | "serve" | undefined, config: import("../types/needleConfig").needleMeta | null, userSettings: import("../types").userSettings): import("vite").Plugin | undefined;
@@ -7,6 +7,7 @@ import { existsSync, readFileSync, statSync } from 'fs';
7
7
  import { fileURLToPath } from 'url';
8
8
  import { needleLog } from './logging.js';
9
9
 
10
+ const pluginName = "needle-reload";
10
11
  const __filename = fileURLToPath(import.meta.url);
11
12
  const __dirname = path.dirname(__filename);
12
13
 
@@ -15,14 +16,12 @@ const filesUsingHotReload = new Set();
15
16
  let assetsDirectory = "";
16
17
 
17
18
  /**
18
- * @param {"build" | "serve"} command
19
+ * @param {"build" | "serve" | undefined} _command
19
20
  * @param {import('../types/needleConfig').needleMeta | null} config
20
21
  * @param {import('../types').userSettings} userSettings
21
22
  * @returns {import('vite').Plugin | undefined}
22
23
  */
23
- export function needleReload(command, config, userSettings) {
24
- if (command === "build") return undefined;
25
-
24
+ export function needleReload(_command, config, userSettings) {
26
25
  if (userSettings?.noReload === true) return undefined;
27
26
 
28
27
 
@@ -41,7 +40,7 @@ export function needleReload(command, config, userSettings) {
41
40
  const buildDirectory = projectConfig?.buildDirectory?.length ? process.cwd().replaceAll("\\", "/") + "/" + projectConfig?.buildDirectory : "";
42
41
  if (buildDirectory?.length) {
43
42
  const relativeBuildDirectory = path.relative(process.cwd(), buildDirectory).replaceAll("\\", "/") || ".";
44
- setTimeout(() => needleLog("needle-reload", "Build directory: " + relativeBuildDirectory), 100);
43
+ setTimeout(() => needleLog(pluginName, "Build directory: " + relativeBuildDirectory), 100);
45
44
  }
46
45
 
47
46
  // These ignore patterns will be injected into user config to better control vite reloading
@@ -51,6 +50,7 @@ export function needleReload(command, config, userSettings) {
51
50
 
52
51
  return {
53
52
  name: 'needle:reload',
53
+ apply: 'serve',
54
54
  /** @param {import('vite').UserConfig} config */
55
55
  config(config) {
56
56
  if (!config.server) config.server = { watch: { ignored: [] } };
@@ -60,7 +60,7 @@ export function needleReload(command, config, userSettings) {
60
60
  // @ts-ignore - watch.ignored is guaranteed to be string[] by the guards above
61
61
  config.server.watch.ignored.push(pattern);
62
62
  if (userSettings?.debug === true)
63
- setTimeout(() => needleLog("needle-reload", "Updated server ignore patterns: " + JSON.stringify(config.server?.watch?.ignored)), 100);
63
+ setTimeout(() => needleLog(pluginName, "Updated server ignore patterns: " + JSON.stringify(config.server?.watch?.ignored)), 100);
64
64
  },
65
65
  /** @param {import('vite').HmrContext & {buildDirectory?: string}} args */
66
66
  handleHotUpdate(args) {
@@ -119,7 +119,7 @@ function getHot(server) {
119
119
 
120
120
  /** @param {import('vite').ViteDevServer} server @param {string} [file] */
121
121
  function notifyClientWillReload(server, file) {
122
- console.log("Send reload notification");
122
+ needleLog(pluginName, "Send reload notification");
123
123
  getHot(server).send('needle:reload', { type: 'will-reload', file: file });
124
124
  }
125
125
 
@@ -135,7 +135,7 @@ async function handleReload({ file, server, modules: _modules, read, buildDirect
135
135
 
136
136
  // Dont reload the whole server when a file that is using hot reload changes
137
137
  if (filesUsingHotReload.has(file)) {
138
- console.log("File is using hot reload: " + file);
138
+ needleLog(pluginName, "File is using hot reload: " + file);
139
139
  return null;
140
140
  }
141
141
 
@@ -154,7 +154,7 @@ async function handleReload({ file, server, modules: _modules, read, buildDirect
154
154
  // instead of relying on the vite server watch ignore array
155
155
  // we could here also match paths that we know we dont want to track
156
156
  if (ignorePatterns.length > 0 && ignoreRegex.test(file)) {
157
- console.log("Ignore change in file: " + getFileNameLog(file));
157
+ needleLog(pluginName, "Ignore change in file: " + getFileNameLog(file));
158
158
  return [];
159
159
  }
160
160
 
@@ -164,7 +164,7 @@ async function handleReload({ file, server, modules: _modules, read, buildDirect
164
164
  if (buildDirectory?.length) {
165
165
  const dir = path.dirname(file).replaceAll("\\", "/");
166
166
  if (dir.startsWith(buildDirectory)) {
167
- console.log("Ignore change in build directory: " + getFileNameLog(file));
167
+ needleLog(pluginName, "Ignore change in build directory: " + getFileNameLog(file));
168
168
  return [];
169
169
  }
170
170
  }
@@ -172,19 +172,20 @@ async function handleReload({ file, server, modules: _modules, read, buildDirect
172
172
  // Check if codegen files actually changed their content
173
173
  // this will return false if its the first update
174
174
  // meaning if its the first export after the server starts those will not trigger a reload
175
+ // Ignore d.ts
175
176
  const shouldCheckIfContentChanged = file.includes("/codegen/") || file.includes("/generated/") || file.endsWith("gen.js");// || file.endsWith(".glb") || file.endsWith(".gltf") || file.endsWith(".bin");
176
- if (shouldCheckIfContentChanged) {
177
+ if (!file.includes("/.svelte-kit/") && !file.endsWith(".d.ts") && !file.endsWith("needle-html-data.json") && shouldCheckIfContentChanged) {
177
178
  if (reloadIsScheduled) {
178
179
  return [];
179
180
  }
180
181
  if (await testIfFileContentChanged(file, read) === false) {
181
- console.log("File content didnt change: " + getFileNameLog(file));
182
+ needleLog(pluginName, "File content didnt change: " + getFileNameLog(file));
182
183
  return [];
183
184
  }
184
185
  }
185
186
 
186
187
  // these are known file types we export from integrations
187
- const knownExportFileTypes = [ ".glb", ".gltf", ".bin", "exr", ".ktx2", ".mp3", ".ogg", ".mp4", ".webm" ];
188
+ const knownExportFileTypes = [".glb", ".gltf", ".bin", "exr", ".ktx2", ".mp3", ".ogg", ".mp4", ".webm"];
188
189
  if (!knownExportFileTypes.some((type) => file.endsWith(type)))
189
190
  return null;
190
191
 
@@ -208,7 +209,7 @@ async function handleReload({ file, server, modules: _modules, read, buildDirect
208
209
  }
209
210
  }
210
211
 
211
- console.log("> Detected file change: ", getFileNameLog(file) + " (" + ((fileSize / (1024 * 1024)).toFixed(1)) + " MB)");
212
+ needleLog(pluginName, "> Detected file change: " + getFileNameLog(file) + " (" + ((fileSize / (1024 * 1024)).toFixed(1)) + " MB)");
212
213
  notifyClientWillReload(server);
213
214
  scheduleReload(server);
214
215
  return [];
@@ -223,7 +224,7 @@ async function scheduleReload(server, level = 0) {
223
224
  const lockFile = path.join(process.cwd(), lockFileName);
224
225
  if (existsSync(lockFile)) {
225
226
  if (level === 0)
226
- console.log("Lock file exists, waiting for export to finish...");
227
+ needleLog(pluginName, "Lock file exists, waiting for export to finish...");
227
228
  setTimeout(() => scheduleReload(server, level += 1), 300);
228
229
  return;
229
230
  }
@@ -234,13 +235,13 @@ async function scheduleReload(server, level = 0) {
234
235
  if (timeDiff < 1000) {
235
236
  // Sometimes file changes happen immediately after triggering a reload
236
237
  // we dont want to reload again in that case
237
- console.log("Ignoring reload, last reload was too recent", timeDiff);
238
+ needleLog(pluginName, "Ignoring reload, last reload was too recent" + timeDiff + "ms ago");
238
239
  return;
239
240
  }
240
241
 
241
242
  lastReloadTime = Date.now();
242
243
  const readableTime = new Date(lastReloadTime).toLocaleTimeString();
243
- console.log("< Reloading... " + readableTime)
244
+ needleLog(pluginName, "< Reloading... " + readableTime);
244
245
  getHot(server).send({
245
246
  type: 'full-reload',
246
247
  path: '*'
@@ -299,10 +300,10 @@ function removeVersionQueryArgument(content) {
299
300
  function insertScriptRegisterHotReloadCode(src, filePath) {
300
301
 
301
302
  // We only want to inject the hot reload code in the needle-engine root file
302
- if(!filePath.includes("/src/needle-engine.ts")) {
303
+ if (!filePath.includes("/src/needle-engine.ts")) {
303
304
  return src;
304
305
  }
305
- console.log("[Needle HMR] Hot reload is enabled");
306
+ needleLog(pluginName, "[Needle HMR] Hot reload is enabled");
306
307
  // this code let's the engine know that we are in hot reload mode
307
308
  const code = `
308
309
  globalThis.NEEDLE_HOT_RELOAD_ENABLED = true;
@@ -338,12 +339,12 @@ function insertScriptHotReloadCode(src, filePath) {
338
339
  return undefined;
339
340
  // make import path from engine package
340
341
  const fullPathToHotReload = process.cwd() + "/node_modules/@needle-tools/engine/src/engine/engine_hot_reload.ts";
341
- // console.log(fullPathToHotReload);
342
+ // needleLog(pluginName, fullPathToHotReload);
342
343
  const fileDirectory = path.dirname(filePath);
343
- // console.log("DIR", fileDirectory)
344
+ // needleLog(pluginName, "DIR", fileDirectory)
344
345
  const relativePath = path.relative(fileDirectory, fullPathToHotReload);
345
346
  importPath = relativePath.replace(/\\/g, "/");
346
- // console.log("importPath: ", importPath);
347
+ // needleLog(pluginName, "importPath: ", importPath);
347
348
  }
348
349
 
349
350
  // console.log(importPath, ">", filePath);