@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
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { ChildProcess, exec } from 'child_process';
|
|
2
2
|
import { NEEDLE_CLOUD_CLI_NAME } from '../common/cloud.js';
|
|
3
3
|
import { getOutputDirectory, loadConfig } from './config.js';
|
|
4
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync,
|
|
5
|
-
import { relative } from 'path';
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'fs';
|
|
6
5
|
import { copyFilesSync } from '../common/files.js';
|
|
7
6
|
import { delay } from '../common/timers.js';
|
|
8
|
-
import { needleBlue, needleDim, needleLog, needleSupportsColor, setTransientLogLineCleaner } from './logging.js';
|
|
9
|
-
|
|
10
|
-
const PIPELINE_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
11
|
-
const PIPELINE_STRUCTURED_LOG_PREFIX = "__needle_pipeline_log__:";
|
|
12
7
|
|
|
13
8
|
/**
|
|
14
9
|
* @param {import('../types').userSettings} config
|
|
@@ -40,12 +35,10 @@ env:
|
|
|
40
35
|
// see https://linear.app/needle/issue/NE-3798
|
|
41
36
|
|
|
42
37
|
|
|
43
|
-
/** @type {Promise<
|
|
38
|
+
/** @type {Promise<any>|null} */
|
|
44
39
|
let buildPipelineTask;
|
|
45
40
|
/** @type {null | {tempDirectory:string, outputDirectory:string}} */
|
|
46
41
|
let buildPipelineTaskResults = null;
|
|
47
|
-
/** @type {null | string} */
|
|
48
|
-
let buildPipelineStepSummary = null;
|
|
49
42
|
|
|
50
43
|
export function waitForBuildPipelineToFinish() {
|
|
51
44
|
return buildPipelineTask;
|
|
@@ -107,9 +100,7 @@ export const needleBuildPipeline = async (command, config, userSettings) => {
|
|
|
107
100
|
}
|
|
108
101
|
|
|
109
102
|
if (!shouldRun) {
|
|
110
|
-
|
|
111
|
-
log("Skipping build pipeline because this is a development build.\n- Invoke with `--production` to run the build pipeline.\n- For example \"vite build -- --production\".");
|
|
112
|
-
}
|
|
103
|
+
log("Skipping build pipeline because this is a development build.\n- Invoke with `--production` to run the build pipeline.\n- For example \"vite build -- --production\".");
|
|
113
104
|
await new Promise((resolve, _) => setTimeout(resolve, 1000));
|
|
114
105
|
return null;
|
|
115
106
|
}
|
|
@@ -122,7 +113,6 @@ export const needleBuildPipeline = async (command, config, userSettings) => {
|
|
|
122
113
|
|
|
123
114
|
const verboseOutput = userSettings?.buildPipeline?.verbose || false;
|
|
124
115
|
let taskHasCompleted = false;
|
|
125
|
-
let taskSucceeded = false;
|
|
126
116
|
|
|
127
117
|
return {
|
|
128
118
|
name: 'needle:buildpipeline',
|
|
@@ -158,22 +148,26 @@ export const needleBuildPipeline = async (command, config, userSettings) => {
|
|
|
158
148
|
log("Build pipeline already running...");
|
|
159
149
|
return;
|
|
160
150
|
}
|
|
151
|
+
let taskSucceeded = false;
|
|
161
152
|
// start the compression process once vite is done copying the files
|
|
162
153
|
buildPipelineTask = invokeBuildPipeline(userSettings, { verbose: verboseOutput })
|
|
163
154
|
.then((res) => {
|
|
164
155
|
if (verboseOutput) log("Build pipeline task result:", res);
|
|
165
156
|
taskSucceeded = res;
|
|
166
157
|
})
|
|
167
|
-
.catch(
|
|
168
|
-
|
|
158
|
+
.catch(err => {
|
|
159
|
+
console.error("[needle-buildpipeline] - Error during build pipeline: " + err.message);
|
|
169
160
|
if (verboseOutput) log("Error details:", err);
|
|
170
161
|
})
|
|
171
162
|
.finally(() => {
|
|
172
163
|
taskHasCompleted = true;
|
|
173
164
|
if (!taskSucceeded) {
|
|
174
|
-
|
|
165
|
+
console.error("[needle-buildpipeline] - Build pipeline task did not succeed.");
|
|
175
166
|
throw new Error("[needle-buildpipeline] - Build pipeline failed. Please check the logs above for more information.");
|
|
176
167
|
}
|
|
168
|
+
else {
|
|
169
|
+
log("Finished successfully");
|
|
170
|
+
}
|
|
177
171
|
});
|
|
178
172
|
}
|
|
179
173
|
},
|
|
@@ -181,34 +175,21 @@ export const needleBuildPipeline = async (command, config, userSettings) => {
|
|
|
181
175
|
if (!buildPipelineTask) {
|
|
182
176
|
return;
|
|
183
177
|
}
|
|
178
|
+
if (!taskHasCompleted) {
|
|
179
|
+
log("Waiting for build pipeline to finish...");
|
|
180
|
+
}
|
|
184
181
|
// // this is the last hook that is called, so we can wait for the task to finish here
|
|
185
182
|
return buildPipelineTask = buildPipelineTask?.then(() => {
|
|
186
|
-
const lines = /** @type {string[]} */ ([]);
|
|
187
|
-
if (buildPipelineStepSummary) {
|
|
188
|
-
lines.push(buildPipelineStepSummary);
|
|
189
|
-
}
|
|
190
|
-
if (taskSucceeded) {
|
|
191
|
-
lines.push(needleDim("✓ Finished successfully"));
|
|
192
|
-
}
|
|
193
183
|
// Copy the results to their final output directory.
|
|
194
184
|
if (buildPipelineTaskResults != null) {
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
const outputPath = relative(process.cwd(), buildPipelineTaskResults.outputDirectory).replaceAll("\\", "/") || ".";
|
|
198
|
-
const moved = getDirectoryStats(buildPipelineTaskResults.tempDirectory);
|
|
199
|
-
lines.push(`${key("Copying files")}: \"${outputPath}\"`);
|
|
200
|
-
const ctx = { count: 0, bytes: 0 }
|
|
185
|
+
log(`Copying files from temporary output directory to final output directory at \"${buildPipelineTaskResults.outputDirectory}\"`);
|
|
186
|
+
const ctx = { count: 0 }
|
|
201
187
|
copyFilesSync(buildPipelineTaskResults.tempDirectory, buildPipelineTaskResults.outputDirectory, true, ctx);
|
|
202
|
-
|
|
203
|
-
lines.push(`${key("Data moved")}: ${formatBytes(moved.totalBytes)} (${moved.fileCount} files)`);
|
|
188
|
+
log(`Copied ${ctx.count} file(s)`);
|
|
204
189
|
}
|
|
205
190
|
else {
|
|
206
|
-
|
|
191
|
+
log("No files to copy - build pipeline did not run or did not finish successfully");
|
|
207
192
|
}
|
|
208
|
-
if (lines.length > 0) {
|
|
209
|
-
needleLog("needle-buildpipeline", lines.join("\n"), "log", { dimBody: false });
|
|
210
|
-
}
|
|
211
|
-
buildPipelineStepSummary = null;
|
|
212
193
|
});
|
|
213
194
|
},
|
|
214
195
|
}
|
|
@@ -235,31 +216,21 @@ async function fixPackageJson(packageJsonPath) {
|
|
|
235
216
|
writeFileSync(packageJsonPath, fixed);
|
|
236
217
|
}
|
|
237
218
|
|
|
238
|
-
/** @param {
|
|
219
|
+
/** @param {any} args */
|
|
239
220
|
function log(...args) {
|
|
240
|
-
|
|
221
|
+
console.log("[needle-buildpipeline]", ...args);
|
|
241
222
|
}
|
|
242
|
-
/** @param {
|
|
223
|
+
/** @param {any} args */
|
|
243
224
|
function warn(...args) {
|
|
244
|
-
|
|
225
|
+
console.warn("WARN: [needle-buildpipeline]", ...args);
|
|
245
226
|
}
|
|
246
227
|
|
|
247
|
-
/**
|
|
248
|
-
* @typedef {{ event?: string, phase?: string, target?: string, message?: string, level?: string }} BuildPipelinePayload
|
|
249
|
-
*/
|
|
250
228
|
/**
|
|
251
229
|
* @param {import('../types').userSettings} opts
|
|
252
230
|
* @param {{verbose?:boolean}} [options]
|
|
253
231
|
* @returns {Promise<boolean>}
|
|
254
232
|
*/
|
|
255
233
|
async function invokeBuildPipeline(opts, options = {}) {
|
|
256
|
-
const rel = (/** @type {string} */ pathValue) => {
|
|
257
|
-
const value = relative(process.cwd(), pathValue).replaceAll("\\", "/");
|
|
258
|
-
return value?.length ? value : ".";
|
|
259
|
-
};
|
|
260
|
-
const supportsColor = needleSupportsColor();
|
|
261
|
-
const key = (/** @type {string} */ text) => supportsColor ? needleBlue(text) : text;
|
|
262
|
-
|
|
263
234
|
const installPath = "node_modules/@needle-tools/gltf-build-pipeline";
|
|
264
235
|
const fullInstallPath = process.cwd() + "/" + installPath;
|
|
265
236
|
const existsLocally = existsSync(fullInstallPath);
|
|
@@ -286,7 +257,7 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
286
257
|
log("Max wait time exceeded - aborting...");
|
|
287
258
|
return Promise.resolve(false);
|
|
288
259
|
}
|
|
289
|
-
if (iteration <= 0)
|
|
260
|
+
if (iteration <= 0) log(`Waiting for output directory to be created... (${outputDirectory})`);
|
|
290
261
|
return delay(1000).then(() => waitForOutputDirectory(iteration + 1));
|
|
291
262
|
}
|
|
292
263
|
if (options?.verbose) log(`Output directory found after ${iteration} iteration(s) at "${outputDirectory}" - continuing...`);
|
|
@@ -297,19 +268,7 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
297
268
|
return false;
|
|
298
269
|
}
|
|
299
270
|
const files = readdirSync(outputDirectory).filter(f => f.endsWith(".glb") || f.endsWith(".gltf") || f.endsWith(".vrm") || f.endsWith(".fbx"));
|
|
300
|
-
|
|
301
|
-
try {
|
|
302
|
-
return total + statSync(outputDirectory + "/" + file).size;
|
|
303
|
-
}
|
|
304
|
-
catch {
|
|
305
|
-
return total;
|
|
306
|
-
}
|
|
307
|
-
}, 0);
|
|
308
|
-
needleLog("needle-buildpipeline", [
|
|
309
|
-
`${key("Files to process")}: ${files.length} in ${rel(outputDirectory)}`,
|
|
310
|
-
`${key("Input size")}: ${formatBytes(filesBytes)}`,
|
|
311
|
-
existsSync(process.cwd() + "/node_modules/.needle/build-pipeline/output") ? needleDim("Removing temporary output directory") : undefined,
|
|
312
|
-
].filter(Boolean), "log", { dimBody: false });
|
|
271
|
+
log(`${files.length} file(s) to process in ${outputDirectory}`);
|
|
313
272
|
|
|
314
273
|
/** @type {null | ChildProcess} */
|
|
315
274
|
let proc = null;
|
|
@@ -331,6 +290,7 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
331
290
|
// this is so that processes like sveltekit-static-adapter can run first and does not override already compressed files
|
|
332
291
|
const tempOutputPath = process.cwd() + "/node_modules/.needle/build-pipeline/output";
|
|
333
292
|
if (existsSync(tempOutputPath)) {
|
|
293
|
+
log("Removing temporary output directory at " + tempOutputPath);
|
|
334
294
|
rmSync(tempOutputPath, { recursive: true, force: true });
|
|
335
295
|
}
|
|
336
296
|
mkdirSync(tempOutputPath, { recursive: true });
|
|
@@ -347,7 +307,6 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
347
307
|
|
|
348
308
|
// allow running the build pipeline in the cloud. It requires and access token to be set in the vite.config.js
|
|
349
309
|
// this can be set via e.g. process.env.NEEDLE_CLOUD_TOKEN
|
|
350
|
-
const commandEnv = { ...process.env, NEEDLE_PIPELINE_STRUCTURED_LOGS: "1" };
|
|
351
310
|
if (runInCloud) {
|
|
352
311
|
if (!cloudAccessToken || !(typeof cloudAccessToken === "string") || cloudAccessToken.length <= 0) {
|
|
353
312
|
throw new Error("No cloud access token configured. Please set it via process.env.NEEDLE_CLOUD_TOKEN or in the vite.config.js");
|
|
@@ -369,12 +328,12 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
369
328
|
console.log("\n");
|
|
370
329
|
const obfuscatedToken = `${cloudAccessToken.slice(0, 2)}*****${cloudAccessToken.slice(-2)}`;
|
|
371
330
|
log(`Running compression in cloud ⛅ using access token: ${obfuscatedToken}`);
|
|
372
|
-
proc = exec(cmd
|
|
331
|
+
proc = exec(cmd);
|
|
373
332
|
}
|
|
374
333
|
else if (existsLocally) {
|
|
375
334
|
const cmd = `needle-gltf transform "${outputDirectory}" \"${tempOutputPath}\"`;
|
|
376
335
|
log("Running command \"" + cmd + "\" at " + process.cwd() + "...");
|
|
377
|
-
proc = exec(cmd, { cwd: installPath
|
|
336
|
+
proc = exec(cmd, { cwd: installPath });
|
|
378
337
|
}
|
|
379
338
|
else {
|
|
380
339
|
// First check if the user passed in a specific version to use via the vite config
|
|
@@ -394,140 +353,33 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
394
353
|
|
|
395
354
|
const cmd = `npx --yes @needle-tools/gltf-build-pipeline@${version} transform "${outputDirectory}" \"${tempOutputPath}\"`;
|
|
396
355
|
log(`Running compression locally using version '${version}'`);
|
|
397
|
-
proc = exec(cmd
|
|
398
|
-
}
|
|
399
|
-
let pipelineSpinnerIndex = 0;
|
|
400
|
-
let pipelineSpinnerActive = false;
|
|
401
|
-
let transformStepCount = 0;
|
|
402
|
-
let compressStepCount = 0;
|
|
403
|
-
|
|
404
|
-
function clearPipelineProgress() {
|
|
405
|
-
if (!process.stdout.isTTY || !pipelineSpinnerActive) return;
|
|
406
|
-
process.stdout.write("\r\x1b[2K");
|
|
407
|
-
pipelineSpinnerActive = false;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/** @param {string} text */
|
|
411
|
-
function updatePipelineProgress(text) {
|
|
412
|
-
if (!process.stdout.isTTY) return;
|
|
413
|
-
const frame = PIPELINE_SPINNER_FRAMES[pipelineSpinnerIndex++ % PIPELINE_SPINNER_FRAMES.length];
|
|
414
|
-
const maxLength = Math.max(24, (process.stdout.columns || 120) - 4);
|
|
415
|
-
const value = text.length > maxLength ? `${text.slice(0, Math.max(0, maxLength - 1))}…` : text;
|
|
416
|
-
process.stdout.write(`\r\x1b[2K${frame} ${value}\x1b[0K`);
|
|
417
|
-
pipelineSpinnerActive = true;
|
|
356
|
+
proc = exec(cmd);
|
|
418
357
|
}
|
|
419
|
-
|
|
420
|
-
setTransientLogLineCleaner(() => clearPipelineProgress());
|
|
421
|
-
|
|
422
|
-
/** @param {Buffer|string} data */
|
|
358
|
+
/** @param {any} data */
|
|
423
359
|
function onLog(data) {
|
|
424
360
|
if (data.length <= 0) return;
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
if (
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
let payload = /** @type {BuildPipelinePayload | null} */ (null);
|
|
432
|
-
try {
|
|
433
|
-
payload = /** @type {BuildPipelinePayload} */ (JSON.parse(line.slice(PIPELINE_STRUCTURED_LOG_PREFIX.length)));
|
|
434
|
-
}
|
|
435
|
-
catch {
|
|
436
|
-
payload = null;
|
|
437
|
-
}
|
|
438
|
-
if (payload) {
|
|
439
|
-
if (payload.event === "progress") {
|
|
440
|
-
if (payload.phase === "transform") transformStepCount++;
|
|
441
|
-
if (payload.phase === "compress") compressStepCount++;
|
|
442
|
-
updatePipelineProgress(`Build pipeline ${payload.phase === "compress" ? "Compressing" : "Transform"} ${payload.target || payload.message || ""}`.trim());
|
|
443
|
-
continue;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
clearPipelineProgress();
|
|
447
|
-
|
|
448
|
-
if (payload.event === "summary") {
|
|
449
|
-
needleLog("needle-buildpipeline", payload.message || "Build pipeline summary", "log", { showHeader: false, leadingNewline: true });
|
|
450
|
-
continue;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
const level = String(payload.level || "info").toLowerCase();
|
|
454
|
-
const message = payload.message || line;
|
|
455
|
-
if (level === "error") {
|
|
456
|
-
needleLog("needle-buildpipeline", message, "error", { dimBody: false, showHeader: false, leadingNewline: true });
|
|
457
|
-
}
|
|
458
|
-
else if (level === "warn") {
|
|
459
|
-
needleLog("needle-buildpipeline", message, "warn", { dimBody: false, showHeader: false, leadingNewline: true });
|
|
460
|
-
}
|
|
461
|
-
else {
|
|
462
|
-
needleLog("needle-buildpipeline", message, "log", { showHeader: false, leadingNewline: true });
|
|
463
|
-
}
|
|
464
|
-
continue;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
if (line.startsWith("info: [Needle Build Pipeline]") || line.startsWith("info: No \"gltf\" config found") || line.startsWith("info: No config found") || line.startsWith("Limit cache size to ") || line.startsWith("Current cache size is ")) {
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
if (line.startsWith("[NEEDLE_progressive] Skipping")) {
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
if (line.startsWith("objc[") || line.includes("Class GNotificationCenterDelegate is implemented in both") || line.includes("This may cause spurious casting failures and mysterious crashes")) {
|
|
477
|
-
continue;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
if (line.startsWith("INFO: Environment variable 'NEEDLE_TOKTX' not set")) {
|
|
481
|
-
continue;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
if (line.startsWith("metalRough: KHR_materials_pbrSpecularGlossiness not found")) {
|
|
485
|
-
continue;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
if (line.startsWith("WARN: Could not validate image type")) {
|
|
489
|
-
continue;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
const progressMatch = line.match(/^info:\s*→\s*(Transform|Compressing)\s+(.+)$/i);
|
|
493
|
-
if (progressMatch) {
|
|
494
|
-
if (progressMatch[1].toLowerCase() === "transform") transformStepCount++;
|
|
495
|
-
if (progressMatch[1].toLowerCase() === "compressing") compressStepCount++;
|
|
496
|
-
updatePipelineProgress(`Build pipeline ${progressMatch[1]} ${progressMatch[2]}`);
|
|
497
|
-
continue;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
if (line.startsWith("info: ← Writing to ") || line.startsWith("info: ← Compressing done in ")) {
|
|
501
|
-
continue;
|
|
361
|
+
// ensure that it doesnt end with a newline
|
|
362
|
+
while (data.endsWith("\n")) data = data.slice(0, -1);
|
|
363
|
+
if (typeof data === "string") {
|
|
364
|
+
if (data.startsWith("ERR:")) {
|
|
365
|
+
console.error(data);
|
|
366
|
+
return;
|
|
502
367
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
if (line.startsWith("ERR:")) {
|
|
507
|
-
needleLog("needle-buildpipeline", line, "error", { dimBody: false, showHeader: false, leadingNewline: true });
|
|
508
|
-
continue;
|
|
368
|
+
else if (data.startsWith("WARN:")) {
|
|
369
|
+
console.warn(data);
|
|
370
|
+
return;
|
|
509
371
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
372
|
+
// Ignore empty lines
|
|
373
|
+
else if (data.trim().length <= 0) {
|
|
374
|
+
return;
|
|
513
375
|
}
|
|
514
|
-
|
|
515
|
-
const shouldDim = line.includes("Loaded compressed file from cache");
|
|
516
|
-
needleLog("needle-buildpipeline", line, "log", { showHeader: false, leadingNewline: true, dimBody: shouldDim });
|
|
517
376
|
}
|
|
377
|
+
log(data);
|
|
518
378
|
}
|
|
519
379
|
proc.stdout?.on('data', onLog);
|
|
520
380
|
proc.stderr?.on('data', onLog);
|
|
521
381
|
return new Promise((resolve, reject) => {
|
|
522
382
|
proc.on('exit', (code) => {
|
|
523
|
-
clearPipelineProgress();
|
|
524
|
-
setTransientLogLineCleaner(null);
|
|
525
|
-
|
|
526
|
-
if (transformStepCount > 0 || compressStepCount > 0) {
|
|
527
|
-
buildPipelineStepSummary = `✓ Pipeline steps: transformed ${transformStepCount} file(s), compressed ${compressStepCount} file(s)`;
|
|
528
|
-
}
|
|
529
|
-
else buildPipelineStepSummary = null;
|
|
530
|
-
|
|
531
383
|
if (code === null || code === undefined) {
|
|
532
384
|
if (options?.verbose) log("Process exited with no code - assuming success");
|
|
533
385
|
code = 0;
|
|
@@ -539,38 +391,3 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
539
391
|
});
|
|
540
392
|
});
|
|
541
393
|
}
|
|
542
|
-
|
|
543
|
-
/** @param {number | undefined} bytes */
|
|
544
|
-
function formatBytes(bytes) {
|
|
545
|
-
const value = Number(bytes || 0);
|
|
546
|
-
if (value < 1024) return `${value} B`;
|
|
547
|
-
if (value < 1024 * 1024) return `${(value / 1024).toFixed(2)} kB`;
|
|
548
|
-
if (value < 1024 * 1024 * 1024) return `${(value / (1024 * 1024)).toFixed(2)} MB`;
|
|
549
|
-
return `${(value / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
/** @param {string | null | undefined} directory */
|
|
553
|
-
function getDirectoryStats(directory) {
|
|
554
|
-
if (!directory || !existsSync(directory)) return { fileCount: 0, totalBytes: 0 };
|
|
555
|
-
let fileCount = 0;
|
|
556
|
-
let totalBytes = 0;
|
|
557
|
-
const entries = readdirSync(directory, { withFileTypes: true });
|
|
558
|
-
for (const entry of entries) {
|
|
559
|
-
const path = directory + "/" + entry.name;
|
|
560
|
-
if (entry.isDirectory()) {
|
|
561
|
-
const stats = getDirectoryStats(path);
|
|
562
|
-
fileCount += stats.fileCount;
|
|
563
|
-
totalBytes += stats.totalBytes;
|
|
564
|
-
}
|
|
565
|
-
else {
|
|
566
|
-
try {
|
|
567
|
-
const stat = statSync(path);
|
|
568
|
-
fileCount += 1;
|
|
569
|
-
totalBytes += stat.size;
|
|
570
|
-
}
|
|
571
|
-
catch {
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
return { fileCount, totalBytes };
|
|
576
|
-
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { closeLogStreams } from '../common/logger.js';
|
|
1
|
+
import { createBuildInfoFile } from '../common/buildinfo.js';
|
|
3
2
|
import { waitForBuildPipelineToFinish } from './build-pipeline.js';
|
|
4
3
|
import { getOutputDirectory } from './config.js';
|
|
5
|
-
import { needleBlue, needleDim, needleLog, needleSupportsColor } from './logging.js';
|
|
6
4
|
|
|
7
5
|
let level = 0;
|
|
8
6
|
|
|
@@ -23,80 +21,22 @@ export const needleBuildInfo = (command, config, userSettings) => {
|
|
|
23
21
|
},
|
|
24
22
|
closeBundle: async () => {
|
|
25
23
|
if (--level > 0) {
|
|
26
|
-
|
|
24
|
+
console.log("[needle-buildinfo] - Skipped because of nested build");
|
|
27
25
|
return;
|
|
28
26
|
}
|
|
29
|
-
const buildDirectory = getOutputDirectory();
|
|
30
|
-
const beforeStats = collectBuildDirectoryStats(buildDirectory);
|
|
31
27
|
const task = waitForBuildPipelineToFinish();
|
|
32
28
|
if (task instanceof Promise) {
|
|
33
|
-
|
|
34
|
-
await task.catch(() => { });
|
|
29
|
+
console.log("[needle-buildinfo] - Waiting for build pipeline to finish");
|
|
30
|
+
await task.catch(() => { }).finally(() => console.log("[needle-buildinfo] - Build pipeline finished!"));
|
|
35
31
|
}
|
|
36
32
|
// wait for gzip
|
|
37
33
|
await delay(500);
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
if (!result?.ok) {
|
|
41
|
-
needleLog("needle-buildinfo", result?.error || "Failed to create build info file", "warn", { dimBody: false });
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const supportsColor = needleSupportsColor();
|
|
45
|
-
const key = (text) => supportsColor ? needleBlue(text) : text;
|
|
46
|
-
const sizeDelta = (result.totalSizeBytes || 0) - (beforeStats.totalSizeBytes || 0);
|
|
47
|
-
const fileDelta = (result.fileCount || 0) - (beforeStats.fileCount || 0);
|
|
48
|
-
const lines = [
|
|
49
|
-
"Build pipeline finished!",
|
|
50
|
-
`${key("Before pipeline")}: ${beforeStats.fileCount} files (${formatBytes(beforeStats.totalSizeBytes)})`,
|
|
51
|
-
`${key("After pipeline")}: ${result.fileCount} files (${formatBytes(result.totalSizeBytes)})`,
|
|
52
|
-
`${key("Delta")}: ${(sizeDelta >= 0 ? "+" : "-") + formatBytes(Math.abs(sizeDelta))}, ${(fileDelta >= 0 ? "+" : "") + fileDelta} files`,
|
|
53
|
-
needleDim(`Begin collecting files in \"${result.buildDirectory}\"`),
|
|
54
|
-
needleDim(`Collected ${result.fileCount} files (${result.totalSizeInMB.toFixed(2)} MB). Writing build info to \"${result.buildInfoPath}\"`),
|
|
55
|
-
needleDim(`Build info file successfully written to \"${result.buildInfoPath}\"`),
|
|
56
|
-
];
|
|
57
|
-
if (closedHandles > 0) lines.push(`Closed ${closedHandles} dangling network handle(s)`);
|
|
58
|
-
needleLog("needle-buildinfo", lines.join("\n"), "log", { dimBody: false });
|
|
59
|
-
closeLogStreams();
|
|
34
|
+
const buildDirectory = getOutputDirectory();
|
|
35
|
+
createBuildInfoFile(buildDirectory);
|
|
60
36
|
}
|
|
61
37
|
}
|
|
62
38
|
}
|
|
63
39
|
|
|
64
|
-
function formatBytes(bytes) {
|
|
65
|
-
const value = Number(bytes || 0);
|
|
66
|
-
if (value < 1024) return `${value} B`;
|
|
67
|
-
if (value < 1024 * 1024) return `${(value / 1024).toFixed(2)} kB`;
|
|
68
|
-
if (value < 1024 * 1024 * 1024) return `${(value / (1024 * 1024)).toFixed(2)} MB`;
|
|
69
|
-
return `${(value / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
40
|
function delay(ms) {
|
|
73
41
|
return new Promise(res => setTimeout(res, ms));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function closeDanglingNetworkHandles() {
|
|
77
|
-
/** @type {() => any[] | undefined} */
|
|
78
|
-
// @ts-ignore private node api
|
|
79
|
-
const getActiveHandles = process._getActiveHandles;
|
|
80
|
-
if (typeof getActiveHandles !== "function") {
|
|
81
|
-
return 0;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
let closed = 0;
|
|
85
|
-
const handles = getActiveHandles() || [];
|
|
86
|
-
for (const handle of handles) {
|
|
87
|
-
if (!handle || handle === process.stdin || handle === process.stdout || handle === process.stderr) continue;
|
|
88
|
-
const hasNetworkShape = typeof handle.destroy === "function" && (typeof handle.remotePort === "number" || typeof handle.remoteAddress === "string");
|
|
89
|
-
if (!hasNetworkShape) continue;
|
|
90
|
-
|
|
91
|
-
if (typeof handle.remotePort === "number" && handle.remotePort > 0) {
|
|
92
|
-
try {
|
|
93
|
-
handle.destroy();
|
|
94
|
-
closed++;
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
// ignore cleanup failures
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return closed;
|
|
102
42
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
import { resolve, join, isAbsolute
|
|
2
|
+
import { resolve, join, isAbsolute } from 'path'
|
|
3
3
|
import { existsSync, statSync, mkdirSync, readdirSync, copyFileSync, mkdir, rmSync } from 'fs';
|
|
4
4
|
import { builtAssetsDirectory, tryLoadProjectConfig } from './config.js';
|
|
5
5
|
import { copyFilesSync } from '../common/files.js';
|
|
6
|
-
import { needleBlue, needleDim, needleLog, needleSupportsColor } from './logging.js';
|
|
7
6
|
|
|
8
7
|
const pluginName = "needle-copy-files";
|
|
9
8
|
|
|
@@ -36,19 +35,11 @@ export const needleCopyFiles = (command, config, userSettings) => {
|
|
|
36
35
|
* @param {import('../types').userSettings} config
|
|
37
36
|
*/
|
|
38
37
|
async function run(buildstep, config) {
|
|
39
|
-
|
|
38
|
+
console.log(`[${pluginName}] - Copy files at ${buildstep}`);
|
|
40
39
|
const copyIncludesFromEngine = config?.copyIncludesFromEngine ?? true;
|
|
41
|
-
const copied = { count: 0, bytes: 0 };
|
|
42
40
|
|
|
43
41
|
const baseDir = process.cwd();
|
|
44
42
|
const override = buildstep === "start";
|
|
45
|
-
const supportsColor = needleSupportsColor();
|
|
46
|
-
const key = (text) => supportsColor ? needleBlue(text) : text;
|
|
47
|
-
const rel = (pathValue) => {
|
|
48
|
-
const result = relative(baseDir, pathValue).replaceAll("\\", "/");
|
|
49
|
-
if (!result || result.length === 0) return ".";
|
|
50
|
-
return result;
|
|
51
|
-
};
|
|
52
43
|
|
|
53
44
|
let assetsDirName = "assets";
|
|
54
45
|
let outdirName = "dist";
|
|
@@ -66,9 +57,9 @@ async function run(buildstep, config) {
|
|
|
66
57
|
// copy include from engine
|
|
67
58
|
const engineIncludeDir = resolve(baseDir, 'node_modules', '@needle-tools', 'engine', 'src', 'include');
|
|
68
59
|
if (existsSync(engineIncludeDir)) {
|
|
69
|
-
|
|
60
|
+
console.log(`[${pluginName}] - Copy engine include to ${baseDir}/include`)
|
|
70
61
|
const projectIncludeDir = resolve(baseDir, 'include');
|
|
71
|
-
copyFilesSync(engineIncludeDir, projectIncludeDir
|
|
62
|
+
copyFilesSync(engineIncludeDir, projectIncludeDir);
|
|
72
63
|
}
|
|
73
64
|
}
|
|
74
65
|
|
|
@@ -94,8 +85,8 @@ async function run(buildstep, config) {
|
|
|
94
85
|
const src = resolve(baseDir, entry);
|
|
95
86
|
const dest = resolvePath(outDir, entry);
|
|
96
87
|
if (existsSync(src) && dest) {
|
|
97
|
-
|
|
98
|
-
copyFilesSync(src, dest, override
|
|
88
|
+
console.log(`[${pluginName}] - Copy ${entry} to ${outdirName}/${entry}`)
|
|
89
|
+
copyFilesSync(src, dest, override);
|
|
99
90
|
}
|
|
100
91
|
}
|
|
101
92
|
}
|
|
@@ -107,41 +98,21 @@ async function run(buildstep, config) {
|
|
|
107
98
|
// ensure that the target directory exists and is cleared if it already exists
|
|
108
99
|
// otherwise we might run into issues where the build pipeline is running for already compressed files
|
|
109
100
|
if (override && existsSync(targetDir)) {
|
|
110
|
-
|
|
101
|
+
console.log(`[${pluginName}] - Clearing target directory \"${targetDir}\"`);
|
|
111
102
|
rmSync(targetDir, { recursive: true, force: true });
|
|
112
103
|
}
|
|
113
|
-
|
|
114
|
-
copyFilesSync(assetsDir, targetDir, override
|
|
104
|
+
console.log(`[${pluginName}] - Copy assets to ${outdirName}/${builtAssetsDirectory()}`)
|
|
105
|
+
copyFilesSync(assetsDir, targetDir, override);
|
|
115
106
|
}
|
|
116
|
-
else
|
|
107
|
+
else console.log(`WARN: No assets directory found. Skipping copy of ${assetsDirName} resolved to ${assetsDir}`)
|
|
117
108
|
|
|
118
109
|
// copy include dir
|
|
119
110
|
const includeDir = resolve(baseDir, 'include');
|
|
120
111
|
if (existsSync(includeDir)) {
|
|
112
|
+
console.log(`[${pluginName}] - Copy include to ${outdirName}/include`)
|
|
121
113
|
const targetDir = resolve(outDir, 'include');
|
|
122
|
-
|
|
123
|
-
copyFilesSync(includeDir, targetDir, override, copied);
|
|
114
|
+
copyFilesSync(includeDir, targetDir, override);
|
|
124
115
|
}
|
|
125
|
-
|
|
126
|
-
logLines.push(`${key("Copied files")}: ${copied.count}`);
|
|
127
|
-
logLines.push(`${key("Copied size")}: ${formatBytes(copied.bytes || 0)}`);
|
|
128
|
-
if (buildstep === "end") {
|
|
129
|
-
logLines.push("");
|
|
130
|
-
logLines.push("✨ Happy creating! 🌵");
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
needleLog(pluginName, logLines.join("\n"), "log", {
|
|
134
|
-
leadingNewline: buildstep === "end",
|
|
135
|
-
dimBody: false,
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function formatBytes(bytes) {
|
|
140
|
-
const value = Number(bytes || 0);
|
|
141
|
-
if (value < 1024) return `${value} B`;
|
|
142
|
-
if (value < 1024 * 1024) return `${(value / 1024).toFixed(2)} kB`;
|
|
143
|
-
if (value < 1024 * 1024 * 1024) return `${(value / (1024 * 1024)).toFixed(2)} MB`;
|
|
144
|
-
return `${(value / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
145
116
|
}
|
|
146
117
|
|
|
147
118
|
/** resolves relative or absolute paths to a path inside the out directory
|