Transpile geist by default (#67402)
### What Transpile `geist` package by default. Currently users need to add `geist` into `transpilePackages` to make it work with pages router as it's external packages but require transform. This PR will resolve that issue. cc @JohnPhamous ### Why geist package is using `next/font` which requires to work with SWC transform. But so far only ESM syntax can be captured by SWC since CJS is pretty difficult to cover as the package import is not statically analyzable. We introduced a new list that it could be transpiled and include geist package into bundle. Add it in both `next/jest` side and webpack/turbopack bundling side --------- Co-authored-by: 강동윤 (Donny) <kdy1997.dev@gmail.com>
This commit is contained in:
parent
49f9c46fb1
commit
b369ccfcf4
9 changed files with 98 additions and 34 deletions
|
@ -72,7 +72,10 @@ use crate::{
|
||||||
get_decorators_transform_options, get_jsx_transform_options,
|
get_decorators_transform_options, get_jsx_transform_options,
|
||||||
get_typescript_transform_options,
|
get_typescript_transform_options,
|
||||||
},
|
},
|
||||||
util::{foreign_code_context_condition, load_next_js_templateon, NextRuntime},
|
util::{
|
||||||
|
foreign_code_context_condition, get_transpiled_packages, load_next_js_templateon,
|
||||||
|
NextRuntime,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[turbo_tasks::value(serialization = "auto_for_input")]
|
#[turbo_tasks::value(serialization = "auto_for_input")]
|
||||||
|
@ -140,13 +143,6 @@ pub async fn get_server_resolve_options_context(
|
||||||
let invalid_styled_jsx_client_only_resolve_plugin =
|
let invalid_styled_jsx_client_only_resolve_plugin =
|
||||||
get_invalid_styled_jsx_resolve_plugin(project_path);
|
get_invalid_styled_jsx_resolve_plugin(project_path);
|
||||||
|
|
||||||
let mut transpile_packages = next_config.transpile_packages().await?.clone_value();
|
|
||||||
transpile_packages.extend(
|
|
||||||
(*next_config.optimize_package_imports().await?)
|
|
||||||
.iter()
|
|
||||||
.cloned(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Always load these predefined packages as external.
|
// Always load these predefined packages as external.
|
||||||
let mut external_packages: Vec<RcStr> = load_next_js_templateon(
|
let mut external_packages: Vec<RcStr> = load_next_js_templateon(
|
||||||
project_path,
|
project_path,
|
||||||
|
@ -154,9 +150,19 @@ pub async fn get_server_resolve_options_context(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let mut transpiled_packages = get_transpiled_packages(next_config, project_path)
|
||||||
|
.await?
|
||||||
|
.clone_value();
|
||||||
|
|
||||||
|
transpiled_packages.extend(
|
||||||
|
(*next_config.optimize_package_imports().await?)
|
||||||
|
.iter()
|
||||||
|
.cloned(),
|
||||||
|
);
|
||||||
|
|
||||||
let server_external_packages = &*next_config.server_external_packages().await?;
|
let server_external_packages = &*next_config.server_external_packages().await?;
|
||||||
|
|
||||||
let conflicting_packages = transpile_packages
|
let conflicting_packages = transpiled_packages
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|package| server_external_packages.contains(package))
|
.filter(|package| server_external_packages.contains(package))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -172,7 +178,7 @@ pub async fn get_server_resolve_options_context(
|
||||||
// Add the config's own list of external packages.
|
// Add the config's own list of external packages.
|
||||||
external_packages.extend(server_external_packages.iter().cloned());
|
external_packages.extend(server_external_packages.iter().cloned());
|
||||||
|
|
||||||
external_packages.retain(|item| !transpile_packages.contains(item));
|
external_packages.retain(|item| !transpiled_packages.contains(item));
|
||||||
|
|
||||||
let ty = ty.into_value();
|
let ty = ty.into_value();
|
||||||
|
|
||||||
|
@ -203,7 +209,7 @@ pub async fn get_server_resolve_options_context(
|
||||||
ExternalCjsModulesResolvePlugin::new(
|
ExternalCjsModulesResolvePlugin::new(
|
||||||
project_path,
|
project_path,
|
||||||
project_path.root(),
|
project_path.root(),
|
||||||
ExternalPredicate::AllExcept(Vc::cell(transpile_packages)).cell(),
|
ExternalPredicate::AllExcept(Vc::cell(transpiled_packages)).cell(),
|
||||||
*next_config.import_externals().await?,
|
*next_config.import_externals().await?,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
|
@ -89,11 +89,29 @@ pub fn get_asset_path_from_pathname(pathname: &str, ext: &str) -> String {
|
||||||
format!("{}{}", get_asset_prefix_from_pathname(pathname), ext)
|
format!("{}{}", get_asset_prefix_from_pathname(pathname), ext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[turbo_tasks::function]
|
||||||
|
pub async fn get_transpiled_packages(
|
||||||
|
next_config: Vc<NextConfig>,
|
||||||
|
project_path: Vc<FileSystemPath>,
|
||||||
|
) -> Result<Vc<Vec<RcStr>>> {
|
||||||
|
let mut transpile_packages: Vec<RcStr> = next_config.transpile_packages().await?.clone_value();
|
||||||
|
|
||||||
|
let default_transpiled_packages: Vec<RcStr> = load_next_js_templateon(
|
||||||
|
project_path,
|
||||||
|
"dist/lib/default-transpiled-packages.json".into(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
transpile_packages.extend(default_transpiled_packages.iter().cloned());
|
||||||
|
|
||||||
|
Ok(Vc::cell(transpile_packages))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn foreign_code_context_condition(
|
pub async fn foreign_code_context_condition(
|
||||||
next_config: Vc<NextConfig>,
|
next_config: Vc<NextConfig>,
|
||||||
project_path: Vc<FileSystemPath>,
|
project_path: Vc<FileSystemPath>,
|
||||||
) -> Result<ContextCondition> {
|
) -> Result<ContextCondition> {
|
||||||
let transpile_packages = next_config.transpile_packages().await?;
|
let transpiled_packages = get_transpiled_packages(next_config, project_path).await?;
|
||||||
|
|
||||||
// The next template files are allowed to import the user's code via import
|
// The next template files are allowed to import the user's code via import
|
||||||
// mapping, and imports must use the project-level [ResolveOptions] instead
|
// mapping, and imports must use the project-level [ResolveOptions] instead
|
||||||
|
@ -103,23 +121,16 @@ pub async fn foreign_code_context_condition(
|
||||||
get_next_package(project_path).join(NEXT_TEMPLATE_PATH.into()),
|
get_next_package(project_path).join(NEXT_TEMPLATE_PATH.into()),
|
||||||
));
|
));
|
||||||
|
|
||||||
let result = if transpile_packages.is_empty() {
|
let result = ContextCondition::all(vec![
|
||||||
ContextCondition::all(vec![
|
ContextCondition::InDirectory("node_modules".to_string()),
|
||||||
ContextCondition::InDirectory("node_modules".to_string()),
|
not_next_template_dir,
|
||||||
not_next_template_dir,
|
ContextCondition::not(ContextCondition::any(
|
||||||
])
|
transpiled_packages
|
||||||
} else {
|
.iter()
|
||||||
ContextCondition::all(vec![
|
.map(|package| ContextCondition::InDirectory(format!("node_modules/{package}")))
|
||||||
ContextCondition::InDirectory("node_modules".to_string()),
|
.collect(),
|
||||||
not_next_template_dir,
|
)),
|
||||||
ContextCondition::not(ContextCondition::any(
|
]);
|
||||||
transpile_packages
|
|
||||||
.iter()
|
|
||||||
.map(|package| ContextCondition::InDirectory(format!("node_modules/{package}")))
|
|
||||||
.collect(),
|
|
||||||
)),
|
|
||||||
])
|
|
||||||
};
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,11 +135,13 @@ export function makeExternalHandler({
|
||||||
config,
|
config,
|
||||||
optOutBundlingPackages,
|
optOutBundlingPackages,
|
||||||
optOutBundlingPackageRegex,
|
optOutBundlingPackageRegex,
|
||||||
|
transpiledPackages,
|
||||||
dir,
|
dir,
|
||||||
}: {
|
}: {
|
||||||
config: NextConfigComplete
|
config: NextConfigComplete
|
||||||
optOutBundlingPackages: string[]
|
optOutBundlingPackages: string[]
|
||||||
optOutBundlingPackageRegex: RegExp
|
optOutBundlingPackageRegex: RegExp
|
||||||
|
transpiledPackages: string[]
|
||||||
dir: string
|
dir: string
|
||||||
}) {
|
}) {
|
||||||
let resolvedExternalPackageDirs: Map<string, string>
|
let resolvedExternalPackageDirs: Map<string, string>
|
||||||
|
@ -322,10 +324,10 @@ export function makeExternalHandler({
|
||||||
|
|
||||||
// If a package should be transpiled by Next.js, we skip making it external.
|
// If a package should be transpiled by Next.js, we skip making it external.
|
||||||
// It doesn't matter what the extension is, as we'll transpile it anyway.
|
// It doesn't matter what the extension is, as we'll transpile it anyway.
|
||||||
if (config.transpilePackages && !resolvedExternalPackageDirs) {
|
if (transpiledPackages && !resolvedExternalPackageDirs) {
|
||||||
resolvedExternalPackageDirs = new Map()
|
resolvedExternalPackageDirs = new Map()
|
||||||
// We need to resolve all the external package dirs initially.
|
// We need to resolve all the external package dirs initially.
|
||||||
for (const pkg of config.transpilePackages) {
|
for (const pkg of transpiledPackages) {
|
||||||
const pkgRes = await resolveExternal(
|
const pkgRes = await resolveExternal(
|
||||||
dir,
|
dir,
|
||||||
config.experimental.esmExternals,
|
config.experimental.esmExternals,
|
||||||
|
@ -350,6 +352,7 @@ export function makeExternalHandler({
|
||||||
externalType,
|
externalType,
|
||||||
isOptOutBundling,
|
isOptOutBundling,
|
||||||
request,
|
request,
|
||||||
|
transpiledPackages,
|
||||||
})
|
})
|
||||||
if (resolvedBundlingOptOutRes) {
|
if (resolvedBundlingOptOutRes) {
|
||||||
return resolvedBundlingOptOutRes
|
return resolvedBundlingOptOutRes
|
||||||
|
@ -368,6 +371,7 @@ function resolveBundlingOptOutPackages({
|
||||||
externalType,
|
externalType,
|
||||||
isOptOutBundling,
|
isOptOutBundling,
|
||||||
request,
|
request,
|
||||||
|
transpiledPackages,
|
||||||
}: {
|
}: {
|
||||||
resolvedRes: string
|
resolvedRes: string
|
||||||
config: NextConfigComplete
|
config: NextConfigComplete
|
||||||
|
@ -376,6 +380,7 @@ function resolveBundlingOptOutPackages({
|
||||||
externalType: string
|
externalType: string
|
||||||
isOptOutBundling: boolean
|
isOptOutBundling: boolean
|
||||||
request: string
|
request: string
|
||||||
|
transpiledPackages: string[]
|
||||||
}) {
|
}) {
|
||||||
if (nodeModulesRegex.test(resolvedRes)) {
|
if (nodeModulesRegex.test(resolvedRes)) {
|
||||||
const shouldBundlePages =
|
const shouldBundlePages =
|
||||||
|
@ -385,7 +390,7 @@ function resolveBundlingOptOutPackages({
|
||||||
shouldBundlePages ||
|
shouldBundlePages ||
|
||||||
isResourceInPackages(
|
isResourceInPackages(
|
||||||
resolvedRes,
|
resolvedRes,
|
||||||
config.transpilePackages,
|
transpiledPackages,
|
||||||
resolvedExternalPackageDirs
|
resolvedExternalPackageDirs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ import { loadBindings, lockfilePatchPromise } from '../swc'
|
||||||
import type { JestTransformerConfig } from '../swc/jest-transformer'
|
import type { JestTransformerConfig } from '../swc/jest-transformer'
|
||||||
import type { Config } from '@jest/types'
|
import type { Config } from '@jest/types'
|
||||||
|
|
||||||
|
const DEFAULT_TRANSPILED_PACKAGES: string[] = require('../../lib/default-transpiled-packages.json')
|
||||||
|
|
||||||
async function getConfig(dir: string) {
|
async function getConfig(dir: string) {
|
||||||
const conf = await loadConfig(PHASE_TEST, dir)
|
const conf = await loadConfig(PHASE_TEST, dir)
|
||||||
return conf
|
return conf
|
||||||
|
@ -100,7 +102,9 @@ export default function nextJest(options: { dir?: string } = {}) {
|
||||||
await lockfilePatchPromise.cur
|
await lockfilePatchPromise.cur
|
||||||
}
|
}
|
||||||
|
|
||||||
const transpiled = (nextConfig?.transpilePackages ?? []).join('|')
|
const transpiled = (nextConfig?.transpilePackages ?? [])
|
||||||
|
.concat(DEFAULT_TRANSPILED_PACKAGES)
|
||||||
|
.join('|')
|
||||||
|
|
||||||
const jestTransformerConfig: JestTransformerConfig = {
|
const jestTransformerConfig: JestTransformerConfig = {
|
||||||
modularizeImports: nextConfig?.modularizeImports,
|
modularizeImports: nextConfig?.modularizeImports,
|
||||||
|
|
|
@ -98,6 +98,9 @@ type ClientEntries = {
|
||||||
const EXTERNAL_PACKAGES =
|
const EXTERNAL_PACKAGES =
|
||||||
require('../lib/server-external-packages.json') as string[]
|
require('../lib/server-external-packages.json') as string[]
|
||||||
|
|
||||||
|
const DEFAULT_TRANSPILED_PACKAGES =
|
||||||
|
require('../lib/default-transpiled-packages.json') as string[]
|
||||||
|
|
||||||
export const NEXT_PROJECT_ROOT = path.join(__dirname, '..', '..')
|
export const NEXT_PROJECT_ROOT = path.join(__dirname, '..', '..')
|
||||||
export const NEXT_PROJECT_ROOT_DIST = path.join(NEXT_PROJECT_ROOT, 'dist')
|
export const NEXT_PROJECT_ROOT_DIST = path.join(NEXT_PROJECT_ROOT, 'dist')
|
||||||
const NEXT_PROJECT_ROOT_DIST_CLIENT = path.join(
|
const NEXT_PROJECT_ROOT_DIST_CLIENT = path.join(
|
||||||
|
@ -395,7 +398,9 @@ export default async function getBaseWebpackConfig(
|
||||||
|
|
||||||
// since `pages` doesn't always bundle by default we need to
|
// since `pages` doesn't always bundle by default we need to
|
||||||
// auto-include optimizePackageImports in transpilePackages
|
// auto-include optimizePackageImports in transpilePackages
|
||||||
const finalTranspilePackages: string[] = config.transpilePackages || []
|
const finalTranspilePackages: string[] = (
|
||||||
|
config.transpilePackages || []
|
||||||
|
).concat(DEFAULT_TRANSPILED_PACKAGES)
|
||||||
|
|
||||||
for (const pkg of config.experimental.optimizePackageImports || []) {
|
for (const pkg of config.experimental.optimizePackageImports || []) {
|
||||||
if (!finalTranspilePackages.includes(pkg)) {
|
if (!finalTranspilePackages.includes(pkg)) {
|
||||||
|
@ -855,6 +860,7 @@ export default async function getBaseWebpackConfig(
|
||||||
config,
|
config,
|
||||||
optOutBundlingPackages,
|
optOutBundlingPackages,
|
||||||
optOutBundlingPackageRegex,
|
optOutBundlingPackageRegex,
|
||||||
|
transpiledPackages: finalTranspilePackages,
|
||||||
dir,
|
dir,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
1
packages/next/src/lib/default-transpiled-packages.json
Normal file
1
packages/next/src/lib/default-transpiled-packages.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
["geist"]
|
19
test/e2e/geist-font/geist-font.test.ts
Normal file
19
test/e2e/geist-font/geist-font.test.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { nextTestSetup } from 'e2e-utils'
|
||||||
|
import { assertNoRedbox } from 'next-test-utils'
|
||||||
|
|
||||||
|
describe('geist-font', () => {
|
||||||
|
const { next } = nextTestSetup({
|
||||||
|
files: __dirname,
|
||||||
|
dependencies: {
|
||||||
|
geist: 'latest',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should work with geist font in pages router', async () => {
|
||||||
|
const browser = await next.browser('/foo')
|
||||||
|
|
||||||
|
await assertNoRedbox(browser)
|
||||||
|
const text = await browser.elementByCss('p').text()
|
||||||
|
expect(text).toBe('Foo page')
|
||||||
|
})
|
||||||
|
})
|
9
test/e2e/geist-font/pages/_app.js
Normal file
9
test/e2e/geist-font/pages/_app.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { GeistSans } from 'geist/font/sans'
|
||||||
|
|
||||||
|
export default function MyApp({ Component, pageProps }) {
|
||||||
|
return (
|
||||||
|
<main className={GeistSans.className}>
|
||||||
|
<Component {...pageProps} />
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
3
test/e2e/geist-font/pages/foo.js
Normal file
3
test/e2e/geist-font/pages/foo.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default function Page() {
|
||||||
|
return <p>Foo page</p>
|
||||||
|
}
|
Loading…
Reference in a new issue