frontend-app v2.2.0
frontend-app
Start using latest ES syntax and features, Livereload and Hot Module Replacement.
This includes babel-polyfill and whatwg-fetch polyfill and transpiles code using env and stage-0 Babel presets, so you may use many new features of the language out-of-the-box.
JSX and AngularJS dependency injection annotations (babel-plugin-ng-annotate, ng-annotate) are also supported.
Contents
- Getting Started
- Commands
- Scripts
- Options
- React Intl helper Components
- List of Preconfigured Loaders
- Livereload
- Dev Server
- Single Entry
- Multiple Entries
- Examples
- Webpack Configuration
- Cookbook
Getting Started
Make sure you have at least Node.js 6.13.1 and npm 5.2 installed:
node -v
npm -v
Create package.json
and install frontend-app:
mkdir my-app
cd my-app
npm init -y
npm install --save-dev frontend-app
Install one of the examples, have your app entry point(s) in src
directory and webpack.config.js
in place. Then build using:
npm run build
Commands
init
Initialize example project.
npx frontend-app init <example-name>
Scripts
build
Files are transpiled, bundled and minified to the dist
directory and are ready to be served. Source maps are also generated.
You may pass additional arguments to Webpack using --
. For example, don't show progress information:
npm run build -- --progress false
dev
Bundle files to disk in watch mode (see Livereload).
dev-server
Bundle files in watch mode (see Dev Server).
Options
You may use package.json
config
field to set options. Example:
{
"name": "my-app",
"config": {
"frontendApp": {
"type": "react",
"port": "8080",
"devServer": {
"port": "8081"
}
}
}
}
type
react
: Adds JSX support.angular
: Adds ng-annotate support.all
: Support JSX and ng-annotate.ng-strict-di
is not supported in livereload mode.slim
(default): Just ES2015. No React or Angular specific transformations.portlet
: Setsoutput.publicPath
in Spring MVC Liferay plugin project built with Maven.publicPath
is determined byartifactId
inpom.xml
.
Multiple values are also supported. package.json
config.frontendApp
property example:
{
"type": ["angular", "portlet"]
}
intl
Set "intl": "true"
to use default list of available locales.
intl.locales
Available locales for Moment.js, React Intl and Intl. Default: ["fi", "sv", "en"]
(When "intl": "true"
).
polyfills
Included polyfills. Default: ["babel-polyfill", "whatwg-fetch"]
. Set "false"
to not include any polyfills.
React Intl helper Components
Moved to @visma/react-intl-helpers.
List of Preconfigured Loaders
Extension | Loader |
---|---|
JS | |
.js | (ng-annotate-loader,) babel-loader |
HTML | |
.html | raw-loader |
.hbs | handlebars-template-loader |
Styles | |
.css | css-loader |
.scss | sass-loader |
.less | less-loader |
Fonts & Media | |
.woff , .woff2 | url-loader |
.ttf | url-loader |
.eot | file-loader |
.svg | url-loader |
.png | url-loader |
.gif | file-loader |
.jpg | file-loader |
Other | |
.txt | raw-loader |
.properties | @visma/react-intl-helpers/cjs/loader |
Livereload
Start build process, watch changes and refresh browser automatically on changes.
npm run dev
If you wish to serve files from different directory while developing (i.e. server content base is elsewhere):
npm run dev -- --output-path /some/other/content/base/dist
Dev Server
Use this for Hot Module Replacement with React or as an alternative to Livereload.
npm run dev-server
Single Entry (src/index.js
)
my-app/
dist/ // generated
bundle.aa4915533a5b5f53c072.css
bundle.aa4915533a5b5f53c072.css.map
bundle.aa4915533a5b5f53c072.js
bundle.aa4915533a5b5f53c072.js.map
src/
index.js // entry point
other.js
package.json
<link href="/dist/bundle.aa4915533a5b5f53c072.css" rel="stylesheet">
<script src="/dist/bundle.aa4915533a5b5f53c072.js"></script>
Multiple Entries (src/entries
)
my-app/
dist/ // generated
commons.ee0ce0879c8b5aaefae4.css
commons.ee0ce0879c8b5aaefae4.css.map
commons.ee0ce0879c8b5aaefae4.js
commons.ee0ce0879c8b5aaefae4.js.map
entries/pageA/bundle.8036db3af1be1831e295.css
entries/pageA/bundle.8036db3af1be1831e295.css.map
entries/pageA/bundle.8036db3af1be1831e295.js
entries/pageA/bundle.8036db3af1be1831e295.js.map
entries/pageB/bundle.19596c286128bce14cf9.css
entries/pageB/bundle.19596c286128bce14cf9.css.map
entries/pageB/bundle.19596c286128bce14cf9.js
entries/pageB/bundle.19596c286128bce14cf9.js.map
src/
entries/ // entry points e.g. for each page
pageA.js
pageB.js
other.js
package.json
You must load commons bundle before the entry point. pageA.html
:
<link href="/dist/commons.ee0ce0879c8b5aaefae4.css" rel="stylesheet">
<link href="/dist/entries/pageA/bundle.8036db3af1be1831e295.css" rel="stylesheet">
<script src="/dist/commons.ee0ce0879c8b5aaefae4.js"></script>
<script src="/dist/entries/pageA/bundle.8036db3af1be1831e295.js"></script>
Examples
React
Install example project with React Router, React-Bootstrap, Bootstrap 3 custom build, React Intl and mock API:
npx frontend-app init react
Start Webpack Dev Server:
npm run dev-server
Go to http://localhost:8080/
.
React (multiple entries)
Install React example project for multi-portlet use, with Bootstrap 3 custom build, React Intl and mock API:
npx frontend-app init react-multiple-entries
Start Webpack Dev Server:
npm run dev-server
Go to http://localhost:8080/
.
Angular
Install example project with AngularUI Router and Bootstrap:
npx frontend-app init angular
Start Webpack Dev Server:
npm run dev-server
Go to http://localhost:8080/
.
Webpack Configuration
Provide the built-in Webpack configuration. webpack.config.js
:
module.exports = require("frontend-app/cjs/webpack/config").default;
Optionally merge own configuration. It will be merged into the built-in configuration using webpack-merge in smartStrategy
mode:
module.exports = require("frontend-app/cjs/webpack/config").default.merge({
module: {
rules: [
{
test: /\.js$/,
loader: "babel-loader",
include: join(process.cwd(), "node_modules/my-lib")
}
]
},
output: {
publicPath: "/dist/"
}
});
And/or customize the final configuration:
module.exports = require("frontend-app/cjs/webpack/config")
.default.merge({
output: {
publicPath: "/dist/"
}
})
.customize(config => {
// Edit entries.
config.entry[2] = join(config.entry[2], "client");
return config;
});
Cookbook
History API Fallback with HTML Webpack Plugin
Suppose there is no backend or it's on another machine, and you just need to test some frontend code with Webpack Dev Server.
Install dependencies:
npm install --save-dev html-webpack-plugin
Edit webpack.config.js
:
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = require("frontend-app/cjs/webpack/config").default.merge({
output: {
publicPath: "/dist/"
},
devServer: {
// Webpack Dev Server needs generated index.html in /dist directory for historyApiFallback to work.
historyApiFallback: { index: "/dist/" }
},
plugins: [
new HtmlWebpackPlugin({
title: "My App"
})
]
});
CKEditor from /libs/ckeditor
Install dependencies:
npm install --save-dev html-webpack-plugin html-webpack-template
Edit webpack.config.js
:
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = require("frontend-app/cjs/webpack/config").default.merge({
plugins: [
new HtmlWebpackPlugin({
inject: false,
template: require("html-webpack-template"),
scripts: ["/libs/ckeditor/ckeditor.js"],
window: {
CKEDITOR_BASEPATH: "/libs/ckeditor/"
}
})
]
});
Use CKEDITOR
in your app:
// CKEDITOR is available.
window.CKEDITOR;
CKEditor from node_modules
Install dependencies:
npm install --save-dev html-webpack-plugin html-webpack-template ckeditor
Edit webpack.config.js
:
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = require("frontend-app/cjs/webpack/config").default.merge({
plugins: [
new HtmlWebpackPlugin({
inject: false,
template: require("html-webpack-template"),
window: {
CKEDITOR_BASEPATH: "/node_modules/ckeditor/"
}
})
]
});
Use CKEDITOR
in your app:
import "ckeditor";
// CKEDITOR is available.
window.CKEDITOR;
Different output.publicPath
in production
process.env.NODE_ENV
is set correctly. You may use NODE_ENV
, and for example process.env.npm_lifecycle_event
to customize your configuration. webpack.config.js
:
const production = process.env.NODE_ENV === "production";
// process.env.npm_lifecycle_event => "build" | "dev" | "dev-server"
module.exports = require("frontend-app/cjs/webpack/config").default.merge({
output: {
publicPath: production ? "/somepath/dist/" : "/dist/"
}
});
Maven
Install in webapp
directory:
cd .../src/main/webapp
npm init
npm install --save-dev frontend-app
Add build step to pom.xml
:
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<id>::install node and npm::</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
</execution>
<execution>
<id>::npm ci::</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>ci</arguments>
</configuration>
</execution>
<execution>
<id>::npm run build::</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>run build</arguments>
</configuration>
</execution>
</executions>
<configuration>
<nodeVersion>v8.12.0</nodeVersion>
<npmVersion>6.4.1</npmVersion>
<workingDirectory>src/main/webapp</workingDirectory>
</configuration>
</plugin>
Add exclude rules to pom.xml
:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warSourceExcludes>src/,node/,node_modules/,package.json</warSourceExcludes>
<webResources>
<resource>
<directory>src/main/webapp</directory>
<filtering>false</filtering>
<excludes>
<exclude>src/**</exclude>
<exclude>node/**</exclude>
<exclude>node_modules/**</exclude>
<exclude>package.json</exclude>
</excludes>
</resource>
</webResources>
</configuration>
</plugin>
.gitignore
/dist/
/node_modules/
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago