wireweaver 0.1.1 → 0.1.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.
@@ -395,7 +395,8 @@ function discoverInterfaceImplementations(root, extraScanDirs = []) {
395
395
  scanSourceFilesForImplementations(root, implementations, allClassHeritage, beanReturnTypes, false);
396
396
  for (const dir of extraScanDirs) {
397
397
  const packageRoot = findPackageRoot(dir);
398
- scanSourceFilesForImplementations(dir, implementations, allClassHeritage, beanReturnTypes, true, packageRoot);
398
+ const componentNames = collectComponentClassNames(dir);
399
+ scanSourceFilesForImplementations(dir, implementations, allClassHeritage, beanReturnTypes, true, packageRoot, componentNames);
399
400
  }
400
401
  for (const typeName of beanReturnTypes) {
401
402
  const entries = allClassHeritage.get(typeName);
@@ -411,7 +412,7 @@ function discoverInterfaceImplementations(root, extraScanDirs = []) {
411
412
  }
412
413
  return implementations;
413
414
  }
414
- function scanSourceFilesForImplementations(root, implementations, allClassHeritage, beanReturnTypes, allowNodeModules, packageRoot) {
415
+ function scanSourceFilesForImplementations(root, implementations, allClassHeritage, beanReturnTypes, allowNodeModules, packageRoot, componentNames = /* @__PURE__ */ new Set()) {
415
416
  for (const sourceFile of getScanSourceFiles(root, allowNodeModules)) {
416
417
  import_typescript.default.forEachChild(sourceFile, (node) => {
417
418
  if (import_typescript.default.isClassDeclaration(node) && node.name) {
@@ -426,7 +427,7 @@ function scanSourceFilesForImplementations(root, implementations, allClassHerita
426
427
  heritageEntries.push({ interfaceName, filePath: sourceFile.fileName, importSpecifier });
427
428
  allClassHeritage.set(node.name.text, heritageEntries);
428
429
  }
429
- if (!hasServiceDecorator(node)) continue;
430
+ if (!hasServiceDecorator(node) && !componentNames.has(node.name.text)) continue;
430
431
  const existing = implementations.get(interfaceName) ?? [];
431
432
  if (existing.some((impl) => impl.className === node.name.text && normalizeFilePath(impl.filePath) === normalizeFilePath(sourceFile.fileName))) {
432
433
  continue;
@@ -537,14 +538,17 @@ function buildImportInsertions(code, sourceFile, importsToInject, sideEffectImpo
537
538
  if (unresolved.length === 0) continue;
538
539
  const existing = findImportByModule(sourceFile, importPath);
539
540
  if (existing && existing.importClause?.namedBindings && import_typescript.default.isNamedImports(existing.importClause.namedBindings)) {
540
- const existingNames = new Set(existing.importClause.namedBindings.elements.map((element) => element.name.text));
541
+ const namedImports = existing.importClause.namedBindings;
542
+ const existingNames = new Set(namedImports.elements.map((element) => element.name.text));
541
543
  const missing = unresolved.filter((name) => !existingNames.has(name));
542
544
  if (missing.length === 0) continue;
543
- const namedImports = existing.importClause.namedBindings;
545
+ const hasElements = namedImports.elements.length > 0;
546
+ const insertionPoint2 = namedImports.getEnd() - 1;
547
+ const prefix = hasElements ? ", " : "";
544
548
  replacements.push({
545
- start: namedImports.getStart(sourceFile) + 1,
546
- end: namedImports.getEnd() - 1,
547
- text: `${[...existingNames, ...missing].sort().join(", ")}`
549
+ start: insertionPoint2,
550
+ end: insertionPoint2,
551
+ text: `${prefix}${missing.sort().join(", ")}`
548
552
  });
549
553
  continue;
550
554
  }
@@ -651,6 +655,51 @@ function resolveExtraScanDirs(root, extraScanDirs) {
651
655
  if (!extraScanDirs || extraScanDirs.length === 0) return [];
652
656
  return extraScanDirs.map((dir) => (0, import_node_path.isAbsolute)(dir) ? dir : (0, import_node_path.resolve)(root, dir));
653
657
  }
658
+ function collectComponentClassNames(dir) {
659
+ const names = /* @__PURE__ */ new Set();
660
+ const stage3 = /_([A-Za-z_$][\w$]*)_decorators\s*=\s*\[([^\]]*)\]/g;
661
+ const legacy = /__decorate\(\s*\[([^\]]*?)\]\s*,\s*([A-Za-z_$][\w$]*)/g;
662
+ function containsComponentDecorator(decoratorList) {
663
+ return /\b(Component|Service)\s*\(/.test(decoratorList);
664
+ }
665
+ function walk(current) {
666
+ let entries;
667
+ try {
668
+ entries = (0, import_node_fs.readdirSync)(current);
669
+ } catch {
670
+ return;
671
+ }
672
+ for (const name of entries) {
673
+ const fullPath = (0, import_node_path.join)(current, name);
674
+ let isDir = false;
675
+ try {
676
+ isDir = (0, import_node_fs.statSync)(fullPath).isDirectory();
677
+ } catch {
678
+ continue;
679
+ }
680
+ if (isDir) {
681
+ if (name === "node_modules") continue;
682
+ walk(fullPath);
683
+ continue;
684
+ }
685
+ if (!/\.(js|cjs|mjs)$/.test(name)) continue;
686
+ let content;
687
+ try {
688
+ content = (0, import_node_fs.readFileSync)(fullPath, "utf-8");
689
+ } catch {
690
+ continue;
691
+ }
692
+ for (const match of content.matchAll(stage3)) {
693
+ if (containsComponentDecorator(match[2])) names.add(match[1]);
694
+ }
695
+ for (const match of content.matchAll(legacy)) {
696
+ if (containsComponentDecorator(match[1])) names.add(match[2]);
697
+ }
698
+ }
699
+ }
700
+ walk(dir);
701
+ return names;
702
+ }
654
703
  function findPackageRoot(startDir) {
655
704
  let current = startDir;
656
705
  while (true) {
@@ -358,7 +358,8 @@ function discoverInterfaceImplementations(root, extraScanDirs = []) {
358
358
  scanSourceFilesForImplementations(root, implementations, allClassHeritage, beanReturnTypes, false);
359
359
  for (const dir of extraScanDirs) {
360
360
  const packageRoot = findPackageRoot(dir);
361
- scanSourceFilesForImplementations(dir, implementations, allClassHeritage, beanReturnTypes, true, packageRoot);
361
+ const componentNames = collectComponentClassNames(dir);
362
+ scanSourceFilesForImplementations(dir, implementations, allClassHeritage, beanReturnTypes, true, packageRoot, componentNames);
362
363
  }
363
364
  for (const typeName of beanReturnTypes) {
364
365
  const entries = allClassHeritage.get(typeName);
@@ -374,7 +375,7 @@ function discoverInterfaceImplementations(root, extraScanDirs = []) {
374
375
  }
375
376
  return implementations;
376
377
  }
377
- function scanSourceFilesForImplementations(root, implementations, allClassHeritage, beanReturnTypes, allowNodeModules, packageRoot) {
378
+ function scanSourceFilesForImplementations(root, implementations, allClassHeritage, beanReturnTypes, allowNodeModules, packageRoot, componentNames = /* @__PURE__ */ new Set()) {
378
379
  for (const sourceFile of getScanSourceFiles(root, allowNodeModules)) {
379
380
  ts.forEachChild(sourceFile, (node) => {
380
381
  if (ts.isClassDeclaration(node) && node.name) {
@@ -389,7 +390,7 @@ function scanSourceFilesForImplementations(root, implementations, allClassHerita
389
390
  heritageEntries.push({ interfaceName, filePath: sourceFile.fileName, importSpecifier });
390
391
  allClassHeritage.set(node.name.text, heritageEntries);
391
392
  }
392
- if (!hasServiceDecorator(node)) continue;
393
+ if (!hasServiceDecorator(node) && !componentNames.has(node.name.text)) continue;
393
394
  const existing = implementations.get(interfaceName) ?? [];
394
395
  if (existing.some((impl) => impl.className === node.name.text && normalizeFilePath(impl.filePath) === normalizeFilePath(sourceFile.fileName))) {
395
396
  continue;
@@ -500,14 +501,17 @@ function buildImportInsertions(code, sourceFile, importsToInject, sideEffectImpo
500
501
  if (unresolved.length === 0) continue;
501
502
  const existing = findImportByModule(sourceFile, importPath);
502
503
  if (existing && existing.importClause?.namedBindings && ts.isNamedImports(existing.importClause.namedBindings)) {
503
- const existingNames = new Set(existing.importClause.namedBindings.elements.map((element) => element.name.text));
504
+ const namedImports = existing.importClause.namedBindings;
505
+ const existingNames = new Set(namedImports.elements.map((element) => element.name.text));
504
506
  const missing = unresolved.filter((name) => !existingNames.has(name));
505
507
  if (missing.length === 0) continue;
506
- const namedImports = existing.importClause.namedBindings;
508
+ const hasElements = namedImports.elements.length > 0;
509
+ const insertionPoint2 = namedImports.getEnd() - 1;
510
+ const prefix = hasElements ? ", " : "";
507
511
  replacements.push({
508
- start: namedImports.getStart(sourceFile) + 1,
509
- end: namedImports.getEnd() - 1,
510
- text: `${[...existingNames, ...missing].sort().join(", ")}`
512
+ start: insertionPoint2,
513
+ end: insertionPoint2,
514
+ text: `${prefix}${missing.sort().join(", ")}`
511
515
  });
512
516
  continue;
513
517
  }
@@ -614,6 +618,51 @@ function resolveExtraScanDirs(root, extraScanDirs) {
614
618
  if (!extraScanDirs || extraScanDirs.length === 0) return [];
615
619
  return extraScanDirs.map((dir) => isAbsolute(dir) ? dir : resolvePath(root, dir));
616
620
  }
621
+ function collectComponentClassNames(dir) {
622
+ const names = /* @__PURE__ */ new Set();
623
+ const stage3 = /_([A-Za-z_$][\w$]*)_decorators\s*=\s*\[([^\]]*)\]/g;
624
+ const legacy = /__decorate\(\s*\[([^\]]*?)\]\s*,\s*([A-Za-z_$][\w$]*)/g;
625
+ function containsComponentDecorator(decoratorList) {
626
+ return /\b(Component|Service)\s*\(/.test(decoratorList);
627
+ }
628
+ function walk(current) {
629
+ let entries;
630
+ try {
631
+ entries = readdirSync(current);
632
+ } catch {
633
+ return;
634
+ }
635
+ for (const name of entries) {
636
+ const fullPath = joinPath(current, name);
637
+ let isDir = false;
638
+ try {
639
+ isDir = statSync(fullPath).isDirectory();
640
+ } catch {
641
+ continue;
642
+ }
643
+ if (isDir) {
644
+ if (name === "node_modules") continue;
645
+ walk(fullPath);
646
+ continue;
647
+ }
648
+ if (!/\.(js|cjs|mjs)$/.test(name)) continue;
649
+ let content;
650
+ try {
651
+ content = readFileSync(fullPath, "utf-8");
652
+ } catch {
653
+ continue;
654
+ }
655
+ for (const match of content.matchAll(stage3)) {
656
+ if (containsComponentDecorator(match[2])) names.add(match[1]);
657
+ }
658
+ for (const match of content.matchAll(legacy)) {
659
+ if (containsComponentDecorator(match[1])) names.add(match[2]);
660
+ }
661
+ }
662
+ }
663
+ walk(dir);
664
+ return names;
665
+ }
617
666
  function findPackageRoot(startDir) {
618
667
  let current = startDir;
619
668
  while (true) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "wireweaver",
3
3
  "description": "A lightweight TypeScript Dependency Injection Library",
4
4
  "productName": "WireWeaver",
5
- "version": "0.1.1",
5
+ "version": "0.1.2",
6
6
  "type": "module",
7
7
  "main": "./dist/index.cjs",
8
8
  "module": "./dist/index.js",