The property apps in the angular-cli.json file is of array type. If I add a second element into this array, how can I instruct ng build to build both elements?
7 Answers
Currently v1.0.0 you can only select app which you want to build by the following command:
ng build -a appName
or
ng build --app appName
you also will need to add name property to each element in the apps array so you will have something like that:
"apps": [
{
"name": "app1",
"root": "src/app1root",
...
},
{
"name": "app2",
"root": "src/app2root",
...
},
...
]
also you can use app indices like ng build -a 0 or ng build -a 1 in that case you don't need to specify app names.
From angular-cli sorces you can see that there is no possibility to run all apps in one command, you should either specify index either app name otherwise apps[0] will be used, so you can't build all apps at the same time using one ng build call.
Comments
I searched the angular-cli source code but could not find any reference to code that iterates or otherwise inspects the contents of apps as an array.
As of now (angular-cli version 1.0.0-beta.15), every instance of code that deals with apps uses the first element of the array hardcoded (apps[0]). There does not seem to be a way to select an app to build or alter the default behaviour of using the first element of the array.
The JSON schema for the apps element describes it this way:
Properties of the different applications in this project.
/**
* Properties of the different applications in this project.
*/
apps?: {
root?: string;
outDir?: string;
assets?: string;
index?: string;
main?: string;
test?: string;
tsconfig?: string;
prefix?: string;
mobile?: boolean;
/**
* Global styles to be included in the build.
*/
styles?: string[];
/**
* Global scripts to be included in the build.
*/
scripts?: string[];
/**
* Name and corresponding file for environment config.
*/
environments?: {
[name: string]: any;
};
}[];
It seems to be a future intent of the project to support building multiple apps out of the same code base but it does not look like it is something doable right now (1.0.0-beta.15 version).
4 Comments
As of October 28th, 2019 the proper way from the docs to build a specific project:
ng build <app name>
There is an open issue in GitHub to add an ability to build multiple projects at once.
Comments
Still there isn't any flag for ng build --app to build all apps with one command, so best way to solve this is to create Makefile file in root of project:
my-app
--dist/
--e2e/
--src/
.angular-cli.json
package.json
Makefile
Makefiles allows to organize code compilation according of instructions you provided. So we need to provide instruction to build frontend for all apps into an output directory. Now Makefile file looks like this:
help:
@echo "_______BUILD ANGULAR FRONTEND______\n"
@echo "To build all apps run make make build-frontend"
build-frontend:
ng build -a=0 &&\
ng build -a=1
However after you copy-paste code above, change spaces into tab. Otherwise you will get invalid Makefile with *** missing separator error
Navigate to root project and just to test, type make in terminal and you should get help message printed. After that type:
make build-frontend
Now you have multiple apps build with just one command.
1 Comment
I also answered a similar question here -> Angular 6 CLI -> how to make ng build build project + libraries
This is for >= ng6. If you'd like something for older versions of the cli, you can take a look at this version -> https://gist.github.com/bmarti44/f6b8d3d7b331cd79305ca8f45eb8997b/03c3b788551cd43db38d2f48e207a730aaba5b6f
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);
});
}
});