33

I'm working on ng2 application and I use @angular/cli to build it. As output it emits several js files like .inline.js, .vendor.js etc.

The question is -- how to set up angular-cli to emit only one file as a result, i.e. bundle vendor.js, inline.js etc. into one big file?

I understand that it could be done by using additional bundler but it would be nice to achieve it via ng-cli

PS I don't use lazy loading in this app and definitely won't.

PPS Just concat files afterwards is not an option because:

  • it doesn't work with hash in file name, need to update html as well
  • it introduces an additional build step which is not necessary

As for now it looks like moving to pure webpack would be the easiest and best option

UPDATE

there is possibility to avoid vendor.js setting --vendor-chunk to true but as a result I still get several files: inline.bundle.js main.bundle.js polyfills.bundle.js

Leo
  • 1,683
  • 2
  • 20
  • 25
  • 1
    Good question. I usually further bundle (concatenate) the vendor.js/inline.js using gulp. But would be nice to have it all bundled up with the angular-cli. However, if you use lazy loading, then of course you have to keep the separately bundled chunks separate. – brando Mar 21 '17 at 17:14
  • @VSO and Leo what versions of the CLI are you using? – Lucho Nov 05 '18 at 22:03
  • @Lucho this question was asked when ng-cli v1 was the actual version – Leo Nov 05 '18 at 23:39
  • looks like it did not change much since when https://github.com/angular/angular-cli/wiki/build, the latest release is v6.2.7. There is still only --vendor-chunk and --common-chunk options which allow reducing the amount of emitted files but there still will be more than one file – Leo Nov 05 '18 at 23:45
  • @Leo The reason i asked was just narrow down on how to solve this v1 covered ng4 aswell and question was specific on ng2 where the cli evolved in a certain way. I was looking into that aswell but as you say it has not changed for that type of conf. However there might be a solution Im looking into without involving other cli's – Lucho Nov 06 '18 at 00:23
  • correction up to ng5 – Lucho Nov 06 '18 at 00:35
  • @Leo I am using the latest version. – VSO Nov 06 '18 at 13:36
  • Can I ask why you want to do this? – hevans900 Nov 06 '18 at 16:48
  • @hevans900 I want to be able to throw my code in a file, on plunkr, code pen, etc and just run it. – VSO Nov 10 '18 at 15:38
  • Can I suggest trying stackblitz? It can sync directly to a repo from github and spin up Angular apps – hevans900 Nov 13 '18 at 09:53

6 Answers6

20

Angular CLI natively doesn't support this.

There are other solutions, including further processing with other tooling after Angular CLI finishes doing its thing, but this will hinder or remove the debugging capabilities Angular provides out-of-the-box.

As the ng eject command is also being deprecated, the option to reconfigure webpack is not as attractive as it used to be. However, that is still technically a possibility.

My solution:

The best solution I have found is the ngx-build-plus plugin, which can be found at https://github.com/manfredsteyer/ngx-build-plus or added to a project via the ng add ngx-build-plus Angular-CLI command currently. Once installed, it provides additional configuration options for building, including the --single-bundle true flag that can be added to an ng build or ng serve command.

With that flag, a main.js file and a main.js.map file are created, already referenced in the HTML, and it'll just run like that properly out of the box, with full source mapping and debugging.

Community
  • 1
  • 1
Qwertronix
  • 407
  • 3
  • 6
  • 3
    Im using this currently but including "--single-bundle true", it is now generating 2 js files, main.js and scripts.js(actually 3 files includings polyfills.js). How to configure it so it would generate only 1 js file? – Alex Coroza Sep 11 '19 at 18:36
  • For this, find the build scripts option in `angular.json` and manually import those scripts in your main code instead, leaving the scripts array empty in `angular.json`. You could do the same for polyfills, though of course you increase that tradeoff of having to redownload more each time your app code change whereas a cached polyfills file would save some time and bandwidth. – Qwertronix Sep 12 '19 at 14:39
  • does ngx-build-plus include pollyfill.js to the main? – Yiping Mar 08 '22 at 06:04
  • 1
    @Yiping By default polyfills are kept separate. Last I did this you did have the option to configure the build script to include those, with the obvious size vs file count vs caching tradeoffs. – Qwertronix Mar 10 '22 at 19:20
  • @Qwertronix I am trying to build for web component so single .js file would be great. Seems we can't do that now. – Yiping Mar 11 '22 at 03:04
15

1- use the ng build --prod --output-hashing=none, which creates the files without the cash-buster ( the random hash).

2- Use cat to bundle them in one file

"build":"ng build --prod --output-hashing=none",
"package":" cat dist/angular-project/{polyfills,runtime,main}.js > ./package.js",
"bundle":"npm run build && npm run package"

And use it like :

npm run bundle
Milad
  • 27,506
  • 11
  • 76
  • 85
5

I have not seen any functionality in angular-cli that builds into one bundle, however, it should be fairly easy to do with a nodejs script or using one of the concat libraries available such as concat-files https://www.npmjs.com/package/concat-files

install concat-files then: add a concat.js file in your project at the same level as dist folder

var concat = require('concat-files');
concat([
    './dist/inline.bundle.js',
    './dist/vendor.bundle.js',
    './dist/vendor.bundle.js'
], './dist/app.js', function(err) {
    if (err) throw err
    console.log('done');
});

in your package.json under scripts add a new script build:

"scripts":{
    "build": "ng build && node concat.js"
}

then run it npm run build it will run the angular cli build first the run the concat.js script which will concatenate the resulting bundles

Ahmed Musallam
  • 9,523
  • 4
  • 27
  • 47
  • 5
    This will concatenate the bundles but not link them to the index.html. Is there a way to easily create the link from the index.html to the resulting "app.js" file? – Thomas Graziani Nov 24 '17 at 05:13
5

I can see some good attempts made using concatenating all the js files, but it might not work all the time.

To generate one single index.html that contains all js and css files, I wrote a gulp script.

Install following dependencies

npm install --save-dev gulp gulp-inline

create gulpfile.js in the root folder.

enter image description here

gulpfile.js:

const gulp = require("gulp");
const inline = require("gulp-inline");

gulp.task("default", () => {
  return gulp
    .src("./dist/*/*.html")
    .pipe(inline())
    .pipe(gulp.dest("./single-dist"));
});

If you run npx gulp now it will generate a folder called single-dist with just a .html file, that include all html, css and js code.

To make this process easier, you can add a npm script like this:

"build-single": "npm run build && npx gulp"

This will automatically build the angular app into a single html file.

B45i
  • 2,368
  • 2
  • 23
  • 33
1

On Windows you can use type instead of cat and follow a similar approach proposed by @Milad:

1: Extend the scripts in package.json

"build-prod": "ng build --prod --output-hashing=none",
"package-es5": "cd dist/app-name && type runtime-es5.js polyfills-es5.js main-es5.js > bundle-es5.js",
"package-es2015": "cd dist/app-name && type runtime-es2015.js polyfills-es2015.js main-es2015.js > bundle-es2015.js",
"bundle": "npm run build-prod app-name && npm run package-es5 && npm run package-es2015",

2: Run the bundling process

npm run bundle

3: Replace the scripts automatically added in index.html manually

<script src="bundle-es2015.js" type="module"></script>
<script src="bundle-es5.js" nomodule defer></script>

To overcome the manual step, I'm using the following workaround:

1: Create an empty placeholder.html in the same folder as index.html

2: Copy the index.html file and rename it to index.prod.html

2: Modify index.prod.html with static stylesheet and scripts

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>app-name</title>
<base href="/">

<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.css"></head>
</head>
<body>

<app-root></app-root>

<script src="bundle-es2015.js" type="module"></script>
<script src="bundle-es5.js" nomodule defer></script>

</body>
</html>

4: Add index.html to the assets array in angular.json

"app-name": {
  "architect": {
    "build": {
      "options": {
        "assets": [
          "projects/app-name/src/index.html",
          ...
        ],

5: Modify the index entry in angular.json to point to placeholder.html and replace index.html with index.prod.html

"app-name": {
  "architect": {
    "build": {
      "configurations": {
        "production": {
          "index": "projects/app-name/src/placeholder.html",
          "fileReplacements": [
            {
              "replace": "projects/app-name/src/index.html",
              "with": "projects/app-name/src/index.prod.html"
            },
            ...
          ],          
niklr
  • 1,671
  • 3
  • 24
  • 40
0

Type:

.\\dist\\angular-project\\main.js .\\dist\\angular-project\\runtime.js .\\dist\\angular-project\\polyfills.js > .\\dist\\package.js

it's works for me.

4b0
  • 21,981
  • 30
  • 95
  • 142
  • You are saying thanks for an answer. But I do not see that user name. What are you referring to, with you expression of gratitude? – Yunnosch Sep 23 '22 at 16:36