From f7cc14d4021f57b833ef5cc44ad685519c2e387e Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 22 Jun 2021 10:55:05 -0500 Subject: [PATCH 1/2] Update to only add image import types when enabled --- packages/next/build/index.ts | 8 ++- packages/next/image-types/global.d.ts | 62 +++++++++++++++++++ .../typescript/writeAppTypeDeclarations.ts | 27 ++++---- packages/next/lib/verifyTypeScriptSetup.ts | 3 +- packages/next/server/next-dev-server.ts | 7 ++- packages/next/types/global.d.ts | 60 ------------------ .../image-component/typescript/next.config.js | 1 + .../typescript/test/index.test.js | 35 +++++++++++ 8 files changed, 128 insertions(+), 75 deletions(-) create mode 100644 packages/next/image-types/global.d.ts diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index edb867d66eea4..79465df567397 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -192,7 +192,13 @@ export default async function build( const verifyResult = await nextBuildSpan .traceChild('verify-typescript-setup') .traceAsyncFn(() => - verifyTypeScriptSetup(dir, pagesDir, !ignoreTypeScriptErrors, cacheDir) + verifyTypeScriptSetup( + dir, + pagesDir, + !ignoreTypeScriptErrors, + !config.images.disableStaticImages, + cacheDir + ) ) const typeCheckEnd = process.hrtime(typeCheckStart) diff --git a/packages/next/image-types/global.d.ts b/packages/next/image-types/global.d.ts new file mode 100644 index 0000000000000..394e4bf3a47e2 --- /dev/null +++ b/packages/next/image-types/global.d.ts @@ -0,0 +1,62 @@ +// this file is conditionally added/removed to next-env.d.ts +// if the static image import handling is enabled + +interface StaticImageData { + src: string + height: number + width: number + placeholder?: string +} + +declare module '*.png' { + const content: StaticImageData + + 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 + + export default content +} + +declare module '*.jpeg' { + const content: StaticImageData + + export default content +} + +declare module '*.gif' { + const content: StaticImageData + + export default content +} + +declare module '*.webp' { + const content: StaticImageData + + export default content +} + +declare module '*.ico' { + const content: StaticImageData + + export default content +} + +declare module '*.bmp' { + const content: StaticImageData + + export default content +} diff --git a/packages/next/lib/typescript/writeAppTypeDeclarations.ts b/packages/next/lib/typescript/writeAppTypeDeclarations.ts index 9b0914b1d2a53..9bc1a039dd56b 100644 --- a/packages/next/lib/typescript/writeAppTypeDeclarations.ts +++ b/packages/next/lib/typescript/writeAppTypeDeclarations.ts @@ -1,19 +1,22 @@ import { promises as fs } from 'fs' import os from 'os' import path from 'path' -import { fileExists } from '../file-exists' -export async function writeAppTypeDeclarations(baseDir: string): Promise { +export async function writeAppTypeDeclarations( + baseDir: string, + imageImportsEnabled: boolean +): Promise { // Reference `next` types const appTypeDeclarations = path.join(baseDir, 'next-env.d.ts') - const hasAppTypeDeclarations = await fileExists(appTypeDeclarations) - if (!hasAppTypeDeclarations) { - await fs.writeFile( - appTypeDeclarations, - '/// ' + - os.EOL + - '/// ' + - os.EOL - ) - } + + await fs.writeFile( + appTypeDeclarations, + '/// ' + + os.EOL + + '/// ' + + os.EOL + + (imageImportsEnabled + ? '/// ' + os.EOL + : '') + ) } diff --git a/packages/next/lib/verifyTypeScriptSetup.ts b/packages/next/lib/verifyTypeScriptSetup.ts index 8274e3d68195d..44a201baa6972 100644 --- a/packages/next/lib/verifyTypeScriptSetup.ts +++ b/packages/next/lib/verifyTypeScriptSetup.ts @@ -18,6 +18,7 @@ export async function verifyTypeScriptSetup( dir: string, pagesDir: string, typeCheckPreflight: boolean, + imageImportsEnabled: boolean, cacheDir?: string ): Promise<{ result?: TypeCheckResult; version: string | null }> { const tsConfigPath = path.join(dir, 'tsconfig.json') @@ -52,7 +53,7 @@ export async function verifyTypeScriptSetup( await writeConfigurationDefaults(ts, tsConfigPath, firstTimeSetup) // Write out the necessary `next-env.d.ts` file to correctly register // Next.js' types: - await writeAppTypeDeclarations(dir) + await writeAppTypeDeclarations(dir, imageImportsEnabled) let result if (typeCheckPreflight) { diff --git a/packages/next/server/next-dev-server.ts b/packages/next/server/next-dev-server.ts index 725dd63325035..6919bfb8f94bd 100644 --- a/packages/next/server/next-dev-server.ts +++ b/packages/next/server/next-dev-server.ts @@ -275,7 +275,12 @@ export default class DevServer extends Server { } async prepare(): Promise { - await verifyTypeScriptSetup(this.dir, this.pagesDir!, false) + await verifyTypeScriptSetup( + this.dir, + this.pagesDir!, + false, + !this.nextConfig.images.disableStaticImages + ) this.customRoutes = await loadCustomRoutes(this.nextConfig) diff --git a/packages/next/types/global.d.ts b/packages/next/types/global.d.ts index f30fe25230a69..706b8a1dbd184 100644 --- a/packages/next/types/global.d.ts +++ b/packages/next/types/global.d.ts @@ -25,63 +25,3 @@ declare module '*.module.scss' { const classes: { readonly [key: string]: string } export default classes } - -interface StaticImageData { - src: string - height: number - width: number - placeholder?: string -} - -declare module '*.png' { - const content: StaticImageData - - 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 - - export default content -} - -declare module '*.jpeg' { - const content: StaticImageData - - export default content -} - -declare module '*.gif' { - const content: StaticImageData - - export default content -} - -declare module '*.webp' { - const content: StaticImageData - - export default content -} - -declare module '*.ico' { - const content: StaticImageData - - export default content -} - -declare module '*.bmp' { - const content: StaticImageData - - export default content -} diff --git a/test/integration/image-component/typescript/next.config.js b/test/integration/image-component/typescript/next.config.js index 64af59160546d..51d2e7ea8f73d 100644 --- a/test/integration/image-component/typescript/next.config.js +++ b/test/integration/image-component/typescript/next.config.js @@ -1,5 +1,6 @@ module.exports = { images: { domains: ['via.placeholder.com'], + // disableStaticImages: true, }, } diff --git a/test/integration/image-component/typescript/test/index.test.js b/test/integration/image-component/typescript/test/index.test.js index 30aa6da81be59..66477dd5b838c 100644 --- a/test/integration/image-component/typescript/test/index.test.js +++ b/test/integration/image-component/typescript/test/index.test.js @@ -1,5 +1,6 @@ /* eslint-env jest */ +import fs from 'fs-extra' import { join } from 'path' import { renderViaHTTP, @@ -12,6 +13,7 @@ import { jest.setTimeout(1000 * 60 * 2) const appDir = join(__dirname, '..') +const nextConfig = join(appDir, 'next.config.js') let appPort let app let output @@ -27,6 +29,21 @@ describe('TypeScript Image Component', () => { expect(stderr).toMatch(/Failed to compile/) expect(stderr).toMatch(/is not assignable to type/) expect(code).toBe(1) + const envTypes = await fs.readFile(join(appDir, 'next-env.d.ts'), 'utf8') + expect(envTypes).toContain('image-types/global') + }) + + it('should remove global image types when disabled', async () => { + const content = await fs.readFile(nextConfig, 'utf8') + await fs.writeFile( + nextConfig, + content.replace('// disableStaticImages', 'disableStaticImages') + ) + const { code } = await nextBuild(appDir, [], { stderr: true }) + expect(code).toBe(1) + await fs.writeFile(nextConfig, content) + const envTypes = await fs.readFile(join(appDir, 'next-env.d.ts'), 'utf8') + expect(envTypes).not.toContain('image-types/global') }) }) @@ -41,6 +58,11 @@ describe('TypeScript Image Component', () => { }) afterAll(() => killApp(app)) + it('should have image types when enabled', async () => { + const envTypes = await fs.readFile(join(appDir, 'next-env.d.ts'), 'utf8') + expect(envTypes).toContain('image-types/global') + }) + it('should render the valid Image usage and not print error', async () => { const html = await renderViaHTTP(appPort, '/valid', {}) expect(html).toMatch(/This is valid usage of the Image component/) @@ -54,4 +76,17 @@ describe('TypeScript Image Component', () => { ) }) }) + + it('should remove global image types when disabled (dev)', async () => { + const content = await fs.readFile(nextConfig, 'utf8') + await fs.writeFile( + nextConfig, + content.replace('// disableStaticImages', 'disableStaticImages') + ) + const app = await launchApp(appDir, await findPort(), []) + await killApp(app) + await fs.writeFile(nextConfig, content) + const envTypes = await fs.readFile(join(appDir, 'next-env.d.ts'), 'utf8') + expect(envTypes).not.toContain('image-types/global') + }) }) From cdff814dffa5272d3678fb1355afebd306eb3d62 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 22 Jun 2021 11:08:43 -0500 Subject: [PATCH 2/2] add type check to test --- .../integration/image-component/typescript/test/index.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/integration/image-component/typescript/test/index.test.js b/test/integration/image-component/typescript/test/index.test.js index 66477dd5b838c..1aaa73c678cbb 100644 --- a/test/integration/image-component/typescript/test/index.test.js +++ b/test/integration/image-component/typescript/test/index.test.js @@ -39,7 +39,9 @@ describe('TypeScript Image Component', () => { nextConfig, content.replace('// disableStaticImages', 'disableStaticImages') ) - const { code } = await nextBuild(appDir, [], { stderr: true }) + const { code, stderr } = await nextBuild(appDir, [], { stderr: true }) + expect(stderr).toMatch(/Failed to compile/) + expect(stderr).toMatch(/is not assignable to type/) expect(code).toBe(1) await fs.writeFile(nextConfig, content) const envTypes = await fs.readFile(join(appDir, 'next-env.d.ts'), 'utf8')