/
verifyTypeScriptSetup.ts
142 lines (131 loc) · 4.45 KB
/
verifyTypeScriptSetup.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import chalk from 'next/dist/compiled/chalk'
import path from 'path'
import {
hasNecessaryDependencies,
NecessaryDependencies,
} from './has-necessary-dependencies'
import semver from 'next/dist/compiled/semver'
import { CompileError } from './compile-error'
import { FatalError } from './fatal-error'
import * as log from '../build/output/log'
import { getTypeScriptIntent } from './typescript/getTypeScriptIntent'
import { TypeCheckResult } from './typescript/runTypeCheck'
import { writeAppTypeDeclarations } from './typescript/writeAppTypeDeclarations'
import { writeConfigurationDefaults } from './typescript/writeConfigurationDefaults'
import { installDependencies } from './install-dependencies'
import { isCI } from '../telemetry/ci-info'
import { missingDepsError } from './typescript/missingDependencyError'
const requiredPackages = [
{
file: 'typescript/lib/typescript.js',
pkg: 'typescript',
exportsRestrict: true,
},
{
file: '@types/react/index.d.ts',
pkg: '@types/react',
exportsRestrict: true,
},
{
file: '@types/node/index.d.ts',
pkg: '@types/node',
exportsRestrict: true,
},
]
export async function verifyTypeScriptSetup({
dir,
cacheDir,
intentDirs,
tsconfigPath,
typeCheckPreflight,
disableStaticImages,
}: {
dir: string
cacheDir?: string
tsconfigPath: string
intentDirs: string[]
typeCheckPreflight: boolean
disableStaticImages: boolean
}): Promise<{ result?: TypeCheckResult; version: string | null }> {
const resolvedTsConfigPath = path.join(dir, tsconfigPath)
try {
// Check if the project uses TypeScript:
const intent = await getTypeScriptIntent(dir, intentDirs, tsconfigPath)
if (!intent) {
return { version: null }
}
// Ensure TypeScript and necessary `@types/*` are installed:
let deps: NecessaryDependencies = await hasNecessaryDependencies(
dir,
requiredPackages
)
if (deps.missing?.length > 0) {
if (isCI) {
// we don't attempt auto install in CI to avoid side-effects
// and instead log the error for installing needed packages
await missingDepsError(dir, deps.missing)
}
console.log(
chalk.bold.yellow(
`It looks like you're trying to use TypeScript but do not have the required package(s) installed.`
) +
'\n' +
'Installing dependencies' +
'\n\n' +
chalk.bold(
'If you are not trying to use TypeScript, please remove the ' +
chalk.cyan('tsconfig.json') +
' file from your package root (and any TypeScript files in your pages directory).'
) +
'\n'
)
await installDependencies(dir, deps.missing, true).catch((err) => {
if (err && typeof err === 'object' && 'command' in err) {
console.error(
`Failed to install required TypeScript dependencies, please install them manually to continue:\n` +
(err as any).command +
'\n'
)
}
throw err
})
deps = await hasNecessaryDependencies(dir, requiredPackages)
}
// Load TypeScript after we're sure it exists:
const ts = (await Promise.resolve(
require(deps.resolved.get('typescript')!)
)) as typeof import('typescript')
if (semver.lt(ts.version, '4.3.2')) {
log.warn(
`Minimum recommended TypeScript version is v4.3.2, older versions can potentially be incompatible with Next.js. Detected: ${ts.version}`
)
}
// Reconfigure (or create) the user's `tsconfig.json` for them:
await writeConfigurationDefaults(
ts,
resolvedTsConfigPath,
intent.firstTimeSetup
)
// Write out the necessary `next-env.d.ts` file to correctly register
// Next.js' types:
await writeAppTypeDeclarations(dir, !disableStaticImages)
let result
if (typeCheckPreflight) {
const { runTypeCheck } = require('./typescript/runTypeCheck')
// Verify the project passes type-checking before we go to webpack phase:
result = await runTypeCheck(ts, dir, resolvedTsConfigPath, cacheDir)
}
return { result, version: ts.version }
} catch (err) {
// These are special errors that should not show a stack trace:
if (err instanceof CompileError) {
console.error(chalk.red('Failed to compile.\n'))
console.error(err.message)
process.exit(1)
} else if (err instanceof FatalError) {
console.error(err.message)
process.exit(1)
}
throw err
}
}