@sdeverywhere/build 0.3.0 → 0.3.2
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/README.md +25 -1
- package/dist/index.cjs +142 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +440 -0
- package/dist/index.d.ts +17 -8
- package/dist/index.js +133 -11
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
# @sdeverywhere/build
|
|
2
2
|
|
|
3
|
-
This package provides the core build and plugin API for SDEverywhere.
|
|
3
|
+
This package provides the core build and plugin API for [SDEverywhere](https://github.com/climateinteractive/SDEverywhere).
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
The best way to get started with SDEverywhere is to follow the [Quick Start](https://github.com/climateinteractive/SDEverywhere#quick-start) instructions.
|
|
8
|
+
If you follow those instructions, the `@sdeverywhere/build` package will be added to your project automatically, in which case you can skip the following section.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
# npm
|
|
14
|
+
npm install --save-dev @sdeverywhere/build
|
|
15
|
+
|
|
16
|
+
# pnpm
|
|
17
|
+
pnpm add -D @sdeverywhere/build
|
|
18
|
+
|
|
19
|
+
# yarn
|
|
20
|
+
yarn add -D @sdeverywhere/build
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
Most users do not need to interact with the `@sdeverywhere/build` package directly; it is primarily used in the implementation of the `sde` command line tool (from the `@sdeverywhere/cli` package).
|
|
26
|
+
|
|
27
|
+
However, if you are building a custom plugin or a tool that drives the build process programmatically, you can refer to the API documentation below for more details.
|
|
4
28
|
|
|
5
29
|
## Documentation
|
|
6
30
|
|
package/dist/index.cjs
CHANGED
|
@@ -17,6 +17,10 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
17
|
return to;
|
|
18
18
|
};
|
|
19
19
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
20
24
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
21
25
|
mod
|
|
22
26
|
));
|
|
@@ -29,6 +33,10 @@ __export(src_exports, {
|
|
|
29
33
|
});
|
|
30
34
|
module.exports = __toCommonJS(src_exports);
|
|
31
35
|
|
|
36
|
+
// ../../node_modules/.pnpm/tsup@7.2.0_typescript@5.2.2/node_modules/tsup/assets/cjs_shims.js
|
|
37
|
+
var getImportMetaUrl = () => typeof document === "undefined" ? new URL("file:" + __filename).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
|
|
38
|
+
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
39
|
+
|
|
32
40
|
// src/build/build.ts
|
|
33
41
|
var import_path7 = require("path");
|
|
34
42
|
var import_neverthrow3 = require("neverthrow");
|
|
@@ -38,7 +46,6 @@ var import_fs = require("fs");
|
|
|
38
46
|
var import_path = require("path");
|
|
39
47
|
var import_url = require("url");
|
|
40
48
|
var import_neverthrow = require("neverthrow");
|
|
41
|
-
var import_meta = {};
|
|
42
49
|
async function loadConfig(mode, config, sdeDir, sdeCmdPath) {
|
|
43
50
|
let userConfig;
|
|
44
51
|
if (typeof config === "object") {
|
|
@@ -126,7 +133,7 @@ function resolveUserConfig(userConfig, mode, sdeDir, sdeCmdPath) {
|
|
|
126
133
|
};
|
|
127
134
|
}
|
|
128
135
|
function relativeToSourcePath(filePath) {
|
|
129
|
-
const srcDir = (0, import_path.dirname)((0, import_url.fileURLToPath)(
|
|
136
|
+
const srcDir = (0, import_path.dirname)((0, import_url.fileURLToPath)(importMetaUrl));
|
|
130
137
|
const relPath = (0, import_path.relative)(srcDir, filePath);
|
|
131
138
|
return relPath.replaceAll("\\", "/");
|
|
132
139
|
}
|
|
@@ -210,7 +217,7 @@ var import_neverthrow2 = require("neverthrow");
|
|
|
210
217
|
var import_cross_spawn = require("cross-spawn");
|
|
211
218
|
function spawnChild(cwd, command, args, abortSignal, opts) {
|
|
212
219
|
return new Promise((resolve, reject) => {
|
|
213
|
-
if (abortSignal
|
|
220
|
+
if (abortSignal?.aborted) {
|
|
214
221
|
reject(new Error("ABORT"));
|
|
215
222
|
return;
|
|
216
223
|
}
|
|
@@ -229,12 +236,12 @@ function spawnChild(cwd, command, args, abortSignal, opts) {
|
|
|
229
236
|
}
|
|
230
237
|
reject(new Error("ABORT"));
|
|
231
238
|
};
|
|
232
|
-
abortSignal
|
|
239
|
+
abortSignal?.addEventListener("abort", abortHandler, { once: true });
|
|
233
240
|
const stdoutMessages = [];
|
|
234
241
|
const stderrMessages = [];
|
|
235
242
|
const logMessage = (msg, err4) => {
|
|
236
243
|
let includeMessage = true;
|
|
237
|
-
if (
|
|
244
|
+
if (opts?.ignoredMessageFilter && msg.trim().startsWith(opts.ignoredMessageFilter)) {
|
|
238
245
|
includeMessage = false;
|
|
239
246
|
}
|
|
240
247
|
if (includeMessage) {
|
|
@@ -249,19 +256,19 @@ function spawnChild(cwd, command, args, abortSignal, opts) {
|
|
|
249
256
|
});
|
|
250
257
|
childProc.stdout.on("data", (data) => {
|
|
251
258
|
const msg = data.toString();
|
|
252
|
-
if (
|
|
259
|
+
if (opts?.captureOutput === true) {
|
|
253
260
|
stdoutMessages.push(msg);
|
|
254
261
|
}
|
|
255
|
-
if (
|
|
262
|
+
if (opts?.logOutput !== false) {
|
|
256
263
|
logMessage(msg, false);
|
|
257
264
|
}
|
|
258
265
|
});
|
|
259
266
|
childProc.stderr.on("data", (data) => {
|
|
260
267
|
const msg = data.toString();
|
|
261
|
-
if (
|
|
268
|
+
if (opts?.captureOutput === true) {
|
|
262
269
|
stderrMessages.push(msg);
|
|
263
270
|
}
|
|
264
|
-
if (
|
|
271
|
+
if (opts?.logOutput !== false) {
|
|
265
272
|
logMessage(msg, true);
|
|
266
273
|
}
|
|
267
274
|
});
|
|
@@ -269,7 +276,7 @@ function spawnChild(cwd, command, args, abortSignal, opts) {
|
|
|
269
276
|
localLog(`Process error: ${err4}`, true);
|
|
270
277
|
});
|
|
271
278
|
childProc.on("close", (code, signal) => {
|
|
272
|
-
abortSignal
|
|
279
|
+
abortSignal?.removeEventListener("abort", abortHandler);
|
|
273
280
|
childProc = void 0;
|
|
274
281
|
if (signal) {
|
|
275
282
|
return;
|
|
@@ -282,7 +289,7 @@ function spawnChild(cwd, command, args, abortSignal, opts) {
|
|
|
282
289
|
if (code === 0) {
|
|
283
290
|
resolve(processOutput);
|
|
284
291
|
} else if (!signal) {
|
|
285
|
-
if (
|
|
292
|
+
if (opts?.ignoreError === true) {
|
|
286
293
|
resolve(processOutput);
|
|
287
294
|
} else {
|
|
288
295
|
reject(new Error(`Child process failed (code=${code})`));
|
|
@@ -294,20 +301,69 @@ function spawnChild(cwd, command, args, abortSignal, opts) {
|
|
|
294
301
|
|
|
295
302
|
// src/context/context.ts
|
|
296
303
|
var BuildContext = class {
|
|
304
|
+
/**
|
|
305
|
+
* @param config The resolved configuration.
|
|
306
|
+
* @hidden
|
|
307
|
+
*/
|
|
297
308
|
constructor(config, stagedFiles, abortSignal) {
|
|
298
309
|
this.config = config;
|
|
299
310
|
this.stagedFiles = stagedFiles;
|
|
300
311
|
this.abortSignal = abortSignal;
|
|
301
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Log a message to the console and/or the in-browser overlay panel.
|
|
315
|
+
*
|
|
316
|
+
* @param level The log level (verbose, info, error).
|
|
317
|
+
* @param msg The message.
|
|
318
|
+
*/
|
|
302
319
|
log(level, msg) {
|
|
303
320
|
log(level, msg);
|
|
304
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* Prepare for writing a file to the staged directory.
|
|
324
|
+
*
|
|
325
|
+
* This will add the path to the array of tracked files and will create the
|
|
326
|
+
* staged directory if needed.
|
|
327
|
+
*
|
|
328
|
+
* @param srcDir The directory underneath the configured `staged` directory where
|
|
329
|
+
* the file will be written (this must be a relative path).
|
|
330
|
+
* @param srcFile The name of the file as written to the `staged` directory.
|
|
331
|
+
* @param dstDir The absolute path to the destination directory where the staged
|
|
332
|
+
* file will be copied when the build has completed.
|
|
333
|
+
* @param dstFile The name of the file as written to the destination directory.
|
|
334
|
+
* @return The absolute path to the staged file.
|
|
335
|
+
*/
|
|
305
336
|
prepareStagedFile(srcDir, srcFile, dstDir, dstFile) {
|
|
306
337
|
return this.stagedFiles.prepareStagedFile(srcDir, srcFile, dstDir, dstFile);
|
|
307
338
|
}
|
|
339
|
+
/**
|
|
340
|
+
* Write a file to the staged directory.
|
|
341
|
+
*
|
|
342
|
+
* This file will be copied (along with other staged files) into the destination
|
|
343
|
+
* directory only after the build process has completed. Copying all staged files
|
|
344
|
+
* at once helps improve the local development experience by making it so that
|
|
345
|
+
* live reloading tools only need to refresh once instead of every time a build
|
|
346
|
+
* file is written.
|
|
347
|
+
*
|
|
348
|
+
* @param srcDir The directory underneath the configured `staged` directory where
|
|
349
|
+
* the file will be written (this must be a relative path).
|
|
350
|
+
* @param dstDir The absolute path to the destination directory where the staged
|
|
351
|
+
* file will be copied when the build has completed.
|
|
352
|
+
* @param filename The name of the file.
|
|
353
|
+
* @param content The file content.
|
|
354
|
+
*/
|
|
308
355
|
writeStagedFile(srcDir, dstDir, filename, content) {
|
|
309
356
|
this.stagedFiles.writeStagedFile(srcDir, dstDir, filename, content);
|
|
310
357
|
}
|
|
358
|
+
/**
|
|
359
|
+
* Spawn a child process that runs the given command.
|
|
360
|
+
*
|
|
361
|
+
* @param cwd The directory in which the command will be executed.
|
|
362
|
+
* @param command The command to execute.
|
|
363
|
+
* @param args The arguments to pass to the command.
|
|
364
|
+
* @param opts Additional options to configure the process.
|
|
365
|
+
* @returns The output of the process.
|
|
366
|
+
*/
|
|
311
367
|
spawnChild(cwd, command, args, opts) {
|
|
312
368
|
return spawnChild(cwd, command, args, this.abortSignal, opts);
|
|
313
369
|
}
|
|
@@ -321,6 +377,20 @@ var StagedFiles = class {
|
|
|
321
377
|
this.stagedFiles = [];
|
|
322
378
|
this.baseStagedDir = (0, import_path2.join)(prepDir, "staged");
|
|
323
379
|
}
|
|
380
|
+
/**
|
|
381
|
+
* Prepare for writing a file to the staged directory.
|
|
382
|
+
*
|
|
383
|
+
* This will add the path to the array of tracked files and will create the
|
|
384
|
+
* staged directory if needed.
|
|
385
|
+
*
|
|
386
|
+
* @param srcDir The directory underneath the configured `staged` directory where
|
|
387
|
+
* the file will be written (this must be a relative path).
|
|
388
|
+
* @param srcFile The name of the file as written to the `staged` directory.
|
|
389
|
+
* @param dstDir The absolute path to the destination directory where the staged
|
|
390
|
+
* file will be copied when the build has completed.
|
|
391
|
+
* @param dstFile The name of the file as written to the destination directory.
|
|
392
|
+
* @return The absolute path to the staged file.
|
|
393
|
+
*/
|
|
324
394
|
prepareStagedFile(srcDir, srcFile, dstDir, dstFile) {
|
|
325
395
|
const stagedFile = {
|
|
326
396
|
srcDir,
|
|
@@ -337,17 +407,54 @@ var StagedFiles = class {
|
|
|
337
407
|
}
|
|
338
408
|
return (0, import_path2.join)(stagedDir, srcFile);
|
|
339
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* Write a file to the staged directory.
|
|
412
|
+
*
|
|
413
|
+
* This file will be copied (along with other staged files) into the destination
|
|
414
|
+
* directory only after the build process has completed. Copying all staged files
|
|
415
|
+
* at once helps improve the local development experience by making it so that
|
|
416
|
+
* live reloading tools only need to refresh once instead of every time a build
|
|
417
|
+
* file is written.
|
|
418
|
+
*
|
|
419
|
+
* @param srcDir The directory underneath the configured `staged` directory where
|
|
420
|
+
* the file will be written (this must be a relative path).
|
|
421
|
+
* @param dstDir The absolute path to the destination directory where the staged
|
|
422
|
+
* file will be copied when the build has completed.
|
|
423
|
+
* @param filename The name of the file.
|
|
424
|
+
* @param content The file content.
|
|
425
|
+
*/
|
|
340
426
|
writeStagedFile(srcDir, dstDir, filename, content) {
|
|
341
427
|
const stagedFilePath = this.prepareStagedFile(srcDir, filename, dstDir, filename);
|
|
342
428
|
(0, import_fs3.writeFileSync)(stagedFilePath, content);
|
|
343
429
|
}
|
|
430
|
+
/**
|
|
431
|
+
* Return the absolute path to the staged file for the given source directory and file name.
|
|
432
|
+
*
|
|
433
|
+
* @param srcDir The directory underneath the configured `staged` directory where
|
|
434
|
+
* the file would be written initially (this must be a relative path).
|
|
435
|
+
* @param srcFile The name of the file.
|
|
436
|
+
*/
|
|
344
437
|
getStagedFilePath(srcDir, srcFile) {
|
|
345
438
|
return (0, import_path2.join)(this.baseStagedDir, srcDir, srcFile);
|
|
346
439
|
}
|
|
440
|
+
/**
|
|
441
|
+
* Return true if the staged file exists for the given source directory and file name.
|
|
442
|
+
*
|
|
443
|
+
* @param srcDir The directory underneath the configured `staged` directory where
|
|
444
|
+
* the file would be written initially (this must be a relative path).
|
|
445
|
+
* @param srcFile The name of the file.
|
|
446
|
+
*/
|
|
347
447
|
stagedFileExists(srcDir, srcFile) {
|
|
348
448
|
const fullSrcPath = this.getStagedFilePath(srcDir, srcFile);
|
|
349
449
|
return (0, import_fs3.existsSync)(fullSrcPath);
|
|
350
450
|
}
|
|
451
|
+
/**
|
|
452
|
+
* Return true if the destination file exists for the given source directory and file name.
|
|
453
|
+
*
|
|
454
|
+
* @param srcDir The directory underneath the configured `staged` directory where
|
|
455
|
+
* the file would be written initially (this must be a relative path).
|
|
456
|
+
* @param srcFile The name of the file.
|
|
457
|
+
*/
|
|
351
458
|
destinationFileExists(srcDir, srcFile) {
|
|
352
459
|
const f = this.stagedFiles.find((f2) => f2.srcDir === srcDir && f2.srcFile === srcFile);
|
|
353
460
|
if (f === void 0) {
|
|
@@ -356,6 +463,12 @@ var StagedFiles = class {
|
|
|
356
463
|
const fullDstPath = (0, import_path2.join)(f.dstDir, f.dstFile);
|
|
357
464
|
return (0, import_fs3.existsSync)(fullDstPath);
|
|
358
465
|
}
|
|
466
|
+
/**
|
|
467
|
+
* Copy staged files to their destination; this will only copy the staged
|
|
468
|
+
* files if they are different than the existing destination files. We
|
|
469
|
+
* copy the files in a batch like this so that hot module reload is only
|
|
470
|
+
* triggered once at the end of the whole build process.
|
|
471
|
+
*/
|
|
359
472
|
copyChangedFiles() {
|
|
360
473
|
log("info", "Copying changed files into place...");
|
|
361
474
|
for (const f of this.stagedFiles) {
|
|
@@ -363,6 +476,13 @@ var StagedFiles = class {
|
|
|
363
476
|
}
|
|
364
477
|
log("info", "Done copying files");
|
|
365
478
|
}
|
|
479
|
+
/**
|
|
480
|
+
* Copy a file from the `staged` directory to its destination. If the file already
|
|
481
|
+
* exists in the destination directory and has the same contents as the source file,
|
|
482
|
+
* the file will not be copied and this function will return false.
|
|
483
|
+
*
|
|
484
|
+
* @param f The staged file entry.
|
|
485
|
+
*/
|
|
366
486
|
copyStagedFile(f) {
|
|
367
487
|
if (!(0, import_fs3.existsSync)(f.dstDir)) {
|
|
368
488
|
(0, import_fs3.mkdirSync)(f.dstDir, { recursive: true });
|
|
@@ -489,8 +609,14 @@ async function flattenMdls(context, sdeCmdPath, prepDir, modelFiles) {
|
|
|
489
609
|
async function generateC(context, sdeDir, sdeCmdPath, prepDir) {
|
|
490
610
|
log("verbose", " Generating C code");
|
|
491
611
|
const command = sdeCmdPath;
|
|
492
|
-
const
|
|
493
|
-
await context.spawnChild(prepDir, command,
|
|
612
|
+
const gencArgs = ["generate", "--genc", "--spec", "spec.json", "processed"];
|
|
613
|
+
await context.spawnChild(prepDir, command, gencArgs, {
|
|
614
|
+
// By default, ignore lines that start with "WARNING: Data for" since these are often harmless
|
|
615
|
+
// TODO: Don't filter by default, but make it configurable
|
|
616
|
+
// ignoredMessageFilter: 'WARNING: Data for'
|
|
617
|
+
});
|
|
618
|
+
const listArgs = ["generate", "--list", "--spec", "spec.json", "processed"];
|
|
619
|
+
await context.spawnChild(prepDir, command, listArgs, {});
|
|
494
620
|
const buildDir = (0, import_path3.join)(prepDir, "build");
|
|
495
621
|
const sdeCDir = (0, import_path3.join)(sdeDir, "src", "c");
|
|
496
622
|
const files = await (0, import_promises.readdir)(sdeCDir);
|
|
@@ -662,7 +788,10 @@ function watch(config, userConfig, plugins) {
|
|
|
662
788
|
watchPaths = config.modelFiles;
|
|
663
789
|
}
|
|
664
790
|
const watcher = import_chokidar.default.watch(watchPaths, {
|
|
791
|
+
// Watch paths are resolved relative to the project root directory
|
|
665
792
|
cwd: config.rootDir,
|
|
793
|
+
// XXX: Include a delay, otherwise on macOS we sometimes get multiple
|
|
794
|
+
// change events when the csv file is saved just once
|
|
666
795
|
awaitWriteFinish: {
|
|
667
796
|
stabilityThreshold: 200
|
|
668
797
|
}
|