Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
lilnasy committed Jan 23, 2024
1 parent 5994a90 commit e2e71ee
Show file tree
Hide file tree
Showing 21 changed files with 280 additions and 473 deletions.
7 changes: 3 additions & 4 deletions packages/astro/src/assets/build/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fs, { readFileSync } from 'node:fs';
import { basename, join } from 'node:path/posix';
import type PQueue from 'p-queue';
import type { AstroConfig } from '../../@types/astro.js';
import type { BuildPipeline } from '../../core/build/buildPipeline.js';
import type { BuildEnvironment } from '../../core/build/environment.js';
import { getOutDirWithinCwd } from '../../core/build/common.js';
import { getTimeStat } from '../../core/build/util.js';
import { AstroError } from '../../core/errors/errors.js';
Expand Down Expand Up @@ -47,11 +47,10 @@ type AssetEnv = {
type ImageData = { data: Uint8Array; expires: number };

export async function prepareAssetsGenerationEnv(
pipeline: BuildPipeline,
environment: BuildEnvironment,
totalCount: number
): Promise<AssetEnv> {
const config = pipeline.getConfig();
const logger = pipeline.getLogger();
const { config, logger } = environment;
let useCache = true;
const assetsCacheDir = new URL('assets/', config.cacheDir);
const count = { total: totalCount, current: 1 };
Expand Down
3 changes: 3 additions & 0 deletions packages/astro/src/core/app/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Environment } from "../render/environment.js";

export class AppEnvironment extends Environment {}
97 changes: 37 additions & 60 deletions packages/astro/src/core/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,25 @@ import type {
SSRElement,
SSRManifest,
} from '../../@types/astro.js';
import { createI18nMiddleware, i18nPipelineHook } from '../../i18n/middleware.js';
import type { SinglePageBuiltModule } from '../build/types.js';
import { getSetCookiesFromResponse } from '../cookies/index.js';
import { consoleLogDestination } from '../logger/console.js';
import { AstroIntegrationLogger, Logger } from '../logger/core.js';
import { sequence } from '../middleware/index.js';
import {
collapseDuplicateSlashes,
prependForwardSlash,
removeTrailingForwardSlash,
} from '../path.js';
import { RedirectSinglePageBuiltModule } from '../redirects/index.js';
import { createEnvironment, createRenderContext, type RenderContext } from '../render/index.js';
import { createRenderContext, type RenderContext } from '../render/index.js';
import { RouteCache } from '../render/route-cache.js';
import {
createAssetLink,
createModuleScriptElement,
createStylesheetElementSet,
} from '../render/ssr-element.js';
import { matchRoute } from '../routing/match.js';
import { SSRRoutePipeline } from './ssrPipeline.js';
import { AppEnvironment } from './environment.js';
import type { RouteInfo } from './types.js';
export { deserializeManifest } from './common.js';

Expand Down Expand Up @@ -93,7 +91,7 @@ export class App {
level: 'info',
});
#baseWithoutTrailingSlash: string;
#pipeline: SSRRoutePipeline;
#environment: AppEnvironment;
#adapterLogger: AstroIntegrationLogger;
#renderOptionsDeprecationWarningShown = false;

Expand All @@ -104,7 +102,7 @@ export class App {
};
this.#routeDataToRouteInfo = new Map(manifest.routes.map((route) => [route.routeData, route]));
this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base);
this.#pipeline = new SSRRoutePipeline(this.#createEnvironment(streaming));
this.#environment = this.#createEnvironment(streaming);
this.#adapterLogger = new AstroIntegrationLogger(
this.#logger.options,
this.#manifest.adapterName
Expand All @@ -121,34 +119,35 @@ export class App {
* @param streaming
* @private
*/
#createEnvironment(streaming = false) {
return createEnvironment({
adapterName: this.#manifest.adapterName,
logger: this.#logger,
mode: 'production',
compressHTML: this.#manifest.compressHTML,
renderers: this.#manifest.renderers,
clientDirectives: this.#manifest.clientDirectives,
resolve: async (specifier: string) => {
if (!(specifier in this.#manifest.entryModules)) {
throw new Error(`Unable to resolve [${specifier}]`);
#createEnvironment(streaming: boolean) {
const mode = 'production';
const manifest = this.#manifest;
const serverLike = true;
async function resolve(specifier: string) {
if (!(specifier in manifest.entryModules)) {
throw new Error(`Unable to resolve [${specifier}]`);
}
const bundlePath = manifest.entryModules[specifier];
switch (true) {
case bundlePath.startsWith('data:'):
case bundlePath.length === 0: {
return bundlePath;
}
const bundlePath = this.#manifest.entryModules[specifier];
switch (true) {
case bundlePath.startsWith('data:'):
case bundlePath.length === 0: {
return bundlePath;
}
default: {
return createAssetLink(bundlePath, this.#manifest.base, this.#manifest.assetsPrefix);
}
default: {
return createAssetLink(bundlePath, manifest.base, manifest.assetsPrefix);
}
},
routeCache: new RouteCache(this.#logger),
site: this.#manifest.site,
ssr: true,
}
}
return new AppEnvironment(
this.#logger,
manifest,
mode,
manifest.renderers,
resolve,
serverLike,
streaming,
});
new RouteCache(this.#logger)
);
}

set setManifestData(newManifestData: ManifestData) {
Expand Down Expand Up @@ -250,27 +249,10 @@ export class App {
mod,
defaultStatus
);
const pipeline = this.#environment.createPipeline({}, pathname, request, renderContext);
let response;
try {
const i18nMiddleware = createI18nMiddleware(
this.#manifest.i18n,
this.#manifest.base,
this.#manifest.trailingSlash,
this.#manifest.buildFormat
);
if (i18nMiddleware) {
if (mod.onRequest) {
this.#pipeline.setMiddlewareFunction(sequence(i18nMiddleware, mod.onRequest));
} else {
this.#pipeline.setMiddlewareFunction(i18nMiddleware);
}
this.#pipeline.onBeforeRenderRoute(i18nPipelineHook);
} else {
if (mod.onRequest) {
this.#pipeline.setMiddlewareFunction(mod.onRequest);
}
}
response = await this.#pipeline.renderRoute(renderContext, pageModule);
response = await pipeline.renderRoute(pageModule);
} catch (err: any) {
this.#logger.error(null, err.stack || err.message || String(err));
return this.#renderError(request, { status: 500 });
Expand Down Expand Up @@ -346,7 +328,7 @@ export class App {
pathname,
route: routeData,
status,
env: this.#pipeline.env,
env: this.#environment,
mod: handler as any,
locales: this.#manifest.i18n?.locales,
routing: this.#manifest.i18n?.routing,
Expand Down Expand Up @@ -384,7 +366,7 @@ export class App {
route: routeData,
status,
mod,
env: this.#pipeline.env,
env: this.#environment,
locales: this.#manifest.i18n?.locales,
routing: this.#manifest.i18n?.routing,
defaultLocale: this.#manifest.i18n?.defaultLocale,
Expand Down Expand Up @@ -420,22 +402,17 @@ export class App {
}
const mod = await this.#getModuleForRoute(errorRouteData);
try {
const pathname = prependForwardSlash(this.removeBase(url.pathname))
const newRenderContext = await this.#createRenderContext(
url,
request,
errorRouteData,
mod,
status
);
const pipeline = this.#environment.createPipeline({}, pathname, request, newRenderContext)
const page = (await mod.page()) as any;
if (skipMiddleware === false && mod.onRequest) {
this.#pipeline.setMiddlewareFunction(mod.onRequest);
}
if (skipMiddleware) {
// make sure middleware set by other requests is cleared out
this.#pipeline.unsetMiddlewareFunction();
}
const response = await this.#pipeline.renderRoute(newRenderContext, page);
const response = await pipeline.renderRoute(page);
return this.#mergeResponses(response, originalResponse);
} catch {
// Middleware may be the cause of the error, so we try rendering 404/500.astro without it.
Expand Down
3 changes: 0 additions & 3 deletions packages/astro/src/core/app/ssrPipeline.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import type { AstroConfig, AstroSettings, SSRLoadedRenderer } from '../../@types/astro.js';
import type { SSRLoadedRenderer } from '../../@types/astro.js';
import { getOutputDirectory, isServerLikeOutput } from '../../prerender/utils.js';
import { BEFORE_HYDRATION_SCRIPT_ID } from '../../vite-plugin-scripts/index.js';
import type { SSRManifest } from '../app/types.js';
import type { Logger } from '../logger/core.js';
import { Pipeline } from '../pipeline.js';
import { routeIsFallback, routeIsRedirect } from '../redirects/helpers.js';
import { createEnvironment } from '../render/index.js';
import { Environment } from '../render/index.js';
import { createAssetLink } from '../render/ssr-element.js';
import type { BuildInternals } from './internal.js';
import {
Expand All @@ -18,81 +16,39 @@ import type { PageBuildData, StaticBuildOptions } from './types.js';
import { i18nHasFallback } from './util.js';

/**
* This pipeline is responsible to gather the files emitted by the SSR build and generate the pages by executing these files.
* This build environment is responsible to gather the files emitted by the SSR build and generate the pages by executing these files.
*/
export class BuildPipeline extends Pipeline {
#internals: BuildInternals;
#staticBuildOptions: StaticBuildOptions;
#manifest: SSRManifest;

export class BuildEnvironment extends Environment {
constructor(
staticBuildOptions: StaticBuildOptions,
internals: BuildInternals,
manifest: SSRManifest
readonly options: StaticBuildOptions,
readonly internals: BuildInternals,
readonly manifest: SSRManifest,
readonly config = options.settings.config,
readonly settings = options.settings
) {
const ssr = isServerLikeOutput(staticBuildOptions.settings.config);
const resolveCache = new Map<string, string>();
super(
createEnvironment({
adapterName: manifest.adapterName,
logger: staticBuildOptions.logger,
mode: staticBuildOptions.mode,
renderers: manifest.renderers,
clientDirectives: manifest.clientDirectives,
compressHTML: manifest.compressHTML,
async resolve(specifier: string) {
if (resolveCache.has(specifier)) {
return resolveCache.get(specifier)!;
}
const hashedFilePath = manifest.entryModules[specifier];
if (typeof hashedFilePath !== 'string' || hashedFilePath === '') {
// If no "astro:scripts/before-hydration.js" script exists in the build,
// then we can assume that no before-hydration scripts are needed.
if (specifier === BEFORE_HYDRATION_SCRIPT_ID) {
resolveCache.set(specifier, '');
return '';
}
throw new Error(`Cannot find the built path for ${specifier}`);
}
const assetLink = createAssetLink(hashedFilePath, manifest.base, manifest.assetsPrefix);
resolveCache.set(specifier, assetLink);
return assetLink;
},
routeCache: staticBuildOptions.routeCache,
site: manifest.site,
ssr,
streaming: true,
})
);
this.#internals = internals;
this.#staticBuildOptions = staticBuildOptions;
this.#manifest = manifest;
}

getInternals(): Readonly<BuildInternals> {
return this.#internals;
}

getSettings(): Readonly<AstroSettings> {
return this.#staticBuildOptions.settings;
}

getStaticBuildOptions(): Readonly<StaticBuildOptions> {
return this.#staticBuildOptions;
}

getConfig(): AstroConfig {
return this.#staticBuildOptions.settings.config;
}

getManifest(): SSRManifest {
return this.#manifest;
}

getLogger(): Logger {
return this.getEnvironment().logger;
async function resolve(specifier: string) {
if (resolveCache.has(specifier)) {
return resolveCache.get(specifier)!;
}
const hashedFilePath = manifest.entryModules[specifier];
if (typeof hashedFilePath !== 'string' || hashedFilePath === '') {
// If no "astro:scripts/before-hydration.js" script exists in the build,
// then we can assume that no before-hydration scripts are needed.
if (specifier === BEFORE_HYDRATION_SCRIPT_ID) {
resolveCache.set(specifier, '');
return '';
}
throw new Error(`Cannot find the built path for ${specifier}`);
}
const assetLink = createAssetLink(hashedFilePath, manifest.base, manifest.assetsPrefix);
resolveCache.set(specifier, assetLink);
return assetLink;
}
const serverLike = isServerLikeOutput(config);
const streaming = true;
super(options.logger, manifest, options.mode, manifest.renderers, resolve, serverLike, streaming, options.routeCache)
}

/**
* The SSR build emits two important files:
* - dist/server/manifest.mjs
Expand Down Expand Up @@ -144,7 +100,7 @@ export class BuildPipeline extends Pipeline {
retrieveRoutesToGenerate(): Map<PageBuildData, string> {
const pages = new Map<PageBuildData, string>();

for (const [entrypoint, filePath] of this.#internals.entrySpecifierToBundleMap) {
for (const [entrypoint, filePath] of this.internals.entrySpecifierToBundleMap) {
// virtual pages can be emitted with different prefixes:
// - the classic way are pages emitted with prefix ASTRO_PAGE_RESOLVED_MODULE_ID -> plugin-pages
// - pages emitted using `build.split`, in this case pages are emitted with prefix RESOLVED_SPLIT_MODULE_ID
Expand All @@ -153,7 +109,7 @@ export class BuildPipeline extends Pipeline {
entrypoint.includes(RESOLVED_SPLIT_MODULE_ID)
) {
const [, pageName] = entrypoint.split(':');
const pageData = this.#internals.pagesByComponent.get(
const pageData = this.internals.pagesByComponent.get(
`${pageName.replace(ASTRO_PAGE_EXTENSION_POST_PATTERN, '.')}`
);
if (!pageData) {
Expand All @@ -166,12 +122,12 @@ export class BuildPipeline extends Pipeline {
}
}

for (const [path, pageData] of this.#internals.pagesByComponent.entries()) {
for (const [path, pageData] of this.internals.pagesByComponent.entries()) {
if (routeIsRedirect(pageData.route)) {
pages.set(pageData, path);
} else if (
routeIsFallback(pageData.route) &&
(i18nHasFallback(this.getConfig()) ||
(i18nHasFallback(this.config) ||
(routeIsFallback(pageData.route) && pageData.route.route === '/'))
) {
// The original component is transformed during the first build, so we have to retrieve
Expand All @@ -182,7 +138,7 @@ export class BuildPipeline extends Pipeline {
// Here, we take the component path and transform it in the virtual module name
const moduleSpecifier = getVirtualModulePageNameFromPath(path);
// We retrieve the original JS module
const filePath = this.#internals.entrySpecifierToBundleMap.get(moduleSpecifier);
const filePath = this.internals.entrySpecifierToBundleMap.get(moduleSpecifier);
if (filePath) {
// it exists, added it to pages to render, using the file path that we jus retrieved
pages.set(pageData, filePath);
Expand Down

0 comments on commit e2e71ee

Please sign in to comment.