Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Integrating file minification into Cordova/Phonegap build process

I read somewhere that there is a noticeable performance boost if Javascript files are minified in Cordova/Phonegap application.

I decided to integrate minification scripts into my build process but I cannot find appropriate moment and folder where it is safe to minify files.

Obviously, I don't want to change files in the global www folder during build because we are developing in the global www folder.

Most probably, I should apply minification to files in www folder for each platform after Cordova updates them from the global www folder (and maybe merges some platform speciofic css from the merges folder). This means, I can't use cordova before_prepare hook - it's too early, files don't exist yet in platform-specific www folders.

Thus we are left with cordova after_prepare hook script. I tried it and failed. At after_prepare moment cordova has already generated platform-specific project files. For example, the Windows Phone csproj file already refecences all the files which where initially in the global www folder, and if I remove my original js files and add new minified bundles, I get a build error about failed XAP packaging.

Conclusion: before_prepare is too early and after_prepare is too late.

How do I execute my minifying build action after files in platform www folders are updated but before they are referenced in platform-specific project build files?

like image 392
JustAMartin Avatar asked Jun 18 '14 14:06

JustAMartin


2 Answers

As per Cordova official site: the below approach is the recommended one to use hooks.

A Cordova application built with cordova-cli will have the following directory structure:

myApp/
|-- config.xml
|-- hooks/
|-- merges/
| | |-- android/
| | |-- blackberry10/
| | `-- ios/
|-- www/
|-- platforms/
| |-- android/
| |-- blackberry10/
| `-- ios/
`-- plugins/

hooks/

This directory may contains scripts used to customize cordova commands. This directory used to exist at .cordova/hooks, but has now been moved to the project root. Any scripts you add to these directories will be executed before and after the commands corresponding to the directory name. Useful for integrating your own build systems or integrating with version control systems.

Cordova Hooks represent special scripts which could be added by application and plugin developers or even by your own build system to customize cordova commands. Hook scripts could be defined by adding them to the special predefined folder (/hooks) or via configuration files (config.xml and plugin.xml) and run serially in the following order:

  1. Application hooks from /hooks;
  2. Application hooks from config.xml;
  3. Plugin hooks from plugins /.../plugin.xml.

Remember: Make your scripts executable.

The following hook types are supported:

after_build/
after_compile/
after_docs/
after_emulate/
after_platform_add/
after_platform_rm/
after_platform_ls/
after_plugin_add/
after_plugin_ls/
after_plugin_rm/
after_plugin_search/
after_plugin_install/   <-- Plugin hooks defined in plugin.xml are executed exclusively for a plugin being installed
after_prepare/
after_run/
after_serve/
before_build/
before_compile/
before_docs/
before_emulate/
before_platform_add/
before_platform_rm/
before_platform_ls/
before_plugin_add/
before_plugin_ls/
before_plugin_rm/
before_plugin_search/
before_plugin_install/   <-- Plugin hooks defined in plugin.xml are executed exclusively for a plugin being installed
before_plugin_uninstall/   <-- Plugin hooks defined in plugin.xml are executed exclusively for a plugin being uninstalled
before_prepare/
before_run/
before_serve/
pre_package/ <-- Windows 8 and Windows Phone only.

Ways to define hooks

(1) Via '/hooks' directory

To execute custom action when corresponding hook type is fired, use hook type as a name for a subfolder inside 'hooks' directory and place you script file here, for example:

# script file will be automatically executed after each build
hooks/after_build/after_build_custom_action.js

(2) Config.xml

Hooks can be defined in project's config.xml using elements, for example:

<hook type="before_build" src="scripts/appBeforeBuild.bat" />
<hook type="before_build" src="scripts/appBeforeBuild.js" />
<hook type="before_plugin_install" src="scripts/appBeforePluginInstall.js" />

<platform name="wp8">
    <hook type="before_build" src="scripts/wp8/appWP8BeforeBuild.bat" />
    <hook type="before_build" src="scripts/wp8/appWP8BeforeBuild.js" />
    <hook type="before_plugin_install" src="scripts/wp8/appWP8BeforePluginInstall.js" />
    ...
</platform>

<platform name="windows8">
    <hook type="before_build" src="scripts/windows8/appWin8BeforeBuild.bat" />
    <hook type="before_build" src="scripts/windows8/appWin8BeforeBuild.js" />
    <hook type="before_plugin_install" src="scripts/windows8/appWin8BeforePluginInstall.js" />
    ...
</platform>

(3) Plugin hooks (plugin.xml)

As a plugin developer you can define hook scripts using elements in a plugin.xml like that:

<hook type="before_plugin_install" src="scripts/beforeInstall.js" />
<hook type="after_build" src="scripts/afterBuild.js" />

<platform name="wp8">
    <hook type="before_plugin_install" src="scripts/wp8BeforeInstall.js" />
    <hook type="before_build" src="scripts/wp8BeforeBuild.js" />
    ...
</platform>

before_plugin_install, after_plugin_install, before_plugin_uninstall plugin hooks will be fired exclusively for the plugin being installed/uninstalled.

3 best examples of writing hooks with source code

  1. Add Plugins via Hooks example:
  2. Replace Text Depending on Environment (DEV,PROD,UAT) via hooks example
  3. Copy Icons and Splashscreens via hooks example

http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/

It is strongly recommended to read followings:

  • https://github.com/apache/cordova-cli#project_commands
  • https://github.com/apache/cordova-lib/blob/master/cordova-lib/templates/hooks-README.md
  • Three hooks your Cordova/PhoneGap project needs
like image 116
AAhad Avatar answered Sep 28 '22 00:09

AAhad


At the end it turned out that it was not so hard to implement a new hook in Cordova's prepare.js file (on Windows its located in %appdata%\npm\node_modules\cordova\src).

I called the new hook after_platform_www_prepare and it is called once for each platform right after the www folder is prepared but before platform-specific config files are generated. One confusion was with Cordova's parser.update_www function because for Windows and WP platforms it has a misleading comment:

// Replace the www dir with contents of platform_www and app www and updates the csproj file.

When looking through the code I found out that this function actually does not update the project file, there's another parser.update_project function for that purpose, and it's called separately at the very end in prepare.js file, thus it seemed safe to add my custom hook between parser.update_www and parser.update_project calls.

Also I added a custom hacky workaround to pass the platform name currently being processed to the hook script.

After that, I created hooks\after_platform_www_prepare folder and created a script which calls Grunt task which in turn concatenates and minifies CSS and JS files, processes index.html to replace references and finally deletes the uncompressed files which were previously referenced. I won't post my Grunt tasks here - that's worth another topic - but I'll post the relevant code fragment with my Corodva prepare.js fix. It seems to work fine but still I'm not sure that it won't break anything, so use at your own risk. Here you go:

// Replace the existing web assets with the app master versions
parser.update_www();
events.emit('log', 'Updated platform WWW folder for platform "' + platform + '"');

// pass only the current platform, use clone to avoid breaking the global options object
// hacky cloning, no time to look for better way, no extend available here...
var optionsPLatform = JSON.parse(JSON.stringify(options));
optionsPLatform.platforms = [platform];
// now the platform name will be available as process.env.CORDOVA_PLATFORMS in your hook scripts

// be warned that these hooks will execute in parallel for each platform!
// thus you should process only files for the current platform in your hook script 
// to avoid conflicts with other platforms
return hooks.fire('after_platform_www_prepare', optionsPLatform)
    .then(function () {
        events.emit('log', 'after_platform_www_prepare completed for platform "' + platform + '"');
        // .. some Cordova's code omitted for brevity

        return parser.update_project(cfg);
    });
});
like image 24
JustAMartin Avatar answered Sep 27 '22 23:09

JustAMartin