Skip to content

Commit

Permalink
App font loader tree shaking (vercel#41384)
Browse files Browse the repository at this point in the history
Fixes tree shaking for font loaders in server components.

## 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)
  • Loading branch information
Hannes Bornö authored and Kikobeats committed Oct 24, 2022
1 parent 0bf74e8 commit aec2b69
Show file tree
Hide file tree
Showing 20 changed files with 150 additions and 189 deletions.
2 changes: 0 additions & 2 deletions packages/next/build/webpack-config.ts
Expand Up @@ -1967,12 +1967,10 @@ export default async function getBaseWebpackConfig(
(isClient
? new FlightManifestPlugin({
dev,
fontLoaderTargets,
})
: new FlightClientEntryPlugin({
dev,
isEdgeServer,
fontLoaderTargets,
})),
!dev &&
isClient &&
Expand Down
@@ -1,5 +1,6 @@
import { RSC_MODULE_TYPES } from '../../../shared/lib/constants'
import { getModuleBuildInfo } from './get-module-build-info'
import { regexCSS } from './utils'

export type ClientComponentImports = string[]
export type CssImports = Record<string, string[]>
Expand All @@ -22,7 +23,7 @@ export default async function transformSource(this: any): Promise<string> {
const requests = modules as string[]
const code = requests
// Filter out css files on the server
.filter((request) => (isServer ? !request.endsWith('.css') : true))
.filter((request) => (isServer ? !regexCSS.test(request) : true))
.map((request) =>
request.endsWith('.css')
? `(() => import(/* webpackMode: "lazy" */ ${JSON.stringify(request)}))`
Expand Down
3 changes: 3 additions & 0 deletions packages/next/build/webpack/loaders/utils.ts
Expand Up @@ -10,3 +10,6 @@ export function isClientComponentModule(mod: {
const hasClientDirective = mod.buildInfo.rsc?.type === RSC_MODULE_TYPES.client
return hasClientDirective || imageRegex.test(mod.resource)
}

// TODO-APP: ensure .scss / .sass also works.
export const regexCSS = /\.css(\?.*)?$/
21 changes: 5 additions & 16 deletions packages/next/build/webpack/plugins/flight-client-entry-plugin.ts
Expand Up @@ -19,12 +19,11 @@ import {
} from '../../../shared/lib/constants'
import { FlightCSSManifest, traverseModules } from './flight-manifest-plugin'
import { ASYNC_CLIENT_MODULES } from './flight-manifest-plugin'
import { isClientComponentModule } from '../loaders/utils'
import { isClientComponentModule, regexCSS } from '../loaders/utils'

interface Options {
dev: boolean
isEdgeServer: boolean
fontLoaderTargets?: string[]
}

const PLUGIN_NAME = 'ClientEntryPlugin'
Expand All @@ -34,21 +33,16 @@ export const injectedClientEntries = new Map()
export const serverModuleIds = new Map<string, string | number>()
export const edgeServerModuleIds = new Map<string, string | number>()

// TODO-APP: ensure .scss / .sass also works.
const regexCSS = /\.css$/

// TODO-APP: move CSS manifest generation to the flight manifest plugin.
const flightCSSManifest: FlightCSSManifest = {}

export class FlightClientEntryPlugin {
dev: boolean
isEdgeServer: boolean
fontLoaderTargets?: string[]

constructor(options: Options) {
this.dev = options.dev
this.isEdgeServer = options.isEdgeServer
this.fontLoaderTargets = options.fontLoaderTargets
}

apply(compiler: webpack.Compiler) {
Expand Down Expand Up @@ -376,18 +370,14 @@ export class FlightClientEntryPlugin {
// Request could be undefined or ''
if (!rawRequest) return

const isFontLoader = this.fontLoaderTargets?.some((fontLoaderTarget) =>
mod.userRequest.startsWith(`${fontLoaderTarget}?`)
)
const isCSS = regexCSS.test(rawRequest)
const modRequest: string | undefined =
!rawRequest.endsWith('.css') &&
!isCSS &&
!rawRequest.startsWith('.') &&
!rawRequest.startsWith('/') &&
!rawRequest.startsWith(APP_DIR_ALIAS)
? isFontLoader
? mod.userRequest
: rawRequest
: mod.resourceResolveData?.path
? rawRequest
: mod.resourceResolveData?.path + mod.resourceResolveData?.query

// Ensure module is not walked again if it's already been visited
if (!visitedBySegment[layoutOrPageRequest]) {
Expand All @@ -401,7 +391,6 @@ export class FlightClientEntryPlugin {
}
visitedBySegment[layoutOrPageRequest].add(modRequest)

const isCSS = isFontLoader || regexCSS.test(modRequest)
const isClientComponent = isClientComponentModule(mod)

if (isCSS) {
Expand Down
12 changes: 2 additions & 10 deletions packages/next/build/webpack/plugins/flight-manifest-plugin.ts
Expand Up @@ -8,7 +8,7 @@
import { webpack, sources } from 'next/dist/compiled/webpack/webpack'
import { FLIGHT_MANIFEST } from '../../../shared/lib/constants'
import { relative } from 'path'
import { isClientComponentModule } from '../loaders/utils'
import { isClientComponentModule, regexCSS } from '../loaders/utils'

import {
edgeServerModuleIds,
Expand All @@ -24,7 +24,6 @@ import {

interface Options {
dev: boolean
fontLoaderTargets?: string[]
}

/**
Expand Down Expand Up @@ -111,11 +110,9 @@ export function traverseModules(

export class FlightManifestPlugin {
dev: Options['dev'] = false
fontLoaderTargets?: Options['fontLoaderTargets']

constructor(options: Options) {
this.dev = options.dev
this.fontLoaderTargets = options.fontLoaderTargets
}

apply(compiler: webpack.Compiler) {
Expand Down Expand Up @@ -156,7 +153,6 @@ export class FlightManifestPlugin {
__edge_ssr_module_mapping__: {},
}
const dev = this.dev
const fontLoaderTargets = this.fontLoaderTargets

const clientRequestsSet = new Set()

Expand All @@ -181,12 +177,8 @@ export class FlightManifestPlugin {
id: ModuleId,
mod: webpack.NormalModule
) {
const isFontLoader = fontLoaderTargets?.some((fontLoaderTarget) =>
mod.resource?.startsWith(`${fontLoaderTarget}?`)
)
const isCSSModule =
isFontLoader ||
mod.resource?.endsWith('.css') ||
regexCSS.test(mod.resource) ||
mod.type === 'css/mini-extract' ||
(!!mod.loaders &&
(dev
Expand Down
5 changes: 2 additions & 3 deletions packages/next/server/app-render.tsx
Expand Up @@ -582,9 +582,8 @@ function getPreloadedFontFilesInlineLinkTags(
const fontFiles = new Set<string>()

for (const css of layoutOrPageCss) {
// We only include the CSS if it's a global CSS, or it is used by this
// entrypoint.
if (serverCSSForEntries.includes(css) || !/\.module\.css/.test(css)) {
// We only include the CSS if it is used by this entrypoint.
if (serverCSSForEntries.includes(css)) {
const preloadedFontFiles = fontLoaderManifest.app[css]
if (preloadedFontFiles) {
for (const fontFile of preloadedFontFiles) {
Expand Down

0 comments on commit aec2b69

Please sign in to comment.