mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
Don't suppress usage of deprecated TypeScript compilerOptions (#93377)
Co-authored-by: Dmitrii Troitskii <[email protected]>
This commit is contained in:
co-authored by
Dmitrii Troitskii
parent
47bcfa0956
commit
5150cafadb
@@ -1,79 +1,9 @@
|
||||
import { bold, cyan } from '../picocolors'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
import semver from 'next/dist/compiled/semver'
|
||||
|
||||
import isError from '../is-error'
|
||||
|
||||
function resolvePathAliasTarget(baseUrl: string, target: string): string {
|
||||
if (
|
||||
path.isAbsolute(target) ||
|
||||
target.startsWith('./') ||
|
||||
target.startsWith('../')
|
||||
) {
|
||||
return target
|
||||
}
|
||||
|
||||
if (baseUrl === '.' || baseUrl === './') {
|
||||
return `./${target}`
|
||||
}
|
||||
|
||||
const resolvedTarget = path.join(baseUrl, target)
|
||||
if (
|
||||
path.isAbsolute(resolvedTarget) ||
|
||||
resolvedTarget.startsWith('./') ||
|
||||
resolvedTarget.startsWith('../')
|
||||
) {
|
||||
return resolvedTarget
|
||||
}
|
||||
|
||||
return `./${resolvedTarget}`
|
||||
}
|
||||
|
||||
function rewritePathAliasesWithoutBaseUrl(
|
||||
baseUrl: string,
|
||||
originalPaths: unknown
|
||||
): Record<string, unknown> {
|
||||
const rewrittenPaths: Record<string, unknown> =
|
||||
originalPaths && typeof originalPaths === 'object'
|
||||
? Object.fromEntries(
|
||||
Object.entries(originalPaths).map(([key, values]) => [
|
||||
key,
|
||||
Array.isArray(values)
|
||||
? values.map((value) =>
|
||||
typeof value === 'string'
|
||||
? resolvePathAliasTarget(baseUrl, value)
|
||||
: value
|
||||
)
|
||||
: values,
|
||||
])
|
||||
)
|
||||
: {
|
||||
'*': [resolvePathAliasTarget(baseUrl, '*')],
|
||||
}
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(rewrittenPaths, '*')) {
|
||||
rewrittenPaths['*'] = [resolvePathAliasTarget(baseUrl, '*')]
|
||||
}
|
||||
|
||||
return rewrittenPaths
|
||||
}
|
||||
|
||||
function getNormalizedBaseUrlForPaths(
|
||||
baseUrl: string,
|
||||
tsConfigPath: string
|
||||
): string {
|
||||
const tsConfigDir = path.resolve(path.dirname(tsConfigPath))
|
||||
const absoluteBaseUrl = path.isAbsolute(baseUrl)
|
||||
? baseUrl
|
||||
: baseUrl.startsWith('./') || baseUrl.startsWith('../')
|
||||
? path.resolve(tsConfigDir, baseUrl)
|
||||
: path.resolve(baseUrl)
|
||||
const relativeBaseUrl = path.relative(tsConfigDir, absoluteBaseUrl)
|
||||
|
||||
return relativeBaseUrl || '.'
|
||||
}
|
||||
|
||||
export async function getTypeScriptConfiguration(
|
||||
typescript: typeof import('typescript'),
|
||||
tsConfigPath: string,
|
||||
@@ -95,49 +25,6 @@ export async function getTypeScriptConfiguration(
|
||||
}
|
||||
|
||||
let configToParse: any = config
|
||||
if (semver.gte(typescript.version, '6.0.0')) {
|
||||
const target = configToParse.compilerOptions?.target
|
||||
if (
|
||||
typeof target === 'string' &&
|
||||
(target.toLowerCase() === 'es3' || target.toLowerCase() === 'es5')
|
||||
) {
|
||||
const { target: _target, ...restCompilerOptions } =
|
||||
configToParse.compilerOptions ?? {}
|
||||
|
||||
// TypeScript 6 deprecates ES3/ES5 targets. Rewrite deprecated
|
||||
// targets in-memory to keep typechecking working without requiring
|
||||
// `ignoreDeprecations`.
|
||||
configToParse = {
|
||||
...configToParse,
|
||||
compilerOptions: {
|
||||
...restCompilerOptions,
|
||||
target: 'es2015',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const baseUrl = configToParse.compilerOptions?.baseUrl
|
||||
const hasBaseUrl = typeof baseUrl === 'string' && baseUrl.length > 0
|
||||
|
||||
if (hasBaseUrl) {
|
||||
const rewrittenPaths = rewritePathAliasesWithoutBaseUrl(
|
||||
baseUrl,
|
||||
configToParse.compilerOptions?.paths
|
||||
)
|
||||
const { baseUrl: _baseUrl, ...restCompilerOptions } =
|
||||
configToParse.compilerOptions ?? {}
|
||||
|
||||
// TypeScript 6 deprecates `baseUrl`; rewrite aliases to explicit
|
||||
// relative paths so path mapping still works without this option.
|
||||
configToParse = {
|
||||
...configToParse,
|
||||
compilerOptions: {
|
||||
...restCompilerOptions,
|
||||
paths: rewrittenPaths,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = typescript.parseJsonConfigFileContent(
|
||||
configToParse,
|
||||
@@ -154,25 +41,6 @@ export async function getTypeScriptConfiguration(
|
||||
path.dirname(tsConfigPath)
|
||||
)
|
||||
|
||||
if (semver.gte(typescript.version, '6.0.0')) {
|
||||
const parsedBaseUrl = result.options.baseUrl
|
||||
if (typeof parsedBaseUrl === 'string' && parsedBaseUrl.length > 0) {
|
||||
const normalizedBaseUrl = getNormalizedBaseUrlForPaths(
|
||||
parsedBaseUrl,
|
||||
tsConfigPath
|
||||
)
|
||||
|
||||
// `baseUrl` can come from extended tsconfigs. Rewrite paths from the
|
||||
// fully-resolved baseUrl and remove it so TS6 deprecation checks do not
|
||||
// fail type checking.
|
||||
result.options.paths = rewritePathAliasesWithoutBaseUrl(
|
||||
normalizedBaseUrl,
|
||||
result.options.paths
|
||||
) as import('typescript').MapLike<string[]>
|
||||
delete (result.options as { baseUrl?: unknown }).baseUrl
|
||||
}
|
||||
}
|
||||
|
||||
if (result.errors) {
|
||||
result.errors = result.errors.filter(
|
||||
({ code }) =>
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
export const greet = (name: string): string => 'Hello, ' + name + '!'
|
||||
@@ -0,0 +1,2 @@
|
||||
packages:
|
||||
- web
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"ignoreDeprecations": "6.0",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@shared/*": ["packages/shared/src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { greet } from '@shared/index'
|
||||
|
||||
export default function Page() {
|
||||
return <p>{greet('world')}</p>
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const config = {
|
||||
typescript: {
|
||||
ignoreBuildErrors: false,
|
||||
},
|
||||
}
|
||||
module.exports = config
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"plugins": [{ "name": "next" }]
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import path from 'path'
|
||||
import { FileRef, nextTestSetup } from 'e2e-utils'
|
||||
|
||||
// Reproduces https://github.com/vercel/next.js/issues/93336
|
||||
describe('typescript paths with deprecated, inherited baseUrl', () => {
|
||||
const fixtureDir = path.join(__dirname, 'fixtures/inherited')
|
||||
const { skipped, next } = nextTestSetup({
|
||||
files: {
|
||||
'../pnpm-workspace.yaml': new FileRef(
|
||||
path.join(fixtureDir, 'pnpm-workspace.yaml')
|
||||
),
|
||||
'../tsconfig.base.json': new FileRef(
|
||||
path.join(fixtureDir, 'tsconfig.base.json')
|
||||
),
|
||||
'../packages': new FileRef(path.join(fixtureDir, 'packages')),
|
||||
'next.config.js': new FileRef(
|
||||
path.join(fixtureDir, 'web/next.config.js')
|
||||
),
|
||||
'tsconfig.json': new FileRef(path.join(fixtureDir, 'web/tsconfig.json')),
|
||||
app: new FileRef(path.join(fixtureDir, 'web/app')),
|
||||
},
|
||||
subDir: 'web',
|
||||
dependencies: {
|
||||
// Specifically testing compilerOptions that were deprecated in 6.0.
|
||||
// This test would fail in 7.0
|
||||
typescript: '^6.0.0',
|
||||
},
|
||||
// Only next-build is interesting here.
|
||||
skipDeployment: true,
|
||||
})
|
||||
|
||||
if (skipped) {
|
||||
return
|
||||
}
|
||||
|
||||
it('should render the page that uses the aliased module', async () => {
|
||||
const html = await next.render('/')
|
||||
expect(html).toContain('Hello, world!')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user