/
index.ts
133 lines (123 loc) · 3.86 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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')
return fontLoaderSpan.traceAsyncFn(async () => {
const callback = this.async()
const {
isDev,
isServer,
assetPrefix,
fontLoaderOptions,
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(
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 font loader call into JSON
const {
path: relativeFilePathFromRoot,
import: functionName,
arguments: data,
variableName,
} = JSON.parse(this.resourceQuery.slice(1))
try {
const fontLoader: FontLoader = require(path.join(
this.resourcePath,
'../loader.js'
)).default
let { css, fallbackFonts, adjustFontFallback, weight, style, variable } =
await fontLoader({
functionName,
variableName,
data,
config: fontLoaderOptions,
emitFontFile,
resolve: (src: string) =>
promisify(this.resolve)(
path.dirname(
path.join(this.rootContext, relativeFilePathFromRoot)
),
src.startsWith('.') ? src : `./${src}`
),
isDev,
isServer,
loaderContext: this,
})
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)
}
})
}