rsnext/packages/next/build/webpack/loaders/next-font-loader/index.ts
Hannes Bornö 299f392d7b
Add support for fallback font and css variable for font/local (#40990)
Adds support for generating a fallback font by providing font override
metrics for the given local font. Also adds support for providing a CSS
variable name that then can be accessed through the `.variable` export,
it contains the hashed font family name.

## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have a helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing
doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)

Co-authored-by: JJ Kasper <jj@jjsweb.site>
2022-09-28 12:19:52 -07:00

107 lines
3.2 KiB
TypeScript

import type { FontLoader } from '../../../../font'
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'
export default async function nextFontLoader(this: any) {
const fontLoaderSpan = this.currentTraceSpan.traceChild('next-font-loader')
return fontLoaderSpan.traceAsyncFn(async () => {
const callback = this.async()
const {
isServer,
assetPrefix,
fontLoaderOptions,
postcss: getPostcss,
} = this.getOptions()
const emitFontFile = (content: Buffer, ext: string, preload: boolean) => {
const opts = { context: this.rootContext, content }
const interpolatedName = loaderUtils.interpolateName(
this,
// Font files ending with .p.(woff|woff2|eot|ttf|otf) are preloaded
`static/media/[hash]${preload ? '.p' : ''}.${ext}`,
opts
)
const outputPath = `${assetPrefix}/_next/${interpolatedName}`
if (!isServer) {
this.emitFile(interpolatedName, content, null)
}
return outputPath
}
// next-swc next_font_loaders turns each function call argument into JSON seperated by semicolons
let [relativeFilePathFromRoot, functionName, ...data] = this.resourceQuery
.slice(1)
.split(';')
data = data.map((value: string) => JSON.parse(value))
try {
const fontLoader: FontLoader = require(path.join(
this.resourcePath,
'../loader.js'
)).default
let { css, fallbackFonts, adjustFontFallback, weight, style, variable } =
await fontLoader({
functionName,
data,
config: fontLoaderOptions,
emitFontFile,
resolve: (src: string) =>
promisify(this.resolve)(
path.dirname(
path.join(this.rootContext, relativeFilePathFromRoot)
),
src
),
fs: this.fs,
})
const { postcss } = await getPostcss()
// Exports will be exported as is from css-loader instead of a CSS module export
const exports: { name: any; value: any }[] = []
const fontFamilyHash = loaderUtils.getHashDigest(
Buffer.from(css),
'md5',
'hex',
6
)
// Add CSS classes, exports and make the font-family localy scoped by turning it unguessable
const result = await postcss(
postcssFontLoaderPlugn({
exports,
fontFamilyHash,
fallbackFonts,
weight,
style,
adjustFontFallback,
variable,
})
).process(css, {
from: undefined,
})
// Reuse ast in css-loader
const ast = {
type: 'postcss',
version: result.processor.version,
root: result.root,
}
callback(null, result.css, null, {
exports,
ast,
fontFamilyHash,
})
} catch (err: any) {
err.stack = false
err.message = `Font loader error:\n${err.message}`
err.message += `
${chalk.cyan(`Location: ${relativeFilePathFromRoot}`)}`
callback(err)
}
})
}