@needle-tools/engine 4.14.0 → 4.15.0-next.f391a30

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 (198) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/components.needle.json +1 -1
  3. package/dist/{gltf-progressive-BttGBXw6.umd.cjs → gltf-progressive-CMwJPwEt.umd.cjs} +1 -1
  4. package/dist/{gltf-progressive-Bm_6aEi4.js → gltf-progressive-CTlvpS3A.js} +1 -1
  5. package/dist/{gltf-progressive-T5WKTux5.min.js → gltf-progressive-DYL3SLVb.min.js} +1 -1
  6. package/dist/materialx-4jJLLe9Q.js +4174 -0
  7. package/dist/materialx-Bt9FHwco.min.js +158 -0
  8. package/dist/materialx-NDD0y4JY.umd.cjs +158 -0
  9. package/dist/{needle-engine.bundle-COL2Bar3.umd.cjs → needle-engine.bundle-C1BFRZDF.umd.cjs} +150 -140
  10. package/dist/{needle-engine.bundle-Z_gAD7Kg.js → needle-engine.bundle-DB4kLWO_.js} +6651 -6400
  11. package/dist/{needle-engine.bundle-NolzHLqO.min.js → needle-engine.bundle-DsTdfmeb.min.js} +151 -141
  12. package/dist/needle-engine.d.ts +345 -88
  13. package/dist/needle-engine.js +322 -322
  14. package/dist/needle-engine.min.js +1 -1
  15. package/dist/needle-engine.umd.cjs +1 -1
  16. package/dist/{postprocessing-06AXuvdv.min.js → postprocessing-BN-f4viE.min.js} +1 -1
  17. package/dist/{postprocessing-CPDcA21P.umd.cjs → postprocessing-DYmYOVm4.umd.cjs} +1 -1
  18. package/dist/{postprocessing-CI2x8Cln.js → postprocessing-De9ZpJrk.js} +1 -1
  19. package/dist/{three-examples-BMmNgNCN.umd.cjs → three-examples-BHqRVpO_.umd.cjs} +12 -12
  20. package/dist/{three-examples-CMYCd5nH.js → three-examples-C0ZCCA_K.js} +182 -192
  21. package/dist/{three-examples-CQl1fFZp.min.js → three-examples-DmTY8tGr.min.js} +14 -14
  22. package/lib/engine/api.d.ts +0 -2
  23. package/lib/engine/api.js +0 -2
  24. package/lib/engine/api.js.map +1 -1
  25. package/lib/engine/debug/debug.js +1 -1
  26. package/lib/engine/debug/debug.js.map +1 -1
  27. package/lib/engine/debug/debug_spatial_console.js +1 -1
  28. package/lib/engine/debug/debug_spatial_console.js.map +1 -1
  29. package/lib/engine/engine_accessibility.d.ts +77 -0
  30. package/lib/engine/engine_accessibility.js +162 -0
  31. package/lib/engine/engine_accessibility.js.map +1 -0
  32. package/lib/engine/engine_context.d.ts +2 -0
  33. package/lib/engine/engine_context.js +8 -1
  34. package/lib/engine/engine_context.js.map +1 -1
  35. package/lib/engine/engine_create_objects.js +1 -1
  36. package/lib/engine/engine_create_objects.js.map +1 -1
  37. package/lib/engine/engine_gizmos.js +1 -1
  38. package/lib/engine/engine_gizmos.js.map +1 -1
  39. package/lib/engine/engine_license.js +7 -2
  40. package/lib/engine/engine_license.js.map +1 -1
  41. package/lib/engine/engine_materialpropertyblock.d.ts +90 -4
  42. package/lib/engine/engine_materialpropertyblock.js +97 -7
  43. package/lib/engine/engine_materialpropertyblock.js.map +1 -1
  44. package/lib/engine/engine_math.d.ts +34 -1
  45. package/lib/engine/engine_math.js +34 -1
  46. package/lib/engine/engine_math.js.map +1 -1
  47. package/lib/engine/engine_networking.js +1 -1
  48. package/lib/engine/engine_networking.js.map +1 -1
  49. package/lib/engine/engine_types.d.ts +2 -0
  50. package/lib/engine/engine_types.js +2 -0
  51. package/lib/engine/engine_types.js.map +1 -1
  52. package/lib/engine/engine_utils.js +2 -2
  53. package/lib/engine/engine_utils.js.map +1 -1
  54. package/lib/engine/export/gltf/EXT_mesh_gpu_instancing_exporter.js.map +1 -0
  55. package/lib/engine/export/gltf/index.js +1 -1
  56. package/lib/engine/export/gltf/index.js.map +1 -1
  57. package/lib/engine/webcomponents/icons.js +3 -0
  58. package/lib/engine/webcomponents/icons.js.map +1 -1
  59. package/lib/engine/webcomponents/logo-element.d.ts +7 -3
  60. package/lib/engine/webcomponents/logo-element.js +21 -1
  61. package/lib/engine/webcomponents/logo-element.js.map +1 -1
  62. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
  63. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
  64. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +10 -7
  65. package/lib/engine/webcomponents/needle menu/needle-menu.js +14 -4
  66. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  67. package/lib/engine/webcomponents/needle-button.d.ts +37 -11
  68. package/lib/engine/webcomponents/needle-button.js +42 -11
  69. package/lib/engine/webcomponents/needle-button.js.map +1 -1
  70. package/lib/engine/webcomponents/needle-engine.ar-overlay.js +10 -1
  71. package/lib/engine/webcomponents/needle-engine.ar-overlay.js.map +1 -1
  72. package/lib/engine/webcomponents/needle-engine.d.ts +13 -2
  73. package/lib/engine/webcomponents/needle-engine.js +23 -3
  74. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  75. package/lib/engine-components/Component.d.ts +1 -2
  76. package/lib/engine-components/Component.js +1 -3
  77. package/lib/engine-components/Component.js.map +1 -1
  78. package/lib/engine-components/DragControls.d.ts +1 -0
  79. package/lib/engine-components/DragControls.js +21 -0
  80. package/lib/engine-components/DragControls.js.map +1 -1
  81. package/lib/engine-components/NeedleMenu.d.ts +2 -0
  82. package/lib/engine-components/NeedleMenu.js +2 -0
  83. package/lib/engine-components/NeedleMenu.js.map +1 -1
  84. package/lib/engine-components/Networking.d.ts +28 -3
  85. package/lib/engine-components/Networking.js +28 -3
  86. package/lib/engine-components/Networking.js.map +1 -1
  87. package/lib/engine-components/ReflectionProbe.d.ts +25 -2
  88. package/lib/engine-components/ReflectionProbe.js +46 -2
  89. package/lib/engine-components/ReflectionProbe.js.map +1 -1
  90. package/lib/engine-components/Skybox.js +4 -2
  91. package/lib/engine-components/Skybox.js.map +1 -1
  92. package/lib/engine-components/export/gltf/GltfExport.js +1 -1
  93. package/lib/engine-components/export/gltf/GltfExport.js.map +1 -1
  94. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +2 -2
  95. package/lib/engine-components/export/usdz/USDZExporter.js +1 -1
  96. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  97. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.d.ts +15 -0
  98. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +77 -0
  99. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
  100. package/lib/engine-components/export/usdz/extensions/behavior/PhysicsExtension.js +2 -2
  101. package/lib/engine-components/export/usdz/extensions/behavior/PhysicsExtension.js.map +1 -1
  102. package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
  103. package/lib/engine-components/ui/Button.d.ts +1 -0
  104. package/lib/engine-components/ui/Button.js +11 -0
  105. package/lib/engine-components/ui/Button.js.map +1 -1
  106. package/lib/engine-components/ui/Text.d.ts +1 -0
  107. package/lib/engine-components/ui/Text.js +11 -0
  108. package/lib/engine-components/ui/Text.js.map +1 -1
  109. package/package.json +18 -14
  110. package/plugins/common/buildinfo.js +46 -10
  111. package/plugins/common/files.js +2 -1
  112. package/plugins/common/license.js +144 -69
  113. package/plugins/common/logger.js +172 -11
  114. package/plugins/common/needle-engine-skill.md +175 -0
  115. package/plugins/common/worker.js +5 -4
  116. package/plugins/types/userconfig.d.ts +40 -2
  117. package/plugins/vite/ai.js +71 -0
  118. package/plugins/vite/alias.js +6 -5
  119. package/plugins/vite/asap.js +6 -5
  120. package/plugins/vite/build-pipeline.js +224 -41
  121. package/plugins/vite/buildinfo.js +66 -6
  122. package/plugins/vite/copyfiles.js +41 -12
  123. package/plugins/vite/custom-element-data.js +26 -16
  124. package/plugins/vite/defines.js +8 -5
  125. package/plugins/vite/dependencies.js +16 -10
  126. package/plugins/vite/dependency-watcher.js +35 -7
  127. package/plugins/vite/drop-client.js +7 -5
  128. package/plugins/vite/drop.js +16 -14
  129. package/plugins/vite/editor-connection.js +18 -16
  130. package/plugins/vite/imports-logger.js +12 -2
  131. package/plugins/vite/index.js +8 -3
  132. package/plugins/vite/local-files-analysis.js +789 -0
  133. package/plugins/vite/local-files-core.js +992 -0
  134. package/plugins/vite/local-files-internals.js +28 -0
  135. package/plugins/vite/local-files-types.d.ts +111 -0
  136. package/plugins/vite/local-files-utils.js +359 -0
  137. package/plugins/vite/local-files.js +2 -441
  138. package/plugins/vite/logger.client.js +45 -35
  139. package/plugins/vite/logger.js +6 -3
  140. package/plugins/vite/logging.js +129 -0
  141. package/plugins/vite/meta.js +18 -4
  142. package/plugins/vite/needle-app.js +4 -3
  143. package/plugins/vite/peer.js +2 -1
  144. package/plugins/vite/pwa.js +33 -17
  145. package/plugins/vite/reload.js +24 -2
  146. package/src/engine/api.ts +0 -3
  147. package/src/engine/debug/debug.ts +1 -1
  148. package/src/engine/debug/debug_spatial_console.ts +5 -1
  149. package/src/engine/engine_accessibility.ts +198 -0
  150. package/src/engine/engine_context.ts +10 -1
  151. package/src/engine/engine_create_objects.ts +1 -1
  152. package/src/engine/engine_gizmos.ts +9 -5
  153. package/src/engine/engine_license.ts +7 -2
  154. package/src/engine/engine_materialpropertyblock.ts +102 -11
  155. package/src/engine/engine_math.ts +34 -1
  156. package/src/engine/engine_networking.ts +1 -1
  157. package/src/engine/engine_types.ts +5 -0
  158. package/src/engine/engine_utils.ts +2 -2
  159. package/src/engine/export/gltf/index.ts +1 -1
  160. package/src/engine/webcomponents/icons.ts +3 -0
  161. package/src/engine/webcomponents/logo-element.ts +24 -4
  162. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +6 -2
  163. package/src/engine/webcomponents/needle menu/needle-menu.ts +23 -11
  164. package/src/engine/webcomponents/needle-button.ts +44 -13
  165. package/src/engine/webcomponents/needle-engine.ar-overlay.ts +13 -2
  166. package/src/engine/webcomponents/needle-engine.ts +31 -8
  167. package/src/engine-components/Component.ts +2 -5
  168. package/src/engine-components/DragControls.ts +29 -4
  169. package/src/engine-components/NeedleMenu.ts +5 -3
  170. package/src/engine-components/Networking.ts +29 -4
  171. package/src/engine-components/ReflectionProbe.ts +52 -9
  172. package/src/engine-components/Skybox.ts +4 -2
  173. package/src/engine-components/export/gltf/GltfExport.ts +1 -1
  174. package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +2 -2
  175. package/src/engine-components/export/usdz/USDZExporter.ts +1 -1
  176. package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +108 -32
  177. package/src/engine-components/export/usdz/extensions/behavior/PhysicsExtension.ts +2 -2
  178. package/src/engine-components/ui/Button.ts +12 -0
  179. package/src/engine-components/ui/Text.ts +13 -0
  180. package/dist/materialx-CJyQZtjt.min.js +0 -90
  181. package/dist/materialx-DMs1E08Z.js +0 -4636
  182. package/dist/materialx-DaKKOoVk.umd.cjs +0 -90
  183. package/lib/engine/engine_test_utils.d.ts +0 -39
  184. package/lib/engine/engine_test_utils.js +0 -84
  185. package/lib/engine/engine_test_utils.js.map +0 -1
  186. package/lib/include/three/EXT_mesh_gpu_instancing_exporter.js.map +0 -1
  187. package/src/engine/engine_test_utils.ts +0 -109
  188. package/src/include/draco/draco_decoder.js +0 -34
  189. package/src/include/draco/draco_decoder.wasm +0 -0
  190. package/src/include/draco/draco_wasm_wrapper.js +0 -117
  191. package/src/include/ktx2/basis_transcoder.js +0 -19
  192. package/src/include/ktx2/basis_transcoder.wasm +0 -0
  193. package/src/include/needle/arial-msdf.json +0 -1472
  194. package/src/include/needle/arial.png +0 -0
  195. package/src/include/needle/poweredbyneedle.webp +0 -0
  196. /package/lib/{include/three → engine/export/gltf}/EXT_mesh_gpu_instancing_exporter.d.ts +0 -0
  197. /package/lib/{include/three → engine/export/gltf}/EXT_mesh_gpu_instancing_exporter.js +0 -0
  198. /package/src/{include/three → engine/export/gltf}/EXT_mesh_gpu_instancing_exporter.js +0 -0
@@ -0,0 +1,129 @@
1
+ import picocolors from 'picocolors';
2
+
3
+ const { createColors, isColorSupported } = picocolors;
4
+
5
+ const colors = createColors(isColorSupported);
6
+ /** @type {null | (() => void)} */
7
+ let transientLogLineCleaner = null;
8
+
9
+ function getConsoleMethod(level) {
10
+ if (level === 'error') return console.error;
11
+ if (level === 'warn') return console.warn;
12
+ return console.log;
13
+ }
14
+
15
+ function colorBodyByLevel(level, text) {
16
+ if (!isColorSupported) return text;
17
+ if (level === 'error') return colors.red(text);
18
+ if (level === 'warn') return colors.yellow(text);
19
+ return text;
20
+ }
21
+
22
+ function normalizeMessage(message) {
23
+ if (Array.isArray(message)) return message.join("\n");
24
+ if (message === undefined || message === null) return "";
25
+ return String(message);
26
+ }
27
+
28
+ function formatHeader(pluginName) {
29
+ if (!isColorSupported) return `[${pluginName}]`;
30
+
31
+ const name = String(pluginName ?? "");
32
+ let prefix = "";
33
+ let suffix = name;
34
+ if (name.startsWith("needle-")) {
35
+ prefix = "needle-";
36
+ suffix = name.substring("needle-".length);
37
+ }
38
+ else if (name.startsWith("needle:")) {
39
+ prefix = "needle:";
40
+ suffix = name.substring("needle:".length);
41
+ }
42
+
43
+ const open = colors.green("[");
44
+ const close = colors.green("]");
45
+ if (!prefix.length) {
46
+ return `${open}${colors.bold(colors.green(name))}${close}`;
47
+ }
48
+ const prefixStyled = colors.green(prefix);
49
+ const suffixStyled = colors.bold(colors.green(suffix));
50
+ return `${open}${prefixStyled}${suffixStyled}${close}`;
51
+ }
52
+
53
+ /**
54
+ * @param {string} pluginName
55
+ * @param {string | string[]} message
56
+ * @param {'log' | 'warn' | 'error'} [level='log']
57
+ * @param {{ dimBody?: boolean, leadingNewline?: boolean, showHeader?: boolean }} [options]
58
+ */
59
+ export function needleLog(pluginName, message, level = 'log', options = undefined) {
60
+ if (typeof transientLogLineCleaner === "function") {
61
+ transientLogLineCleaner();
62
+ }
63
+ const log = getConsoleMethod(level);
64
+ const headerText = formatHeader(pluginName);
65
+ const bodyText = normalizeMessage(message);
66
+ const dimBody = options?.dimBody ?? (level === 'log');
67
+ const leadingNewline = options?.leadingNewline === true;
68
+ const showHeader = options?.showHeader !== false;
69
+
70
+ if (isColorSupported) {
71
+ const header = `\x1b[0m${headerText}`;
72
+ const leveledBody = colorBodyByLevel(level, bodyText);
73
+ const body = bodyText.length > 0
74
+ ? (dimBody ? leveledBody.split('\n').map(l => colors.dim(l)).join('\n') : leveledBody)
75
+ : "";
76
+ const payloadCore = showHeader
77
+ ? (body.length > 0 ? `${header}\n${body}\n` : `${header}\n`)
78
+ : (body.length > 0 ? `${body}\n` : "");
79
+ const payload = leadingNewline ? `\n${payloadCore}` : payloadCore;
80
+ log(payload);
81
+ return;
82
+ }
83
+
84
+ if (bodyText.length > 0) {
85
+ const out = showHeader ? `${headerText} ${bodyText}` : bodyText;
86
+ log(leadingNewline ? `\n${out}` : out);
87
+ }
88
+ else {
89
+ const out = showHeader ? headerText : "";
90
+ if (out.length > 0) {
91
+ log(leadingNewline ? `\n${out}` : out);
92
+ }
93
+ }
94
+ }
95
+
96
+ /**
97
+ * @param {string} pluginName
98
+ * @param {string[]} lines
99
+ * @param {'log' | 'warn' | 'error'} [level='log']
100
+ */
101
+ export function needleLogLines(pluginName, lines, level = 'log') {
102
+ needleLog(pluginName, lines.join("\n"), level);
103
+ }
104
+
105
+ export function needleSupportsColor() {
106
+ return isColorSupported;
107
+ }
108
+
109
+ export function needleBlue(text) {
110
+ const value = String(text ?? "");
111
+ return isColorSupported ? colors.blue(value) : value;
112
+ }
113
+
114
+ export function needleDim(text) {
115
+ const value = String(text ?? "");
116
+ return isColorSupported ? colors.dim(value) : value;
117
+ }
118
+
119
+ export function needleGreenBold(text) {
120
+ const value = String(text ?? "");
121
+ return isColorSupported ? colors.bold(colors.green(value)) : value;
122
+ }
123
+
124
+ /**
125
+ * @param {(() => void) | null} cleaner
126
+ */
127
+ export function setTransientLogLineCleaner(cleaner) {
128
+ transientLogLineCleaner = typeof cleaner === "function" ? cleaner : null;
129
+ }
@@ -3,6 +3,7 @@ import fs from 'fs';
3
3
  import { tryGetNeedleEngineVersion } from '../common/version.js';
4
4
  import { loadConfig } from './config.js';
5
5
  import { getPosterPath } from './poster.js';
6
+ import { needleGreenBold, needleLog } from './logging.js';
6
7
 
7
8
  /**
8
9
  * @param {string} command
@@ -25,7 +26,7 @@ export const needleMeta = (command, config, userSettings) => {
25
26
  name: 'needle:meta',
26
27
  transformIndexHtml: {
27
28
  order: 'pre',
28
- handler(html, _ctx) {
29
+ handler(/** @type {string} */ html, /** @type {unknown} */ _ctx) {
29
30
 
30
31
  if (userSettings.allowMetaPlugin === false) return [];
31
32
 
@@ -74,7 +75,7 @@ export const needleMeta = (command, config, userSettings) => {
74
75
  tags.push({ tag: 'meta', attrs: { name: 'og:image:height', content: 1080 } });
75
76
  }
76
77
  else if (isBuild) {
77
- console.warn(`Needle: meta.image is set but absolutePath is ${config.absolutePath} in userSettings. Skipping meta.image.`);
78
+ needleLog("needle-meta", `meta.image is set but absolutePath is ${config.absolutePath} in userSettings. Skipping meta.image.`, "warn", { dimBody: false });
78
79
  }
79
80
  }
80
81
 
@@ -114,10 +115,10 @@ export const needleMeta = (command, config, userSettings) => {
114
115
  const needleEngineVersion = tryGetNeedleEngineVersion();
115
116
  if (needleEngineVersion) {
116
117
  if (command === "build")
117
- console.log("Needle Engine version: " + needleEngineVersion);
118
+ needleLog("needle-meta", needleGreenBold(`✨ Needle Engine version: ${needleEngineVersion} 🌵`), "log", { dimBody: false });
118
119
  tags.push({ tag: 'meta', attrs: { name: 'needle-engine', content: needleEngineVersion } });
119
120
  }
120
- else console.log("WARN: could not find needle engine package.json")
121
+ else needleLog("needle-meta", "Could not find needle engine package.json", "warn", { dimBody: false })
121
122
 
122
123
  tags.push({ tag: 'meta', attrs: { name: 'needle:buildtime', content: new Date().toISOString() } });
123
124
 
@@ -128,6 +129,11 @@ export const needleMeta = (command, config, userSettings) => {
128
129
  }
129
130
 
130
131
 
132
+ /**
133
+ * @param {string} html
134
+ * @param {string} url
135
+ * @returns {string}
136
+ */
131
137
  function updateUrlMetaTag(html, url) {
132
138
  const result = `<meta name="url" content="${url}">`;
133
139
  html = html.replace(`<meta name="url" content="https://needle.tools">`, result);
@@ -135,14 +141,21 @@ function updateUrlMetaTag(html, url) {
135
141
  return html;
136
142
  }
137
143
 
144
+ /** @param {string} str @returns {string} */
138
145
  function appendVersion(str) {
139
146
  return str + "?v=" + Date.now();
140
147
  }
141
148
 
149
+ /** @param {string} url @returns {string} */
142
150
  function removeDuplicateSlashesInUrl(url) {
143
151
  return url.replace(/([^:]\/)\/+/g, "$1");
144
152
  }
145
153
 
154
+ /**
155
+ * @param {string} html
156
+ * @param {string} name
157
+ * @returns {string}
158
+ */
146
159
  function removeMetaTag(html, name) {
147
160
  // TODO: maybe we could also just replace the content
148
161
  const regex = new RegExp(`<meta (name|property)="${name}".+?\/?>`, 'gs');
@@ -154,6 +167,7 @@ function removeMetaTag(html, name) {
154
167
  return newHtml;
155
168
  }
156
169
 
170
+ /** @param {string} html @returns {string} */
157
171
  function insertNeedleCredits(html) {
158
172
  const needleCredits = `<!-- 🌵 Made with Needle — https://needle.tools -->`;
159
173
  html = html.replace(
@@ -1,5 +1,6 @@
1
1
  import { writeFile } from 'fs';
2
2
  import { tryParseNeedleEngineSrcAttributeFromHtml } from '../common/needle-engine.js';
3
+ import { needleLog } from './logging.js';
3
4
 
4
5
 
5
6
 
@@ -54,15 +55,15 @@ export const needleApp = (command, config, userSettings) => {
54
55
  const webComponent = generateNeedleEmbedWebComponent(path, main_asset);
55
56
  await writeFile(`${outputDir}/needle-app.js`, webComponent, (err) => {
56
57
  if (err) {
57
- console.error("[needle-app] could not create needle-app.js", err);
58
+ needleLog("needle-app", "Could not create needle-app.js: " + err.message, "error", { dimBody: false });
58
59
  }
59
60
  else {
60
- console.log("[needle-app] created needle-app.js");
61
+ needleLog("needle-app", "Created needle-app.js", "log", { dimBody: false });
61
62
  }
62
63
  });
63
64
  }
64
65
  catch (e) {
65
- console.warn("WARN: could not create needle-app.js\n", e);
66
+ needleLog("needle-app", "Could not create needle-app.js: " + e.message, "warn", { dimBody: false });
66
67
  }
67
68
  }
68
69
  }
@@ -1,4 +1,5 @@
1
1
  const peerjsString = `/* needle: fix for peerjs */ window.global = window; var parcelRequire;`
2
+ import { needleLog } from './logging.js';
2
3
 
3
4
  /**
4
5
  * @param {import('../types').userSettings} userSettings
@@ -80,7 +81,7 @@ function patchWebRTCAdapterForGemini(code, id) {
80
81
  if(!didTransform) return undefined;
81
82
 
82
83
  if(!didLog) {
83
- console.log("[needle:peerjs] Fixed WebRTC assignment");
84
+ needleLog("needle:peerjs", "Fixed WebRTC assignment", "log", { dimBody: false });
84
85
  didLog = true;
85
86
  }
86
87
 
@@ -7,6 +7,7 @@ import { getPosterPath } from './poster.js';
7
7
  const pwaErrorWithInstructions = "It seems that you're trying to build a PWA using 'vite-plugin-pwa'!\nNeedle can manage PWA settings for you – just pass the same 'pwaOptions' to the needlePlugins and VitePWA plugins:\n\n1. Install the vite PWA plugin: npm install vite-plugin-pwa --save-dev\n\n2. Then update your vite.config.js:\n\n import { VitePWA } from 'vite-plugin-pwa';\n ...\n needlePlugins(command, needleConfig, { pwa: pwaOptions }),\n VitePWA(pwaOptions),\n\nIf you want to manage PWA building yourself and skip this check, please pass '{ pwa: false }' to needlePlugins.";
8
8
 
9
9
  /** Provides reasonable defaults for a PWA manifest and workbox settings.
10
+ * @param {string} command
10
11
  * @param {import('../types').userSettings} userSettings
11
12
  * @param {import("../types/needleConfig").needleMeta | null} config
12
13
  * @returns {import('vite').Plugin | void}
@@ -71,7 +72,7 @@ export const needlePWA = (command, config, userSettings) => {
71
72
  const customManifest = manifests.length > 0 ? manifests[0] : {};
72
73
 
73
74
  // ensure we have proper icons/name/description to match user settings
74
- processPWA(customManifest, context, pwaOptions, config, userSettings).catch(e => log("Error post processing PWA", customManifest, e));
75
+ processPWA(customManifest, context, pwaOptions, config, userSettings).catch((/** @type {unknown} */ e) => log("Error post processing PWA", customManifest, e));
75
76
  // ensures we have a valid workbox config
76
77
  processWorkboxConfig(pwaOptions);
77
78
 
@@ -81,14 +82,15 @@ export const needlePWA = (command, config, userSettings) => {
81
82
  // for debugging
82
83
  // log("PWA options", pwaOptions);
83
84
 
84
- return {
85
+ return /** @type {import('vite').Plugin} */ ({
85
86
  name: 'needle:pwa',
86
87
  apply: 'build',
87
88
  enforce: "post",
88
- config(viteConfig) {
89
+ config(/** @type {{ plugins?: Array<Record<string,unknown>> }} */ viteConfig) {
89
90
  // Move the gzip plugin after PWA bundling
90
91
  let gzipPluginIndex = -1;
91
92
  let pwaPluginIndex = -1;
93
+ /** @type {Record<string, unknown> | null} */
92
94
  let gzipPlugin = null;
93
95
  if (viteConfig.plugins) {
94
96
  for (let i = viteConfig.plugins.length - 1; i >= 0; i--) {
@@ -168,7 +170,7 @@ export const needlePWA = (command, config, userSettings) => {
168
170
  }
169
171
  }
170
172
  }
171
- catch (err) {
173
+ catch (/** @type {unknown} */ err) {
172
174
  cleanup(context);
173
175
  throw err;
174
176
  }
@@ -251,7 +253,7 @@ updateSW = registerSW({
251
253
  try {
252
254
  copyIcons(pwaOptions.manifest, outputDir);
253
255
  }
254
- catch (e) {
256
+ catch (/** @type {unknown} */ e) {
255
257
  log("Error post processing PWA", e);
256
258
  }
257
259
 
@@ -261,20 +263,23 @@ updateSW = registerSW({
261
263
  console.log(msg);
262
264
  }
263
265
  }
264
- }
266
+ })
265
267
  }
266
268
 
267
269
  /** Checks if the vite-plugin-pwa is present in the vite config
268
- * @param {import('vite').ResolvedConfig} config
270
+ * @param {{ plugins?: readonly unknown[] }} config
269
271
  * @returns {import('vite-plugin-pwa').VitePWAOptions | null}
270
272
  */
271
273
  function findVitePWAPlugin(config) {
272
- const plugins = config.plugins || [];
274
+ const plugins = /** @type {Array<Record<string,unknown>>} */ (config.plugins || []);
275
+ /** @param {unknown} p
276
+ * @returns {Record<string, unknown> | undefined}
277
+ */
273
278
  function _findVitePWAPlugin(p) {
274
279
  if (Array.isArray(p))
275
- return p.find(_findVitePWAPlugin);
276
- if (p?.name === "vite-plugin-pwa")
277
- return p;
280
+ return /** @type {Array<unknown>} */ (p).find(_findVitePWAPlugin);
281
+ if (/** @type {Record<string,unknown>} */(p)?.name === "vite-plugin-pwa")
282
+ return /** @type {Record<string, unknown>} */ (p);
278
283
  }
279
284
  for (const plugin of plugins) {
280
285
  const foundVitePWAPlugin = _findVitePWAPlugin(plugin);
@@ -283,6 +288,7 @@ function findVitePWAPlugin(config) {
283
288
  return null;
284
289
  }
285
290
 
291
+ /** @param {import('../types/index.d.ts').NeedlePWAProcessContext} context */
286
292
  function cleanup(context) {
287
293
  for (const file of context.generatedFiles) {
288
294
  log("Cleanup generated file", file);
@@ -291,6 +297,7 @@ function cleanup(context) {
291
297
  context.generatedFiles.length = 0;
292
298
  }
293
299
 
300
+ /** @param {...unknown} args */
294
301
  function log(...args) {
295
302
  console.log("[needle-pwa]", ...args);
296
303
  }
@@ -337,10 +344,16 @@ async function processPWA(webmanifestPath, context, pwaOptions, config, userSett
337
344
  // pwaOptions.includeAssets = [...pwaOptions.includeAssets, ...manifest.icons?.map(i => i.src)];
338
345
 
339
346
  const packageJsonPath = process.cwd() + "/package.json";
340
- const packageJson = existsSync(packageJsonPath) ? JSON.parse(readFileSync(packageJsonPath, 'utf8')) : {};
347
+ const packageJson = /** @type {{ name?: string, description?: string }} */ (existsSync(packageJsonPath) ? JSON.parse(readFileSync(packageJsonPath, 'utf8')) : {});
341
348
 
342
- const name = packageJson.name;
343
349
  const appName = config?.sceneName || packageJson.name || "Needle App";
350
+ const packageName = typeof packageJson.name === "string" && packageJson.name.length > 0
351
+ ? packageJson.name
352
+ : appName;
353
+ const packageNameSanitized = packageName
354
+ .toLowerCase()
355
+ .replace(/[^a-z0-9\/\-]/g, '')
356
+ .replace("\/", ".");
344
357
  const description = typeof config?.meta === "string"
345
358
  ? config.meta : config?.meta?.description
346
359
  || packageJson.description
@@ -351,9 +364,11 @@ async function processPWA(webmanifestPath, context, pwaOptions, config, userSett
351
364
  // Use the same title as in NeedleMeta
352
365
  name: appName,
353
366
  short_name: appName,
354
- id: "app.made-with-needle." + name.toLowerCase().replace(/[^a-z0-9\/\-]/g, '').replace("\/", "."),
367
+ id: "app.made-with-needle." + packageNameSanitized,
355
368
  description,
356
369
  start_url: "./index.html",
370
+ theme_color: "#000000",
371
+ background_color: "#000000",
357
372
  display: "standalone",
358
373
  display_override: [
359
374
  "window-controls-overlay",
@@ -423,7 +438,7 @@ function processIcons(manifest, outDir, context) {
423
438
  modified = true;
424
439
  }
425
440
  }
426
- catch (e) {
441
+ catch (/** @type {unknown} */ e) {
427
442
  log("Error processing PWA icon[" + i + "]", e);
428
443
  }
429
444
  }
@@ -460,6 +475,7 @@ function generateIcons(manifest, context) {
460
475
  /** Tries to copy the icons to the output directory
461
476
  * TODO this should not be needed if we use pwaOptions.includeAssets
462
477
  * @param {Partial<import("vite-plugin-pwa").ManifestOptions>} manifest
478
+ * @param {string} outDir
463
479
  */
464
480
  function copyIcons(manifest, outDir) {
465
481
  for (let i = 0; i < manifest.icons?.length; i++) {
@@ -492,7 +508,7 @@ function copyIcons(manifest, outDir) {
492
508
  copyFileSync(srcPath, targetPath);
493
509
  }
494
510
  }
495
- catch (e) {
511
+ catch (/** @type {unknown} */ e) {
496
512
  log("Error processing PWA icon[" + i + "]", e);
497
513
  }
498
514
  }
@@ -582,7 +598,7 @@ function processWorkboxConfig(manifest) {
582
598
  },
583
599
  // allow caching local resources
584
600
  {
585
- urlPattern: ({ url }) => url,
601
+ urlPattern: (/** @type {{ url: URL }} */ { url }) => url,
586
602
  // Apply a network-first strategy.
587
603
  handler: 'NetworkFirst',
588
604
  options: {
@@ -1,17 +1,22 @@
1
+ // @ts-check
1
2
  import path from 'path';
2
3
  import { loadConfig, tryLoadProjectConfig } from './config.js';
3
4
  import { getPosterPath } from './poster.js';
4
5
  import * as crypto from 'crypto';
5
6
  import { existsSync, readFileSync, statSync } from 'fs';
6
7
  import { fileURLToPath } from 'url';
8
+ import { needleLog } from './logging.js';
7
9
 
8
10
  const __filename = fileURLToPath(import.meta.url);
9
11
  const __dirname = path.dirname(__filename);
10
12
 
13
+ /** @type {Set<string>} */
11
14
  const filesUsingHotReload = new Set();
12
15
  let assetsDirectory = "";
13
16
 
14
17
  /**
18
+ * @param {string} command
19
+ * @param {import('../types').userSettings | null} config
15
20
  * @param {import('../types').userSettings} userSettings
16
21
  */
17
22
  export const needleReload = (command, config, userSettings) => {
@@ -34,7 +39,8 @@ export const needleReload = (command, config, userSettings) => {
34
39
 
35
40
  const buildDirectory = projectConfig?.buildDirectory?.length ? process.cwd().replaceAll("\\", "/") + "/" + projectConfig?.buildDirectory : "";
36
41
  if (buildDirectory?.length) {
37
- setTimeout(() => console.log("Build directory: ", buildDirectory), 100);
42
+ const relativeBuildDirectory = path.relative(process.cwd(), buildDirectory).replaceAll("\\", "/") || ".";
43
+ setTimeout(() => needleLog("needle-reload", "Build directory: " + relativeBuildDirectory), 100);
38
44
  }
39
45
 
40
46
  // These ignore patterns will be injected into user config to better control vite reloading
@@ -44,6 +50,7 @@ export const needleReload = (command, config, userSettings) => {
44
50
 
45
51
  return {
46
52
  name: 'needle:reload',
53
+ /** @param {import('vite').UserConfig} config */
47
54
  config(config) {
48
55
  if (!config.server) config.server = { watch: { ignored: [] } };
49
56
  else if (!config.server.watch) config.server.watch = { ignored: [] };
@@ -51,12 +58,14 @@ export const needleReload = (command, config, userSettings) => {
51
58
  for (const pattern of ignorePatterns)
52
59
  config.server.watch.ignored.push(pattern);
53
60
  if (config?.debug === true || userSettings?.debug === true)
54
- setTimeout(() => console.log("Updated server ignore patterns: ", config.server.watch.ignored), 100);
61
+ setTimeout(() => needleLog("needle-reload", "Updated server ignore patterns: " + JSON.stringify(config.server.watch.ignored)), 100);
55
62
  },
63
+ /** @param {{file: string, server: import('vite').ViteDevServer, modules: unknown[], read: (file?: string) => Promise<string>, buildDirectory?: string}} args */
56
64
  handleHotUpdate(args) {
57
65
  args.buildDirectory = buildDirectory;
58
66
  return handleReload(args);
59
67
  },
68
+ /** @param {string} src @param {string} id */
60
69
  transform(src, id) {
61
70
  if (!id.includes(".ts")) return;
62
71
  updateConfig();
@@ -67,6 +76,7 @@ export const needleReload = (command, config, userSettings) => {
67
76
  },
68
77
  transformIndexHtml: {
69
78
  order: 'pre',
79
+ /** @param {string} html @param {unknown} _ */
70
80
  handler(html, _) {
71
81
  if (config?.allowHotReload === false) return html;
72
82
  if (userSettings?.allowHotReload === false) return html;
@@ -91,6 +101,7 @@ export const needleReload = (command, config, userSettings) => {
91
101
  }
92
102
 
93
103
 
104
+ /** @type {string[]} */
94
105
  const ignorePatterns = [];
95
106
  const ignoreRegex = new RegExp(ignorePatterns.join("|"));
96
107
 
@@ -99,11 +110,15 @@ const posterPath = getPosterPath();
99
110
  let reloadIsScheduled = false;
100
111
  const lockFileName = "needle.lock";
101
112
 
113
+ /** @param {import('vite').ViteDevServer} server @param {string} [file] */
102
114
  function notifyClientWillReload(server, file) {
103
115
  console.log("Send reload notification");
104
116
  server.ws.send('needle:reload', { type: 'will-reload', file: file });
105
117
  }
106
118
 
119
+ /**
120
+ * @param {{file: string, server: import('vite').ViteDevServer, modules: unknown[], read: (file?: string) => Promise<string>, buildDirectory?: string}} param0
121
+ */
107
122
  async function handleReload({ file, server, modules, read, buildDirectory }) {
108
123
 
109
124
  // dont reload the full page on css changes
@@ -191,6 +206,7 @@ async function handleReload({ file, server, modules, read, buildDirectory }) {
191
206
  }
192
207
 
193
208
 
209
+ /** @param {import('vite').ViteDevServer} server @param {number} [level] */
194
210
  async function scheduleReload(server, level = 0) {
195
211
  if (reloadIsScheduled && level === 0) return;
196
212
  reloadIsScheduled = true;
@@ -224,6 +240,7 @@ async function scheduleReload(server, level = 0) {
224
240
 
225
241
  const projectDirectory = process.cwd().replaceAll("\\", "/");
226
242
 
243
+ /** @param {string} file @returns {string} */
227
244
  function getFileNameLog(file) {
228
245
  if (file.startsWith(projectDirectory)) {
229
246
  return file.substring(projectDirectory.length);
@@ -231,9 +248,11 @@ function getFileNameLog(file) {
231
248
  return file;
232
249
  }
233
250
 
251
+ /** @type {Map<string, string>} */
234
252
  const hashes = new Map();
235
253
  const hash256 = crypto.createHash('sha256');
236
254
 
255
+ /** @param {string} file @param {(file?: string) => Promise<string>} read @returns {Promise<boolean>} */
237
256
  async function testIfFileContentChanged(file, read) {
238
257
  let content = await read(file);
239
258
  content = removeVersionQueryArgument(content);
@@ -252,6 +271,7 @@ async function testIfFileContentChanged(file, read) {
252
271
  }
253
272
  return false;
254
273
  }
274
+ /** @param {string} content @returns {string} */
255
275
  function removeVersionQueryArgument(content) {
256
276
  if (typeof content === "string") {
257
277
  // Some codegen files include hashes for loading glb files (e.g. ?v=213213124)
@@ -266,6 +286,7 @@ function removeVersionQueryArgument(content) {
266
286
 
267
287
 
268
288
 
289
+ /** @param {string} src @param {string} filePath @returns {string} */
269
290
  function insertScriptRegisterHotReloadCode(src, filePath) {
270
291
 
271
292
  // We only want to inject the hot reload code in the needle-engine root file
@@ -284,6 +305,7 @@ globalThis.NEEDLE_HOT_RELOAD_ENABLED = true;
284
305
  const HOT_RELOAD_START_MARKER = "NEEDLE_HOT_RELOAD_BEGIN";
285
306
  const HOT_RELOAD_END_MARKER = "NEEDLE_HOT_RELOAD_END";
286
307
 
308
+ /** @param {string} src @param {string} filePath @returns {{code: string, map: null} | undefined} */
287
309
  function insertScriptHotReloadCode(src, filePath) {
288
310
  if (filePath.includes("engine_hot_reload")) return;
289
311
  if (filePath.includes(".vite")) return;
package/src/engine/api.ts CHANGED
@@ -354,9 +354,6 @@ export { type ISerializable } from "./engine_serialization_core.js";
354
354
  // General-purpose utility functions and helpers.
355
355
  // ============================================================================
356
356
 
357
- /** Testing utilities for automated tests */
358
- export * from "./engine_test_utils.js";
359
-
360
357
  /** Texture utilities and processing */
361
358
  export * from "./engine_texture.js";
362
359
 
@@ -8,7 +8,7 @@ export {
8
8
  clearMessages as clearOverlayMessages,
9
9
  LogType,
10
10
  setAllowBalloonMessages,
11
- // eslint-disable-next-line deprecation/deprecation
11
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
12
12
  setAllowOverlayMessages,
13
13
  };
14
14
  export { enableSpatialConsole } from "./debug_spatial_console.js";
@@ -169,7 +169,11 @@ class SpatialMessagesHandler {
169
169
 
170
170
  if (!fontFamily) {
171
171
  fontFamily = ThreeMeshUI.FontLibrary.addFontFamily(this.familyName);
172
- const variant = fontFamily.addVariant("normal", "normal", "./include/needle/arial-msdf.json", "./include/needle/arial.png") as any as ThreeMeshUI.FontVariant;
172
+ const variant = fontFamily.addVariant(
173
+ "normal",
174
+ "normal",
175
+ "https://cdn.needle.tools/static/fonts/msdf/arial/arial-msdf.json",
176
+ "https://cdn.needle.tools/static/fonts/msdf/arial/arial.png") as any as ThreeMeshUI.FontVariant;
173
177
  /** @ts-ignore */
174
178
  variant?.addEventListener('ready', () => {
175
179
  ThreeMeshUI.update();