Turbopack: align automatic externals code with webpack (#58851)

### What?

* remove the additional check which verifies identity of resolved and
external module.
* `serverComponentsExternals` will force it being an external

### Why?

### How?


Closes PACK-2032

---------

Co-authored-by: Tim Neutkens <tim@timneutkens.nl>
This commit is contained in:
Tobias Koppers 2023-11-24 12:16:13 +01:00 committed by GitHub
parent 6bfd1458b2
commit 2e28fa113e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -3,15 +3,12 @@ use turbo_tasks::{Value, Vc};
use turbopack_binding::{
turbo::tasks_fs::{glob::Glob, FileJsonContent, FileSystemPath},
turbopack::core::{
reference_type::{
CommonJsReferenceSubType, EcmaScriptModulesReferenceSubType, ReferenceType,
},
reference_type::{EcmaScriptModulesReferenceSubType, ReferenceType},
resolve::{
find_context_file,
node::{node_cjs_resolve_options, node_esm_resolve_options},
package_json,
parse::Request,
pattern::Pattern,
plugin::{ResolvePlugin, ResolvePluginCondition},
resolve, FindContextFileResult, ResolveResult, ResolveResultItem, ResolveResultOption,
},
@ -60,44 +57,6 @@ impl ExternalCjsModulesResolvePlugin {
}
}
#[turbo_tasks::function]
async fn is_node_resolveable(
context: Vc<FileSystemPath>,
request: Vc<Request>,
expected: Vc<FileSystemPath>,
is_esm: bool,
) -> Result<Vc<bool>> {
let node_resolve_result = if is_esm {
resolve(
context,
Value::new(ReferenceType::EcmaScriptModules(
EcmaScriptModulesReferenceSubType::Undefined,
)),
request,
node_esm_resolve_options(context.root()),
)
} else {
resolve(
context,
Value::new(ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined)),
request,
node_cjs_resolve_options(context.root()),
)
};
let primary_node_assets = node_resolve_result.primary_sources().await?;
let Some(&node_asset) = primary_node_assets.first() else {
// can't resolve request with node.js options
return Ok(Vc::cell(false));
};
if node_asset.ident().path().resolve().await? != expected.resolve().await? {
// node.js resolves to a different file
return Ok(Vc::cell(false));
}
Ok(Vc::cell(true))
}
#[turbo_tasks::function]
fn condition(root: Vc<FileSystemPath>) -> Vc<ResolvePluginCondition> {
ResolvePluginCondition::new(root, Glob::new("**/node_modules/**".to_string()))
@ -151,7 +110,8 @@ impl ResolvePlugin for ExternalCjsModulesResolvePlugin {
}
}
let is_esm = ReferenceType::EcmaScriptModules(EcmaScriptModulesReferenceSubType::Undefined)
let is_esm = self.import_externals
&& ReferenceType::EcmaScriptModules(EcmaScriptModulesReferenceSubType::Undefined)
.includes(&reference_type);
enum FileType {
@ -197,82 +157,47 @@ impl ResolvePlugin for ExternalCjsModulesResolvePlugin {
Ok(FileType::Unsupported)
}
let file_type = get_file_type(fs_path, raw_fs_path).await?;
let (expected, is_esm) = match (file_type, is_esm) {
(FileType::Unsupported, _) => {
// unsupported file type, bundle it
return Ok(ResolveResultOption::none());
}
(FileType::CommonJs, false) => (fs_path, false),
(FileType::CommonJs, true) => (fs_path, self.import_externals),
(FileType::EcmaScriptModule, false) => (fs_path, false),
(FileType::EcmaScriptModule, true) => {
if self.import_externals {
(fs_path, true)
} else {
// We verify with the CommonJS alternative
let cjs_resolved = resolve(
let node_resolved = resolve(
context,
reference_type.clone(),
request,
node_cjs_resolve_options(context.root()),
if is_esm {
node_esm_resolve_options(context.root())
} else {
node_cjs_resolve_options(context.root())
},
);
let Some(result) = *cjs_resolved.first_source().await? else {
// this can't resolve with commonjs, so bundle it
let Some(result) = *node_resolved.first_source().await? else {
// this can't resolve with node.js, so bundle it
return Ok(ResolveResultOption::none());
};
let path = result.ident().path();
let file_type = get_file_type(path, &*path.await?).await?;
if !matches!(file_type, FileType::CommonJs) {
// even with require() this resolves to a ESM, which would break node.js
// bundle it
// This happens for invalid packages like `textlinestream`
return Ok(ResolveResultOption::none());
}
(path, false)
match (file_type, is_esm) {
(FileType::Unsupported, _) => {
// unsupported file type, bundle it
Ok(ResolveResultOption::none())
}
}
};
let is_resolveable =
*is_node_resolveable(self.project_path, request, expected, is_esm).await?;
if !is_resolveable {
if is_esm {
// When it's not resolveable as ESM, there is maybe an extension missing,
// try to add .js
if let Some(mut request_str) = request.await?.request() {
if !request_str.ends_with(".js") {
request_str += ".js";
let new_request =
Request::parse(Value::new(Pattern::Constant(request_str.clone())));
let is_resolveable =
*is_node_resolveable(self.project_path, new_request, expected, is_esm)
.await?;
if is_resolveable {
// mark as external, but with .js extension
return Ok(ResolveResultOption::some(
ResolveResult::primary(
ResolveResultItem::OriginalReferenceTypeExternal(request_str),
)
.cell(),
));
}
}
}
}
// this can't resolve with node.js, so bundle it
return Ok(ResolveResultOption::none());
}
(FileType::CommonJs, _) => {
// mark as external
Ok(ResolveResultOption::some(
ResolveResult::primary(ResolveResultItem::OriginalReferenceExternal).cell(),
))
}
(FileType::EcmaScriptModule, true) => {
// mark as external
Ok(ResolveResultOption::some(
ResolveResult::primary(ResolveResultItem::OriginalReferenceExternal).cell(),
))
}
(FileType::EcmaScriptModule, false) => {
// even with require() this resolves to a ESM,
// which would break node.js, bundle it
Ok(ResolveResultOption::none())
}
}
}
}
// TODO move that to turbo