Compiler features
The IMA.js CLI uses webpack behind the scenes to compile, minify and run the application in dev mode. It comes pre-configured with some options, plugins and loaders, which are described in the following sections.
Server and client bundles
The CLI creates 3 separate bundles (2 in dev mode for performance reasons) with their own configurations. One server
bundle (used in express for SSR) and two client bundles - client
and client.es
, where one targets the es2018
and the other es2022
version of the javascript.
This can be further customized using the disableLegacyBuilt
option in ima.config.js
.
To make the CLI build both es versions in dev mode, run it with npx ima dev --legacy
option.
Keep in mind that hot module replacement (HMR) is configured to work only with the latest es version (manual browser reload is required to see any changes on the legacy version).
Filesystem Cache
The webpack filesystem cache feature is enabled by default to improve consecutive build times in development and production mode.
The CLI automatically generates cache key based on used set of CLI options, which somehow affect the produced output. Not all options affect cache key generation, however you may notice that sometimes the build speeds can behave as if there is no filesystem cache. To see which options affect the cache key generation, take a look at the createCacheKey()
function.
Note that each command and bundle maintains it's own set of coexisting cache. To clear the cache, use --clearCache
option in build
or dev
commands.
JavaScript/React
To bundle JS files we opted to use swc, a Rust-based JavaScript compiler. This decision was based on our results from testing and measuring build times, where we saw 2-3 times the speed benefit (depending on the application size) of using swc over babel compiler.
By default the application compiles both, the application files (sourced from ./app
folder) and vendor files (sourced from ./node_modules
directory) to make sure that it can run in targeted environments without any issues.
The swc compiler is configured to leverage the power of "env" functionality (preset-env in babel), in combination with core-js to automatically polyfill missing APIs that are used throughout the codebase, that the targeted environment doesn't support.
This configuration can be easily customized using swc option in ima.config.js.
This means that you can write your code using the latest and greatest from the ECMAscript language and the swc makes sure to compile these features down to the latest supported syntax or automatically inject core-js polyfills.
Keep in mind that overuse of these may result in larger JS bundles due to the need to inject more core-js polyfills. Also browser APIs like for example AbortController
or fetch
are not handled by the core-js and must be included manually. See polyfills in advanced features section.
React
In dev we use the development version of react library (for better debugging) and react-refresh
for HMR. This is switched to production for production builds. By default the compiler is configured to work with automatic JSX runtime, so there's no need to import react
library at top of every jsx file. This can be changed to classic
in ima.config.js.
Typescript
From IMA.js v18 we've introduced support for Typescript in your application code. To enable it, simply install typescript
dependency and create tsconfig.json
file in the root of your project.
For more information and additional tips about TypeScript usage in IMA.js applications, see the TypeScript section.
CSS/LESS
There's built in support for CSS and LESS preprocessor. Both of these have the same featureset. To use any CSS you have to import the files anywhere in your application. These imports are then combined and extracted to single app.css
file.
./app/main.js
is a good place to use for global CSS files, since it is an entry point to IMA.js application and these imports will be included at top of the built app.css
file.
CSS Modules
Both loaders fully support CSS Modules for files with *.modules.css
or *.modules.less
postfixes, with local
as default scoping.
:global {
:root: {
--bg-color: #fff;
}
}
.home {
background: var(--bg-color);
}
import styles from './home.module.less';
function Home() {
return
<div className={styles.home}>HomePage</div>
);
}
globals.less
This file is located at ./app/less/globals.less
and it is automatically imported on top of every other processed LESS file. It allows you to easily share globals across less files.
Use this file to import other mixins and global variables which are then available in all other *.less files automatically, without the need to import them explicitly.
@import './mixins/*.less';
@global-red: red;
body {
// No direct import of 'globals.less' is needed.
background: @global-red;
}
Glob less imports
The less-loader
uses less-plugin-glob by default in it's configuration. This means that glob imports are fully supported.
@import './mixins/*.less';
/* Non-relative imports are resolved through node resolver. */
@import "@ima/**/atoms/**/*.less";
@import "@ima/**/molecules/**/*.less";
@import "@ima/**/organisms/**/*.less";
PostCSS
IMA.js has built-in support for PostCSS during CSS/LESS compilation.
Out of the box without any additional configuration, it comes pre-configured with following plugins:
- postcss-flexbugs-fixes - tries to fix all known flexbox issues.
- postcss-preset-env - converts modern CSS back into something the old browsers can understand (back to IE11). It comes with:
autoprefixer
,stage 3
andcustom-properties: false
features.
All these features can be easily customized using postcss
option in ima.config.js.
Assets
All other assets are either inlined as base64 encoded string or copied to the ./build/static/media
folder, where default import represents assets's public URL.
Images
Images (bmp, gif, jpeg, png, webp, svg) are automatically inlined if their size is below imageInlineSizeLimit
, which can be configured in ima.config.js, with default value of 8Kb. Images exceeding this size limit are copied to the static media folder and import return's their public URL.
To enforce either one of the two modes, you can use ?external
or ?inline
query parameter in the import path:
// This always converts the image to base64 encoded string and inlines it.
import InlineImage from './image.png?inline';
// This always returns image public URL, no matter it's size
import ImageURL from './image.png?external';
Text files
When you import one of these text files - csv, txt, html, you receive their contents. Similarly to the images, you can enforce getting their public URL by using the ?external
query parameter.
// Returns file contents in the default import
import IndexSource from './index.html';
// Returns the file public URL
import IndexURL from './index.html?external';
./app/public
folder
Everything in this folder is copied to the ./build/static/public
and available through the express static files route (http://localhost:3001/static/public/
).
Compression
When you built the application bundle, all static assets are additionally compressed using brotli
and gzip
compression (with .br
and .gz
extensions respectively). To customize this behavior, take a look at ima.config.js configuration section.
Languages
The language files are compile using messageformat library.