@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 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)(import_meta.url));
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 == null ? void 0 : abortSignal.aborted) {
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 == null ? void 0 : abortSignal.addEventListener("abort", abortHandler, { once: true });
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 ((opts == null ? void 0 : opts.ignoredMessageFilter) && msg.trim().startsWith(opts.ignoredMessageFilter)) {
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 ((opts == null ? void 0 : opts.captureOutput) === true) {
259
+ if (opts?.captureOutput === true) {
253
260
  stdoutMessages.push(msg);
254
261
  }
255
- if ((opts == null ? void 0 : opts.logOutput) !== false) {
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 ((opts == null ? void 0 : opts.captureOutput) === true) {
268
+ if (opts?.captureOutput === true) {
262
269
  stderrMessages.push(msg);
263
270
  }
264
- if ((opts == null ? void 0 : opts.logOutput) !== false) {
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 == null ? void 0 : abortSignal.removeEventListener("abort", abortHandler);
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 ((opts == null ? void 0 : opts.ignoreError) === true) {
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 args = ["generate", "--genc", "--spec", "spec.json", "processed"];
493
- await context.spawnChild(prepDir, command, args, {});
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
  }