brepjs-cad 0.173.0 → 0.175.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.175.0](https://github.com/andymai/brepjs/compare/brepjs-cad-v0.174.0...brepjs-cad-v0.175.0) (2026-08-26)
4
+
5
+
6
+ ### Features
7
+
8
+ * **brepjs-cad:** transpiling loader for multi-file TS/TSX projects + working watch ([#2228](https://github.com/andymai/brepjs/issues/2228)) ([980ead6](https://github.com/andymai/brepjs/commit/980ead6508867c31fcec1fd06a683f03bacb1e6f))
9
+
10
+ ## [0.174.0](https://github.com/andymai/brepjs/compare/brepjs-cad-v0.173.0...brepjs-cad-v0.174.0) (2026-08-22)
11
+
12
+
13
+ ### Features
14
+
15
+ * **families:** route BIM export on a declared archetype, not the family name ([#2222](https://github.com/andymai/brepjs/issues/2222)) ([53c90ae](https://github.com/andymai/brepjs/commit/53c90aef092ad24d11483fc2fbbff99b0e8eb285))
16
+
3
17
  ## [0.173.0](https://github.com/andymai/brepjs/compare/brepjs-cad-v0.172.0...brepjs-cad-v0.173.0) (2026-08-21)
4
18
 
5
19
 
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_diff = require("./diff-DDu3OLyd.cjs");
2
+ const require_diff = require("./diff-DX9zgrss.cjs");
3
3
  exports.DEFAULT_TOLERANCE_PCT = require_diff.DEFAULT_TOLERANCE_PCT;
4
4
  exports.TYPECHECK_CODE = require_diff.TYPECHECK_CODE;
5
5
  exports.emptyReport = require_diff.emptyReport;
@@ -1,2 +1,2 @@
1
- import { a as typecheckPart, c as isExpectedDims, d as emptyReport, i as TYPECHECK_CODE, l as pctDelta, m as serializeReport, n as runMeasure, o as DEFAULT_TOLERANCE_PCT, r as runPart, s as evaluateExpected, t as runDiff, u as runChecks } from "./diff-CRu6Pqyr.js";
1
+ import { a as typecheckPart, c as isExpectedDims, d as emptyReport, i as TYPECHECK_CODE, l as pctDelta, m as serializeReport, n as runMeasure, o as DEFAULT_TOLERANCE_PCT, r as runPart, s as evaluateExpected, t as runDiff, u as runChecks } from "./diff-B1sSWjLa.js";
2
2
  export { DEFAULT_TOLERANCE_PCT, TYPECHECK_CODE, emptyReport, evaluateExpected, isExpectedDims, pctDelta, runChecks, runDiff, runMeasure, runPart, serializeReport, typecheckPart };
package/dist/cli/main.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const require_diff = require("../diff-DDu3OLyd.cjs");
3
+ const require_diff = require("../diff-DX9zgrss.cjs");
4
4
  let node_url = require("node:url");
5
5
  let node_fs = require("node:fs");
6
6
  let node_path = require("node:path");
@@ -117,6 +117,74 @@ function debounce(fn, delayMs = 150) {
117
117
  cancel
118
118
  };
119
119
  }
120
+ var SOURCE_FILE_RE = /\.(?:m?[jt]s|[jt]sx)$/;
121
+ function isWatchRelevant(filename) {
122
+ if (filename === void 0 || filename === null) return true;
123
+ const name = filename.toString();
124
+ if (/(?:^|[\\/])tsconfig(?:\..+)?\.json$/.test(name)) return true;
125
+ return SOURCE_FILE_RE.test(name);
126
+ }
127
+ /**
128
+ * Root the watcher at the project, not the entry's own directory: with the common
129
+ * `src/main.ts` layout, tsconfig.json (whose JSX options change how sources transpile)
130
+ * and `../` imports live one level up. Nearest package.json or tsconfig.json walking up
131
+ * wins; entries with no project marker fall back to their own directory.
132
+ */
133
+ function watchRootFor(entryPath) {
134
+ const start = (0, node_path.dirname)(entryPath);
135
+ let dir = start;
136
+ for (;;) {
137
+ if ((0, node_fs.existsSync)((0, node_path.join)(dir, "package.json")) || (0, node_fs.existsSync)((0, node_path.join)(dir, "tsconfig.json"))) return dir;
138
+ const parent = (0, node_path.dirname)(dir);
139
+ if (parent === dir) return start;
140
+ dir = parent;
141
+ }
142
+ }
143
+ var IGNORED_DIR_NAMES = /* @__PURE__ */ new Set([
144
+ "node_modules",
145
+ "dist",
146
+ ".git"
147
+ ]);
148
+ /**
149
+ * Watch a directory tree with one non-recursive `fs.watch` per directory, skipping
150
+ * node_modules/dist/.git and never following symlinks. `fs.watch({recursive})` has no
151
+ * exclusion API, so on a real project it would descend into node_modules — tens of
152
+ * thousands of inotify watches. Whenever an event fires in a directory it is re-scanned,
153
+ * which picks up newly created subdirectories. Returns a stop function.
154
+ */
155
+ function watchTree(root, onEvent) {
156
+ const watchers = /* @__PURE__ */ new Map();
157
+ const add = (dir) => {
158
+ if (watchers.has(dir)) return;
159
+ try {
160
+ watchers.set(dir, (0, node_fs.watch)(dir, (_event, filename) => {
161
+ scan(dir);
162
+ onEvent(filename);
163
+ }));
164
+ } catch {}
165
+ };
166
+ const scan = (dir) => {
167
+ let entries;
168
+ try {
169
+ entries = (0, node_fs.readdirSync)(dir, { withFileTypes: true });
170
+ } catch {
171
+ return;
172
+ }
173
+ for (const entry of entries) {
174
+ if (!entry.isDirectory() || IGNORED_DIR_NAMES.has(entry.name)) continue;
175
+ const child = (0, node_path.join)(dir, entry.name);
176
+ if (watchers.has(child)) continue;
177
+ add(child);
178
+ scan(child);
179
+ }
180
+ };
181
+ add(root);
182
+ scan(root);
183
+ return () => {
184
+ for (const watcher of watchers.values()) watcher.close();
185
+ watchers.clear();
186
+ };
187
+ }
120
188
  //#endregion
121
189
  //#region src/disposeShape.ts
122
190
  function disposeShape(shape) {
@@ -436,9 +504,9 @@ program.command("init").argument("<name>", "part name; scaffolds <name>.brep.ts
436
504
  });
437
505
  program.command("watch").argument("<file>", "path to a .brep.ts module; re-verifies on each save until Ctrl-C").action((file) => {
438
506
  const path = (0, node_path.resolve)(file);
439
- const run = async () => {
507
+ const run = async (fresh) => {
440
508
  try {
441
- const { report, shape } = await require_diff.runPart(path);
509
+ const { report, shape } = await require_diff.runPart(path, { freshImport: fresh });
442
510
  try {
443
511
  process.stdout.write(require_diff.serializeReport(report) + "\n");
444
512
  } finally {
@@ -448,18 +516,33 @@ program.command("watch").argument("<file>", "path to a .brep.ts module; re-verif
448
516
  process.stderr.write(`watch run failed: ${e.message}\n`);
449
517
  }
450
518
  };
451
- const { trigger } = debounce(run, 150);
452
- process.stderr.write(`watching ${path} (Ctrl-C to stop)\n`);
453
- run();
454
- const watcher = (0, node_fs.watch)((0, node_path.dirname)(path), (_event, filename) => {
455
- if (filename === void 0 || filename === null) {
456
- trigger();
519
+ let inFlight = false;
520
+ let rerunQueued = false;
521
+ const start = (fresh) => {
522
+ inFlight = true;
523
+ run(fresh).finally(() => {
524
+ inFlight = false;
525
+ if (rerunQueued) {
526
+ rerunQueued = false;
527
+ start(true);
528
+ }
529
+ });
530
+ };
531
+ const schedule = () => {
532
+ if (inFlight) {
533
+ rerunQueued = true;
457
534
  return;
458
535
  }
459
- if ((0, node_path.basename)(path) === filename.toString()) trigger();
536
+ start(true);
537
+ };
538
+ const { trigger } = debounce(schedule, 150);
539
+ process.stderr.write(`watching ${path} (Ctrl-C to stop)\n`);
540
+ start(false);
541
+ const stopWatching = watchTree(watchRootFor(path), (filename) => {
542
+ if (isWatchRelevant(filename)) trigger();
460
543
  });
461
544
  const stop = () => {
462
- watcher.close();
545
+ stopWatching();
463
546
  process.exit(0);
464
547
  };
465
548
  process.on("SIGINT", stop);
package/dist/cli/main.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { f as pushError, h as loadBrep, m as serializeReport, n as runMeasure, p as reportOk, r as runPart, t as runDiff } from "../diff-CRu6Pqyr.js";
2
+ import { f as pushError, h as loadBrep, m as serializeReport, n as runMeasure, p as reportOk, r as runPart, t as runDiff } from "../diff-B1sSWjLa.js";
3
3
  import { fileURLToPath } from "node:url";
4
- import { existsSync, globSync, mkdirSync, realpathSync, watch, writeFileSync } from "node:fs";
4
+ import { existsSync, globSync, mkdirSync, readdirSync, realpathSync, watch, writeFileSync } from "node:fs";
5
5
  import { basename, dirname, join, resolve } from "node:path";
6
6
  import { Command } from "commander";
7
7
  import { tmpdir } from "node:os";
@@ -116,6 +116,74 @@ function debounce(fn, delayMs = 150) {
116
116
  cancel
117
117
  };
118
118
  }
119
+ var SOURCE_FILE_RE = /\.(?:m?[jt]s|[jt]sx)$/;
120
+ function isWatchRelevant(filename) {
121
+ if (filename === void 0 || filename === null) return true;
122
+ const name = filename.toString();
123
+ if (/(?:^|[\\/])tsconfig(?:\..+)?\.json$/.test(name)) return true;
124
+ return SOURCE_FILE_RE.test(name);
125
+ }
126
+ /**
127
+ * Root the watcher at the project, not the entry's own directory: with the common
128
+ * `src/main.ts` layout, tsconfig.json (whose JSX options change how sources transpile)
129
+ * and `../` imports live one level up. Nearest package.json or tsconfig.json walking up
130
+ * wins; entries with no project marker fall back to their own directory.
131
+ */
132
+ function watchRootFor(entryPath) {
133
+ const start = dirname(entryPath);
134
+ let dir = start;
135
+ for (;;) {
136
+ if (existsSync(join(dir, "package.json")) || existsSync(join(dir, "tsconfig.json"))) return dir;
137
+ const parent = dirname(dir);
138
+ if (parent === dir) return start;
139
+ dir = parent;
140
+ }
141
+ }
142
+ var IGNORED_DIR_NAMES = /* @__PURE__ */ new Set([
143
+ "node_modules",
144
+ "dist",
145
+ ".git"
146
+ ]);
147
+ /**
148
+ * Watch a directory tree with one non-recursive `fs.watch` per directory, skipping
149
+ * node_modules/dist/.git and never following symlinks. `fs.watch({recursive})` has no
150
+ * exclusion API, so on a real project it would descend into node_modules — tens of
151
+ * thousands of inotify watches. Whenever an event fires in a directory it is re-scanned,
152
+ * which picks up newly created subdirectories. Returns a stop function.
153
+ */
154
+ function watchTree(root, onEvent) {
155
+ const watchers = /* @__PURE__ */ new Map();
156
+ const add = (dir) => {
157
+ if (watchers.has(dir)) return;
158
+ try {
159
+ watchers.set(dir, watch(dir, (_event, filename) => {
160
+ scan(dir);
161
+ onEvent(filename);
162
+ }));
163
+ } catch {}
164
+ };
165
+ const scan = (dir) => {
166
+ let entries;
167
+ try {
168
+ entries = readdirSync(dir, { withFileTypes: true });
169
+ } catch {
170
+ return;
171
+ }
172
+ for (const entry of entries) {
173
+ if (!entry.isDirectory() || IGNORED_DIR_NAMES.has(entry.name)) continue;
174
+ const child = join(dir, entry.name);
175
+ if (watchers.has(child)) continue;
176
+ add(child);
177
+ scan(child);
178
+ }
179
+ };
180
+ add(root);
181
+ scan(root);
182
+ return () => {
183
+ for (const watcher of watchers.values()) watcher.close();
184
+ watchers.clear();
185
+ };
186
+ }
119
187
  //#endregion
120
188
  //#region src/disposeShape.ts
121
189
  function disposeShape(shape) {
@@ -435,9 +503,9 @@ program.command("init").argument("<name>", "part name; scaffolds <name>.brep.ts
435
503
  });
436
504
  program.command("watch").argument("<file>", "path to a .brep.ts module; re-verifies on each save until Ctrl-C").action((file) => {
437
505
  const path = resolve(file);
438
- const run = async () => {
506
+ const run = async (fresh) => {
439
507
  try {
440
- const { report, shape } = await runPart(path);
508
+ const { report, shape } = await runPart(path, { freshImport: fresh });
441
509
  try {
442
510
  process.stdout.write(serializeReport(report) + "\n");
443
511
  } finally {
@@ -447,18 +515,33 @@ program.command("watch").argument("<file>", "path to a .brep.ts module; re-verif
447
515
  process.stderr.write(`watch run failed: ${e.message}\n`);
448
516
  }
449
517
  };
450
- const { trigger } = debounce(run, 150);
451
- process.stderr.write(`watching ${path} (Ctrl-C to stop)\n`);
452
- run();
453
- const watcher = watch(dirname(path), (_event, filename) => {
454
- if (filename === void 0 || filename === null) {
455
- trigger();
518
+ let inFlight = false;
519
+ let rerunQueued = false;
520
+ const start = (fresh) => {
521
+ inFlight = true;
522
+ run(fresh).finally(() => {
523
+ inFlight = false;
524
+ if (rerunQueued) {
525
+ rerunQueued = false;
526
+ start(true);
527
+ }
528
+ });
529
+ };
530
+ const schedule = () => {
531
+ if (inFlight) {
532
+ rerunQueued = true;
456
533
  return;
457
534
  }
458
- if (basename(path) === filename.toString()) trigger();
535
+ start(true);
536
+ };
537
+ const { trigger } = debounce(schedule, 150);
538
+ process.stderr.write(`watching ${path} (Ctrl-C to stop)\n`);
539
+ start(false);
540
+ const stopWatching = watchTree(watchRootFor(path), (filename) => {
541
+ if (isWatchRelevant(filename)) trigger();
459
542
  });
460
543
  const stop = () => {
461
- watcher.close();
544
+ stopWatching();
462
545
  process.exit(0);
463
546
  };
464
547
  process.on("SIGINT", stop);
@@ -3,3 +3,19 @@ export declare function debounce(fn: () => void | Promise<void>, delayMs?: numbe
3
3
  trigger: () => void;
4
4
  cancel: () => void;
5
5
  };
6
+ export declare function isWatchRelevant(filename: string | Buffer | null | undefined): boolean;
7
+ /**
8
+ * Root the watcher at the project, not the entry's own directory: with the common
9
+ * `src/main.ts` layout, tsconfig.json (whose JSX options change how sources transpile)
10
+ * and `../` imports live one level up. Nearest package.json or tsconfig.json walking up
11
+ * wins; entries with no project marker fall back to their own directory.
12
+ */
13
+ export declare function watchRootFor(entryPath: string): string;
14
+ /**
15
+ * Watch a directory tree with one non-recursive `fs.watch` per directory, skipping
16
+ * node_modules/dist/.git and never following symlinks. `fs.watch({recursive})` has no
17
+ * exclusion API, so on a real project it would descend into node_modules — tens of
18
+ * thousands of inotify watches. Whenever an event fires in a directory it is re-scanned,
19
+ * which picks up newly created subdirectories. Returns a stop function.
20
+ */
21
+ export declare function watchTree(root: string, onEvent: (filename: string | Buffer | null | undefined) => void): () => void;
@@ -19,8 +19,9 @@ function toolDir() {
19
19
  }
20
20
  }
21
21
  function loaderUrl(dir) {
22
- const built = resolve(dir, "dist", "loader", "brepjsResolve.mjs");
23
22
  const source = resolve(dir, "src", "loader", "brepjsResolve.mjs");
23
+ const built = resolve(dir, "dist", "loader", "brepjsResolve.mjs");
24
+ if (import.meta.url.startsWith(pathToFileURL(resolve(dir, "src") + "/").href)) return pathToFileURL(source).href;
24
25
  return pathToFileURL(existsSync(built) ? built : source).href;
25
26
  }
26
27
  function registerHook() {
@@ -696,6 +697,33 @@ function packageRootOf(startDir, name) {
696
697
  dir = dirname(dir);
697
698
  }
698
699
  }
700
+ /**
701
+ * JSX options for a part come from the nearest tsconfig.json — the same source the
702
+ * runtime load hook reads — so `--check` and execution agree on the dialect
703
+ * (families projects set `jsx: react-jsx` + `jsxImportSource: brepjs-families`).
704
+ * A `.tsx` part with nothing configured defaults to the automatic runtime.
705
+ */
706
+ function jsxCompilerOptions(partPath) {
707
+ const out = {};
708
+ const configPath = ts.findConfigFile(dirname(partPath), (f) => ts.sys.fileExists(f));
709
+ if (configPath) {
710
+ const host = {
711
+ ...ts.sys,
712
+ onUnRecoverableConfigFileDiagnostic: () => {}
713
+ };
714
+ const parsed = ts.getParsedCommandLineOfConfigFile(configPath, void 0, host);
715
+ if (parsed) {
716
+ for (const key of [
717
+ "jsx",
718
+ "jsxImportSource",
719
+ "jsxFactory",
720
+ "jsxFragmentFactory"
721
+ ]) if (parsed.options[key] !== void 0) out[key] = parsed.options[key];
722
+ }
723
+ }
724
+ if (partPath.endsWith(".tsx") && out.jsx === void 0) out.jsx = ts.JsxEmit.ReactJSX;
725
+ return out;
726
+ }
699
727
  var COMPILER_OPTIONS = {
700
728
  target: ts.ScriptTarget.ES2022,
701
729
  module: ts.ModuleKind.ESNext,
@@ -747,7 +775,10 @@ function diagnosticToErrorInfo(d) {
747
775
  */
748
776
  function typecheckPart(partPath, toolDir) {
749
777
  const dts = resolveBrepjsTypes(partPath, toolDir);
750
- const options = { ...COMPILER_OPTIONS };
778
+ const options = {
779
+ ...COMPILER_OPTIONS,
780
+ ...jsxCompilerOptions(partPath)
781
+ };
751
782
  if (dts) options.paths = { brepjs: [dts] };
752
783
  const typesRoot = nodeTypesRoot(partPath, toolDir);
753
784
  if (typesRoot) {
@@ -800,12 +831,15 @@ function buildMaterialMap(m, spec) {
800
831
  }
801
832
  return map.size > 0 ? { map } : {};
802
833
  }
803
- async function loadPart(modulePath) {
834
+ var importGeneration = 0;
835
+ async function loadPart(modulePath, fresh = false) {
836
+ const url = new URL(pathToFileURL(modulePath).href);
837
+ if (fresh) url.searchParams.set("v", String(++importGeneration));
804
838
  try {
805
- return await import(pathToFileURL(modulePath).href);
839
+ return await import(url.href);
806
840
  } catch (e) {
807
841
  const msg = e instanceof Error ? e.message : String(e);
808
- if (/\.[mc]?tsx?$/.test(modulePath) && /import statement|file extension/i.test(msg)) throw new Error(`cannot load TypeScript part "${modulePath}": author parts in an ESM project (set "type": "module" in package.json) or rename the file to .mts. (${msg})`, { cause: e });
842
+ if (/\.[mc]?tsx?$/.test(modulePath) && /import statement|file extension/i.test(msg)) throw new Error(`cannot load TypeScript part "${modulePath}": author parts in an ESM project (set "type": "module" in package.json). (${msg})`, { cause: e });
809
843
  throw e;
810
844
  }
811
845
  }
@@ -865,7 +899,7 @@ async function runPart(modulePath, opts = {}) {
865
899
  const { isOk, mesh, exportGlb, exportSTEP } = brep;
866
900
  let mod;
867
901
  try {
868
- mod = await loadPart(modulePath);
902
+ mod = await loadPart(modulePath, opts.freshImport);
869
903
  } catch (e) {
870
904
  pushError(report, toErrorInfo("import failed", e));
871
905
  return finalize({
@@ -21,8 +21,9 @@ function toolDir() {
21
21
  }
22
22
  }
23
23
  function loaderUrl(dir) {
24
- const built = (0, node_path.resolve)(dir, "dist", "loader", "brepjsResolve.mjs");
25
24
  const source = (0, node_path.resolve)(dir, "src", "loader", "brepjsResolve.mjs");
25
+ const built = (0, node_path.resolve)(dir, "dist", "loader", "brepjsResolve.mjs");
26
+ if ({}.url.startsWith((0, node_url.pathToFileURL)((0, node_path.resolve)(dir, "src") + "/").href)) return (0, node_url.pathToFileURL)(source).href;
26
27
  return (0, node_url.pathToFileURL)((0, node_fs.existsSync)(built) ? built : source).href;
27
28
  }
28
29
  function registerHook() {
@@ -698,6 +699,33 @@ function packageRootOf(startDir, name) {
698
699
  dir = (0, node_path.dirname)(dir);
699
700
  }
700
701
  }
702
+ /**
703
+ * JSX options for a part come from the nearest tsconfig.json — the same source the
704
+ * runtime load hook reads — so `--check` and execution agree on the dialect
705
+ * (families projects set `jsx: react-jsx` + `jsxImportSource: brepjs-families`).
706
+ * A `.tsx` part with nothing configured defaults to the automatic runtime.
707
+ */
708
+ function jsxCompilerOptions(partPath) {
709
+ const out = {};
710
+ const configPath = typescript.default.findConfigFile((0, node_path.dirname)(partPath), (f) => typescript.default.sys.fileExists(f));
711
+ if (configPath) {
712
+ const host = {
713
+ ...typescript.default.sys,
714
+ onUnRecoverableConfigFileDiagnostic: () => {}
715
+ };
716
+ const parsed = typescript.default.getParsedCommandLineOfConfigFile(configPath, void 0, host);
717
+ if (parsed) {
718
+ for (const key of [
719
+ "jsx",
720
+ "jsxImportSource",
721
+ "jsxFactory",
722
+ "jsxFragmentFactory"
723
+ ]) if (parsed.options[key] !== void 0) out[key] = parsed.options[key];
724
+ }
725
+ }
726
+ if (partPath.endsWith(".tsx") && out.jsx === void 0) out.jsx = typescript.default.JsxEmit.ReactJSX;
727
+ return out;
728
+ }
701
729
  var COMPILER_OPTIONS = {
702
730
  target: typescript.default.ScriptTarget.ES2022,
703
731
  module: typescript.default.ModuleKind.ESNext,
@@ -749,7 +777,10 @@ function diagnosticToErrorInfo(d) {
749
777
  */
750
778
  function typecheckPart(partPath, toolDir) {
751
779
  const dts = resolveBrepjsTypes(partPath, toolDir);
752
- const options = { ...COMPILER_OPTIONS };
780
+ const options = {
781
+ ...COMPILER_OPTIONS,
782
+ ...jsxCompilerOptions(partPath)
783
+ };
753
784
  if (dts) options.paths = { brepjs: [dts] };
754
785
  const typesRoot = nodeTypesRoot(partPath, toolDir);
755
786
  if (typesRoot) {
@@ -802,12 +833,15 @@ function buildMaterialMap(m, spec) {
802
833
  }
803
834
  return map.size > 0 ? { map } : {};
804
835
  }
805
- async function loadPart(modulePath) {
836
+ var importGeneration = 0;
837
+ async function loadPart(modulePath, fresh = false) {
838
+ const url = new URL((0, node_url.pathToFileURL)(modulePath).href);
839
+ if (fresh) url.searchParams.set("v", String(++importGeneration));
806
840
  try {
807
- return await import((0, node_url.pathToFileURL)(modulePath).href);
841
+ return await import(url.href);
808
842
  } catch (e) {
809
843
  const msg = e instanceof Error ? e.message : String(e);
810
- if (/\.[mc]?tsx?$/.test(modulePath) && /import statement|file extension/i.test(msg)) throw new Error(`cannot load TypeScript part "${modulePath}": author parts in an ESM project (set "type": "module" in package.json) or rename the file to .mts. (${msg})`, { cause: e });
844
+ if (/\.[mc]?tsx?$/.test(modulePath) && /import statement|file extension/i.test(msg)) throw new Error(`cannot load TypeScript part "${modulePath}": author parts in an ESM project (set "type": "module" in package.json). (${msg})`, { cause: e });
811
845
  throw e;
812
846
  }
813
847
  }
@@ -867,7 +901,7 @@ async function runPart(modulePath, opts = {}) {
867
901
  const { isOk, mesh, exportGlb, exportSTEP } = brep;
868
902
  let mod;
869
903
  try {
870
- mod = await loadPart(modulePath);
904
+ mod = await loadPart(modulePath, opts.freshImport);
871
905
  } catch (e) {
872
906
  pushError(report, toErrorInfo("import failed", e));
873
907
  return finalize({