Angular 6 CLI -> how to make ng build build project + libraries
Asked Answered
P

9

29

So the question is pretty basic but I can't find it.

I created a new app through ng new my-project, followed by a ng g library my-library. Then I executed the command ng build, but is is only building my app, and not my library or my e2e projects. This is because in the angular.json defaultProject is set to my-project. I could change it to my-library, and then ng build will build the lib.

Is there a way to let angular build all the project and the libraries in one ng-build?

Priorate answered 4/5, 2018 at 18:30 Comment(0)
O
19

I just added a script to package.json to do that, could not find a better way.

  "scripts": {
    "build-all": "ng build lib1 && ng build lib2 && ng build",
    "build-all-prod": "ng build lib1 --prod && ng build lib2 --prod && ng build --prod"
  },

and then

yarn run build-all
Orang answered 10/5, 2018 at 21:47 Comment(2)
but that creates them in seperate builds and each build deployed seperateKennethkennett
I don't understand why this is the accepted answer. As @ChrisTarasovs said, this creates separate deployments. I've actually also tried to simply edit the built index.html to include the separate library, but it's one error after another. Still looking for a solution.Muslin
B
5

Currently there is no supported way to do this out of the box. As suggested by @oklymenk you should for now go with a custom script which will chain all these build commands.

Also the link shared by @Eutrepe, you can see that they are planning to get rid of this re build thing everytime you make changes to your library.

Running ng build my-lib every time you change a file is bothersome and takes time.

Some similar setups instead add the path to the source code directly inside tsconfig. This makes seeing changes in your app faster.

But doing that is risky. When you do that, the build system for your app is building the library as well. But your library is built using a different build system than your app.

Those two build systems can build things slightly different, or support completely different features.

This leads to subtle bugs where your published library behaves differently from the one in your development setup.

For this reason we decided to err on the side of caution, and make the recommended usage the safe one.

In the future we want to add watch support to building libraries so it is faster to see changes.

We are also planning to add internal dependency support to Angular CLI. This means that Angular CLI would know your app depends on your library, and automatically rebuilds the library when the app needs it.

Why do I need to build the library everytime I make changes?

Bestialize answered 17/5, 2018 at 7:16 Comment(0)
R
1

I created a script that, when placed in the same folder as angular.json, will pull in the file, loop over the projects, and build them in batches asynchronously.

Here's a quick gist, you can toggle the output path and the number of asynchronous builds. I've excluded e2e for the moment, but you can remove the reference to the filteredProjects function, and it will run for e2e as projects as well. It would also be easy to add this to package.json as an npm run script. So far, it has been working well.

https://gist.github.com/bmarti44/f6b8d3d7b331cd79305ca8f45eb8997b

const fs = require('fs'),
  spawn = require('child_process').spawn,
  // Custom output path.
  outputPath = '/nba-angular',
  // Number of projects to build asynchronously.
  batch = 3;

let ngCli;

function buildProject(project) {
  return new Promise((resolve, reject) => {
    let child = spawn('ng', ['build', '--project', project, '--prod', '--extract-licenses', '--build-optimizer', `--output-path=${outputPath}/dist/` + project]);

    child.stdout.on('data', (data) => {
      console.log(data.toString());
    });

    child.stderr.on('data', (data) => {
      process.stdout.write('.');
    });

    child.on('close', (code) => {
      if (code === 0) {
        resolve(code);
      } else {
        reject(code);
      }
    });
  })
}

function filterProjects(projects) {
  return Object.keys(projects).filter(project => project.indexOf('e2e') === -1);
}

function batchProjects(projects) {
  let currentBatch = 0,
    i,
    batches = {};

  for (i = 0; i < projects.length; i += 1) {
    if ((i) % batch === 0) {
      currentBatch += 1;
    }
    if (typeof (batches['batch' + currentBatch]) === 'undefined') {
      batches['batch' + currentBatch] = [];
    }

    batches['batch' + currentBatch].push(projects[i]);
  }
  return batches;
}

fs.readFile('angular.json', 'utf8', async (err, data) => {
  let batches = {},
    batchesArray = [],
    i;

  if (err) {
    throw err;
  }

  ngCli = JSON.parse(data);

  batches = batchProjects(filterProjects(ngCli.projects));
  batchesArray = Object.keys(batches);

  for (i = 0; i < batchesArray.length; i += 1) {
    let promises = [];

    batches[batchesArray[i]].forEach((project) => {
      promises.push(buildProject(project));
    });

    console.log('Building projects ' + batches[batchesArray[i]].join(','));

    await Promise.all(promises).then(statusCode => {
      console.log('Projects ' + batches[batchesArray[i]].join(',') + ' built successfully!');
      if (i + 1 === batchesArray.length) {
        process.exit(0);
      }
    }, (reject) => {
      console.log(reject);
      process.exit(1);
    });
  }
});
Rockhampton answered 17/7, 2018 at 14:51 Comment(2)
If you come across an error calling spawn then specify {shell: true} for the third parameter.Mukund
I added the ability to specify the --prod flag or not. const prod = process.argv[2] === "prod" ? "--prod" : ""; const outputPath = prod === "" ? "--output-path=dist/unminified/" + project : ""; return new Promise((resolve, reject) => { let child = spawn('ng', ['build', '--project', project, prod, outputPath], {shell: true});`Mukund
A
1

Maybe this works for you:

Build the library with ng build --prod --project=your-library, then in your package.json dependencies:

"example-ng6-lib": "file:../example-ng6-lib/dist/example-ng6-lib/example-ng6-lib-0.0.1.tgz",

Then ng build --prod your root project.

Example taken from here: https://blog.angularindepth.com/creating-a-library-in-angular-6-part-2-6e2bc1e14121

Assay answered 26/9, 2018 at 5:50 Comment(0)
P
1
"build-all": "for PROJ in lib1 lib ''; do ng build $PROJ --prod || break; done"
  • The '' provides an empty string to the loop, building the top-level project
  • The || break stops the for loop once a library build fails

OR the follow works if

  • Build system has jq
  • None of your libraries have dependencies on each-other
    • This blindly builds over a list of projects found in angular.json
  • Use scoped packages for your libraries
    • Why would you not?!
    • See next bullet's reason
  • Top-level project is not scoped
    • Normally my top-level is a demo project, never published, so never scoped
    • Reason for this is jq always returns @ prefixed keys first in the array to loop over

"build-all": "for PROJ in $(cat angular.json | jq -r '.projects | keys[]'); do ng build $PROJ --prod; done"

At some point I want to look into building up a dependency tree based on the package.json(s) found in each project and sort the projects to build based on that. Just not a high priority over up-keeping a hard-coded list from the first suggestion :)

Polysynthetic answered 6/12, 2020 at 20:27 Comment(0)
C
0

I find and test this: https://github.com/angular/angular-cli/wiki/stories-create-library

So instead ng build --prod you should use ng build my-lib --prod

Coccidioidomycosis answered 5/5, 2018 at 14:55 Comment(1)
I know. But I need a command to build the main app, AND all my libraries in the projects folder.Priorate
G
0

As much as I know, there is no build-in way to do this in the current version (Angular 8).
It might be possible to use the new builders but I don't know much about them yet.
So what I did instead was to create a script, which reads in the angular.json file and determines all application projects and all configurations.
Then it executes ng build for every project and configuration.
Also, it will collect all the failed builds and log them to the console at the end.
This script looks like this:

import { ProjectType, WorkspaceSchema } from "@schematics/angular/utility/workspace-models";
import { execSync } from "child_process";
import { readFileSync } from "fs";

interface ExecParams {
  project: string;
  config: string;
}

interface ExecError extends ExecParams {
  message: string;
}

function buildAll() {
  const json: WorkspaceSchema = JSON.parse(readFileSync("./angular.json").toString());
  const errors: ExecError[] = Object.keys(json.projects)
    // Filter application-projects
    .filter(name => json.projects[name].projectType === ProjectType.Application)
    // Determine parameters needed for the build command
    .reduce<ExecParams[]>((arr, project) => {
      const proj = json.projects[project];
      let build = proj.architect && proj.architect.build;
      if (build) {
        arr = arr.concat(...Object.keys(build.configurations || {})
          .map(config => ({ project, config }))
        );
      }
      return arr;
    }, [])
    // Execute `ng build` and collect errors.
    .reduce<ExecError[]>((err, exec) => {
      try {
        console.log(`Building ${exec.project} (${exec.config}):`);
        execSync(`ng build --prod --project ${exec.project} --configuration ${exec.config}`, {stdio: "inherit"});
      }
      catch (error) {
        err.push({
          project: exec.project,
          config: exec.config,
          message: error.message
        });
      }
      console.log("\n");
      return err;
    }, []);

  // Conditionally log errors
  if (errors.length === 0)
    console.log("Completed");
  else {
    console.log("\n");
    errors.forEach(error => {
      console.error(`Building ${error.project} (${error.config}) failed:\n\t${error.message}`);
    });
  }
}

buildAll();

You can compile it using tsc and then run it with NodeJs.

Gosh answered 29/8, 2019 at 5:53 Comment(0)
C
0

use with simple node js like this:

create js file in ~App root (e.g: build.js).

then placed the following codes in your file.

var child_process = require('child_process');

console.log("building all packages...\n");

var files = GetProjectList();
var count = files.length;
var counter = 1;

files.forEach(item => 
{
    try 
    {
        console.log(`${counter++} of ${count} building ${item.fileName}...`);
        child_process.execSync(`ng b ${item.fileName}`);
        console.log(`${item.fileName} built successfully.\n`);
    } 
    catch (er) 
    {
        console.error(` Couldn't build ${item.fileName} .\n`);
    }
});



function GetProjectList()
{
    // enter list of projects here.
    var lst = [];
    lst.push({ fileName : 'project1' });
    lst.push({ fileName : 'project2' });
    lst.push({ fileName : 'project3' });
    //...
    return lst;
}

Finally, run your file with node js command. like this:

node build.js             // `build.js` is name of your created file on above

pay attention to the unicode your file. // build.js

it's working well.

I hope is useful.

Chalcopyrite answered 15/12, 2019 at 5:8 Comment(0)
S
-3

ng-build already includes your libraries in the main.js bundle. No need to separately build each library.

Spillway answered 12/6, 2019 at 10:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.