Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test for bug reproduction #10738

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/astro/test/fixtures/ssr-prerender-chunks/astro.config.mjs
@@ -0,0 +1,10 @@
import serverlessAdapter from '@test/serverless-test-adapter';
import { defineConfig } from 'astro/config';
import react from "@astrojs/react";

// https://astro.build/config
export default defineConfig({
adapter: serverlessAdapter(),
output: 'server',
integrations: [react()]
});
14 changes: 14 additions & 0 deletions packages/astro/test/fixtures/ssr-prerender-chunks/package.json
@@ -0,0 +1,14 @@
{
"name": "@test/ssr-prerender-chunks",
"version": "0.0.0",
"private": true,
"dependencies": {
"@astrojs/react": "^3.2.0",
"@test/serverless-test-adapter": "workspace:*",
"@types/react": "^18.2.75",
"@types/react-dom": "^18.2.24",
"astro": "workspace:*",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
@@ -0,0 +1,26 @@
import React, { useState } from "react";

const Counter: React.FC = () => {
const [count, setCount] = useState<number>(0);

const increment = () => {
setCount((prevCount) => prevCount + 1);
};

const decrement = () => {
setCount((prevCount) => prevCount - 1);
};

return (
<div>
<h2>Counter</h2>
<div>
<button onClick={decrement}>-</button>
<span>{count}</span>
<button onClick={increment}>+</button>
</div>
</div>
);
};

export default Counter;
@@ -0,0 +1,13 @@
---
export const prerender = true;
import Counter from "../components/Counter";
---

<html>
<head>
<title>Static Page</title>
</head>
<body>
<Counter client:load />
</body>
</html>
@@ -0,0 +1,7 @@
{
"extends": "astro/tsconfigs/base",
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react"
}
}
20 changes: 20 additions & 0 deletions packages/astro/test/ssr-prerender-chunk.test.js
@@ -0,0 +1,20 @@
import assert from 'node:assert/strict';
import { before, describe, it } from 'node:test';
import { loadFixture } from './test-utils.js';

describe('Chunks', () => {
/** @type {import('./test-utils').Fixture} */
let fixtureServerless;

before(async () => {
fixtureServerless = await loadFixture({
root: './fixtures/ssr-prerender-chunks/',
});
await fixtureServerless.build();
});
it('has wrong chunks', async () => {
const content = await fixtureServerless.readFile('_worker.js/renderers.mjs')
const hasImportFromPrerender = !content.includes(`React } from './chunks/prerender`)
assert.ok(hasImportFromPrerender)
})
})
4 changes: 2 additions & 2 deletions packages/astro/test/test-adapter.js
Expand Up @@ -12,8 +12,8 @@ export default function (
setMiddlewareEntryPoint = undefined,
setRoutes = undefined,
} = {
provideAddress: true,
}
provideAddress: true,
}
) {
return {
name: 'my-ssr-adapter',
Expand Down
13 changes: 13 additions & 0 deletions packages/serverless-test-adapter/package.json
@@ -0,0 +1,13 @@
{
"name": "@test/serverless-test-adapter",
"private": true,
"type": "module",
"exports": {
".": "./src/index.js",
"./server.js": "./src/server.js",
"./package.json": "./package.json"
},
"files": [
"src"
]
}
85 changes: 85 additions & 0 deletions packages/serverless-test-adapter/src/index.js
@@ -0,0 +1,85 @@
/**
*
* @returns {import('../src/@types/astro').AstroIntegration}
*/
export default function () {
return {
name: '@test/serverless-test-adapter',
hooks: {
'astro:config:setup': ({ updateConfig, config }) => {
updateConfig({
build: {
client: config.outDir,
server: new URL('./_worker.js/', config.outDir),
serverEntry: 'index.js',
redirects: false,
}
});
},
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@test/serverless-test-adapter',
serverEntrypoint: '@test/serverless-test-adapter/server.js',
exports: ['default'],
supportedAstroFeatures: {
serverOutput: 'stable',
},
});
},
'astro:build:setup': ({ vite, target }) => {
if (target === 'server') {
vite.resolve ||= {};
vite.resolve.alias ||= {};

const aliases = [
{
find: 'react-dom/server',
replacement: 'react-dom/server.browser',
},
];

if (Array.isArray(vite.resolve.alias)) {
vite.resolve.alias = [...vite.resolve.alias, ...aliases];
} else {
for (const alias of aliases) {
(vite.resolve.alias)[alias.find] = alias.replacement;
}
}

vite.resolve.conditions ||= [];
// We need those conditions, previous these conditions where applied at the esbuild step which we removed
// https://github.com/withastro/astro/pull/7092
vite.resolve.conditions.push('workerd', 'worker');

vite.ssr ||= {};
vite.ssr.target = 'webworker';
vite.ssr.noExternal = true;

vite.build ||= {};
vite.build.rollupOptions ||= {};
vite.build.rollupOptions.output ||= {};
vite.build.rollupOptions.output.banner ||=
'globalThis.process ??= {}; globalThis.process.env ??= {};';

// Cloudflare env is only available per request. This isn't feasible for code that access env vars
// in a global way, so we shim their access as `process.env.*`. This is not the recommended way for users to access environment variables. But we'll add this for compatibility for chosen variables. Mainly to support `@astrojs/db`
vite.define = {
'process.env': 'process.env',
...vite.define,
};
}
// we thought that vite config inside `if (target === 'server')` would not apply for client
// but it seems like the same `vite` reference is used for both
// so we need to reset the previous conflicting setting
// in the future we should look into a more robust solution
if (target === 'client') {
vite.resolve ||= {};
vite.resolve.conditions ||= [];
vite.resolve.conditions = vite.resolve.conditions.filter(
(c) => c !== 'workerd' && c !== 'worker'
);
}
},
},
};
}
65 changes: 65 additions & 0 deletions packages/serverless-test-adapter/src/server.js
@@ -0,0 +1,65 @@
import { App } from 'astro/app';

export function createExports(manifest) {
const app = new App(manifest);

const fetch = async (
request,
env,
context
) => {
const { pathname } = new URL(request.url);

// static assets fallback, in case default _routes.json is not used
if (manifest.assets.has(pathname)) {
return env.ASSETS.fetch(request.url.replace(/\.html$/, ''));
}

const routeData = app.match(request);
if (!routeData) {
// https://developers.cloudflare.com/pages/functions/api-reference/#envassetsfetch
const asset = await env.ASSETS.fetch(
request.url.replace(/index.html$/, '').replace(/\.html$/, '')
);
if (asset.status !== 404) {
return asset;
}
}

Reflect.set(
request,
Symbol.for('astro.clientAddress'),
request.headers.get('cf-connecting-ip')
);

process.env.ASTRO_STUDIO_APP_TOKEN ??= (() => {
if (typeof env.ASTRO_STUDIO_APP_TOKEN === 'string') {
return env.ASTRO_STUDIO_APP_TOKEN;
}
})();

const locals = {
runtime: {
env: env,
cf: request.cf,
caches,
ctx: {
waitUntil: (promise) => context.waitUntil(promise),
passThroughOnException: () => context.passThroughOnException(),
},
},
};

const response = await app.render(request, { routeData, locals });

if (app.setCookieHeaders) {
for (const setCookieHeader of app.setCookieHeaders(response)) {
response.headers.append('Set-Cookie', setCookieHeader);
}
}

return response;
};

return { default: { fetch } };
}
33 changes: 33 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.