@@ -103,6 +103,37 @@ function getExtensionPriority(sourceFile: string): string[][] {
103103 return [ [ ] , [ '.ts' , '.js' ] , [ '.d.ts' ] , [ '.json' ] ]
104104}
105105
106+ /**
107+ * Resolve an alias specifier to the directory path within a file path.
108+ * Supports #, ~, and @ prefixes (e.g. #app, ~/app, @/app).
109+ * The alias must match a path segment exactly (no partial matches).
110+ */
111+ export function resolveAliasToDir ( aliasSpec : string , filePath ?: string | null ) : string | null {
112+ if (
113+ ( ! aliasSpec . startsWith ( '#' ) && ! aliasSpec . startsWith ( '~' ) && ! aliasSpec . startsWith ( '@' ) ) ||
114+ ! filePath
115+ ) {
116+ return null
117+ }
118+
119+ // Support #app, #/app, ~app, ~/app, @app, @/app
120+ const alias = aliasSpec . replace ( / ^ [ # ~ @ ] \/ ? / , '' )
121+ const segments = filePath . split ( '/' )
122+
123+ let lastMatchIndex = - 1
124+ for ( let i = 0 ; i < segments . length ; i ++ ) {
125+ if ( segments [ i ] === alias ) {
126+ lastMatchIndex = i
127+ }
128+ }
129+
130+ if ( lastMatchIndex === - 1 ) {
131+ return null
132+ }
133+
134+ return segments . slice ( 0 , lastMatchIndex + 1 ) . join ( '/' )
135+ }
136+
106137/**
107138 * Get index file extensions to try for directory imports.
108139 */
@@ -222,6 +253,60 @@ function normalizeInternalImportTarget(target: InternalImportTarget): string | n
222253 return null
223254}
224255
256+ function guessInternalImportTarget (
257+ imports : InternalImportsMap ,
258+ specifier : string ,
259+ files : FileSet ,
260+ currentFile : string ,
261+ ) : string | null {
262+ for ( const [ key , value ] of Object . entries ( imports ) ) {
263+ if ( specifier . startsWith ( key ) ) {
264+ const basePath = resolveAliasToDir ( key , normalizeInternalImportTarget ( value ) )
265+ if ( ! basePath ) continue
266+
267+ const suffix = specifier . substring ( key . length ) . trim ( ) . replace ( / ^ \/ / , '' )
268+ const pathWithoutExt = suffix ? `${ basePath } /${ suffix } ` : basePath
269+
270+ const toCheckPath = ( p : string ) => files . has ( normalizePath ( p ) ) || files . has ( p )
271+
272+ // Path already has an extension-like suffix on the last segment - return as is if exists
273+ const filename = pathWithoutExt . split ( '/' ) . pop ( ) ?? ''
274+ if ( filename . includes ( '.' ) && ! filename . endsWith ( '.' ) ) {
275+ if ( toCheckPath ( pathWithoutExt ) ) {
276+ return pathWithoutExt . startsWith ( './' ) ? pathWithoutExt : `./${ pathWithoutExt } `
277+ }
278+ return null
279+ }
280+
281+ // Try adding extensions based on currentFile type
282+ const extensionGroups = getExtensionPriority ( currentFile )
283+ for ( const extensions of extensionGroups ) {
284+ if ( extensions . length === 0 ) {
285+ if ( toCheckPath ( pathWithoutExt ) ) {
286+ return pathWithoutExt . startsWith ( './' ) ? pathWithoutExt : `./${ pathWithoutExt } `
287+ }
288+ } else {
289+ for ( const ext of extensions ) {
290+ const pathWithExt = pathWithoutExt + ext
291+ if ( toCheckPath ( pathWithExt ) ) {
292+ return pathWithExt . startsWith ( './' ) ? pathWithExt : `./${ pathWithExt } `
293+ }
294+ }
295+ }
296+ }
297+
298+ // Try as directory with index file
299+ for ( const indexFile of getIndexExtensions ( currentFile ) ) {
300+ const indexPath = `${ pathWithoutExt } /${ indexFile } `
301+ if ( toCheckPath ( indexPath ) ) {
302+ return indexPath . startsWith ( './' ) ? indexPath : `./${ indexPath } `
303+ }
304+ }
305+ }
306+ }
307+ return null
308+ }
309+
225310/**
226311 * import ... from '#components/Button.vue'
227312 * import ... from '#/components/Button.vue'
@@ -230,6 +315,7 @@ function normalizeInternalImportTarget(target: InternalImportTarget): string | n
230315 */
231316export function resolveInternalImport (
232317 specifier : string ,
318+ currentFile : string ,
233319 imports : InternalImportsMap | undefined ,
234320 files : FileSet ,
235321) : ResolvedImport | null {
@@ -239,8 +325,12 @@ export function resolveInternalImport(
239325 return null
240326 }
241327
242- const target = normalizeInternalImportTarget ( imports [ cleanSpecifier ] )
243- console . log ( 'resolved internal import' , imports , cleanSpecifier , target )
328+ const importTarget = normalizeInternalImportTarget ( imports [ cleanSpecifier ] )
329+ const target =
330+ importTarget != null
331+ ? importTarget
332+ : guessInternalImportTarget ( imports , cleanSpecifier , files , currentFile )
333+
244334 if ( ! target || ! target . startsWith ( './' ) ) {
245335 return null
246336 }
@@ -265,8 +355,8 @@ export function createImportResolver(
265355) : ( specifier : string ) => string | null {
266356 return ( specifier : string ) => {
267357 const relativeResolved = resolveRelativeImport ( specifier , currentFile , files )
268- const internalResolved = resolveInternalImport ( specifier , internalImports , files )
269- const resolved = relativeResolved ?? internalResolved
358+ const internalResolved = resolveInternalImport ( specifier , currentFile , internalImports , files )
359+ const resolved = relativeResolved != null ? relativeResolved : internalResolved
270360
271361 if ( resolved ) {
272362 return `/package-code/${ packageName } /v/${ version } /${ resolved . path } `
0 commit comments