Skip to content

Commit 683db9a

Browse files
authored
Support tsconfig paths without baseurl (#34926)
tsconfig behaves differently with `tsc` vs. Next.js. When `baseurl` is omitted, `paths` should be resolved against the tsconfig location. Consequentially, in this case the paths must start with `./`. This PR aligns Next.js behavior with `tsc` around `paths` without `baseurl`. Related: microsoft/TypeScript#40101
1 parent e0d7ee0 commit 683db9a

File tree

3 files changed

+80
-5
lines changed

3 files changed

+80
-5
lines changed

packages/next/build/load-jsconfig.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export default async function loadJsConfig(
4949
typeScriptPath && (await fileExists(tsConfigPath))
5050
)
5151

52+
let implicitBaseurl
5253
let jsConfig
5354
// jsconfig is a subset of tsconfig
5455
if (useTypeScript) {
@@ -65,17 +66,24 @@ export default async function loadJsConfig(
6566
)) as typeof import('typescript')
6667
const tsConfig = await getTypeScriptConfiguration(ts, tsConfigPath, true)
6768
jsConfig = { compilerOptions: tsConfig.options }
69+
implicitBaseurl = path.dirname(tsConfigPath)
6870
}
6971

7072
const jsConfigPath = path.join(dir, 'jsconfig.json')
7173
if (!useTypeScript && (await fileExists(jsConfigPath))) {
7274
jsConfig = parseJsonFile(jsConfigPath)
75+
implicitBaseurl = path.dirname(jsConfigPath)
7376
}
7477

7578
let resolvedBaseUrl
76-
if (jsConfig?.compilerOptions?.baseUrl) {
77-
resolvedBaseUrl = path.resolve(dir, jsConfig.compilerOptions.baseUrl)
79+
if (jsConfig) {
80+
if (jsConfig.compilerOptions?.baseUrl) {
81+
resolvedBaseUrl = path.resolve(dir, jsConfig.compilerOptions.baseUrl)
82+
} else {
83+
resolvedBaseUrl = implicitBaseurl
84+
}
7885
}
86+
7987
return {
8088
useTypeScript,
8189
jsConfig,

test/integration/jsconfig-paths/test/index.test.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
import fs from 'fs-extra'
44
import { join } from 'path'
55
import cheerio from 'cheerio'
6+
import * as path from 'path'
67
import {
78
renderViaHTTP,
89
findPort,
910
launchApp,
1011
nextBuild,
1112
killApp,
1213
check,
14+
File,
1315
} from 'next-test-utils'
16+
import * as JSON5 from 'json5'
1417

1518
const appDir = join(__dirname, '..')
1619
let appPort
@@ -21,7 +24,7 @@ async function get$(path, query) {
2124
return cheerio.load(html)
2225
}
2326

24-
describe('TypeScript Features', () => {
27+
function runTests() {
2528
describe('default behavior', () => {
2629
let output = ''
2730

@@ -141,4 +144,30 @@ describe('TypeScript Features', () => {
141144
).toBe(false)
142145
})
143146
})
147+
}
148+
149+
describe('jsconfig paths', () => {
150+
runTests()
151+
})
152+
153+
const jsconfig = new File(path.resolve(__dirname, '../jsconfig.json'))
154+
155+
describe('jsconfig paths without baseurl', () => {
156+
beforeAll(() => {
157+
const jsconfigContent = JSON5.parse(jsconfig.originalContent)
158+
delete jsconfigContent.compilerOptions.baseUrl
159+
jsconfigContent.compilerOptions.paths = {
160+
'@c/*': ['./components/*'],
161+
'@lib/*': ['./lib/a/*', './lib/b/*'],
162+
'@mycomponent': ['./components/hello.js'],
163+
'*': ['./node_modules/*'],
164+
}
165+
jsconfig.write(JSON.stringify(jsconfigContent, null, 2))
166+
})
167+
168+
afterAll(() => {
169+
jsconfig.restore()
170+
})
171+
172+
runTests()
144173
})

test/integration/typescript-paths/test/index.test.js

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22

33
import { join } from 'path'
44
import cheerio from 'cheerio'
5-
import { renderViaHTTP, findPort, launchApp, killApp } from 'next-test-utils'
5+
import * as path from 'path'
6+
import {
7+
renderViaHTTP,
8+
findPort,
9+
launchApp,
10+
killApp,
11+
File,
12+
} from 'next-test-utils'
13+
import * as JSON5 from 'json5'
614

715
const appDir = join(__dirname, '..')
816
let appPort
@@ -13,7 +21,7 @@ async function get$(path, query) {
1321
return cheerio.load(html)
1422
}
1523

16-
describe('TypeScript Features', () => {
24+
function runTests() {
1725
describe('default behavior', () => {
1826
beforeAll(async () => {
1927
appPort = await findPort()
@@ -46,4 +54,34 @@ describe('TypeScript Features', () => {
4654
expect($('body').text()).toMatch(/Not aliased to d\.ts file/)
4755
})
4856
})
57+
}
58+
59+
describe('typescript paths', () => {
60+
runTests()
61+
})
62+
63+
const tsconfig = new File(path.resolve(__dirname, '../tsconfig.json'))
64+
65+
describe('typescript paths without baseurl', () => {
66+
beforeAll(async () => {
67+
const tsconfigContent = JSON5.parse(tsconfig.originalContent)
68+
delete tsconfigContent.compilerOptions.baseUrl
69+
tsconfigContent.compilerOptions.paths = {
70+
'isomorphic-unfetch': ['./types/unfetch.d.ts'],
71+
'@c/*': ['./components/*'],
72+
'@lib/*': ['./lib/a/*', './lib/b/*'],
73+
'@mycomponent': ['./components/hello.tsx'],
74+
'd-ts-alias': [
75+
'./components/alias-to-d-ts.d.ts',
76+
'./components/alias-to-d-ts.tsx',
77+
],
78+
}
79+
tsconfig.write(JSON.stringify(tsconfigContent, null, 2))
80+
})
81+
82+
afterAll(() => {
83+
tsconfig.restore()
84+
})
85+
86+
runTests()
4987
})

0 commit comments

Comments
 (0)