Rebel Fornea
Rebel Fornea

Reputation: 71

Run Angular CLI scripts from a script file instead of from the CLI? Localization/Internationalization

We're setting ourselves up to do internationalization/localization in our Angular 2 app, and I'd like to be able to write some scripts that will do various tasks, such as generating translation source files, building and/or serving the application with the translations from a particular language.

Rather than writing the scripts in the package.json file, I'd like to be able to capture the scripts in separate files in a scripts/directory and the scripts in the package.json file would just point to those.

For example, I'd like to take something like this in package.json:

"scripts": {

    //other irrelevant scripts

    "generateXLF": "ng-xi18n --i18nFormat=xlf  --outFile=./src/locale/baseFile/messages.xlf",

    //other irrelevant scripts
},

And instead have something like this:

"scripts": {

    //other irrelevant scripts

    "generateXLF": "node ./build/scripts/locale/generateXLF.js"

    //other irrelevant scripts
},

Or instead of something like this:

"scripts": {

    //other irrelevant scripts

    "serveLocale": : "./node_modules/.bin/ngc --i18nFile=./src/locale/languages/($someLocaleIPassIn)/messages.($someLocaleIPassIn).xlf --locale=($someLocaleIPassIn) --i18nFormat=xlf",

    //other irrelevant scripts
},

And instead have this:

"scripts": {

    //other irrelevant scripts

    "serveLocale": : "node ./build/scripts/locale/serveLocale.js $someLocaleIPassIn"

    //other irrelevant scripts
},

Basically I need to do the following:

Currently I'm part of the way there. For example in my generateXLF.js file I'm currently just trying to run ng serve, with no parameters or flags, like so:

var ngServe = require('@angular/cli/commands/serve');
new ngServe.default();

but I keep getting this error:

C:\Projects\MyProject\StyleGuide\node_modules\@angular\cli\ember-cli\lib\models\command.js:77
    this.isWithinProject = this.project.isEmberCLIProject();
                                   ^

TypeError: Cannot read property 'isEmberCLIProject' of undefined
    at Class.init (C:\Projects\MyProject\StyleGuide\node_modules\@angular\cli\ember-cli\lib\models\command.js:77:40)
    at Class.superWrapper [as init] (C:\Projects\MyProject\StyleGuide\node_modules\core-object\lib\assign-properties.js:34:20)
    at Class.CoreObject (C:\Projects\MyProject\StyleGuide\node_modules\core-object\core-object.js:9:15)
    at Class (C:\Projects\MyProject\StyleGuide\node_modules\core-object\core-object.js:21:5)
    at Class (C:\Projects\MyProject\StyleGuide\node_modules\core-object\core-object.js:21:5)
    at C:\Projects\MyProject\StyleGuide\build\scripts\locale\generateXLF.js:32:5
    at Object.<anonymous> (C:\Projects\MyProject\StyleGuide\build\scripts\locale\generateXLF.js:37:3)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)

Process finished with exit code 1

ng serve works just fine when I run it from the CLI but not when I try to run it from a script. I'm guessing somehow I need to set the script file up to know where my .angular-cli.json file and my package.json file is, and to do things with the information in those files to make ng serve run correctly from a script file. But I may be totally wrong about why it's failing.

TLDR: How do I successfully run Angular CLI scripts, with flags, from a script file? How to pick up parameters that I've passed?

Upvotes: 3

Views: 6532

Answers (2)

Stephanie Cray
Stephanie Cray

Reputation: 13

Use angularCli.default({}) instead of angularCli({})

Upvotes: 0

Rebel Fornea
Rebel Fornea

Reputation: 71

Ended up figuring it out. Here is my script, compileInLocale.js:

#! /usr/bin/env node

const angularCli = require('@angular/cli');
const fileSystem = require('fs');
const chalk = require('chalk');

var command = process.argv.slice(2, 3).toString();
var locale = process.argv.slice(3, 4).toString();

console.log(chalk.bgBlueBright.bold('Attempting to execute ' + __filename + ' with command "' +
command + '" and locale "' + locale + '".\n'));

makeArguments(command, locale);

/**
 * @name makeArguments
 * @description Create the array of arguments to send to the Angular CLI
 * @param {String} commandToRun the ng command we want to run, such as 'serve' or 'build'
 * @param {String} specifiedLocale the locale that the developer specified when they called
 * the script.  For example, the string 'es' from the following command line
 * entry:  'npm run serveInLocale es'
 * @returns {void} Nothing.
 */
function makeArguments(commandToRun, specifiedLocale) {

var localeFile = './src/locale/languages/' + specifiedLocale + '/messages.' + specifiedLocale + '.xlf';

if (fileSystem.existsSync(localeFile)) {

    /*
     @TODO: add in logic to build the base translation file, then compare the contents of the
     @TODO <source> tags in the translation file to the <source> tags in the file we are trying
     @TODO to serve to see if it is up-to-date.
     */

    var argArray = [
        commandToRun,
        '--aot',
        '--i18nFile=' + localeFile,
        '--locale=' + specifiedLocale,
        '--i18nFormat=xlf'
    ];

    console.log(chalk.bgGreen.bold('Preparing to ' + commandToRun + ' file ' + localeFile + '.\n'));

    serveInSpecifiedLocale(argArray);
}
else {
    console.log(chalk.bgRed.bold('Cannot ' + commandToRun + ' file ' + localeFile + '.  File does not exist!\n'));
}
}

/**
 * @name serveInSpecifiedLocale
 * @description Send an object to the Angular CLI containing our arguments array, and other
 * properties that Angular CLI needs to execute our command.
 * @param {Array} argArray the array of arguments we want to give to the CLI
 * @returns {void} Nothing.
 */
function serveInSpecifiedLocale(argArray) {

/*
@TODO: move this to its own file.  We will have more scripts in the
@TODO future which need to send arguments to the Angular CLI.
*/

angularCli({
    cliArgs: argArray,
    inputStream: process.stdin,
    outputStream: process.stdout
});
}

Run it with a command such as this:
npm run buildInLocale es
or this:
npm run serveInLocale fr

In package.json have something like this:

"serveInLocale": "node ./build/scripts/locale/compileInLocale.js serve",
"buildInLocale": "node ./build/scripts/locale/compileInLocale.js build"    

Depending on whether you typed buildInLocale or serveInLocale, the script will be hit with either serve or build as an argument, followed by the locale as another argument, and if we have a file for that locale, the script works.

Sidenote: I ended up doing a bunch of work to figure this out for no reason. It's pointless because the native tool in Angular 2 to do internationalization (the i18n attribute) doesn't have a means to support translations when 2-way binding is involved, so we are going to end up using something like ngx-translate instead. I still wanted to post my solution though in case someone else needs to write a script to let them run scripts that would normally be ran from the CLI.

Upvotes: 3

Related Questions