Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add possible to override type of '*.svg' #30060

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
128 changes: 68 additions & 60 deletions packages/next/build/index.ts
Expand Up @@ -90,7 +90,7 @@ import {
printTreeView,
getCssFilePaths,
} from './utils'
import getBaseWebpackConfig from './webpack-config'
import getBaseWebpackConfig, { hasCustomSvgLoader } from './webpack-config'
import { PagesManifest } from './webpack/plugins/pages-manifest-plugin'
import { writeBuildId } from './write-build-id'
import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path'
Expand Down Expand Up @@ -190,65 +190,8 @@ export default async function build(
telemetry.record(events)
)

const ignoreTypeScriptErrors = Boolean(config.typescript.ignoreBuildErrors)
const typeCheckStart = process.hrtime()
const typeCheckingSpinner = createSpinner({
prefixText: `${Log.prefixes.info} ${
ignoreTypeScriptErrors
? 'Skipping validation of types'
: 'Checking validity of types'
}`,
})

const verifyResult = await nextBuildSpan
.traceChild('verify-typescript-setup')
.traceAsyncFn(() =>
verifyTypeScriptSetup(
dir,
pagesDir,
!ignoreTypeScriptErrors,
config,
cacheDir
)
)

const typeCheckEnd = process.hrtime(typeCheckStart)

if (!ignoreTypeScriptErrors) {
telemetry.record(
eventTypeCheckCompleted({
durationInSeconds: typeCheckEnd[0],
typescriptVersion: verifyResult.version,
inputFilesCount: verifyResult.result?.inputFilesCount,
totalFilesCount: verifyResult.result?.totalFilesCount,
incremental: verifyResult.result?.incremental,
})
)
}

if (typeCheckingSpinner) {
typeCheckingSpinner.stopAndPersist()
}

const ignoreESLint = Boolean(config.eslint.ignoreDuringBuilds)
const eslintCacheDir = path.join(cacheDir, 'eslint/')
if (!ignoreESLint && runLint) {
await nextBuildSpan
.traceChild('verify-and-lint')
.traceAsyncFn(async () => {
await verifyAndLint(
dir,
eslintCacheDir,
config.eslint?.dirs,
config.experimental.cpus,
config.experimental.workerThreads,
telemetry
)
})
}

const buildSpinner = createSpinner({
prefixText: `${Log.prefixes.info} Creating an optimized production build`,
const configCheckSpinner = createSpinner({
prefixText: `${Log.prefixes.info} Checking build configuration`,
})

const isLikeServerless = isTargetLikeServerless(target)
Expand Down Expand Up @@ -595,7 +538,72 @@ export default async function build(
)
}

if (configCheckSpinner) {
configCheckSpinner.stopAndPersist()
}

const ignoreTypeScriptErrors = Boolean(config.typescript.ignoreBuildErrors)
const typeCheckStart = process.hrtime()
const typeCheckingSpinner = createSpinner({
prefixText: `${Log.prefixes.info} ${
ignoreTypeScriptErrors
? 'Skipping validation of types'
: 'Checking validity of types'
}`,
})

const verifyResult = await nextBuildSpan
.traceChild('verify-typescript-setup')
.traceAsyncFn(() =>
verifyTypeScriptSetup(
dir,
pagesDir,
!ignoreTypeScriptErrors,
config,
hasCustomSvgLoader(clientConfig),
cacheDir
)
)

const typeCheckEnd = process.hrtime(typeCheckStart)

if (!ignoreTypeScriptErrors) {
telemetry.record(
eventTypeCheckCompleted({
durationInSeconds: typeCheckEnd[0],
typescriptVersion: verifyResult.version,
inputFilesCount: verifyResult.result?.inputFilesCount,
totalFilesCount: verifyResult.result?.totalFilesCount,
incremental: verifyResult.result?.incremental,
})
)
}

if (typeCheckingSpinner) {
typeCheckingSpinner.stopAndPersist()
}

const ignoreESLint = Boolean(config.eslint.ignoreDuringBuilds)
const eslintCacheDir = path.join(cacheDir, 'eslint/')
if (!ignoreESLint && runLint) {
await nextBuildSpan
.traceChild('verify-and-lint')
.traceAsyncFn(async () => {
await verifyAndLint(
dir,
eslintCacheDir,
config.eslint?.dirs,
config.experimental.cpus,
config.experimental.workerThreads,
telemetry
)
})
}

const webpackBuildStart = process.hrtime()
const buildSpinner = createSpinner({
prefixText: `${Log.prefixes.info} Creating an optimized production build`,
})

let result: CompilerResult = { warnings: [], errors: [] }
// We run client and server compilation separately to optimize for memory usage
Expand Down
22 changes: 15 additions & 7 deletions packages/next/build/webpack-config.ts
Expand Up @@ -178,6 +178,20 @@ export function attachReactRefresh(
}
}

export function hasCustomSvgLoader(webpackConfig: webpack.Configuration) {
const rules = webpackConfig.module?.rules || []

const hasCustomSvg = rules.some(
(rule) =>
rule.loader !== 'next-image-loader' &&
'test' in rule &&
rule.test instanceof RegExp &&
rule.test.test('.svg')
)

return hasCustomSvg
}

export const NODE_RESOLVE_OPTIONS = {
dependencyType: 'commonjs',
modules: ['node_modules'],
Expand Down Expand Up @@ -1552,13 +1566,7 @@ export default async function getBaseWebpackConfig(

if (!config.images.disableStaticImages) {
const rules = webpackConfig.module?.rules || []
const hasCustomSvg = rules.some(
(rule) =>
rule.loader !== 'next-image-loader' &&
'test' in rule &&
rule.test instanceof RegExp &&
rule.test.test('.svg')
)
const hasCustomSvg = hasCustomSvgLoader(webpackConfig)
const nextImageRule = rules.find(
(rule) => rule.loader === 'next-image-loader'
)
Expand Down
8 changes: 8 additions & 0 deletions packages/next/image-types/global-svg.d.ts
@@ -0,0 +1,8 @@
// this file is conditionally added/removed to next-env.d.ts
// if the static image import handling is enabled and there is no custom SVG loader

declare module '*.svg' {
const content: StaticImageData

export default content
}
11 changes: 0 additions & 11 deletions packages/next/image-types/global.d.ts
Expand Up @@ -14,17 +14,6 @@ declare module '*.png' {
export default content
}

declare module '*.svg' {
/**
* Use `any` to avoid conflicts with
* `@svgr/webpack` plugin or
* `babel-plugin-inline-react-svg` plugin.
*/
const content: any

export default content
}

declare module '*.jpg' {
const content: StaticImageData

Expand Down
6 changes: 5 additions & 1 deletion packages/next/lib/typescript/writeAppTypeDeclarations.ts
Expand Up @@ -4,7 +4,8 @@ import { promises as fs } from 'fs'

export async function writeAppTypeDeclarations(
baseDir: string,
imageImportsEnabled: boolean
imageImportsEnabled: boolean,
hasCustomSvgLoader: boolean
): Promise<void> {
// Reference `next` types
const appTypeDeclarations = path.join(baseDir, 'next-env.d.ts')
Expand Down Expand Up @@ -35,6 +36,9 @@ export async function writeAppTypeDeclarations(
(imageImportsEnabled
? '/// <reference types="next/image-types/global" />' + eol
: '') +
(imageImportsEnabled && !hasCustomSvgLoader
? '/// <reference types="next/image-types/global-svg" />' + eol
: '') +
eol +
'// NOTE: This file should not be edited' +
eol +
Expand Down
7 changes: 6 additions & 1 deletion packages/next/lib/verifyTypeScriptSetup.ts
Expand Up @@ -27,6 +27,7 @@ export async function verifyTypeScriptSetup(
pagesDir: string,
typeCheckPreflight: boolean,
config: NextConfigComplete,
hasCustomSvgLoader: boolean,
cacheDir?: string
): Promise<{ result?: TypeCheckResult; version: string | null }> {
const tsConfigPath = path.join(dir, config.typescript.tsconfigPath)
Expand Down Expand Up @@ -63,7 +64,11 @@ export async function verifyTypeScriptSetup(
await writeConfigurationDefaults(ts, tsConfigPath, intent.firstTimeSetup)
// Write out the necessary `next-env.d.ts` file to correctly register
// Next.js' types:
await writeAppTypeDeclarations(dir, !config.images.disableStaticImages)
await writeAppTypeDeclarations(
dir,
!config.images.disableStaticImages,
hasCustomSvgLoader
)

let result
if (typeCheckPreflight) {
Expand Down
3 changes: 2 additions & 1 deletion packages/next/package.json
Expand Up @@ -44,7 +44,8 @@
"amp.d.ts",
"types/index.d.ts",
"types/global.d.ts",
"image-types/global.d.ts"
"image-types/global.d.ts",
"image-types/global-svg.d.ts"
],
"bin": {
"next": "./dist/bin/next"
Expand Down
13 changes: 12 additions & 1 deletion packages/next/server/dev/hot-reloader.ts
Expand Up @@ -11,7 +11,9 @@ import {
finalizeEntrypoint,
} from '../../build/entries'
import { watchCompilers } from '../../build/output'
import getBaseWebpackConfig from '../../build/webpack-config'
import getBaseWebpackConfig, {
hasCustomSvgLoader,
} from '../../build/webpack-config'
import { API_ROUTE, MIDDLEWARE_ROUTE } from '../../lib/constants'
import { recursiveDelete } from '../../lib/recursive-delete'
import { BLOCKED_PAGES } from '../../shared/lib/constants'
Expand All @@ -34,6 +36,7 @@ import { DecodeError } from '../../shared/lib/utils'
import { Span, trace } from '../../trace'
import isError from '../../lib/is-error'
import ws from 'next/dist/compiled/ws'
import { verifyTypeScriptSetup } from '../../lib/verifyTypeScriptSetup'

const wsServer = new ws.Server({ noServer: true })

Expand Down Expand Up @@ -400,6 +403,14 @@ export default class HotReloader {

const configs = await this.getWebpackConfig(startSpan)

await verifyTypeScriptSetup(
this.dir,
this.pagesDir!,
false,
this.config,
hasCustomSvgLoader(configs[0])
)

for (const config of configs) {
const defaultEntry = config.entry
config.entry = async (...args) => {
Expand Down
7 changes: 0 additions & 7 deletions packages/next/server/dev/next-dev-server.ts
Expand Up @@ -14,7 +14,6 @@ import { PUBLIC_DIR_MIDDLEWARE_CONFLICT } from '../../lib/constants'
import { fileExists } from '../../lib/file-exists'
import { findPagesDir } from '../../lib/find-pages-dir'
import loadCustomRoutes, { CustomRoutes } from '../../lib/load-custom-routes'
import { verifyTypeScriptSetup } from '../../lib/verifyTypeScriptSetup'
import {
PHASE_DEVELOPMENT_SERVER,
CLIENT_STATIC_FILES_PATH,
Expand Down Expand Up @@ -320,12 +319,6 @@ export default class DevServer extends Server {
async prepare(): Promise<void> {
setGlobal('distDir', this.distDir)
setGlobal('phase', PHASE_DEVELOPMENT_SERVER)
await verifyTypeScriptSetup(
this.dir,
this.pagesDir!,
false,
this.nextConfig
)

this.customRoutes = await loadCustomRoutes(this.nextConfig)

Expand Down
@@ -0,0 +1,5 @@
declare module '*.svg' {
import React from 'react'
const SVG: React.FC<React.SVGProps<SVGSVGElement>>
export default SVG
}