RC0 Injecting Custom Scripts

Hey Guys,

Really enjoying RC0 so far, excellent work.

With the beta I had created custom GULP commands that would copy libs like JQuery and Font Awesome into the final build directory before the ionic build commands ran.

Since there’s no more gulp file to edit, I created a node script that I can run with “node copy-scripts.js” that does the same thing using the fs module. The idea is to adjust the primary build script to run this instead:
node copy-scripts.js && ionic-app-scripts build

It works perfectly, but then the Ionic Clean command runs and deletes everything! How do I inject the copy scripts command into the build system AFTER the clean command. I know I could just put the node copy-scripts.js after the build command, but this fails when I need to run the watch command.

Any ideas?

I am trying to accomplish the same thing. It works if you add a custom copy.config.js file in your package.json file.

"config": {
    "ionic_copy": "/path/relative/to/package.json/copy.config.js"
}

The original used in the ionic build script can be found in

./node_modules/@ionic/app-scripts/config/copy.config.js

However, I would to like to run a different node copy commands based on using ionic serve or ionic build platform. What is the file path where you have placed your copy-scripts.js file?

2 Likes

Thanks for the heads up on that pickup man! I have no idea how you found to use the “config” in your package file but it’s working!

You put this in the package.json file:

  "config": {
       "ionic_copy": "./copy-libs.js"
  }

And in the root folder of your Ionic project you make a file called “copy-libs.js”, here’s what I put in mine:

var fs = require('fs-extra')

var dependencies = [
    ['node_modules/jquery/dist/jquery.min.js','www/libs/jquery.min.js'],
    ['node_modules/js-md5/build/md5.min.js','www/libs/md5.min.js'],
    ['node_modules/moment/min/moment.min.js','www/libs/moment.min.js'],
    ['node_modules/font-awesome/css/font-awesome.min.css','www/libs/fa/font-awesome.min.css'],
    ['node_modules/font-awesome/fonts','www/libs/fonts']
];

dependencies.forEach(function(value) {
    fs.copy(value[0],value[1]);
});
5 Likes

I used a similar solution as well. Got to finally work how I wanted as well. I was wanting to copy a enviroment configuration file that changes depending on using ionic serve vs ionic build platform. That way I can dynamically change the api url’s. For others the following solution below will also work:

package.json

"scripts": {
"build": "ionic-app-scripts build --dev --copy ./config/production.js",
"watch": "ionic-app-scripts watch --copy ./config/development.js",
"serve:before": "watch",
"emulate:before": "build",
"deploy:before": "build",
"build:before": "build",
"run:before": "build"
 },

config/production.js

module.exports = {
  include: [
  {
    src: 'src/assets/',
    dest: 'www/assets/'
   },
  {
      src: 'src/index.html',
      dest: 'www/index.html'
  },
  {
    src: 'node_modules/ionic-angular/polyfills/polyfills.js',
    dest: 'www/build/polyfills.js'
  },
  {
    src: 'node_modules/ionicons/dist/fonts/',
    dest: 'www/assets/fonts/'
   },
   {
      src: 'config/config.production.ts',
      dest: 'src/config/config.ts'
    }
  ]
};

So my copy script just appends my config.ts file into the proper folder. The development version (config.development.js) will copy the file config/config.dev.ts to src/config/config.ts. I then am importing the config.ts file into my providers.

Both solutions works for either injecting scripts into your build like your version, or having a method for dynamically changing build files depending on the target.
`

1 Like

@scottlott - THANK YOU!!! :smile:

I guess it’s the risk with beta software of course but that info is EXACTLY what I needed, I’d buy you a beer if I could!

1 Like

I use a similar solution but if my config file already exists, the compilation stops with an error saying that the config module isn’t found (in my imports from ts files).
If I remove the config.ts (copied by the script) before launching the build, the error isn’t there…

Do you also have the issue ?

Is the config files being copied to the correct folders when you view in in the /src folder? Are you importing from new path where the file gets copied to:

IE. /config/config.production.ts gets copied to /src/config/config.ts. Then in my controller for that page I am importing using

// config/config.ts
import { Injectable } from '@angular/core';

@Injectable()
export class AppConfig {
	public prodMode: Boolean = false;
	public apiBaseUrl: String = '';
	public googleAnalyticsId: String = '';
	public loginDefaultEmail: String = '';
	public loginDefaultPassword: String = '';
	public supportEmail: String = '';
}

// src/provider/user.ts
import { AppConfig } from '../../config/config';

// @Injectable code here with template
class UserProvider {
   public factoryUrl: String;
   construct(public appConfig: AppConfig) {
       this.factoryUrl = this.appConfig.apiBaseUrl + '/user';
   }

   /**
   * Get user model from API
   * @param void
   * @return Observable
   */
   get(): Observable {
      return this.http.get(this.factoryUrl).map( res => res.json());
   }
}

With RC1 I have an issue here, even though the copy.config.js override can help copying external JS files to www, I find the JS reference in src/index.html is getting wiped when built to www/index.html. Any idea how I can prevent the JS included in index.html from being removed?

1 Like

Same issue here with RC1 :frowning: I did what @pickupman said but I can’t import jQuery and even if I change index.html in www folder manually.

When i use:
import * as $ from 'jquery';

I get this error:
bundle failed: Cannot call a namespace ('$')

I tried “jQuery” and “JQuery” as well.

Thus, my selectors are not working :frowning: Is there any instruction from zero to add jQuery to a Ionic 2 project with an example?

1 Like

Personally, I’ve never been able to actually import jquery.

I end up just putting this at the top of the files I use Jquery in:

declare var $:any;

This is a messy hack that doesn’t let you use any typescript goodness with jQuery, but it does work.

is this overriding the ionic copy script hat runs when you build or is it running both?
it’s named ionic_copy so I guess it overrides it.

I have recently updated to RC1 and updated my custom ionic_copy file that is processed by ionic-app-scripts. I am using the following command in my package.json file:

    "scripts": {
        "build": "ionic-app-scripts build --dev --copy ./config/production.js",
        "watch": "ionic-app-scripts watch --copy ./config/development.js",
     },

In my /config/development.js

var exports = require('@ionic/app-scripts/config/copy.config.js');

exports.include.push(
  {
    src: 'node_modules/jstimezonedetect/dist/jstz.min.js',
    dest: '{{BUILD}}/jstz.min.js'
  },
  {
      src: 'config/config.dev.ts',
      dest: 'src/config/config.ts'
  }
);

This now imports the standard ionc_copy files to be copied into the www/build folder. I am copying the jstz (Timezone detect) library into www/build/jstz.min.js and then also copying my config/config.dev.ts to src/config/config.ts. This may help others who may come back to this post. The latest changes in ionic-app-scripts have changed contents in their file breaking my build. I found importing that file, and then appending to its contents was easier to maintain in the future.

1 Like

@pickupman This does not work anymore in RC3 :frowning:

@xtof88 I just updated to RC3 and it is still working for me. Make sure you have update ionic and @ionic/app-scripts according to the changelogs.

Mind sharing some code or error messages?

@pickupman I’m using RC3 with @ionic/app-scripts@0.0.47. I’m utilizing a similar technique to what you described, but i’m doing the following:

var exports = require('@ionic/app-scripts/config/copy.config.js');

if (process.env.NODE_ENV === 'production') {
  exports['copyConfig'] = {
    src: '{{ROOT}}/config/prod/config.ts',
    dest: '{{SRC}}/config'
  };
} else {
  exports['copyConfig'] = {
    src: '{{ROOT}}/config/dev/config.ts',
    dest: '{{SRC}}/config'
  };
}

module.exports = exports;

This technique copies the file without a problem, but my main issue is that when I execute npm run ionic:build, the transpile and webpack steps start executing before my copy step has finished, so the copied file doesn’t get included in that particular build. Here’s the log of the build steps:

[16:44:42]  ionic-app-scripts 0.0.47
[16:44:42]  build dev started ...
[16:44:42]  clean started ...
[16:44:42]  clean finished in 4 ms
[16:44:42]  copy started ...
[16:44:42]  transpile started ...
[16:44:45]  transpile finished in 2.96 s
[16:44:45]  webpack started ...
[16:44:45]  copy finished in 3.11 s
[16:44:54]  webpack finished in 9.44 s

Any thoughts on getting transpile and webpack to wait until copying is done?

@pickupman I’m using a similar technique to what you’re describing. Here’s my configuration:

var exports = require('@ionic/app-scripts/config/copy.config.js');

if (process.env.NODE_ENV === 'production') {
  exports['copyConfig'] = {
    src: '{{ROOT}}/config/prod/config.ts',
    dest: '{{SRC}}/config'
  };
} else {
  exports['copyConfig'] = {
    src: '{{ROOT}}/config/dev/config.ts',
    dest: '{{SRC}}/config'
  };
}

module.exports = exports;

This works in the sense that it copies the config file correctly, but it does so out of sync with the rest of the build steps. When executing npm run ionic:build, by the time the copy step finishes, the transpile and webpack steps have already started. This causes the build to use the wrong config file some of the time.

Here’s an output of a build:

> npm run ionic:build
[16:44:42]  ionic-app-scripts 0.0.47
[16:44:42]  build dev started ...
[16:44:42]  clean started ...
[16:44:42]  clean finished in 4 ms
[16:44:42]  copy started ...
[16:44:42]  transpile started ...
[16:44:45]  transpile finished in 2.96 s
[16:44:45]  webpack started ...
[16:44:45]  copy finished in 3.11 s
[16:44:54]  webpack finished in 9.44 s
...

I’m using Ionic2 RC3 and @ionic/app-scripts@0.0.47. Any thoughts on how to make the other steps wait until the file copying is done?

1 Like

Is it possible to run a script node in the act that I execute the android run?

@gceduvieira
Quite a bit has changed in the build process since I posted this. I am still using a similar setup, but it is a little different now using Ionic 2/3 release.

I create two configuration files, /config/production/config.ts and /config/development/config.ts. There is no config folder in the root of a ionic project, so you will need to create those folders along with the two environment subfolders. Next create a folder in /src called config so you end up with the structure /src/config/. Now will have added three new folders.

Next add a .gitignore file inside of /src/config/. Create two more files /config/production.js, and /config/development.js

/config/production.js

var fs = require('fs-extra');
var path = require('path');

console.log('Copying production environment configuration');

fs.copy(path.join(__dirname, 'production', 'config.ts'), path.join(__dirname, '..', 'src', 'config', 'config.ts'), function(err){
    if (err) {
        console.log('Error while copying production configuration');
        return console.log(err);
    }
});

/config/development.js

var fs = require('fs-extra');
var path = require('path');

console.log('Copying development environment configuration');

fs.copy(path.join(__dirname, 'development', 'config.ts'), path.join(__dirname, '..', 'src', 'config', 'config.ts'), function(err){
    if (err) {
        console.log('Error while copying development configuration');
        return console.log(err);
    }
});

/config/development/config.ts

import { Injectable } from '@angular/core';

@Injectable()
export class AppConfig {
	public prodMode: Boolean = false;
	public apiBaseUrl: String = '';
	public googleAnalyticsId: String = '';
	public loginDefaultEmail: String = '';
	public loginDefaultPassword: String = '';
	public supportEmail: String = '';
	public googleApiKey: String ='';
}

/config/production/config.ts

import { Injectable } from '@angular/core';

@Injectable()
export class AppConfig {
	public prodMode: Boolean = true;
	public apiBaseUrl: String = 'http://your-api-url.com';
	public googleAnalyticsId: String = '';
	public loginDefaultEmail: String = '';
	public loginDefaultPassword: String = '';
	public supportEmail: String = '';
	public googleApiKey: String ='';
}

You need to import the config into your /src/app/app.module.ts file.

import { AppConfig } from '../config/config';

// Add to providers array
providers: [
    AppConfig,
... Your other providers ...
]

If you are running Mac or Linux, you may want to run chmod +x /config/production.js to make the scripts executable. Now we need to add two lines to your package.json that will handle automatically copying the environment config file.

package.json

// Add to your scripts object 
scripts: {
    "ionic:build:before": "node ./config/production.js",
    "ionic:watch:before": "node ./config/development.js"
}

Now whenever you run ionic serve it will copy the development config.ts file into the web browser, and running ionic cordova build <platform> it will copy the production config.ts file into the app. I then have ignored changes to /src/config/config.ts as it will change based on the environment I am targeting. I am also importing the AppConfig module into my providers. I create a baseUrl property in provider like:

import { AppConfig } from '../../config/config';

@Injectable()
class AuthProvider
{
   public baseUrl: string;

   constructor(public appConfig: AppConfig, public http: Http)
   {
        this.baseUrl = this.appConfig.apiBaseUrl + '/auth';
   }

   login(credentials: {email: string, password: string}): Observable
   {
        return this.http.post(this.baseUrl + '/login', credentials).map( res => res.json() );
   }
}

Using this you can easily use different domains for testing and production.

Hi all,

Just posting an update as configs seem to have changed since the original replies. This is what worked for us:

  1. Add this to package.json
    "config": {
        "ionic_copy": "./copy.config.js"
    }
  1. Create a new file copy.config.js (in the same folder as package.json) with this code:
var exports = require('@ionic/app-scripts/config/copy.config.js');

exports.copyIndexContent.src.push('{{SRC}}/<FILE_NAME>');

Note: <FILE_NAME> is whatever you want to copy, for example: aws-config.js

P.S.: this reference explains how to customize the build process: https://ionicframework.com/docs/developer-resources/app-scripts/

Cheers

4 Likes

Nice and looking more simple. It worked for me with

"ionic_copy": "./config/copy.config.js"

Thanks, @ricardos