-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
31 changed files
with
861 additions
and
431 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
const loadConfig = require("next/dist/server/config").default; | ||
const { PHASE_DEVELOPMENT_SERVER } = require("next/dist/shared/lib/constants"); | ||
|
||
module.exports = (async () => { | ||
module.exports = async () => { | ||
const nextConfig = await loadConfig(PHASE_DEVELOPMENT_SERVER, process.cwd()); | ||
nextConfig.rewrites = await nextConfig.rewrites?.(); | ||
nextConfig.redirects = await nextConfig.redirects?.(); | ||
return nextConfig; | ||
})(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
const { | ||
lazyPostCSS, | ||
} = require("next/dist/build/webpack/config/blocks/css/index"); | ||
const { getSupportedBrowsers } = require("next/dist/build/utils"); | ||
|
||
module.exports = async (cssContent, from, to) => { | ||
const rootDir = process.cwd(); | ||
const supportedBrowsers = getSupportedBrowsers(rootDir, true, { | ||
experimental: { | ||
legacyBrowsers: false, | ||
}, | ||
}); | ||
/**@type {{ postcssWithPlugins: import('postcss').Processor }} */ | ||
const { postcssWithPlugins } = await lazyPostCSS( | ||
rootDir, | ||
supportedBrowsers, | ||
true | ||
); | ||
const { css, map } = await postcssWithPlugins.process(cssContent, { | ||
from, | ||
to, | ||
map: { | ||
inline: true, | ||
}, | ||
}); | ||
return { css, map }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
use std::collections::BTreeMap; | ||
|
||
use indexmap::IndexMap; | ||
use serde::{Deserialize, Serialize}; | ||
use turbopack_dev_server::source::{query::Query, HeaderValue}; | ||
use turbopack_node::{ResponseHeaders, StructuredError}; | ||
|
||
pub mod issue; | ||
pub mod node_api_source; | ||
pub mod render_proxy; | ||
pub mod render_static; | ||
pub mod rendered_source; | ||
|
||
pub use render_static::create_node_evaluate_asset_context; | ||
|
||
#[turbo_tasks::value(shared)] | ||
pub struct RenderData { | ||
params: IndexMap<String, String>, | ||
method: String, | ||
url: String, | ||
query: Query, | ||
headers: BTreeMap<String, HeaderValue>, | ||
path: String, | ||
} | ||
|
||
#[derive(Serialize)] | ||
#[serde(tag = "type", rename_all = "camelCase")] | ||
enum RenderStaticOutgoingMessage<'a> { | ||
Headers { data: &'a RenderData }, | ||
} | ||
|
||
#[derive(Serialize)] | ||
#[serde(tag = "type", rename_all = "camelCase")] | ||
enum RenderProxyOutgoingMessage<'a> { | ||
Headers { data: &'a RenderData }, | ||
BodyChunk { data: &'a [u8] }, | ||
BodyEnd, | ||
} | ||
|
||
#[derive(Deserialize)] | ||
#[serde(tag = "type", rename_all = "camelCase")] | ||
enum RenderProxyIncomingMessage { | ||
Headers { data: ResponseHeaders }, | ||
Body { data: Vec<u8> }, | ||
Error(StructuredError), | ||
} | ||
|
||
#[derive(Deserialize)] | ||
#[serde(tag = "type", rename_all = "camelCase")] | ||
enum RenderStaticIncomingMessage { | ||
Result { result: RenderResult }, | ||
Error(StructuredError), | ||
} | ||
|
||
#[derive(Deserialize)] | ||
#[serde(untagged)] | ||
pub enum RenderResult { | ||
Simple(String), | ||
Advanced { | ||
body: String, | ||
#[serde(rename = "contentType")] | ||
content_type: Option<String>, | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
use anyhow::{bail, Result}; | ||
use turbo_tasks::primitives::StringVc; | ||
use turbo_tasks_fs::FileSystemPathVc; | ||
use turbopack_core::{asset::AssetVc, chunk::ChunkingContextVc}; | ||
use turbopack_dev_server::source::{BodyVc, ProxyResult, ProxyResultVc}; | ||
use turbopack_ecmascript::{chunk::EcmascriptChunkPlaceablesVc, EcmascriptModuleAssetVc}; | ||
use turbopack_node::{get_intermediate_asset, get_renderer_pool, trace_stack, NodeJsOperation}; | ||
|
||
use super::{ | ||
issue::RenderingIssue, RenderDataVc, RenderProxyIncomingMessage, RenderProxyOutgoingMessage, | ||
ResponseHeaders, | ||
}; | ||
|
||
/// Renders a module as static HTML in a node.js process. | ||
#[turbo_tasks::function] | ||
pub async fn render_proxy( | ||
path: FileSystemPathVc, | ||
module: EcmascriptModuleAssetVc, | ||
runtime_entries: EcmascriptChunkPlaceablesVc, | ||
chunking_context: ChunkingContextVc, | ||
intermediate_output_path: FileSystemPathVc, | ||
data: RenderDataVc, | ||
body: BodyVc, | ||
) -> Result<ProxyResultVc> { | ||
let intermediate_asset = get_intermediate_asset( | ||
module.as_evaluated_chunk(chunking_context, Some(runtime_entries)), | ||
intermediate_output_path, | ||
); | ||
let renderer_pool = get_renderer_pool(intermediate_asset, intermediate_output_path); | ||
let pool = renderer_pool.await?; | ||
let mut operation = match pool.operation().await { | ||
Ok(operation) => operation, | ||
Err(err) => { | ||
return proxy_error(path, err, None).await; | ||
} | ||
}; | ||
|
||
match run_proxy_operation( | ||
&mut operation, | ||
data, | ||
body, | ||
intermediate_asset, | ||
intermediate_output_path, | ||
) | ||
.await | ||
{ | ||
Ok(proxy_result) => Ok(proxy_result.cell()), | ||
Err(err) => Ok(proxy_error(path, err, Some(operation)).await?), | ||
} | ||
} | ||
|
||
async fn run_proxy_operation( | ||
operation: &mut NodeJsOperation, | ||
data: RenderDataVc, | ||
body: BodyVc, | ||
intermediate_asset: AssetVc, | ||
intermediate_output_path: FileSystemPathVc, | ||
) -> Result<ProxyResult> { | ||
let data = data.await?; | ||
// First, send the render data. | ||
operation | ||
.send(RenderProxyOutgoingMessage::Headers { data: &data }) | ||
.await?; | ||
|
||
let body = body.await?; | ||
// Then, send the binary body in chunks. | ||
for chunk in body.chunks() { | ||
operation | ||
.send(RenderProxyOutgoingMessage::BodyChunk { | ||
data: chunk.as_bytes(), | ||
}) | ||
.await?; | ||
} | ||
|
||
operation.send(RenderProxyOutgoingMessage::BodyEnd).await?; | ||
|
||
let (status, headers) = match operation.recv().await? { | ||
RenderProxyIncomingMessage::Headers { | ||
data: ResponseHeaders { status, headers }, | ||
} => (status, headers), | ||
RenderProxyIncomingMessage::Error(error) => { | ||
bail!(trace_stack(error, intermediate_asset, intermediate_output_path).await?) | ||
} | ||
_ => { | ||
bail!("unexpected response from the Node.js process while reading response headers") | ||
} | ||
}; | ||
|
||
let body = match operation.recv().await? { | ||
RenderProxyIncomingMessage::Body { data: body } => body, | ||
RenderProxyIncomingMessage::Error(error) => { | ||
bail!(trace_stack(error, intermediate_asset, intermediate_output_path).await?) | ||
} | ||
_ => { | ||
bail!("unexpected response from the Node.js process while reading response body") | ||
} | ||
}; | ||
|
||
Ok(ProxyResult { | ||
status, | ||
headers, | ||
body: body.into(), | ||
}) | ||
} | ||
|
||
async fn proxy_error( | ||
path: FileSystemPathVc, | ||
error: anyhow::Error, | ||
operation: Option<NodeJsOperation>, | ||
) -> Result<ProxyResultVc> { | ||
let message = format!("{error:?}"); | ||
|
||
let status = match operation { | ||
Some(operation) => Some(operation.wait_or_kill().await?), | ||
None => None, | ||
}; | ||
|
||
let mut details = vec![]; | ||
if let Some(status) = status { | ||
details.push(format!("status: {status}")); | ||
} | ||
|
||
let body = format!( | ||
"An error occurred while proxying a request to Node.js:\n{message}\n{}", | ||
details.join("\n") | ||
); | ||
|
||
RenderingIssue { | ||
context: path, | ||
message: StringVc::cell(message), | ||
status: status.and_then(|status| status.code()), | ||
} | ||
.cell() | ||
.as_issue() | ||
.emit(); | ||
|
||
Ok(ProxyResult { | ||
status: 500, | ||
headers: vec![ | ||
"content-type".to_string(), | ||
"text/html; charset=utf-8".to_string(), | ||
], | ||
body: body.into(), | ||
} | ||
.cell()) | ||
} |
Oops, something went wrong.