This commit is contained in:
Matthew Phillips 2023-09-28 05:06:11 +08:00 committed by GitHub
parent 4ed410db50
commit 87d5b841af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 1 additions and 1436 deletions

File diff suppressed because one or more lines are too long

View file

@ -1,176 +1,3 @@
# @astrojs/deno 🦖 # @astrojs/deno 🦖
This adapter allows Astro to deploy your SSR site to Deno targets. 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.
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 projects dependencies using your preferred package manager. If youre using npm or arent 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/

View file

@ -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:*"
}
}

View file

@ -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() {}

View file

@ -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 {}
},
},
};
}

View file

@ -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);
},
};
}

View file

@ -1,11 +0,0 @@
export interface Options {
port?: number;
hostname?: string;
start?: boolean;
}
export interface BuildConfig {
server: URL;
serverEntry: string;
assets: string;
}

View file

@ -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();
},
});

View file

@ -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();
},
});

View file

@ -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'
})

View file

@ -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"
}
}

View file

@ -1,7 +0,0 @@
import React from 'react';
export default function() {
return (
<div id="react">testing</div>
);
}

View file

@ -1 +0,0 @@
<h1 id="custom-404">Custom 404 Page</h1>

View file

@ -1,8 +0,0 @@
---
Astro.cookies.set('logged-in', false, {
maxAge: 60 * 60 * 24 * 900,
path: '/'
});
return Astro.redirect('/login');
---

View file

@ -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>

View file

@ -1,10 +0,0 @@
---
---
<html>
<head>
<title>Testing</title>
</head>
<body>
<h1>Testing</h1>
</body>
</html>

View file

@ -1,6 +0,0 @@
---
title: Title
description: Description
---
# Heading from Markdown

View file

@ -1,6 +0,0 @@
---
title: Title
description: Description
---
# Heading from MDX

View file

@ -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>

View file

@ -1,9 +0,0 @@
---
export const prerender = true;
---
<html>
<body>
<h1>test</h1>
</body>
</html>

View file

@ -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();

View file

@ -1,7 +0,0 @@
import { defineConfig } from 'astro/config';
import deno from '@astrojs/deno';
export default defineConfig({
adapter: deno(),
output: 'server'
})

View file

@ -1,9 +0,0 @@
{
"name": "@test/deno-astro-dynimport",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/deno": "workspace:*"
}
}

View file

@ -1,4 +0,0 @@
---
---
<div id="thing">testing</div>

View file

@ -1,11 +0,0 @@
---
const { default: Thing } = await import('../components/Thing.astro');
---
<html>
<head>
<title>testing</title>
</head>
<body>
<Thing />
</body>
</html>

View file

@ -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 };
}

View file

@ -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"
}
}