Skip to content

Commit

Permalink
use edge environment in server-side rendering of client components too
Browse files Browse the repository at this point in the history
  • Loading branch information
sokra committed Jul 24, 2023
1 parent 84d730d commit 6e9958d
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 47 deletions.
55 changes: 50 additions & 5 deletions packages/next-swc/crates/next-core/src/app_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,38 @@ fn next_ssr_client_module_transition(
)
}

#[turbo_tasks::function]
fn next_edge_ssr_client_module_transition(
project_path: Vc<FileSystemPath>,
execution_context: Vc<ExecutionContext>,
app_dir: Vc<FileSystemPath>,
next_config: Vc<NextConfig>,
server_addr: Vc<ServerAddr>,
) -> Vc<Box<dyn Transition>> {
let ty = Value::new(ServerContextType::AppSSR { app_dir });
let mode = NextMode::Development;
Vc::upcast(
NextSSRClientModuleTransition {
ssr_module_options_context: get_server_module_options_context(
project_path,
execution_context,
ty,
mode,
next_config,
),
ssr_resolve_options_context: get_edge_resolve_options_context(
project_path,
ty,
mode,
next_config,
execution_context,
),
ssr_environment: get_edge_compile_time_info(project_path, server_addr),
}
.cell(),
)
}

#[turbo_tasks::function]
fn next_server_component_transition(
project_path: Vc<FileSystemPath>,
Expand Down Expand Up @@ -377,8 +409,6 @@ fn app_context(
server_addr: Vc<ServerAddr>,
output_path: Vc<FileSystemPath>,
) -> Vc<ModuleAssetContext> {
let next_server_to_client_transition = Vc::upcast(NextServerToClientTransition { ssr }.cell());

let mut transitions = HashMap::new();
transitions.insert(
"next-edge-route".to_string(),
Expand Down Expand Up @@ -420,6 +450,11 @@ fn app_context(
Vc::cell(ecmacscript_client_reference_transition_name.clone()),
),
);
transitions.insert(
ecmacscript_client_reference_transition_name,
Vc::upcast(NextServerToClientTransition { ssr, edge: false }.cell()),
);
let ecmacscript_edge_client_reference_transition_name = "edge-server-to-client".to_string();
transitions.insert(
"next-edge-server-component".to_string(),
next_edge_server_component_transition(
Expand All @@ -430,12 +465,12 @@ fn app_context(
mode,
next_config,
server_addr,
Vc::cell(ecmacscript_client_reference_transition_name.clone()),
Vc::cell(ecmacscript_edge_client_reference_transition_name.clone()),
),
);
transitions.insert(
ecmacscript_client_reference_transition_name,
next_server_to_client_transition,
ecmacscript_edge_client_reference_transition_name,
Vc::upcast(NextServerToClientTransition { ssr, edge: true }.cell()),
);
transitions.insert(
"next-client".to_string(),
Expand Down Expand Up @@ -473,6 +508,16 @@ fn app_context(
server_addr,
),
);
transitions.insert(
"next-edge-ssr-client-module".to_string(),
next_edge_ssr_client_module_transition(
project_path,
execution_context,
app_dir,
next_config,
server_addr,
),
);

let ssr_ty = Value::new(ServerContextType::AppSSR { app_dir });
ModuleAssetContext::new(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::embed_js::next_asset;
#[turbo_tasks::value(shared)]
pub struct NextServerToClientTransition {
pub ssr: bool,
pub edge: bool,
}

#[turbo_tasks::value_impl]
Expand All @@ -41,14 +42,17 @@ impl Transition for NextServerToClientTransition {
Ok(match this.ssr {
true => {
let internal_source = next_asset("entry/app/server-to-client-ssr.tsx".to_string());
let client_module = context
.with_transition("next-ssr-client-module".to_string())
.process(
source,
Value::new(ReferenceType::Entry(
EntryReferenceSubType::AppClientComponent,
)),
);
let transition = if this.edge {
"next-edge-ssr-client-module"
} else {
"next-ssr-client-module"
};
let client_module = context.with_transition(transition.to_string()).process(
source,
Value::new(ReferenceType::Entry(
EntryReferenceSubType::AppClientComponent,
)),
);
context.process(
internal_source,
Value::new(ReferenceType::Internal(Vc::cell(indexmap! {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import edgeThenNode from 'edge-then-node'
import nodeThenEdge from 'node-then-edge'
import ClientComponent from '../client'

export const runtime = 'edge'

export default function AppEdge() {
return JSON.stringify({
edgeThenNode,
nodeThenEdge,
})
return (
<>
<div id="server">
{JSON.stringify({
edgeThenNode,
nodeThenEdge,
})}
</div>
<ClientComponent />
</>
)
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import edgeThenNode from 'edge-then-node'
import nodeThenEdge from 'node-then-edge'
import ClientComponent from '../client'

export const runtime = 'nodejs'

export default function AppNodeJs() {
return JSON.stringify({
edgeThenNode,
nodeThenEdge,
})
return (
<>
<div id="server">
{JSON.stringify({
edgeThenNode,
nodeThenEdge,
})}
</div>
<ClientComponent />
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client'

import edgeThenNode from 'edge-then-node'
import nodeThenEdge from 'node-then-edge'

export const runtime = 'edge'

export default function ClientComponent() {
return (
<div id="client">
{JSON.stringify({
edgeThenNode,
nodeThenEdge,
})}
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,24 @@ export default function Test() {
async function getJson(url) {
const res = await fetch(url)
const text = await res.text()
const jsonText = /(\{[^}]*\})/.exec(text)
const regex = /<div id="(\w+)">(\{[^}]*\})/g
const matches = text.matchAll(regex)
const result = {}
try {
return JSON.parse(jsonText[0].replace(/&quot;/g, '"'))
for (const match of matches) {
result[match[1]] = JSON.parse(match[2].replace(/&quot;/g, '"'))
}
return result
} catch (err) {
throw new Error(`Expected JSON but got:\n${text}`)
}
}

async function getJsonApi(url) {
const res = await fetch(url)
const text = await res.text()
try {
return JSON.parse(text)
} catch (err) {
throw new Error(`Expected JSON but got:\n${text}`)
}
Expand All @@ -21,8 +36,10 @@ function runTests() {
it('page with nodejs runtime should import node conditions', async () => {
const json = await getJson('/page-nodejs')
expect(json).toMatchObject({
edgeThenNode: 'node',
nodeThenEdge: 'node',
server: {
edgeThenNode: 'node',
nodeThenEdge: 'node',
},
})
})

Expand All @@ -31,26 +48,30 @@ function runTests() {
// TODO We don't currently support edge config in page rendering.
// When we do, this needs to be updated.
expect(json).not.toMatchObject({
edgeThenNode: 'edge',
nodeThenEdge: 'edge',
server: {
edgeThenNode: 'edge',
nodeThenEdge: 'edge',
},
})
// TODO: delete this.
expect(json).toMatchObject({
edgeThenNode: 'node',
nodeThenEdge: 'node',
server: {
edgeThenNode: 'node',
nodeThenEdge: 'node',
},
})
})

it('page api with nodejs runtime should import node conditions', async () => {
const json = await getJson('/api/api-nodejs')
const json = await getJsonApi('/api/api-nodejs')
expect(json).toMatchObject({
edgeThenNode: 'node',
nodeThenEdge: 'node',
})
})

it('page api with edge runtime should import edge conditions', async () => {
const json = await getJson('/api/api-edge')
const json = await getJsonApi('/api/api-edge')
expect(json).toMatchObject({
edgeThenNode: 'edge',
nodeThenEdge: 'edge',
Expand All @@ -60,38 +81,49 @@ function runTests() {
it('app with nodejs runtime should import node conditions', async () => {
const json = await getJson('/app-nodejs')
expect(json).toMatchObject({
edgeThenNode: 'node',
nodeThenEdge: 'node',
server: {
edgeThenNode: 'node',
nodeThenEdge: 'node',
},
client: {
edgeThenNode: 'node',
nodeThenEdge: 'node',
},
})
})

it('app with edge runtime should import edge conditions', async () => {
const json = await getJson('/app-edge')
expect(json).toMatchObject({
edgeThenNode: 'edge',
nodeThenEdge: 'edge',
server: {
edgeThenNode: 'edge',
nodeThenEdge: 'edge',
},
client: {
edgeThenNode: 'edge',
nodeThenEdge: 'edge',
},
})
})

it('app route with nodejs runtime should import node conditions', async () => {
const json = await getJson('/route-nodejs')
const json = await getJsonApi('/route-nodejs')
expect(json).toMatchObject({
edgeThenNode: 'node',
nodeThenEdge: 'node',
})
})

it('app route with edge runtime should import edge conditions', async () => {
const json = await getJson('/route-edge')
const json = await getJsonApi('/route-edge')
expect(json).toMatchObject({
edgeThenNode: 'edge',
nodeThenEdge: 'edge',
})
})

it('middleware should import edge conditions', async () => {
const res = await fetch('/middleware')
const json = await res.json()
const json = await getJsonApi('/middleware')
expect(json).toMatchObject({
edgeThenNode: 'edge',
nodeThenEdge: 'edge',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ export const config = {
}

export default function PageEdge() {
return JSON.stringify({
edgeThenNode,
nodeThenEdge,
})
return (
<div id="server">
{JSON.stringify({
edgeThenNode,
nodeThenEdge,
})}
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ export const config = {
}

export default function PageNodeJs() {
return JSON.stringify({
edgeThenNode,
nodeThenEdge,
})
return (
<div id="server">
{JSON.stringify({
edgeThenNode,
nodeThenEdge,
})}
</div>
)
}

0 comments on commit 6e9958d

Please sign in to comment.