Angular v17: the Application Builder
Angular team is working on a new builder called application (the current builder is called browser), it’s available now as a developer preview in Angular 16.2 and will be the default for new applications generated using Angular 17
so first what’s a builder in Angular?
Angular builder (called executer in nx repos) is basically the compiler that convert your Angular application TS, Angular HTML, and SCSS files into plain HTML, JS and css files that can be understood by the browser
right now we have multiple builder
@angular-devkit/build-angular:browser
for production build@angular-devkit/build-angular:dev-server
for serving e.g.ng serve
that still uses@angular-devkit/build-angular:browser
under the hood but without a lot of optimization and exposing some parts of Angular compiler in the runtime@angular-devkit/build-angular:server
for ssr production build@nguniversal/builders:ssr-dev-server
for ssr serve@nguniversal/builders:prerender
for prerender
now all of these are based on webpack, there is a new one@angular-devkit/build-angular:browser-esbuild
and as the name hint uses esbuild, it’s currently available as developer preview (actually I’m using in production for https://interview.community without any problems)
so where does the new builder fit?
It will be using @angular-devkit/build-angular:browser-esbuild
under the hood but will extend it to replace also @angular-devkit/build-angular:server
and @nguniversal/builders:prerender
and since now it can do it both the browser and ssr build, it will make @angular-devkit/build-angular:dev-server
able to replace @nguniversal/builders:ssr-dev-server
so basically in the future we will have 2 instead of the current 5 (well there are some other builder for unit testing and i18n, but that’s different story)
so what is the benefits of doing all of this?
- Simpler configuration in the
angular.json
(orproject.json
in case of nx), since you don’t need to repeat your configuration in a lot of places) - It’s faster, since there are some common steps between building the browser, prerender, and ssr having one builder will mean these steps will be executed once instead of 3 now
- it will enable using es modules (aka esm) in the SSR, it’s now working for projects that don’t have SSR
so how to test this new builder?
- upgrade to the latest version of Angular (
@angular/cli
is 16.2.3 by the time I published the article) - update your
angular.json
file
– change your builder in build from@angular-devkit/build-angular:browser
to@angular-devkit/build-angular:application
– (optional) update outputPath to be without browser since it’s now one folder for server and browser
– replace main key to be browser which points still to yourmain.ts
file
– add"server": "src/main.server.ts"
or whatever is the path for yourmain.server.ts
– add"ssr: { "entry": "server.ts" }
in your production configurations or if you prefer to have ssr in your development then add it as option
– deleteserver
,serve-ssr
, andprerender
from your file - update your
server.ts
file to be
import { APP_BASE_HREF } from '@angular/common';
import { ngExpressEngine } from '@nguniversal/express-engine';
import express from 'express';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import bootstrap from './src/main.server';
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const distFolder = dirname(fileURLToPath(import.meta.url));
const indexHtml = join(distFolder, 'index.server.html');
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap
}));
server.set('view engine', 'html');
server.set('views', distFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
function run(): void {
const port = process.env['PORT'] || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
run();
4. update your tsconfig.app.json
to be
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": [],
"allowSyntheticDefaultImports": true
},
"files": [
"src/main.ts",
"src/main.server.ts",
"server.ts"
],
"include": [
"src/**/*.d.ts"
]
}
5. your serve:ssr
script will be node dist/project_name/server.mjs
and that’s it, you are good to go now
you can see changes example and working application here
https://github.com/robertIsaac/ng_ssr_esm/compare/browser-builder...master