Nuxt/packages/nuxi/src/utils/prepare.ts

142 lines
5.0 KiB
TypeScript
Raw Normal View History

import { promises as fsp } from 'node:fs'
import { isAbsolute, join, relative, resolve } from 'pathe'
import type { Nuxt, TSReference } from '@nuxt/schema'
import { defu } from 'defu'
import type { TSConfig } from 'pkg-types'
import { withTrailingSlash } from 'ufo'
import { getModulePaths, getNearestPackage } from './cjs'
export const writeTypes = async (nuxt: Nuxt) => {
const modulePaths = getModulePaths(nuxt.options.modulesDir)
const rootDirWithSlash = withTrailingSlash(nuxt.options.rootDir)
2021-11-16 12:32:21 +00:00
const tsConfig: TSConfig = defu(nuxt.options.typescript?.tsConfig, {
compilerOptions: {
forceConsistentCasingInFileNames: true,
jsx: 'preserve',
target: 'ESNext',
module: 'ESNext',
moduleResolution: nuxt.options.experimental?.typescriptBundlerResolution ? 'Bundler' : 'Node',
skipLibCheck: true,
strict: nuxt.options.typescript?.strict ?? true,
allowJs: true,
// TODO: remove by default in 3.7
baseUrl: nuxt.options.srcDir,
noEmit: true,
resolveJsonModule: true,
allowSyntheticDefaultImports: true,
types: ['node'],
paths: {}
},
include: [
'./nuxt.d.ts',
join(relative(nuxt.options.buildDir, nuxt.options.rootDir), '**/*'),
...nuxt.options.srcDir !== nuxt.options.rootDir ? [join(relative(nuxt.options.buildDir, nuxt.options.srcDir), '**/*')] : [],
...nuxt.options._layers.map(layer => layer.config.srcDir ?? layer.cwd)
.filter(srcOrCwd => !srcOrCwd.startsWith(rootDirWithSlash) || srcOrCwd.includes('node_modules'))
.map(srcOrCwd => join(relative(nuxt.options.buildDir, srcOrCwd), '**/*')),
...nuxt.options.typescript.includeWorkspace && nuxt.options.workspaceDir !== nuxt.options.rootDir ? [join(relative(nuxt.options.buildDir, nuxt.options.workspaceDir), '**/*')] : []
],
exclude: [
// nitro generate output: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/core/nitro.ts#L186
relative(nuxt.options.buildDir, resolve(nuxt.options.rootDir, 'dist'))
]
} satisfies TSConfig)
const aliases: Record<string, string> = {
...nuxt.options.alias,
'#build': nuxt.options.buildDir
}
// Exclude bridge alias types to support Volar
const excludedAlias = [/^@vue\/.*$/]
const basePath = tsConfig.compilerOptions!.baseUrl ? resolve(nuxt.options.buildDir, tsConfig.compilerOptions!.baseUrl) : nuxt.options.buildDir
tsConfig.compilerOptions = tsConfig.compilerOptions || {}
tsConfig.include = tsConfig.include || []
for (const alias in aliases) {
if (excludedAlias.some(re => re.test(alias))) {
continue
}
const absolutePath = resolve(basePath, aliases[alias])
const stats = await fsp.stat(absolutePath).catch(() => null /* file does not exist */)
if (stats?.isDirectory()) {
tsConfig.compilerOptions.paths[alias] = [absolutePath]
tsConfig.compilerOptions.paths[`${alias}/*`] = [`${absolutePath}/*`]
if (!absolutePath.startsWith(rootDirWithSlash)) {
tsConfig.include.push(absolutePath)
tsConfig.include.push(`${absolutePath}/*`)
}
} else {
const path = stats?.isFile()
? absolutePath.replace(/(?<=\w)\.\w+$/g, '') /* remove extension */
: absolutePath
tsConfig.compilerOptions.paths[alias] = [path]
if (!absolutePath.startsWith(rootDirWithSlash)) {
tsConfig.include.push(path)
}
}
}
const references: TSReference[] = [
...nuxt.options.modules,
...nuxt.options._modules
]
.filter(f => typeof f === 'string')
.map(id => ({ types: getNearestPackage(id, modulePaths)?.name || id }))
if (nuxt.options.experimental?.reactivityTransform) {
references.push({ types: 'vue/macros-global' })
}
const declarations: string[] = []
await nuxt.callHook('prepare:types', { references, declarations, tsConfig })
const declaration = [
...references.map((ref) => {
if ('path' in ref && isAbsolute(ref.path)) {
ref.path = relative(nuxt.options.buildDir, ref.path)
}
return `/// <reference ${renderAttrs(ref)} />`
}),
...declarations,
'',
'export {}',
''
].join('\n')
async function writeFile () {
const GeneratedBy = '// Generated by nuxi'
const tsConfigPath = resolve(nuxt.options.buildDir, 'tsconfig.json')
await fsp.mkdir(nuxt.options.buildDir, { recursive: true })
await fsp.writeFile(tsConfigPath, GeneratedBy + '\n' + JSON.stringify(tsConfig, null, 2))
const declarationPath = resolve(nuxt.options.buildDir, 'nuxt.d.ts')
await fsp.writeFile(declarationPath, GeneratedBy + '\n' + declaration)
}
// This is needed for Nuxt 2 which clears the build directory again before building
2023-02-01 18:38:15 +00:00
// https://github.com/nuxt/nuxt/blob/2.x/packages/builder/src/builder.js#L144
// @ts-expect-error TODO: Nuxt 2 hook
nuxt.hook('builder:prepared', writeFile)
await writeFile()
}
function renderAttrs (obj: Record<string, string>) {
return Object.entries(obj).map(e => renderAttr(e[0], e[1])).join(' ')
}
function renderAttr (key: string, value: string) {
return value ? `${key}="${value}"` : ''
}