@@ -1,85 +1,21 @@
|
|
1
|
-
import { ChildProcess, exec } from 'child_process';
|
2
|
-
import { getOutputDirectory, loadConfig } from './config.js';
|
3
|
-
import { existsSync,
|
4
|
-
import { copyFilesSync } from '../common/files.js';
|
5
|
-
import { delay } from '../common/timers.js';
|
1
|
+
import { ChildProcess, exec, execSync, spawn } from 'child_process';
|
2
|
+
import { getOutputDirectory, loadConfig, tryLoadProjectConfig } from './config.js';
|
3
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'fs';
|
6
4
|
|
7
|
-
/**
|
8
|
-
* @param {import('../types').userSettings} config
|
9
|
-
* @returns {boolean}
|
10
|
-
*/
|
11
|
-
function validateCloudBuildConfiguration(config) {
|
12
|
-
if (config.buildPipeline != undefined) {
|
13
|
-
if (process.env.CI) {
|
14
|
-
if ((process.env.NEEDLE_CLOUD_TOKEN == undefined || process.env.NEEDLE_CLOUD_TOKEN.length <= 0)) {
|
15
|
-
const isGithubAction = process.env.CI && process.env.GITHUB_ACTION;
|
16
|
-
let msg = `Missing Needle Cloud access token. Please set your Needle Cloud token via \`process.env.NEEDLE_CLOUD_TOKEN\`.`;
|
17
|
-
if (isGithubAction) {
|
18
|
-
msg += `
|
19
|
-
Make sure to pass the token as a secret to the Github action.
|
20
|
-
For this you may have to modify your .github workflow yaml file to include the following code:
|
21
|
-
env:
|
22
|
-
- NEEDLE_CLOUD_TOKEN: \${{ secrets.NEEDLE_CLOUD_TOKEN }}
|
23
|
-
`;
|
24
|
-
let error = `${msg}`;
|
25
|
-
throw new Error(error);
|
26
|
-
}
|
27
|
-
}
|
28
|
-
}
|
29
|
-
}
|
30
|
-
|
31
|
-
return true;
|
32
|
-
}
|
33
|
-
|
34
5
|
// see https://linear.app/needle/issue/NE-3798
|
35
6
|
|
7
|
+
export let buildPipelineTask;
|
36
8
|
|
37
|
-
/** @type {Promise<any>|null} */
|
38
|
-
let buildPipelineTask;
|
39
|
-
/** @type {null | {tempDirectory:string, outputDirectory:string}} */
|
40
|
-
let buildPipelineTaskResults = null;
|
41
|
-
|
42
|
-
export function waitForBuildPipelineToFinish() {
|
43
|
-
return buildPipelineTask;
|
44
|
-
}
|
45
|
-
|
46
|
-
/** This time is set when a build plugin is triggered to run
|
47
|
-
* We increase the time by 10-20 seconds each time because we might have a multi step process
|
48
|
-
* where the build pipeline is triggered in the SSR build (which we can not always detect)
|
49
|
-
* and the final client build is triggered later (but the build pipeline is still running and waiting)
|
50
|
-
*/
|
51
|
-
let maxOutputDirectoryCreatedWaitTime = 0;
|
52
9
|
/**
|
53
|
-
* @param {boolean} debugLog
|
54
|
-
*/
|
55
|
-
function increaseMaxWaitTime(debugLog) {
|
56
|
-
maxOutputDirectoryCreatedWaitTime = Date.now();
|
57
|
-
let maxWaitTime = 10_000;
|
58
|
-
if (process.env.CI) {
|
59
|
-
maxWaitTime += 50_000;
|
60
|
-
}
|
61
|
-
maxOutputDirectoryCreatedWaitTime += maxWaitTime;
|
62
|
-
if (debugLog) {
|
63
|
-
log(`Increased max wait time by ${maxWaitTime / 1000}sec until ${new Date(maxOutputDirectoryCreatedWaitTime).toISOString()}`);
|
64
|
-
}
|
65
|
-
}
|
66
|
-
|
67
|
-
/**
|
68
10
|
* Runs the needle build pipeline as part of the vite build process
|
69
|
-
* @param {string} command
|
70
|
-
* @param {import('vite').UserConfig} config
|
71
11
|
* @param {import('../types').userSettings} userSettings
|
72
|
-
* @returns {
|
12
|
+
* @returns {import('vite').Plugin}
|
73
13
|
*/
|
74
14
|
export const needleBuildPipeline = async (command, config, userSettings) => {
|
75
15
|
|
76
16
|
// we only want to run compression here if this is a distribution build
|
77
17
|
// this is handled however in the `apply` hook
|
78
|
-
if (userSettings.noBuildPipeline) return
|
79
|
-
if (userSettings.buildPipeline?.enabled === false) {
|
80
|
-
log("Skipping build pipeline because it is disabled in the user settings via `buildPipeline.enabled: false`");
|
81
|
-
return null;
|
82
|
-
}
|
18
|
+
if (userSettings.noBuildPipeline) return;
|
83
19
|
|
84
20
|
const packageJsonPath = process.cwd() + "/package.json";
|
85
21
|
await fixPackageJson(packageJsonPath);
|
@@ -100,98 +36,43 @@
|
|
100
36
|
if (!shouldRun) {
|
101
37
|
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\".");
|
102
38
|
await new Promise((resolve, _) => setTimeout(resolve, 1000));
|
103
|
-
return
|
39
|
+
return;
|
104
40
|
}
|
105
41
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
validateCloudBuildConfiguration(userSettings);
|
111
|
-
|
112
|
-
const verboseOutput = userSettings?.buildPipeline?.verbose || false;
|
113
|
-
let taskHasCompleted = false;
|
114
|
-
|
42
|
+
/** @type {Promise<any>|null} */
|
43
|
+
let task = null;
|
44
|
+
let taskFinished = false;
|
45
|
+
let taskSucceeded = false;
|
115
46
|
return {
|
116
47
|
name: 'needle-buildpipeline',
|
117
48
|
enforce: "post",
|
118
|
-
apply:
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
if (env.command === "build") {
|
127
|
-
increaseMaxWaitTime(verboseOutput);
|
128
|
-
if (buildPipelineTask) {
|
129
|
-
return false;
|
130
|
-
}
|
131
|
-
return true;
|
132
|
-
}
|
133
|
-
return false;
|
49
|
+
apply: 'build',
|
50
|
+
buildEnd() {
|
51
|
+
// start the compression process once vite is done copying the files
|
52
|
+
task = invokeBuildPipeline(userSettings).then((res) => {
|
53
|
+
taskFinished = true;
|
54
|
+
taskSucceeded = res;
|
55
|
+
});
|
56
|
+
buildPipelineTask = task;
|
134
57
|
},
|
135
|
-
buildEnd(error) {
|
136
|
-
increaseMaxWaitTime(verboseOutput);
|
137
|
-
|
138
|
-
if (verboseOutput) {
|
139
|
-
log("Build end:", error ?? "No error");
|
140
|
-
}
|
141
|
-
if (error) {
|
142
|
-
// if there was an error during the build we should not run the build pipeline
|
143
|
-
}
|
144
|
-
else {
|
145
|
-
if (buildPipelineTask) {
|
146
|
-
log("Build pipeline already running...");
|
147
|
-
return;
|
148
|
-
}
|
149
|
-
let taskSucceeded = false;
|
150
|
-
// start the compression process once vite is done copying the files
|
151
|
-
buildPipelineTask = invokeBuildPipeline(userSettings)
|
152
|
-
.then((res) => {
|
153
|
-
taskSucceeded = res;
|
154
|
-
})
|
155
|
-
.finally(() => {
|
156
|
-
taskHasCompleted = true;
|
157
|
-
if (!taskSucceeded) {
|
158
|
-
throw new Error("[needle-buildpipeline] - Build pipeline failed. Check the logs for more information.");
|
159
|
-
}
|
160
|
-
else {
|
161
|
-
log("Finished successfully");
|
162
|
-
}
|
163
|
-
});
|
164
|
-
}
|
165
|
-
},
|
166
58
|
closeBundle() {
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
// // this is the last hook that is called, so we can wait for the task to finish here
|
174
|
-
return buildPipelineTask = buildPipelineTask?.then(() => {
|
175
|
-
// Copy the results to their final output directory.
|
176
|
-
if (buildPipelineTaskResults != null) {
|
177
|
-
log("Copying files from temporary output directory to final output directory...");
|
178
|
-
const ctx = { count: 0 }
|
179
|
-
copyFilesSync(buildPipelineTaskResults.tempDirectory, buildPipelineTaskResults.outputDirectory, true, ctx);
|
180
|
-
log(`Copied ${ctx.count} file(s)`);
|
181
|
-
}
|
182
|
-
else {
|
183
|
-
log("No files to copy - build pipeline did not run or did not finish successfully");
|
184
|
-
}
|
59
|
+
log("Waiting for postprocessing to finish...");
|
60
|
+
// this is the last hook that is called, so we can wait for the task to finish here
|
61
|
+
if (taskFinished) return;
|
62
|
+
// delay the final log slightly to give other plugins a chance to log their stuff
|
63
|
+
wait(100).then(() => {
|
64
|
+
if (!taskFinished) log("Waiting for postprocessing to finish...")
|
185
65
|
});
|
66
|
+
return task.then(_ => {
|
67
|
+
log("finished", taskSucceeded ? "successfully" : "with errors");
|
68
|
+
});
|
186
69
|
},
|
187
70
|
}
|
188
71
|
}
|
189
72
|
|
190
|
-
|
191
73
|
/**
|
192
74
|
* Previously we did always install the build pipeline and run an extra command to invoke the build pipeline.
|
193
75
|
* This is now done automatically by the needle build pipeline plugin - so we update all legacy projects to use the new method.
|
194
|
-
* @param {string} packageJsonPath
|
195
76
|
*/
|
196
77
|
async function fixPackageJson(packageJsonPath) {
|
197
78
|
if (!existsSync(packageJsonPath)) {
|
@@ -208,11 +89,9 @@
|
|
208
89
|
writeFileSync(packageJsonPath, fixed);
|
209
90
|
}
|
210
91
|
|
211
|
-
/** @param {any} args */
|
212
92
|
function log(...args) {
|
213
93
|
console.log("[needle-buildpipeline]", ...args);
|
214
94
|
}
|
215
|
-
/** @param {any} args */
|
216
95
|
function warn(...args) {
|
217
96
|
console.warn("WARN: [needle-buildpipeline]", ...args);
|
218
97
|
}
|
@@ -225,113 +104,63 @@
|
|
225
104
|
const installPath = "node_modules/@needle-tools/gltf-build-pipeline";
|
226
105
|
const fullInstallPath = process.cwd() + "/" + installPath;
|
227
106
|
const existsLocally = existsSync(fullInstallPath);
|
228
|
-
if (existsLocally) {
|
229
|
-
|
107
|
+
if (!existsLocally) {
|
108
|
+
// await execSync("npx --yes @needle-tools/n")
|
109
|
+
// throw new Error(`ERR: Build pipeline not found at \"${fullInstallPath}\". \nTo disable this plugin you can pass \"noBuildPipeline: true\" to the needle config in vite.config.js`);
|
110
|
+
warn("@needle-tools/gltf-build-pipeline not found in package - using latest version")
|
230
111
|
}
|
231
|
-
await
|
112
|
+
await wait(500);
|
232
113
|
const outputDirectory = getOutputDirectory() + "/assets";
|
233
|
-
|
234
|
-
const maxEndTime = startWaitTime + 120_000;
|
235
|
-
/** wait until the output directory exists
|
236
|
-
* @param {number} iteration
|
237
|
-
* @returns {Promise<boolean>}
|
238
|
-
*/
|
114
|
+
// wait until the output directory exists - this depends on speed
|
239
115
|
function waitForOutputDirectory(iteration) {
|
240
|
-
// we wait for the output directory
|
241
116
|
if (!existsSync(outputDirectory)) {
|
242
|
-
if (
|
117
|
+
if (iteration > 10) {
|
243
118
|
return Promise.resolve(false);
|
244
119
|
}
|
245
|
-
|
246
|
-
|
247
|
-
return Promise.resolve(false);
|
248
|
-
}
|
249
|
-
if (iteration <= 0) log(`Waiting for output directory to be created... (${outputDirectory})`);
|
250
|
-
return delay(1000).then(() => waitForOutputDirectory(iteration + 1));
|
120
|
+
if (iteration <= 0) log("Waiting for output directory to be created...");
|
121
|
+
return wait(1000).then(() => waitForOutputDirectory(iteration + 1));
|
251
122
|
}
|
252
123
|
return Promise.resolve(true);
|
253
124
|
}
|
254
125
|
if (!await waitForOutputDirectory(0)) {
|
255
|
-
warn(
|
256
|
-
return
|
126
|
+
warn("Directory not found at " + outputDirectory);
|
127
|
+
return;
|
257
128
|
}
|
258
|
-
const files = readdirSync(outputDirectory).filter(f => f.endsWith(".glb") || f.endsWith(".gltf")
|
259
|
-
log(
|
129
|
+
const files = readdirSync(outputDirectory).filter(f => f.endsWith(".glb") || f.endsWith(".gltf"));
|
130
|
+
log(files.length + " file(s) to process");
|
260
131
|
|
261
132
|
/** @type {null | ChildProcess} */
|
262
|
-
let
|
133
|
+
let sub = null;
|
263
134
|
|
264
|
-
|
265
|
-
const runInCloud = typeof cloudAccessToken === "string" && cloudAccessToken.length > 0;
|
266
|
-
// if a user has defined the build pipeline settings object but not passed in a token we should print out some information
|
267
|
-
// or perhaps log an error / prevent the build from running completely
|
268
|
-
if (opts.buildPipeline && !runInCloud && process.env.CI) {
|
269
|
-
warn(`No cloud access token found. Please set it via process.env.NEEDLE_CLOUD_TOKEN`);
|
270
|
-
return false;
|
271
|
-
}
|
272
|
-
|
273
|
-
|
274
|
-
// put the processed files first in a temporary directory. They will be moved to the output directory at the end of the buildstep
|
275
|
-
// this is so that processes like sveltekit-static-adapter can run first and does not override already compressed files
|
276
|
-
const tempOutputPath = process.cwd() + "/node_modules/.needle/build-pipeline/output";
|
277
|
-
if (existsSync(tempOutputPath)) {
|
278
|
-
log("Removing temporary output directory at " + tempOutputPath);
|
279
|
-
rmSync(tempOutputPath, { recursive: true, force: true });
|
280
|
-
}
|
281
|
-
mkdirSync(tempOutputPath, { recursive: true });
|
282
|
-
|
283
|
-
/** @param {number} code */
|
284
|
-
function onExit(code) {
|
285
|
-
if (code === 0)
|
286
|
-
buildPipelineTaskResults = {
|
287
|
-
tempDirectory: tempOutputPath,
|
288
|
-
outputDirectory: outputDirectory
|
289
|
-
}
|
290
|
-
}
|
291
|
-
|
292
|
-
// allow running the build pipeline in the cloud. It requires and access token to be set in the vite.config.js
|
293
|
-
// this can be set via e.g. process.env.NEEDLE_CLOUD_TOKEN
|
294
|
-
if (runInCloud) {
|
295
|
-
if (!cloudAccessToken || !(typeof cloudAccessToken === "string") || cloudAccessToken.length <= 0) {
|
296
|
-
throw new Error("No cloud access token configured. Please set it via process.env.NEEDLE_CLOUD_TOKEN or in the vite.config.js");
|
297
|
-
}
|
298
|
-
let cmd = `npx --yes @needle-tools/cloud-cli@latest optimize "${outputDirectory}" --accessToken ${cloudAccessToken}`;
|
299
|
-
const projectName = opts.buildPipeline?.projectName;
|
300
|
-
if (projectName) {
|
301
|
-
cmd += ` --name "${projectName}"`;
|
302
|
-
}
|
303
|
-
if (opts.buildPipeline?.verbose === true) {
|
304
|
-
cmd += " --verbose";
|
305
|
-
}
|
306
|
-
cmd += " --outdir \"" + tempOutputPath + "\"";
|
307
|
-
console.log("\n")
|
308
|
-
log("Running compression in cloud");
|
309
|
-
proc = exec(cmd);
|
310
|
-
}
|
311
|
-
else if (existsLocally) {
|
135
|
+
if (existsLocally) {
|
312
136
|
const cmd = `needle-gltf transform "${outputDirectory}"`;
|
313
137
|
log("Running command \"" + cmd + "\" at " + process.cwd() + "...");
|
314
|
-
|
138
|
+
sub = exec(cmd, { cwd: installPath });
|
315
139
|
}
|
316
140
|
else {
|
317
|
-
const version = opts.
|
318
|
-
const cmd = `npx --yes @needle-tools/gltf-build-pipeline@${version} transform "${outputDirectory}"
|
319
|
-
log(
|
320
|
-
|
141
|
+
const version = opts.buildPipelineVersion || "latest";
|
142
|
+
const cmd = `npx --yes @needle-tools/gltf-build-pipeline@${version} transform "${outputDirectory}"`;
|
143
|
+
log("Running command \"" + cmd);
|
144
|
+
sub = exec(cmd);
|
321
145
|
}
|
322
|
-
|
323
|
-
function onLog(data) {
|
146
|
+
sub.stdout.on('data', data => {
|
324
147
|
if (data.length <= 0) return;
|
325
148
|
// ensure that it doesnt end with a newline
|
326
|
-
|
327
|
-
log(data);
|
328
|
-
}
|
329
|
-
|
330
|
-
proc.stderr?.on('data', onLog);
|
149
|
+
if (data.endsWith("\n")) data = data.slice(0, -1);
|
150
|
+
console.log(data);
|
151
|
+
});
|
152
|
+
sub.stderr.on('data', console.error);
|
331
153
|
return new Promise((resolve, reject) => {
|
332
|
-
|
333
|
-
onExit(code || 0);
|
154
|
+
sub.on('exit', (code) => {
|
334
155
|
resolve(code === 0);
|
335
156
|
});
|
336
157
|
});
|
337
158
|
}
|
159
|
+
|
160
|
+
function wait(ms) {
|
161
|
+
return new Promise((resolve, reject) => {
|
162
|
+
setTimeout(() => {
|
163
|
+
resolve();
|
164
|
+
}, ms);
|
165
|
+
});
|
166
|
+
}
|
@@ -16,11 +16,11 @@
|
|
16
16
|
totalsize: 0,
|
17
17
|
files: []
|
18
18
|
};
|
19
|
-
console.log(
|
19
|
+
console.log("[needle-buildinfo] - Collect files in " + buildDirectory);
|
20
20
|
recursivelyCollectFiles(buildDirectory, "", buildInfo);
|
21
21
|
const buildInfoPath = `${buildDirectory}/needle.buildinfo.json`;
|
22
22
|
const totalSizeInMB = buildInfo.totalsize / 1024 / 1024;
|
23
|
-
console.log(`[needle-buildinfo] - Collected ${buildInfo.files.length} files (${totalSizeInMB.toFixed(2)} MB).
|
23
|
+
console.log(`[needle-buildinfo] - Collected ${buildInfo.files.length} files (${totalSizeInMB.toFixed(2)} MB). Write info to \"${buildInfoPath}\"`);
|
24
24
|
fs.writeFileSync(buildInfoPath, JSON.stringify(buildInfo));
|
25
25
|
}
|
26
26
|
|
@@ -39,11 +39,10 @@
|
|
39
39
|
}
|
40
40
|
const newPath = path?.length <= 0 ? entry.name : `${path}/${entry.name}`;
|
41
41
|
const newDirectory = `${directory}/${entry.name}`;
|
42
|
-
console.log(
|
42
|
+
console.log("[needle-buildinfo] - Collect files in " + newPath);
|
43
43
|
recursivelyCollectFiles(newDirectory, newPath, info);
|
44
44
|
} else {
|
45
45
|
const relpath = `${path}/${entry.name}`;
|
46
|
-
// console.log("[needle-buildinfo] - New File: " + relpath);
|
47
46
|
const filehash = crypto.createHash('sha256');
|
48
47
|
const fullpath = `${directory}/${entry.name}`;
|
49
48
|
filehash.update(fs.readFileSync(fullpath));
|
@@ -1,38 +1,32 @@
|
|
1
1
|
import { createBuildInfoFile } from '../common/buildinfo.js';
|
2
|
-
import {
|
2
|
+
import { buildPipelineTask } from './build-pipeline.js';
|
3
3
|
import { getOutputDirectory } from './config.js';
|
4
4
|
|
5
|
-
let level = 0;
|
6
5
|
|
6
|
+
|
7
7
|
/** Create a buildinfo file in the build directory
|
8
8
|
* @param {import('../types').userSettings} userSettings
|
9
|
-
* @returns {import('vite').Plugin
|
9
|
+
* @returns {import('vite').Plugin}
|
10
10
|
*/
|
11
11
|
export const needleBuildInfo = (command, config, userSettings) => {
|
12
12
|
|
13
|
-
if (userSettings?.noBuildInfo) return
|
13
|
+
if (userSettings?.noBuildInfo) return;
|
14
14
|
|
15
15
|
return {
|
16
16
|
name: 'needle-buildinfo',
|
17
17
|
apply: "build",
|
18
18
|
enforce: "post",
|
19
|
-
buildStart: () => {
|
20
|
-
level++;
|
21
|
-
},
|
22
19
|
closeBundle: async () => {
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
await delay(500);
|
34
|
-
const buildDirectory = getOutputDirectory();
|
35
|
-
createBuildInfoFile(buildDirectory);
|
20
|
+
return new Promise(async res => {
|
21
|
+
if (buildPipelineTask instanceof Promise) {
|
22
|
+
await buildPipelineTask.catch(() => { });
|
23
|
+
}
|
24
|
+
// wait for gzip
|
25
|
+
await delay(500);
|
26
|
+
const buildDirectory = getOutputDirectory();
|
27
|
+
createBuildInfoFile(buildDirectory);
|
28
|
+
res();
|
29
|
+
});
|
36
30
|
}
|
37
31
|
}
|
38
32
|
}
|
@@ -2,55 +2,40 @@
|
|
2
2
|
import { resolve, join, isAbsolute } from 'path'
|
3
3
|
import { existsSync, statSync, mkdirSync, readdirSync, copyFileSync, mkdir } from 'fs';
|
4
4
|
import { builtAssetsDirectory, tryLoadProjectConfig } from './config.js';
|
5
|
-
import { copyFilesSync } from '../common/files.js';
|
6
5
|
|
7
|
-
const pluginName = "needle-copy-files";
|
8
6
|
|
9
|
-
|
10
7
|
/** copy files on build from assets to dist
|
11
8
|
* @param {import('../types').userSettings} userSettings
|
12
|
-
* @returns {import('vite').Plugin | null}
|
13
9
|
*/
|
14
10
|
export const needleCopyFiles = (command, config, userSettings) => {
|
15
11
|
|
16
12
|
if (config?.noCopy === true || userSettings?.noCopy === true) {
|
17
|
-
return
|
13
|
+
return;
|
18
14
|
}
|
19
15
|
|
20
16
|
return {
|
21
17
|
name: 'needle-copy-files',
|
22
|
-
// explicitly don't enforce post or pre because we want to run before the build-pipeline plugin
|
23
|
-
enforce: "pre",
|
24
|
-
apply: (_conf, env) => {
|
25
|
-
if (env.ssrBuild === true) return false;
|
26
|
-
return true;
|
27
|
-
},
|
28
18
|
buildStart() {
|
29
|
-
return run(
|
19
|
+
return run(false, config);
|
30
20
|
},
|
31
21
|
closeBundle() {
|
32
|
-
return run(
|
22
|
+
return run(true, config);
|
33
23
|
},
|
34
24
|
}
|
35
25
|
}
|
36
26
|
|
37
|
-
|
38
|
-
* @param {"start" | "end"} buildstep
|
39
|
-
* @param {import('../types').userSettings} config
|
40
|
-
*/
|
41
|
-
async function run(buildstep, config) {
|
42
|
-
console.log(`[${pluginName}] - Copy files at ${buildstep}`);
|
27
|
+
async function run(isBuild, config) {
|
43
28
|
const copyIncludesFromEngine = config?.copyIncludesFromEngine ?? true;
|
44
29
|
|
45
30
|
const baseDir = process.cwd();
|
46
|
-
const
|
31
|
+
const pluginName = "needle-copy-files";
|
47
32
|
|
48
33
|
let assetsDirName = "assets";
|
49
34
|
let outdirName = "dist";
|
50
35
|
|
51
36
|
const needleConfig = tryLoadProjectConfig();
|
52
37
|
if (needleConfig) {
|
53
|
-
assetsDirName = needleConfig.assetsDirectory
|
38
|
+
assetsDirName = needleConfig.assetsDirectory;
|
54
39
|
while (assetsDirName.startsWith('/')) assetsDirName = assetsDirName.substring(1);
|
55
40
|
|
56
41
|
if (needleConfig.buildDirectory)
|
@@ -63,61 +48,60 @@
|
|
63
48
|
if (existsSync(engineIncludeDir)) {
|
64
49
|
console.log(`[${pluginName}] - Copy engine include to ${baseDir}/include`)
|
65
50
|
const projectIncludeDir = resolve(baseDir, 'include');
|
66
|
-
|
51
|
+
copyRecursiveSync(engineIncludeDir, projectIncludeDir);
|
67
52
|
}
|
68
53
|
}
|
69
54
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
55
|
+
if (isBuild) {
|
56
|
+
const outDir = resolve(baseDir, outdirName);
|
57
|
+
if (!existsSync(outDir)) {
|
58
|
+
mkdirSync(outDir);
|
59
|
+
}
|
74
60
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
61
|
+
// copy a list of files or directories declared in build.copy = [] in the needle.config.json
|
62
|
+
/*
|
63
|
+
"build": {
|
64
|
+
"copy": ["myFolder", "myFile.txt"]
|
65
|
+
}
|
66
|
+
*/
|
67
|
+
if (needleConfig?.build?.copy) {
|
68
|
+
const arr = needleConfig.build.copy;
|
69
|
+
for (let i = 0; i < arr.length; i++) {
|
70
|
+
const entry = arr[i];
|
71
|
+
if (Array.isArray(entry)) {
|
72
|
+
console.log("WARN: build.copy can only contain string paths to copy to. Found array instead.");
|
73
|
+
continue;
|
74
|
+
}
|
75
|
+
const src = resolve(baseDir, entry);
|
76
|
+
const dest = resolvePath(outDir, entry);
|
77
|
+
if (existsSync(src)) {
|
78
|
+
console.log(`[${pluginName}] - Copy ${entry} to ${outdirName}/${entry}`)
|
79
|
+
copyRecursiveSync(src, dest);
|
80
|
+
}
|
88
81
|
}
|
89
|
-
const src = resolve(baseDir, entry);
|
90
|
-
const dest = resolvePath(outDir, entry);
|
91
|
-
if (existsSync(src) && dest) {
|
92
|
-
console.log(`[${pluginName}] - Copy ${entry} to ${outdirName}/${entry}`)
|
93
|
-
copyFilesSync(src, dest, override);
|
94
|
-
}
|
95
82
|
}
|
96
|
-
}
|
97
83
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
84
|
+
// copy assets dir
|
85
|
+
const assetsDir = resolve(baseDir, assetsDirName);
|
86
|
+
if (existsSync(assetsDir)) {
|
87
|
+
console.log(`[${pluginName}] - Copy assets to ${outdirName}/${builtAssetsDirectory()}`)
|
88
|
+
const targetDir = resolve(outDir, 'assets');
|
89
|
+
copyRecursiveSync(assetsDir, targetDir);
|
90
|
+
}
|
91
|
+
else console.log(`WARN: No assets directory found. Skipping copy of ${assetsDirName} resolved to ${assetsDir}`)
|
92
|
+
// copy include dir
|
93
|
+
const includeDir = resolve(baseDir, 'include');
|
94
|
+
if (existsSync(includeDir)) {
|
95
|
+
console.log(`[${pluginName}] - Copy include to ${outdirName}/include`)
|
96
|
+
const targetDir = resolve(outDir, 'include');
|
97
|
+
copyRecursiveSync(includeDir, targetDir);
|
98
|
+
}
|
104
99
|
}
|
105
|
-
else console.log(`WARN: No assets directory found. Skipping copy of ${assetsDirName} resolved to ${assetsDir}`)
|
106
|
-
|
107
|
-
// copy include dir
|
108
|
-
const includeDir = resolve(baseDir, 'include');
|
109
|
-
if (existsSync(includeDir)) {
|
110
|
-
console.log(`[${pluginName}] - Copy include to ${outdirName}/include`)
|
111
|
-
const targetDir = resolve(outDir, 'include');
|
112
|
-
copyFilesSync(includeDir, targetDir, override);
|
113
|
-
}
|
114
100
|
}
|
115
101
|
|
116
102
|
/** resolves relative or absolute paths to a path inside the out directory
|
117
103
|
* for example D:/myFile.txt would resolve to outDir/myFile.txt
|
118
104
|
* wherereas "some/relative/path" would become outDir/some/relative/path
|
119
|
-
* @param {string} outDir
|
120
|
-
* @param {string} pathValue
|
121
105
|
*/
|
122
106
|
function resolvePath(outDir, pathValue) {
|
123
107
|
if (isAbsolute(pathValue)) {
|
@@ -126,12 +110,29 @@
|
|
126
110
|
var stats = exists && statSync(pathValue);
|
127
111
|
if (stats.isDirectory()) {
|
128
112
|
const dirName = pathValue.replaceAll('\\', '/').split('/').pop();
|
129
|
-
if (!dirName) return null;
|
130
113
|
return resolve(outDir, dirName);
|
131
114
|
}
|
132
115
|
const fileName = pathValue.replaceAll('\\', '/').split('/').pop();
|
133
|
-
if (!fileName) return null;
|
134
116
|
return resolve(outDir, fileName);
|
135
117
|
}
|
136
118
|
return resolve(outDir, pathValue);
|
137
|
-
}
|
119
|
+
}
|
120
|
+
|
121
|
+
function copyRecursiveSync(src, dest) {
|
122
|
+
if (dest === null) {
|
123
|
+
console.log(`[${pluginName}] - Copy ${src} to ${dest} - dest is null`)
|
124
|
+
return;
|
125
|
+
}
|
126
|
+
var exists = existsSync(src);
|
127
|
+
var stats = exists && statSync(src);
|
128
|
+
var isDirectory = exists && stats.isDirectory();
|
129
|
+
if (isDirectory) {
|
130
|
+
if (!existsSync(dest))
|
131
|
+
mkdirSync(dest, { recursive: true });
|
132
|
+
readdirSync(src).forEach(function (childItemName) {
|
133
|
+
copyRecursiveSync(join(src, childItemName), join(dest, childItemName));
|
134
|
+
});
|
135
|
+
} else {
|
136
|
+
copyFileSync(src, dest);
|
137
|
+
}
|
138
|
+
};
|
@@ -1,31 +0,0 @@
|
|
1
|
-
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
|
2
|
-
import { join } from "path";
|
3
|
-
|
4
|
-
/**
|
5
|
-
* @param {string} src
|
6
|
-
* @param {string} dest
|
7
|
-
* @param {boolean} override
|
8
|
-
* @param {{count:number} | undefined} ctx
|
9
|
-
*/
|
10
|
-
export function copyFilesSync(src, dest, override = true, ctx = undefined) {
|
11
|
-
if (dest === null) {
|
12
|
-
console.log(`[needle-copy] - Copy ${src} to ${dest} - dest is null`)
|
13
|
-
return;
|
14
|
-
}
|
15
|
-
let exists = existsSync(src);
|
16
|
-
let stats = exists && statSync(src);
|
17
|
-
let isDirectory = exists && typeof stats != "boolean" && stats.isDirectory();
|
18
|
-
if (isDirectory) {
|
19
|
-
if (!existsSync(dest))
|
20
|
-
mkdirSync(dest, { recursive: true });
|
21
|
-
readdirSync(src).forEach(function (childItemName) {
|
22
|
-
// recurse
|
23
|
-
copyFilesSync(join(src, childItemName), join(dest, childItemName), override, ctx);
|
24
|
-
});
|
25
|
-
} else if (!exists || override) {
|
26
|
-
if (ctx) {
|
27
|
-
ctx.count += 1;
|
28
|
-
}
|
29
|
-
copyFileSync(src, dest);
|
30
|
-
}
|
31
|
-
};
|
@@ -96,7 +96,7 @@
|
|
96
96
|
}
|
97
97
|
* ```
|
98
98
|
* @param {string} command
|
99
|
-
* @param {import('../types
|
99
|
+
* @param {import('../types').userSettings} userSettings
|
100
100
|
*/
|
101
101
|
export const needlePlugins = async (command, config, userSettings) => {
|
102
102
|
|
@@ -1,8 +0,0 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
/**
|
4
|
-
* @param {number} ms
|
5
|
-
*/
|
6
|
-
export function delay(ms) {
|
7
|
-
return new Promise(res => setTimeout(res, ms));
|
8
|
-
}
|
@@ -140,6 +140,7 @@
|
|
140
140
|
width: max(600px, 100%);
|
141
141
|
height: max(300px, 100%);
|
142
142
|
touch-action: none;
|
143
|
+
-webkit-tap-highlight-color: transparent;
|
143
144
|
}
|
144
145
|
|
145
146
|
@media (max-width: 600px) {
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import { Camera, Color, ColorRepresentation, PerspectiveCamera } from "three";
|
2
2
|
|
3
3
|
import { Renderer } from "../engine-components/Renderer.js";
|
4
|
+
import { RGBAColor } from "./api.js";
|
4
5
|
import { getComponentsInChildren } from "./engine_components.js";
|
5
6
|
import { ContextRegistry } from "./engine_context_registry.js";
|
6
7
|
import { Context } from "./engine_setup.js";
|
7
|
-
import { RGBAColor } from "./api.js";
|
8
8
|
|
9
9
|
declare type ScreenshotImageMimeType = "image/webp" | "image/png";
|
10
10
|
|
@@ -22,7 +22,4 @@
|
|
22
22
|
assetsDirectory?: string;
|
23
23
|
scriptsDirectory?: string;
|
24
24
|
codegenDirectory?: string;
|
25
|
-
build?: {
|
26
|
-
copy?: Array<string>;
|
27
|
-
}
|
28
25
|
}
|
@@ -5,9 +5,6 @@
|
|
5
5
|
webpack: object | undefined
|
6
6
|
}
|
7
7
|
|
8
|
-
/**
|
9
|
-
* Settings for the Needle plugin
|
10
|
-
*/
|
11
8
|
export type userSettings = {
|
12
9
|
|
13
10
|
/** disable needle asap plugin */
|
@@ -23,8 +20,6 @@
|
|
23
20
|
|
24
21
|
/** disable automatic copying of files to include and output directory (dist) */
|
25
22
|
noCopy?: boolean;
|
26
|
-
/** When enabled the needle-engine include directory will be copied */
|
27
|
-
copyIncludesFromEngine?: boolean;
|
28
23
|
/** set to false to tree-shake rapier physics engine to the reduce bundle size */
|
29
24
|
useRapier?: boolean;
|
30
25
|
noDependencyWatcher?: boolean;
|
@@ -49,25 +44,12 @@
|
|
49
44
|
|
50
45
|
/** Set to true to disable the needle build pipeline (running compression and optimization as a postprocessing step on the exported glTF files) */
|
51
46
|
noBuildPipeline?: boolean;
|
47
|
+
/** Set to a specific version of the Needle Build Pipeline.
|
48
|
+
* @default "latest"
|
49
|
+
* @example "2.2.0-alpha"
|
50
|
+
*/
|
51
|
+
buildPipelineVersion?: string;
|
52
52
|
|
53
|
-
/**
|
54
|
-
* Use to configure optimized builds
|
55
|
-
*/
|
56
|
-
buildPipeline?: {
|
57
|
-
/** Set to false to prevent the build pipeline from running */
|
58
|
-
enabled?: boolean,
|
59
|
-
/** Set a project name (cloud only) */
|
60
|
-
projectName?: string,
|
61
|
-
/** Enable for verbose log output */
|
62
|
-
verbose?: boolean,
|
63
|
-
|
64
|
-
/** Set to a specific version of the Needle Build Pipeline.
|
65
|
-
* @default "latest"
|
66
|
-
* @example "2.2.0-alpha"
|
67
|
-
*/
|
68
|
-
version?: string;
|
69
|
-
}
|
70
|
-
|
71
53
|
/** required for @serializable https://github.com/vitejs/vite/issues/13736 */
|
72
54
|
vite44Hack?: boolean;
|