For Liferay DXP to recognize your npm modules, they must be formatted for the Liferay AMD Loader. Luckily, the liferay-npm-bundler handles this for you, you just have to provide the proper configuration and add it to your build script. This tutorial shows how to use the liferay-npm-bundler to set up npm-based portlet projects.
The example structure below is referenced throughout this tutorial. You can download it here if you want to follow along:
npm-angular5-portlet-say-hello/
META-INF/
resources/
package.json
- name: npm-angular5-portlet-say-hello
- version: 1.0.0
- main: js/angular.pre.loader.js
- scripts:
- build: tsc && liferay-npm-bundler
tsconfig.json
- target: es5
- moduleResolution: node
.npmbundlerrc
- exclude:
- *: true
- config:
- imports:
- npm-angular5-provider:
- @angular/animations: ^5.0.0
- @angular/cdk: ^5.0.0
- @angular/common: ^5.0.0
- @angular/compiler: ^5.0.0
- "" :
- npm-angular5-provider: ^1.0.0
- npm-angular5-provider:
- imports:
- exclude:
js/
indigo-pink.css
angular.pre.loader.ts
// Bootstrap shims and providers- import npm-angular5-provider;
npm-angular5-provider
package.json
- name: npm-angular5-provider
- version: 1.0.0
- main: bootstrap.js
- scripts:
- build: liferay-npm-bundler
- dependencies:
- @angular/animations: ^5.0.0
- @angular/cdk: ^5.0.0
- @angular/common: ^5.0.0
- @angular/compiler: ^5.0.0
- …
src/main/resources/META-INF/resources/
bootstrap.js
// This file includes polyfills needed by Angular and must be loaded before the app- require(‘core-js/es6/reflect’);
- require(‘core-js/es7/reflect’);
- require(‘zone-js/dist/zone’);
Follow these steps to configure your project to use the liferay-npm-bundler:
-
Install NodeJS >= v6.11.0 if you don’t have it installed.
-
Navigate to your portlet’s project folder and initialize a
package.json
file if it’s not present yet.If you don’t have a portlet already, create an empty MVC portlet project. For convenience, you can use Blade CLI to create an empty portlet with the mvc portlet blade template.
If you don’t have a
package.json
file, you can runnpm init -y
to create an empty one based on the project directory’s name. -
Run the following command to install the liferay-npm-bundler:
npm install --save-dev liferay-npm-bundler
-
Add the
liferay-npm-bundler
to yourpackage.json
’s build script to pack the needed npm packages and transform them to AMD:"scripts": { "build": "[... && ] liferay-npm-bundler" }
The
[...&&]
refers to any previous steps you need to perform (for example, transpiling your sources with Babel, compiling SOY templates, transpiling Typescript, etc.). The example includes the Typescript compiler (tsc
) in its build script because Angular requires it for transpiling code to ES5:"build": "tsc && liferay-npm-bundler"
-
Configure your project for the bundler, using the
.npmbundlerrc
file (create this file in your project’s root folder if it doesn’t exist). You can specify packages to exclude from the output JAR, imports for shared dependencies, and more. See the Configuring liferay-npm-bundler reference for more information on the available options.The example excludes every dependency (using the wildcard (
*
) symbol) of thenpm-angular5-portlet-say-hello
widget to prevent Angular from appearing in its JAR, making the build process faster and optimizing deployment. Note thatnpm-angular5-provider
is also imported with no namespace (""
) because one of its modules is going to be invoked to bootstrap Angular shims: see theangular.pre.loader.ts
file, wherenpm-angular5-provider
is imported. That import, in turn, loadsnpm-angular5-provider
’s main file (bootstrap.js
):{ ... "exclude": { "*": true }, "config": { "imports": { "npm-angular5-provider": { "@angular/animations": "^5.0.0", "@angular/cdk": "^5.0.0", "@angular/common": "^5.0.0", "@angular/compiler": "^5.0.0", ... }, "": { "npm-angular5-provider": "^1.0.0" } } } }
-
Run
npm install
to install the required dependencies. -
Run the build script to bundle your dependencies with the liferay-npm-bundler:
npm run-script build
The bundler copies the project and node_modules
’ JS files to the output and
wraps them inside a Liferay.Loader.define()
call so that the Liferay AMD
Loader knows how to handle them. It also namespaces the module names in
require()
calls and inside the Liferay.Loader.define()
call with the
project’s name prefix (npm-angular5-provider$
in the example) to achieve
dependency isolation.
the bundler injects the dependencies in the package.json
pertaining to
npm-angular5-provider
to make them available at runtime. The resulting build
for the example widget is shown below:
npm-angular5-portlet-say-hello/
build/
resources/main/META-INF/resources
package.json
- dependencies:
- @npm-angular5-provider$angular/animations: ^5.0.0
- @npm-angular5-provider$angular/cdk: ^5.0.0
- @npm-angular5-provider$angular/common: ^5.0.0
- @npm-angular5-provider$angular/compiler: ^5.0.0
- dependencies:
js/
angular.loader.js
- Liferay.Loader.define(“npm-angular5-portlet-say-hello@1.0.0/js/angular.loader”
- [‘module’, ‘exports’, ‘require’, ‘@npm-angular5-provider$angular/platform-browser-dynamic’, …]
npm-angular5-provider
package.json
- name: npm-angular5-provider
- version: 1.0.0
- main: bootstrap.js
- dependencies:
- @npm-angular5-provider$angular/animations: ^5.0.0
- @npm-angular5-provider$angular/cdk: ^5.0.0
- @npm-angular5-provider$angular/common: ^5.0.0
- @npm-angular5-provider$angular/compiler: ^5.0.0
- …
bootstrap.js
- Liferay.Loader.define(‘npm-angular5-provider@1.0.0/bootstrap’
- [‘module’, ‘exports’, ‘require’, ‘npm-angular5-provider$core-js/es6/reflect’, ‘npm-angular5-provider$core-js/es7/reflect’, ‘npm-angular5-provider$zone.js/dist/zone’]
src/main/resources/META-INF/resources/
bootstrap.js
// This file includes polyfills needed by Angular and must be loaded before the app- require(‘core-js/es6/reflect’);
- require(‘core-js/es7/reflect’);
- require(‘zone-js/dist/zone’);
Now you know how to use the liferay-npm-bundler to bundle your npm-based portlets for the Liferay AMD Loader!