feat(next-core): support unsupported module runtime error (#63491)
### What Implement webpack's middleware plugin equivalent for webpack, to raise unsupported error in runtime. PR utilizes import map alias for the edge context, to resolve into modulereplacer internally provides a virtualsource to call runtime error logic. Since we already have globally augmented, the virtualsource only need to take export those into module. Closes PACK-2789
This commit is contained in:
parent
b2b5ab4aff
commit
3689c03d60
5 changed files with 165 additions and 12 deletions
|
@ -1,3 +1,4 @@
|
|||
pub mod context;
|
||||
pub mod entry;
|
||||
pub mod route_regex;
|
||||
pub mod unsupported;
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
use anyhow::Result;
|
||||
use indoc::formatdoc;
|
||||
use turbo_tasks::Vc;
|
||||
use turbo_tasks_fs::File;
|
||||
use turbopack_binding::{
|
||||
turbo::tasks_fs::FileSystemPath,
|
||||
turbopack::{
|
||||
core::{
|
||||
asset::AssetContent,
|
||||
resolve::{
|
||||
options::{ImportMapResult, ImportMapping, ImportMappingReplacement},
|
||||
parse::Request,
|
||||
ResolveResult,
|
||||
},
|
||||
virtual_source::VirtualSource,
|
||||
},
|
||||
node::execution_context::ExecutionContext,
|
||||
},
|
||||
};
|
||||
|
||||
/// Intercepts requests for the given request to `unsupported` error messages
|
||||
/// by returning a VirtualSource proxies to any import request to raise a
|
||||
/// runtime error.
|
||||
///
|
||||
/// This can be used by import map alias, refer `next_import_map` for the setup.
|
||||
#[turbo_tasks::value(shared)]
|
||||
pub struct NextEdgeUnsupportedModuleReplacer {
|
||||
project_path: Vc<FileSystemPath>,
|
||||
execution_context: Vc<ExecutionContext>,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl NextEdgeUnsupportedModuleReplacer {
|
||||
#[turbo_tasks::function]
|
||||
pub fn new(
|
||||
project_path: Vc<FileSystemPath>,
|
||||
execution_context: Vc<ExecutionContext>,
|
||||
) -> Vc<Self> {
|
||||
Self::cell(NextEdgeUnsupportedModuleReplacer {
|
||||
project_path,
|
||||
execution_context,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl ImportMappingReplacement for NextEdgeUnsupportedModuleReplacer {
|
||||
#[turbo_tasks::function]
|
||||
fn replace(&self, _capture: String) -> Vc<ImportMapping> {
|
||||
ImportMapping::Ignore.into()
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn result(
|
||||
&self,
|
||||
context: Vc<FileSystemPath>,
|
||||
request: Vc<Request>,
|
||||
) -> Result<Vc<ImportMapResult>> {
|
||||
let request = &*request.await?;
|
||||
if let Request::Module { module, .. } = request {
|
||||
// packages/next/src/server/web/globals.ts augments global with
|
||||
// `__import_unsupported` and necessary functions.
|
||||
let code = formatdoc! {
|
||||
r#"
|
||||
__turbopack_export_namespace__(__import_unsupported(`{module}`));
|
||||
"#
|
||||
};
|
||||
let content = AssetContent::file(File::from(code).into());
|
||||
let source = VirtualSource::new(context, content);
|
||||
return Ok(
|
||||
ImportMapResult::Result(ResolveResult::source(Vc::upcast(source)).into()).into(),
|
||||
);
|
||||
};
|
||||
|
||||
Ok(ImportMapResult::NoEntry.into())
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ use crate::{
|
|||
mode::NextMode,
|
||||
next_client::context::ClientContextType,
|
||||
next_config::NextConfig,
|
||||
next_edge::unsupported::NextEdgeUnsupportedModuleReplacer,
|
||||
next_font::{
|
||||
google::{
|
||||
NextFontGoogleCssModuleReplacer, NextFontGoogleFontFileReplacer, NextFontGoogleReplacer,
|
||||
|
@ -38,6 +39,57 @@ use crate::{
|
|||
util::NextRuntime,
|
||||
};
|
||||
|
||||
const NODE_INTERNALS: [&str; 48] = [
|
||||
"assert",
|
||||
"async_hooks",
|
||||
"child_process",
|
||||
"cluster",
|
||||
"console",
|
||||
"constants",
|
||||
"dgram",
|
||||
"diagnostics_channel",
|
||||
"dns",
|
||||
"dns/promises",
|
||||
"domain",
|
||||
"events",
|
||||
"fs",
|
||||
"fs/promises",
|
||||
"http",
|
||||
"http2",
|
||||
"https",
|
||||
"inspector",
|
||||
"module",
|
||||
"net",
|
||||
"os",
|
||||
"path",
|
||||
"path/posix",
|
||||
"path/win32",
|
||||
"perf_hooks",
|
||||
"process",
|
||||
"punycode",
|
||||
"querystring",
|
||||
"readline",
|
||||
"repl",
|
||||
"stream",
|
||||
"stream/promises",
|
||||
"stream/web",
|
||||
"string_decoder",
|
||||
"sys",
|
||||
"timers",
|
||||
"timers/promises",
|
||||
"tls",
|
||||
"trace_events",
|
||||
"tty",
|
||||
"util",
|
||||
"util/types",
|
||||
"v8",
|
||||
"vm",
|
||||
"wasi",
|
||||
"worker_threads",
|
||||
"zlib",
|
||||
"pnpapi",
|
||||
];
|
||||
|
||||
// Make sure to not add any external requests here.
|
||||
/// Computes the Next-specific client import map.
|
||||
#[turbo_tasks::function]
|
||||
|
@ -416,9 +468,29 @@ pub async fn get_next_edge_import_map(
|
|||
)
|
||||
.await?;
|
||||
|
||||
insert_unsupported_node_internal_aliases(&mut import_map, project_path, execution_context);
|
||||
|
||||
Ok(import_map.cell())
|
||||
}
|
||||
|
||||
/// Insert default aliases for the node.js's internal to raise unsupported
|
||||
/// runtime errors. User may provide polyfills for their own by setting user
|
||||
/// config's alias.
|
||||
fn insert_unsupported_node_internal_aliases(
|
||||
import_map: &mut ImportMap,
|
||||
project_path: Vc<FileSystemPath>,
|
||||
execution_context: Vc<ExecutionContext>,
|
||||
) {
|
||||
let unsupported_replacer = ImportMapping::Dynamic(Vc::upcast(
|
||||
NextEdgeUnsupportedModuleReplacer::new(project_path, execution_context),
|
||||
))
|
||||
.into();
|
||||
|
||||
NODE_INTERNALS.iter().for_each(|module| {
|
||||
import_map.insert_alias(AliasPattern::exact(*module), unsupported_replacer);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_next_client_resolved_map(
|
||||
_context: Vc<FileSystemPath>,
|
||||
_root: Vc<FileSystemPath>,
|
||||
|
|
|
@ -55,7 +55,10 @@ export function expectUnsupportedModuleDevError(
|
|||
output = context.logs.output
|
||||
) {
|
||||
expectUnsupportedModuleProdError(moduleName, output)
|
||||
// turbopack have correct error overly, but doesn't emit those into cli
|
||||
if (!process.env.TURBOPACK) {
|
||||
expect(stripAnsi(output)).toContain(importStatement)
|
||||
}
|
||||
|
||||
const moduleNotSupportedMessage = getUnsupportedModule(moduleName)
|
||||
expect(responseText).toContain(escapeLF(moduleNotSupportedMessage))
|
||||
|
|
|
@ -9533,16 +9533,16 @@
|
|||
"test/integration/edge-runtime-module-errors/test/index.test.js": {
|
||||
"passed": [
|
||||
"Edge runtime code with imports Edge API importing vanilla 3rd party module does not throw in dev at runtime",
|
||||
"Edge runtime code with imports Edge API using Buffer polyfill does not throw in dev at runtime",
|
||||
"Edge runtime code with imports Middleware importing vanilla 3rd party module does not throw in dev at runtime",
|
||||
"Edge runtime code with imports Middleware using Buffer polyfill does not throw in dev at runtime"
|
||||
"Edge runtime code with imports Edge API dynamically importing node.js module development mode throws unsupported module error in dev at runtime and highlights the faulty line",
|
||||
"Edge runtime code with imports Middleware dynamically importing node.js module development mode throws unsupported module error in dev at runtime and highlights the faulty line",
|
||||
"Edge runtime code with imports Edge API dynamically importing node.js module in a lib development mode throws unsupported module error in dev at runtime and highlights the faulty line",
|
||||
"Edge runtime code with imports Middleware dynamically importing node.js module in a lib development mode throws unsupported module error in dev at runtime and highlights the faulty line"
|
||||
],
|
||||
"failed": [
|
||||
"Edge runtime code with imports Edge API dynamically importing node.js module development mode throws unsupported module error in dev at runtime and highlights the faulty line",
|
||||
"Edge runtime code with imports Edge API dynamically importing node.js module in a lib development mode throws unsupported module error in dev at runtime and highlights the faulty line",
|
||||
"Edge runtime code with imports Edge API using Buffer polyfill does not throw in dev at runtime",
|
||||
"Edge runtime code with imports Middleware using Buffer polyfill does not throw in dev at runtime",
|
||||
"Edge runtime code with imports Edge API statically importing 3rd party module throws not-found module error in dev at runtime and highlights the faulty line",
|
||||
"Edge runtime code with imports Middleware dynamically importing node.js module development mode throws unsupported module error in dev at runtime and highlights the faulty line",
|
||||
"Edge runtime code with imports Middleware dynamically importing node.js module in a lib development mode throws unsupported module error in dev at runtime and highlights the faulty line",
|
||||
"Edge runtime code with imports Middleware statically importing 3rd party module throws not-found module error in dev at runtime and highlights the faulty line"
|
||||
],
|
||||
"pending": [
|
||||
|
@ -9562,16 +9562,16 @@
|
|||
},
|
||||
"test/integration/edge-runtime-module-errors/test/module-imports.test.js": {
|
||||
"passed": [
|
||||
"Edge runtime code with imports Edge API importing unused node.js module does not throw in dev at runtime"
|
||||
"Edge runtime code with imports Edge API importing unused node.js module does not throw in dev at runtime",
|
||||
"Edge runtime code with imports Middleware importing unused node.js module does not throw in dev at runtime",
|
||||
"Edge runtime code with imports Edge API statically importing node.js module throws unsupported module error in dev at runtime and highlights the faulty line",
|
||||
"Edge runtime code with imports Middleware statically importing node.js module throws unsupported module error in dev at runtime and highlights the faulty line"
|
||||
],
|
||||
"failed": [
|
||||
"Edge runtime code with imports Edge API dynamically importing 3rd party module throws not-found module error in dev at runtime and highlights the faulty line",
|
||||
"Edge runtime code with imports Edge API importing unused 3rd party module throws not-found module error in dev at runtime and highlights the faulty line",
|
||||
"Edge runtime code with imports Edge API statically importing node.js module throws unsupported module error in dev at runtime and highlights the faulty line",
|
||||
"Edge runtime code with imports Middleware dynamically importing 3rd party module throws not-found module error in dev at runtime and highlights the faulty line",
|
||||
"Edge runtime code with imports Middleware importing unused 3rd party module throws not-found module error in dev at runtime and highlights the faulty line",
|
||||
"Edge runtime code with imports Middleware importing unused node.js module does not throw in dev at runtime",
|
||||
"Edge runtime code with imports Middleware statically importing node.js module throws unsupported module error in dev at runtime and highlights the faulty line"
|
||||
"Edge runtime code with imports Middleware importing unused 3rd party module throws not-found module error in dev at runtime and highlights the faulty line"
|
||||
],
|
||||
"pending": [
|
||||
"Edge runtime code with imports Edge API dynamically importing 3rd party module production mode does not build and reports module not found error",
|
||||
|
|
Loading…
Reference in a new issue