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

Font loader default config #41628

Merged
merged 17 commits into from Oct 22, 2022
8 changes: 7 additions & 1 deletion packages/font/google/index.js
@@ -1 +1,7 @@
throw new Error('@next/font/google is not correctly configured')
let message = '@next/font/google failed to run or is incorrectly configured.'
if (process.env.NODE_ENV === 'development') {
message +=
'\nIf you just installed `@next/font`, please try restarting `next dev` and resaving your file.'
}

throw new Error(message)
8 changes: 7 additions & 1 deletion packages/font/local/index.js
@@ -1 +1,7 @@
throw new Error('@next/font/local is not correctly configured')
let message = '@next/font/local failed to run or is incorrectly configured.'
if (process.env.NODE_ENV === 'development') {
message +=
'\nIf you just installed `@next/font`, please try restarting `next dev` and resaving your file.'
}

throw new Error(message)
17 changes: 13 additions & 4 deletions packages/font/src/google/loader.ts
@@ -1,6 +1,10 @@
import type { AdjustFontFallback, FontLoader } from 'next/font'
// @ts-ignore
import { calculateSizeAdjustValues } from 'next/dist/server/font-utils'
// @ts-ignore
import * as Log from 'next/dist/build/output/log'
// @ts-ignore
import chalk from 'next/dist/compiled/chalk'
import {
fetchCSSFromGoogleFonts,
fetchFontFile,
Expand All @@ -19,10 +23,15 @@ const downloadGoogleFonts: FontLoader = async ({
emitFontFile,
}) => {
if (!config?.subsets) {
throw new Error(
'Please specify subsets for `@next/font/google` in your `next.config.js`'
Log.warn(
`${chalk.bold('@next/font/google')} is missing ${chalk.bold(
'options.subsets'
)} in your ${chalk.bold(
'next.config.js'
)}. Please specify subsets, otherwise no fonts will be preloaded.`
)
}
const subsets = config?.subsets || []

const {
fontFamily,
Expand Down Expand Up @@ -63,7 +72,7 @@ const downloadGoogleFonts: FontLoader = async ({
if (googleFontFileUrl) {
fontFiles.push({
googleFontFileUrl,
preloadFontFile: !!preload && config.subsets.includes(currentSubset),
preloadFontFile: !!preload && subsets.includes(currentSubset),
})
}
}
Expand Down Expand Up @@ -121,7 +130,7 @@ const downloadGoogleFonts: FontLoader = async ({
sizeAdjust: `${sizeAdjust}%`,
}
} catch {
console.error(
Log.error(
`Failed to find font override values for font \`${fontFamily}\``
)
}
Expand Down
2 changes: 1 addition & 1 deletion packages/font/src/local/utils.ts
Expand Up @@ -60,7 +60,7 @@ export function validateData(functionName: string, data: any): FontOptions {
throw new Error(`Unexpected file \`${src}\``)
}

const family = /.+\/(.+?)\./.exec(src)![1]
const family = /(.*\/)?(.+?)\.(woff|woff2|eot|ttf|otf)$/.exec(src)![2]

if (Array.isArray(declarations)) {
declarations.forEach((declaration) => {
Expand Down
22 changes: 21 additions & 1 deletion packages/next/build/webpack/loaders/next-font-loader/index.ts
@@ -1,10 +1,12 @@
import type { FontLoader } from '../../../../font'

import { promises as fs } from 'fs'
import path from 'path'
import loaderUtils from 'next/dist/compiled/loader-utils3'
import postcssFontLoaderPlugn from './postcss-font-loader'
import { promisify } from 'util'
import chalk from 'next/dist/compiled/chalk'
import { CONFIG_FILES } from '../../../../shared/lib/constants'

export default async function nextFontLoader(this: any) {
const fontLoaderSpan = this.currentTraceSpan.traceChild('next-font-loader')
Expand All @@ -17,6 +19,24 @@ export default async function nextFontLoader(this: any) {
postcss: getPostcss,
} = this.getOptions()

const nextConfigPaths = CONFIG_FILES.map((config) =>
path.join(this.rootContext, config)
)
// Add next.config.js as a dependency, loaders must rerun in case options changed
await Promise.all(
nextConfigPaths.map(async (configPath) => {
const hasConfig = await fs.access(configPath).then(
() => true,
() => false
)
if (hasConfig) {
this.addDependency(configPath)
} else {
this.addMissingDependency(configPath)
}
})
)

const emitFontFile = (content: Buffer, ext: string, preload: boolean) => {
const opts = { context: this.rootContext, content }
const interpolatedName = loaderUtils.interpolateName(
Expand Down Expand Up @@ -55,7 +75,7 @@ export default async function nextFontLoader(this: any) {
path.dirname(
path.join(this.rootContext, relativeFilePathFromRoot)
),
src
src.startsWith('.') ? src : `./${src}`
),
fs: this.fs,
})
Expand Down
2 changes: 1 addition & 1 deletion packages/next/lib/get-package-version.ts
Expand Up @@ -10,7 +10,7 @@ type PackageJsonDependencies = {

let cachedDeps: Promise<PackageJsonDependencies>

function getDependencies({
export function getDependencies({
cwd,
}: {
cwd: string
Expand Down
2 changes: 1 addition & 1 deletion packages/next/server/config-shared.ts
Expand Up @@ -162,7 +162,7 @@ export interface ExperimentalConfig {
// A list of packages that should always be transpiled and bundled in the server
transpilePackages?: string[]

fontLoaders?: [{ loader: string; options?: any }]
fontLoaders?: Array<{ loader: string; options?: any }>

webVitalsAttribution?: Array<typeof WEB_VITALS[number]>
}
Expand Down
46 changes: 45 additions & 1 deletion packages/next/server/config.ts
Expand Up @@ -24,6 +24,7 @@ import {
} from '../shared/lib/image-config'
import { loadEnvConfig } from '@next/env'
import { gte as semverGte } from 'next/dist/compiled/semver'
import { getDependencies } from '../lib/get-package-version'

export { DomainLocale, NextConfig, normalizeConfig } from './config-shared'

Expand Down Expand Up @@ -77,6 +78,46 @@ export function setHttpClientAndAgentOptions(options: NextConfig) {
;(global as any).__NEXT_HTTPS_AGENT = new HttpsAgent(options.httpAgentOptions)
}

async function setFontLoaderDefaults(config: NextConfigComplete, dir: string) {
if (config.experimental?.fontLoaders) return

// Add @next/font loaders by default if they're installed
const hasNextFontDependency = (
await getDependencies({
cwd: dir,
})
).dependencies['@next/font']

if (hasNextFontDependency) {
const googleFontLoader = {
loader: '@next/font/google',
}
const localFontLoader = {
loader: '@next/font/local',
}
if (!config.experimental) {
config.experimental = {}
}
if (!config.experimental.fontLoaders) {
config.experimental.fontLoaders = []
}
if (
!config.experimental.fontLoaders.find(
({ loader }: any) => loader === '@next/font/goggle'
)
) {
config.experimental.fontLoaders.push(googleFontLoader)
}
if (
!config.experimental.fontLoaders.find(
({ loader }: any) => loader === '@next/font/local'
)
) {
config.experimental.fontLoaders.push(localFontLoader)
}
}
}

function assignDefaults(dir: string, userConfig: { [key: string]: any }) {
const configFileName = userConfig.configFileName
if (typeof userConfig.exportTrailingSlash !== 'undefined') {
Expand Down Expand Up @@ -835,12 +876,14 @@ export default async function loadConfig(
: canonicalBase) || ''
}

return assignDefaults(dir, {
const completeConfig = assignDefaults(dir, {
configOrigin: relative(dir, path),
configFile: path,
configFileName,
...userConfig,
}) as NextConfigComplete
await setFontLoaderDefaults(completeConfig, dir)
return completeConfig
} else {
const configBaseName = basename(CONFIG_FILES[0], extname(CONFIG_FILES[0]))
const nonJsPath = findUp.sync(
Expand Down Expand Up @@ -869,5 +912,6 @@ export default async function loadConfig(
) as NextConfigComplete
completeConfig.configFileName = configFileName
setHttpClientAndAgentOptions(completeConfig)
await setFontLoaderDefaults(completeConfig, dir)
return completeConfig
}
6 changes: 3 additions & 3 deletions test/e2e/app-dir/next-font/fonts/index.js
@@ -1,16 +1,16 @@
import localFont from '@next/font/local'

export const font1 = localFont({ src: './font1.woff2', variable: '--font-1' })
export const font2 = localFont({ src: './font2.woff2', variable: '--font-2' })
export const font2 = localFont({ src: 'font2.woff2', variable: '--font-2' })
export const font3 = localFont({
src: './font3.woff2',
weight: '900',
style: 'italic',
})
export const font4 = localFont({ src: './font4.woff2', weight: '100' })
export const font5 = localFont({
src: './font5.woff2',
src: './test/font5.woff2',
style: 'italic',
preload: false,
})
export const font6 = localFont({ src: './font6.woff2' })
export const font6 = localFont({ src: 'test/font6.woff2' })
5 changes: 0 additions & 5 deletions test/e2e/app-dir/next-font/next.config.js
@@ -1,10 +1,5 @@
module.exports = {
experimental: {
appDir: true,
fontLoaders: [
{
loader: '@next/font/local',
},
],
},
}
3 changes: 0 additions & 3 deletions test/e2e/next-font/font-loader-in-document-error.test.ts
Expand Up @@ -17,9 +17,6 @@ describe('font-loader-in-document-error', () => {
next = await createNext({
files: {
pages: new FileRef(join(__dirname, 'font-loader-in-document/pages')),
'next.config.js': new FileRef(
join(__dirname, 'font-loader-in-document/next.config.js')
),
},
dependencies: {
'@next/font': 'canary',
Expand Down
10 changes: 0 additions & 10 deletions test/e2e/next-font/font-loader-in-document/next.config.js

This file was deleted.

15 changes: 0 additions & 15 deletions test/unit/google-font-loader.test.ts
Expand Up @@ -201,21 +201,6 @@ describe('@next/font/google loader', () => {
`)
})

test('Missing config with subsets', async () => {
await expect(
loader({
functionName: 'Inter',
data: [],
config: undefined,
emitFontFile: jest.fn(),
resolve: jest.fn(),
fs: {} as any,
})
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Please specify subsets for \`@next/font/google\` in your \`next.config.js\`"`
)
})

test('Missing function name', async () => {
await expect(
loader({
Expand Down