@needle-tools/engine 4.14.0 → 4.15.0-next.f391a30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/components.needle.json +1 -1
- package/dist/{gltf-progressive-BttGBXw6.umd.cjs → gltf-progressive-CMwJPwEt.umd.cjs} +1 -1
- package/dist/{gltf-progressive-Bm_6aEi4.js → gltf-progressive-CTlvpS3A.js} +1 -1
- package/dist/{gltf-progressive-T5WKTux5.min.js → gltf-progressive-DYL3SLVb.min.js} +1 -1
- package/dist/materialx-4jJLLe9Q.js +4174 -0
- package/dist/materialx-Bt9FHwco.min.js +158 -0
- package/dist/materialx-NDD0y4JY.umd.cjs +158 -0
- package/dist/{needle-engine.bundle-COL2Bar3.umd.cjs → needle-engine.bundle-C1BFRZDF.umd.cjs} +150 -140
- package/dist/{needle-engine.bundle-Z_gAD7Kg.js → needle-engine.bundle-DB4kLWO_.js} +6651 -6400
- package/dist/{needle-engine.bundle-NolzHLqO.min.js → needle-engine.bundle-DsTdfmeb.min.js} +151 -141
- package/dist/needle-engine.d.ts +345 -88
- package/dist/needle-engine.js +322 -322
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-06AXuvdv.min.js → postprocessing-BN-f4viE.min.js} +1 -1
- package/dist/{postprocessing-CPDcA21P.umd.cjs → postprocessing-DYmYOVm4.umd.cjs} +1 -1
- package/dist/{postprocessing-CI2x8Cln.js → postprocessing-De9ZpJrk.js} +1 -1
- package/dist/{three-examples-BMmNgNCN.umd.cjs → three-examples-BHqRVpO_.umd.cjs} +12 -12
- package/dist/{three-examples-CMYCd5nH.js → three-examples-C0ZCCA_K.js} +182 -192
- package/dist/{three-examples-CQl1fFZp.min.js → three-examples-DmTY8tGr.min.js} +14 -14
- package/lib/engine/api.d.ts +0 -2
- package/lib/engine/api.js +0 -2
- 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 +77 -0
- package/lib/engine/engine_accessibility.js +162 -0
- package/lib/engine/engine_accessibility.js.map +1 -0
- package/lib/engine/engine_context.d.ts +2 -0
- package/lib/engine/engine_context.js +8 -1
- 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 +7 -2
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_materialpropertyblock.d.ts +90 -4
- package/lib/engine/engine_materialpropertyblock.js +97 -7
- package/lib/engine/engine_materialpropertyblock.js.map +1 -1
- package/lib/engine/engine_math.d.ts +34 -1
- package/lib/engine/engine_math.js +34 -1
- package/lib/engine/engine_math.js.map +1 -1
- package/lib/engine/engine_networking.js +1 -1
- package/lib/engine/engine_networking.js.map +1 -1
- package/lib/engine/engine_types.d.ts +2 -0
- package/lib/engine/engine_types.js +2 -0
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/engine_utils.js +2 -2
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/export/gltf/EXT_mesh_gpu_instancing_exporter.js.map +1 -0
- package/lib/engine/export/gltf/index.js +1 -1
- package/lib/engine/export/gltf/index.js.map +1 -1
- package/lib/engine/webcomponents/icons.js +3 -0
- package/lib/engine/webcomponents/icons.js.map +1 -1
- package/lib/engine/webcomponents/logo-element.d.ts +7 -3
- package/lib/engine/webcomponents/logo-element.js +21 -1
- 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 +10 -7
- package/lib/engine/webcomponents/needle menu/needle-menu.js +14 -4
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-button.d.ts +37 -11
- package/lib/engine/webcomponents/needle-button.js +42 -11
- package/lib/engine/webcomponents/needle-button.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.ar-overlay.js +10 -1
- package/lib/engine/webcomponents/needle-engine.ar-overlay.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +13 -2
- package/lib/engine/webcomponents/needle-engine.js +23 -3
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine-components/Component.d.ts +1 -2
- package/lib/engine-components/Component.js +1 -3
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/DragControls.d.ts +1 -0
- package/lib/engine-components/DragControls.js +21 -0
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/NeedleMenu.d.ts +2 -0
- package/lib/engine-components/NeedleMenu.js +2 -0
- package/lib/engine-components/NeedleMenu.js.map +1 -1
- package/lib/engine-components/Networking.d.ts +28 -3
- package/lib/engine-components/Networking.js +28 -3
- package/lib/engine-components/Networking.js.map +1 -1
- package/lib/engine-components/ReflectionProbe.d.ts +25 -2
- package/lib/engine-components/ReflectionProbe.js +46 -2
- package/lib/engine-components/ReflectionProbe.js.map +1 -1
- package/lib/engine-components/Skybox.js +4 -2
- 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/BehaviourComponents.d.ts +15 -0
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +77 -0
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.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/engine-components/ui/Button.d.ts +1 -0
- package/lib/engine-components/ui/Button.js +11 -0
- package/lib/engine-components/ui/Button.js.map +1 -1
- package/lib/engine-components/ui/Text.d.ts +1 -0
- package/lib/engine-components/ui/Text.js +11 -0
- package/lib/engine-components/ui/Text.js.map +1 -1
- package/package.json +18 -14
- package/plugins/common/buildinfo.js +46 -10
- package/plugins/common/files.js +2 -1
- package/plugins/common/license.js +144 -69
- package/plugins/common/logger.js +172 -11
- package/plugins/common/needle-engine-skill.md +175 -0
- package/plugins/common/worker.js +5 -4
- package/plugins/types/userconfig.d.ts +40 -2
- package/plugins/vite/ai.js +71 -0
- package/plugins/vite/alias.js +6 -5
- package/plugins/vite/asap.js +6 -5
- package/plugins/vite/build-pipeline.js +224 -41
- package/plugins/vite/buildinfo.js +66 -6
- package/plugins/vite/copyfiles.js +41 -12
- package/plugins/vite/custom-element-data.js +26 -16
- package/plugins/vite/defines.js +8 -5
- package/plugins/vite/dependencies.js +16 -10
- package/plugins/vite/dependency-watcher.js +35 -7
- package/plugins/vite/drop-client.js +7 -5
- package/plugins/vite/drop.js +16 -14
- package/plugins/vite/editor-connection.js +18 -16
- package/plugins/vite/imports-logger.js +12 -2
- package/plugins/vite/index.js +8 -3
- package/plugins/vite/local-files-analysis.js +789 -0
- package/plugins/vite/local-files-core.js +992 -0
- package/plugins/vite/local-files-internals.js +28 -0
- package/plugins/vite/local-files-types.d.ts +111 -0
- package/plugins/vite/local-files-utils.js +359 -0
- package/plugins/vite/local-files.js +2 -441
- package/plugins/vite/logger.client.js +45 -35
- package/plugins/vite/logger.js +6 -3
- package/plugins/vite/logging.js +129 -0
- package/plugins/vite/meta.js +18 -4
- package/plugins/vite/needle-app.js +4 -3
- package/plugins/vite/peer.js +2 -1
- package/plugins/vite/pwa.js +33 -17
- package/plugins/vite/reload.js +24 -2
- package/src/engine/api.ts +0 -3
- package/src/engine/debug/debug.ts +1 -1
- package/src/engine/debug/debug_spatial_console.ts +5 -1
- package/src/engine/engine_accessibility.ts +198 -0
- package/src/engine/engine_context.ts +10 -1
- package/src/engine/engine_create_objects.ts +1 -1
- package/src/engine/engine_gizmos.ts +9 -5
- package/src/engine/engine_license.ts +7 -2
- package/src/engine/engine_materialpropertyblock.ts +102 -11
- package/src/engine/engine_math.ts +34 -1
- package/src/engine/engine_networking.ts +1 -1
- package/src/engine/engine_types.ts +5 -0
- package/src/engine/engine_utils.ts +2 -2
- package/src/engine/export/gltf/index.ts +1 -1
- package/src/engine/webcomponents/icons.ts +3 -0
- package/src/engine/webcomponents/logo-element.ts +24 -4
- package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +6 -2
- package/src/engine/webcomponents/needle menu/needle-menu.ts +23 -11
- package/src/engine/webcomponents/needle-button.ts +44 -13
- package/src/engine/webcomponents/needle-engine.ar-overlay.ts +13 -2
- package/src/engine/webcomponents/needle-engine.ts +31 -8
- package/src/engine-components/Component.ts +2 -5
- package/src/engine-components/DragControls.ts +29 -4
- package/src/engine-components/NeedleMenu.ts +5 -3
- package/src/engine-components/Networking.ts +29 -4
- package/src/engine-components/ReflectionProbe.ts +52 -9
- package/src/engine-components/Skybox.ts +4 -2
- 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/BehaviourComponents.ts +108 -32
- package/src/engine-components/export/usdz/extensions/behavior/PhysicsExtension.ts +2 -2
- package/src/engine-components/ui/Button.ts +12 -0
- package/src/engine-components/ui/Text.ts +13 -0
- package/dist/materialx-CJyQZtjt.min.js +0 -90
- package/dist/materialx-DMs1E08Z.js +0 -4636
- package/dist/materialx-DaKKOoVk.umd.cjs +0 -90
- package/lib/engine/engine_test_utils.d.ts +0 -39
- package/lib/engine/engine_test_utils.js +0 -84
- package/lib/engine/engine_test_utils.js.map +0 -1
- package/lib/include/three/EXT_mesh_gpu_instancing_exporter.js.map +0 -1
- package/src/engine/engine_test_utils.ts +0 -109
- package/src/include/draco/draco_decoder.js +0 -34
- package/src/include/draco/draco_decoder.wasm +0 -0
- package/src/include/draco/draco_wasm_wrapper.js +0 -117
- package/src/include/ktx2/basis_transcoder.js +0 -19
- package/src/include/ktx2/basis_transcoder.wasm +0 -0
- package/src/include/needle/arial-msdf.json +0 -1472
- package/src/include/needle/arial.png +0 -0
- package/src/include/needle/poweredbyneedle.webp +0 -0
- /package/lib/{include/three → engine/export/gltf}/EXT_mesh_gpu_instancing_exporter.d.ts +0 -0
- /package/lib/{include/three → engine/export/gltf}/EXT_mesh_gpu_instancing_exporter.js +0 -0
- /package/src/{include/three → engine/export/gltf}/EXT_mesh_gpu_instancing_exporter.js +0 -0
package/plugins/vite/asap.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import { tryParseNeedleEngineSrcAttributeFromHtml } from '../common/needle-engine.js';
|
|
4
4
|
import { preloadScriptPaths } from './dependencies.js';
|
|
5
5
|
import { makeFilesLocalIsEnabled } from './local-files.js';
|
|
6
|
+
import { needleLog } from './logging.js';
|
|
6
7
|
|
|
7
8
|
const code = `import('@needle-tools/engine/src/asap/needle-asap.ts');`
|
|
8
9
|
|
|
@@ -30,7 +31,7 @@ export const needleAsap = async (command, config, userSettings) => {
|
|
|
30
31
|
logoSvg = assets.NEEDLE_LOGO_SVG_URL;
|
|
31
32
|
}
|
|
32
33
|
catch (err) {
|
|
33
|
-
|
|
34
|
+
needleLog("needle:asap", "Could not load needle logo svg: " + err.message, "warn", { dimBody: false });
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
/** @type {import("vite").ResolvedConfig | null} */
|
|
@@ -122,7 +123,7 @@ function fixMainTs() {
|
|
|
122
123
|
if (existsSync(mainTsFilePath)) {
|
|
123
124
|
let code = readFileSync(mainTsFilePath, 'utf-8');
|
|
124
125
|
if (code.includes('import \"@needle-tools/engine\"')) {
|
|
125
|
-
|
|
126
|
+
needleLog("needle:asap", "Changed main.ts and replaced needle engine import with async import", "log", { dimBody: false });
|
|
126
127
|
code = code.replace(/import \"@needle-tools\/engine\"/g, 'import("@needle-tools/engine") /* async import of needle engine */');
|
|
127
128
|
writeFileSync(mainTsFilePath, code);
|
|
128
129
|
}
|
|
@@ -151,7 +152,7 @@ function generateScriptPreloadLinks(_config, tags) {
|
|
|
151
152
|
}
|
|
152
153
|
}
|
|
153
154
|
catch (err) {
|
|
154
|
-
|
|
155
|
+
needleLog("needle:asap", "Error generating script preload links: " + err.message, "error", { dimBody: false });
|
|
155
156
|
}
|
|
156
157
|
}
|
|
157
158
|
|
|
@@ -201,11 +202,11 @@ function generateGltfPreloadLinks(config, html, tags) {
|
|
|
201
202
|
filepath = decodeURIComponent(filepath);
|
|
202
203
|
const fullpath = path.join(process.cwd(), filepath);
|
|
203
204
|
if (!existsSync(fullpath)) {
|
|
204
|
-
|
|
205
|
+
needleLog("needle:asap", `Could not insert head preload link: file not found at \"${filepath}\"`, "warn", { dimBody: false });
|
|
205
206
|
continue;
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
|
-
|
|
209
|
+
needleLog("needle:asap", `Insert head glTF preload link: ${value}`);
|
|
209
210
|
insertPreloadLink(tags, value, "model/gltf+json");
|
|
210
211
|
}
|
|
211
212
|
}
|
|
@@ -1,9 +1,14 @@
|
|
|
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, writeFileSync } from 'fs';
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'fs';
|
|
5
|
+
import { relative } from 'path';
|
|
5
6
|
import { copyFilesSync } from '../common/files.js';
|
|
6
7
|
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__:";
|
|
7
12
|
|
|
8
13
|
/**
|
|
9
14
|
* @param {import('../types').userSettings} config
|
|
@@ -35,10 +40,12 @@ env:
|
|
|
35
40
|
// see https://linear.app/needle/issue/NE-3798
|
|
36
41
|
|
|
37
42
|
|
|
38
|
-
/** @type {Promise<
|
|
43
|
+
/** @type {Promise<void>|null} */
|
|
39
44
|
let buildPipelineTask;
|
|
40
45
|
/** @type {null | {tempDirectory:string, outputDirectory:string}} */
|
|
41
46
|
let buildPipelineTaskResults = null;
|
|
47
|
+
/** @type {null | string} */
|
|
48
|
+
let buildPipelineStepSummary = null;
|
|
42
49
|
|
|
43
50
|
export function waitForBuildPipelineToFinish() {
|
|
44
51
|
return buildPipelineTask;
|
|
@@ -100,7 +107,9 @@ export const needleBuildPipeline = async (command, config, userSettings) => {
|
|
|
100
107
|
}
|
|
101
108
|
|
|
102
109
|
if (!shouldRun) {
|
|
103
|
-
|
|
110
|
+
if (command === "build") {
|
|
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
|
+
}
|
|
104
113
|
await new Promise((resolve, _) => setTimeout(resolve, 1000));
|
|
105
114
|
return null;
|
|
106
115
|
}
|
|
@@ -113,6 +122,7 @@ export const needleBuildPipeline = async (command, config, userSettings) => {
|
|
|
113
122
|
|
|
114
123
|
const verboseOutput = userSettings?.buildPipeline?.verbose || false;
|
|
115
124
|
let taskHasCompleted = false;
|
|
125
|
+
let taskSucceeded = false;
|
|
116
126
|
|
|
117
127
|
return {
|
|
118
128
|
name: 'needle:buildpipeline',
|
|
@@ -148,26 +158,22 @@ export const needleBuildPipeline = async (command, config, userSettings) => {
|
|
|
148
158
|
log("Build pipeline already running...");
|
|
149
159
|
return;
|
|
150
160
|
}
|
|
151
|
-
let taskSucceeded = false;
|
|
152
161
|
// start the compression process once vite is done copying the files
|
|
153
162
|
buildPipelineTask = invokeBuildPipeline(userSettings, { verbose: verboseOutput })
|
|
154
163
|
.then((res) => {
|
|
155
164
|
if (verboseOutput) log("Build pipeline task result:", res);
|
|
156
165
|
taskSucceeded = res;
|
|
157
166
|
})
|
|
158
|
-
.catch(err => {
|
|
159
|
-
|
|
167
|
+
.catch((/** @type {{ message?: string }} */ err) => {
|
|
168
|
+
needleLog("needle-buildpipeline", "- Error during build pipeline: " + err.message, "error");
|
|
160
169
|
if (verboseOutput) log("Error details:", err);
|
|
161
170
|
})
|
|
162
171
|
.finally(() => {
|
|
163
172
|
taskHasCompleted = true;
|
|
164
173
|
if (!taskSucceeded) {
|
|
165
|
-
|
|
174
|
+
needleLog("needle-buildpipeline", "- Build pipeline task did not succeed.", "error");
|
|
166
175
|
throw new Error("[needle-buildpipeline] - Build pipeline failed. Please check the logs above for more information.");
|
|
167
176
|
}
|
|
168
|
-
else {
|
|
169
|
-
log("Finished successfully");
|
|
170
|
-
}
|
|
171
177
|
});
|
|
172
178
|
}
|
|
173
179
|
},
|
|
@@ -175,21 +181,34 @@ export const needleBuildPipeline = async (command, config, userSettings) => {
|
|
|
175
181
|
if (!buildPipelineTask) {
|
|
176
182
|
return;
|
|
177
183
|
}
|
|
178
|
-
if (!taskHasCompleted) {
|
|
179
|
-
log("Waiting for build pipeline to finish...");
|
|
180
|
-
}
|
|
181
184
|
// // this is the last hook that is called, so we can wait for the task to finish here
|
|
182
185
|
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
|
+
}
|
|
183
193
|
// Copy the results to their final output directory.
|
|
184
194
|
if (buildPipelineTaskResults != null) {
|
|
185
|
-
|
|
186
|
-
const
|
|
195
|
+
const supportsColor = needleSupportsColor();
|
|
196
|
+
const key = (/** @type {string} */ text) => supportsColor ? needleBlue(text) : text;
|
|
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 }
|
|
187
201
|
copyFilesSync(buildPipelineTaskResults.tempDirectory, buildPipelineTaskResults.outputDirectory, true, ctx);
|
|
188
|
-
|
|
202
|
+
lines.push(`${key("Copied files")}: ${ctx.count}`);
|
|
203
|
+
lines.push(`${key("Data moved")}: ${formatBytes(moved.totalBytes)} (${moved.fileCount} files)`);
|
|
189
204
|
}
|
|
190
205
|
else {
|
|
191
|
-
|
|
206
|
+
lines.push("No files to copy - build pipeline did not run or did not finish successfully");
|
|
192
207
|
}
|
|
208
|
+
if (lines.length > 0) {
|
|
209
|
+
needleLog("needle-buildpipeline", lines.join("\n"), "log", { dimBody: false });
|
|
210
|
+
}
|
|
211
|
+
buildPipelineStepSummary = null;
|
|
193
212
|
});
|
|
194
213
|
},
|
|
195
214
|
}
|
|
@@ -216,21 +235,31 @@ async function fixPackageJson(packageJsonPath) {
|
|
|
216
235
|
writeFileSync(packageJsonPath, fixed);
|
|
217
236
|
}
|
|
218
237
|
|
|
219
|
-
/** @param {
|
|
238
|
+
/** @param {...unknown} args */
|
|
220
239
|
function log(...args) {
|
|
221
|
-
|
|
240
|
+
needleLog("needle-buildpipeline", args.join(" "));
|
|
222
241
|
}
|
|
223
|
-
/** @param {
|
|
242
|
+
/** @param {...unknown} args */
|
|
224
243
|
function warn(...args) {
|
|
225
|
-
|
|
244
|
+
needleLog("needle-buildpipeline", args.join(" "), "warn");
|
|
226
245
|
}
|
|
227
246
|
|
|
247
|
+
/**
|
|
248
|
+
* @typedef {{ event?: string, phase?: string, target?: string, message?: string, level?: string }} BuildPipelinePayload
|
|
249
|
+
*/
|
|
228
250
|
/**
|
|
229
251
|
* @param {import('../types').userSettings} opts
|
|
230
252
|
* @param {{verbose?:boolean}} [options]
|
|
231
253
|
* @returns {Promise<boolean>}
|
|
232
254
|
*/
|
|
233
255
|
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
|
+
|
|
234
263
|
const installPath = "node_modules/@needle-tools/gltf-build-pipeline";
|
|
235
264
|
const fullInstallPath = process.cwd() + "/" + installPath;
|
|
236
265
|
const existsLocally = existsSync(fullInstallPath);
|
|
@@ -257,7 +286,7 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
257
286
|
log("Max wait time exceeded - aborting...");
|
|
258
287
|
return Promise.resolve(false);
|
|
259
288
|
}
|
|
260
|
-
if (iteration <= 0)
|
|
289
|
+
if (iteration <= 0) needleLog("needle-buildpipeline", `Waiting for output directory to be created... (${outputDirectory})`, "log", { leadingNewline: true });
|
|
261
290
|
return delay(1000).then(() => waitForOutputDirectory(iteration + 1));
|
|
262
291
|
}
|
|
263
292
|
if (options?.verbose) log(`Output directory found after ${iteration} iteration(s) at "${outputDirectory}" - continuing...`);
|
|
@@ -268,7 +297,19 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
268
297
|
return false;
|
|
269
298
|
}
|
|
270
299
|
const files = readdirSync(outputDirectory).filter(f => f.endsWith(".glb") || f.endsWith(".gltf") || f.endsWith(".vrm") || f.endsWith(".fbx"));
|
|
271
|
-
|
|
300
|
+
const filesBytes = files.reduce((total, file) => {
|
|
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 });
|
|
272
313
|
|
|
273
314
|
/** @type {null | ChildProcess} */
|
|
274
315
|
let proc = null;
|
|
@@ -290,7 +331,6 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
290
331
|
// this is so that processes like sveltekit-static-adapter can run first and does not override already compressed files
|
|
291
332
|
const tempOutputPath = process.cwd() + "/node_modules/.needle/build-pipeline/output";
|
|
292
333
|
if (existsSync(tempOutputPath)) {
|
|
293
|
-
log("Removing temporary output directory at " + tempOutputPath);
|
|
294
334
|
rmSync(tempOutputPath, { recursive: true, force: true });
|
|
295
335
|
}
|
|
296
336
|
mkdirSync(tempOutputPath, { recursive: true });
|
|
@@ -307,6 +347,7 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
307
347
|
|
|
308
348
|
// allow running the build pipeline in the cloud. It requires and access token to be set in the vite.config.js
|
|
309
349
|
// this can be set via e.g. process.env.NEEDLE_CLOUD_TOKEN
|
|
350
|
+
const commandEnv = { ...process.env, NEEDLE_PIPELINE_STRUCTURED_LOGS: "1" };
|
|
310
351
|
if (runInCloud) {
|
|
311
352
|
if (!cloudAccessToken || !(typeof cloudAccessToken === "string") || cloudAccessToken.length <= 0) {
|
|
312
353
|
throw new Error("No cloud access token configured. Please set it via process.env.NEEDLE_CLOUD_TOKEN or in the vite.config.js");
|
|
@@ -328,12 +369,12 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
328
369
|
console.log("\n");
|
|
329
370
|
const obfuscatedToken = `${cloudAccessToken.slice(0, 2)}*****${cloudAccessToken.slice(-2)}`;
|
|
330
371
|
log(`Running compression in cloud ⛅ using access token: ${obfuscatedToken}`);
|
|
331
|
-
proc = exec(cmd);
|
|
372
|
+
proc = exec(cmd, { env: commandEnv });
|
|
332
373
|
}
|
|
333
374
|
else if (existsLocally) {
|
|
334
375
|
const cmd = `needle-gltf transform "${outputDirectory}" \"${tempOutputPath}\"`;
|
|
335
376
|
log("Running command \"" + cmd + "\" at " + process.cwd() + "...");
|
|
336
|
-
proc = exec(cmd, { cwd: installPath });
|
|
377
|
+
proc = exec(cmd, { cwd: installPath, env: commandEnv });
|
|
337
378
|
}
|
|
338
379
|
else {
|
|
339
380
|
// First check if the user passed in a specific version to use via the vite config
|
|
@@ -353,33 +394,140 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
353
394
|
|
|
354
395
|
const cmd = `npx --yes @needle-tools/gltf-build-pipeline@${version} transform "${outputDirectory}" \"${tempOutputPath}\"`;
|
|
355
396
|
log(`Running compression locally using version '${version}'`);
|
|
356
|
-
proc = exec(cmd);
|
|
397
|
+
proc = exec(cmd, { env: commandEnv });
|
|
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;
|
|
357
418
|
}
|
|
358
|
-
|
|
419
|
+
|
|
420
|
+
setTransientLogLineCleaner(() => clearPipelineProgress());
|
|
421
|
+
|
|
422
|
+
/** @param {Buffer|string} data */
|
|
359
423
|
function onLog(data) {
|
|
360
424
|
if (data.length <= 0) return;
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
if (
|
|
365
|
-
|
|
366
|
-
|
|
425
|
+
const str = String(data).replace(/\r/g, "");
|
|
426
|
+
const lines = str.split("\n");
|
|
427
|
+
for (let line of lines) {
|
|
428
|
+
if (!line?.trim().length) continue;
|
|
429
|
+
|
|
430
|
+
if (line.startsWith(PIPELINE_STRUCTURED_LOG_PREFIX)) {
|
|
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
|
+
}
|
|
367
466
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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;
|
|
371
470
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
471
|
+
|
|
472
|
+
if (line.startsWith("[NEEDLE_progressive] Skipping")) {
|
|
473
|
+
continue;
|
|
375
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;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
clearPipelineProgress();
|
|
505
|
+
|
|
506
|
+
if (line.startsWith("ERR:")) {
|
|
507
|
+
needleLog("needle-buildpipeline", line, "error", { dimBody: false, showHeader: false, leadingNewline: true });
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
else if (line.startsWith("WARN:")) {
|
|
511
|
+
needleLog("needle-buildpipeline", line, "warn", { dimBody: false, showHeader: false, leadingNewline: true });
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const shouldDim = line.includes("Loaded compressed file from cache");
|
|
516
|
+
needleLog("needle-buildpipeline", line, "log", { showHeader: false, leadingNewline: true, dimBody: shouldDim });
|
|
376
517
|
}
|
|
377
|
-
log(data);
|
|
378
518
|
}
|
|
379
519
|
proc.stdout?.on('data', onLog);
|
|
380
520
|
proc.stderr?.on('data', onLog);
|
|
381
521
|
return new Promise((resolve, reject) => {
|
|
382
522
|
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
|
+
|
|
383
531
|
if (code === null || code === undefined) {
|
|
384
532
|
if (options?.verbose) log("Process exited with no code - assuming success");
|
|
385
533
|
code = 0;
|
|
@@ -391,3 +539,38 @@ async function invokeBuildPipeline(opts, options = {}) {
|
|
|
391
539
|
});
|
|
392
540
|
});
|
|
393
541
|
}
|
|
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,6 +1,8 @@
|
|
|
1
|
-
import { createBuildInfoFile } from '../common/buildinfo.js';
|
|
1
|
+
import { collectBuildDirectoryStats, createBuildInfoFile } from '../common/buildinfo.js';
|
|
2
|
+
import { closeLogStreams } from '../common/logger.js';
|
|
2
3
|
import { waitForBuildPipelineToFinish } from './build-pipeline.js';
|
|
3
4
|
import { getOutputDirectory } from './config.js';
|
|
5
|
+
import { needleBlue, needleDim, needleLog, needleSupportsColor } from './logging.js';
|
|
4
6
|
|
|
5
7
|
let level = 0;
|
|
6
8
|
|
|
@@ -21,22 +23,80 @@ export const needleBuildInfo = (command, config, userSettings) => {
|
|
|
21
23
|
},
|
|
22
24
|
closeBundle: async () => {
|
|
23
25
|
if (--level > 0) {
|
|
24
|
-
|
|
26
|
+
needleLog("needle-buildinfo", "Skipped because of nested build");
|
|
25
27
|
return;
|
|
26
28
|
}
|
|
29
|
+
const buildDirectory = getOutputDirectory();
|
|
30
|
+
const beforeStats = collectBuildDirectoryStats(buildDirectory);
|
|
27
31
|
const task = waitForBuildPipelineToFinish();
|
|
28
32
|
if (task instanceof Promise) {
|
|
29
|
-
|
|
30
|
-
await task.catch(() => { })
|
|
33
|
+
needleLog("needle-buildinfo", "Waiting for build pipeline to finish");
|
|
34
|
+
await task.catch(() => { });
|
|
31
35
|
}
|
|
32
36
|
// wait for gzip
|
|
33
37
|
await delay(500);
|
|
34
|
-
const
|
|
35
|
-
|
|
38
|
+
const result = createBuildInfoFile(buildDirectory, { log: false });
|
|
39
|
+
const closedHandles = closeDanglingNetworkHandles();
|
|
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();
|
|
36
60
|
}
|
|
37
61
|
}
|
|
38
62
|
}
|
|
39
63
|
|
|
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
|
+
|
|
40
72
|
function delay(ms) {
|
|
41
73
|
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;
|
|
42
102
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
import { resolve, join, isAbsolute } from 'path'
|
|
2
|
+
import { resolve, join, isAbsolute, relative } 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';
|
|
6
7
|
|
|
7
8
|
const pluginName = "needle-copy-files";
|
|
8
9
|
|
|
@@ -35,11 +36,19 @@ export const needleCopyFiles = (command, config, userSettings) => {
|
|
|
35
36
|
* @param {import('../types').userSettings} config
|
|
36
37
|
*/
|
|
37
38
|
async function run(buildstep, config) {
|
|
38
|
-
|
|
39
|
+
const logLines = [needleDim(`Copy files at ${buildstep}`)];
|
|
39
40
|
const copyIncludesFromEngine = config?.copyIncludesFromEngine ?? true;
|
|
41
|
+
const copied = { count: 0, bytes: 0 };
|
|
40
42
|
|
|
41
43
|
const baseDir = process.cwd();
|
|
42
44
|
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
|
+
};
|
|
43
52
|
|
|
44
53
|
let assetsDirName = "assets";
|
|
45
54
|
let outdirName = "dist";
|
|
@@ -57,9 +66,9 @@ async function run(buildstep, config) {
|
|
|
57
66
|
// copy include from engine
|
|
58
67
|
const engineIncludeDir = resolve(baseDir, 'node_modules', '@needle-tools', 'engine', 'src', 'include');
|
|
59
68
|
if (existsSync(engineIncludeDir)) {
|
|
60
|
-
|
|
69
|
+
logLines.push(`Engine include: ${rel(engineIncludeDir)} -> ${rel(resolve(baseDir, 'include'))}`);
|
|
61
70
|
const projectIncludeDir = resolve(baseDir, 'include');
|
|
62
|
-
copyFilesSync(engineIncludeDir, projectIncludeDir);
|
|
71
|
+
copyFilesSync(engineIncludeDir, projectIncludeDir, true, copied);
|
|
63
72
|
}
|
|
64
73
|
}
|
|
65
74
|
|
|
@@ -85,8 +94,8 @@ async function run(buildstep, config) {
|
|
|
85
94
|
const src = resolve(baseDir, entry);
|
|
86
95
|
const dest = resolvePath(outDir, entry);
|
|
87
96
|
if (existsSync(src) && dest) {
|
|
88
|
-
|
|
89
|
-
copyFilesSync(src, dest, override);
|
|
97
|
+
logLines.push(`Configured copy: ${rel(src)} -> ${rel(dest)}`);
|
|
98
|
+
copyFilesSync(src, dest, override, copied);
|
|
90
99
|
}
|
|
91
100
|
}
|
|
92
101
|
}
|
|
@@ -98,21 +107,41 @@ async function run(buildstep, config) {
|
|
|
98
107
|
// ensure that the target directory exists and is cleared if it already exists
|
|
99
108
|
// otherwise we might run into issues where the build pipeline is running for already compressed files
|
|
100
109
|
if (override && existsSync(targetDir)) {
|
|
101
|
-
|
|
110
|
+
logLines.push(needleDim(`Clearing target directory "${rel(targetDir)}"`));
|
|
102
111
|
rmSync(targetDir, { recursive: true, force: true });
|
|
103
112
|
}
|
|
104
|
-
|
|
105
|
-
copyFilesSync(assetsDir, targetDir, override);
|
|
113
|
+
logLines.push(needleDim(`Assets: ${rel(assetsDir)} -> ${rel(targetDir)}`));
|
|
114
|
+
copyFilesSync(assetsDir, targetDir, override, copied);
|
|
106
115
|
}
|
|
107
|
-
else
|
|
116
|
+
else logLines.push(`No assets directory found. Skipping copy of ${assetsDirName} resolved to ${rel(assetsDir)}`);
|
|
108
117
|
|
|
109
118
|
// copy include dir
|
|
110
119
|
const includeDir = resolve(baseDir, 'include');
|
|
111
120
|
if (existsSync(includeDir)) {
|
|
112
|
-
console.log(`[${pluginName}] - Copy include to ${outdirName}/include`)
|
|
113
121
|
const targetDir = resolve(outDir, 'include');
|
|
114
|
-
|
|
122
|
+
logLines.push(needleDim(`Include: ${rel(includeDir)} -> ${rel(targetDir)}`));
|
|
123
|
+
copyFilesSync(includeDir, targetDir, override, copied);
|
|
115
124
|
}
|
|
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`;
|
|
116
145
|
}
|
|
117
146
|
|
|
118
147
|
/** resolves relative or absolute paths to a path inside the out directory
|