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

Fix @next/font imports from outside of the root directory #42678

Merged
merged 10 commits into from Nov 9, 2022
Merged
Show file tree
Hide file tree
Changes from 7 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
4 changes: 2 additions & 2 deletions errors/babel-font-loader-conflict.md
@@ -1,8 +1,8 @@
# Babel and Font loader conflict
# Babel and `@next/font` conflict

#### Why This Error Occurred

You have tried to use `experimental.fontLoaders` 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 `experimental.fontLoaders`.
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
5 changes: 5 additions & 0 deletions packages/next/build/babel/loader/get-config.ts
Expand Up @@ -121,6 +121,10 @@ function getPlugins(
{ type: 'plugin' }
)
: null
const nextFontUnsupported = createConfigItem(
[require('../plugins/next-font-unsupported')],
{ type: 'plugin' }
)

return [
reactRefreshItem,
Expand All @@ -130,6 +134,7 @@ function getPlugins(
transformDefineItem,
nextSsgItem,
commonJsItem,
nextFontUnsupported,
].filter(Boolean)
}

Expand Down
23 changes: 23 additions & 0 deletions packages/next/build/babel/plugins/next-font-unsupported.ts
@@ -0,0 +1,23 @@
import { NodePath, PluginObj, types } from 'next/dist/compiled/babel/core'

export default function NextPageDisallowReExportAllExports(): PluginObj<any> {
return {
visitor: {
ImportDeclaration(path: NodePath<types.ImportDeclaration>) {
if (
['@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`
)
;(err as any).code = 'BABEL_PARSE_ERROR'
;(err as any).loc =
path.node.loc?.start ?? path.node.loc?.end ?? path.node.loc
throw err
}
},
},
}
}
10 changes: 0 additions & 10 deletions packages/next/build/webpack-config.ts
Expand Up @@ -662,16 +662,6 @@ export default async function getBaseWebpackConfig(
loggedIgnoredCompilerOptions = true
}

if (!useSWCLoader && babelConfigFile && config.experimental.fontLoaders) {
Log.error(
`"experimental.fontLoaders" is enabled which requires SWC although Babel is being used due to custom babel config being present "${path.relative(
dir,
babelConfigFile
)}".\nSee more info here: https://nextjs.org/docs/messages/babel-font-loader-conflict`
)
process.exit(1)
}

const getBabelLoader = () => {
return {
loader: require.resolve('./babel/loader/index'),
Expand Down
27 changes: 0 additions & 27 deletions packages/next/build/webpack/config/blocks/css/index.ts
Expand Up @@ -11,7 +11,6 @@ import {
getGlobalModuleImportError,
getLocalModuleImportError,
getFontLoaderDocumentImportError,
getFontLoaderImportError,
} from './messages'
import { getPostCssPlugins } from './plugins'

Expand All @@ -25,8 +24,6 @@ const regexCssModules = /\.module\.css$/
// RegExps for Syntactically Awesome Style Sheets
const regexSassGlobal = /(?<!\.module)\.(scss|sass)$/
const regexSassModules = /\.module\.(scss|sass)$/
// Also match the virtual client entry which doesn't have file path
const regexClientEntry = /^$/

/**
* Mark a rule as removable if built-in CSS support is disabled
Expand Down Expand Up @@ -218,35 +215,11 @@ export const css = curry(async function css(
markRemovable({
sideEffects: false,
test: fontLoaderPath,
issuer: {
and: [
{
or: [ctx.rootDirectory, regexClientEntry],
},
],
not: [/node_modules/],
},
use: getFontLoader(ctx, lazyPostCSSInitializer, fontLoaderOptions),
}),
],
})
)

fns.push(
loader({
oneOf: [
markRemovable({
test: fontLoaderPath,
use: {
loader: 'error-loader',
options: {
reason: getFontLoaderImportError(),
},
},
}),
],
})
)
})

// CSS cannot be imported in _document. This comes before everything because
Expand Down
6 changes: 0 additions & 6 deletions packages/next/build/webpack/config/blocks/css/messages.ts
Expand Up @@ -37,9 +37,3 @@ export function getFontLoaderDocumentImportError() {
'cannot'
)} be used within ${chalk.cyan('pages/_document.js')}.`
}

export function getFontLoaderImportError() {
return `Font loader error:\nFont loaders ${chalk.bold(
'cannot'
)} be used from within ${chalk.bold('node_modules')}.`
}
25 changes: 13 additions & 12 deletions packages/next/server/config.ts
Expand Up @@ -24,7 +24,6 @@ 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 @@ -93,15 +92,17 @@ export function setHttpClientAndAgentOptions(config: {
)
}

async function setFontLoaderDefaults(config: NextConfigComplete, dir: string) {
// Add @next/font loaders by default if they're installed
const hasNextFontDependency = (
await getDependencies({
cwd: dir,
})
).dependencies['@next/font']
function setFontLoaderDefaults(config: NextConfigComplete) {
try {
// eslint-disable-next-line import/no-extraneous-dependencies
const nextFontVersion = require('@next/font/package.json').version
const nextVersion = require('next/package.json').version
if (nextFontVersion !== nextVersion) {
Log.warn(
`Different versions of @next/font (${nextFontVersion}) and next (${nextVersion}) detected. This may lead to unexpected behavior.`
)
}

if (hasNextFontDependency) {
const googleFontLoader = {
loader: '@next/font/google',
}
Expand All @@ -128,7 +129,7 @@ async function setFontLoaderDefaults(config: NextConfigComplete, dir: string) {
) {
config.experimental.fontLoaders.push(localFontLoader)
}
}
} catch {}
}

function assignDefaults(dir: string, userConfig: { [key: string]: any }) {
Expand Down Expand Up @@ -906,7 +907,7 @@ export default async function loadConfig(
configFileName,
...userConfig,
}) as NextConfigComplete
await setFontLoaderDefaults(completeConfig, dir)
setFontLoaderDefaults(completeConfig)
return completeConfig
} else {
const configBaseName = basename(CONFIG_FILES[0], extname(CONFIG_FILES[0]))
Expand Down Expand Up @@ -936,6 +937,6 @@ export default async function loadConfig(
) as NextConfigComplete
completeConfig.configFileName = configFileName
setHttpClientAndAgentOptions(completeConfig)
await setFontLoaderDefaults(completeConfig, dir)
setFontLoaderDefaults(completeConfig)
return completeConfig
}
11 changes: 11 additions & 0 deletions test/e2e/app-dir/rsc-external.test.ts
Expand Up @@ -30,6 +30,7 @@ describe('app dir - rsc external dependency', () => {
next = await createNext({
files: new FileRef(path.join(__dirname, './rsc-external')),
dependencies: {
'@next/font': 'canary',
react: 'latest',
'react-dom': 'latest',
swr: '2.0.0-rc.0',
Expand Down Expand Up @@ -155,4 +156,14 @@ describe('app dir - rsc external dependency', () => {
)
).toBe('rgb(255, 0, 0)')
})

it('should handle external @next/font', async () => {
const browser = await webdriver(next.url, '/font')

expect(
await browser.eval(
`window.getComputedStyle(document.querySelector('p')).fontFamily`
)
).toMatch(/^__myFont_.{6}, __myFont_Fallback_.{6}$/)
})
})
5 changes: 5 additions & 0 deletions test/e2e/app-dir/rsc-external/app/font/page.js
@@ -0,0 +1,5 @@
import { myFont } from 'font'

export default function Page() {
return <p className={myFont.className}>Hello world</p>
}
2 changes: 1 addition & 1 deletion test/e2e/app-dir/rsc-external/next.config.js
Expand Up @@ -3,6 +3,6 @@ module.exports = {
experimental: {
appDir: true,
serverComponentsExternalPackages: ['conditional-exports-optout'],
transpilePackages: ['untranspiled-module', 'css'],
transpilePackages: ['untranspiled-module', 'css', 'font'],
},
}
@@ -0,0 +1,3 @@
import localFont from '@next/font/local'

export const myFont = localFont({ src: './my-font.woff2' })
Binary file not shown.
@@ -0,0 +1,4 @@
{
"name": "font",
"main": "index.ts"
}
10 changes: 0 additions & 10 deletions test/e2e/next-font/babel/next.config.js

This file was deleted.

Expand Up @@ -2,19 +2,13 @@ import { createNext, FileRef } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { join } from 'path'

describe('@next/font/google babel', () => {
const isNextStart = (global as any).isNextStart
describe('@next/fon babel unsupported', () => {
let next: NextInstance

if (!isNextStart) {
it('should only run on next start', () => {})
return
}

beforeAll(async () => {
next = await createNext({
skipStart: true,
files: new FileRef(join(__dirname, 'babel')),
files: new FileRef(join(__dirname, 'babel-unsupported')),
dependencies: {
'@next/font': 'canary',
},
Expand All @@ -27,7 +21,7 @@ describe('@next/font/google babel', () => {
'next build failed with code/signal 1'
)
expect(next.cliOutput).toMatch(
/"experimental.fontLoaders" is enabled which requires SWC although Babel is being used due to custom babel config being present ".babelrc"./
/"@next\/font" requires SWC although Babel is being used due to a custom babel config being present./
)
})
})
@@ -1,3 +1,7 @@
import { Inter } from '@next/font/google'

console.log(Inter)

export default function Page() {
return <p>Hello world</p>
}