Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,88 @@ describe('resolveType', () => {
expect(deps && [...deps]).toStrictEqual(['/user.ts'])
})

test('node subpath imports', () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Test failure indicates the feature isn't working as expected.

Based on the pipeline failure message "Failed to resolve import source '#t1'", the Node subpath imports resolution is not working correctly. This could be due to:

  1. The incorrect import statement for resolve.exports (as noted in the other file)
  2. The package being in devDependencies instead of dependencies
  3. Missing __CJS__ flag during test execution

Let me verify the test environment setup:


🏁 Script executed:

#!/bin/bash
# Check if __CJS__ is defined in the test environment
rg -n "__CJS__" packages/compiler-sfc/__tests__/ -A 2 -B 2

# Check how the tests are built and run
fd "jest.config" --type f | xargs cat 2>/dev/null

# Check the build configuration for tests
fd "rollup.config" --type f | head -5 | xargs grep -l "compiler-sfc" 2>/dev/null | xargs cat 2>/dev/null | grep -A 10 -B 10 "__CJS__"

Length of output: 993


🏁 Script executed:

#!/bin/bash
# Search for usages of resolve.exports in the compiler-sfc package
rg -n "resolve\\.exports" -C3 packages/compiler-sfc

# Inspect dependencies and devDependencies in the compiler-sfc package.json
grep -n "\"dependencies\"" -A20 packages/compiler-sfc/package.json
grep -n "\"devDependencies\"" -A20 packages/compiler-sfc/package.json

Length of output: 2326


🏁 Script executed:

#!/bin/bash
# Show code around __CJS__ usage in resolveType.ts
rg -n "__CJS__" -C5 packages/compiler-sfc/src/script/resolveType.ts

# Show how resolveImports is invoked
rg -n "resolveImports" -C5 packages/compiler-sfc/src/script/resolveType.ts

# Inspect the failing test for '#t1' import source
rg -n "'#t1'" -C5 packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts

Length of output: 2807


Ensure Node subpath imports are resolved at runtime and in tests

The failure “Failed to resolve import source ‘#t1’” is expected:

  • At runtime, resolve.exports is only listed under devDependencies, so it isn’t available when this code path executes.
  • In the unbundled test harness, the __CJS__ guard is falsey by default (no global definition), so resolveWithNodeSubpathImports immediately returns without attempting subpath resolution.

To fix this:

  • Move resolve.exports into dependencies so it’s always installed at runtime.
  • Define __CJS__ = true in the test environment (e.g. via a Jest setup file or globals in your test config) so the Node‐only resolution path is exercised.
  • No change is needed to the import statement (import { imports as resolveImports } from 'resolve.exports' is correct).

Locations to update:

  • packages/compiler-sfc/package.json
     "dependencies": {
       "@babel/parser": "catalog:",
       // …
  • },
  • "devDependencies": {
  • "resolve.exports": "^2.0.3",
  • }
    +},
  • "dependencies": {
  • "resolve.exports": "^2.0.3",
  • // …
    +},
  • "devDependencies": {
  • // other dev deps…
    // …
    }

- **Test setup** (e.g. add a `setupTests.ts` or update Jest/Vitest config)  
```ts
// packages/compiler-sfc/__tests__/setupTests.ts
;(global as any).__CJS__ = true

and ensure it’s loaded before tests (via your test runner’s setupFiles/setup option).

These changes will install the resolver at runtime and unblock the resolveWithNodeSubpathImports logic in tests.

🤖 Prompt for AI Agents
In packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts around line
1201, the test fails because resolve.exports is only in devDependencies and the
test runtime doesn't set the __CJS__ global, so Node subpath resolution path
isn't executed; fix by moving "resolve.exports" from devDependencies to
dependencies in packages/compiler-sfc/package.json so it is installed at
runtime, and add a test setup file (or update your Jest/Vitest config's
setupFiles) that sets global.__CJS__ = true before tests run so
resolveWithNodeSubpathImports is exercised (no change to the import statement is
required).

const files = {
'/package.json': JSON.stringify({
imports: {
'#t1': './t1.ts',
'#t2': '/t2.ts',
'#o/*.ts': './other/*.ts',
},
}),
'/t1.ts': 'export type T1 = { foo: string }',
'/t2.ts': 'export type T2 = { bar: number }',
'/other/t3.ts': 'export type T3 = { baz: string }',
}

const { props, deps } = resolve(
`
import type { T1 } from '#t1'
import type { T2 } from '#t2'
import type { T3 } from '#o/t3.ts'
defineProps<T1 & T2 & T3>()
`,
files,
)

expect(props).toStrictEqual({
foo: ['String'],
bar: ['Number'],
baz: ['String'],
})
expect(deps && [...deps]).toStrictEqual([
'/t1.ts',
'/t2.ts',
'/other/t3.ts',
])
})

test.runIf(process.platform === 'win32')(
'node subpath imports on Windows',
() => {
const files = {
'C:\\Test\\package.json': JSON.stringify({
imports: {
'#t1': '.\\t1.ts',
'#t2': '..\\t2.ts',
'#t3': 'C:\\Test/t3.ts',
'#o/*.ts': '.\\Other\\*.ts',
},
}),
'C:\\Test\\t1.ts': 'export type T1 = { foo: string }',
'C:\\t2.ts': 'export type T2 = { bar: number }',
'C:\\Test\\t3.ts': 'export type T3 = { baz: number }',
'C:\\Test\\Other\\t4.ts': 'export type T4 = { qux: string }',
}

const { props, deps } = resolve(
`
import type { T1 } from '#t1'
import type { T2 } from '#t2'
import type { T3 } from '#t3'
import type { T4 } from '#o/t4.ts'
defineProps<T1 & T2 & T3 & T4>()
`,
files,
{},
'C:\\Test\\Test.vue',
)

expect(props).toStrictEqual({
foo: ['String'],
bar: ['Number'],
baz: ['Number'],
qux: ['String'],
})
expect(deps && [...deps]).toStrictEqual([
'C:/Test/t1.ts',
'C:/t2.ts',
'C:/Test/t3.ts',
'C:/Test/Other/t4.ts',
])
},
)

test('ts module resolve w/ project reference folder', () => {
const files = {
'/tsconfig.json': JSON.stringify({
Expand Down
1 change: 1 addition & 0 deletions packages/compiler-sfc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"estree-walker": "catalog:",
"magic-string": "catalog:",
"postcss": "^8.5.6",
"resolve.exports": "^2.0.3",
"source-map-js": "catalog:"
},
"devDependencies": {
Expand Down
59 changes: 57 additions & 2 deletions packages/compiler-sfc/src/script/resolveType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ import { parse as babelParse } from '@babel/parser'
import { parse } from '../parse'
import { createCache } from '../cache'
import type TS from 'typescript'
import { dirname, extname, join } from 'path'
import { dirname, extname, isAbsolute, join } from 'path'
import { minimatch as isMatch } from 'minimatch'
import * as process from 'process'
import { imports as resolveImports } from 'resolve.exports'

export type SimpleTypeResolveOptions = Partial<
Pick<
Expand Down Expand Up @@ -958,7 +959,9 @@ function importSourceToScope(
)
}
}
resolved = resolveWithTS(scope.filename, source, ts, fs)
resolved =
resolveWithTS(scope.filename, source, ts, fs) ||
resolveWithNodeSubpathImports(scope.filename, source, fs)
}
if (resolved) {
resolved = scope.resolvedImportSources[source] = normalizePath(resolved)
Expand Down Expand Up @@ -1123,6 +1126,58 @@ function loadTSConfig(
return res
}

function resolveWithNodeSubpathImports(
containingFile: string,
source: string,
fs: FS,
): string | undefined {
if (!__CJS__) return

try {
const pkgPath = findPackageJsonFile(containingFile, fs)
if (!pkgPath) {
return
}

const pkgStr = fs.readFile(pkgPath)
if (!pkgStr) {
return
}

const pkg = JSON.parse(pkgStr)
const resolvedImports = resolveImports(pkg, source)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Update to use correct import name from resolve.exports.

After fixing the import statement, update this line to use the correct function name.

-    const resolvedImports = resolveImports(pkg, source)
+    const resolvedImports = imports(pkg, source)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const resolvedImports = resolveImports(pkg, source)
const resolvedImports = imports(pkg, source)
🤖 Prompt for AI Agents
In packages/compiler-sfc/src/script/resolveType.ts around line 1147, the call
currently uses resolveImports(pkg, source) but the correct function name from
resolve.exports is resolveExports; change this invocation to resolveExports(pkg,
source) and ensure the file's import statement at the top imports resolveExports
(not resolveImports) from resolve.exports so the symbol matches.

if (!resolvedImports || !resolvedImports.length) {
return
}

const resolved = isAbsolute(resolvedImports[0])
? resolvedImports[0]
: joinPaths(dirname(pkgPath), resolvedImports[0])

return fs.realpath ? fs.realpath(resolved) : resolved
} catch (e) {}
}

function findPackageJsonFile(
searchStartPath: string,
fs: FS,
): string | undefined {
let currDir = searchStartPath
while (true) {
const filePath = joinPaths(currDir, 'package.json')
if (fs.fileExists(filePath)) {
return filePath
}

const parentDir = dirname(currDir)
if (parentDir === currDir) {
return
}

currDir = parentDir
}
}

const fileToScopeCache = createCache<TypeScope>()

/**
Expand Down
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading