@needle-tools/engine 4.11.0-next.358bed1 → 4.11.0-next.91b9cf1

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.
@@ -8,26 +8,23 @@ export const preloadScriptPaths = [];
8
8
 
9
9
  /**
10
10
  * @param {import('../types').userSettings} userSettings
11
- * @returns {import('vite').Plugin[]}
12
11
  */
13
12
  export const needleDependencies = (command, config, userSettings) => {
14
13
 
15
14
  /**
16
15
  * @type {import('vite').Plugin}
17
16
  */
18
- return [
19
- {
20
- name: 'needle:dependencies',
21
- enforce: 'pre',
22
- /**
23
- * @param {import('vite').UserConfig} config
24
- */
25
- config: (config, env) => {
26
- handleOptimizeDeps(config);
27
- handleManualChunks(config);
28
- },
29
- },
30
- ]
17
+ return {
18
+ name: 'needle:dependencies',
19
+ enforce: 'pre',
20
+ /**
21
+ * @param {import('vite').UserConfig} config
22
+ */
23
+ config: (config, env) => {
24
+ handleOptimizeDeps(config);
25
+ handleManualChunks(config);
26
+ }
27
+ }
31
28
  }
32
29
 
33
30
  const excludeDependencies = [
@@ -170,13 +167,6 @@ function handleManualChunks(config) {
170
167
  return name;
171
168
  }
172
169
  }
173
- // else if(chunk.name === 'index') {
174
- // console.log(chunk);
175
- // debugger
176
- // // this is the main chunk
177
- // // we don't want to add a hash here to be able to easily import the main script
178
- // return `index.js`;
179
- // }
180
170
  return `assets/[name].[hash].js`;
181
171
  }
182
172
 
@@ -68,8 +68,6 @@ import { needleNPM } from "./npm.js";
68
68
  import { needleTransformCode } from "./transform.js";
69
69
  import { needleMaterialXLoader } from "./materialx.js";
70
70
  import { needleLogger } from "./logger.js";
71
- import { needleApp } from "./needle-app.js";
72
- import { viteFixWorkerImport } from "../common/worker.js";
73
71
  export { needleServer } from "./server.js";
74
72
 
75
73
 
@@ -139,8 +137,6 @@ export const needlePlugins = async (command, config = undefined, userSettings =
139
137
  needleServer(command, config, userSettings),
140
138
  needleNPM(command, config, userSettings),
141
139
  needleMaterialXLoader(command, config, userSettings),
142
- needleApp(command, config, userSettings),
143
- viteFixWorkerImport()
144
140
  ];
145
141
 
146
142
  const asap = await needleAsap(command, config, userSettings);
@@ -572,9 +572,6 @@ declare module 'three' {
572
572
 
573
573
 
574
574
  namespace NEMeshBVH {
575
-
576
- let failedToCreateMeshBVHWorker = 0;
577
-
578
575
  export function runMeshBVHRaycast(method: Raycaster | Sphere, mesh: Mesh, results: Intersection[], context: Pick<Context, "xr">, options: { allowSlowRaycastFallback?: boolean }): boolean {
579
576
  if (!mesh.geometry) {
580
577
  return false;
@@ -633,10 +630,6 @@ namespace NEMeshBVH {
633
630
  || geom.index && geom.index?.["isInterleavedBufferAttribute"]) {
634
631
  canUseWorker = false;
635
632
  }
636
- else if (failedToCreateMeshBVHWorker > 10) {
637
- // if we failed to create a worker multiple times, don't try again
638
- canUseWorker = false;
639
- }
640
633
 
641
634
  // if we have a worker use that
642
635
  if (canUseWorker && _GenerateMeshBVHWorker) {
@@ -653,23 +646,8 @@ namespace NEMeshBVH {
653
646
  }
654
647
  // if there are no workers available, create a new one
655
648
  if (!workerInstance && workerInstances.length < 3) {
656
- try {
657
- workerInstance = new _GenerateMeshBVHWorker();
658
- workerInstances.push(workerInstance);
659
- }
660
- catch (err) {
661
- const isSecurityError = err instanceof DOMException && err.name === "SecurityError";
662
- if (isSecurityError) {
663
- console.warn("Failed to create MeshBVH worker, falling back to main thread generation. This can happen when running from file://, if the browser does not support workers or if the browser is blocking workers for other reasons.");
664
- console.debug(err);
665
- failedToCreateMeshBVHWorker += 10;
666
- }
667
- else {
668
- console.error("Failed to create MeshBVH worker");
669
- console.debug(err);
670
- }
671
- failedToCreateMeshBVHWorker++;
672
- }
649
+ workerInstance = new _GenerateMeshBVHWorker();
650
+ workerInstances.push(workerInstance);
673
651
  }
674
652
 
675
653
  if (workerInstance != null && !workerInstance.running) {
@@ -828,9 +806,6 @@ namespace NEMeshBVH {
828
806
  if (debugPhysics || isDevEnvironment()) {
829
807
  console.warn("Failed to setup mesh bvh worker");
830
808
  }
831
- else {
832
- console.debug("Failed to setup mesh bvh worker", _err);
833
- }
834
809
  })
835
810
  .finally(() => {
836
811
  isRequestingWorker = false;
@@ -4,16 +4,14 @@ import { MeshBVH } from 'three-mesh-bvh';
4
4
  // Modified according to https://github.com/gkjohnson/three-mesh-bvh/issues/636#issuecomment-2209571751
5
5
  import { WorkerBase } from "three-mesh-bvh/src/workers/utils/WorkerBase.js";
6
6
 
7
-
8
7
  export class GenerateMeshBVHWorker extends WorkerBase {
9
8
 
10
9
  constructor() {
11
10
  // TODO: make mesh bvh worker "work" for prebundled CDN loading
12
11
  // https://linear.app/needle/issue/NE-6572
13
12
  // Also we don't use toplevel imports to not completely fail to load needle-engine where loading the worker fails
14
- // const meta_url = import.meta.url;
13
+ // const url = new URL('three-mesh-bvh/src/workers/generateMeshBVH.worker.js', import.meta.url)
15
14
  // const getWorker = () => new Worker(url, { type: 'module' })
16
- // console.log(meta_url, url, getWorker());
17
15
  super(new Worker(new URL('three-mesh-bvh/src/workers/generateMeshBVH.worker.js', import.meta.url), { type: 'module' }));
18
16
  this.name = 'GenerateMeshBVHWorker';
19
17
 
@@ -1,41 +0,0 @@
1
-
2
-
3
- // https://regex101.com/r/SVhzzD/1
4
- // @ts-ignore
5
- const needleEngineRegex = /<needle-engine.*?src=["'](?<src>[\w\d]+?)["']>/gm;
6
-
7
-
8
- /**
9
- * @param {string} html
10
- * @returns {string[]} urls
11
- */
12
- export function tryParseNeedleEngineSrcAttributeFromHtml(html) {
13
- const needleEngineMatches = html.matchAll(needleEngineRegex);
14
-
15
- /**
16
- * @type {string[]}
17
- */
18
- const results = [];
19
-
20
- if (needleEngineMatches) {
21
- while (true) {
22
- const match = needleEngineMatches.next();
23
- if (match.done) break;
24
- /** @type {undefined | null | string} */
25
- const value = match.value?.groups?.src;
26
- if (value) {
27
- if (value.startsWith("[")) {
28
- // we have an array assigned
29
- const arr = JSON.parse(value);
30
- for (const item of arr) {
31
- results.push(item);
32
- }
33
- }
34
- else {
35
- results.push(value);
36
- }
37
- }
38
- }
39
- }
40
- return results;
41
- }
@@ -1,130 +0,0 @@
1
-
2
-
3
-
4
-
5
- /**
6
- * @type {() => import("vite").Plugin}
7
- */
8
- export function viteFixWorkerImport() {
9
- return {
10
- name: 'vite-rewriteWorkerImport',
11
- config: function (config, env) {
12
- if (!config.build) {
13
- config.build = {};
14
- }
15
- if (!config.build.rollupOptions) {
16
- config.build.rollupOptions = {};
17
- }
18
- if (!config.build.rollupOptions.plugins) {
19
- config.build.rollupOptions.plugins = [];
20
- }
21
- if (!Array.isArray(config.build.rollupOptions.plugins)) {
22
- const value = config.build.rollupOptions.plugins;
23
- config.build.rollupOptions.plugins = [];
24
- config.build.rollupOptions.plugins.push(value);
25
- }
26
- config.build.rollupOptions.plugins.push(rollupFixWorkerImport({ logFail: false }));
27
- }
28
- }
29
- }
30
-
31
-
32
-
33
- // https://regex101.com/r/hr01H4/1
34
- const regex = /new\s+Worker\s*\(\s*new\s+URL\s*\(\s*(?:\/\*.*?\*\/\s*)?\"(?<url>[^"]+)\"\s*,\s*(?<base>import\.meta\.url|self\.location)[^)]*\)/gm;
35
-
36
-
37
-
38
- /**
39
- * @type {(opts?: {logFail:boolean}) => import("vite").Plugin}
40
- */
41
- export function rollupFixWorkerImport(opts = { logFail: true }) {
42
- return {
43
- name: 'rewriteWorkerImport',
44
- renderChunk: {
45
- order: 'post',
46
- async handler(code, chunk, outputOptions) {
47
- let regexMatchedWorkerCode = false;
48
- const newWorkerStartIndex = code.indexOf("new Worker");
49
- if (newWorkerStartIndex >= 0) {
50
- const res = code.replace(regex, (match, url, _base) => {
51
- regexMatchedWorkerCode = true;
52
- // console.log("WORKER?", url)
53
- if (url?.startsWith("/")) {
54
- console.log(`[rollup] Rewrite worker import in ${chunk.fileName}`);
55
- // Make url file-relative
56
- const newUrl = url.replace(/^\//, "");
57
- // For CORS issues we need to use importScripts: https://linear.app/needle/issue/NE-6572#comment-ea5dc65e
58
- const output = `/* new-worker */ new Worker(URL.createObjectURL(new Blob(["import '" + \`\${new URL('./${newUrl}', import.meta.url).toString()}\` + "';"], { type: 'text/javascript' }))`;
59
- console.log("[rollup] Did rewrite worker output to:", output);
60
- return output;
61
- // return `new Worker(new URL("./${newUrl}", import.meta.url)`;
62
- }
63
- return match;
64
- });
65
- if (!regexMatchedWorkerCode) {
66
-
67
- const fixedCode = fixWorkerSelfLocation(chunk.fileName, code);
68
- if (fixedCode !== code) {
69
- return fixedCode;
70
- }
71
- if (opts?.logFail !== false) {
72
- const str = `[...]${code.substring(newWorkerStartIndex, newWorkerStartIndex + 200)}[...]`
73
- console.warn(`\n[rollup] Worker import in ${chunk.fileName} was not rewritten: ${str}`);
74
- }
75
- else console.log(`[rollup] Worker import in ${chunk.fileName}: ${str}`);
76
- }
77
- return res;
78
- }
79
- },
80
- }
81
- };
82
- }
83
-
84
-
85
- /**
86
- * Fix worker self.location to import.meta.url
87
- * @param {string} filename
88
- * @param {string} code
89
- */
90
- function fixWorkerSelfLocation(filename, code) {
91
- let lastIndex = 0;
92
- while (true) {
93
- const startIndex = code.indexOf("new Worker", lastIndex);
94
- if (startIndex < 0) break;
95
- let index = startIndex + 1;
96
- let endIndex = -1;
97
- let openingBraceCount = 0;
98
- let foundAnyOpening = false;
99
- while (true) {
100
- const char = code[index];
101
- if (char === "(") {
102
- openingBraceCount++;
103
- foundAnyOpening = true;
104
- }
105
- if (char === ")") openingBraceCount--;
106
- if (openingBraceCount === 0 && foundAnyOpening) {
107
- endIndex = index;
108
- break;
109
- }
110
- // console.log(openingBraceCount, char, index, code.length);
111
- index++;
112
- if (index >= code.length) break;
113
- }
114
- if (endIndex > startIndex) {
115
- const workerCode = code.substring(startIndex, endIndex + 1);
116
- if (workerCode.indexOf("self.location") >= 0) {
117
- const fixedCode = workerCode.replace("self.location", "import.meta.url");
118
- code = code.substring(0, startIndex) + fixedCode + code.substring(endIndex + 1);
119
- lastIndex = startIndex + fixedCode.length;
120
- console.log(`[rollup] Rewrite worker 'self.location' to 'import.meta.url' in ${filename}`);
121
- } else {
122
- lastIndex = endIndex;
123
- }
124
- }
125
- else {
126
- lastIndex = startIndex + "new Worker".length;
127
- }
128
- }
129
- return code;
130
- }
@@ -1,148 +0,0 @@
1
- import { writeFile } from 'fs';
2
- import { tryParseNeedleEngineSrcAttributeFromHtml } from '../common/needle-engine.js';
3
-
4
-
5
-
6
- /**
7
- * @param {'serve' | 'build'} command
8
- * @param {{} | undefined | null} config
9
- * @param {import('../types').userSettings} userSettings
10
- * @returns {import('vite').Plugin[] | null}
11
- */
12
- export const needleApp = (command, config, userSettings) => {
13
-
14
- if (command !== "build") {
15
- return null;
16
- }
17
-
18
- /** @type {Array<import("rollup").OutputChunk>} */
19
- const entryFiles = new Array();
20
-
21
- let outputDir = "dist";
22
-
23
- /**
24
- * @type {import('vite').Plugin}
25
- */
26
- return [
27
- {
28
- name: 'needle:app',
29
- enforce: "post",
30
- configResolved(config) {
31
- outputDir = config.build.outDir || "dist";
32
- },
33
- transformIndexHtml: {
34
- handler: async function (html, context) {
35
- const name = context.filename;
36
- if (name.includes("index.html")) {
37
- if (context.chunk?.isEntry) {
38
- try {
39
- entryFiles.push(context.chunk);
40
- const path = context.chunk.fileName;
41
- // console.log("[needle-dependencies] entry chunk imports", {
42
- // name: context.chunk.fileName,
43
- // imports: context.chunk.imports,
44
- // dynamicImports: context.chunk.dynamicImports,
45
- // refs: context.chunk.referencedFiles,
46
- // });
47
-
48
- const referencedGlbs = tryParseNeedleEngineSrcAttributeFromHtml(html);
49
- const webComponent = generateNeedleEmbedWebComponent(path, referencedGlbs);
50
- await writeFile(`${outputDir}/needle-app.js`, webComponent, (err) => {
51
- if (err) {
52
- console.error("[needle-app] could not create needle-app.js", err);
53
- }
54
- else {
55
- console.log("[needle-app] created needle-app.js");
56
- }
57
- });
58
- }
59
- catch (e) {
60
- console.warn("WARN: could not create needle-app.js\n", e);
61
- }
62
- }
63
- }
64
- }
65
- },
66
-
67
- }
68
- ]
69
- }
70
-
71
-
72
- /**
73
- * @param {string} filepath
74
- * @param {string[]} needleEngineSrcPaths
75
- * @returns {string}
76
- */
77
- function generateNeedleEmbedWebComponent(filepath, needleEngineSrcPaths) {
78
-
79
-
80
- // filepath is e.g. `assets/index-XXXXXXXX.js`
81
- // we want to make sure the path is correct relative to where the component will be used
82
- // this script will be emitted in the output directory root (e.g. needle-embed.js)
83
-
84
- const src = needleEngineSrcPaths?.length ? `${needleEngineSrcPaths[0]}` : "";
85
-
86
- const componentName = 'needle-app';
87
- const className = 'NeedleApp';
88
-
89
- return `
90
- class ${className} extends HTMLElement {
91
- constructor() {
92
- super();
93
- this.attachShadow({ mode: 'open' });
94
- const template = document.createElement('template');
95
- template.innerHTML = \`
96
- <style>
97
- :host {
98
- position: relative;
99
- display: block;
100
- width: 100%;
101
- height: 100%;
102
- margin: 0;
103
- padding: 0;
104
- }
105
- needle-engine {
106
- position: absolute;
107
- top: 0;
108
- left: 0;
109
- width: 100%;
110
- height: 100%;
111
- }
112
- </style>
113
- \`;
114
- this.shadowRoot.appendChild(template.content.cloneNode(true));
115
-
116
- const script = document.createElement('script');
117
- script.type = 'module';
118
- const url = new URL('.', import.meta.url);
119
- let basePath = this.getAttribute('base-path') || \`\${url.protocol}//\${url.host}\${url.pathname}\`;
120
- while(basePath.endsWith('/')) {
121
- basePath = basePath.slice(0, -1);
122
- }
123
- script.src = this.getAttribute('script-src') || \`\${basePath}/${filepath}\`;
124
- this.shadowRoot.appendChild(script);
125
-
126
- const needleEngine = document.createElement('needle-engine');
127
- needleEngine.src = this.getAttribute('src') || ${src?.length ? `\${basePath}/${needleEngineSrcPaths}` : undefined};
128
- this.shadowRoot.appendChild(needleEngine);
129
-
130
- console.debug(basePath, script.src, needleEngine.getAttribute("src"));
131
- }
132
-
133
- onConnectedCallback() {
134
- console.debug('NeedleEmbed connected to the DOM');
135
- }
136
-
137
- disconnectedCallback() {
138
- console.debug('NeedleEmbed disconnected from the DOM');
139
- }
140
- }
141
-
142
-
143
- if (!customElements.get('${componentName}')) {
144
- console.debug("Defining ${componentName}");
145
- customElements.define('${componentName}', ${className});
146
- }
147
- `
148
- }