@jointhedots/gear 1.1.10 → 1.1.12

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 (44) 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 +2 -0
  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/config-loader.js +10 -10
  12. package/esm/model/helpers/create-manifests.js +26 -12
  13. package/esm/model/helpers/discover-workspace.js +71 -44
  14. package/esm/model/helpers/logger.js +25 -1
  15. package/esm/model/workspace.js +30 -8
  16. package/package.json +5 -3
  17. package/readme.md +136 -0
  18. package/schemas/declaration.schema.json +76 -0
  19. package/esm/builder/build-app-bundle.js.map +0 -1
  20. package/esm/builder/build-app-host.js.map +0 -1
  21. package/esm/builder/build-application.js.map +0 -1
  22. package/esm/builder/build-library.js.map +0 -1
  23. package/esm/builder/build-target.js.map +0 -1
  24. package/esm/builder/emit-dts.js.map +0 -1
  25. package/esm/builder/esbuild-plugins.js.map +0 -1
  26. package/esm/builder/helpers/path-helpers.js.map +0 -1
  27. package/esm/cli.js.map +0 -1
  28. package/esm/commands/init.js.map +0 -1
  29. package/esm/commands/make.js.map +0 -1
  30. package/esm/commands/publish.js.map +0 -1
  31. package/esm/commands/run.js.map +0 -1
  32. package/esm/commands/serve.js.map +0 -1
  33. package/esm/model/component.js.map +0 -1
  34. package/esm/model/helpers/config-loader.js.map +0 -1
  35. package/esm/model/helpers/create-manifests.js.map +0 -1
  36. package/esm/model/helpers/discover-workspace.js.map +0 -1
  37. package/esm/model/helpers/logger.js.map +0 -1
  38. package/esm/model/helpers/package-npm.js.map +0 -1
  39. package/esm/model/storage.js.map +0 -1
  40. package/esm/model/workspace.js.map +0 -1
  41. package/esm/publish/publish_aws_s3.js.map +0 -1
  42. package/esm/utils/file.js.map +0 -1
  43. package/esm/utils/graph-ordering.js.map +0 -1
  44. 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) {
@@ -36,6 +36,8 @@ export function create_application_monolith_target(opts) {
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
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);
39
41
  // Generate hotreload assets
40
42
  const html_injects = [];
41
43
  if (opts.devserver) {
@@ -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;
@@ -67,7 +67,7 @@ export function readConfigFileSync(path) {
67
67
  * Find config files matching a base name (e.g. "bundle.component") in a directory.
68
68
  * Returns all matching paths across supported extensions.
69
69
  */
70
- export function findConfigFiles(dir, baseName, fnames) {
70
+ export function findConfigFile(dir, baseName, fnames) {
71
71
  const results = [];
72
72
  for (const ext of config_extensions) {
73
73
  const fname = baseName + ext;
@@ -80,7 +80,12 @@ export function findConfigFiles(dir, baseName, fnames) {
80
80
  results.push(`${dir}/${fname}`);
81
81
  }
82
82
  }
83
- return results;
83
+ if (results.length === 0)
84
+ return undefined;
85
+ if (results.length > 1) {
86
+ throw new Error(`Multiple config files found for '${baseName}': ${results.join(", ")}. Only one format is allowed.`);
87
+ }
88
+ return results[0];
84
89
  }
85
90
  /**
86
91
  * Read a singleton config file (exactly one format must exist).
@@ -88,13 +93,8 @@ export function findConfigFiles(dir, baseName, fnames) {
88
93
  * Returns `undefined` if no matching file exists.
89
94
  */
90
95
  export async function readSingletonConfigFile(dir, baseName, fnames) {
91
- const paths = findConfigFiles(dir, baseName, fnames);
92
- if (paths.length === 0)
93
- return undefined;
94
- if (paths.length > 1) {
95
- throw new Error(`Multiple config files found for '${baseName}': ${paths.join(", ")}. Only one format is allowed.`);
96
- }
97
- const data = await readConfigFile(paths[0]);
98
- return data !== undefined ? { data, path: paths[0] } : undefined;
96
+ const path = findConfigFile(dir, baseName, fnames);
97
+ const data = await readConfigFile(path);
98
+ return data !== undefined ? { data, path } : undefined;
99
99
  }
100
100
  //# sourceMappingURL=config-loader.js.map
@@ -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
  }
@@ -5,9 +5,18 @@ import { checkComponentManifest } from "../component.js";
5
5
  import { makeNormalizedName, NameStyle } from "../../utils/normalized-name.js";
6
6
  import { create_manifests } from "./create-manifests.js";
7
7
  import { Bundle, Library, Workspace } from "../workspace.js";
8
- import { make_canonical_path, make_normalized_dirname, make_normalized_path, make_relative_path } from "../../utils/file.js";
9
- import { is_config_filename, readConfigFile, readSingletonConfigFile } from "./config-loader.js";
10
- const exclude_dirs = ["node_modules"];
8
+ import { file, make_canonical_path, make_normalized_dirname, make_normalized_path, make_relative_path } from "../../utils/file.js";
9
+ import { findConfigFile, is_config_filename, readConfigFile, readSingletonConfigFile } from "./config-loader.js";
10
+ const exclude_dirs = ["node_modules", ".git"];
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;
@@ -88,7 +97,9 @@ async function discover_library_components(lib, path, subdir = false) {
88
97
  const fstat = await Fsp.stat(fpath);
89
98
  if (fstat.isDirectory()) {
90
99
  if (!exclude_dirs.includes(fname)) {
91
- await discover_library_components(lib, fpath, true);
100
+ if (await discover_library(lib.workspace, fpath, lib.installed) === Discovered.None) {
101
+ await discover_library_components(lib, fpath, true);
102
+ }
92
103
  }
93
104
  }
94
105
  else if (fstat.isFile()) {
@@ -106,15 +117,6 @@ async function discover_library_components(lib, path, subdir = false) {
106
117
  }
107
118
  }
108
119
  }
109
- // Analyze library deployment manifest (supports json/yaml/yml/toml, singleton)
110
- const manifest_result = await readSingletonConfigFile(path, "bundle.manifest", fnames);
111
- if (manifest_result) {
112
- const manifest = manifest_result.data;
113
- for (const pub of manifest.data.components) {
114
- const fpath = path + "/" + (pub.ref ?? pub.id);
115
- await discover_component(lib, fpath);
116
- }
117
- }
118
120
  }
119
121
  function make_library_bundle_manifest(lib, file_desc) {
120
122
  let $id = file_desc?.$id;
@@ -165,46 +167,71 @@ function make_library_bundle_manifest(lib, file_desc) {
165
167
  };
166
168
  return manifest;
167
169
  }
168
- async function discover_library(ws, location) {
170
+ var Discovered;
171
+ (function (Discovered) {
172
+ Discovered[Discovered["None"] = 0] = "None";
173
+ Discovered[Discovered["Ignored"] = 1] = "Ignored";
174
+ Discovered[Discovered["Registered"] = 2] = "Registered";
175
+ })(Discovered || (Discovered = {}));
176
+ async function discover_library(ws, location, installed) {
169
177
  const lib_path = make_canonical_path(ws.path, location);
178
+ if (is_ignored_dir(ws, lib_path))
179
+ return Discovered.Ignored;
180
+ const package_path = lib_path + "/package.json";
181
+ const manifest_path = findConfigFile(lib_path, "bundle.manifest");
182
+ if (!file.exists(package_path) && !file.exists(manifest_path))
183
+ return Discovered.None;
170
184
  const lib_not_exists = ws.libraries.reduce((r, lib) => r && lib.path !== lib_path, true);
171
- if (lib_not_exists) {
172
- const lib_desc = await readJsonFile(lib_path + "/package.json");
173
- const bundle_result = await readSingletonConfigFile(lib_path, "bundle.component");
174
- const bundle_desc = bundle_result?.data;
175
- if (bundle_desc || lib_desc?.componentsContainer) {
176
- const other = ws.get_library(lib_desc.name);
177
- if (other) {
178
- if (lib_path.includes(other.path)) {
179
- ws.log.info(`ignore library build at ${lib_path}`);
180
- return;
181
- }
182
- else {
183
- throw new Error(`library '${lib_desc.name}' declared multiple times\n - ${other.path}\n - ${lib_path}`);
184
- }
185
+ if (!lib_not_exists)
186
+ return Discovered.Ignored;
187
+ const lib_desc = await readJsonFile(package_path);
188
+ if (!lib_desc?.name)
189
+ return Discovered.Ignored;
190
+ const manifest_desc = await readConfigFile(manifest_path);
191
+ if (manifest_desc || !installed) {
192
+ const other = ws.get_library(lib_desc.name);
193
+ if (other) {
194
+ if (lib_path.includes(other.path)) {
195
+ ws.log.info(`ignore library build at ${lib_path}`);
196
+ return Discovered.Ignored;
185
197
  }
186
- const lib = new Library(lib_desc.name, lib_path, lib_desc, ws);
187
- ws.libraries.push(lib);
188
- if (bundle_desc) {
189
- setup_library_bundle(lib, bundle_desc);
198
+ else {
199
+ throw new Error(`library '${lib_desc.name}' declared multiple times\n - ${other.path}\n - ${lib_path}`);
190
200
  }
191
- const lib_search_path = lib_path + "/node_modules";
192
- if (Fs.existsSync(lib_search_path)) {
193
- lib.search_directories.push(lib_search_path);
201
+ }
202
+ const lib = new Library(lib_desc.name, lib_path, lib_desc, ws, installed);
203
+ ws.libraries.push(lib);
204
+ ws.log.info(`+ 📚 library: ${installed ? "⏬" : "🐣"} ${lib.get_id()} (${make_relative_path(ws.path, location)})`);
205
+ // Setup library infos from bundle manifest
206
+ if (manifest_desc) {
207
+ setup_library_bundle(lib, manifest_desc);
208
+ // Analyze library deployment manifest (supports json/yaml/yml/toml, singleton)
209
+ for (const pub of manifest_desc.data.components) {
210
+ const fpath = lib_path + "/" + (pub.ref ?? pub.id);
211
+ await discover_component(lib, fpath);
194
212
  }
195
- await discover_library_components(lib, lib_path);
196
213
  }
214
+ // Setup node search directory
215
+ const lib_search_path = lib_path + "/node_modules";
216
+ if (Fs.existsSync(lib_search_path)) {
217
+ lib.search_directories.push(lib_search_path);
218
+ }
219
+ // Collect library declaration
220
+ await discover_library_components(lib, lib_path);
221
+ return Discovered.Registered;
197
222
  }
223
+ return Discovered.None;
198
224
  }
199
225
  async function discover_workspace_libraries(ws) {
200
226
  async function walk(dir) {
201
- await discover_library(ws, dir);
202
- for (const entry of Fs.readdirSync(dir, { withFileTypes: true })) {
203
- if (entry.name === "node_modules")
204
- continue;
205
- const fullPath = dir + "/" + entry.name;
206
- if (entry.isDirectory()) {
207
- await walk(fullPath);
227
+ if (await discover_library(ws, dir, false) === Discovered.None) {
228
+ for (const entry of Fs.readdirSync(dir, { withFileTypes: true })) {
229
+ if (entry.name === "node_modules")
230
+ continue;
231
+ const fullPath = dir + "/" + entry.name;
232
+ if (entry.isDirectory()) {
233
+ await walk(fullPath);
234
+ }
208
235
  }
209
236
  }
210
237
  }
@@ -245,7 +272,7 @@ export async function discover_workspace(ws) {
245
272
  }
246
273
  await discover_workspace_libraries(ws);
247
274
  for (const location in package_lock.packages) {
248
- await discover_library(ws, location);
275
+ await discover_library(ws, location, true);
249
276
  }
250
277
  for (const bun of ws.bundles) {
251
278
  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 "";