1+ /**
2+ * @license
3+ * Copyright Google LLC
4+ *
5+ * Use of this source code is governed by an MIT-style license that can be
6+ * found in the LICENSE file at https://angular.io/license
7+ */
8+
9+ import { isBuiltin } from 'node:module' ;
110import fs from 'node:fs/promises' ;
211import path from 'node:path' ;
312import ts from 'typescript' ;
413import { createDiagnostic } from './diagnostic.mjs' ;
514import { StrictDepsManifest } from './manifest.mjs' ;
615import { getImportsInSourceFile } from './visitor.mjs' ;
16+ import { readTsConfig } from './tsconfig.mjs' ;
717
818const [ manifestExecPath , expectedFailureRaw ] = process . argv . slice ( 2 ) ;
919const expectedFailure = expectedFailureRaw === 'true' ;
@@ -13,22 +23,45 @@ const manifest: StrictDepsManifest = JSON.parse(await fs.readFile(manifestExecPa
1323/**
1424 * Regex matcher to extract a npm package name, potentially with scope from a subpackage import path.
1525 */
16- const moduleSpeciferMatcher = / ^ ( @ [ \w \d - _ ] + \/ ) ? ( [ \w \d - _ ] + ) / ;
17- const extensionRemoveRegex = / \. [ m c ] ? ( j s | ( d \. ) ? [ m c ] ? t s ) $ / ;
18- const allowedModuleNames = new Set < string > ( manifest . allowedModuleNames ) ;
26+ const moduleSpeciferMatcher = / ^ ( @ [ \w \d - _ \. ] + \/ ) ? ( [ \w \d - _ \. ] + ) / ;
27+ const extensionRemoveRegex = / \. [ m c ] ? ( j s | ( d \. ) ? [ m c ] ? t s x ? ) $ / ;
28+ const allowedModuleNames = new Set < string > (
29+ manifest . allowedModuleNames . map ( ( m ) => {
30+ return (
31+ m
32+ // Scoped types from DefinitelyTyped are split using a __ delimiter, so we put it back together.
33+ . replace ( / (?: @ t y p e s \/ ) ( .* ) _ _ ( .* ) / , '@$1/$2' )
34+ // Replace any unscoped types package from DefinitelyTyped with just to package name.
35+ . replace ( / (?: @ t y p e s \/ ) ( .* ) / , '$1' )
36+ ) ;
37+ } ) ,
38+ ) ;
1939const allowedSources = new Set < string > (
2040 manifest . allowedSources . map ( ( s ) => s . replace ( extensionRemoveRegex , '' ) ) ,
2141) ;
22-
42+ const tsconfig = readTsConfig ( path . join ( process . cwd ( ) , manifest . tsconfigPath ) ) ;
2343const diagnostics : ts . Diagnostic [ ] = [ ] ;
2444
45+ /** Check if the moduleSpecifier matches any of the provided paths. */
46+ function checkPathsForMatch ( moduleSpecifier : string , paths ?: ts . MapLike < string [ ] > ) : boolean {
47+ for ( const matcher of Object . keys ( paths || { } ) ) {
48+ if ( new RegExp ( matcher ) . test ( moduleSpecifier ) ) {
49+ return true ;
50+ }
51+ }
52+ return false ;
53+ }
54+
2555for ( const fileExecPath of manifest . testFiles ) {
2656 const content = await fs . readFile ( fileExecPath , 'utf8' ) ;
2757 const sf = ts . createSourceFile ( fileExecPath , content , ts . ScriptTarget . ESNext , true ) ;
2858 const imports = getImportsInSourceFile ( sf ) ;
2959
3060 for ( const i of imports ) {
31- const moduleSpecifier = i . moduleSpecifier . replace ( extensionRemoveRegex , '' ) ;
61+ const moduleSpecifier =
62+ i . moduleSpecifier === 'zone.js'
63+ ? 'zone.js'
64+ : i . moduleSpecifier . replace ( extensionRemoveRegex , '' ) ;
3265 // When the module specified is the file itself this is always a valid dep.
3366 if ( i . moduleSpecifier === '' ) {
3467 continue ;
@@ -44,16 +77,24 @@ for (const fileExecPath of manifest.testFiles) {
4477 }
4578 }
4679
47- if ( moduleSpecifier . startsWith ( 'node:' ) && allowedModuleNames . has ( '@types/node' ) ) {
80+ if (
81+ isBuiltin ( moduleSpecifier ) &&
82+ ( allowedModuleNames . has ( 'node' ) || tsconfig . options . types ?. includes ( 'node' ) )
83+ ) {
4884 continue ;
4985 }
5086
5187 if (
52- allowedModuleNames . has ( moduleSpecifier . match ( moduleSpeciferMatcher ) ?. [ 0 ] || moduleSpecifier )
88+ allowedModuleNames . has ( moduleSpecifier . match ( moduleSpeciferMatcher ) ?. [ 0 ] || '' ) ||
89+ allowedModuleNames . has ( moduleSpecifier )
5390 ) {
5491 continue ;
5592 }
5693
94+ if ( checkPathsForMatch ( moduleSpecifier , tsconfig . options . paths ) ) {
95+ continue ;
96+ }
97+
5798 diagnostics . push (
5899 createDiagnostic ( `No explicit Bazel dependency for this module.` , i . diagnosticNode ) ,
59100 ) ;
0 commit comments