@needle-tools/engine 4.15.0-next.f391a30 → 4.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components.needle.json +1 -1
- package/dist/{gltf-progressive-CTlvpS3A.js → gltf-progressive-Bm_6aEi4.js} +1 -1
- package/dist/{gltf-progressive-CMwJPwEt.umd.cjs → gltf-progressive-BttGBXw6.umd.cjs} +1 -1
- package/dist/{gltf-progressive-DYL3SLVb.min.js → gltf-progressive-T5WKTux5.min.js} +1 -1
- package/dist/materialx-CJyQZtjt.min.js +90 -0
- package/dist/materialx-DMs1E08Z.js +4636 -0
- package/dist/materialx-DaKKOoVk.umd.cjs +90 -0
- package/dist/{needle-engine.bundle-DsTdfmeb.min.js → needle-engine.bundle-CBq_OMnI.min.js} +122 -124
- package/dist/{needle-engine.bundle-DB4kLWO_.js → needle-engine.bundle-DGyiwNWR.js} +3226 -3232
- package/dist/{needle-engine.bundle-C1BFRZDF.umd.cjs → needle-engine.bundle-JN3eiiYc.umd.cjs} +113 -115
- package/dist/needle-engine.d.ts +52 -33
- package/dist/needle-engine.js +288 -287
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-BN-f4viE.min.js → postprocessing-06AXuvdv.min.js} +1 -1
- package/dist/{postprocessing-De9ZpJrk.js → postprocessing-CI2x8Cln.js} +1 -1
- package/dist/{postprocessing-DYmYOVm4.umd.cjs → postprocessing-CPDcA21P.umd.cjs} +1 -1
- package/dist/{three-examples-BHqRVpO_.umd.cjs → three-examples-BMmNgNCN.umd.cjs} +12 -12
- package/dist/{three-examples-C0ZCCA_K.js → three-examples-CMYCd5nH.js} +192 -182
- package/dist/{three-examples-DmTY8tGr.min.js → three-examples-CQl1fFZp.min.js} +14 -14
- package/lib/engine/api.d.ts +2 -0
- package/lib/engine/api.js +2 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/debug/debug.js +1 -1
- package/lib/engine/debug/debug.js.map +1 -1
- package/lib/engine/debug/debug_spatial_console.js +1 -1
- package/lib/engine/debug/debug_spatial_console.js.map +1 -1
- package/lib/engine/engine_accessibility.d.ts +1 -1
- package/lib/engine/engine_accessibility.js +1 -1
- package/lib/engine/engine_accessibility.js.map +1 -1
- package/lib/engine/engine_context.d.ts +1 -1
- package/lib/engine/engine_context.js +2 -2
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_create_objects.js +1 -1
- package/lib/engine/engine_create_objects.js.map +1 -1
- package/lib/engine/engine_gizmos.js +1 -1
- package/lib/engine/engine_gizmos.js.map +1 -1
- package/lib/engine/engine_license.js +2 -7
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_test_utils.d.ts +39 -0
- package/lib/engine/engine_test_utils.js +84 -0
- package/lib/engine/engine_test_utils.js.map +1 -0
- package/lib/engine/engine_utils.js +2 -2
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/export/gltf/index.js +1 -1
- package/lib/engine/export/gltf/index.js.map +1 -1
- package/lib/engine/webcomponents/logo-element.d.ts +3 -6
- package/lib/engine/webcomponents/logo-element.js +0 -18
- package/lib/engine/webcomponents/logo-element.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +7 -10
- package/lib/engine/webcomponents/needle menu/needle-menu.js +4 -14
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.ar-overlay.js +1 -10
- package/lib/engine/webcomponents/needle-engine.ar-overlay.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +0 -3
- package/lib/engine/webcomponents/needle-engine.js +0 -10
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine-components/Component.js +1 -0
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/ReflectionProbe.d.ts +2 -24
- package/lib/engine-components/ReflectionProbe.js +2 -28
- package/lib/engine-components/ReflectionProbe.js.map +1 -1
- package/lib/engine-components/Skybox.js +2 -4
- package/lib/engine-components/Skybox.js.map +1 -1
- package/lib/engine-components/export/gltf/GltfExport.js +1 -1
- package/lib/engine-components/export/gltf/GltfExport.js.map +1 -1
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +2 -2
- package/lib/engine-components/export/usdz/USDZExporter.js +1 -1
- package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/behavior/PhysicsExtension.js +2 -2
- package/lib/engine-components/export/usdz/extensions/behavior/PhysicsExtension.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
- package/lib/include/three/EXT_mesh_gpu_instancing_exporter.js.map +1 -0
- package/package.json +14 -18
- package/plugins/common/buildinfo.js +10 -46
- package/plugins/common/files.js +1 -2
- package/plugins/common/license.js +69 -144
- package/plugins/common/logger.js +11 -172
- package/plugins/common/worker.js +4 -5
- package/plugins/types/userconfig.d.ts +2 -40
- package/plugins/vite/alias.js +5 -6
- package/plugins/vite/asap.js +5 -6
- package/plugins/vite/build-pipeline.js +41 -224
- package/plugins/vite/buildinfo.js +6 -66
- package/plugins/vite/copyfiles.js +12 -41
- package/plugins/vite/custom-element-data.js +16 -26
- package/plugins/vite/defines.js +5 -8
- package/plugins/vite/dependencies.js +10 -16
- package/plugins/vite/dependency-watcher.js +7 -35
- package/plugins/vite/drop-client.js +5 -7
- package/plugins/vite/drop.js +14 -16
- package/plugins/vite/editor-connection.js +16 -18
- package/plugins/vite/imports-logger.js +2 -12
- package/plugins/vite/index.js +3 -8
- package/plugins/vite/local-files.js +441 -2
- package/plugins/vite/logger.client.js +35 -45
- package/plugins/vite/logger.js +3 -6
- package/plugins/vite/meta.js +4 -18
- package/plugins/vite/needle-app.js +3 -4
- package/plugins/vite/peer.js +1 -2
- package/plugins/vite/pwa.js +17 -33
- package/plugins/vite/reload.js +2 -24
- package/src/engine/api.ts +3 -0
- package/src/engine/debug/debug.ts +1 -1
- package/src/engine/debug/debug_spatial_console.ts +1 -5
- package/src/engine/engine_accessibility.ts +1 -2
- package/src/engine/engine_context.ts +2 -2
- package/src/engine/engine_create_objects.ts +1 -1
- package/src/engine/engine_gizmos.ts +5 -9
- package/src/engine/engine_license.ts +2 -7
- package/src/engine/engine_test_utils.ts +109 -0
- package/src/engine/engine_utils.ts +2 -2
- package/src/engine/export/gltf/index.ts +1 -1
- package/src/engine/webcomponents/logo-element.ts +3 -20
- package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -6
- package/src/engine/webcomponents/needle menu/needle-menu.ts +11 -23
- package/src/engine/webcomponents/needle-engine.ar-overlay.ts +2 -13
- package/src/engine/webcomponents/needle-engine.ts +1 -13
- package/src/engine-components/Component.ts +2 -1
- package/src/engine-components/ReflectionProbe.ts +9 -33
- package/src/engine-components/Skybox.ts +2 -4
- package/src/engine-components/export/gltf/GltfExport.ts +1 -1
- package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +2 -2
- package/src/engine-components/export/usdz/USDZExporter.ts +1 -1
- package/src/engine-components/export/usdz/extensions/behavior/PhysicsExtension.ts +2 -2
- package/src/include/draco/draco_decoder.js +34 -0
- package/src/include/draco/draco_decoder.wasm +0 -0
- package/src/include/draco/draco_wasm_wrapper.js +117 -0
- package/src/include/ktx2/basis_transcoder.js +19 -0
- package/src/include/ktx2/basis_transcoder.wasm +0 -0
- package/src/include/needle/arial-msdf.json +1472 -0
- package/src/include/needle/arial.png +0 -0
- package/src/include/needle/poweredbyneedle.webp +0 -0
- package/dist/materialx-4jJLLe9Q.js +0 -4174
- package/dist/materialx-Bt9FHwco.min.js +0 -158
- package/dist/materialx-NDD0y4JY.umd.cjs +0 -158
- package/lib/engine/export/gltf/EXT_mesh_gpu_instancing_exporter.js.map +0 -1
- package/plugins/common/needle-engine-skill.md +0 -175
- package/plugins/vite/ai.js +0 -71
- package/plugins/vite/local-files-analysis.js +0 -789
- package/plugins/vite/local-files-core.js +0 -992
- package/plugins/vite/local-files-internals.js +0 -28
- package/plugins/vite/local-files-types.d.ts +0 -111
- package/plugins/vite/local-files-utils.js +0 -359
- package/plugins/vite/logging.js +0 -129
- /package/lib/{engine/export/gltf → include/three}/EXT_mesh_gpu_instancing_exporter.d.ts +0 -0
- /package/lib/{engine/export/gltf → include/three}/EXT_mesh_gpu_instancing_exporter.js +0 -0
- /package/src/{engine/export/gltf → include/three}/EXT_mesh_gpu_instancing_exporter.js +0 -0
package/plugins/vite/index.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { needleAI } from "./ai.js";
|
|
2
|
-
export { needleAI } from "./ai.js";
|
|
3
|
-
|
|
4
1
|
import { needleAsap } from "./asap.js";
|
|
5
2
|
export { needleAsap } from "./asap.js";
|
|
6
3
|
|
|
@@ -46,8 +43,8 @@ export { needleTransformCodegen } from "./transform-codegen.js";
|
|
|
46
43
|
import { needleLicense } from "./license.js";
|
|
47
44
|
export { needleLicense } from "./license.js";
|
|
48
45
|
|
|
49
|
-
import { needleMakeFilesLocal
|
|
50
|
-
export { needleMakeFilesLocal
|
|
46
|
+
import { needleMakeFilesLocal } from "./local-files.js";
|
|
47
|
+
export { needleMakeFilesLocal } from "./local-files.js";
|
|
51
48
|
|
|
52
49
|
import { needlePeerjs } from "./peer.js";
|
|
53
50
|
export { needlePeerjs } from "./peer.js";
|
|
@@ -122,7 +119,6 @@ export const needlePlugins = async (command, config = undefined, userSettings =
|
|
|
122
119
|
userSettings = { ...defaultUserSettings, ...userSettings };
|
|
123
120
|
|
|
124
121
|
const array = [
|
|
125
|
-
needleAI(command, config, userSettings),
|
|
126
122
|
needleLogger(command, config, userSettings),
|
|
127
123
|
needleDefines(command, config, userSettings),
|
|
128
124
|
needleLicense(command, config, userSettings),
|
|
@@ -131,7 +127,6 @@ export const needlePlugins = async (command, config = undefined, userSettings =
|
|
|
131
127
|
needleMeta(command, config, userSettings),
|
|
132
128
|
needlePoster(command, config, userSettings),
|
|
133
129
|
needleReload(command, config, userSettings),
|
|
134
|
-
needleLocalFilesSceneAnalysis(command, config, userSettings),
|
|
135
130
|
needleMakeFilesLocal(command, config, userSettings),
|
|
136
131
|
needleBuild(command, config, userSettings),
|
|
137
132
|
needleBuildInfo(command, config, userSettings),
|
|
@@ -140,6 +135,7 @@ export const needlePlugins = async (command, config = undefined, userSettings =
|
|
|
140
135
|
needleTransformCode(command, config, userSettings),
|
|
141
136
|
needleDrop(command, config, userSettings),
|
|
142
137
|
needlePeerjs(command, config, userSettings),
|
|
138
|
+
needleDependencyWatcher(command, config, userSettings),
|
|
143
139
|
needleDependencies(command, config, userSettings),
|
|
144
140
|
vite_4_4_hack(command, config, userSettings),
|
|
145
141
|
needleFacebookInstantGames(command, config, userSettings),
|
|
@@ -156,6 +152,5 @@ export const needlePlugins = async (command, config = undefined, userSettings =
|
|
|
156
152
|
if(asap) array.push(asap);
|
|
157
153
|
|
|
158
154
|
array.push(await editorConnection(command, config, userSettings, array));
|
|
159
|
-
array.push(needleDependencyWatcher(command, config, userSettings));
|
|
160
155
|
return array;
|
|
161
156
|
}
|
|
@@ -1,2 +1,441 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import https from 'https';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
4
|
+
import { resolve } from 'path';
|
|
5
|
+
import { start } from 'repl';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {{pluginContext:import('rollup').TransformPluginContext, cache:Cache, command:string, viteConfig:import("vite").ResolvedConfig | null}} Context
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const debug = false;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Checks if the local files plugin is enabled in user settings.
|
|
15
|
+
* @param {import('../types/userconfig.js').userSettings} userSettings - The user settings object
|
|
16
|
+
*/
|
|
17
|
+
export const makeFilesLocalIsEnabled = (userSettings) => {
|
|
18
|
+
if (typeof userSettings?.makeFilesLocal === "object") return userSettings?.makeFilesLocal?.enabled === true;
|
|
19
|
+
return userSettings?.makeFilesLocal === true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Download files and rewrite code
|
|
24
|
+
* @param {string} command - The command that is being run
|
|
25
|
+
* @param {object} config - The config object
|
|
26
|
+
* @param {import('../types/userconfig.js').userSettings} userSettings
|
|
27
|
+
* @returns {import('vite').Plugin | null}
|
|
28
|
+
*/
|
|
29
|
+
export const needleMakeFilesLocal = (command, config, userSettings) => {
|
|
30
|
+
|
|
31
|
+
if (!makeFilesLocalIsEnabled(userSettings)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log(`[needle:local-files] Local files plugin is enabled`);
|
|
36
|
+
|
|
37
|
+
const cache = new Cache();
|
|
38
|
+
|
|
39
|
+
/** @type {import("vite").ResolvedConfig | null} */
|
|
40
|
+
let viteConfig = null;
|
|
41
|
+
|
|
42
|
+
/** @type {import("vite").Plugin} */
|
|
43
|
+
const plugin = {
|
|
44
|
+
name: "needle:local-files",
|
|
45
|
+
// enforce: 'pre', // explictly DON'T define enforce:pre because of svelte postcss compat
|
|
46
|
+
apply: "build",
|
|
47
|
+
configResolved(config) {
|
|
48
|
+
viteConfig = config;
|
|
49
|
+
},
|
|
50
|
+
// transform bundle
|
|
51
|
+
async transform(src, _id) {
|
|
52
|
+
src = await makeLocal(src, "ext/", "", {
|
|
53
|
+
pluginContext: this,
|
|
54
|
+
cache: cache,
|
|
55
|
+
command: command,
|
|
56
|
+
viteConfig: viteConfig,
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
code: src,
|
|
60
|
+
map: null,
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
buildEnd() {
|
|
64
|
+
const map = cache.map;
|
|
65
|
+
console.log(""); // Make a new line for better readability
|
|
66
|
+
console.log(`[needle:local-files] Made ${map.size} files local:`);
|
|
67
|
+
for (const [key, value] of map.entries()) {
|
|
68
|
+
console.log(`- ${key} → ${value}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return plugin;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Rewrites the source code to make local files
|
|
77
|
+
* @param {string} src - The source code to rewrite
|
|
78
|
+
* @param {string} basePath - The base path where the files will be saved
|
|
79
|
+
* @param {string} currentDir - The current directory of the file being processed
|
|
80
|
+
* @param {Context} context - The Vite plugin context and command
|
|
81
|
+
*/
|
|
82
|
+
async function makeLocal(src, basePath, currentDir, context) {
|
|
83
|
+
|
|
84
|
+
const command = context.command;
|
|
85
|
+
|
|
86
|
+
if (debug) {
|
|
87
|
+
// Find all urls in the source code.
|
|
88
|
+
// Exclude URLs inside comments, like:
|
|
89
|
+
// - // https://example.com
|
|
90
|
+
// - /* ... https://example.com ... */
|
|
91
|
+
// - @link https://example.com
|
|
92
|
+
// - * ... https://example.com
|
|
93
|
+
const urlRegexExcludingComments = /(?<!\/\/.*)(?<!\/\*.*)(?<!@link\s+)(["'])(https?:\/\/[^\s'"]+?)\1/g;
|
|
94
|
+
let match0;
|
|
95
|
+
while ((match0 = urlRegexExcludingComments.exec(src)) !== null) {
|
|
96
|
+
const url = match0[2];
|
|
97
|
+
if (debug) console.log(`\nFound URL: ${url} in ${currentDir}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Google Fonts URLs
|
|
102
|
+
while (true) {
|
|
103
|
+
const match = /["'](https:\/\/fonts\.googleapis\.com\/.+?)["']/g.exec(src);
|
|
104
|
+
if (match === null) {
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
const url = match[1];
|
|
108
|
+
if (debug) console.log(`\nFound google font URL: ${url}`);
|
|
109
|
+
// Check if the font URL is already in the cache
|
|
110
|
+
const cachedPath = context.cache.getFromCache(url);
|
|
111
|
+
if (cachedPath) {
|
|
112
|
+
if (debug) console.log(`Using cached font URL: ${cachedPath}`);
|
|
113
|
+
src = src.replace(url, cachedPath);
|
|
114
|
+
continue; // Skip downloading if already cached
|
|
115
|
+
}
|
|
116
|
+
let font = await downloadText(url);
|
|
117
|
+
const familyNameMatch = /family=([^&]+)/.exec(url);
|
|
118
|
+
const familyName = familyNameMatch ? getValidFilename(familyNameMatch[1], font) : (new URL(url).pathname.split('/').pop());
|
|
119
|
+
font = await makeLocal(font, basePath, basePath, context);
|
|
120
|
+
const fontFileName = `font-${familyName}.css`;
|
|
121
|
+
const outputPath = basePath + fontFileName;
|
|
122
|
+
let newPath;
|
|
123
|
+
if (command === 'build') {
|
|
124
|
+
const referenceId = context.pluginContext.emitFile({
|
|
125
|
+
type: 'asset',
|
|
126
|
+
fileName: outputPath,
|
|
127
|
+
source: font,
|
|
128
|
+
});
|
|
129
|
+
const localPath = `${context.pluginContext.getFileName(referenceId)}`;
|
|
130
|
+
newPath = getRelativeToBasePath(localPath, currentDir);
|
|
131
|
+
// ensureFileExists(localPath, font);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// Create base64 URL for dev mode
|
|
135
|
+
const base64Font = Buffer.from(font).toString('base64');
|
|
136
|
+
newPath = `data:text/css;base64,${base64Font}`;
|
|
137
|
+
}
|
|
138
|
+
if (newPath) {
|
|
139
|
+
context.cache.addToCache(url, newPath);
|
|
140
|
+
src = src.replace(url, newPath);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Google Fonts gstatic URLs
|
|
145
|
+
while (true) {
|
|
146
|
+
const match = /["'(](https:\/\/fonts\.gstatic\.com\/)(.+?)["')]/g.exec(src);
|
|
147
|
+
if (match === null) {
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
const fontPath = match[2];
|
|
151
|
+
const url = match[1] + fontPath;
|
|
152
|
+
if (debug) console.log(`\nFound gstatic URL: ${url}`);
|
|
153
|
+
// Check if the font URL is already in the cache
|
|
154
|
+
const cachedPath = context.cache.getFromCache(url);
|
|
155
|
+
if (cachedPath) {
|
|
156
|
+
if (debug) console.log(`Using cached gstatic font URL: ${cachedPath}`);
|
|
157
|
+
src = src.replace(url, cachedPath);
|
|
158
|
+
continue; // Skip downloading if already cached
|
|
159
|
+
}
|
|
160
|
+
const font = await downloadBinary(url);
|
|
161
|
+
const filename = getValidFilename(fontPath, font);
|
|
162
|
+
if (debug) console.log(`Saving font to: ${basePath + filename}`);
|
|
163
|
+
let newPath;
|
|
164
|
+
if (command === 'build') {
|
|
165
|
+
const referenceId = context.pluginContext.emitFile({
|
|
166
|
+
type: 'asset',
|
|
167
|
+
fileName: basePath + filename,
|
|
168
|
+
source: font,
|
|
169
|
+
});
|
|
170
|
+
const localPath = `${context.pluginContext.getFileName(referenceId)}`;
|
|
171
|
+
newPath = getRelativeToBasePath(localPath, currentDir);
|
|
172
|
+
// ensureFileExists(localPath, font);
|
|
173
|
+
} else {
|
|
174
|
+
// Create base64 URL for dev mode
|
|
175
|
+
const base64Font = Buffer.from(font).toString('base64');
|
|
176
|
+
newPath = `data:text/css;base64,${base64Font}`;
|
|
177
|
+
}
|
|
178
|
+
context.cache.addToCache(url, newPath);
|
|
179
|
+
src = src.replace(url, newPath);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Load QRCode.js
|
|
183
|
+
while (true) {
|
|
184
|
+
// https://cdn.jsdelivr.net/gh/davidshimjs/qrcodejs@gh-pages/qrcode.min.js
|
|
185
|
+
const match = /["'](https:\/\/cdn\.jsdelivr\.net\/gh\/davidshimjs\/qrcodejs@[^'"]+?\/qrcode\.min\.js)["']/g.exec(src);
|
|
186
|
+
if (match === null) {
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
const url = match[1];
|
|
190
|
+
if (debug) console.log(`\nFound QR code URL: ${url}`);
|
|
191
|
+
// Check if the QR code URL is already in the cache
|
|
192
|
+
const cachedPath = context.cache.getFromCache(url);
|
|
193
|
+
if (cachedPath) {
|
|
194
|
+
if (debug) console.log(`Using cached QR code URL: ${cachedPath}`);
|
|
195
|
+
src = src.replace(url, cachedPath);
|
|
196
|
+
continue; // Skip downloading if already cached
|
|
197
|
+
}
|
|
198
|
+
const qrCode = await downloadBinary(url);
|
|
199
|
+
const filename = getValidFilename(url, qrCode);
|
|
200
|
+
if (debug) console.log(`Saving QR code to: ${basePath + filename}`);
|
|
201
|
+
let newPath;
|
|
202
|
+
if (command === 'build') {
|
|
203
|
+
const referenceId = context.pluginContext.emitFile({
|
|
204
|
+
type: 'asset',
|
|
205
|
+
fileName: basePath + filename,
|
|
206
|
+
source: qrCode,
|
|
207
|
+
});
|
|
208
|
+
const localPath = `${context.pluginContext.getFileName(referenceId)}`;
|
|
209
|
+
newPath = getRelativeToBasePath(localPath, currentDir);
|
|
210
|
+
// ensureFileExists(localPath, qrCode);
|
|
211
|
+
} else {
|
|
212
|
+
// create base64 URL for dev mode
|
|
213
|
+
const base64QrCode = Buffer.from(qrCode).toString('base64');
|
|
214
|
+
newPath = `data:application/javascript;base64,${base64QrCode}`;
|
|
215
|
+
}
|
|
216
|
+
context.cache.addToCache(url, newPath);
|
|
217
|
+
src = src.replace(url, newPath);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Polyhaven.org URLs
|
|
221
|
+
let startIndex = 0;
|
|
222
|
+
while (true) {
|
|
223
|
+
const match = /["'](https:\/\/dl\.polyhaven\.org\/file\/.+?)["']/g.exec(src.slice(startIndex));
|
|
224
|
+
if (match === null) {
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
startIndex += match.index + match[0].length; // Update startIndex to continue searching
|
|
228
|
+
const url = match[1];
|
|
229
|
+
if (url.endsWith("/")) {
|
|
230
|
+
if (debug) console.warn(`Skipping Polyhaven URL that ends with a slash: ${url}`);
|
|
231
|
+
continue; // Skip URLs that end with a slash
|
|
232
|
+
}
|
|
233
|
+
if (url.includes("\"") || url.includes("'")) {
|
|
234
|
+
if (debug) console.warn(`Skipping Polyhaven URL with quotes: ${url}`);
|
|
235
|
+
continue; // Skip URLs with quotes
|
|
236
|
+
}
|
|
237
|
+
if (debug) console.log(`\nFound Polyhaven URL: ${url}`);
|
|
238
|
+
// Check if the Polyhaven URL is already in the cache
|
|
239
|
+
const cachedPath = context.cache.getFromCache(url);
|
|
240
|
+
if (cachedPath) {
|
|
241
|
+
if (debug) console.log(`Using cached Polyhaven URL: ${cachedPath}`);
|
|
242
|
+
src = src.replace(url, cachedPath);
|
|
243
|
+
continue; // Skip downloading if already cached
|
|
244
|
+
}
|
|
245
|
+
const polyhavenFile = await downloadBinary(url);
|
|
246
|
+
const filename = getValidFilename(url, polyhavenFile);
|
|
247
|
+
if (debug) console.log(`Saving Polyhaven file to: ${basePath + filename}`);
|
|
248
|
+
let newPath;
|
|
249
|
+
if (command === 'build') {
|
|
250
|
+
const referenceId = context.pluginContext.emitFile({
|
|
251
|
+
type: 'asset',
|
|
252
|
+
fileName: basePath + filename,
|
|
253
|
+
source: polyhavenFile,
|
|
254
|
+
});
|
|
255
|
+
const localPath = `${context.pluginContext.getFileName(referenceId)}`;
|
|
256
|
+
newPath = getRelativeToBasePath(localPath, currentDir);
|
|
257
|
+
// ensureFileExists(localPath, polyhavenFile);
|
|
258
|
+
} else {
|
|
259
|
+
// Create base64 URL for dev mode
|
|
260
|
+
const base64PolyhavenFile = Buffer.from(polyhavenFile).toString('base64');
|
|
261
|
+
newPath = `data:application/octet-stream;base64,${base64PolyhavenFile}`;
|
|
262
|
+
}
|
|
263
|
+
if (newPath) {
|
|
264
|
+
context.cache.addToCache(url, newPath);
|
|
265
|
+
src = src.replace(url, newPath);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return src;
|
|
270
|
+
|
|
271
|
+
// /**
|
|
272
|
+
// * Ensures that a file exists at the specified relative path with the given content.
|
|
273
|
+
// * If the file does not exist, it will be created with the provided content.
|
|
274
|
+
// * @param {string} relPath - The relative path to the file
|
|
275
|
+
// * @param {string|Uint8Array} content - The content to write to the file
|
|
276
|
+
// * @returns {void}
|
|
277
|
+
// */
|
|
278
|
+
// function ensureFileExists(relPath, content) {
|
|
279
|
+
// const outputPath = context.viteConfig?.build?.outDir || "dist";
|
|
280
|
+
// const fullPath = resolve(outputPath, relPath);
|
|
281
|
+
// if (!existsSync(fullPath)) {
|
|
282
|
+
// if (debug) console.log(`Creating file: ${fullPath}`);
|
|
283
|
+
// const dir = resolve(fullPath, '..');
|
|
284
|
+
// mkdirSync(dir, { recursive: true });
|
|
285
|
+
// writeFileSync(fullPath, content);
|
|
286
|
+
// }
|
|
287
|
+
// }
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class Cache {
|
|
292
|
+
__cache = new Map();
|
|
293
|
+
/**
|
|
294
|
+
* Adds a key-value pair to the cache.
|
|
295
|
+
* @param {string} key - The key to store the value under
|
|
296
|
+
* @param {string|Uint8Array} value - The value to store in the cache
|
|
297
|
+
* @returns {void}
|
|
298
|
+
*/
|
|
299
|
+
addToCache(key, value) {
|
|
300
|
+
if (this.__cache.has(key)) {
|
|
301
|
+
if (debug) console.warn(`Key ${key} already exists in cache, overwriting.`);
|
|
302
|
+
}
|
|
303
|
+
this.__cache.set(key, value);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Retrieves a value from the cache by its key.
|
|
307
|
+
* @param {string} key - The key to look up in the cache
|
|
308
|
+
*/
|
|
309
|
+
getFromCache(key) {
|
|
310
|
+
if (this.__cache.has(key)) {
|
|
311
|
+
return this.__cache.get(key);
|
|
312
|
+
} else {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
get map() {
|
|
317
|
+
return this.__cache;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Returns a relative path based on the base path.
|
|
324
|
+
* @param {string} path - The path to check
|
|
325
|
+
* @param {string | undefined | null} basePath - The base path to compare against
|
|
326
|
+
* @return {string} - The relative path if it starts with the base path, otherwise the original path
|
|
327
|
+
*/
|
|
328
|
+
function getRelativeToBasePath(path, basePath) {
|
|
329
|
+
if (basePath?.length && path.startsWith(basePath)) {
|
|
330
|
+
return "./" + path.substring(basePath.length);
|
|
331
|
+
}
|
|
332
|
+
return path;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Generates a valid filename from a given path.
|
|
338
|
+
* @param {string} path - The path to generate a filename from
|
|
339
|
+
* @param {string|Uint8Array} content - The content to hash for uniqueness (not used in this example)
|
|
340
|
+
*/
|
|
341
|
+
function getValidFilename(path, content) {
|
|
342
|
+
if (path.startsWith("http:") || path.startsWith("https:")) {
|
|
343
|
+
const url = new URL(path);
|
|
344
|
+
const pathParts = url.pathname.split('/');
|
|
345
|
+
const filename = pathParts.pop() || 'file';
|
|
346
|
+
const nameParts = filename.split('.');
|
|
347
|
+
const nameWithoutExt = nameParts.slice(0, -1).join('.');
|
|
348
|
+
path = `${nameWithoutExt}-${createContentMd5(url.host + pathParts.join('/'))}.${nameParts.pop() || 'unknown'}`;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Remove any characters that are not valid in filenames
|
|
352
|
+
let name = path.replace(/[^a-z0-9_\-\.\+]/gi, '-');
|
|
353
|
+
|
|
354
|
+
const maxLength = 200;
|
|
355
|
+
if (path.length > maxLength) {
|
|
356
|
+
// If the name is too long, hash it to create a unique identifier
|
|
357
|
+
const hash = createContentMd5(content);
|
|
358
|
+
let ext = "";
|
|
359
|
+
const extIndex = name.lastIndexOf('.');
|
|
360
|
+
if (extIndex !== -1) {
|
|
361
|
+
ext = name.substring(extIndex + 1);
|
|
362
|
+
name = name.substring(0, extIndex);
|
|
363
|
+
}
|
|
364
|
+
name = `${name.substring(0, maxLength)}-${hash}${ext ? `.${ext}` : ''}`;
|
|
365
|
+
}
|
|
366
|
+
return name;
|
|
367
|
+
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Creates a hash of the content using MD5.
|
|
372
|
+
* @param {string|Uint8Array} str - The content to hash
|
|
373
|
+
*/
|
|
374
|
+
function createContentMd5(str) {
|
|
375
|
+
return createHash('md5')
|
|
376
|
+
.update(str)
|
|
377
|
+
.digest('hex');
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* @param {string} url - The URL of the font to download
|
|
383
|
+
*/
|
|
384
|
+
function downloadText(url) {
|
|
385
|
+
return new Promise(((res, rej) => {
|
|
386
|
+
https.get(url, (response) => {
|
|
387
|
+
if (response.statusCode !== 200) {
|
|
388
|
+
console.log();
|
|
389
|
+
console.error(`Failed to download (${response.statusCode}): ${url}`);
|
|
390
|
+
rej(new Error(`Failed to download (${response.statusCode}): ${url}`));
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
console.log(""); // Make a new line for better readability
|
|
395
|
+
console.log(`[needle:local-files] Make local: ${url}`);
|
|
396
|
+
|
|
397
|
+
let data = '';
|
|
398
|
+
response.on('data', (chunk) => {
|
|
399
|
+
data += chunk;
|
|
400
|
+
});
|
|
401
|
+
response.on('end', () => {
|
|
402
|
+
// Here you can save the data to a file or process it as needed
|
|
403
|
+
if (debug) console.log(`Downloaded from ${url}`);
|
|
404
|
+
res(data);
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
}))
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function downloadBinary(url) {
|
|
411
|
+
return new Promise((res, rej) => {
|
|
412
|
+
https.get(url, (response) => {
|
|
413
|
+
|
|
414
|
+
// Handle redirects
|
|
415
|
+
if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
416
|
+
if (debug) console.log(`Redirecting to ${response.headers.location}`);
|
|
417
|
+
return downloadBinary(response.headers.location).then(res).catch(rej);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (response.statusCode !== 200) {
|
|
421
|
+
console.log();
|
|
422
|
+
console.error(`Failed to download (${response.statusCode}): ${url}`);
|
|
423
|
+
rej(new Error(`Failed to download (${response.statusCode}): ${url}`));
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
console.log(""); // Make a new line for better readability
|
|
428
|
+
console.log(`[needle:local-files] Make local: ${url}`);
|
|
429
|
+
|
|
430
|
+
const chunks = [];
|
|
431
|
+
response.on('data', (chunk) => {
|
|
432
|
+
chunks.push(chunk);
|
|
433
|
+
});
|
|
434
|
+
response.on('end', () => {
|
|
435
|
+
// Here you can save the data to a file or process it as needed
|
|
436
|
+
if (debug) console.log(`Downloaded from ${url}`);
|
|
437
|
+
res(Buffer.concat(chunks));
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @ts-check
|
|
2
1
|
import path from "path";
|
|
3
2
|
|
|
4
3
|
let isStringifying = false;
|
|
@@ -7,7 +6,7 @@ let isStringifying = false;
|
|
|
7
6
|
* Patches console methods to capture log messages and send them to the server.
|
|
8
7
|
* This is useful for debugging and logging in the client.
|
|
9
8
|
* @param {"log" | "warn" | "info" | "debug" | "error" | "internal"} level
|
|
10
|
-
* @param {
|
|
9
|
+
* @param {any} message - The log message to capture.
|
|
11
10
|
*/
|
|
12
11
|
function sendLogToServer(level, ...message) {
|
|
13
12
|
if (isStringifying) return;
|
|
@@ -15,20 +14,20 @@ function sendLogToServer(level, ...message) {
|
|
|
15
14
|
try {
|
|
16
15
|
isStringifying = true;
|
|
17
16
|
// console.time("sendLogToServer");
|
|
18
|
-
|
|
17
|
+
message = stringifyLog(message);
|
|
19
18
|
// console.timeEnd("sendLogToServer");
|
|
20
19
|
// keep messages below payload limit
|
|
21
|
-
if (
|
|
22
|
-
|
|
20
|
+
if (message.length > 100_000) {
|
|
21
|
+
message = message.slice(0, 100_000) + "... <truncated>";
|
|
23
22
|
}
|
|
24
23
|
// @ts-ignore
|
|
25
|
-
import.meta.hot.send("needle:client-log", { level, message:
|
|
26
|
-
} catch (
|
|
24
|
+
import.meta.hot.send("needle:client-log", { level, message: message });
|
|
25
|
+
} catch (e) {
|
|
27
26
|
// silently fail but send a message
|
|
28
27
|
try {
|
|
29
28
|
import.meta.hot.send("needle:client-log", { level: "error", message: `Error during logging: ${e.message}` });
|
|
30
29
|
}
|
|
31
|
-
catch (
|
|
30
|
+
catch (e2) {
|
|
32
31
|
// fallback failed as well
|
|
33
32
|
}
|
|
34
33
|
}
|
|
@@ -38,7 +37,6 @@ function sendLogToServer(level, ...message) {
|
|
|
38
37
|
}
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
/** @param {(...args: unknown[]) => void} fn @param {unknown[]} args */
|
|
42
40
|
function logHelper(fn, args) {
|
|
43
41
|
const error = new Error();
|
|
44
42
|
const stack = error.stack;
|
|
@@ -54,32 +52,32 @@ function logHelper(fn, args) {
|
|
|
54
52
|
if (import.meta && "hot" in import.meta) {
|
|
55
53
|
|
|
56
54
|
function patchLogs() {
|
|
57
|
-
const originalLog =
|
|
58
|
-
const originalWarn =
|
|
59
|
-
const originalInfo =
|
|
60
|
-
const originalDebug =
|
|
61
|
-
const originalError =
|
|
55
|
+
const originalLog = console.log.bind(console);
|
|
56
|
+
const originalWarn = console.warn.bind(console);
|
|
57
|
+
const originalInfo = console.info.bind(console);
|
|
58
|
+
const originalDebug = console.debug.bind(console);
|
|
59
|
+
const originalError = console.error.bind(console);
|
|
62
60
|
|
|
63
|
-
console.log =
|
|
61
|
+
console.log = function (...args) {
|
|
64
62
|
logHelper(originalLog, args);
|
|
65
63
|
sendLogToServer("log", ...args);
|
|
66
|
-
}
|
|
67
|
-
console.warn =
|
|
64
|
+
}
|
|
65
|
+
console.warn = (...args) => {
|
|
68
66
|
logHelper(originalWarn, args);
|
|
69
67
|
sendLogToServer("warn", ...args);
|
|
70
|
-
}
|
|
71
|
-
console.info =
|
|
68
|
+
}
|
|
69
|
+
console.info = (...args) => {
|
|
72
70
|
logHelper(originalInfo, args);
|
|
73
71
|
sendLogToServer("info", ...args);
|
|
74
|
-
}
|
|
75
|
-
console.debug =
|
|
72
|
+
}
|
|
73
|
+
console.debug = (...args) => {
|
|
76
74
|
logHelper(originalDebug, args);
|
|
77
75
|
sendLogToServer("debug", ...args);
|
|
78
|
-
}
|
|
79
|
-
console.error =
|
|
76
|
+
}
|
|
77
|
+
console.error = (...args) => {
|
|
80
78
|
logHelper(originalError, args);
|
|
81
79
|
sendLogToServer("error", ...args);
|
|
82
|
-
}
|
|
80
|
+
}
|
|
83
81
|
return () => {
|
|
84
82
|
console.log = originalLog;
|
|
85
83
|
console.warn = originalWarn;
|
|
@@ -127,12 +125,11 @@ Connection: ${"connection" in navigator ? JSON.stringify(navigator.connection) :
|
|
|
127
125
|
User Activation: ${"userActivation" in navigator ? JSON.stringify(navigator.userActivation) : "Not available"}
|
|
128
126
|
`);
|
|
129
127
|
|
|
130
|
-
/** @typedef {{ vendor?: string, architecture?: string, device?: string, description?: string, features?: unknown, limits?: unknown }} GPUAdapterInfoLike */
|
|
131
128
|
if ("gpu" in navigator) {
|
|
132
129
|
// @ts-ignore
|
|
133
130
|
navigator.gpu.requestAdapter()
|
|
134
|
-
.then(
|
|
135
|
-
.then(
|
|
131
|
+
.then(adapter => adapter ? adapter.requestDevice() : null)
|
|
132
|
+
.then(device => {
|
|
136
133
|
if (device) {
|
|
137
134
|
const adapterInfo = device.adapterInfo;
|
|
138
135
|
if (adapterInfo) {
|
|
@@ -149,21 +146,17 @@ User Activation: ${"userActivation" in navigator ? JSON.stringify(navigator.user
|
|
|
149
146
|
});
|
|
150
147
|
}
|
|
151
148
|
}
|
|
152
|
-
catch (
|
|
149
|
+
catch (e) {
|
|
153
150
|
// silently fail
|
|
154
151
|
sendLogToServer("error", `Error during initial log: ${e.message}`);
|
|
155
152
|
}
|
|
156
153
|
|
|
157
154
|
window.addEventListener('error', (event) => {
|
|
158
|
-
const
|
|
159
|
-
const error = /** @type {{ stack?: string, message?: string } | null | undefined} */ (typedErrorEvent.error);
|
|
160
|
-
const errorMessage = error ? error.stack || error.message : typedErrorEvent.message;
|
|
155
|
+
const errorMessage = event.error ? event.error.stack || event.error.message : event.message;
|
|
161
156
|
sendLogToServer("error", errorMessage);
|
|
162
157
|
});
|
|
163
158
|
window.addEventListener('unhandledrejection', (event) => {
|
|
164
|
-
const
|
|
165
|
-
const rejectionReason = /** @type {{ stack?: string, message?: string } | null | undefined} */ (typedRejEvent.reason);
|
|
166
|
-
const reason = rejectionReason ? rejectionReason.stack || rejectionReason.message : "Unhandled rejection without reason";
|
|
159
|
+
const reason = event.reason ? event.reason.stack || event.reason.message : "Unhandled rejection without reason";
|
|
167
160
|
sendLogToServer("error", `Unhandled promise rejection: ${reason}`);
|
|
168
161
|
});
|
|
169
162
|
window.addEventListener('beforeunload', () => {
|
|
@@ -218,7 +211,7 @@ User Activation: ${"userActivation" in navigator ? JSON.stringify(navigator.user
|
|
|
218
211
|
sendLogToServer("internal", `URL hash changed to ${location.hash}`);
|
|
219
212
|
});
|
|
220
213
|
window.addEventListener('popstate', () => {
|
|
221
|
-
sendLogToServer("internal", `History state changed: ${JSON.stringify(
|
|
214
|
+
sendLogToServer("internal", `History state changed: ${JSON.stringify(history.state)}`);
|
|
222
215
|
});
|
|
223
216
|
|
|
224
217
|
|
|
@@ -234,10 +227,10 @@ User Activation: ${"userActivation" in navigator ? JSON.stringify(navigator.user
|
|
|
234
227
|
|
|
235
228
|
/**
|
|
236
229
|
* Stringifies a log message, handling circular references and formatting.
|
|
237
|
-
* @param {
|
|
238
|
-
* @param {Set<
|
|
230
|
+
* @param {any} log
|
|
231
|
+
* @param {Set<any>} [seen]
|
|
239
232
|
*/
|
|
240
|
-
function stringifyLog(log, seen =
|
|
233
|
+
function stringifyLog(log, seen = new Set(), depth = 0) {
|
|
241
234
|
const isServer = typeof window === "undefined";
|
|
242
235
|
const stringify_limits = {
|
|
243
236
|
string: isServer ? 100_000 : 1_000,
|
|
@@ -279,9 +272,8 @@ function stringifyLog(log, seen = /** @type {Set<unknown>} */ (new Set()), depth
|
|
|
279
272
|
|| log instanceof BigUint64Array
|
|
280
273
|
|| log instanceof Float64Array
|
|
281
274
|
) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
return stringifyArray(logArr);
|
|
275
|
+
seen.add(log);
|
|
276
|
+
return stringifyArray(log);
|
|
285
277
|
}
|
|
286
278
|
if (typeof log === "object") {
|
|
287
279
|
|
|
@@ -295,12 +287,11 @@ function stringifyLog(log, seen = /** @type {Set<unknown>} */ (new Set()), depth
|
|
|
295
287
|
return `<Error: ${log.message}\nStack: ${log.stack}>`;
|
|
296
288
|
}
|
|
297
289
|
|
|
298
|
-
const
|
|
299
|
-
const keys = Object.keys(logObj);
|
|
290
|
+
const keys = Object.keys(log);
|
|
300
291
|
let res = "{";
|
|
301
292
|
for (let i = 0; i < keys.length; i++) {
|
|
302
293
|
const key = keys[i];
|
|
303
|
-
let value =
|
|
294
|
+
let value = log[key];
|
|
304
295
|
if (i >= stringify_limits.object_keys) {
|
|
305
296
|
res += `, ... <truncated ${keys.length - i} keys>`;
|
|
306
297
|
break;
|
|
@@ -335,7 +326,6 @@ function stringifyLog(log, seen = /** @type {Set<unknown>} */ (new Set()), depth
|
|
|
335
326
|
|
|
336
327
|
return String(log);
|
|
337
328
|
|
|
338
|
-
/** @param {ArrayLike<unknown>} arr @returns {string} */
|
|
339
329
|
function stringifyArray(arr) {
|
|
340
330
|
let res = "";
|
|
341
331
|
for (let i = 0; i < arr.length; i++) {
|