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 next/font import #45707

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions errors/babel-font-loader-conflict.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Babel and `@next/font` conflict
# Babel and `next/font` conflict

#### Why This Error Occurred

You have tried to use `@next/font` with a custom babel config. When your application has a custom babel config you opt-out of the Next.js Compiler which is required to use `@next/font`.
You have tried to use `next/font` with a custom babel config. When your application has a custom babel config you opt-out of the Next.js Compiler which is required to use `next/font`.

#### Possible Ways to Fix It

Expand Down
2 changes: 1 addition & 1 deletion packages/font/src/google/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function validateData(
const subsets = callSubsets ?? config?.subsets ?? []

if (functionName === '') {
nextFontError(`@next/font/google has no default export`)
nextFontError(`next/font/google has no default export`)
}

const fontFamily = functionName.replace(/_/g, ' ')
Expand Down
2 changes: 1 addition & 1 deletion packages/font/src/local/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type FontOptions = {
}
export function validateData(functionName: string, fontData: any): FontOptions {
if (functionName) {
nextFontError(`@next/font/local has no named exports`)
nextFontError(`next/font/local has no named exports`)
}
let {
src,
Expand Down
1 change: 1 addition & 0 deletions packages/next/font/google/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from '@next/font/dist/google'
3 changes: 0 additions & 3 deletions packages/next/font/google/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +0,0 @@
throw new Error(
'You tried to import `next/font/google`, did you mean `@next/font/google`?\nRead more: https://nextjs.org/docs/basic-features/font-optimization'
)
1 change: 1 addition & 0 deletions packages/next/font/google/loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@next/font/dist/google/loader')
1 change: 1 addition & 0 deletions packages/next/font/google/target.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* target file for webpack loader */
3 changes: 0 additions & 3 deletions packages/next/font/index.js

This file was deleted.

1 change: 1 addition & 0 deletions packages/next/font/local/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@next/font/dist/local'
3 changes: 0 additions & 3 deletions packages/next/font/local/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +0,0 @@
throw new Error(
'You tried to import `next/font/local`, did you mean `@next/font/local`?\nRead more: https://nextjs.org/docs/basic-features/font-optimization'
)
1 change: 1 addition & 0 deletions packages/next/font/local/loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@next/font/dist/local/loader')
1 change: 1 addition & 0 deletions packages/next/font/local/target.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* target file for webpack loader */
1 change: 1 addition & 0 deletions packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
]
},
"dependencies": {
"@next/font": "13.1.7-canary.8",
"@next/env": "13.1.7-canary.8",
"@swc/helpers": "0.4.14",
"caniuse-lite": "^1.0.30001406",
Expand Down
11 changes: 7 additions & 4 deletions packages/next/src/build/babel/plugins/next-font-unsupported.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ export default function NextPageDisallowReExportAllExports(): PluginObj<any> {
visitor: {
ImportDeclaration(path: NodePath<types.ImportDeclaration>) {
if (
['@next/font/local', '@next/font/google'].includes(
path.node.source.value
)
[
'@next/font/local',
'@next/font/google',
'next/font/local',
'next/font/google',
].includes(path.node.source.value)
) {
const err = new SyntaxError(
`"@next/font" requires SWC although Babel is being used due to a custom babel config being present.\nRead more: https://nextjs.org/docs/messages/babel-font-loader-conflict`
`"next/font" requires SWC although Babel is being used due to a custom babel config being present.\nRead more: https://nextjs.org/docs/messages/babel-font-loader-conflict`
)
;(err as any).code = 'BABEL_PARSE_ERROR'
;(err as any).loc =
Expand Down
1 change: 0 additions & 1 deletion packages/next/src/build/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,6 @@ export function getEdgeServerEntry(opts: {
pagesType: opts.pagesType,
appDirLoader: Buffer.from(opts.appDirLoader || '').toString('base64'),
sriEnabled: !opts.isDev && !!opts.config.experimental.sri?.algorithm,
hasFontLoaders: !!opts.config.experimental.fontLoaders,
}

return {
Expand Down
8 changes: 2 additions & 6 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -912,12 +912,8 @@ export default async function build(
: null,
BUILD_ID_FILE,
appDir ? path.join(SERVER_DIRECTORY, APP_PATHS_MANIFEST) : null,
...(config.experimental.fontLoaders
? [
path.join(SERVER_DIRECTORY, FONT_LOADER_MANIFEST + '.js'),
path.join(SERVER_DIRECTORY, FONT_LOADER_MANIFEST + '.json'),
]
: []),
path.join(SERVER_DIRECTORY, FONT_LOADER_MANIFEST + '.js'),
path.join(SERVER_DIRECTORY, FONT_LOADER_MANIFEST + '.json'),
]
.filter(nonNullable)
.map((file) => path.join(config.distDir, file)),
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/build/jest/jest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ export default function nextJest(options: { dir?: string } = {}) {

// Handle @next/font
'@next/font/(.*)': require.resolve('./__mocks__/nextFontMock.js'),
// Handle next/font
'next/font/(.*)': require.resolve('./__mocks__/nextFontMock.js'),

// custom config comes last to ensure the above rules are matched,
// fixes the case where @pages/(.*) -> src/pages/$! doesn't break
Expand Down
16 changes: 9 additions & 7 deletions packages/next/src/build/swc/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,14 +250,16 @@ any) {
hasServerComponents,
isServerLayer,
})
baseOptions.fontLoaders = {
fontLoaders: [
'next/font/local',
'next/font/google',

if (nextConfig?.experimental?.fontLoaders && relativeFilePathFromRoot) {
baseOptions.fontLoaders = {
fontLoaders: nextConfig.experimental.fontLoaders.map(
({ loader }: any) => loader
),
relativeFilePathFromRoot,
}
// TODO: remove this in the next major version
'@next/font/local',
'@next/font/google',
],
relativeFilePathFromRoot,
}

const isNextDist = nextDistPath.test(filename)
Expand Down
10 changes: 0 additions & 10 deletions packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1364,13 +1364,6 @@ export default async function getBaseWebpackConfig(
},
}

const fontLoaderTargets =
config.experimental.fontLoaders &&
config.experimental.fontLoaders.map(({ loader }) => {
const resolved = require.resolve(loader)
return path.join(resolved, '../target.css')
})

let webpackConfig: webpack.Configuration = {
parallelism: Number(process.env.NEXT_WEBPACK_PARALLELISM) || undefined,
...(isNodeServer ? { externalsPresets: { node: true } } : {}),
Expand Down Expand Up @@ -2093,7 +2086,6 @@ export default async function getBaseWebpackConfig(
new MiddlewarePlugin({
dev,
sriEnabled: !dev && !!config.experimental.sri?.algorithm,
hasFontLoaders: !!config.experimental.fontLoaders,
}),
isClient &&
new BuildManifestPlugin({
Expand Down Expand Up @@ -2158,10 +2150,8 @@ export default async function getBaseWebpackConfig(
!!config.experimental.sri?.algorithm &&
new SubresourceIntegrityPlugin(config.experimental.sri.algorithm),
isClient &&
fontLoaderTargets &&
new FontLoaderManifestPlugin({
appDirEnabled: !!config.experimental.appDir,
fontLoaderTargets,
}),
!dev &&
isClient &&
Expand Down
67 changes: 41 additions & 26 deletions packages/next/src/build/webpack/config/blocks/css/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import path from 'path'
import curry from 'next/dist/compiled/lodash.curry'
import { webpack } from 'next/dist/compiled/webpack/webpack'
import { loader, plugin } from '../../helpers'
Expand Down Expand Up @@ -181,32 +180,48 @@ export const css = curry(async function css(

const fns: ConfigurationFn[] = []

// Resolve the configured font loaders, the resolved files are noop files that next-font-loader will match
let fontLoaders: [string, string][] | undefined = ctx.experimental.fontLoaders
? ctx.experimental.fontLoaders.map(({ loader: fontLoader, options }) => [
path.join(require.resolve(fontLoader), '../target.css'),
options,
])
: undefined
const googleLoaderOptions =
ctx.experimental?.fontLoaders?.find(
(loaderConfig) => loaderConfig.loader === '@next/font/google'
hanneslund marked this conversation as resolved.
Show resolved Hide resolved
)?.options ?? {}
const fontLoaders: Array<[string | RegExp, string, any?]> = [
[
require.resolve('next/font/google/target.css'),
'@next/font/google/loader',
googleLoaderOptions,
],
[require.resolve('next/font/local/target.css'), '@next/font/local/loader'],

// TODO: remove this in the next major version
[
/node_modules\/@next\/font\/google\/target.css/,
'@next/font/google/loader',
googleLoaderOptions,
],
[/node_modules\/@next\/font\/local\/target.css/, '@next/font/local/loader'],
]

fontLoaders?.forEach(([fontLoaderPath, fontLoaderOptions]) => {
// Matches the resolved font loaders noop files to run next-font-loader
fns.push(
loader({
oneOf: [
markRemovable({
sideEffects: false,
test: fontLoaderPath,
use: getNextFontLoader(
ctx,
lazyPostCSSInitializer,
fontLoaderOptions
),
}),
],
})
)
})
fontLoaders.forEach(
([fontLoaderTarget, fontLoaderPath, fontLoaderOptions]) => {
// Matches the resolved font loaders noop files to run next-font-loader
fns.push(
loader({
oneOf: [
markRemovable({
sideEffects: false,
test: fontLoaderTarget,
use: getNextFontLoader(
ctx,
lazyPostCSSInitializer,
fontLoaderPath,
fontLoaderOptions
),
}),
],
})
)
}
)

// CSS cannot be imported in _document. This comes before everything because
// global CSS nor CSS modules work in said file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { cssFileResolve } from './file-resolve'
export function getNextFontLoader(
ctx: ConfigurationContext,
postcss: any,
fontLoaderPath: string,
fontLoaderOptions: any
): webpack.RuleSetUseItem[] {
const loaders: webpack.RuleSetUseItem[] = []
Expand Down Expand Up @@ -62,6 +63,7 @@ export function getNextFontLoader(
isDev: ctx.isDevelopment,
isServer: ctx.isServer,
assetPrefix: ctx.assetPrefix,
fontLoaderPath,
fontLoaderOptions,
postcss,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export type EdgeSSRLoaderQuery = {
appDirLoader?: string
pagesType: 'app' | 'pages' | 'root'
sriEnabled: boolean
hasFontLoaders: boolean
}

/*
Expand Down Expand Up @@ -45,7 +44,6 @@ export default async function edgeSSRLoader(this: any) {
appDirLoader: appDirLoaderBase64,
pagesType,
sriEnabled,
hasFontLoaders,
} = this.getOptions()

const appDirLoader = Buffer.from(
Expand Down Expand Up @@ -127,9 +125,7 @@ export default async function edgeSSRLoader(this: any) {
const subresourceIntegrityManifest = ${
sriEnabled ? 'self.__SUBRESOURCE_INTEGRITY_MANIFEST' : 'undefined'
}
const fontLoaderManifest = ${
hasFontLoaders ? 'self.__FONT_LOADER_MANIFEST' : 'undefined'
}
const fontLoaderManifest = self.__FONT_LOADER_MANIFEST

const render = getRender({
pageType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default async function nextFontLoader(this: any) {
isDev,
isServer,
assetPrefix,
fontLoaderPath,
fontLoaderOptions,
postcss: getPostcss,
} = this.getOptions()
Expand Down Expand Up @@ -82,10 +83,7 @@ export default async function nextFontLoader(this: any) {
}

try {
const fontLoader: FontLoader = require(path.join(
this.resourcePath,
'../loader.js'
)).default
const fontLoader: FontLoader = require(fontLoaderPath).default
let { css, fallbackFonts, adjustFontFallback, weight, style, variable } =
await fontLoader({
functionName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,20 @@ export type FontLoaderManifest = {
}
const PLUGIN_NAME = 'FontLoaderManifestPlugin'

const fontLoaderTargets = [
require.resolve('next/font/google/target.css'),
require.resolve('next/font/local/target.css'),
// TODO: remove this in the next major version
/node_modules\/@next\/font\/google\/target\.css\?{.+}$/,
/node_modules\/@next\/font\/local\/target\.css\?{.+}$/,
]

// Creates a manifest of all fonts that should be preloaded given a route
export class FontLoaderManifestPlugin {
private appDirEnabled: boolean
private fontLoaderTargets: string[]

constructor(options: {
appDirEnabled: boolean
fontLoaderTargets: string[]
}) {
constructor(options: { appDirEnabled: boolean }) {
this.appDirEnabled = options.appDirEnabled
this.fontLoaderTargets = options.fontLoaderTargets
}

apply(compiler: webpack.Compiler) {
Expand All @@ -36,8 +39,10 @@ export class FontLoaderManifestPlugin {
compilation.hooks.finishModules.tap(PLUGIN_NAME, (modules) => {
const modulesArr = Array.from(modules)
fontLoaderModules = modulesArr.filter((mod: any) =>
this.fontLoaderTargets.some((fontLoaderTarget) =>
mod.userRequest?.startsWith(`${fontLoaderTarget}?`)
fontLoaderTargets.some((fontLoaderTarget) =>
typeof fontLoaderTarget === 'string'
? mod.userRequest?.startsWith(`${fontLoaderTarget}?`)
: fontLoaderTarget.test(mod.userRequest)
)
)
})
Expand Down
Loading