@jointhedots/gear 1.1.9 → 1.1.11

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.
Files changed (43) hide show
  1. package/esm/builder/build-app-bundle.js +3 -2
  2. package/esm/builder/build-app-host.js +2 -0
  3. package/esm/builder/build-application.js +27 -1
  4. package/esm/builder/build-library.js +3 -2
  5. package/esm/builder/build-target.js +18 -3
  6. package/esm/builder/esbuild-plugins.js +88 -11
  7. package/esm/commands/init.js +13 -0
  8. package/esm/commands/make.js +5 -1
  9. package/esm/commands/publish.js +4 -1
  10. package/esm/commands/serve.js +5 -2
  11. package/esm/model/helpers/create-manifests.js +26 -12
  12. package/esm/model/helpers/discover-workspace.js +21 -5
  13. package/esm/model/helpers/logger.js +35 -3
  14. package/esm/model/workspace.js +27 -7
  15. package/package.json +5 -4
  16. package/readme.md +136 -0
  17. package/schemas/declaration.schema.json +76 -0
  18. package/esm/builder/build-app-bundle.js.map +0 -1
  19. package/esm/builder/build-app-host.js.map +0 -1
  20. package/esm/builder/build-application.js.map +0 -1
  21. package/esm/builder/build-library.js.map +0 -1
  22. package/esm/builder/build-target.js.map +0 -1
  23. package/esm/builder/emit-dts.js.map +0 -1
  24. package/esm/builder/esbuild-plugins.js.map +0 -1
  25. package/esm/builder/helpers/path-helpers.js.map +0 -1
  26. package/esm/cli.js.map +0 -1
  27. package/esm/commands/init.js.map +0 -1
  28. package/esm/commands/make.js.map +0 -1
  29. package/esm/commands/publish.js.map +0 -1
  30. package/esm/commands/run.js.map +0 -1
  31. package/esm/commands/serve.js.map +0 -1
  32. package/esm/model/component.js.map +0 -1
  33. package/esm/model/helpers/config-loader.js.map +0 -1
  34. package/esm/model/helpers/create-manifests.js.map +0 -1
  35. package/esm/model/helpers/discover-workspace.js.map +0 -1
  36. package/esm/model/helpers/logger.js.map +0 -1
  37. package/esm/model/helpers/package-npm.js.map +0 -1
  38. package/esm/model/storage.js.map +0 -1
  39. package/esm/model/workspace.js.map +0 -1
  40. package/esm/publish/publish_aws_s3.js.map +0 -1
  41. package/esm/utils/file.js.map +0 -1
  42. package/esm/utils/graph-ordering.js.map +0 -1
  43. package/esm/utils/normalized-name.js.map +0 -1
@@ -19,15 +19,16 @@ export function create_bundle_target(opts) {
19
19
  const lib = library;
20
20
  const target = new BuildTarget(bundle.id, storage, lib.workspace, opts.devmode == true, opts.watch == true, opts.clean == true);
21
21
  const manifs = create_manifests(lib, bundle, opts.version);
22
+ // Prepare esm setup
23
+ target.esmodules.set_root(lib.path);
22
24
  // Add bundle package.json
23
25
  target.assets.add_static_json("package.json", manifs.package);
24
26
  // Add bundle types.d.ts
25
27
  target.tasks.push(new TypescriptDefinitionTask(target, lib));
26
28
  // Add bundle exporteds
27
- const { esmodules } = target;
28
29
  for (const exp_id in manifs.entries) {
29
30
  const exp = manifs.entries[exp_id];
30
- esmodules.add_entry(exp.basename, exp.source);
31
+ target.esmodules.add_entry(exp.basename, exp.source);
31
32
  }
32
33
  // Add bundle content
33
34
  if (bundle) {
@@ -12,6 +12,8 @@ function create_application_composable_target(opts) {
12
12
  const bundle = lib.bundle;
13
13
  const ws = lib.workspace;
14
14
  const target = new BuildTarget(name, opts.storage, ws, opts.devmode == true, opts.watch == true, opts.clean == true);
15
+ // Prepare esm setup
16
+ target.esmodules.set_root(lib.path);
15
17
  // Generate hotreload assets
16
18
  const html_injects = [];
17
19
  if (opts.devserver) {
@@ -13,8 +13,8 @@ function collect_app_libraries(app) {
13
13
  const ws = lib.workspace;
14
14
  const deps = {
15
15
  ...lib.descriptor.dependencies,
16
- ...lib.descriptor.devDependencies,
17
16
  ...lib.descriptor.peerDependencies,
17
+ ...lib.descriptor.optionalDependencies,
18
18
  };
19
19
  for (const depId in deps) {
20
20
  const lib = ws.get_library(depId);
@@ -35,6 +35,9 @@ export function create_application_monolith_target(opts) {
35
35
  const ws = lib.workspace;
36
36
  const target = new BuildTarget(name, opts.storage, ws, opts.devmode == true, opts.watch == true, opts.clean == true);
37
37
  const libs = collect_app_libraries(app);
38
+ target.log.info(`+ 🧭 app-library-graph: ${libs.map(lib => `${lib.name}@${lib.descriptor.version}`).join(", ")}`);
39
+ // Prepare esm setup
40
+ target.esmodules.set_root(lib.path);
38
41
  // Generate hotreload assets
39
42
  const html_injects = [];
40
43
  if (opts.devserver) {
@@ -100,13 +103,36 @@ export function create_application_monolith_target(opts) {
100
103
  // Add bundle manifest
101
104
  target.tasks.push(new BundleManifestTask(target, bundle));
102
105
  // Add workspace components
106
+ const added_components = new Map();
103
107
  for (const lib of libs) {
104
108
  if (lib.bundle) {
105
109
  for (const [path, desc] of lib.bundle.components) {
106
110
  if (!matchComponentSelection(components, desc.selectors))
107
111
  continue;
112
+ const cid = desc.$id;
113
+ const added = added_components.get(cid);
114
+ if (added) {
115
+ target.log.error({
116
+ id: "duplicate-component-skip",
117
+ text: `skip duplicate component '${cid}'`,
118
+ notes: [
119
+ {
120
+ title: "kept",
121
+ text: `${added.lib.name}@${added.lib.descriptor.version}`,
122
+ location: added.path,
123
+ },
124
+ {
125
+ title: "skipped",
126
+ text: `${lib.name}@${lib.descriptor.version}`,
127
+ location: path,
128
+ },
129
+ ],
130
+ });
131
+ continue;
132
+ }
108
133
  const baseDir = Path.dirname(path);
109
134
  target.add_component(desc, baseDir, lib);
135
+ added_components.set(cid, { lib, path });
110
136
  }
111
137
  }
112
138
  }
@@ -9,11 +9,12 @@ export function create_library_target(opts) {
9
9
  const lib = opts.library;
10
10
  const target = new BuildTarget(lib.name, opts.storage, lib.workspace, opts.devmode == true, opts.watch == true, opts.clean == true);
11
11
  const manifs = create_manifests(lib, lib.bundle, opts.version);
12
+ // Prepare esm setup
13
+ target.esmodules.set_root(lib.path);
12
14
  // Add bundle exporteds
13
- const { esmodules } = target;
14
15
  for (const exp_id in manifs.entries) {
15
16
  const exp = manifs.entries[exp_id];
16
- esmodules.add_entry(exp.basename, exp.source);
17
+ target.esmodules.add_entry(exp.basename, exp.source);
17
18
  }
18
19
  // Add library types.d.ts
19
20
  target.tasks.push(new TypescriptDefinitionTask(target, lib));
@@ -87,6 +87,10 @@ export class ESModulesTask extends BuildTask {
87
87
  context = null;
88
88
  transaction = null;
89
89
  polyfilled = true;
90
+ rootPath = null;
91
+ set_root(path) {
92
+ this.rootPath = path;
93
+ }
90
94
  add_entry(name, path) {
91
95
  this.entries[name] = path;
92
96
  this.imports[path] = name;
@@ -194,17 +198,28 @@ export class BuildTarget {
194
198
  }
195
199
  this.components.set(id, manifest);
196
200
  }
201
+ async chrona(task) {
202
+ const name = task.constructor.name;
203
+ const start = Date.now();
204
+ await task.execute();
205
+ const elapsed = (Date.now() - start) / 1000;
206
+ this.log.info(`⏱ ${name} completed in ${elapsed.toFixed(2)}s`);
207
+ }
197
208
  async build() {
209
+ const buildStartTime = Date.now();
198
210
  if (this.clean)
199
211
  this.storage.clean();
200
212
  // Make assets
201
- await this.assets.execute();
213
+ await this.chrona(this.assets);
202
214
  // Make generic tasks
203
215
  for (const task of this.tasks) {
204
- await task.execute();
216
+ await this.chrona(task);
205
217
  }
206
218
  // Make esmodules
207
- await this.esmodules.execute();
219
+ await this.chrona(this.esmodules);
220
+ // Trace time
221
+ const buildTime = (Date.now() - buildStartTime) / 1000;
222
+ this.log.info(`Build completed in ${buildTime.toFixed(2)}s`);
208
223
  if (this.watch) {
209
224
  await new Promise((resolve) => {
210
225
  process.on('SIGQUIT', () => resolve(null));
@@ -9,6 +9,17 @@ import { ESModulesTask } from "./build-target.js";
9
9
  import { PackageRootDir } from "../utils/file.js";
10
10
  import { Library } from "../model/workspace.js";
11
11
  const VirtualOutDir = Path.normalize('X:/');
12
+ function getEsbuildLogLevel(mode) {
13
+ switch (mode) {
14
+ case "verbose":
15
+ return "debug";
16
+ case "debug":
17
+ return "info";
18
+ case "normal":
19
+ default:
20
+ return "silent";
21
+ }
22
+ }
12
23
  export async function create_esbuild_context(task, devmode) {
13
24
  const ws = task.target.workspace;
14
25
  // Define modules mapping - using @jspm/core polyfills (same as Vite)
@@ -40,6 +51,7 @@ export async function create_esbuild_context(task, devmode) {
40
51
  }
41
52
  return result;
42
53
  }
54
+ const tsconfig = new TSConfig(task.rootPath, ws.path);
43
55
  const modules_mapping = {
44
56
  task,
45
57
  routeds: {
@@ -74,13 +86,14 @@ export async function create_esbuild_context(task, devmode) {
74
86
  format: 'esm',
75
87
  target: 'es2022',
76
88
  platform: "browser",
89
+ tsconfig: tsconfig.configFile ?? undefined,
77
90
  sourcemap: devmode ? "linked" : false,
78
91
  minify: devmode ? false : true,
79
92
  bundle: true,
80
93
  splitting: true,
81
94
  treeShaking: true,
82
95
  write: false,
83
- logLevel: process.env.ESBUILD_LOG_LEVEL || 'silent',
96
+ logLevel: getEsbuildLogLevel(ws.logger.mode),
84
97
  chunkNames: "chunk.[hash]",
85
98
  jsx: "automatic",
86
99
  jsxImportSource: "react",
@@ -94,8 +107,8 @@ export async function create_esbuild_context(task, devmode) {
94
107
  ...workspace_constants,
95
108
  },
96
109
  plugins: [
110
+ ESModuleResolverPlugin(modules_mapping, tsconfig),
97
111
  ...task.plugins,
98
- ESModuleResolverPlugin(modules_mapping),
99
112
  StyleSheetPlugin(task),
100
113
  StoragePlugin(task),
101
114
  ],
@@ -245,11 +258,10 @@ export function StoragePlugin(task) {
245
258
  return {
246
259
  name: "dipatch-files",
247
260
  setup: (build) => {
248
- var buildStartTime = 0;
261
+ let buildStartTime = 0;
249
262
  build.onStart(() => {
250
263
  task.log.clear();
251
- task.log.info("Build started...");
252
- buildStartTime = performance.now();
264
+ buildStartTime = Date.now();
253
265
  });
254
266
  build.onEnd(async (result) => {
255
267
  if (result.errors.length > 0) {
@@ -262,7 +274,7 @@ export function StoragePlugin(task) {
262
274
  task.log.warn(warning);
263
275
  }
264
276
  if (result.outputFiles) {
265
- const storeStart = performance.now();
277
+ const storeStart = Date.now();
266
278
  const tx = task.target.edit();
267
279
  for (const file of result.outputFiles) {
268
280
  if (file.path.startsWith(VirtualOutDir)) {
@@ -275,9 +287,9 @@ export function StoragePlugin(task) {
275
287
  }
276
288
  }
277
289
  task.target.store();
278
- const storeTime = (performance.now() - storeStart) / 1000;
279
- const buildTime = (performance.now() - buildStartTime) / 1000;
280
- task.log.info(`Build completed with ${result.outputFiles.length} file(s) in ${buildTime.toFixed(2)}s (store: ${storeTime.toFixed(2)}s)`);
290
+ const storeTime = (Date.now() - storeStart) / 1000;
291
+ const buildTime = (Date.now() - buildStartTime) / 1000;
292
+ task.log.info(`Build ES modules in ${result.outputFiles.length} file(s) (compile: ${buildTime.toFixed(2)}s, store: ${storeTime.toFixed(2)}s)`);
281
293
  }
282
294
  return null;
283
295
  });
@@ -389,7 +401,7 @@ export function DependencyDeduplicationPlugin(libraries, rootNodeModules, log) {
389
401
  const is = isSingletonPackage(result.path, pkg);
390
402
  singletonCache.set(pkg, is);
391
403
  if (is)
392
- log.info(`[dedup] singleton: ${pkg}`);
404
+ log.debug(`[dedup] singleton: ${pkg}`);
393
405
  }
394
406
  // Singleton → force root resolution
395
407
  if (singletonCache.get(pkg)) {
@@ -411,7 +423,65 @@ export function DependencyDeduplicationPlugin(libraries, rootNodeModules, log) {
411
423
  }
412
424
  };
413
425
  }
414
- export function ESModuleResolverPlugin(opts) {
426
+ /**
427
+ * Parses tsconfig.json compilerOptions (paths + baseUrl) and exposes resolved alias patterns.
428
+ * Looks for tsconfig.json in libraryPath first, then workspacePath as fallback.
429
+ */
430
+ export class TSConfig {
431
+ baseUrl = null;
432
+ patterns = [];
433
+ configFile = null;
434
+ configData = null;
435
+ constructor(libraryPath, workspacePath) {
436
+ const tsconfigFile = [libraryPath, workspacePath]
437
+ .filter(Boolean)
438
+ .map(dir => Path.join(dir, 'tsconfig.json'))
439
+ .find(f => Fs.existsSync(f));
440
+ if (!tsconfigFile)
441
+ return;
442
+ try {
443
+ const raw = Fs.readFileSync(tsconfigFile, 'utf-8');
444
+ const cleaned = raw.replace(/^\s*\/\/.*$/gm, '').replace(/,\s*([}\]])/g, '$1');
445
+ const tsconfig = JSON.parse(cleaned);
446
+ const opts = tsconfig.compilerOptions;
447
+ if (!opts?.paths)
448
+ return;
449
+ this.configData = tsconfig;
450
+ this.configFile = tsconfigFile;
451
+ this.baseUrl = Path.resolve(Path.dirname(tsconfigFile), opts.baseUrl || '.');
452
+ for (const [alias, targets] of Object.entries(opts.paths)) {
453
+ for (const target of targets) {
454
+ const prefix = alias === '*' ? '' : alias.endsWith('/*') ? alias.slice(0, -2) : alias;
455
+ const targetPath = target.endsWith('/*') ? target.slice(0, -2) : target;
456
+ this.patterns.push([prefix, Path.resolve(this.baseUrl, targetPath)]);
457
+ }
458
+ }
459
+ }
460
+ catch { }
461
+ }
462
+ get hasAliases() {
463
+ return this.patterns.length > 0;
464
+ }
465
+ /** Resolve an import path against tsconfig path aliases. Returns the resolved file path or null. */
466
+ resolve(importPath, resolveFilePath) {
467
+ for (const [prefix, dir] of this.patterns) {
468
+ if (prefix === '') {
469
+ // Wildcard "*" pattern: try resolving the full import path under the target dir
470
+ const resolved = resolveFilePath('./' + importPath, dir);
471
+ if (resolved)
472
+ return resolved;
473
+ }
474
+ else if (importPath === prefix || importPath.startsWith(prefix + '/')) {
475
+ const rest = importPath.slice(prefix.length);
476
+ const resolved = resolveFilePath('.' + rest, dir);
477
+ if (resolved)
478
+ return resolved;
479
+ }
480
+ }
481
+ return null;
482
+ }
483
+ }
484
+ export function ESModuleResolverPlugin(opts, tsconfigPaths) {
415
485
  const internalLoaders = {
416
486
  ".ts": "ts", ".tsx": "tsx", ".js": "js", ".jsx": "jsx",
417
487
  ".json": "json", ".txt": "text", ".md": "text", ".css": "css",
@@ -481,6 +551,13 @@ export function ESModuleResolverPlugin(opts) {
481
551
  return { path: resolved, namespace: "file" };
482
552
  }
483
553
  }
554
+ // 4. Resolve tsconfig path aliases (e.g. @app/* -> ./src/*)
555
+ if (tsconfigPaths?.hasAliases) {
556
+ const resolved = tsconfigPaths.resolve(args.path, resolveFilePath);
557
+ if (resolved) {
558
+ return { path: resolved, namespace: "file" };
559
+ }
560
+ }
484
561
  });
485
562
  // Loader for internal modules
486
563
  build.onLoad({ filter: /.*/, namespace: 'internal' }, (args) => {
@@ -36,6 +36,19 @@ export function command_init() {
36
36
  ],
37
37
  "url": "./node_modules/@jointhedots/gear/schemas/component.schema.json"
38
38
  },
39
+ {
40
+ "fileMatch": [
41
+ "declaration.json",
42
+ "*.declaration.json",
43
+ "declaration.yaml",
44
+ "*.declaration.yaml",
45
+ "declaration.yml",
46
+ "*.declaration.yml",
47
+ "declaration.toml",
48
+ "*.declaration.toml",
49
+ ],
50
+ "url": "./node_modules/@jointhedots/gear/schemas/declaration.schema.json"
51
+ },
39
52
  ]
40
53
  });
41
54
  }
@@ -61,7 +61,11 @@ export function command_make() {
61
61
  default: "./dist",
62
62
  }),
63
63
  handler: async (argv) => {
64
- const ws = await open_workspace(argv.ws, argv.devmode);
64
+ const ws = await open_workspace({
65
+ workspace_path: argv.ws,
66
+ devmode: argv.devmode,
67
+ ignored_directory: Path.resolve(argv.dist),
68
+ });
65
69
  if (argv.devmode)
66
70
  ws.log.warn("Use devmode");
67
71
  let version = argv.versioned;
@@ -25,7 +25,10 @@ export function command_publish() {
25
25
  }),
26
26
  handler: async (argv) => {
27
27
  const outputDir = Path.resolve(argv.dist);
28
- const ws = await open_workspace(".", false);
28
+ const ws = await open_workspace({
29
+ workspace_path: ".",
30
+ devmode: false,
31
+ });
29
32
  await publish_aws_s3(argv.app, ws, argv.bucket, argv.region, outputDir);
30
33
  }
31
34
  };
@@ -38,10 +38,13 @@ export function command_serve() {
38
38
  default: "./dist",
39
39
  }),
40
40
  handler: async (argv) => {
41
- const ws = await open_workspace(argv.ws, argv.devmode);
41
+ const ws = await open_workspace({
42
+ workspace_path: argv.ws,
43
+ devmode: argv.devmode,
44
+ });
42
45
  const app = ws.get_application(argv.app);
43
46
  if (!app)
44
- throw new Error(`Application '${argv.app}' not exists. Note: 'package.json' shall have "componentsContainer": true`);
47
+ throw new Error(`Application '${argv.app}' not exists.`);
45
48
  if (argv.devmode)
46
49
  ws.log.warn("Use devmode");
47
50
  let version = argv.versioned;
@@ -1,4 +1,19 @@
1
+ import Path from "node:path";
1
2
  import { makeComponentPublication } from "../component.js";
3
+ function add_export_entry(exports, lib, key, value, baseDir) {
4
+ const basename = key.startsWith("./") ? key.slice(2) : key;
5
+ const filename = lib.make_file_id("export", basename);
6
+ const source_ref = typeof value === "string" ? value : value?.import ?? value?.default ?? value?.require;
7
+ const entry = source_ref ? lib.resolve_entry_path(source_ref, baseDir) : null;
8
+ const id = `${lib.name}${basename === "." ? "" : "/" + basename}`;
9
+ exports[id] = {
10
+ id,
11
+ exported: key,
12
+ basename: filename,
13
+ filename: `${filename}.js`,
14
+ source: entry,
15
+ };
16
+ }
2
17
  function create_export_map(lib, bun) {
3
18
  const exports = {};
4
19
  if (bun) {
@@ -13,19 +28,18 @@ function create_export_map(lib, bun) {
13
28
  };
14
29
  }
15
30
  }
16
- for (const key in lib.descriptor.exports) {
31
+ for (const key in (lib.descriptor.exports || {})) {
17
32
  const exp = lib.descriptor.exports[key];
18
- const basename = key.startsWith("./") ? key.slice(2) : key;
19
- const filename = lib.make_file_id("export", basename);
20
- const entry = lib.resolve_entry_path(typeof exp === "string" ? exp : exp?.import, lib.path);
21
- const id = `${lib.name}${basename === "." ? "" : "/" + basename}`;
22
- exports[id] = {
23
- id,
24
- exported: key,
25
- basename: filename,
26
- filename: `${filename}.js`,
27
- source: entry,
28
- };
33
+ add_export_entry(exports, lib, key, exp, lib.path);
34
+ }
35
+ for (const [decl_path, declaration] of lib.declarations) {
36
+ if (!declaration.exports)
37
+ continue;
38
+ const decl_base_dir = Path.dirname(decl_path);
39
+ for (const key in declaration.exports) {
40
+ const exp = declaration.exports[key];
41
+ add_export_entry(exports, lib, key, exp, decl_base_dir);
42
+ }
29
43
  }
30
44
  return exports;
31
45
  }
@@ -8,6 +8,15 @@ import { Bundle, Library, Workspace } from "../workspace.js";
8
8
  import { make_canonical_path, make_normalized_dirname, make_normalized_path, make_relative_path } from "../../utils/file.js";
9
9
  import { is_config_filename, readConfigFile, readSingletonConfigFile } from "./config-loader.js";
10
10
  const exclude_dirs = ["node_modules"];
11
+ function is_ignored_dir(ws, path) {
12
+ const normalized_path = make_normalized_path(path);
13
+ for (const ignored of ws.ignored_directories) {
14
+ if (normalized_path === ignored || normalized_path.startsWith(ignored + "/")) {
15
+ return true;
16
+ }
17
+ }
18
+ return false;
19
+ }
11
20
  function setup_library_bundle(lib, bundle_desc) {
12
21
  const manif = make_library_bundle_manifest(lib, bundle_desc);
13
22
  const ws = lib.workspace;
@@ -165,19 +174,23 @@ function make_library_bundle_manifest(lib, file_desc) {
165
174
  };
166
175
  return manifest;
167
176
  }
168
- async function discover_library(ws, location) {
177
+ async function discover_library(ws, location, installed) {
169
178
  const lib_path = make_canonical_path(ws.path, location);
179
+ if (is_ignored_dir(ws, lib_path))
180
+ return false;
170
181
  const lib_not_exists = ws.libraries.reduce((r, lib) => r && lib.path !== lib_path, true);
171
182
  if (lib_not_exists) {
172
183
  const lib_desc = await readJsonFile(lib_path + "/package.json");
184
+ if (!lib_desc?.name)
185
+ return false;
173
186
  const bundle_result = await readSingletonConfigFile(lib_path, "bundle.component");
174
187
  const bundle_desc = bundle_result?.data;
175
- if (bundle_desc || lib_desc?.componentsContainer) {
188
+ if (bundle_desc || !installed) {
176
189
  const other = ws.get_library(lib_desc.name);
177
190
  if (other) {
178
191
  if (lib_path.includes(other.path)) {
179
192
  ws.log.info(`ignore library build at ${lib_path}`);
180
- return;
193
+ return false;
181
194
  }
182
195
  else {
183
196
  throw new Error(`library '${lib_desc.name}' declared multiple times\n - ${other.path}\n - ${lib_path}`);
@@ -185,6 +198,7 @@ async function discover_library(ws, location) {
185
198
  }
186
199
  const lib = new Library(lib_desc.name, lib_path, lib_desc, ws);
187
200
  ws.libraries.push(lib);
201
+ ws.log.info(`+ 📚 library: ${lib.get_id()} (${make_relative_path(ws.path, location)})`);
188
202
  if (bundle_desc) {
189
203
  setup_library_bundle(lib, bundle_desc);
190
204
  }
@@ -193,12 +207,14 @@ async function discover_library(ws, location) {
193
207
  lib.search_directories.push(lib_search_path);
194
208
  }
195
209
  await discover_library_components(lib, lib_path);
210
+ return true;
196
211
  }
197
212
  }
213
+ return false;
198
214
  }
199
215
  async function discover_workspace_libraries(ws) {
200
216
  async function walk(dir) {
201
- await discover_library(ws, dir);
217
+ await discover_library(ws, dir, false);
202
218
  for (const entry of Fs.readdirSync(dir, { withFileTypes: true })) {
203
219
  if (entry.name === "node_modules")
204
220
  continue;
@@ -245,7 +261,7 @@ export async function discover_workspace(ws) {
245
261
  }
246
262
  await discover_workspace_libraries(ws);
247
263
  for (const location in package_lock.packages) {
248
- await discover_library(ws, location);
264
+ await discover_library(ws, location, true);
249
265
  }
250
266
  for (const bun of ws.bundles) {
251
267
  if (bun.source) {
@@ -1,3 +1,4 @@
1
+ import Process from "node:process";
1
2
  export class Log {
2
3
  id;
3
4
  entries = [];
@@ -41,7 +42,12 @@ export class Log {
41
42
  }
42
43
  export class Logger {
43
44
  loggers = new Map();
44
- silentKinds = new Set(["trace", "debug"]);
45
+ mode;
46
+ silentKinds;
47
+ constructor(mode = getLogModeFromEnv()) {
48
+ this.mode = mode;
49
+ this.silentKinds = getSilentKinds(mode);
50
+ }
45
51
  get(id) {
46
52
  let logger = this.loggers.get(id);
47
53
  if (!logger) {
@@ -63,6 +69,24 @@ export class Logger {
63
69
  this.loggers.clear();
64
70
  }
65
71
  }
72
+ function getLogModeFromEnv() {
73
+ const value = Process.env.JTDGEAR_LOG_MODE?.trim().toLowerCase();
74
+ if (value === "debug" || value === "verbose" || value === "normal") {
75
+ return value;
76
+ }
77
+ return "normal";
78
+ }
79
+ function getSilentKinds(mode) {
80
+ switch (mode) {
81
+ case "verbose":
82
+ return new Set();
83
+ case "debug":
84
+ return new Set(["trace"]);
85
+ case "normal":
86
+ default:
87
+ return new Set(["trace", "debug"]);
88
+ }
89
+ }
66
90
  export function stringifyLocation(loc) {
67
91
  if (!loc)
68
92
  return "";
@@ -83,8 +107,12 @@ export function stringifyLogEntry(loggerId, entry) {
83
107
  const locStr = stringifyLocation(entry.message.location);
84
108
  lines.push(`${prefix}${locStr ? ` ${locStr}` : ""} ${entry.message.text}`);
85
109
  for (const note of entry.message.notes ?? []) {
110
+ const noteTitle = note.title || "note";
86
111
  const noteLocStr = stringifyLocation(note.location);
87
- lines.push(` note:${noteLocStr ? ` ${noteLocStr}` : ""} ${note.text}`);
112
+ lines.push(` ${noteTitle}: ${note.text}`);
113
+ if (noteLocStr) {
114
+ lines.push(` at ${noteLocStr}`);
115
+ }
88
116
  }
89
117
  return lines.join("\n");
90
118
  }
@@ -108,8 +136,12 @@ export function stringifyLogEntryPretty(loggerId, entry) {
108
136
  lines.push(` ${dim}↪ ${locStr}${reset}`);
109
137
  }
110
138
  for (const note of entry.message.notes ?? []) {
139
+ const noteTitle = note.title || "note";
111
140
  const noteLocStr = stringifyLocation(note.location);
112
- lines.push(` ${dim}↳${reset} ${noteLocStr ? `${dim}${noteLocStr}${reset} ` : ""}${note.text}`);
141
+ lines.push(` ${dim}↳${reset} ${bold}${noteTitle}:${reset} ${note.text}`);
142
+ if (noteLocStr) {
143
+ lines.push(` ${dim}at ${noteLocStr}${reset}`);
144
+ }
113
145
  }
114
146
  return lines.join("\n");
115
147
  }
@@ -109,6 +109,7 @@ export class Workspace {
109
109
  libraries = [];
110
110
  constants = {};
111
111
  search_directories = [];
112
+ ignored_directories = new Set();
112
113
  logger = new Logger();
113
114
  log;
114
115
  constructor(name, version, path, devmode) {
@@ -152,28 +153,32 @@ export class Workspace {
152
153
  return null;
153
154
  }
154
155
  }
155
- export async function open_workspace(workspace_path, devmode) {
156
- workspace_path = Path.resolve(workspace_path);
156
+ export async function open_workspace(options) {
157
+ const workspace_path = Path.resolve(options.workspace_path);
158
+ const { devmode } = options;
159
+ const parsed_env = load_env_from_cwd_parents();
157
160
  const package_json = await readJsonFile(workspace_path + "/package.json");
158
161
  if (!package_json)
159
162
  throw new Error(`No 'package.json' found at workspace path: ${workspace_path}`);
160
163
  const ws = new Workspace(package_json.name, package_json.version, workspace_path, devmode);
161
- ws.constants = patch_constants_from_env(package_json.constants || {}, devmode);
164
+ if (options.ignored_directory) {
165
+ ws.ignored_directories.add(Path.resolve(options.ignored_directory).replace(/\\/g, "/"));
166
+ }
167
+ ws.constants = patch_constants_from_env(package_json.constants || {}, devmode, parsed_env);
162
168
  await discover_workspace(ws);
163
169
  return ws;
164
170
  }
165
- function patch_constants_from_env(constants, devmode) {
166
- const env = DotEnv.config();
171
+ function patch_constants_from_env(constants, devmode, parsedEnv) {
167
172
  for (const key in constants) {
168
173
  if (devmode) {
169
- const dvalue = env.parsed["DCONST_" + key];
174
+ const dvalue = parsedEnv["DCONST_" + key];
170
175
  if (dvalue !== undefined) {
171
176
  constants[key] = dvalue;
172
177
  continue;
173
178
  }
174
179
  }
175
180
  {
176
- const value = env.parsed["CONST_" + key];
181
+ const value = parsedEnv["CONST_" + key];
177
182
  if (value !== undefined) {
178
183
  constants[key] = value;
179
184
  continue;
@@ -182,6 +187,21 @@ function patch_constants_from_env(constants, devmode) {
182
187
  }
183
188
  return constants;
184
189
  }
190
+ function load_env_from_cwd_parents() {
191
+ let currentPath = Process.cwd();
192
+ while (true) {
193
+ const envPath = Path.join(currentPath, ".env");
194
+ if (Fs.existsSync(envPath)) {
195
+ const env = DotEnv.config({ path: envPath });
196
+ return env.parsed || {};
197
+ }
198
+ const parentPath = Path.dirname(currentPath);
199
+ if (parentPath === currentPath)
200
+ break;
201
+ currentPath = parentPath;
202
+ }
203
+ return {};
204
+ }
185
205
  export function matchComponentSelection(components, selectors) {
186
206
  if (components?.selectors) {
187
207
  if (!selectors)