Ionic apps utilize TypeScript and Sass code. This code needs to be converted into web browser friendly code. A build process is required to achieve this.
@ionic/app-scripts
are a set of configurable scripts provided to make it easy to create a simple or a highly customized build process.
How does it work?
Ionic app’s are developed typically using the ionic serve
and ionic cordova run
commands. Both of these commands need to compile the application’s code and combine it into one bundled file.
When ionic serve
or ionic cordova run
are invoked, it ultimately calls an NPM script. These npm scripts call the @ionic/app-scripts
library to execute the build process.
Why App Scripts?
Most application’s share a similar build process. Rather than have each user create their own build process from scratch for their app, we thought that time would be better spent building out their Ionic app and leaving the details to us. This is our interpretation of what most users need from a build process.
What About Gulp?
Historically Ionic apps have used gulp as tool to facilitate the build process. Over the years, gulp
has seen less and less development, and has started to accumulate deprecation warnings. By taking advantage of standard and dependency free NPM scripts, we are reducing dependencies and simplifying the development experience.
Note: Developers are free to still use gulp if they choose to do so. We think NPM scripts are a better approach, but there is nothing in place restricting the use of gulp if that is the preferred method.
What Scripts are Provided?
@ionic/app-scripts
provides the following scripts:
-
build :
build
calls a set of@ionic/app-scripts
to compile theTypeScript
source, compilesass
, create a bundlejavascript
file, etc. -
bundle :
bundle
uses Rollup.js to produce a single, high performancejavascript
file from the many smallerjavascript
files. -
clean :
clean
deletes any of the files generated by thebuild
process. Basically, it deletes thewww
directory. -
cleancss :
cleancss
uses CleanCSS to minifycss
to it’s smallest, fastest form. -
closure :
closure
uses Google’s Closure Compiler to minifyjavascript
to it’s smallest, fastest form. -
copy :
copy
copies files where they need to go to end up in the builtwww
directory. -
lint :
lint
runs TSLint against theTypeScript
source code. Requires atslint.json
file at the project root. -
minify :
minify
calls the minification tasks forjavascript
(uglify, or closure if available) andcss
(cleancss). -
ngc :
ngc
calls the Angular Ahead-of-time compiler to create extremely fast apps. -
sass :
sass
walks the application’s directory tree to findscss
files and assemble them into onecss
file. -
template :
template
is used to convert Angular templates intoinline-templates
for faster loading and faster applications during the development process. -
transpile :
transpile
convertsTypeScript
code intojavascript
code. -
tsc :
tsc
calls theTypeScript
compiler. -
uglifyjs :
uglifyjs
is used to minifyjavascript
to it’s smallest and fastest form. -
watch :
watch
creates a smaller, fasterbuild
process to be used during application development.
Providing Custom Build Configuration
The default configured provided by @ionic/app-scripts
covers many of the scenarios required by developers. However, if a developer wants to customize and configure the way the build process is run, they can do so.
The easiest way to configure @ionic/app-scripts
is to take advantage of the config option in the package.json
file.
To get started, add a config
entry to the package.json
file. From there, developers can provide their own configuration file for things like minification
(closure compiler, uglify2), and bundling
(rollup).
See an example of specifying a custom configuration file below:
"config": {
...
"ionic_rollup": "./config/rollup.config.js",
"ionic_cleancss": "./config/cleancss.config.js",
...
},
In the above example, custom configurations for rollup
and cleancss
are provided by a path to a config file.
The following config
values are used to map to a task’s config file.
Config File | NPM Config Property |
---|---|
CleanCss | ionic_cleancss |
Closure Compiler | ionic_closure |
Copy | ionic_copy |
NGC | ionic_ngc |
Rollup | ionic_rollup |
Sass | ionic_sass |
TSLint | ionic_tslint |
UglifyJS | ionic_uglifyjs |
The default configurations are great examples to learn about customization options.
Note: Command line flags can also be used to provide custom configuration.
Custom Project Structure
In most cases, the default project structure provided by Ionic works great! However, if a developer chooses to modify the structure to suit their use case, they are welcome to do so.
Using the same concepts outlined above for providing configuration using the package.json
config
option, developers can specify their own custom project structure.
Config Values | NPM Config Property | Defaults |
---|---|---|
root directory | ionic_root_dir |
process.cwd() |
tmp directory | ionic_tmp_dir |
.tmp |
src directory | ionic_src_dir |
src |
www directory | ionic_www_dir |
www |
build directory | ionic_build_dir |
build |
Ionic Environment Variable
An environment variable, IONIC_ENV
is available in the build process to help determining whether a build is a development
build, or a production
build. The environment variable can be accessed like this:
if (process.env.IONIC_ENV === 'prod') {
console.log('we got a production buildp');
} else {
console.log('we got a development build');
}
Task Details
Bundle
Ionic uses a tool called Rollup to combine multiple javascript
files into one combined file. This process is often called bundling code for deployment.
Third Party Modules Export Errors
When using third party libraries in your code, sometimes Rollup needs additional information about the structure of the library code. Most of the time everything “Just Works”. In rare cases, libraries get too clever in how they’re exporting
data, so we need to to tell Rollup what is being exported. This process is called providing a namedExport.
If you have an error message that looks something like this, continue on.
bundle failed: Module myApp/node_modules/js-extend/extend.js does not export extend (imported by myApp/node_modules/pouchdb/lib/index-browser.es.js)
The best way to do this is to follow the steps above to create a custom rollup config file. To get started with that, open the project’s package.json
file. If it doesn’t already exist, create a node at the root level for config
, and then add a key for ionic_rollup
as follows
"config": {
"ionic_rollup": "./scripts/rollup.config.js"
}
The custom rollup config file will live in a scripts
folder at the root of the Ionic app. The file will be called rollup.config.js
in this example.
The easiest way to fill out the Rollup config is to start with the existing Rollup config. Open node_modules/@ionic/app-scripts/config/rollup.config.js
and copy and paste the file content into scripts/rollup.config.js
.
Now that the config file is in good shape, let’s fix the error from above. In this example, we tried to install the super cool library pouchdb. In our error message, we can see that pouchdb
’s lib/index-browser.es.js
file is trying to import extend
from the library js-extend
, resulting in an error.
This error basically says that Rollup does not think that js-extend
exports anything called extend
. The solution to the problem is we need to tell Rollup that js-extend
does in fact export extend
.
Here is the default set of Rollup plugins
plugins: [
builtins(),
commonjs(),
nodeResolve({
...
}),
globals(),
json()
]
In the Rollup config, we want to look for the commonjs
plugin, and add an entry to the namedExports
field.
commonjs({
namedExports: {
// pouchdb
'node_modules/js-extend/extend.js': ['extend']
}),
Let’s break down the namedExport
further.
Our original error message looked like this
bundle failed: Module myApp/node_modules/js-extend/extend.js does not export extend (imported by myApp/node_modules/pouchdb/lib/index-browser.es.js)
The error message has a reference to myApp/node_modules/js-extend/extend.js
. It also has a reference to an export called extend
.
In the namedExport
entry, we can leave off myApp
, but include the rest of the path as the entry’s key. The entry’s value is then an array of strings, in this case a lone entry of extend
.
Providing the namedExports
will resolve any third party library issues the vast majority of time.
Strict Mode
Ionic Apps (and any app that utilizes modern javascript) are written in something called strict mode
by default. Most of the time, developers don’t need to be concerned with this. If a third party library is written without strict mode
support, disabling strict mode
in the Rollup config can be done. One popular library that requires strictMode
to be false
is Firebase.
Script Tag Include
If all else fails and a library simply cannot be bundled, it can always be included in an app via an HTML <script>
tag. This is how libraries were managed for the past twenty years, and it still works very well.
To access the library from an Ionic Page
or Component
, make a declare
statement and create a variable. See an example below with jQuery.
declare const jQuery:any;
When jQuery
is included in a web browser, it can be accessed from window.jQuery
. In the above example, we’re mapping jQuery
to the window.jQuery
object. From here, jQuery
can be used through the TypeScript
code without issue.