@@ -7,6 +7,9 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
77import { init , parse } from 'es-module-lexer'
88import { moduleType } from 'node-module-type'
99
10+ import { getTsconfig , type TsConfigResult } from 'get-tsconfig'
11+ import { createMatchPath , type MatchPath } from 'tsconfig-paths'
12+
1013import { cssWithMeta } from './css.js'
1114import {
1215 determineSelectorVariant ,
@@ -34,12 +37,22 @@ interface DeclarationRecord {
3437 filePath : string
3538}
3639
40+ interface TsconfigResolutionContext {
41+ absoluteBaseUrl ?: string
42+ matchPath ?: MatchPath
43+ }
44+
45+ type CssWithMetaFn = typeof cssWithMeta
46+
47+ let activeCssWithMeta : CssWithMetaFn = cssWithMeta
48+
3749interface GenerateTypesInternalOptions {
3850 rootDir : string
3951 include : string [ ]
4052 outDir : string
4153 typesRoot : string
4254 stableNamespace ?: string
55+ tsconfig ?: TsconfigResolutionContext
4356}
4457
4558export interface GenerateTypesResult {
@@ -84,12 +97,17 @@ const SUPPORTED_EXTENSIONS = new Set([
8497 '.cjs' ,
8598] )
8699
100+ type ModuleTypeDetector = ( ) => ReturnType < typeof moduleType >
101+
102+ let moduleTypeDetector : ModuleTypeDetector = moduleType
103+ let importMetaUrlProvider : ( ) => string | undefined = getImportMetaUrl
104+
87105function resolvePackageRoot ( ) : string {
88- const detectedType = moduleType ( )
106+ const detectedType = moduleTypeDetector ( )
89107 if ( detectedType === 'commonjs' && typeof __dirname === 'string' ) {
90108 return path . resolve ( __dirname , '..' )
91109 }
92- const moduleUrl = getImportMetaUrl ( )
110+ const moduleUrl = importMetaUrlProvider ( )
93111 if ( moduleUrl ) {
94112 return path . resolve ( path . dirname ( fileURLToPath ( moduleUrl ) ) , '..' )
95113 }
@@ -115,6 +133,7 @@ export async function generateTypes(
115133 const include = normalizeIncludeOptions ( options . include , rootDir )
116134 const outDir = path . resolve ( options . outDir ?? DEFAULT_OUT_DIR )
117135 const typesRoot = path . resolve ( options . typesRoot ?? DEFAULT_TYPES_ROOT )
136+ const tsconfig = loadTsconfigResolutionContext ( rootDir )
118137 await init
119138 await fs . mkdir ( outDir , { recursive : true } )
120139 await fs . mkdir ( typesRoot , { recursive : true } )
@@ -125,6 +144,7 @@ export async function generateTypes(
125144 outDir,
126145 typesRoot,
127146 stableNamespace : options . stableNamespace ,
147+ tsconfig,
128148 }
129149
130150 return generateDeclarations ( internalOptions )
@@ -162,6 +182,7 @@ async function generateDeclarations(
162182 resource ,
163183 match . importer ,
164184 options . rootDir ,
185+ options . tsconfig ,
165186 )
166187 if ( ! resolvedPath ) {
167188 warnings . push (
@@ -174,7 +195,7 @@ async function generateDeclarations(
174195 let selectorMap = selectorCache . get ( cacheKey )
175196 if ( ! selectorMap ) {
176197 try {
177- const { css } = await cssWithMeta ( resolvedPath , {
198+ const { css } = await activeCssWithMeta ( resolvedPath , {
178199 cwd : options . rootDir ,
179200 peerResolver,
180201 } )
@@ -342,6 +363,7 @@ async function resolveImportPath(
342363 resourceSpecifier : string ,
343364 importerPath : string ,
344365 rootDir : string ,
366+ tsconfig ?: TsconfigResolutionContext ,
345367) : Promise < string | undefined > {
346368 if ( ! resourceSpecifier ) return undefined
347369 if ( resourceSpecifier . startsWith ( '.' ) ) {
@@ -350,6 +372,10 @@ async function resolveImportPath(
350372 if ( resourceSpecifier . startsWith ( '/' ) ) {
351373 return path . resolve ( rootDir , resourceSpecifier . slice ( 1 ) )
352374 }
375+ const tsconfigResolved = await resolveWithTsconfigPaths ( resourceSpecifier , tsconfig )
376+ if ( tsconfigResolved ) {
377+ return tsconfigResolved
378+ }
353379 const requireFromRoot = getProjectRequire ( rootDir )
354380 try {
355381 return requireFromRoot . resolve ( resourceSpecifier )
@@ -491,6 +517,93 @@ async function fileExists(target: string): Promise<boolean> {
491517 }
492518}
493519
520+ async function resolveWithTsconfigPaths (
521+ specifier : string ,
522+ tsconfig ?: TsconfigResolutionContext ,
523+ ) : Promise < string | undefined > {
524+ if ( ! tsconfig ) {
525+ return undefined
526+ }
527+ if ( tsconfig . matchPath ) {
528+ const matched = tsconfig . matchPath ( specifier )
529+ if ( matched && ( await fileExists ( matched ) ) ) {
530+ return matched
531+ }
532+ }
533+ if ( tsconfig . absoluteBaseUrl && isNonRelativeSpecifier ( specifier ) ) {
534+ const candidate = path . join (
535+ tsconfig . absoluteBaseUrl ,
536+ specifier . split ( '/' ) . join ( path . sep ) ,
537+ )
538+ if ( await fileExists ( candidate ) ) {
539+ return candidate
540+ }
541+ }
542+ return undefined
543+ }
544+
545+ function loadTsconfigResolutionContext (
546+ rootDir : string ,
547+ loader : typeof getTsconfig = getTsconfig ,
548+ ) : TsconfigResolutionContext | undefined {
549+ let result : TsConfigResult | null
550+ try {
551+ result = loader ( rootDir ) as TsConfigResult | null
552+ } catch {
553+ return undefined
554+ }
555+ if ( ! result ) {
556+ return undefined
557+ }
558+ const compilerOptions = result . config . compilerOptions ?? { }
559+ const configDir = path . dirname ( result . path )
560+ const absoluteBaseUrl = compilerOptions . baseUrl
561+ ? path . resolve ( configDir , compilerOptions . baseUrl )
562+ : undefined
563+ const normalizedPaths = normalizeTsconfigPaths ( compilerOptions . paths )
564+ const matchPath =
565+ absoluteBaseUrl && normalizedPaths
566+ ? createMatchPath ( absoluteBaseUrl , normalizedPaths )
567+ : undefined
568+ if ( ! absoluteBaseUrl && ! matchPath ) {
569+ return undefined
570+ }
571+ return { absoluteBaseUrl, matchPath }
572+ }
573+
574+ function normalizeTsconfigPaths (
575+ paths : Record < string , string [ ] | string > | undefined ,
576+ ) : Record < string , string [ ] > | undefined {
577+ if ( ! paths ) {
578+ return undefined
579+ }
580+ const normalized : Record < string , string [ ] > = { }
581+ for ( const [ pattern , replacements ] of Object . entries ( paths ) ) {
582+ if ( ! replacements ) {
583+ continue
584+ }
585+ const values = Array . isArray ( replacements ) ? replacements : [ replacements ]
586+ if ( values . length === 0 ) {
587+ continue
588+ }
589+ normalized [ pattern ] = values
590+ }
591+ return Object . keys ( normalized ) . length > 0 ? normalized : undefined
592+ }
593+
594+ function isNonRelativeSpecifier ( specifier : string ) : boolean {
595+ if ( ! specifier ) {
596+ return false
597+ }
598+ if ( specifier . startsWith ( '.' ) || specifier . startsWith ( '/' ) ) {
599+ return false
600+ }
601+ if ( / ^ [ a - z ] [ \w + . - ] * : / i. test ( specifier ) ) {
602+ return false
603+ }
604+ return true
605+ }
606+
494607function createProjectPeerResolver ( rootDir : string ) {
495608 const resolver = getProjectRequire ( rootDir )
496609 return async ( name : string ) => {
@@ -643,13 +756,40 @@ function reportCliResult(result: GenerateTypesResult): void {
643756 }
644757}
645758
759+ function setCssWithMetaImplementation ( impl ?: CssWithMetaFn ) : void {
760+ activeCssWithMeta = impl ?? cssWithMeta
761+ }
762+
763+ function setModuleTypeDetector ( detector ?: ModuleTypeDetector ) : void {
764+ moduleTypeDetector = detector ?? moduleType
765+ }
766+
767+ function setImportMetaUrlProvider ( provider ?: ( ) => string | undefined ) : void {
768+ importMetaUrlProvider = provider ?? getImportMetaUrl
769+ }
770+
646771export const __generateTypesInternals = {
772+ writeTypesIndex,
647773 stripInlineLoader,
648774 splitResourceAndQuery,
775+ findSpecifierImports,
776+ resolveImportPath,
777+ resolvePackageRoot,
649778 buildDeclarationFileName,
650779 formatModuleDeclaration,
651780 formatSelectorType,
781+ relativeToRoot,
782+ collectCandidateFiles,
652783 normalizeIncludeOptions,
784+ normalizeTsconfigPaths,
785+ setCssWithMetaImplementation,
786+ setModuleTypeDetector,
787+ setImportMetaUrlProvider,
788+ isNonRelativeSpecifier,
789+ createProjectPeerResolver,
790+ getProjectRequire,
791+ loadTsconfigResolutionContext,
792+ resolveWithTsconfigPaths,
653793 parseCliArgs,
654794 printHelp,
655795 reportCliResult,
0 commit comments