New code (#8559)
This commit is contained in:
parent
4ed410db50
commit
87d5b841af
27 changed files with 1 additions and 1436 deletions
File diff suppressed because one or more lines are too long
|
@ -1,176 +1,3 @@
|
|||
# @astrojs/deno 🦖
|
||||
|
||||
This adapter allows Astro to deploy your SSR site to Deno targets.
|
||||
|
||||
Learn how to deploy your Astro site in our [Deno Deploy deployment guide](https://docs.astro.build/en/guides/deploy/deno/).
|
||||
|
||||
- <strong>[Why Astro Deno](#why-astro-deno)</strong>
|
||||
- <strong>[Installation](#installation)</strong>
|
||||
- <strong>[Usage](#usage)</strong>
|
||||
- <strong>[Configuration](#configuration)</strong>
|
||||
- <strong>[Examples](#examples)</strong>
|
||||
- <strong>[Troubleshooting](#troubleshooting)</strong>
|
||||
- <strong>[Contributing](#contributing)</strong>
|
||||
- <strong>[Changelog](#changelog)</strong>
|
||||
|
||||
## Why Astro Deno
|
||||
|
||||
If you're using Astro as a static site builder—its behavior out of the box—you don't need an adapter.
|
||||
|
||||
If you wish to [use server-side rendering (SSR)](https://docs.astro.build/en/guides/server-side-rendering/), Astro requires an adapter that matches your deployment runtime.
|
||||
|
||||
[Deno](https://deno.land/) is a runtime similar to Node, but with an API that's more similar to the browser's API. This adapter provides access to Deno's API and creates a script to run your project on a Deno server.
|
||||
|
||||
## Installation
|
||||
|
||||
Add the Deno adapter to enable SSR in your Astro project with the following `astro add` command. This will install the adapter and make the appropriate changes to your `astro.config.mjs` file in one step.
|
||||
|
||||
```sh
|
||||
# Using NPM
|
||||
npx astro add deno
|
||||
# Using Yarn
|
||||
yarn astro add deno
|
||||
# Using PNPM
|
||||
pnpm astro add deno
|
||||
```
|
||||
|
||||
If you prefer to install the adapter manually instead, complete the following two steps:
|
||||
|
||||
1. Install the Deno adapter to your project’s dependencies using your preferred package manager. If you’re using npm or aren’t sure, run this in the terminal:
|
||||
|
||||
```bash
|
||||
npm install @astrojs/deno
|
||||
```
|
||||
|
||||
1. Update your `astro.config.mjs` project configuration file with the changes below.
|
||||
|
||||
```js ins={3,6-7}
|
||||
// astro.config.mjs
|
||||
import { defineConfig } from 'astro/config';
|
||||
import deno from '@astrojs/deno';
|
||||
|
||||
export default defineConfig({
|
||||
output: 'server',
|
||||
adapter: deno(),
|
||||
});
|
||||
```
|
||||
|
||||
Next, update your `preview` script in `package.json` to run `deno`:
|
||||
|
||||
```json ins={8}
|
||||
// package.json
|
||||
{
|
||||
// ...
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "deno run --allow-net --allow-read --allow-env ./dist/server/entry.mjs"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can now use this command to preview your production Astro site locally with Deno.
|
||||
|
||||
```bash
|
||||
npm run preview
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
After [performing a build](https://docs.astro.build/en/guides/deploy/#building-your-site-locally) there will be a `dist/server/entry.mjs` module. You can start a server by importing this module in your Deno app:
|
||||
|
||||
```js
|
||||
import './dist/server/entry.mjs';
|
||||
```
|
||||
|
||||
See the `start` option below for how you can have more control over starting the Astro server.
|
||||
|
||||
You can also run the script directly using deno:
|
||||
|
||||
```sh
|
||||
deno run --allow-net --allow-read --allow-env ./dist/server/entry.mjs
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
To configure this adapter, pass an object to the `deno()` function call in `astro.config.mjs`.
|
||||
|
||||
```js
|
||||
// astro.config.mjs
|
||||
import { defineConfig } from 'astro/config';
|
||||
import deno from '@astrojs/deno';
|
||||
|
||||
export default defineConfig({
|
||||
output: 'server',
|
||||
adapter: deno({
|
||||
//options go here
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
### start
|
||||
|
||||
This adapter automatically starts a server when it is imported. You can turn this off with the `start` option:
|
||||
|
||||
```js
|
||||
import { defineConfig } from 'astro/config';
|
||||
import deno from '@astrojs/deno';
|
||||
|
||||
export default defineConfig({
|
||||
output: 'server',
|
||||
adapter: deno({
|
||||
start: false,
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
If you disable this, you need to write your own Deno web server. Import and call `handle` from the generated entry script to render requests:
|
||||
|
||||
```ts
|
||||
import { serve } from 'https://deno.land/std@0.167.0/http/server.ts';
|
||||
import { handle } from './dist/server/entry.mjs';
|
||||
|
||||
serve((req: Request) => {
|
||||
// Check the request, maybe do static file handling here.
|
||||
|
||||
return handle(req);
|
||||
});
|
||||
```
|
||||
|
||||
### port and hostname
|
||||
|
||||
You can set the port (default: `8085`) and hostname (default: `0.0.0.0`) for the deno server to use. If `start` is false, this has no effect; your own server must configure the port and hostname.
|
||||
|
||||
```js
|
||||
import { defineConfig } from 'astro/config';
|
||||
import deno from '@astrojs/deno';
|
||||
|
||||
export default defineConfig({
|
||||
output: 'server',
|
||||
adapter: deno({
|
||||
port: 8081,
|
||||
hostname: 'myhost',
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
The [Astro Deno](https://github.com/withastro/astro/tree/main/examples/deno) example includes a `preview` command that runs the entry script directly. Run `npm run build` then `npm run preview` to run the production deno server.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For help, check out the `#support` channel on [Discord](https://astro.build/chat). Our friendly Support Squad members are here to help!
|
||||
|
||||
You can also check our [Astro Integration Documentation][astro-integration] for more on integrations.
|
||||
|
||||
## Contributing
|
||||
|
||||
This package is maintained by Astro's Core team. You're welcome to submit an issue or PR!
|
||||
|
||||
## Changelog
|
||||
|
||||
See [CHANGELOG.md](CHANGELOG.md) for a history of changes to this integration.
|
||||
|
||||
[astro-integration]: https://docs.astro.build/en/guides/integrations-guide/
|
||||
This adapter is no longer maintained by Astro. Please see [the new repository for the Deno adapter](https://github.com/withastro/netlify-adapter) which is now maintained by the Deno organization.
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
{
|
||||
"name": "@astrojs/deno",
|
||||
"description": "Deploy your site to a Deno server",
|
||||
"version": "5.0.1",
|
||||
"type": "module",
|
||||
"types": "./dist/index.d.ts",
|
||||
"author": "withastro",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/withastro/astro.git",
|
||||
"directory": "packages/integrations/deno"
|
||||
},
|
||||
"keywords": [
|
||||
"withastro",
|
||||
"astro-adapter"
|
||||
],
|
||||
"bugs": "https://github.com/withastro/astro/issues",
|
||||
"homepage": "https://docs.astro.build/en/guides/integrations-guide/deno/",
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./server.js": "./dist/server.js",
|
||||
"./__deno_imports.js": "./dist/__deno_imports.js",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "astro-scripts build \"src/**/*.ts\" && tsc",
|
||||
"build:ci": "astro-scripts build \"src/**/*.ts\"",
|
||||
"dev": "astro-scripts dev \"src/**/*.ts\"",
|
||||
"test": "deno test --allow-run --allow-env --allow-read --allow-net ./test/"
|
||||
},
|
||||
"dependencies": {
|
||||
"esbuild": "^0.19.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"astro": "workspace:^3.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"astro": "workspace:*",
|
||||
"astro-scripts": "workspace:*"
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
// This file is a shim for any Deno-specific imports!
|
||||
// It will be replaced in the final Deno build.
|
||||
//
|
||||
// This allows us to prerender pages in Node.
|
||||
export class Server {
|
||||
listenAndServe() {}
|
||||
}
|
||||
|
||||
export function serveFile() {}
|
||||
export function fromFileUrl() {}
|
|
@ -1,196 +0,0 @@
|
|||
import type { AstroAdapter, AstroIntegration } from 'astro';
|
||||
import esbuild from 'esbuild';
|
||||
import * as fs from 'node:fs';
|
||||
import * as npath from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import type { BuildConfig, Options } from './types';
|
||||
|
||||
const SHIM = `globalThis.process = {
|
||||
argv: [],
|
||||
env: Deno.env.toObject(),
|
||||
};`;
|
||||
|
||||
const DENO_VERSION = `0.177.0`;
|
||||
// REF: https://github.com/denoland/deno/tree/main/ext/node/polyfills
|
||||
const COMPATIBLE_NODE_MODULES = [
|
||||
'assert',
|
||||
'assert/strict',
|
||||
'async_hooks',
|
||||
'buffer',
|
||||
'child_process',
|
||||
'cluster',
|
||||
'console',
|
||||
'constants',
|
||||
'crypto',
|
||||
'dgram',
|
||||
'diagnostics_channel',
|
||||
'dns',
|
||||
'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',
|
||||
'url',
|
||||
'util',
|
||||
'util/types',
|
||||
// 'v8',
|
||||
// 'vm',
|
||||
// 'wasi',
|
||||
// 'webcrypto',
|
||||
'worker_threads',
|
||||
'zlib',
|
||||
];
|
||||
|
||||
// We shim deno-specific imports so we can run the code in Node
|
||||
// to prerender pages. In the final Deno build, this import is
|
||||
// replaced with the Deno-specific contents listed below.
|
||||
const DENO_IMPORTS_SHIM = `@astrojs/deno/__deno_imports.js`;
|
||||
const DENO_IMPORTS = `export { Server } from "https://deno.land/std@${DENO_VERSION}/http/server.ts"
|
||||
export { serveFile } from 'https://deno.land/std@${DENO_VERSION}/http/file_server.ts';
|
||||
export { fromFileUrl } from "https://deno.land/std@${DENO_VERSION}/path/mod.ts";`;
|
||||
|
||||
export function getAdapter(args?: Options): AstroAdapter {
|
||||
return {
|
||||
name: '@astrojs/deno',
|
||||
serverEntrypoint: '@astrojs/deno/server.js',
|
||||
args: args ?? {},
|
||||
exports: ['stop', 'handle', 'start', 'running'],
|
||||
supportedAstroFeatures: {
|
||||
hybridOutput: 'stable',
|
||||
staticOutput: 'stable',
|
||||
serverOutput: 'stable',
|
||||
assets: {
|
||||
supportKind: 'stable',
|
||||
isSharpCompatible: false,
|
||||
isSquooshCompatible: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const denoImportsShimPlugin = {
|
||||
name: '@astrojs/deno:shim',
|
||||
setup(build: esbuild.PluginBuild) {
|
||||
build.onLoad({ filter: /__deno_imports\.js$/ }, async () => {
|
||||
return {
|
||||
contents: DENO_IMPORTS,
|
||||
loader: 'js',
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const denoRenameNodeModulesPlugin = {
|
||||
name: '@astrojs/esbuild-rename-node-modules',
|
||||
setup(build: esbuild.PluginBuild) {
|
||||
const filter = new RegExp(COMPATIBLE_NODE_MODULES.map((mod) => `(^${mod}$)`).join('|'));
|
||||
build.onResolve({ filter }, (args) => ({ path: 'node:' + args.path, external: true }));
|
||||
},
|
||||
};
|
||||
|
||||
export default function createIntegration(args?: Options): AstroIntegration {
|
||||
let _buildConfig: BuildConfig;
|
||||
let _vite: any;
|
||||
return {
|
||||
name: '@astrojs/deno',
|
||||
hooks: {
|
||||
'astro:config:done': ({ setAdapter, config }) => {
|
||||
setAdapter(getAdapter(args));
|
||||
_buildConfig = config.build;
|
||||
|
||||
if (config.output === 'static') {
|
||||
console.warn(
|
||||
`[@astrojs/deno] \`output: "server"\` or \`output: "hybrid"\` is required to use this adapter.`
|
||||
);
|
||||
console.warn(
|
||||
`[@astrojs/deno] Otherwise, this adapter is not required to deploy a static site to Deno.`
|
||||
);
|
||||
}
|
||||
},
|
||||
'astro:build:setup': ({ vite, target }) => {
|
||||
if (target === 'server') {
|
||||
_vite = vite;
|
||||
vite.resolve = vite.resolve ?? {};
|
||||
vite.resolve.alias = vite.resolve.alias ?? {};
|
||||
vite.build = vite.build ?? {};
|
||||
vite.build.rollupOptions = vite.build.rollupOptions ?? {};
|
||||
vite.build.rollupOptions.external = vite.build.rollupOptions.external ?? [];
|
||||
|
||||
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 as Record<string, string>)[alias.find] = alias.replacement;
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(vite.build.rollupOptions.external)) {
|
||||
vite.build.rollupOptions.external.push(DENO_IMPORTS_SHIM);
|
||||
} else if (typeof vite.build.rollupOptions.external !== 'function') {
|
||||
vite.build.rollupOptions.external = [
|
||||
vite.build.rollupOptions.external,
|
||||
DENO_IMPORTS_SHIM,
|
||||
];
|
||||
}
|
||||
}
|
||||
},
|
||||
'astro:build:done': async () => {
|
||||
const entryUrl = new URL(_buildConfig.serverEntry, _buildConfig.server);
|
||||
const pth = fileURLToPath(entryUrl);
|
||||
|
||||
await esbuild.build({
|
||||
target: 'esnext',
|
||||
platform: 'browser',
|
||||
entryPoints: [pth],
|
||||
outfile: pth,
|
||||
allowOverwrite: true,
|
||||
format: 'esm',
|
||||
bundle: true,
|
||||
external: [
|
||||
...COMPATIBLE_NODE_MODULES.map((mod) => `node:${mod}`),
|
||||
'@astrojs/markdown-remark',
|
||||
],
|
||||
plugins: [denoImportsShimPlugin, denoRenameNodeModulesPlugin],
|
||||
banner: {
|
||||
js: SHIM,
|
||||
},
|
||||
});
|
||||
|
||||
// Remove chunks, if they exist. Since we have bundled via esbuild these chunks are trash.
|
||||
try {
|
||||
const chunkFileNames =
|
||||
_vite?.build?.rollupOptions?.output?.chunkFileNames ?? `chunks/chunk.[hash].mjs`;
|
||||
const chunkPath = npath.dirname(chunkFileNames);
|
||||
const chunksDirUrl = new URL(chunkPath + '/', _buildConfig.server);
|
||||
await fs.promises.rm(chunksDirUrl, { recursive: true, force: true });
|
||||
} catch {}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
// Normal Imports
|
||||
import type { SSRManifest } from 'astro';
|
||||
import { App } from 'astro/app';
|
||||
import type { Options } from './types';
|
||||
|
||||
// @ts-expect-error
|
||||
import { fromFileUrl, serveFile, Server } from '@astrojs/deno/__deno_imports.js';
|
||||
|
||||
let _server: Server | undefined = undefined;
|
||||
let _startPromise: Promise<void> | undefined = undefined;
|
||||
|
||||
async function* getPrerenderedFiles(clientRoot: URL): AsyncGenerator<URL> {
|
||||
// @ts-expect-error
|
||||
for await (const ent of Deno.readDir(clientRoot)) {
|
||||
if (ent.isDirectory) {
|
||||
yield* getPrerenderedFiles(new URL(`./${ent.name}/`, clientRoot));
|
||||
} else if (ent.name.endsWith('.html')) {
|
||||
yield new URL(`./${ent.name}`, clientRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function start(manifest: SSRManifest, options: Options) {
|
||||
if (options.start === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const clientRoot = new URL('../client/', import.meta.url);
|
||||
const app = new App(manifest);
|
||||
const handler = async (request: Request, connInfo: any) => {
|
||||
if (app.match(request)) {
|
||||
let ip = connInfo?.remoteAddr?.hostname;
|
||||
Reflect.set(request, Symbol.for('astro.clientAddress'), ip);
|
||||
const response = await app.render(request);
|
||||
if (app.setCookieHeaders) {
|
||||
for (const setCookieHeader of app.setCookieHeaders(response)) {
|
||||
response.headers.append('Set-Cookie', setCookieHeader);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
// If the request path wasn't found in astro,
|
||||
// try to fetch a static file instead
|
||||
const url = new URL(request.url);
|
||||
const localPath = new URL('./' + app.removeBase(url.pathname), clientRoot);
|
||||
|
||||
let fileResp = await serveFile(request, fromFileUrl(localPath));
|
||||
|
||||
// Attempt to serve `index.html` if 404
|
||||
if (fileResp.status == 404) {
|
||||
let fallback;
|
||||
for await (const file of getPrerenderedFiles(clientRoot)) {
|
||||
const pathname = file.pathname.replace(/\/(index)?\.html$/, '');
|
||||
if (localPath.pathname.endsWith(pathname)) {
|
||||
fallback = file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fallback) {
|
||||
fileResp = await serveFile(request, fromFileUrl(fallback));
|
||||
}
|
||||
}
|
||||
|
||||
// If the static file can't be found
|
||||
if (fileResp.status == 404) {
|
||||
// Render the astro custom 404 page
|
||||
const response = await app.render(request);
|
||||
|
||||
if (app.setCookieHeaders) {
|
||||
for (const setCookieHeader of app.setCookieHeaders(response)) {
|
||||
response.headers.append('Set-Cookie', setCookieHeader);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
|
||||
// If the static file is found
|
||||
} else {
|
||||
return fileResp;
|
||||
}
|
||||
};
|
||||
|
||||
const port = options.port ?? 8085;
|
||||
_server = new Server({
|
||||
port,
|
||||
hostname: options.hostname ?? '0.0.0.0',
|
||||
handler,
|
||||
});
|
||||
|
||||
_startPromise = Promise.resolve(_server.listenAndServe());
|
||||
console.error(`Server running on port ${port}`);
|
||||
}
|
||||
|
||||
export function createExports(manifest: SSRManifest, options: Options) {
|
||||
const app = new App(manifest);
|
||||
return {
|
||||
async stop() {
|
||||
if (_server) {
|
||||
_server.close();
|
||||
_server = undefined;
|
||||
}
|
||||
await Promise.resolve(_startPromise);
|
||||
},
|
||||
running() {
|
||||
return _server !== undefined;
|
||||
},
|
||||
async start() {
|
||||
return start(manifest, options);
|
||||
},
|
||||
async handle(request: Request) {
|
||||
return app.render(request);
|
||||
},
|
||||
};
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
export interface Options {
|
||||
port?: number;
|
||||
hostname?: string;
|
||||
start?: boolean;
|
||||
}
|
||||
|
||||
export interface BuildConfig {
|
||||
server: URL;
|
||||
serverEntry: string;
|
||||
assets: string;
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
/* Deno types consider DOM elements nullable */
|
||||
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
|
||||
import { DOMParser } from 'https://deno.land/x/deno_dom@v0.1.35-alpha/deno-dom-wasm.ts';
|
||||
import { assert, assertEquals } from 'https://deno.land/std@0.158.0/testing/asserts.ts';
|
||||
import { runBuildAndStartApp, defaultTestPermissions } from './helpers.ts';
|
||||
|
||||
// this needs to be here and not in the specific test case, because
|
||||
// the variables are loaded in the global scope of the built server
|
||||
// module, which is only executed once upon the first load
|
||||
const varContent = 'this is a value stored in env variable';
|
||||
Deno.env.set('SOME_VARIABLE', varContent);
|
||||
|
||||
Deno.test({
|
||||
name: 'Basics',
|
||||
permissions: defaultTestPermissions,
|
||||
sanitizeResources: false,
|
||||
sanitizeOps: false,
|
||||
async fn(t) {
|
||||
const app = await runBuildAndStartApp('./fixtures/basics/');
|
||||
|
||||
await t.step('Works', async () => {
|
||||
const resp = await fetch(app.url);
|
||||
assertEquals(resp.status, 200);
|
||||
|
||||
const html = await resp.text();
|
||||
assert(html);
|
||||
|
||||
const doc = new DOMParser().parseFromString(html, `text/html`);
|
||||
const div = doc!.querySelector('#react');
|
||||
|
||||
assert(div, 'div exists');
|
||||
});
|
||||
|
||||
await t.step('Custom 404', async () => {
|
||||
const resp = await fetch(new URL('this-does-not-exist', app.url));
|
||||
assertEquals(resp.status, 404);
|
||||
|
||||
const html = await resp.text();
|
||||
assert(html);
|
||||
|
||||
const doc = new DOMParser().parseFromString(html, `text/html`);
|
||||
const header = doc!.querySelector('#custom-404');
|
||||
assert(header, 'displays custom 404');
|
||||
});
|
||||
|
||||
await t.step('Loads style assets', async () => {
|
||||
let resp = await fetch(app.url);
|
||||
const html = await resp.text();
|
||||
|
||||
const doc = new DOMParser().parseFromString(html, `text/html`);
|
||||
const style = doc!.querySelector('style');
|
||||
|
||||
assert(style?.textContent?.includes('Courier New'));
|
||||
});
|
||||
|
||||
await t.step('Correctly loads run-time env variables', async () => {
|
||||
const resp = await fetch(app.url);
|
||||
const html = await resp.text();
|
||||
|
||||
const doc = new DOMParser().parseFromString(html, `text/html`);
|
||||
const p = doc!.querySelector('p#env-value');
|
||||
assertEquals(p!.innerText, varContent);
|
||||
});
|
||||
|
||||
await t.step('Can use a module with top-level await', async () => {
|
||||
const resp = await fetch(app.url);
|
||||
const html = await resp.text();
|
||||
|
||||
const doc = new DOMParser().parseFromString(html, `text/html`);
|
||||
const p = doc!.querySelector('p#module-value');
|
||||
assertEquals(p!.innerText, 'bar');
|
||||
});
|
||||
|
||||
await t.step('Works with Markdown', async () => {
|
||||
const resp = await fetch(new URL('markdown', app.url));
|
||||
const html = await resp.text();
|
||||
|
||||
const doc = new DOMParser().parseFromString(html, `text/html`);
|
||||
const h1 = doc!.querySelector('h1');
|
||||
assertEquals(h1!.innerText, 'Heading from Markdown');
|
||||
});
|
||||
|
||||
await t.step('Works with MDX', async () => {
|
||||
const resp = await fetch(new URL('mdx', app.url));
|
||||
const html = await resp.text();
|
||||
|
||||
const doc = new DOMParser().parseFromString(html, `text/html`);
|
||||
const h1 = doc!.querySelector('h1');
|
||||
assertEquals(h1!.innerText, 'Heading from MDX');
|
||||
});
|
||||
|
||||
await t.step('Astro.cookies', async () => {
|
||||
const url = new URL('/admin', app.url);
|
||||
const resp = await fetch(url, { redirect: 'manual' });
|
||||
assertEquals(resp.status, 302);
|
||||
|
||||
const headers = resp.headers;
|
||||
assertEquals(headers.get('set-cookie'), 'logged-in=false; Max-Age=77760000; Path=/');
|
||||
});
|
||||
|
||||
await t.step('perendering', async () => {
|
||||
const resp = await fetch(new URL('/prerender', app.url));
|
||||
assertEquals(resp.status, 200);
|
||||
|
||||
const html = await resp.text();
|
||||
assert(html);
|
||||
|
||||
const doc = new DOMParser().parseFromString(html, `text/html`);
|
||||
const h1 = doc!.querySelector('h1');
|
||||
assertEquals(h1!.innerText, 'test');
|
||||
});
|
||||
|
||||
await t.step('node compatibility', async () => {
|
||||
const resp = await fetch(new URL('/nodecompat', app.url));
|
||||
assertEquals(resp.status, 200);
|
||||
await resp.text();
|
||||
});
|
||||
|
||||
app.stop();
|
||||
},
|
||||
});
|
|
@ -1,24 +0,0 @@
|
|||
/* Deno types consider DOM elements nullable */
|
||||
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
|
||||
import { DOMParser } from 'https://deno.land/x/deno_dom@v0.1.35-alpha/deno-dom-wasm.ts';
|
||||
import { assert, assertEquals } from 'https://deno.land/std@0.158.0/testing/asserts.ts';
|
||||
import { runBuildAndStartAppFromSubprocess } from './helpers.ts';
|
||||
|
||||
Deno.test({
|
||||
name: 'Dynamic import',
|
||||
async fn(t) {
|
||||
const app = await runBuildAndStartAppFromSubprocess('./fixtures/dynimport/');
|
||||
|
||||
await t.step('Works', async () => {
|
||||
const resp = await fetch(app.url);
|
||||
assertEquals(resp.status, 200);
|
||||
const html = await resp.text();
|
||||
assert(html);
|
||||
const doc = new DOMParser().parseFromString(html, `text/html`);
|
||||
const div = doc!.querySelector('#thing');
|
||||
assert(div, 'div exists');
|
||||
});
|
||||
|
||||
app.stop();
|
||||
},
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import deno from '@astrojs/deno';
|
||||
import react from '@astrojs/react';
|
||||
import mdx from '@astrojs/mdx';
|
||||
|
||||
export default defineConfig({
|
||||
adapter: deno(),
|
||||
integrations: [react(), mdx()],
|
||||
output: 'server'
|
||||
})
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"name": "@test/deno-astro-basic",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"astro": "workspace:*",
|
||||
"@astrojs/deno": "workspace:*",
|
||||
"@astrojs/react": "workspace:*",
|
||||
"@astrojs/mdx": "workspace:*",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0"
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
export default function() {
|
||||
return (
|
||||
<div id="react">testing</div>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
<h1 id="custom-404">Custom 404 Page</h1>
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
Astro.cookies.set('logged-in', false, {
|
||||
maxAge: 60 * 60 * 24 * 900,
|
||||
path: '/'
|
||||
});
|
||||
|
||||
return Astro.redirect('/login');
|
||||
---
|
|
@ -1,17 +0,0 @@
|
|||
---
|
||||
import { someData } from '../util/data';
|
||||
import ReactComponent from '../components/React.jsx';
|
||||
const envValue = import.meta.env.SOME_VARIABLE;
|
||||
---
|
||||
<html>
|
||||
<head>
|
||||
<title>Basic App on Deno</title>
|
||||
<style>body { font-family: 'Courier New', Courier, monospace; }</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Basic App on Deno</h1>
|
||||
<p id="env-value">{envValue}</p>
|
||||
<p id="module-value">{someData.foo}</p>
|
||||
<ReactComponent />
|
||||
</body>
|
||||
</html>
|
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
---
|
||||
<html>
|
||||
<head>
|
||||
<title>Testing</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Testing</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
title: Title
|
||||
description: Description
|
||||
---
|
||||
|
||||
# Heading from Markdown
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
title: Title
|
||||
description: Description
|
||||
---
|
||||
|
||||
# Heading from MDX
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
// unprefixed node built-in module
|
||||
import path from 'node:path'
|
||||
|
||||
// prefixed node built-in module
|
||||
import os from 'node:os'
|
||||
---
|
||||
<body>
|
||||
<a href={path.posix.basename('/public/myfile.html')}>Go to my file</a>
|
||||
<details>
|
||||
<summary>CPU Architecture</summary>
|
||||
<code>{os.arch()}</code>
|
||||
</details>
|
||||
<p>Everything went fine.</p>
|
||||
</body>
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
export const prerender = true;
|
||||
---
|
||||
|
||||
<html>
|
||||
<body>
|
||||
<h1>test</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -1,14 +0,0 @@
|
|||
export interface Data {
|
||||
foo: string;
|
||||
}
|
||||
|
||||
export async function getData(): Promise<Data> {
|
||||
return new Promise((resolve, _reject) => {
|
||||
setTimeout(() => {
|
||||
resolve({ foo: "bar" });
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
|
||||
// Testing top-level await, a feature supported in esnext
|
||||
export const someData = await getData();
|
|
@ -1,7 +0,0 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import deno from '@astrojs/deno';
|
||||
|
||||
export default defineConfig({
|
||||
adapter: deno(),
|
||||
output: 'server'
|
||||
})
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"name": "@test/deno-astro-dynimport",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"astro": "workspace:*",
|
||||
"@astrojs/deno": "workspace:*"
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
|
||||
---
|
||||
<div id="thing">testing</div>
|
|
@ -1,11 +0,0 @@
|
|||
---
|
||||
const { default: Thing } = await import('../components/Thing.astro');
|
||||
---
|
||||
<html>
|
||||
<head>
|
||||
<title>testing</title>
|
||||
</head>
|
||||
<body>
|
||||
<Thing />
|
||||
</body>
|
||||
</html>
|
|
@ -1,77 +0,0 @@
|
|||
import { fromFileUrl } from 'https://deno.land/std@0.110.0/path/mod.ts';
|
||||
import { assert } from 'https://deno.land/std@0.158.0/testing/asserts.ts';
|
||||
import { readableStreamFromReader } from 'https://deno.land/std@0.142.0/streams/conversion.ts';
|
||||
|
||||
const dir = new URL('./', import.meta.url);
|
||||
const defaultURL = new URL('http://localhost:8085/');
|
||||
|
||||
export const defaultTestPermissions: Deno.PermissionOptions = {
|
||||
read: true,
|
||||
net: true,
|
||||
run: true,
|
||||
env: true,
|
||||
};
|
||||
|
||||
declare type ExitCallback = () => void;
|
||||
|
||||
export async function runBuild(fixturePath: string) {
|
||||
const proc = Deno.run({
|
||||
cmd: ['node', '../../../../../astro/astro.js', 'build', '--silent'],
|
||||
cwd: fromFileUrl(new URL(fixturePath, dir)),
|
||||
});
|
||||
try {
|
||||
const status = await proc.status();
|
||||
assert(status.success);
|
||||
} finally {
|
||||
proc.close();
|
||||
}
|
||||
}
|
||||
|
||||
export async function startModFromImport(baseUrl: URL): Promise<ExitCallback> {
|
||||
const entryUrl = new URL('./dist/server/entry.mjs', baseUrl);
|
||||
const mod = await import(entryUrl.toString());
|
||||
|
||||
if (!mod.running()) {
|
||||
mod.start();
|
||||
}
|
||||
|
||||
return () => mod.stop();
|
||||
}
|
||||
|
||||
export async function startModFromSubprocess(baseUrl: URL): Promise<ExitCallback> {
|
||||
const entryUrl = new URL('./dist/server/entry.mjs', baseUrl);
|
||||
const proc = Deno.run({
|
||||
cmd: ['deno', 'run', '--allow-env', '--allow-net', fromFileUrl(entryUrl)],
|
||||
cwd: fromFileUrl(baseUrl),
|
||||
stderr: 'piped',
|
||||
});
|
||||
|
||||
const stderr = readableStreamFromReader(proc.stderr);
|
||||
const dec = new TextDecoder();
|
||||
for await (const bytes of stderr) {
|
||||
const msg = dec.decode(bytes);
|
||||
if (msg.includes(`Server running`)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return () => proc.close();
|
||||
}
|
||||
|
||||
export async function runBuildAndStartApp(fixturePath: string) {
|
||||
const url = new URL(fixturePath, dir);
|
||||
|
||||
await runBuild(fixturePath);
|
||||
const stop = await startModFromImport(url);
|
||||
|
||||
return { url: defaultURL, stop };
|
||||
}
|
||||
|
||||
export async function runBuildAndStartAppFromSubprocess(fixturePath: string) {
|
||||
const url = new URL(fixturePath, dir);
|
||||
|
||||
await runBuild(fixturePath);
|
||||
const stop = await startModFromSubprocess(url);
|
||||
|
||||
return { url: defaultURL, stop };
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"include": ["src"],
|
||||
"compilerOptions": {
|
||||
"module": "ES2022",
|
||||
"outDir": "./dist",
|
||||
// TODO: Due to the shim for Deno imports in `server.ts`, we can't use moduleResolution: 'node16' or the types get very weird.
|
||||
"moduleResolution": "Node"
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue