mirror of
https://github.com/withastro/astro.git
synced 2025-01-06 22:10:10 -05:00
Fix build order (#1609)
* Bugfix: restore build to get all paths earlier, when build. Same as main. * Also re-add timings
This commit is contained in:
parent
e1b52506f7
commit
bbf93de41e
2 changed files with 72 additions and 52 deletions
|
@ -10,7 +10,7 @@ import vite, { ViteDevServer } from '../vite.js';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { createVite } from '../create-vite.js';
|
import { createVite } from '../create-vite.js';
|
||||||
import { pad } from '../dev/util.js';
|
import { pad } from '../dev/util.js';
|
||||||
import { defaultLogOptions, levels, warn } from '../logger.js';
|
import { debug, defaultLogOptions, levels, timerMessage, warn } from '../logger.js';
|
||||||
import { ssr } from '../ssr/index.js';
|
import { ssr } from '../ssr/index.js';
|
||||||
import { generatePaginateFunction } from '../ssr/paginate.js';
|
import { generatePaginateFunction } from '../ssr/paginate.js';
|
||||||
import { createRouteManifest, validateGetStaticPathsModule, validateGetStaticPathsResult } from '../ssr/routing.js';
|
import { createRouteManifest, validateGetStaticPathsModule, validateGetStaticPathsResult } from '../ssr/routing.js';
|
||||||
|
@ -36,6 +36,7 @@ class AstroBuilder {
|
||||||
private origin: string;
|
private origin: string;
|
||||||
private routeCache: RouteCache = {};
|
private routeCache: RouteCache = {};
|
||||||
private manifest: ManifestData;
|
private manifest: ManifestData;
|
||||||
|
private viteServer?: ViteDevServer;
|
||||||
|
|
||||||
constructor(config: AstroConfig, options: BuildOptions) {
|
constructor(config: AstroConfig, options: BuildOptions) {
|
||||||
if (!config.buildOptions.site && config.buildOptions.sitemap !== false) {
|
if (!config.buildOptions.site && config.buildOptions.sitemap !== false) {
|
||||||
|
@ -52,9 +53,10 @@ class AstroBuilder {
|
||||||
|
|
||||||
/** Build all pages */
|
/** Build all pages */
|
||||||
async build() {
|
async build() {
|
||||||
const start = performance.now();
|
const timer: Record<string, number> = {}; // keep track of performance timers
|
||||||
|
|
||||||
// 1. initialize fresh Vite instance
|
// 1. initialize fresh Vite instance
|
||||||
|
timer.viteStart = performance.now();
|
||||||
const { logging, origin } = this;
|
const { logging, origin } = this;
|
||||||
const viteConfig = await createVite(
|
const viteConfig = await createVite(
|
||||||
{
|
{
|
||||||
|
@ -68,50 +70,64 @@ class AstroBuilder {
|
||||||
{ astroConfig: this.config, logging }
|
{ astroConfig: this.config, logging }
|
||||||
);
|
);
|
||||||
const viteServer = await vite.createServer(viteConfig);
|
const viteServer = await vite.createServer(viteConfig);
|
||||||
|
this.viteServer = viteServer;
|
||||||
|
debug(logging, 'build', timerMessage('Vite started', timer.viteStart));
|
||||||
|
|
||||||
// 2. get all routes
|
// 2. get all routes
|
||||||
const input: InputHTMLOptions[] = [];
|
timer.renderStart = performance.now();
|
||||||
const assets: Record<string, string> = {}; // additional assets to be written
|
const assets: Record<string, string> = {}; // additional assets to be written
|
||||||
for (const route of this.manifest.routes) {
|
const allPages: Record<string, RouteData & { paths: string[] }> = {};
|
||||||
const { pathname } = route;
|
|
||||||
const filePath = new URL(`./${route.component}`, this.config.projectRoot);
|
// 2a. determine all possible routes first before rendering
|
||||||
// static pages (note: should these be )
|
await Promise.all(
|
||||||
if (pathname) {
|
this.manifest.routes.map(async (route) => {
|
||||||
input.push(
|
// static route
|
||||||
await ssr({ astroConfig: this.config, filePath, logging, mode: 'production', origin, route, routeCache: this.routeCache, pathname, viteServer }).then((html) => ({
|
if (route.pathname) {
|
||||||
html,
|
allPages[route.component] = { ...route, paths: [route.pathname] };
|
||||||
name: pathname.replace(/\/?$/, '/index.html').replace(/^\//, ''),
|
return;
|
||||||
}))
|
}
|
||||||
);
|
// dynamic route
|
||||||
}
|
const result = await this.getStaticPathsForRoute(route);
|
||||||
// dynamic pages
|
// handle RSS while generating routes
|
||||||
else {
|
if (result.rss?.xml) {
|
||||||
const staticPaths = await this.getStaticPathsForRoute(route, viteServer);
|
const rssFile = new URL(result.rss.url.replace(/^\/?/, './'), this.config.dist);
|
||||||
// handle RSS (TODO: improve this?)
|
|
||||||
if (staticPaths.rss && staticPaths.rss.xml) {
|
|
||||||
const rssFile = new URL(staticPaths.rss.url.replace(/^\/?/, './'), this.config.dist);
|
|
||||||
if (assets[fileURLToPath(rssFile)]) {
|
if (assets[fileURLToPath(rssFile)]) {
|
||||||
throw new Error(
|
throw new Error(`[getStaticPaths] RSS feed ${result.rss.url} already exists.\nUse \`rss(data, {url: '...'})\` to choose a unique, custom URL. (${route.component})`);
|
||||||
`[getStaticPaths] RSS feed ${staticPaths.rss.url} already exists.\nUse \`rss(data, {url: '...'})\` to choose a unique, custom URL. (${route.component})`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
assets[fileURLToPath(rssFile)] = staticPaths.rss.xml;
|
assets[fileURLToPath(rssFile)] = result.rss.xml;
|
||||||
}
|
}
|
||||||
// TODO: throw error if conflict
|
allPages[route.component] = { ...route, paths: result.paths };
|
||||||
for (const staticPath of staticPaths.paths) {
|
})
|
||||||
input.push(
|
);
|
||||||
await ssr({ astroConfig: this.config, filePath, logging, mode: 'production', origin, route, routeCache: this.routeCache, pathname: staticPath, viteServer }).then(
|
|
||||||
(html) => ({
|
// 2b. after all paths have been determined, render all pages
|
||||||
html,
|
const input: InputHTMLOptions[] = [];
|
||||||
name: staticPath.replace(/\/?$/, '/index.html').replace(/^\//, ''),
|
await Promise.all(
|
||||||
})
|
Object.entries(allPages).map(([component, route]) =>
|
||||||
)
|
Promise.all(
|
||||||
);
|
route.paths.map(async (pathname) => {
|
||||||
}
|
input.push({
|
||||||
}
|
html: await ssr({
|
||||||
}
|
astroConfig: this.config,
|
||||||
|
filePath: new URL(`./${component}`, this.config.projectRoot),
|
||||||
|
logging,
|
||||||
|
mode: 'production',
|
||||||
|
origin,
|
||||||
|
pathname,
|
||||||
|
route,
|
||||||
|
routeCache: this.routeCache,
|
||||||
|
viteServer,
|
||||||
|
}),
|
||||||
|
name: pathname.replace(/\/?$/, '/index.html').replace(/^\//, ''),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
debug(logging, 'build', timerMessage('All pages rendered', timer.renderStart));
|
||||||
|
|
||||||
// 3. build with Vite
|
// 3. build with Vite
|
||||||
|
timer.buildStart = performance.now();
|
||||||
await vite.build({
|
await vite.build({
|
||||||
logLevel: 'error',
|
logLevel: 'error',
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
|
@ -133,8 +149,10 @@ class AstroBuilder {
|
||||||
root: viteConfig.root,
|
root: viteConfig.root,
|
||||||
server: viteConfig.server,
|
server: viteConfig.server,
|
||||||
});
|
});
|
||||||
|
debug(logging, 'build', timerMessage('Vite build finished', timer.buildStart));
|
||||||
|
|
||||||
// 4. write assets to disk
|
// 4. write assets to disk
|
||||||
|
timer.assetsStart = performance.now();
|
||||||
Object.keys(assets).map((k) => {
|
Object.keys(assets).map((k) => {
|
||||||
if (!assets[k]) return;
|
if (!assets[k]) return;
|
||||||
const filePath = new URL(`file://${k}`);
|
const filePath = new URL(`file://${k}`);
|
||||||
|
@ -142,36 +160,33 @@ class AstroBuilder {
|
||||||
fs.writeFileSync(filePath, assets[k], 'utf8');
|
fs.writeFileSync(filePath, assets[k], 'utf8');
|
||||||
delete assets[k]; // free up memory
|
delete assets[k]; // free up memory
|
||||||
});
|
});
|
||||||
|
debug(logging, 'build', timerMessage('Additional assets copied', timer.assetsStart));
|
||||||
|
|
||||||
// 5. build sitemap
|
// 5. build sitemap
|
||||||
let sitemapTime = 0;
|
timer.sitemapStart = performance.now();
|
||||||
if (this.config.buildOptions.sitemap && this.config.buildOptions.site) {
|
if (this.config.buildOptions.sitemap && this.config.buildOptions.site) {
|
||||||
const sitemapStart = performance.now();
|
const sitemapStart = performance.now();
|
||||||
const sitemap = generateSitemap(input.map(({ name }) => new URL(`/${name}`, this.config.buildOptions.site).href));
|
const sitemap = generateSitemap(input.map(({ name }) => new URL(`/${name}`, this.config.buildOptions.site).href));
|
||||||
const sitemapPath = new URL('./sitemap.xml', this.config.dist);
|
const sitemapPath = new URL('./sitemap.xml', this.config.dist);
|
||||||
await fs.promises.mkdir(new URL('./', sitemapPath), { recursive: true });
|
await fs.promises.mkdir(new URL('./', sitemapPath), { recursive: true });
|
||||||
await fs.promises.writeFile(sitemapPath, sitemap, 'utf8');
|
await fs.promises.writeFile(sitemapPath, sitemap, 'utf8');
|
||||||
sitemapTime = performance.now() - sitemapStart;
|
|
||||||
}
|
}
|
||||||
|
debug(logging, 'build', timerMessage('Sitemap built', timer.sitemapStart));
|
||||||
|
|
||||||
// 6. clean up
|
// 6. clean up
|
||||||
await viteServer.close();
|
await viteServer.close();
|
||||||
|
|
||||||
// 7. log output
|
// 7. log output
|
||||||
if (logging.level && levels[logging.level] <= levels['info']) {
|
if (logging.level && levels[logging.level] <= levels['info']) {
|
||||||
await this.printStats({
|
await this.printStats({ cwd: this.config.dist, pageCount: input.length });
|
||||||
cwd: this.config.dist,
|
|
||||||
pageCount: input.length,
|
|
||||||
pageTime: Math.round(performance.now() - start),
|
|
||||||
sitemapTime,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Extract all static paths from a dynamic route */
|
/** Extract all static paths from a dynamic route */
|
||||||
private async getStaticPathsForRoute(route: RouteData, viteServer: ViteDevServer): Promise<{ paths: string[]; rss?: RSSResult }> {
|
private async getStaticPathsForRoute(route: RouteData): Promise<{ paths: string[]; rss?: RSSResult }> {
|
||||||
|
if (!this.viteServer) throw new Error(`vite.createServer() not called!`);
|
||||||
const filePath = new URL(`./${route.component}`, this.config.projectRoot);
|
const filePath = new URL(`./${route.component}`, this.config.projectRoot);
|
||||||
const mod = (await viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
|
const mod = (await this.viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
|
||||||
validateGetStaticPathsModule(mod);
|
validateGetStaticPathsModule(mod);
|
||||||
const rss = generateRssFunction(this.config.buildOptions.site, route);
|
const rss = generateRssFunction(this.config.buildOptions.site, route);
|
||||||
const staticPaths: GetStaticPathsResult = (await mod.getStaticPaths!({ paginate: generatePaginateFunction(route), rss: rss.generator })).flat();
|
const staticPaths: GetStaticPathsResult = (await mod.getStaticPaths!({ paginate: generatePaginateFunction(route), rss: rss.generator })).flat();
|
||||||
|
@ -184,12 +199,11 @@ class AstroBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stats */
|
/** Stats */
|
||||||
private async printStats({ cwd, pageTime, pageCount, sitemapTime }: { cwd: URL; pageTime: number; pageCount: number; sitemapTime: number }) {
|
private async printStats({ cwd, pageCount }: { cwd: URL; pageCount: number }) {
|
||||||
const end = Math.round(performance.now() - pageTime);
|
|
||||||
const [js, html] = await Promise.all([profileJS({ cwd, entryHTML: new URL('./index.html', cwd) }), profileHTML({ cwd })]);
|
const [js, html] = await Promise.all([profileJS({ cwd, entryHTML: new URL('./index.html', cwd) }), profileHTML({ cwd })]);
|
||||||
|
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
console.log(`${pad(bold(cyan('Done')), 70)}${dim(` ${pad(`${end}ms`, 8, 'left')}`)}
|
console.log(`${bold(cyan('Done'))}
|
||||||
Pages (${pageCount} total)
|
Pages (${pageCount} total)
|
||||||
${green(`✔ All pages under ${kb(html.maxSize)}`)}
|
${green(`✔ All pages under ${kb(html.maxSize)}`)}
|
||||||
JS
|
JS
|
||||||
|
@ -201,6 +215,5 @@ CSS
|
||||||
Images
|
Images
|
||||||
${green(`✔ All images under 50 kB`)}
|
${green(`✔ All images under 50 kB`)}
|
||||||
`);
|
`);
|
||||||
if (sitemapTime > 0) console.log(`Sitemap\n ${green(`✔ Built in ${sitemapTime}`)}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,3 +185,10 @@ if (process.argv.includes('--verbose')) {
|
||||||
} else {
|
} else {
|
||||||
defaultLogLevel = 'info';
|
defaultLogLevel = 'info';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Print out a timer message for debug() */
|
||||||
|
export function timerMessage(message: string, startTime: number = performance.now()) {
|
||||||
|
let timeDiff = performance.now() - startTime;
|
||||||
|
let timeDisplay = timeDiff < 750 ? `${Math.round(timeDiff)}ms` : `${(timeDiff / 1000).toFixed(1)}s`;
|
||||||
|
return `${message}: ${dim(timeDisplay)}]`;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue