0
Fork 0
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:
Drew Powers 2021-10-20 11:05:43 -06:00 committed by Drew Powers
parent e1b52506f7
commit bbf93de41e
2 changed files with 72 additions and 52 deletions

View file

@ -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}`)}`);
} }
} }

View file

@ -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)}]`;
}