Start of devapp exploration

This commit is contained in:
Matthew Phillips 2023-02-10 09:12:24 -05:00
parent 8b7cb64dad
commit 4ce0b918b1
5 changed files with 148 additions and 28 deletions

View file

@ -0,0 +1,88 @@
import type { ManifestData, RouteData } from '../../@types/astro';
import type { SSRManifest as Manifest } from './types';
import { createContainer, type CreateContainerParams } from '../dev/index.js';
import { createViteLoader } from '../module-loader/index.js';
import { createRouteManifest } from '../routing/index.js';
import { createDevelopmentEnvironment, preload, type DevelopmentEnvironment } from '../render/dev/index.js';
import { App, MatchOptions } from './index.js';
export type DevAppParams = Partial<CreateContainerParams> & {
root: URL;
}
export class DevApp extends App {
#createContainerParams: CreateContainerParams;
#manifest: Manifest;
#container: Awaited<ReturnType<typeof createContainer>> | null = null;
#env: DevelopmentEnvironment | null = null;
#root: URL;
constructor(params: DevAppParams) {
const { root, userConfig } = params;
const manifest: Manifest = {
adapterName: 'development',
base: userConfig?.base,
routes: [],
markdown: {
contentDir: root
},
pageMap: new Map(),
renderers: [],
entryModules: {},
assets: new Set(),
propagation: new Map(),
trailingSlash: userConfig?.trailingSlash ?? 'ignore'
};
super(manifest, true);
this.#manifest = manifest;
this.#root = root;
this.#createContainerParams = params;
}
async load() {
const container = this.#container = await createContainer(this.#createContainerParams);
this.#manifest.trailingSlash = container.settings.config.trailingSlash;
const loader = createViteLoader(container.viteServer);
const routeManifest = createRouteManifest({
settings: container.settings,
fsMod: this.#createContainerParams.fs
}, container.logging);
const routes = routeManifest.routes.map(routeData => {
return {
routeData,
file: routeData.component,
links: [],
scripts: []
}
});
this.updateRoutes(routes);
this.#env = createDevelopmentEnvironment(container.settings, container.logging, loader);
return this;
}
async close() {
await this.#container?.close();
}
async render(request: Request, route?: RouteData | undefined): Promise<Response> {
if(!this.#env) {
await this.load();
}
if(!route) {
route = this.match(request, { matchNotFound: false });
}
if(route) {
const filePath = new URL(route.component, this.#root);
debugger;
const [renderers, mod] = await preload({
env: this.#env!,
filePath
});
this.#manifest.renderers.length = 0;
this.#manifest.renderers.push(...renderers);
this.#manifest.pageMap.set(route.component, mod);
}
return super.render(request, route);
}
}

View file

@ -38,8 +38,8 @@ export interface MatchOptions {
export class App {
#env: Environment;
#manifest: Manifest;
#manifestData: ManifestData;
#routeDataToRouteInfo: Map<RouteData, RouteInfo>;
#manifestData: ManifestData = { routes: [] };
#routeDataToRouteInfo: Map<RouteData, RouteInfo> = new Map();
#encoder = new TextEncoder();
#logging: LogOptions = {
dest: consoleLogDestination,
@ -50,10 +50,7 @@ export class App {
constructor(manifest: Manifest, streaming = true) {
this.#manifest = manifest;
this.#manifestData = {
routes: manifest.routes.map((route) => route.routeData),
};
this.#routeDataToRouteInfo = new Map(manifest.routes.map((route) => [route.routeData, route]));
this.updateRoutes(manifest.routes);
this.#env = createEnvironment({
adapterName: manifest.adapterName,
logging: this.#logging,
@ -84,6 +81,12 @@ export class App {
this.#base = this.#manifest.base || '/';
this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#base);
}
updateRoutes(routes: RouteInfo[]) {
this.#manifestData = {
routes: routes.map((route) => route.routeData),
};
this.#routeDataToRouteInfo = new Map(routes.map((route) => [route.routeData, route]));
}
removeBase(pathname: string) {
if (pathname.startsWith(this.#base)) {
return pathname.slice(this.#baseWithoutTrailingSlash.length + 1);
@ -96,7 +99,13 @@ export class App {
if (this.#manifest.assets.has(url.pathname)) {
return undefined;
}
let pathname = '/' + this.removeBase(url.pathname);
let noBase = this.removeBase(url.pathname);
let pathname: string;
if(this.#manifest.trailingSlash === 'never' && noBase === '') {
pathname = noBase;
} else {
pathname = prependForwardSlash(noBase);
}
let routeData = matchRoute(pathname, this.#manifestData);
if (routeData) {

View file

@ -215,6 +215,7 @@ function buildManifest(
renderers: [],
entryModules,
assets: staticFiles.map((s) => settings.config.base + s),
trailingSlash: opts.settings.config.trailingSlash,
};
return ssrManifest;

View file

@ -1,3 +1,3 @@
export { createContainer, isStarted, runInContainer, startContainer } from './container.js';
export { createContainer, isStarted, runInContainer, startContainer, type CreateContainerParams } from './container.js';
export { default } from './dev.js';
export { createContainerWithAutomaticRestart } from './restart.js';

View file

@ -1,6 +1,7 @@
import { expect } from 'chai';
import { runInContainer } from '../../../dist/core/dev/index.js';
import { DevApp } from '../../../dist/core/app/dev.js';
import { createFs, createRequestAndResponse } from '../test-utils.js';
const root = new URL('../../fixtures/alias/', import.meta.url);
@ -16,28 +17,22 @@ describe('base configuration', () => {
root
);
await runInContainer(
{
fs,
root,
userConfig: {
base: '/docs',
trailingSlash: 'never',
},
const app = new DevApp({
root,
fs,
userConfig: {
base: '/docs',
trailingSlash: 'never',
},
async (container) => {
const { req, res, done } = createRequestAndResponse({
method: 'GET',
url: '/docs/',
});
container.handle(req, res);
await done;
expect(res.statusCode).to.equal(404);
}
);
});
const request = new Request(`http://localhost:8080/docs/`);
const response = await app.render(request);
expect(response.status).to.equal(404);
});
it('Requests that exclude a trailing slash 200', async () => {
it.only('Requests that exclude a trailing slash 200', async () => {
const fs = createFs(
{
'/src/pages/index.astro': `<h1>testing</h1>`,
@ -45,6 +40,31 @@ describe('base configuration', () => {
root
);
const app = new DevApp({
root,
fs,
userConfig: {
base: '/docs',
trailingSlash: 'never',
},
});
try {
const request = new Request(`http://localhost:8080/docs`);
debugger;
const response = await app.render(request);
expect(response.status).to.equal(200);
console.log(await response.text());
} finally {
await app.close();
}
/*
await runInContainer(
{
fs,
@ -55,7 +75,7 @@ describe('base configuration', () => {
},
},
async (container) => {
const { req, res, done } = createRequestAndResponse({
const { req, res, done, text } = createRequestAndResponse({
method: 'GET',
url: '/docs',
});
@ -64,6 +84,8 @@ describe('base configuration', () => {
expect(res.statusCode).to.equal(200);
}
);
*/
});
});