@@ -219,12 +219,101 @@ export function getCreateShortName(createPackageName: string): string {
219219 return createPackageName
220220}
221221
222+ /**
223+ * Map of JS extensions to their corresponding declaration file extensions.
224+ */
225+ const DECLARATION_EXTENSIONS : Record < string , string [ ] > = {
226+ '.mjs' : [ '.d.mts' , '.d.ts' ] ,
227+ '.cjs' : [ '.d.cts' , '.d.ts' ] ,
228+ '.js' : [ '.d.ts' , '.d.mts' , '.d.cts' ] ,
229+ }
230+
231+ /**
232+ * Collect concrete file paths from the exports field, skipping the "types"
233+ * condition (which is already checked by analyzeExports).
234+ */
235+ function collectExportPaths ( exports : PackageExports , depth = 0 ) : string [ ] {
236+ if ( depth > 10 ) return [ ]
237+ if ( exports === null || exports === undefined ) return [ ]
238+
239+ if ( typeof exports === 'string' ) {
240+ return [ exports ]
241+ }
242+
243+ if ( Array . isArray ( exports ) ) {
244+ return exports . flatMap ( item => collectExportPaths ( item , depth + 1 ) )
245+ }
246+
247+ if ( typeof exports === 'object' ) {
248+ const paths : string [ ] = [ ]
249+ for ( const [ key , value ] of Object . entries ( exports ) ) {
250+ // Skip "types" condition — already detected by analyzeExports
251+ if ( key === 'types' ) continue
252+ paths . push ( ...collectExportPaths ( value , depth + 1 ) )
253+ }
254+ return paths
255+ }
256+
257+ return [ ]
258+ }
259+
260+ /**
261+ * Normalize a path by stripping a leading "./" prefix.
262+ */
263+ function stripRelativePrefix ( p : string ) : string {
264+ return p . startsWith ( './' ) ? p . slice ( 2 ) : p
265+ }
266+
267+ /**
268+ * Derive expected declaration file paths from a JS entry point path.
269+ * e.g. "./dist/index.mjs" -> ["dist/index.d.mts", "dist/index.d.ts"]
270+ */
271+ function getDeclCandidates ( entryPath : string ) : string [ ] {
272+ const normalized = stripRelativePrefix ( entryPath )
273+ for ( const [ ext , declExts ] of Object . entries ( DECLARATION_EXTENSIONS ) ) {
274+ if ( normalized . endsWith ( ext ) ) {
275+ const base = normalized . slice ( 0 , - ext . length )
276+ return declExts . map ( de => base + de )
277+ }
278+ }
279+ return [ ]
280+ }
281+
282+ /**
283+ * Check if declaration files exist for any of the package's entry points.
284+ * Derives expected declaration paths from exports/main/module entry points
285+ * (e.g. .d.mts for .mjs) and checks if they exist in the published files.
286+ */
287+ function hasImplicitTypesForEntryPoints ( pkg : ExtendedPackageJson , files : Set < string > ) : boolean {
288+ const entryPaths : string [ ] = [ ]
289+
290+ if ( pkg . exports ) {
291+ entryPaths . push ( ...collectExportPaths ( pkg . exports ) )
292+ }
293+ if ( pkg . main ) {
294+ entryPaths . push ( pkg . main )
295+ }
296+ if ( pkg . module ) {
297+ entryPaths . push ( pkg . module )
298+ }
299+
300+ for ( const entryPath of entryPaths ) {
301+ const candidates = getDeclCandidates ( entryPath )
302+ if ( candidates . some ( c => files . has ( c ) ) ) {
303+ return true
304+ }
305+ }
306+
307+ return false
308+ }
309+
222310/**
223311 * Detect TypeScript types status for a package
224312 */
225313export function detectTypesStatus (
226314 pkg : ExtendedPackageJson ,
227315 typesPackageInfo ?: TypesPackageInfo ,
316+ files ?: Set < string > ,
228317) : TypesStatus {
229318 // Check for built-in types
230319 if ( pkg . types || pkg . typings ) {
@@ -239,6 +328,12 @@ export function detectTypesStatus(
239328 }
240329 }
241330
331+ // Check for implicit types by deriving expected declaration file paths from
332+ // entry points (e.g. .d.mts for .mjs) and checking if they exist in the package
333+ if ( files && hasImplicitTypesForEntryPoints ( pkg , files ) ) {
334+ return { kind : 'included' }
335+ }
336+
242337 // Check for @types package
243338 if ( typesPackageInfo ) {
244339 return {
@@ -289,6 +384,8 @@ export function getTypesPackageName(packageName: string): string {
289384export interface AnalyzePackageOptions {
290385 typesPackage ?: TypesPackageInfo
291386 createPackage ?: CreatePackageInfo
387+ /** Flattened package file paths for implicit types detection (e.g. .d.mts next to .mjs) */
388+ files ?: Set < string >
292389}
293390
294391/**
@@ -299,8 +396,7 @@ export function analyzePackage(
299396 options ?: AnalyzePackageOptions ,
300397) : PackageAnalysis {
301398 const moduleFormat = detectModuleFormat ( pkg )
302-
303- const types = detectTypesStatus ( pkg , options ?. typesPackage )
399+ const types = detectTypesStatus ( pkg , options ?. typesPackage , options ?. files )
304400
305401 return {
306402 moduleFormat,
0 commit comments