diff --git a/examples/server-islands/package.json b/examples/server-islands/package.json
index 4c2b0591b6..b80361b5e6 100644
--- a/examples/server-islands/package.json
+++ b/examples/server-islands/package.json
@@ -11,7 +11,7 @@
},
"devDependencies": {
"@astrojs/node": "^8.2.6",
- "@astrojs/react": "workspace:*",
+ "@astrojs/react": "^3.6.0",
"@astrojs/tailwind": "^5.1.0",
"@fortawesome/fontawesome-free": "^6.5.2",
"@tailwindcss/forms": "^0.5.7",
diff --git a/examples/server-islands/src/components/Cart.astro b/examples/server-islands/src/components/Cart.astro
deleted file mode 100644
index a1b40b1641..0000000000
--- a/examples/server-islands/src/components/Cart.astro
+++ /dev/null
@@ -1,17 +0,0 @@
----
-import Cart from './Cart.js';
-
-// Delay for fun
-await new Promise(resolve => setTimeout(resolve, 3000));
----
-
-
- I'm a shopping cart
-
-
-
diff --git a/examples/server-islands/src/components/Cart.tsx b/examples/server-islands/src/components/Cart.tsx
deleted file mode 100644
index 5a64bb6c77..0000000000
--- a/examples/server-islands/src/components/Cart.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { useEffect, useState } from 'react';
-
-export default function() {
- const [count, setCount] = useState(0);
- useEffect(() => {
- setTimeout(() => {
- if(count < 10) {
- setCount(count + 1);
- }
- }, 2000);
- }, [count]);
- return (
- Count: {count}
- )
-}
diff --git a/examples/server-islands/src/components/Header.astro b/examples/server-islands/src/components/Header.astro
deleted file mode 100644
index 2dfc99107b..0000000000
--- a/examples/server-islands/src/components/Header.astro
+++ /dev/null
@@ -1,24 +0,0 @@
----
-import Cart from '../components/Cart.astro';
----
-
-
diff --git a/examples/server-islands/src/pages/index.astro b/examples/server-islands/src/pages/index.astro
index 0fbf0b122c..a36d5df05f 100644
--- a/examples/server-islands/src/pages/index.astro
+++ b/examples/server-islands/src/pages/index.astro
@@ -2,6 +2,7 @@
import '../base.css';
import AddToCart from '../components/AddToCart';
import PersonalBar from '../components/PersonalBar.astro';
+import '@fortawesome/fontawesome-free/css/all.min.css';
---
@@ -19,8 +20,6 @@ import PersonalBar from '../components/PersonalBar.astro';
-
-
diff --git a/examples/server-islands/src/pages/old-index.astro b/examples/server-islands/src/pages/old-index.astro
deleted file mode 100644
index 2d68d4d8b7..0000000000
--- a/examples/server-islands/src/pages/old-index.astro
+++ /dev/null
@@ -1,11 +0,0 @@
----
-import Header from '../components/Header.astro';
----
-
-
- Testing
-
-
-
-
-
diff --git a/packages/astro/src/core/app/common.ts b/packages/astro/src/core/app/common.ts
index e1385ea687..19bbee1954 100644
--- a/packages/astro/src/core/app/common.ts
+++ b/packages/astro/src/core/app/common.ts
@@ -17,6 +17,7 @@ export function deserializeManifest(serializedManifest: SerializedSSRManifest):
const componentMetadata = new Map(serializedManifest.componentMetadata);
const inlinedScripts = new Map(serializedManifest.inlinedScripts);
const clientDirectives = new Map(serializedManifest.clientDirectives);
+ const serverIslandNameMap = new Map(serializedManifest.serverIslandNameMap);
return {
// in case user middleware exists, this no-op middleware will be reassigned (see plugin-ssr.ts)
@@ -29,5 +30,6 @@ export function deserializeManifest(serializedManifest: SerializedSSRManifest):
inlinedScripts,
clientDirectives,
routes,
+ serverIslandNameMap,
};
}
diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts
index 0ef48a9265..b9de9a97a6 100644
--- a/packages/astro/src/core/app/index.ts
+++ b/packages/astro/src/core/app/index.ts
@@ -20,7 +20,7 @@ import {
} from '../path.js';
import { RenderContext } from '../render-context.js';
import { createAssetLink } from '../render/ssr-element.js';
-import { ensure404Route } from '../routing/astro-designed-error-pages.js';
+import { injectDefaultRoutes } from '../routing/default.js';
import { matchRoute } from '../routing/match.js';
import { createOriginCheckMiddleware } from './middlewares.js';
import { AppPipeline } from './pipeline.js';
@@ -87,7 +87,7 @@ export class App {
constructor(manifest: SSRManifest, streaming = true) {
this.#manifest = manifest;
- this.#manifestData = ensure404Route({
+ this.#manifestData = injectDefaultRoutes({
routes: manifest.routes.map((route) => route.routeData),
});
this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base);
diff --git a/packages/astro/src/core/app/pipeline.ts b/packages/astro/src/core/app/pipeline.ts
index a0fc1eadd6..cc53e4a8aa 100644
--- a/packages/astro/src/core/app/pipeline.ts
+++ b/packages/astro/src/core/app/pipeline.ts
@@ -8,12 +8,8 @@ import type {
} from '../../@types/astro.js';
import { Pipeline } from '../base-pipeline.js';
import type { SinglePageBuiltModule } from '../build/types.js';
-import { DEFAULT_404_COMPONENT } from '../constants.js';
-import { RewriteEncounteredAnError } from '../errors/errors-data.js';
-import { AstroError } from '../errors/index.js';
import { RedirectSinglePageBuiltModule } from '../redirects/component.js';
import { createModuleScriptElement, createStylesheetElementSet } from '../render/ssr-element.js';
-import { DEFAULT_404_ROUTE } from '../routing/astro-designed-error-pages.js';
import { findRouteToRewrite } from '../routing/rewrite.js';
export class AppPipeline extends Pipeline {
@@ -103,13 +99,16 @@ export class AppPipeline extends Pipeline {
}
async getModuleForRoute(route: RouteData): Promise {
- if (route.component === DEFAULT_404_COMPONENT) {
- return {
- page: async () =>
- ({ default: () => new Response(null, { status: 404 }) }) as ComponentInstance,
- renderers: [],
- };
+ for(const defaultRoute of this.defaultRoutes) {
+ if(route.component === defaultRoute.component) {
+ //return defaultRoute.instance;
+ return {
+ page: () => Promise.resolve(defaultRoute.instance),
+ renderers: []
+ };
+ }
}
+
if (route.type === 'redirect') {
return RedirectSinglePageBuiltModule;
} else {
diff --git a/packages/astro/src/core/app/types.ts b/packages/astro/src/core/app/types.ts
index 248ab01c38..0c4e671160 100644
--- a/packages/astro/src/core/app/types.ts
+++ b/packages/astro/src/core/app/types.ts
@@ -84,11 +84,12 @@ export type SSRManifestI18n = {
export type SerializedSSRManifest = Omit<
SSRManifest,
- 'middleware' | 'routes' | 'assets' | 'componentMetadata' | 'inlinedScripts' | 'clientDirectives'
+ 'middleware' | 'routes' | 'assets' | 'componentMetadata' | 'inlinedScripts' | 'clientDirectives' | 'serverIslandNameMap'
> & {
routes: SerializedRouteInfo[];
assets: string[];
componentMetadata: [string, SSRComponentMetadata][];
inlinedScripts: [string, string][];
clientDirectives: [string, string][];
+ serverIslandNameMap: [string, string][];
};
diff --git a/packages/astro/src/core/base-pipeline.ts b/packages/astro/src/core/base-pipeline.ts
index a23c3ce271..05a7a83e02 100644
--- a/packages/astro/src/core/base-pipeline.ts
+++ b/packages/astro/src/core/base-pipeline.ts
@@ -14,6 +14,7 @@ import { AstroError } from './errors/errors.js';
import { AstroErrorData } from './errors/index.js';
import type { Logger } from './logger/core.js';
import { RouteCache } from './render/route-cache.js';
+import { createDefaultRoutes } from './routing/default.js';
/**
* The `Pipeline` represents the static parts of rendering that do not change between requests.
@@ -52,7 +53,8 @@ export abstract class Pipeline {
* Used for `Astro.site`.
*/
readonly site = manifest.site ? new URL(manifest.site) : undefined,
- readonly callSetGetEnv = true
+ readonly callSetGetEnv = true,
+ readonly defaultRoutes = createDefaultRoutes(manifest, new URL(import.meta.url))
) {
this.internalMiddleware = [];
// We do use our middleware only if the user isn't using the manual setup
diff --git a/packages/astro/src/core/build/plugins/plugin-manifest.ts b/packages/astro/src/core/build/plugins/plugin-manifest.ts
index 791b33deae..0134aff0f5 100644
--- a/packages/astro/src/core/build/plugins/plugin-manifest.ts
+++ b/packages/astro/src/core/build/plugins/plugin-manifest.ts
@@ -279,6 +279,7 @@ function buildManifest(
buildFormat: settings.config.build.format,
checkOrigin: settings.config.security?.checkOrigin ?? false,
rewritingEnabled: settings.config.experimental.rewriting,
+ serverIslandNameMap: Array.from(settings.serverIslandNameMap),
experimentalEnvGetSecretEnabled:
settings.config.experimental.env !== undefined &&
(settings.adapter?.supportedAstroFeatures.envGetSecret ?? 'unsupported') !== 'unsupported',
diff --git a/packages/astro/src/core/build/plugins/plugin-ssr.ts b/packages/astro/src/core/build/plugins/plugin-ssr.ts
index 880a4d6a8e..572069cb43 100644
--- a/packages/astro/src/core/build/plugins/plugin-ssr.ts
+++ b/packages/astro/src/core/build/plugins/plugin-ssr.ts
@@ -13,6 +13,7 @@ import { SSR_MANIFEST_VIRTUAL_MODULE_ID } from './plugin-manifest.js';
import { MIDDLEWARE_MODULE_ID } from './plugin-middleware.js';
import { ASTRO_PAGE_MODULE_ID } from './plugin-pages.js';
import { RENDERERS_MODULE_ID } from './plugin-renderers.js';
+import { VIRTUAL_ISLAND_MAP_ID } from '../../server-islands/vite-plugin-server-islands.js';
import { getComponentFromVirtualModulePageName, getVirtualModulePageName } from './util.js';
export const SSR_VIRTUAL_MODULE_ID = '@astrojs-ssr-virtual-entry';
@@ -249,12 +250,14 @@ function generateSSRCode(adapter: AstroAdapter, middlewareId: string) {
`import { manifest as defaultManifest } from '${SSR_MANIFEST_VIRTUAL_MODULE_ID}';`,
`import * as serverEntrypointModule from '${adapter.serverEntrypoint}';`,
edgeMiddleware ? `` : `import { onRequest as middleware } from '${middlewareId}';`,
+ `import { serverIslandMap } from '${VIRTUAL_ISLAND_MAP_ID}';`
];
const contents = [
edgeMiddleware ? `const middleware = (_, next) => next()` : '',
`const _manifest = Object.assign(defaultManifest, {`,
` ${pageMap},`,
+ ` serverIslandMap,`,
` renderers,`,
` middleware`,
`});`,
diff --git a/packages/astro/src/core/server-islands/endpoint.ts b/packages/astro/src/core/server-islands/endpoint.ts
index 4599b8cf36..c9d3f5866d 100644
--- a/packages/astro/src/core/server-islands/endpoint.ts
+++ b/packages/astro/src/core/server-islands/endpoint.ts
@@ -41,12 +41,19 @@ export function createEndpoint(manifest: SSRManifest) {
const request = result.request;
const raw = await request.text();
const data = JSON.parse(raw) as RenderOptions;
- const componentId = params.name! as string;
+ if(!params.name) {
+ return new Response(null, {
+ status: 400,
+ statusText: 'Bad request'
+ });
+ }
+ const componentId = params.name;
const imp = manifest.serverIslandMap?.get(componentId);
if(!imp) {
- return new Response('Not found', {
- status: 404
+ return new Response(null, {
+ status: 404,
+ statusText: 'Not found'
});
}
diff --git a/packages/astro/src/core/server-islands/vite-plugin-server-islands.ts b/packages/astro/src/core/server-islands/vite-plugin-server-islands.ts
index d2455d6be3..cad0c77523 100644
--- a/packages/astro/src/core/server-islands/vite-plugin-server-islands.ts
+++ b/packages/astro/src/core/server-islands/vite-plugin-server-islands.ts
@@ -2,44 +2,92 @@ import type { AstroPluginMetadata } from '../../vite-plugin-astro/index.js';
import type { AstroSettings, ComponentInstance } from '../../@types/astro.js';
import type { ViteDevServer, Plugin as VitePlugin } from 'vite';
+export const VIRTUAL_ISLAND_MAP_ID = '@astro-server-islands';
+export const RESOLVED_VIRTUAL_ISLAND_MAP_ID = '\0' + VIRTUAL_ISLAND_MAP_ID;
+const serverIslandPlaceholder = '\'$$server-islands$$\'';
+
export function vitePluginServerIslands({ settings }: { settings: AstroSettings }): VitePlugin {
let viteServer: ViteDevServer | null = null;
+ const referenceIdMap = new Map();
return {
name: 'astro:server-islands',
enforce: 'post',
configureServer(_server) {
viteServer = _server;
},
+ resolveId(name) {
+ if(name === VIRTUAL_ISLAND_MAP_ID) {
+ return RESOLVED_VIRTUAL_ISLAND_MAP_ID;
+ }
+ },
+ load(id) {
+ if(id === RESOLVED_VIRTUAL_ISLAND_MAP_ID) {
+ return `export const serverIslandMap = ${serverIslandPlaceholder};`;
+ }
+ },
transform(code, id, options) {
if(id.endsWith('.astro')) {
const info = this.getModuleInfo(id);
if(info?.meta) {
const astro = info.meta.astro as AstroPluginMetadata['astro'] | undefined;
if(astro?.serverComponents.length) {
- if(viteServer) {
- for(const comp of astro.serverComponents) {
- if(!settings.serverIslandNameMap.has(comp.resolvedPath)) {
- let name = comp.localName;
- let idx = 1;
+ for(const comp of astro.serverComponents) {
+ if(!settings.serverIslandNameMap.has(comp.resolvedPath)) {
+ let name = comp.localName;
+ let idx = 1;
- while(true) {
- // Name not taken, let's use it.
- if(!settings.serverIslandMap.has(name)) {
- break;
- }
- // Increment a number onto the name: Avatar -> Avatar1
- name += idx++;
+ while(true) {
+ // Name not taken, let's use it.
+ if(!settings.serverIslandMap.has(name)) {
+ break;
}
- settings.serverIslandNameMap.set(comp.resolvedPath, name);
- settings.serverIslandMap.set(name, () => {
- return viteServer?.ssrLoadModule(comp.resolvedPath) as any;
+ // Increment a number onto the name: Avatar -> Avatar1
+ name += idx++;
+ }
+
+ // Append the name map, for prod
+ settings.serverIslandNameMap.set(comp.resolvedPath, name);
+
+ settings.serverIslandMap.set(name, () => {
+ return viteServer?.ssrLoadModule(comp.resolvedPath) as any;
+ });
+
+ // Build mode
+ if(!viteServer) {
+ let referenceId = this.emitFile({
+ type: 'chunk',
+ id: comp.specifier,
+ importer: id,
+ name: comp.localName
});
+
+ referenceIdMap.set(comp.resolvedPath, referenceId);
}
}
}
}
}
}
+ },
+ generateBundle(options, bundles) {
+ let mapSource = 'new Map([';
+ for(let [resolvedPath, referenceId] of referenceIdMap) {
+ const fileName = this.getFileName(referenceId);
+ const islandName = settings.serverIslandNameMap.get(resolvedPath)!;
+ mapSource += `\n\t['${islandName}', () => import('./${fileName}')],`
+ }
+ mapSource += '\n]);';
+ referenceIdMap.clear();
+
+ for (const [fileName, output] of Object.entries(bundles)) {
+ if(output.type !== 'chunk') continue;
+
+ //console.log("OUTPUT", output.code);
+
+ if(output.code.includes(serverIslandPlaceholder)) {
+ output.code = output.code.replace(serverIslandPlaceholder, mapSource);
+ }
+ }
}
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 32c81edf35..6b3d759ba8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -379,7 +379,7 @@ importers:
specifier: ^8.2.6
version: link:../../packages/integrations/node
'@astrojs/react':
- specifier: workspace:*
+ specifier: ^3.6.0
version: link:../../packages/integrations/react
'@astrojs/tailwind':
specifier: ^5.1.0