@cyclonedx/cdxgen 12.4.3 → 12.4.4

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 (38) hide show
  1. package/README.md +6 -0
  2. package/bin/audit.js +7 -0
  3. package/bin/cdxgen.js +48 -2
  4. package/bin/evinse.js +7 -0
  5. package/lib/audit/index.js +165 -2
  6. package/lib/audit/index.poku.js +462 -0
  7. package/lib/cli/index.js +317 -169
  8. package/lib/evinser/evinser.js +31 -9
  9. package/lib/helpers/analyzer.js +890 -0
  10. package/lib/helpers/analyzer.poku.js +341 -0
  11. package/lib/helpers/atomUtils.js +445 -0
  12. package/lib/helpers/atomUtils.poku.js +137 -0
  13. package/lib/helpers/bomUtils.js +71 -0
  14. package/lib/helpers/bomUtils.poku.js +45 -0
  15. package/lib/helpers/depsUtils.js +146 -0
  16. package/lib/helpers/depsUtils.poku.js +183 -0
  17. package/lib/helpers/utils.js +585 -191
  18. package/lib/helpers/utils.poku.js +357 -4
  19. package/lib/managers/binary.js +18 -9
  20. package/lib/stages/postgen/postgen.js +215 -0
  21. package/lib/stages/postgen/postgen.poku.js +218 -3
  22. package/lib/validator/bomValidator.js +11 -2
  23. package/package.json +8 -8
  24. package/types/lib/audit/index.d.ts.map +1 -1
  25. package/types/lib/cli/index.d.ts.map +1 -1
  26. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  27. package/types/lib/helpers/atomUtils.d.ts +18 -0
  28. package/types/lib/helpers/atomUtils.d.ts.map +1 -0
  29. package/types/lib/helpers/bomUtils.d.ts +10 -0
  30. package/types/lib/helpers/bomUtils.d.ts.map +1 -1
  31. package/types/lib/helpers/depsUtils.d.ts +9 -0
  32. package/types/lib/helpers/depsUtils.d.ts.map +1 -1
  33. package/types/lib/helpers/utils.d.ts +19 -0
  34. package/types/lib/helpers/utils.d.ts.map +1 -1
  35. package/types/lib/managers/binary.d.ts +2 -1
  36. package/types/lib/managers/binary.d.ts.map +1 -1
  37. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  38. package/types/lib/validator/bomValidator.d.ts.map +1 -1
@@ -332,6 +332,252 @@ const getStringValue = (astNode) => {
332
332
  return undefined;
333
333
  };
334
334
 
335
+ const ANGULAR_WORKSPACE_MANIFEST_NAMES = new Set([
336
+ "angular.json",
337
+ "workspace.json",
338
+ "project.json",
339
+ ]);
340
+
341
+ const ANGULAR_RESOURCE_METADATA_KEYS = new Set([
342
+ "styleUrl",
343
+ "styleUrls",
344
+ "templateUrl",
345
+ ]);
346
+
347
+ const ANGULAR_PACKAGE_STRING_KEYS = new Set([
348
+ "builder",
349
+ "executor",
350
+ "loadChildren",
351
+ ]);
352
+
353
+ const ANGULAR_WORKSPACE_PACKAGE_STRING_KEYS = new Set([
354
+ "builder",
355
+ "executor",
356
+ "polyfills",
357
+ "plugins",
358
+ "scripts",
359
+ "styles",
360
+ ]);
361
+
362
+ const ANGULAR_CONFIG_EVIDENCE_FILE_NAMES = new Set([
363
+ "karma.conf.js",
364
+ "karma.conf.cjs",
365
+ "karma.conf.mjs",
366
+ "protractor.conf.js",
367
+ "protractor.conf.cjs",
368
+ "protractor.conf.mjs",
369
+ ]);
370
+
371
+ const ANGULAR_SCRIPT_COMMAND_PACKAGES = [
372
+ { pattern: /(?:^|[\s;&|()])ng\s+test(?:\s|$)/, packages: ["karma"] },
373
+ { pattern: /(?:^|[\s;&|()])ng\s+e2e(?:\s|$)/, packages: ["protractor"] },
374
+ {
375
+ pattern: /(?:^|[\s;&|()])ng\s+lint(?:\s|$)/,
376
+ packages: ["codelyzer", "tslint"],
377
+ },
378
+ {
379
+ pattern: /(?:^|[\s;&|()])ng\s+(?:build|serve|run|xi18n)(?:\s|$)/,
380
+ packages: ["@angular/compiler-cli", "typescript"],
381
+ },
382
+ { pattern: /(?:^|[\s;&|()])ng(?:\s|$)/, packages: ["@angular/cli"] },
383
+ {
384
+ pattern: /(?:^|[\s;&|()])ngc(?:\s|$)/,
385
+ packages: ["@angular/compiler-cli"],
386
+ },
387
+ { pattern: /(?:^|[\s;&|()])tsc(?:\s|$)/, packages: ["typescript"] },
388
+ { pattern: /(?:^|[\s;&|()])ttsc(?:\s|$)/, packages: ["ttypescript"] },
389
+ { pattern: /(?:^|[\s;&|()])ts-node(?:\s|\/|$)/, packages: ["ts-node"] },
390
+ { pattern: /(?:^|[\s;&|()])tslint(?:\s|$)/, packages: ["tslint"] },
391
+ { pattern: /(?:^|[\s;&|()])karma(?:\s|$)/, packages: ["karma"] },
392
+ { pattern: /(?:^|[\s;&|()])protractor(?:\s|$)/, packages: ["protractor"] },
393
+ { pattern: /(?:^|[\s;&|()])jasmine(?:\s|$)/, packages: ["jasmine"] },
394
+ {
395
+ pattern: /(?:^|[\s;&|()])webpack-bundle-analyzer(?:\s|$)/,
396
+ packages: ["webpack-bundle-analyzer"],
397
+ },
398
+ { pattern: /(?:^|[\s;&|()])ng-packagr(?:\s|$)/, packages: ["ng-packagr"] },
399
+ {
400
+ pattern: /(?:^|[\s;&|()])firebase-tools(?:\s|$)/,
401
+ packages: ["firebase-tools"],
402
+ },
403
+ { pattern: /(?:^|[\s;&|()])typedoc(?:\s|$)/, packages: ["typedoc"] },
404
+ { pattern: /(?:^|[\s;&|()])rimraf(?:\s|$)/, packages: ["rimraf"] },
405
+ { pattern: /(?:^|[\s;&|()])rollup(?:\s|$)/, packages: ["rollup"] },
406
+ ];
407
+
408
+ const ANGULAR_KARMA_CONFIG_PATTERNS = [
409
+ {
410
+ pattern: /\bframeworks\s*:\s*\[[^\]]*['"]jasmine['"]/s,
411
+ packages: ["karma-jasmine", "jasmine-core"],
412
+ },
413
+ {
414
+ pattern: /\bbrowsers\s*:\s*\[[^\]]*['"]Chrome/s,
415
+ packages: ["karma-chrome-launcher"],
416
+ },
417
+ {
418
+ pattern: /\bbrowsers\s*:\s*\[[^\]]*['"]Firefox/s,
419
+ packages: ["karma-firefox-launcher"],
420
+ },
421
+ {
422
+ pattern: /\bbrowsers\s*:\s*\[[^\]]*['"]Safari/s,
423
+ packages: ["karma-safarinative-launcher"],
424
+ },
425
+ {
426
+ pattern: /\breporters\s*:\s*\[[^\]]*['"]coverage-istanbul['"]/s,
427
+ packages: ["karma-coverage-istanbul-reporter"],
428
+ },
429
+ {
430
+ pattern: /\breporters\s*:\s*\[[^\]]*['"]html['"]/s,
431
+ packages: ["karma-jasmine-html-reporter"],
432
+ },
433
+ ];
434
+
435
+ const ANGULAR_PROTRACTOR_CONFIG_PATTERNS = [
436
+ {
437
+ pattern: /\bframework\s*:\s*['"]jasmine['"]/,
438
+ packages: ["jasmine-core", "jasminewd2"],
439
+ },
440
+ { pattern: /\bts-node\/register\b/, packages: ["ts-node"] },
441
+ ];
442
+
443
+ const isAngularTsconfigFileName = (fileName) =>
444
+ /^tsconfig(?:[.-].*)?\.json$/i.test(fileName);
445
+
446
+ const ANGULAR_TEMPLATE_PACKAGE_PATTERNS = [
447
+ {
448
+ packageName: "@angular/router",
449
+ pattern: /<\s*router-outlet\b|\brouterLink(?:Active)?\b/,
450
+ },
451
+ {
452
+ packageName: "@angular/common",
453
+ pattern:
454
+ /\*(ngIf|ngFor|ngSwitch)|\b(ngClass|ngStyle|ngTemplateOutlet)\b|\|\s*(async|date|currency|decimal|json|keyvalue|lowercase|number|percent|slice|titlecase|uppercase)\b/,
455
+ },
456
+ {
457
+ packageName: "@angular/forms",
458
+ pattern:
459
+ /\b(formControl|formControlName|formGroup|formArrayName|ngModel)\b/,
460
+ },
461
+ {
462
+ packageName: "@angular/material",
463
+ pattern: /<\s*mat-[\w-]+\b|\bmat[A-Z][\w-]*\b|\bmat-[\w-]+\b/,
464
+ },
465
+ {
466
+ packageName: "@angular/cdk",
467
+ pattern: /<\s*cdk-[\w-]+\b|\bcdk[A-Z][\w-]*\b|\bcdk-[\w-]+\b/,
468
+ },
469
+ {
470
+ packageName: "@ionic/angular",
471
+ pattern: /<\s*ion-[\w-]+\b/,
472
+ },
473
+ {
474
+ packageName: "@fortawesome/angular-fontawesome",
475
+ pattern: /<\s*fa-icon\b/,
476
+ },
477
+ {
478
+ packageName: "ag-grid-angular",
479
+ pattern: /<\s*ag-grid-angular\b/,
480
+ },
481
+ {
482
+ packageName: "@ng-select/ng-select",
483
+ pattern: /<\s*ng-select\b/,
484
+ },
485
+ {
486
+ packageName: "@swimlane/ngx-charts",
487
+ pattern: /<\s*ngx-charts-[\w-]+\b/,
488
+ },
489
+ ];
490
+
491
+ const angularLocalPathPrefixRegex =
492
+ /^(\.|\/|\\|src\/|app\/|assets\/|styles\/|environments\/)/i;
493
+ const angularUrlRegex = /^[a-z][a-z0-9+.-]*:/i;
494
+ const angularBareFileRegex =
495
+ /^[\w.-]+\.(css|html|js|json|less|mjs|scss|sass|ts)$/i;
496
+
497
+ const stripAngularRouteFragment = (reference) =>
498
+ String(reference || "")
499
+ .split("#")[0]
500
+ .split("?")[0]
501
+ .trim();
502
+
503
+ const extractAngularPackageName = (reference, allowBare = true) => {
504
+ let candidate = stripAngularRouteFragment(reference)
505
+ .replace(/^~+/, "")
506
+ .replace(/^\.\//, "");
507
+ if (!candidate || angularUrlRegex.test(candidate)) {
508
+ return undefined;
509
+ }
510
+ candidate = candidate.replaceAll("\\", "/");
511
+ if (candidate.startsWith("node_modules/")) {
512
+ candidate = candidate.slice("node_modules/".length);
513
+ } else if (angularLocalPathPrefixRegex.test(candidate)) {
514
+ return undefined;
515
+ }
516
+ if (!allowBare && !candidate.includes("/")) {
517
+ return undefined;
518
+ }
519
+ if (candidate === "zone.js") {
520
+ return candidate;
521
+ }
522
+ if (angularBareFileRegex.test(candidate)) {
523
+ return undefined;
524
+ }
525
+ if (candidate.startsWith("@")) {
526
+ const scopedMatch = candidate.match(/^(@[^/\s:]+\/[^/\s:]+)/);
527
+ return scopedMatch?.[1];
528
+ }
529
+ const unscopedMatch = candidate.match(
530
+ /^([a-zA-Z0-9][a-zA-Z0-9._-]*)(?=\/|:|$)/,
531
+ );
532
+ return unscopedMatch?.[1];
533
+ };
534
+
535
+ const setAngularPackageRef = (
536
+ allImports,
537
+ allExports,
538
+ src,
539
+ file,
540
+ reference,
541
+ sourceLoc,
542
+ allowBare = true,
543
+ ) => {
544
+ const packageName = extractAngularPackageName(reference, allowBare);
545
+ if (!packageName) {
546
+ return;
547
+ }
548
+ setSyntheticImportRef(
549
+ allImports,
550
+ allExports,
551
+ src,
552
+ file,
553
+ packageName,
554
+ [],
555
+ sourceLoc,
556
+ );
557
+ };
558
+
559
+ const getObjectPropertyKeyName = (propertyNode) => {
560
+ if (!propertyNode) {
561
+ return undefined;
562
+ }
563
+ return getMemberExpressionPropertyName(propertyNode);
564
+ };
565
+
566
+ const getAngularStringNodes = (astNode) => {
567
+ if (!astNode) {
568
+ return [];
569
+ }
570
+ if (getStringValue(astNode)) {
571
+ return [astNode];
572
+ }
573
+ if (astNode.type === "ArrayExpression") {
574
+ return (astNode.elements || []).filter((element) =>
575
+ getStringValue(element),
576
+ );
577
+ }
578
+ return [];
579
+ };
580
+
335
581
  const unwrapAwait = (astNode) =>
336
582
  astNode?.type === "AwaitExpression" ? astNode.argument : astNode;
337
583
 
@@ -570,6 +816,42 @@ const parseFileASTTree = (src, file, allImports, allExports) => {
570
816
  path.node.specifiers,
571
817
  );
572
818
  const sourceValue = path.node.source?.value;
819
+ if (sourceValue === "@angular/platform-browser/animations") {
820
+ setSyntheticImportRef(
821
+ allImports,
822
+ allExports,
823
+ src,
824
+ file,
825
+ "@angular/animations",
826
+ [],
827
+ path.node.loc?.start,
828
+ );
829
+ }
830
+ if (sourceValue === "@angular/platform-browser-dynamic") {
831
+ setSyntheticImportRef(
832
+ allImports,
833
+ allExports,
834
+ src,
835
+ file,
836
+ "@angular/compiler",
837
+ [],
838
+ path.node.loc?.start,
839
+ );
840
+ }
841
+ if (
842
+ sourceValue === "@angular/fire" ||
843
+ sourceValue?.startsWith("@angular/fire/")
844
+ ) {
845
+ setSyntheticImportRef(
846
+ allImports,
847
+ allExports,
848
+ src,
849
+ file,
850
+ "firebase",
851
+ [],
852
+ path.node.loc?.start,
853
+ );
854
+ }
573
855
  if (sourceValue === "node:wasi" || sourceValue === "wasi") {
574
856
  for (const specifier of path.node.specifiers || []) {
575
857
  if (
@@ -950,6 +1232,37 @@ const parseFileASTTree = (src, file, allImports, allExports) => {
950
1232
  );
951
1233
  }
952
1234
  },
1235
+ ObjectProperty: (path) => {
1236
+ const keyName = getObjectPropertyKeyName(path?.node?.key);
1237
+ if (!keyName) {
1238
+ return;
1239
+ }
1240
+ if (ANGULAR_RESOURCE_METADATA_KEYS.has(keyName)) {
1241
+ for (const stringNode of getAngularStringNodes(path.node.value)) {
1242
+ setFileRef(allImports, allExports, src, file, stringNode);
1243
+ }
1244
+ }
1245
+ if (ANGULAR_PACKAGE_STRING_KEYS.has(keyName)) {
1246
+ for (const stringNode of getAngularStringNodes(path.node.value)) {
1247
+ const reference = getStringValue(stringNode);
1248
+ if (keyName === "loadChildren" && /^[./]/.test(reference || "")) {
1249
+ setFileRef(allImports, allExports, src, file, {
1250
+ value: stripAngularRouteFragment(reference),
1251
+ loc: stringNode.loc,
1252
+ });
1253
+ continue;
1254
+ }
1255
+ setAngularPackageRef(
1256
+ allImports,
1257
+ allExports,
1258
+ src,
1259
+ file,
1260
+ reference,
1261
+ stringNode.loc?.start,
1262
+ );
1263
+ }
1264
+ }
1265
+ },
953
1266
  // Use for export barrells
954
1267
  ExportAllDeclaration: (path) => {
955
1268
  setFileRef(allImports, allExports, src, file, path.node.source);
@@ -992,6 +1305,541 @@ const getAllSrcJSAndTSFiles = (src, deep) =>
992
1305
  ),
993
1306
  );
994
1307
 
1308
+ const getAngularWorkspaceManifestFiles = (
1309
+ dir,
1310
+ deep,
1311
+ result = [],
1312
+ rootDir = dir,
1313
+ excludePatterns = [],
1314
+ ) => {
1315
+ let files;
1316
+ try {
1317
+ files = readdirSync(dir);
1318
+ } catch {
1319
+ return result;
1320
+ }
1321
+ for (const entry of files) {
1322
+ if (entry.startsWith(".")) {
1323
+ continue;
1324
+ }
1325
+ const file = join(dir, entry);
1326
+ let fileStat;
1327
+ try {
1328
+ fileStat = lstatSync(file);
1329
+ } catch {
1330
+ continue;
1331
+ }
1332
+ if (fileStat.isSymbolicLink()) {
1333
+ continue;
1334
+ }
1335
+ if (
1336
+ shouldExcludeAnalyzerPath(
1337
+ rootDir,
1338
+ file,
1339
+ excludePatterns,
1340
+ fileStat.isDirectory(),
1341
+ )
1342
+ ) {
1343
+ continue;
1344
+ }
1345
+ if (fileStat.isDirectory()) {
1346
+ const dirName = basename(file);
1347
+ if (
1348
+ dirName.startsWith("__") ||
1349
+ IGNORE_DIRS.includes(dirName.toLowerCase()) ||
1350
+ dirName === "node_modules"
1351
+ ) {
1352
+ continue;
1353
+ }
1354
+ getAngularWorkspaceManifestFiles(
1355
+ file,
1356
+ deep,
1357
+ result,
1358
+ rootDir,
1359
+ excludePatterns,
1360
+ );
1361
+ continue;
1362
+ }
1363
+ if (ANGULAR_WORKSPACE_MANIFEST_NAMES.has(entry)) {
1364
+ result.push(file);
1365
+ }
1366
+ }
1367
+ return result;
1368
+ };
1369
+
1370
+ const getAngularEvidenceFiles = (
1371
+ dir,
1372
+ deep,
1373
+ result = [],
1374
+ rootDir = dir,
1375
+ excludePatterns = [],
1376
+ ) => {
1377
+ let files;
1378
+ try {
1379
+ files = readdirSync(dir);
1380
+ } catch {
1381
+ return result;
1382
+ }
1383
+ for (const entry of files) {
1384
+ if (entry.startsWith(".")) {
1385
+ continue;
1386
+ }
1387
+ const file = join(dir, entry);
1388
+ let fileStat;
1389
+ try {
1390
+ fileStat = lstatSync(file);
1391
+ } catch {
1392
+ continue;
1393
+ }
1394
+ if (fileStat.isSymbolicLink()) {
1395
+ continue;
1396
+ }
1397
+ if (
1398
+ shouldExcludeAnalyzerPath(
1399
+ rootDir,
1400
+ file,
1401
+ excludePatterns,
1402
+ fileStat.isDirectory(),
1403
+ )
1404
+ ) {
1405
+ continue;
1406
+ }
1407
+ if (fileStat.isDirectory()) {
1408
+ const dirName = basename(file);
1409
+ if (
1410
+ dirName.startsWith("__") ||
1411
+ IGNORE_DIRS.includes(dirName.toLowerCase()) ||
1412
+ dirName === "node_modules" ||
1413
+ (!deep && dirName === "dist")
1414
+ ) {
1415
+ continue;
1416
+ }
1417
+ getAngularEvidenceFiles(file, deep, result, rootDir, excludePatterns);
1418
+ continue;
1419
+ }
1420
+ if (
1421
+ entry === "package.json" ||
1422
+ ANGULAR_CONFIG_EVIDENCE_FILE_NAMES.has(entry) ||
1423
+ isAngularTsconfigFileName(entry)
1424
+ ) {
1425
+ result.push(file);
1426
+ }
1427
+ }
1428
+ return result;
1429
+ };
1430
+
1431
+ const parseAngularJsonFile = (file) => {
1432
+ try {
1433
+ return JSON.parse(readFileSync(file, "utf-8"));
1434
+ } catch {
1435
+ return undefined;
1436
+ }
1437
+ };
1438
+
1439
+ const parseAngularJsonLikeFile = (file) => {
1440
+ try {
1441
+ const content = readFileSync(file, "utf-8")
1442
+ .replace(/\/\*[\s\S]*?\*\//g, "")
1443
+ .replace(/^\s*\/\/.*$/gm, "");
1444
+ return JSON.parse(content);
1445
+ } catch {
1446
+ return undefined;
1447
+ }
1448
+ };
1449
+
1450
+ const addAngularBuilderImpliedPackageRefs = (reference, refs) => {
1451
+ const normalizedReference = String(reference || "").trim();
1452
+ if (!normalizedReference.includes(":")) {
1453
+ return;
1454
+ }
1455
+ const [builderPackage, builderTarget] = normalizedReference.split(":");
1456
+ if (
1457
+ builderPackage === "@angular-devkit/build-angular" ||
1458
+ builderPackage === "@angular/build"
1459
+ ) {
1460
+ if (
1461
+ [
1462
+ "application",
1463
+ "browser",
1464
+ "browser-esbuild",
1465
+ "dev-server",
1466
+ "extract-i18n",
1467
+ "server",
1468
+ ].includes(builderTarget)
1469
+ ) {
1470
+ refs.add("@angular/compiler-cli");
1471
+ refs.add("typescript");
1472
+ }
1473
+ if (builderTarget === "karma") {
1474
+ refs.add("karma");
1475
+ }
1476
+ if (builderTarget === "protractor") {
1477
+ refs.add("protractor");
1478
+ }
1479
+ }
1480
+ if (builderPackage === "@angular-devkit/build-ng-packagr") {
1481
+ refs.add("ng-packagr");
1482
+ }
1483
+ if (builderPackage === "@nguniversal/builders") {
1484
+ refs.add("@angular/compiler-cli");
1485
+ refs.add("typescript");
1486
+ }
1487
+ };
1488
+
1489
+ const isAngularPackageSignal = (packageName) =>
1490
+ packageName === "@angular" ||
1491
+ packageName?.startsWith("@angular/") ||
1492
+ packageName?.startsWith("@angular-") ||
1493
+ packageName === "@nx/angular" ||
1494
+ packageName === "@schematics/angular";
1495
+
1496
+ const collectAngularWorkspacePackageRefs = (node, keyName, refs) => {
1497
+ if (typeof node === "string") {
1498
+ if (ANGULAR_WORKSPACE_PACKAGE_STRING_KEYS.has(keyName)) {
1499
+ const packageName = extractAngularPackageName(node, true);
1500
+ if (packageName) {
1501
+ refs.add(packageName);
1502
+ }
1503
+ if (keyName === "builder" || keyName === "executor") {
1504
+ addAngularBuilderImpliedPackageRefs(node, refs);
1505
+ }
1506
+ }
1507
+ return;
1508
+ }
1509
+ if (Array.isArray(node)) {
1510
+ for (const item of node) {
1511
+ collectAngularWorkspacePackageRefs(item, keyName, refs);
1512
+ }
1513
+ return;
1514
+ }
1515
+ if (!node || typeof node !== "object") {
1516
+ return;
1517
+ }
1518
+ if (
1519
+ ANGULAR_WORKSPACE_PACKAGE_STRING_KEYS.has(keyName) &&
1520
+ typeof node.input === "string"
1521
+ ) {
1522
+ const packageName = extractAngularPackageName(node.input, true);
1523
+ if (packageName) {
1524
+ refs.add(packageName);
1525
+ }
1526
+ }
1527
+ for (const [childKey, childValue] of Object.entries(node)) {
1528
+ if (typeof childKey === "string" && childKey.includes(":")) {
1529
+ const packageName = extractAngularPackageName(childKey, true);
1530
+ if (packageName) {
1531
+ refs.add(packageName);
1532
+ }
1533
+ }
1534
+ if (
1535
+ ANGULAR_WORKSPACE_PACKAGE_STRING_KEYS.has(childKey) &&
1536
+ typeof childValue === "object" &&
1537
+ !Array.isArray(childValue) &&
1538
+ typeof childValue?.input === "string"
1539
+ ) {
1540
+ const packageName = extractAngularPackageName(childValue.input, true);
1541
+ if (packageName) {
1542
+ refs.add(packageName);
1543
+ }
1544
+ }
1545
+ collectAngularWorkspacePackageRefs(childValue, childKey, refs);
1546
+ }
1547
+ };
1548
+
1549
+ const parseAngularWorkspaceManifests = (
1550
+ src,
1551
+ allImports,
1552
+ allExports,
1553
+ deep,
1554
+ excludePatterns,
1555
+ ) => {
1556
+ const manifestFiles = getAngularWorkspaceManifestFiles(
1557
+ src,
1558
+ deep,
1559
+ [],
1560
+ src,
1561
+ excludePatterns,
1562
+ );
1563
+ const parsedAngularManifestFiles = [];
1564
+ const hasAngularSourceEvidence = Object.keys(allImports).some((importName) =>
1565
+ isAngularPackageSignal(importName),
1566
+ );
1567
+ for (const manifestFile of manifestFiles) {
1568
+ const manifest = parseAngularJsonFile(manifestFile);
1569
+ if (!manifest) {
1570
+ continue;
1571
+ }
1572
+ const packageRefs = new Set();
1573
+ collectAngularWorkspacePackageRefs(manifest, undefined, packageRefs);
1574
+ if (
1575
+ basename(manifestFile) === "project.json" &&
1576
+ !hasAngularSourceEvidence &&
1577
+ !Array.from(packageRefs).some((packageRef) =>
1578
+ isAngularPackageSignal(packageRef),
1579
+ )
1580
+ ) {
1581
+ continue;
1582
+ }
1583
+ parsedAngularManifestFiles.push(manifestFile);
1584
+ for (const packageRef of packageRefs) {
1585
+ setAngularPackageRef(
1586
+ allImports,
1587
+ allExports,
1588
+ src,
1589
+ manifestFile,
1590
+ packageRef,
1591
+ undefined,
1592
+ );
1593
+ }
1594
+ }
1595
+ return parsedAngularManifestFiles;
1596
+ };
1597
+
1598
+ const hasAngularImportEvidence = (allImports, manifestFiles) =>
1599
+ manifestFiles.length > 0 ||
1600
+ Object.keys(allImports).some((importName) =>
1601
+ isAngularPackageSignal(importName),
1602
+ );
1603
+
1604
+ const parseAngularTemplateFiles = (
1605
+ src,
1606
+ allImports,
1607
+ allExports,
1608
+ deep,
1609
+ excludePatterns,
1610
+ ) => {
1611
+ const templateFiles = getAllFiles(
1612
+ deep,
1613
+ src,
1614
+ ".html",
1615
+ undefined,
1616
+ undefined,
1617
+ undefined,
1618
+ src,
1619
+ excludePatterns,
1620
+ );
1621
+ for (const templateFile of templateFiles) {
1622
+ let content;
1623
+ try {
1624
+ content = readFileSync(templateFile, "utf-8");
1625
+ } catch {
1626
+ continue;
1627
+ }
1628
+ const lines = content.split(/\r?\n/);
1629
+ for (const [index, line] of lines.entries()) {
1630
+ for (const templatePattern of ANGULAR_TEMPLATE_PACKAGE_PATTERNS) {
1631
+ if (templatePattern.pattern.test(line)) {
1632
+ setAngularPackageRef(
1633
+ allImports,
1634
+ allExports,
1635
+ src,
1636
+ templateFile,
1637
+ templatePattern.packageName,
1638
+ { line: index + 1, column: 0 },
1639
+ );
1640
+ }
1641
+ }
1642
+ }
1643
+ }
1644
+ };
1645
+
1646
+ const addAngularScriptPackageRefs = (
1647
+ allImports,
1648
+ allExports,
1649
+ src,
1650
+ file,
1651
+ scriptValue,
1652
+ ) => {
1653
+ const script = String(scriptValue || "");
1654
+ if (!script) {
1655
+ return;
1656
+ }
1657
+ for (const commandPackage of ANGULAR_SCRIPT_COMMAND_PACKAGES) {
1658
+ if (commandPackage.pattern.test(script)) {
1659
+ for (const packageName of commandPackage.packages) {
1660
+ setAngularPackageRef(
1661
+ allImports,
1662
+ allExports,
1663
+ src,
1664
+ file,
1665
+ packageName,
1666
+ undefined,
1667
+ );
1668
+ }
1669
+ }
1670
+ }
1671
+ };
1672
+
1673
+ const parseAngularPackageJsonScripts = (
1674
+ src,
1675
+ allImports,
1676
+ allExports,
1677
+ evidenceFiles,
1678
+ ) => {
1679
+ for (const evidenceFile of evidenceFiles) {
1680
+ if (basename(evidenceFile) !== "package.json") {
1681
+ continue;
1682
+ }
1683
+ const packageJson = parseAngularJsonFile(evidenceFile);
1684
+ if (!packageJson?.scripts || typeof packageJson.scripts !== "object") {
1685
+ continue;
1686
+ }
1687
+ for (const scriptValue of Object.values(packageJson.scripts)) {
1688
+ addAngularScriptPackageRefs(
1689
+ allImports,
1690
+ allExports,
1691
+ src,
1692
+ evidenceFile,
1693
+ scriptValue,
1694
+ );
1695
+ }
1696
+ }
1697
+ };
1698
+
1699
+ const addAngularConfigPatternPackageRefs = (
1700
+ allImports,
1701
+ allExports,
1702
+ src,
1703
+ file,
1704
+ content,
1705
+ configPatterns,
1706
+ ) => {
1707
+ for (const configPattern of configPatterns) {
1708
+ if (configPattern.pattern.test(content)) {
1709
+ for (const packageName of configPattern.packages) {
1710
+ setAngularPackageRef(
1711
+ allImports,
1712
+ allExports,
1713
+ src,
1714
+ file,
1715
+ packageName,
1716
+ undefined,
1717
+ );
1718
+ }
1719
+ }
1720
+ }
1721
+ };
1722
+
1723
+ const parseAngularToolConfigFiles = (
1724
+ src,
1725
+ allImports,
1726
+ allExports,
1727
+ evidenceFiles,
1728
+ ) => {
1729
+ for (const evidenceFile of evidenceFiles) {
1730
+ const evidenceFileName = basename(evidenceFile);
1731
+ if (!ANGULAR_CONFIG_EVIDENCE_FILE_NAMES.has(evidenceFileName)) {
1732
+ continue;
1733
+ }
1734
+ let content;
1735
+ try {
1736
+ content = readFileSync(evidenceFile, "utf-8");
1737
+ } catch {
1738
+ continue;
1739
+ }
1740
+ setAngularPackageRef(
1741
+ allImports,
1742
+ allExports,
1743
+ src,
1744
+ evidenceFile,
1745
+ evidenceFileName.startsWith("karma.") ? "karma" : "protractor",
1746
+ undefined,
1747
+ );
1748
+ for (const requiredPackage of content.matchAll(
1749
+ /require\(["']([^"']+)["']\)/g,
1750
+ )) {
1751
+ setAngularPackageRef(
1752
+ allImports,
1753
+ allExports,
1754
+ src,
1755
+ evidenceFile,
1756
+ requiredPackage[1],
1757
+ undefined,
1758
+ );
1759
+ }
1760
+ if (evidenceFileName.startsWith("karma.")) {
1761
+ addAngularConfigPatternPackageRefs(
1762
+ allImports,
1763
+ allExports,
1764
+ src,
1765
+ evidenceFile,
1766
+ content,
1767
+ ANGULAR_KARMA_CONFIG_PATTERNS,
1768
+ );
1769
+ continue;
1770
+ }
1771
+ addAngularConfigPatternPackageRefs(
1772
+ allImports,
1773
+ allExports,
1774
+ src,
1775
+ evidenceFile,
1776
+ content,
1777
+ ANGULAR_PROTRACTOR_CONFIG_PATTERNS,
1778
+ );
1779
+ }
1780
+ };
1781
+
1782
+ const parseAngularTsconfigFiles = (
1783
+ src,
1784
+ allImports,
1785
+ allExports,
1786
+ evidenceFiles,
1787
+ ) => {
1788
+ for (const evidenceFile of evidenceFiles) {
1789
+ if (!isAngularTsconfigFileName(basename(evidenceFile))) {
1790
+ continue;
1791
+ }
1792
+ const tsconfig = parseAngularJsonLikeFile(evidenceFile);
1793
+ const compilerOptions = tsconfig?.compilerOptions;
1794
+ if (!compilerOptions || typeof compilerOptions !== "object") {
1795
+ continue;
1796
+ }
1797
+ if (compilerOptions.importHelpers === true) {
1798
+ setAngularPackageRef(
1799
+ allImports,
1800
+ allExports,
1801
+ src,
1802
+ evidenceFile,
1803
+ "tslib",
1804
+ undefined,
1805
+ );
1806
+ }
1807
+ if (Array.isArray(compilerOptions.types)) {
1808
+ for (const typeName of compilerOptions.types) {
1809
+ if (typeof typeName !== "string" || !typeName) {
1810
+ continue;
1811
+ }
1812
+ const typePackageName = typeName.startsWith("@")
1813
+ ? typeName
1814
+ : `@types/${typeName}`;
1815
+ setAngularPackageRef(
1816
+ allImports,
1817
+ allExports,
1818
+ src,
1819
+ evidenceFile,
1820
+ typePackageName,
1821
+ undefined,
1822
+ );
1823
+ }
1824
+ }
1825
+ if (Array.isArray(compilerOptions.plugins)) {
1826
+ for (const plugin of compilerOptions.plugins) {
1827
+ const pluginReference =
1828
+ plugin?.transform || plugin?.name || plugin?.import;
1829
+ if (typeof pluginReference === "string") {
1830
+ setAngularPackageRef(
1831
+ allImports,
1832
+ allExports,
1833
+ src,
1834
+ evidenceFile,
1835
+ pluginReference,
1836
+ undefined,
1837
+ );
1838
+ }
1839
+ }
1840
+ }
1841
+ }
1842
+ };
995
1843
  export const CHROMIUM_EXTENSION_CAPABILITY_CATEGORIES = [
996
1844
  "fileAccess",
997
1845
  "deviceAccess",
@@ -1485,6 +2333,7 @@ export const findJSImportsExports = async (src, deep) => {
1485
2333
  const allImports = {};
1486
2334
  const allExports = {};
1487
2335
  try {
2336
+ const searchOptions = normalizeAnalyzerSearchOptions(deep);
1488
2337
  const promiseMap = await getAllSrcJSAndTSFiles(src, deep);
1489
2338
  const srcFiles = promiseMap.flat();
1490
2339
  for (const file of srcFiles) {
@@ -1494,6 +2343,47 @@ export const findJSImportsExports = async (src, deep) => {
1494
2343
  // ignore parse failures
1495
2344
  }
1496
2345
  }
2346
+ const angularManifestFiles = parseAngularWorkspaceManifests(
2347
+ src,
2348
+ allImports,
2349
+ allExports,
2350
+ searchOptions.deep,
2351
+ searchOptions.exclude,
2352
+ );
2353
+ if (hasAngularImportEvidence(allImports, angularManifestFiles)) {
2354
+ const angularEvidenceFiles = getAngularEvidenceFiles(
2355
+ src,
2356
+ searchOptions.deep,
2357
+ [],
2358
+ src,
2359
+ searchOptions.exclude,
2360
+ );
2361
+ parseAngularPackageJsonScripts(
2362
+ src,
2363
+ allImports,
2364
+ allExports,
2365
+ angularEvidenceFiles,
2366
+ );
2367
+ parseAngularToolConfigFiles(
2368
+ src,
2369
+ allImports,
2370
+ allExports,
2371
+ angularEvidenceFiles,
2372
+ );
2373
+ parseAngularTsconfigFiles(
2374
+ src,
2375
+ allImports,
2376
+ allExports,
2377
+ angularEvidenceFiles,
2378
+ );
2379
+ parseAngularTemplateFiles(
2380
+ src,
2381
+ allImports,
2382
+ allExports,
2383
+ searchOptions.deep,
2384
+ searchOptions.exclude,
2385
+ );
2386
+ }
1497
2387
  return { allImports, allExports };
1498
2388
  } catch (_err) {
1499
2389
  return { allImports, allExports };