Skip to content

Commit

Permalink
feat: deno adapter/middleware integration (QwikDev#3936)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdbradley authored Apr 27, 2023
1 parent 477ef90 commit cb90d2b
Show file tree
Hide file tree
Showing 13 changed files with 382 additions and 2 deletions.
13 changes: 13 additions & 0 deletions @types/deno.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
declare module 'https://deno.land/std/path/mod.ts' {
export function extname(paths: string): string;
export function fromFileUrl(url: string): string;
export function join(...paths: string[]): string;
}

declare const Deno: {
env: any;
readTextFile(path: string): Promise<string>;
version: {
deno: string;
};
};
23 changes: 23 additions & 0 deletions packages/qwik-city/adapters/deno-server/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## API Report File for "@builder.io/qwik-city"

> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts

import { ServerAdapterOptions } from '../../shared/vite';
import type { StaticGenerateRenderOptions } from '@builder.io/qwik-city/static';

// @alpha (undocumented)
export function denoServerAdapter(opts?: DenoServerAdapterOptions): any;

// @alpha (undocumented)
export interface DenoServerAdapterOptions extends ServerAdapterOptions {
// (undocumented)
name?: string;
}

export { StaticGenerateRenderOptions }

// (No @packageDocumentation comment for this package)

```
15 changes: 15 additions & 0 deletions packages/qwik-city/adapters/deno-server/vite/api-extractor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../../api-extractor.json",
"mainEntryPointFilePath": "<projectFolder>/dist-dev/dts-out/packages/qwik-city/adapters/deno-server/vite/index.d.ts",
"apiReport": {
"enabled": true,
"reportFileName": "api.md",
"reportFolder": "<projectFolder>/packages/qwik-city/adapters/deno-server/",
"reportTempFolder": "<projectFolder>/dist-dev/api-extractor/qwik-city/adapters/deno-server"
},
"dtsRollup": {
"enabled": true,
"untrimmedFilePath": "<projectFolder>/packages/qwik-city/lib/adapters/deno-server/vite/index.d.ts"
}
}
50 changes: 50 additions & 0 deletions packages/qwik-city/adapters/deno-server/vite/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { StaticGenerateRenderOptions } from '@builder.io/qwik-city/static';
import { viteAdapter, type ServerAdapterOptions } from '../../shared/vite';

/**
* @alpha
*/
export function denoServerAdapter(opts: DenoServerAdapterOptions = {}): any {
const env = process?.env;
return viteAdapter({
name: opts.name || 'deno-server',
origin: env?.ORIGIN ?? env?.URL ?? 'https://yoursitename.qwik.builder.io',
ssg: opts.ssg,
cleanStaticGenerated: true,

config() {
return {
resolve: {
conditions: ['webworker', 'worker'],
},
ssr: {
target: 'webworker',
noExternal: true,
},
build: {
ssr: true,
target: 'esnext',
rollupOptions: {
output: {
format: 'es',
hoistTransitiveImports: false,
},
},
},
publicDir: false,
};
},
});
}

/**
* @alpha
*/
export interface DenoServerAdapterOptions extends ServerAdapterOptions {
name?: string;
}

/**
* @alpha
*/
export type { StaticGenerateRenderOptions };
15 changes: 15 additions & 0 deletions packages/qwik-city/middleware/deno/api-extractor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../api-extractor.json",
"mainEntryPointFilePath": "<projectFolder>/dist-dev/dts-out/packages/qwik-city/middleware/deno/index.d.ts",
"apiReport": {
"enabled": true,
"reportFileName": "api.md",
"reportFolder": "<projectFolder>/packages/qwik-city/middleware/deno/",
"reportTempFolder": "<projectFolder>/dist-dev/api-extractor/qwik-city/middleware/deno"
},
"dtsRollup": {
"enabled": true,
"untrimmedFilePath": "<projectFolder>/packages/qwik-city/lib/middleware/deno/index.d.ts"
}
}
26 changes: 26 additions & 0 deletions packages/qwik-city/middleware/deno/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
## API Report File for "@builder.io/qwik-city"

> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts

import type { ServerRenderOptions } from '@builder.io/qwik-city/middleware/request-handler';

// @public (undocumented)
export function createQwikCity(opts: QwikCityDenoOptions): {
router: (request: Request) => Promise<Response | null>;
notFound: (request: Request) => Promise<Response>;
staticFile: (request: Request) => Promise<Response | null>;
};

// @public (undocumented)
export interface QwikCityDenoOptions extends ServerRenderOptions {
static?: {
root?: string;
cacheControl?: string;
};
}

// (No @packageDocumentation comment for this package)

```
162 changes: 162 additions & 0 deletions packages/qwik-city/middleware/deno/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import type {
ServerRenderOptions,
ServerRequestEvent,
} from '@builder.io/qwik-city/middleware/request-handler';
import {
mergeHeadersCookies,
requestHandler,
} from '@builder.io/qwik-city/middleware/request-handler';
import { getNotFound } from '@qwik-city-not-found-paths';
import { isStaticPath } from '@qwik-city-static-paths';
import { _deserializeData, _serializeData, _verifySerializable } from '@builder.io/qwik';
import { setServerPlatform } from '@builder.io/qwik/server';
import { MIME_TYPES } from '../request-handler/mime-types';
import { extname, fromFileUrl, join } from 'https://deno.land/std/path/mod.ts';

// @builder.io/qwik-city/middleware/deno

/**
* @public
*/
export function createQwikCity(opts: QwikCityDenoOptions) {
const qwikSerializer = {
_deserializeData,
_serializeData,
_verifySerializable,
};
if (opts.manifest) {
setServerPlatform(opts.manifest);
}

const staticFolder = opts.static?.root ?? join(fromFileUrl(import.meta.url), '..', '..', 'dist');

async function router(request: Request) {
try {
const url = new URL(request.url);

const serverRequestEv: ServerRequestEvent<Response> = {
mode: 'server',
locale: undefined,
url,
env: Deno.env,
request,
getWritableStream: (status, headers, cookies, resolve) => {
const { readable, writable } = new TransformStream<Uint8Array>();
const response = new Response(readable, {
status,
headers: mergeHeadersCookies(headers, cookies),
});
resolve(response);
return writable;
},
platform: {
ssr: true,
deno: Deno.version.deno,
},
};

// send request to qwik city request handler
const handledResponse = await requestHandler(serverRequestEv, opts, qwikSerializer);
if (handledResponse) {
handledResponse.completion.then((v) => {
if (v) {
console.error(v);
}
});
const response = await handledResponse.response;
if (response) {
return response;
}
}

// qwik city did not have a route for this request
return null;
} catch (e: any) {
console.error(e);
return new Response(String(e || 'Error'), {
status: 500,
headers: { 'Content-Type': 'text/plain; charset=utf-8', 'X-Error': 'deno-server' },
});
}
}

const notFound = async (request: Request) => {
try {
const url = new URL(request.url);
const notFoundHtml = getNotFound(url.pathname);
return new Response(notFoundHtml, {
status: 404,
headers: { 'Content-Type': 'text/html; charset=utf-8', 'X-Not-Found': url.pathname },
});
} catch (e) {
console.error(e);
return new Response(String(e || 'Error'), {
status: 500,
headers: { 'Content-Type': 'text/plain; charset=utf-8', 'X-Error': 'deno-server' },
});
}
};

const readStaticFile = async (url: URL) => {
const parts = url.pathname.split('/');
const fileName = parts[parts.length - 1];
let filePath: string;
if (fileName.includes('.')) {
filePath = join(staticFolder, url.pathname);
} else if (opts.qwikCityPlan.trailingSlash) {
filePath = join(staticFolder, url.pathname + 'index.html');
} else {
filePath = join(staticFolder, url.pathname, 'index.html');
}
return {
filePath,
content: await Deno.readTextFile(filePath),
};
};

const staticFile = async (request: Request) => {
try {
const url = new URL(request.url);

if (isStaticPath(request.method || 'GET', url)) {
const { filePath, content } = await readStaticFile(url);
const ext = extname(filePath).replace(/^\./, '');

return new Response(content, {
status: 200,
headers: {
'content-type': MIME_TYPES[ext] || 'text/plain; charset=utf-8',
'Cache-Control': opts.static?.cacheControl || 'max-age=3600',
},
});
}

return null;
} catch (e) {
console.error(e);
return new Response(String(e || 'Error'), {
status: 500,
headers: { 'Content-Type': 'text/plain; charset=utf-8', 'X-Error': 'deno-server' },
});
}
};

return {
router,
notFound,
staticFile,
};
}

/**
* @public
*/
export interface QwikCityDenoOptions extends ServerRenderOptions {
/** Options for serving static files */
static?: {
/** The root folder for statics files. Defaults to /dist */
root?: string;
/** Set the Cache-Control header for all static files */
cacheControl?: string;
};
}
2 changes: 1 addition & 1 deletion packages/qwik-city/middleware/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { IncomingMessage, ServerResponse } from 'node:http';
import { extname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { fromNodeHttp, getUrl } from './http';
import { MIME_TYPES } from './mime-types';
import { MIME_TYPES } from '../request-handler/mime-types';
import { patchGlobalThis } from './node-fetch';
import { _deserializeData, _serializeData, _verifySerializable } from '@builder.io/qwik';

Expand Down
7 changes: 7 additions & 0 deletions packages/qwik-city/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
"import": "./lib/adapters/cloud-run/vite/index.mjs",
"require": "./lib/adapters/cloud-run/vite/index.cjs"
},
"./adapters/deno-server/vite": {
"import": "./lib/adapters/deno-server/vite/index.mjs",
"require": "./lib/adapters/deno-server/vite/index.cjs"
},
"./adapters/node-server/vite": {
"import": "./lib/adapters/node-server/vite/index.mjs",
"require": "./lib/adapters/node-server/vite/index.cjs"
Expand All @@ -50,6 +54,9 @@
"./middleware/cloudflare-pages": {
"import": "./lib/middleware/cloudflare-pages/index.mjs"
},
"./middleware/deno": {
"import": "./lib/middleware/deno/index.mjs"
},
"./middleware/netlify-edge": {
"import": "./lib/middleware/netlify-edge/index.mjs"
},
Expand Down
10 changes: 10 additions & 0 deletions scripts/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ export function apiExtractor(config: BuildConfig) {
join(config.packagesDir, 'qwik-city', 'adapters', 'cloud-run', 'vite'),
join(config.packagesDir, 'qwik-city', 'lib', 'adapters', 'cloud-run', 'vite', 'index.d.ts')
);
createTypesApi(
config,
join(config.packagesDir, 'qwik-city', 'adapters', 'deno-server', 'vite'),
join(config.packagesDir, 'qwik-city', 'lib', 'adapters', 'deno-server', 'vite', 'index.d.ts')
);
createTypesApi(
config,
join(config.packagesDir, 'qwik-city', 'adapters', 'node-server', 'vite'),
Expand Down Expand Up @@ -117,6 +122,11 @@ export function apiExtractor(config: BuildConfig) {
join(config.packagesDir, 'qwik-city', 'middleware', 'cloudflare-pages'),
join(config.packagesDir, 'qwik-city', 'lib', 'middleware', 'cloudflare-pages', 'index.d.ts')
);
createTypesApi(
config,
join(config.packagesDir, 'qwik-city', 'middleware', 'deno'),
join(config.packagesDir, 'qwik-city', 'lib', 'middleware', 'deno', 'index.d.ts')
);
createTypesApi(
config,
join(config.packagesDir, 'qwik-city', 'middleware', 'netlify-edge'),
Expand Down
Loading

0 comments on commit cb90d2b

Please sign in to comment.