Skip to content

Commit

Permalink
Turbopack + app router: always use externals for predefined packages (#…
Browse files Browse the repository at this point in the history
…56440)

This applies the predefined list of packages in server-external-packages.json as always external when used by app router in Turbopack

Test Plan: Added integration tests

Closes WEB-1709
  • Loading branch information
wbinnssmith authored Oct 6, 2023
1 parent c4c60b3 commit 3c326ea
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 4 deletions.
19 changes: 17 additions & 2 deletions packages/next-swc/crates/next-core/src/next_server/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ use crate::{
get_decorators_transform_options, get_jsx_transform_options,
get_typescript_transform_options,
},
util::foreign_code_context_condition,
util::{foreign_code_context_condition, load_next_js_templateon},
};

#[turbo_tasks::value(serialization = "auto_for_input")]
Expand Down Expand Up @@ -105,10 +105,25 @@ pub async fn get_server_resolve_options_context(
let root_dir = project_path.root().resolve().await?;
let module_feature_report_resolve_plugin = ModuleFeatureReportResolvePlugin::new(project_path);
let unsupported_modules_resolve_plugin = UnsupportedModulesResolvePlugin::new(project_path);

// Always load these predefined packages as external.
let mut external_packages: Vec<String> = load_next_js_templateon(
project_path,
"dist/lib/server-external-packages.json".to_string(),
)
.await?;

// Add the config's own list of external packages.
external_packages.extend(
(*next_config.server_component_externals().await?)
.iter()
.cloned(),
);

let server_component_externals_plugin = ExternalCjsModulesResolvePlugin::new(
project_path,
project_path.root(),
ExternalPredicate::Only(next_config.server_component_externals()).cell(),
ExternalPredicate::Only(Vc::cell(external_packages)).cell(),
);
let ty = ty.into_value();

Expand Down
4 changes: 2 additions & 2 deletions packages/next-swc/crates/next-core/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,12 +568,12 @@ pub async fn load_next_js_templateon<T: DeserializeOwned>(
project_path: Vc<FileSystemPath>,
path: String,
) -> Result<T> {
let file_path = get_next_package(project_path).join(path);
let file_path = get_next_package(project_path).join(path.clone());

let content = &*file_path.read().await?;

let FileContent::Content(file) = content else {
bail!("Expected file content for metrics data");
bail!("Expected file content at {}", path);
};

let result: T = parse_json_rope_with_source_context(file.content())?;
Expand Down
7 changes: 7 additions & 0 deletions test/e2e/app-dir/externals/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Root({ children }: { children: React.ReactNode }) {
return (
<html>
<body>{children}</body>
</html>
)
}
5 changes: 5 additions & 0 deletions test/e2e/app-dir/externals/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { foo } from 'external-package'

export default function Page() {
return <div>{foo}</div>
}
5 changes: 5 additions & 0 deletions test/e2e/app-dir/externals/app/predefined/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { foo } from 'sqlite3'

export default function Predefined() {
return <div>{foo}</div>
}
71 changes: 71 additions & 0 deletions test/e2e/app-dir/externals/externals.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import fs from 'fs/promises'
import path from 'path'
import { createNextDescribe } from 'e2e-utils'

async function getAppPageChunkPaths(appDir: string, pageName?: string) {
const rscPath = path.join(appDir, '.next/server/chunks/rsc')
const pageRegex = new RegExp(
`app${pageName ? '_' + pageName : ''}_page_tsx_[0-9a-f]+._.js$`
)

return (await fs.readdir(rscPath))
.filter((p) => p.match(pageRegex))
.map((basename) => path.join(rscPath, basename))
}

createNextDescribe(
'externals-app',
{
files: __dirname,
},
({ next, isNextDev, isTurbopack }) => {
it('should have externals for those in config.experimental.serverComponentsExternalPackages', async () => {
await next.render$('/')

if (isTurbopack) {
const appBundles = await getAppPageChunkPaths(next.testDir)
const bundleTexts = await Promise.all(
appBundles.map((b) => fs.readFile(b, 'utf8'))
)
expect(
bundleTexts.find((t) =>
t.includes(
'__turbopack_external_require__("external-package", true)'
)
)
).not.toBeUndefined()
} else {
const output = await fs.readFile(
path.join(next.testDir, '.next/server/app/page.js'),
'utf8'
)
expect(output).toContain('require("external-package")')
}
})

it('uses externals for predefined list in server-external-packages.json', async () => {
await next.render$('/predefined')

if (isTurbopack) {
const appBundles = await getAppPageChunkPaths(
next.testDir,
'predefined'
)
const bundleTexts = await Promise.all(
appBundles.map((b) => fs.readFile(b, 'utf8'))
)
expect(
bundleTexts.find((t) =>
t.includes('__turbopack_external_require__("sqlite3", true)')
)
).not.toBeUndefined()
} else {
const output = await fs.readFile(
path.join(next.testDir, '.next/server/app/predefined/page.js'),
'utf8'
)
expect(output).toContain('require("sqlite3")')
}
})
}
)
10 changes: 10 additions & 0 deletions test/e2e/app-dir/externals/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
experimental: {
serverComponentsExternalPackages: ['external-package'],
},
}

module.exports = nextConfig

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions test/e2e/app-dir/externals/node_modules/sqlite3/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions test/e2e/app-dir/externals/node_modules/sqlite3/package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3c326ea

Please sign in to comment.