Skip to content

Commit

Permalink
feat(i18n): fallback system in SSG (#8816)
Browse files Browse the repository at this point in the history
* feat(i18n): fallback system in SSG

* suggestion
  • Loading branch information
ematipico committed Oct 20, 2023
1 parent 2f32800 commit 907bd0f
Show file tree
Hide file tree
Showing 13 changed files with 551 additions and 216 deletions.
14 changes: 11 additions & 3 deletions packages/astro/src/core/build/buildPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { ASTRO_PAGE_RESOLVED_MODULE_ID } from './plugins/plugin-pages.js';
import { RESOLVED_SPLIT_MODULE_ID } from './plugins/plugin-ssr.js';
import { ASTRO_PAGE_EXTENSION_POST_PATTERN } from './plugins/util.js';
import type { PageBuildData, StaticBuildOptions } from './types.js';
import { routeIsFallback, routeIsRedirect } from '../redirects/helpers.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.
Expand Down Expand Up @@ -154,11 +156,17 @@ export class BuildPipeline extends Pipeline {
pages.set(pageData, filePath);
}
}
for (const [path, pageData] of this.#internals.pagesByComponent.entries()) {
if (pageData.route.type === 'redirect') {
pages.set(pageData, path);

for (const [path, pageDataList] of this.#internals.pagesByComponents.entries()) {
for (const pageData of pageDataList) {
if (routeIsRedirect(pageData.route)) {
pages.set(pageData, path);
} else if (routeIsFallback(pageData.route) && i18nHasFallback(this.getConfig())) {
pages.set(pageData, path);
}
}
}

return pages;
}

Expand Down
54 changes: 46 additions & 8 deletions packages/astro/src/core/build/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ import { runHookBuildGenerated } from '../../integrations/index.js';
import { getOutputDirectory, isServerLikeOutput } from '../../prerender/utils.js';
import { PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js';
import { AstroError, AstroErrorData } from '../errors/index.js';
import { RedirectSinglePageBuiltModule, getRedirectLocationOrThrow } from '../redirects/index.js';
import {
RedirectSinglePageBuiltModule,
getRedirectLocationOrThrow,
routeIsRedirect,
} from '../redirects/index.js';
import { createRenderContext } from '../render/index.js';
import { callGetStaticPaths } from '../render/route-cache.js';
import {
Expand All @@ -58,6 +62,9 @@ import type {
StylesheetAsset,
} from './types.js';
import { getTimeStat, shouldAppendForwardSlash } from './util.js';
import { createI18nMiddleware } from '../../i18n/middleware.js';
import { sequence } from '../middleware/index.js';
import { routeIsFallback } from '../redirects/helpers.js';

function createEntryURL(filePath: string, outFolder: URL) {
return new URL('./' + filePath + `?time=${Date.now()}`, outFolder);
Expand All @@ -83,6 +90,26 @@ async function getEntryForRedirectRoute(
return RedirectSinglePageBuiltModule;
}

async function getEntryForFallbackRoute(
route: RouteData,
internals: BuildInternals,
outFolder: URL
): Promise<SinglePageBuiltModule> {
if (route.type !== 'fallback') {
throw new Error(`Expected a redirect route.`);
}
if (route.redirectRoute) {
const filePath = getEntryFilePathFromComponentPath(internals, route.redirectRoute.component);
if (filePath) {
const url = createEntryURL(filePath, outFolder);
const ssrEntryPage: SinglePageBuiltModule = await import(url.toString());
return ssrEntryPage;
}
}

return RedirectSinglePageBuiltModule;
}

function shouldSkipDraft(pageModule: ComponentInstance, settings: AstroSettings): boolean {
return (
// Drafts are disabled
Expand Down Expand Up @@ -176,16 +203,15 @@ export async function generatePages(opts: StaticBuildOptions, internals: BuildIn
await generatePage(pageData, ssrEntry, builtPaths, pipeline);
}
}
if (pageData.route.type === 'redirect') {
const entry = await getEntryForRedirectRoute(pageData.route, internals, outFolder);
await generatePage(pageData, entry, builtPaths, pipeline);
}
}
} else {
for (const [pageData, filePath] of pagesToGenerate) {
if (pageData.route.type === 'redirect') {
if (routeIsRedirect(pageData.route)) {
const entry = await getEntryForRedirectRoute(pageData.route, internals, outFolder);
await generatePage(pageData, entry, builtPaths, pipeline);
} else if (routeIsFallback(pageData.route)) {
const entry = await getEntryForFallbackRoute(pageData.route, internals, outFolder);
await generatePage(pageData, entry, builtPaths, pipeline);
} else {
const ssrEntryURLPage = createEntryURL(filePath, outFolder);
const entry: SinglePageBuiltModule = await import(ssrEntryURLPage.toString());
Expand Down Expand Up @@ -257,6 +283,7 @@ async function generatePage(
) {
let timeStart = performance.now();
const logger = pipeline.getLogger();
const config = pipeline.getConfig();
const pageInfo = getPageDataByComponent(pipeline.getInternals(), pageData.route.component);

// may be used in the future for handling rel=modulepreload, rel=icon, rel=manifest etc.
Expand All @@ -269,7 +296,16 @@ async function generatePage(

const pageModulePromise = ssrEntry.page;
const onRequest = ssrEntry.onRequest;
if (onRequest) {
const i18nMiddleware = createI18nMiddleware(config, logger);
if (config.experimental.i18n && i18nMiddleware) {
if (onRequest) {
pipeline.setMiddlewareFunction(
sequence(i18nMiddleware, onRequest as MiddlewareEndpointHandler)
);
} else {
pipeline.setMiddlewareFunction(i18nMiddleware);
}
} else if (onRequest) {
pipeline.setMiddlewareFunction(onRequest as MiddlewareEndpointHandler);
}
if (!pageModulePromise) {
Expand Down Expand Up @@ -297,7 +333,9 @@ async function generatePage(
};

const icon =
pageData.route.type === 'page' || pageData.route.type === 'redirect'
pageData.route.type === 'page' ||
pageData.route.type === 'redirect' ||
pageData.route.type === 'fallback'
? green('▶')
: magenta('λ');
if (isRelativePath(pageData.route.component)) {
Expand Down
4 changes: 3 additions & 1 deletion packages/astro/src/core/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ class AstroBuilder {
await runHookBuildDone({
config: this.settings.config,
pages: pageNames,
routes: Object.values(allPages).map((pd) => pd.route),
routes: Object.values(allPages)
.flat()
.map((pageData) => pageData.route),
logging: this.logger,
});

Expand Down
30 changes: 28 additions & 2 deletions packages/astro/src/core/build/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
} from './plugins/plugin-pages.js';
import { RESOLVED_SPLIT_MODULE_ID } from './plugins/plugin-ssr.js';
import { ASTRO_PAGE_EXTENSION_POST_PATTERN } from './plugins/util.js';
import type { PageBuildData, StylesheetAsset, ViteID } from './types.js';
import type { AllPagesData, PageBuildData, StylesheetAsset, ViteID } from './types.js';
import { routeIsFallback } from '../redirects/helpers.js';

export interface BuildInternals {
/**
Expand Down Expand Up @@ -37,9 +38,16 @@ export interface BuildInternals {

/**
* A map for page-specific information.
* // TODO: Remove in Astro 4.0
* @deprecated
*/
pagesByComponent: Map<string, PageBuildData>;

/**
* TODO: Use this in Astro 4.0
*/
pagesByComponents: Map<string, PageBuildData[]>;

/**
* A map for page-specific output.
*/
Expand Down Expand Up @@ -112,6 +120,7 @@ export function createBuildInternals(): BuildInternals {
entrySpecifierToBundleMap: new Map<string, string>(),
pageToBundleMap: new Map<string, string>(),
pagesByComponent: new Map(),
pagesByComponents: new Map(),
pageOptionsByPage: new Map(),
pagesByViteID: new Map(),
pagesByClientOnly: new Map(),
Expand All @@ -134,7 +143,16 @@ export function trackPageData(
componentURL: URL
): void {
pageData.moduleSpecifier = componentModuleId;
internals.pagesByComponent.set(component, pageData);
if (!routeIsFallback(pageData.route)) {
internals.pagesByComponent.set(component, pageData);
}
const list = internals.pagesByComponents.get(component);
if (list) {
list.push(pageData);
internals.pagesByComponents.set(component, list);
} else {
internals.pagesByComponents.set(component, [pageData]);
}
internals.pagesByViteID.set(viteID(componentURL), pageData);
}

Expand Down Expand Up @@ -230,6 +248,14 @@ export function* eachPageData(internals: BuildInternals) {
yield* internals.pagesByComponent.values();
}

export function* eachPageFromAllPages(allPages: AllPagesData): Generator<[string, PageBuildData]> {
for (const [path, list] of Object.entries(allPages)) {
for (const pageData of list) {
yield [path, pageData];
}
}
}

export function* eachPageDataFromEntryPoint(
internals: BuildInternals
): Generator<[PageBuildData, string]> {
Expand Down
65 changes: 46 additions & 19 deletions packages/astro/src/core/build/page-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,29 @@ export async function collectPagesData(
clearInterval(routeCollectionLogTimeout);
}, 10000);
builtPaths.add(route.pathname);
allPages[route.component] = {
component: route.component,
route,
moduleSpecifier: '',
styles: [],
propagatedStyles: new Map(),
propagatedScripts: new Map(),
hoistedScript: undefined,
};
if (allPages[route.component]) {
allPages[route.component].push({
component: route.component,
route,
moduleSpecifier: '',
styles: [],
propagatedStyles: new Map(),
propagatedScripts: new Map(),
hoistedScript: undefined,
});
} else {
allPages[route.component] = [
{
component: route.component,
route,
moduleSpecifier: '',
styles: [],
propagatedStyles: new Map(),
propagatedScripts: new Map(),
hoistedScript: undefined,
},
];
}

clearInterval(routeCollectionLogTimeout);
if (settings.config.output === 'static') {
Expand All @@ -70,18 +84,31 @@ export async function collectPagesData(
continue;
}
// dynamic route:
allPages[route.component] = {
component: route.component,
route,
moduleSpecifier: '',
styles: [],
propagatedStyles: new Map(),
propagatedScripts: new Map(),
hoistedScript: undefined,
};
if (allPages[route.component]) {
allPages[route.component].push({
component: route.component,
route,
moduleSpecifier: '',
styles: [],
propagatedStyles: new Map(),
propagatedScripts: new Map(),
hoistedScript: undefined,
});
} else {
allPages[route.component] = [
{
component: route.component,
route,
moduleSpecifier: '',
styles: [],
propagatedStyles: new Map(),
propagatedScripts: new Map(),
hoistedScript: undefined,
},
];
}
}

clearInterval(dataCollectionLogTimeout);

return { assets, allPages };
}
4 changes: 2 additions & 2 deletions packages/astro/src/core/build/plugins/plugin-pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Plugin as VitePlugin } from 'vite';
import type { AstroSettings } from '../../../@types/astro.js';
import { routeIsRedirect } from '../../redirects/index.js';
import { addRollupInput } from '../add-rollup-input.js';
import { type BuildInternals } from '../internal.js';
import { type BuildInternals, eachPageFromAllPages } from '../internal.js';
import type { AstroBuildPlugin } from '../plugin.js';
import type { StaticBuildOptions } from '../types.js';
import { MIDDLEWARE_MODULE_ID } from './plugin-middleware.js';
Expand Down Expand Up @@ -42,7 +42,7 @@ function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): V
if (opts.settings.config.output === 'static') {
const inputs = new Set<string>();

for (const [path, pageData] of Object.entries(opts.allPages)) {
for (const [path, pageData] of eachPageFromAllPages(opts.allPages)) {
if (routeIsRedirect(pageData.route)) {
continue;
}
Expand Down
7 changes: 4 additions & 3 deletions packages/astro/src/core/build/plugins/plugin-ssr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { SSR_MANIFEST_VIRTUAL_MODULE_ID } from './plugin-manifest.js';
import { ASTRO_PAGE_MODULE_ID } from './plugin-pages.js';
import { RENDERERS_MODULE_ID } from './plugin-renderers.js';
import { getPathFromVirtualModulePageName, getVirtualModulePageNameFromPath } from './util.js';
import { eachPageFromAllPages } from '../internal.js';

export const SSR_VIRTUAL_MODULE_ID = '@astrojs-ssr-virtual-entry';
export const RESOLVED_SSR_VIRTUAL_MODULE_ID = '\0' + SSR_VIRTUAL_MODULE_ID;
Expand Down Expand Up @@ -42,7 +43,7 @@ function vitePluginSSR(
let i = 0;
const pageMap: string[] = [];

for (const [path, pageData] of Object.entries(allPages)) {
for (const [path, pageData] of eachPageFromAllPages(allPages)) {
if (routeIsRedirect(pageData.route)) {
continue;
}
Expand Down Expand Up @@ -148,7 +149,7 @@ function vitePluginSSRSplit(
if (options.settings.config.build.split || functionPerRouteEnabled) {
const inputs = new Set<string>();

for (const [path, pageData] of Object.entries(options.allPages)) {
for (const [path, pageData] of eachPageFromAllPages(options.allPages)) {
if (routeIsRedirect(pageData.route)) {
continue;
}
Expand Down Expand Up @@ -294,7 +295,7 @@ function storeEntryPoint(
fileName: string
) {
const componentPath = getPathFromVirtualModulePageName(RESOLVED_SPLIT_MODULE_ID, moduleKey);
for (const [page, pageData] of Object.entries(options.allPages)) {
for (const [page, pageData] of eachPageFromAllPages(options.allPages)) {
if (componentPath == page) {
const publicPath = fileURLToPath(options.settings.config.build.server);
internals.entryPoints.set(pageData.route, pathToFileURL(join(publicPath, fileName)));
Expand Down
Loading

0 comments on commit 907bd0f

Please sign in to comment.