Skip to content

Commit 6779a5c

Browse files
authored
Turbopack: error when importing Typescript in node_modules (#83990)
Webpack treats TS files in node_modules as Javascript files (i.e. you get a JS parse error) Turbopack previously did a similar thing (though it parsed as Typescript, but then never ran the TS AST transform). This never worked properly, it failed due to some unreachable assertion in the swc transforms. Now it fails with a proper error ~~, though too many of them~~: ``` ./node_modules/.pnpm/pkg@file+pkg/node_modules/pkg/index.ts Missing module type The module type effect must be applied before adding Ecmascript transforms ./node_modules/.pnpm/pkg@file+pkg/node_modules/pkg/index.ts Unknown module type This module doesn't have an associated type. Use a known file extension, or register a loader for it. Read more: https://nextjs.org/docs/app/api-reference/next-config-js/turbo#webpack-loaders at ignore-listed frames ``` Additionally: don't run styled-jsx/styled-components/relay/... in node_modules in the server. This aligns it what we already had configured for the client context
1 parent 9cf9183 commit 6779a5c

File tree

18 files changed

+324
-232
lines changed

18 files changed

+324
-232
lines changed

crates/next-core/src/next_client/transforms.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ pub async fn get_next_client_transforms_rules(
3232

3333
let modularize_imports_config = &next_config.modularize_imports().await?;
3434
let enable_mdx_rs = next_config.mdx_rs().await?.is_some();
35-
rules.push(get_next_lint_transform_rule(enable_mdx_rs));
35+
36+
if !foreign_code {
37+
rules.push(get_next_lint_transform_rule(enable_mdx_rs));
38+
}
3639

3740
if !modularize_imports_config.is_empty() {
3841
rules.push(get_next_modularize_imports_rule(

crates/next-core/src/next_server/context.rs

Lines changed: 51 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -559,14 +559,16 @@ pub async fn get_server_module_options_context(
559559
.flatten()
560560
.collect();
561561

562-
// Custom ecma transform rules selectively being applied depends on the server
563-
// context type.
564-
let styled_components_transform_rule =
565-
get_styled_components_transform_rule(next_config).await?;
566-
// It's important the client's browserlist config is used for styled-jsx, otherwise we transpile
567-
// the CSS to be compatible with Node.js 20.
568-
let styled_jsx_transform_rule =
569-
get_styled_jsx_transform_rule(next_config, client_environment.runtime_versions()).await?;
562+
// Only relevant for pages, not routes/etc.
563+
let page_transform_rules: Vec<ModuleRule> = vec![
564+
get_styled_components_transform_rule(next_config).await?,
565+
// It's important the client's browserlist config is used for styled-jsx, otherwise we
566+
// transpile the CSS to be compatible with Node.js 20.
567+
get_styled_jsx_transform_rule(next_config, client_environment.runtime_versions()).await?,
568+
]
569+
.into_iter()
570+
.flatten()
571+
.collect();
570572

571573
let source_maps = if *next_config.server_source_maps().await? {
572574
SourceMapsType::Full
@@ -608,23 +610,16 @@ pub async fn get_server_module_options_context(
608610

609611
let module_options_context = match ty {
610612
ServerContextType::Pages { .. } | ServerContextType::PagesApi { .. } => {
611-
let mut custom_source_transform_rules: Vec<ModuleRule> =
612-
vec![styled_components_transform_rule, styled_jsx_transform_rule]
613-
.into_iter()
614-
.flatten()
615-
.collect();
616-
613+
next_server_rules.extend(page_transform_rules);
617614
if let ServerContextType::Pages { .. } = ty {
618-
custom_source_transform_rules.push(
615+
next_server_rules.push(
619616
get_next_react_server_components_transform_rule(next_config, false, None)
620617
.await?,
621618
);
622619
}
623620

624-
next_server_rules.extend(custom_source_transform_rules.iter().cloned());
625621
next_server_rules.extend(source_transform_rules);
626622

627-
foreign_next_server_rules.extend(custom_source_transform_rules);
628623
foreign_next_server_rules.extend(internal_custom_rules);
629624

630625
let url_rewrite_behavior = Some(
@@ -690,21 +685,13 @@ pub async fn get_server_module_options_context(
690685
}
691686
}
692687
ServerContextType::AppSSR { app_dir, .. } => {
693-
let mut custom_source_transform_rules: Vec<ModuleRule> =
694-
vec![styled_components_transform_rule, styled_jsx_transform_rule]
695-
.into_iter()
696-
.flatten()
697-
.collect();
698-
699-
foreign_next_server_rules.extend(custom_source_transform_rules.iter().cloned());
700688
foreign_next_server_rules.extend(internal_custom_rules);
701689

702-
custom_source_transform_rules.push(
690+
next_server_rules.extend(page_transform_rules.clone());
691+
next_server_rules.push(
703692
get_next_react_server_components_transform_rule(next_config, false, Some(app_dir))
704693
.await?,
705694
);
706-
707-
next_server_rules.extend(custom_source_transform_rules.clone());
708695
next_server_rules.extend(source_transform_rules);
709696

710697
let foreign_code_module_options_context = ModuleOptionsContext {
@@ -755,33 +742,29 @@ pub async fn get_server_module_options_context(
755742
ecmascript_client_reference_transition_name,
756743
..
757744
} => {
758-
let mut custom_source_transform_rules: Vec<ModuleRule> =
759-
vec![styled_components_transform_rule, styled_jsx_transform_rule]
760-
.into_iter()
761-
.flatten()
762-
.collect();
763-
764-
if let Some(ecmascript_client_reference_transition_name) =
765-
ecmascript_client_reference_transition_name
766-
{
767-
custom_source_transform_rules.push(get_ecma_transform_rule(
768-
Box::new(ClientDirectiveTransformer::new(
769-
ecmascript_client_reference_transition_name,
770-
)),
771-
enable_mdx_rs.is_some(),
772-
EcmascriptTransformStage::Preprocess,
773-
));
774-
}
745+
next_server_rules.extend(page_transform_rules);
746+
747+
let client_directive_transformer = ecmascript_client_reference_transition_name.map(
748+
|ecmascript_client_reference_transition_name| {
749+
get_ecma_transform_rule(
750+
Box::new(ClientDirectiveTransformer::new(
751+
ecmascript_client_reference_transition_name,
752+
)),
753+
enable_mdx_rs.is_some(),
754+
EcmascriptTransformStage::Preprocess,
755+
)
756+
},
757+
);
775758

776-
foreign_next_server_rules.extend(custom_source_transform_rules.iter().cloned());
759+
foreign_next_server_rules.extend(client_directive_transformer.clone());
777760
foreign_next_server_rules.extend(internal_custom_rules);
778761

779-
custom_source_transform_rules.push(
762+
next_server_rules.extend(client_directive_transformer.clone());
763+
next_server_rules.push(
780764
get_next_react_server_components_transform_rule(next_config, true, Some(app_dir))
781765
.await?,
782766
);
783767

784-
next_server_rules.extend(custom_source_transform_rules.clone());
785768
next_server_rules.extend(source_transform_rules);
786769

787770
let foreign_code_module_options_context = ModuleOptionsContext {
@@ -909,35 +892,28 @@ pub async fn get_server_module_options_context(
909892
app_dir,
910893
ecmascript_client_reference_transition_name,
911894
} => {
912-
let mut custom_source_transform_rules: Vec<ModuleRule> =
913-
vec![styled_components_transform_rule, styled_jsx_transform_rule]
914-
.into_iter()
915-
.flatten()
916-
.collect();
917-
918-
if let Some(ecmascript_client_reference_transition_name) =
919-
ecmascript_client_reference_transition_name
920-
{
921-
custom_source_transform_rules.push(get_ecma_transform_rule(
922-
Box::new(ClientDirectiveTransformer::new(
923-
ecmascript_client_reference_transition_name,
924-
)),
925-
enable_mdx_rs.is_some(),
926-
EcmascriptTransformStage::Preprocess,
927-
));
928-
} else {
929-
custom_source_transform_rules.push(get_ecma_transform_rule(
930-
Box::new(ClientDisallowedDirectiveTransformer::new(
931-
"next/dist/client/use-client-disallowed.js".to_string(),
932-
)),
933-
enable_mdx_rs.is_some(),
934-
EcmascriptTransformStage::Preprocess,
935-
));
936-
}
937-
938-
custom_source_transform_rules.push(
895+
let custom_source_transform_rules: Vec<ModuleRule> = vec![
896+
if let Some(ecmascript_client_reference_transition_name) =
897+
ecmascript_client_reference_transition_name
898+
{
899+
get_ecma_transform_rule(
900+
Box::new(ClientDirectiveTransformer::new(
901+
ecmascript_client_reference_transition_name,
902+
)),
903+
enable_mdx_rs.is_some(),
904+
EcmascriptTransformStage::Preprocess,
905+
)
906+
} else {
907+
get_ecma_transform_rule(
908+
Box::new(ClientDisallowedDirectiveTransformer::new(
909+
"next/dist/client/use-client-disallowed.js".to_string(),
910+
)),
911+
enable_mdx_rs.is_some(),
912+
EcmascriptTransformStage::Preprocess,
913+
)
914+
},
939915
get_next_react_server_components_transform_rule(next_config, true, app_dir).await?,
940-
);
916+
];
941917

942918
internal_custom_rules.extend(custom_source_transform_rules.iter().cloned());
943919

crates/next-core/src/next_server/transforms.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ pub async fn get_next_server_transforms_rules(
3939
let modularize_imports_config = &next_config.modularize_imports().await?;
4040
let mdx_rs = next_config.mdx_rs().await?.is_some();
4141

42-
rules.push(get_next_lint_transform_rule(mdx_rs));
42+
if !foreign_code {
43+
rules.push(get_next_lint_transform_rule(mdx_rs));
44+
}
4345

4446
if !modularize_imports_config.is_empty() {
4547
rules.push(get_next_modularize_imports_rule(
File renamed without changes.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as React from 'react'
2+
3+
export default function Root({ children }) {
4+
return (
5+
<html>
6+
<head></head>
7+
<body>{children}</body>
8+
</html>
9+
)
10+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Foo } from 'pkg/index.ts'
2+
3+
export default function Page() {
4+
return <main>Hello {Foo}</main>
5+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { nextTestSetup } from 'e2e-utils'
2+
3+
describe('transpile-packages-typescript-foreign', () => {
4+
describe('without transpilePackages', () => {
5+
const { next, skipped } = nextTestSetup({
6+
files: __dirname,
7+
skipDeployment: true,
8+
skipStart: true,
9+
dependencies: {
10+
pkg: `file:./pkg`,
11+
},
12+
})
13+
14+
if (skipped) {
15+
return
16+
}
17+
18+
it('should fail', async () => {
19+
try {
20+
await next.start()
21+
await next.render('/')
22+
} catch (e) {}
23+
24+
if (process.env.IS_TURBOPACK_TEST) {
25+
expect(next.cliOutput).toContain(`pkg/index.ts
26+
Unknown module type
27+
This module doesn't have an associated type`)
28+
expect(
29+
next.cliOutput.match(/Unknown module type/g).length
30+
).toBeLessThanOrEqual(1)
31+
expect(
32+
next.cliOutput.match(/Missing module type/g).length
33+
).toBeLessThanOrEqual(1)
34+
} else {
35+
expect(next.cliOutput).toContain(`pkg/index.ts
36+
Module parse failed: Unexpected token`)
37+
}
38+
})
39+
})
40+
41+
describe('with transpilePackages', () => {
42+
const { next, skipped } = nextTestSetup({
43+
files: __dirname,
44+
skipDeployment: true,
45+
dependencies: {
46+
pkg: `file:./pkg`,
47+
},
48+
nextConfig: {
49+
transpilePackages: ['pkg'],
50+
},
51+
})
52+
53+
if (skipped) {
54+
return
55+
}
56+
57+
it('should work', async () => {
58+
const $ = await next.render$('/')
59+
expect($('main').text()).toEqual('Hello 123')
60+
})
61+
})
62+
})
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type Cents = number
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import type { Cents } from './cents'
2+
3+
export type Type = Cents
4+
5+
export const Foo = 123
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "pkg",
3+
"version": "1.0.0"
4+
}

0 commit comments

Comments
 (0)