Node Package Manager
It ultimately depends on your use-case and if you are a framework author, a client developer, ...
Code distribution
Dependency hell
Finding
code
Downloading
code
Installing code
Packaging code
Releasing code
Publishing code
npm install react --save
@ixor/testapp1@1.0.1 /Users/ddewaele/Projects/Node/testapp1
└─┬ react@0.14.7
├─┬ envify@3.4.0
│ ├─┬ jstransform@10.1.0
│ │ ├── base62@0.1.1
│ │ ├── esprima-fb@13001.1001.0-dev-harmony-fb
│ │ └─┬ source-map@0.1.31
│ │ └── amdefine@1.0.0
│ └── through@2.3.8
└─┬ fbjs@0.6.1
├── core-js@1.2.6
├─┬ loose-envify@1.1.0
│ └── js-tokens@1.0.2
├─┬ promise@7.1.1
│ └── asap@2.0.3
├── ua-parser-js@0.7.10
└── whatwg-fetch@0.9.0
{
"name": "@ixor/testapp1",
"version": "1.0.1",
"description": "Greatest app in the world",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ddewaele/testapp1.git"
},
"keywords": [
"greatest",
"app",
"world"
],
"author": "Davy De Waele <ddewaele@gmail.com> (https://github.com/ddewaele/)",
"license": "ISC",
"bugs": {
"url": "https://github.com/ddewaele/testapp1/issues"
},
"homepage": "https://github.com/ddewaele/testapp1#readme",
"dependencies": {
"bootstrap": "^3.3.6",
"react": "^0.14.7"
}
}
{
"name": "react-build",
"private": true,
"version": "0.14.7",
"devDependencies": {
"babel": "^5.8.3",
"babel-eslint": "^4.1.3",
"benchmark": "^1.0.0",
"browserify": "^9.0.3",
"bundle-collapser": "^1.1.1",
"coffee-script": "^1.8.0",
"del": "^1.2.0",
"derequire": "^2.0.0",
"envify": "^3.0.0",
"eslint": "^1.5.1",
"eslint-plugin-react": "^3.4.2",
"eslint-plugin-react-internal": "file:eslint-rules",
"fbjs": "^0.6.1",
"fbjs-scripts": "^0.2.0",
"grunt": "^0.4.5",
"grunt-cli": "^0.1.13",
"grunt-compare-size": "^0.4.0",
"grunt-contrib-clean": "^0.6.0",
"grunt-contrib-compress": "^0.13.0",
"gulp": "^3.9.0",
"gulp-babel": "^5.1.0",
"gulp-flatten": "^0.1.0",
"gulp-util": "^3.0.5",
"gzip-js": "~0.3.2",
"jest-cli": "^0.5.7",
"jstransform": "^11.0.0",
"object-assign": "^3.0.0",
"optimist": "^0.6.1",
"platform": "^1.1.0",
"run-sequence": "^1.1.0",
"through2": "^2.0.0",
"tmp": "~0.0.18",
"typescript": "~1.4.0",
"uglify-js": "^2.4.23",
"uglifyify": "^3.0.1"
},
"devEngines": {
"node": "4.x",
"npm": "2.x"
},
"commonerConfig": {
"version": 7
},
"scripts": {
"build": "grunt build",
"linc": "git diff --name-only --diff-filter=ACMRTUB `git merge-base HEAD master` | grep '\\.js$' | xargs eslint --",
"lint": "grunt lint",
"postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json",
"test": "jest"
},
"jest": {
"modulePathIgnorePatterns": [
"/.module-cache/",
"/react/build/"
],
"persistModuleRegistryBetweenSpecs": true,
"rootDir": "",
"scriptPreprocessor": "scripts/jest/preprocessor.js",
"setupEnvScriptFile": "scripts/jest/environment.js",
"setupTestFrameworkScriptFile": "scripts/jest/test-framework-setup.js",
"testFileExtensions": [
"coffee",
"js",
"ts"
],
"testPathDirs": [
"<rootDir>/eslint-rules",
"<rootDir>/src",
"node_modules/fbjs"
],
"unmockedModulePathPatterns": [
""
]
}
}NPM will download the dependencies, including all the transitive dependencies
"devDependencies": {
"btoa": "~1.1.2",
"glob": "~6.0.1",
"grunt": "~0.4.5",
"grunt-sed": "github:twbs/grunt-sed#v0.2.0",
"markdown-it": "^5.0.0",
"npm-shrinkwrap": "^200.4.0",
"time-grunt": "^1.2.1"
}
Scenario : Flexible patches. Typically used to allow patches
~1.2.3 := >=1.2.3 <1.(2+1).0 := >=1.2.3 <1.3.0
~1.2 := >=1.2.0 <1.(2+1).0 := >=1.2.0 <1.3.0 (Same as 1.2.x)
~1 := >=1.0.0 <(1+1).0.0 := >=1.0.0 <2.0.0 (Same as 1.x)
Scenario :
^0.2.3 will accept versions up untill a possible breaking change : >=0.2.3 <0.3.0
^1.2.3 := >=1.2.3 <2.0.0
^0.2.3 := >=0.2.3 <0.3.0
^0.0.3 := >=0.0.3 <0.0.4but.....
"the team discovered a nested npm dev dependency that broke the world after a change in version from 1.1.1 to 1.2.0"
But wait .... that doesn't count as a breaking change. npm happily installed 1.2.0 based on the package's install rule of ^1.0.0. It shouldn't have broken anything, but it did.
{ "name": "client",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Davy De Waele <ddewaele@gmail.com> (https://github.com/ddewaele/)",
"license": "ISC",
"dependencies": {
"@ddewaele/module-a": "^0.1.1"
}}{
"name": "client",
"version": "1.0.0",
"dependencies": {
"@ddewaele/module-a": {
"version": "0.1.3",
"from": "@ddewaele/module-a@0.1.3"
}
}
}
Module A
0.1.1
Module A
0.1.2
Module A
0.1.3
Client
Module A
^0.1.1
T1
T2
T3
Module A
0.1.4
T4
Client
Client
Client
Module A
0.1.1
Module A
^0.1.1
Module A
^0.1.1
Module A
^0.1.1
Module A
0.1.2
Module A
0.1.3
Module A
0.1.4
{
"name": "client",
"version": "1.0.0",
"dependencies": {
"@ddewaele/module-a": {
"version": "0.1.2",
"from": "@ddewaele/module-a@0.1.2"
}
}
}
npm shrinkwrap, when executed, will look at the current versions of all dependencies, and will store those versions in an npm-shrinkwrap.json file, essentially locking in your dependency versions
Module A
0.1.1
Module A
0.1.2
Module A
0.1.3
Client
Module A
^0.1.1
T1
T2
T3
Module A
0.1.4
T4
Client
Client
Client
Module A
0.1.1
Module A
^0.1.1
Module A
^0.1.1
Module A
^0.1.1
Module A
0.1.2
Module A
0.1.2
Module A
0.1.2
component A
component B
client
//module-a.js
var moduleB = require("@ddewaele/module-b");
module.exports = {
sayHello: function() {
var pjson = require('../package.json');
return "Hello from componentA v" + pjson.version + " and " + moduleB.sayHello()
}
}//client.js
var component = require("@ddewaele/module-a");
console.log(component.sayHello());
{
"name": "app-name",
"version": "0.0.1",
"dependencies": {
"sass-bootstrap": "~3.0.0",
"modernizr": "~2.6.2",
"jquery": "~1.10.2"
},
"private": true
}So what should we put in our webapp npm package ?
Well... according to Laurie Voss, CTO of NPM inc :
| Steps | Commands |
|---|---|
| Creating an app | npm init |
| Adding dependencies | npm install |
| Locking dependency versions | npm shrinkwrap |
| Testing your app | npm test |
| Packaging your app | npm pack |
| Versioning your app | npm version |
| Publishing your app | npm publish |
| Running scripts | npm run-script |
In the beginning there was
/*!
* Bootstrap's Gruntfile
* http://getbootstrap.com
* Copyright 2013-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
module.exports = function (grunt) {
'use strict';
// Force use of Unix newlines
grunt.util.linefeed = '\n';
RegExp.quote = function (string) {
return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
};
var fs = require('fs');
var path = require('path');
var npmShrinkwrap = require('npm-shrinkwrap');
var generateGlyphiconsData = require('./grunt/bs-glyphicons-data-generator.js');
var BsLessdocParser = require('./grunt/bs-lessdoc-parser.js');
var getLessVarsData = function () {
var filePath = path.join(__dirname, 'less/variables.less');
var fileContent = fs.readFileSync(filePath, { encoding: 'utf8' });
var parser = new BsLessdocParser(fileContent);
return { sections: parser.parseFile() };
};
var generateRawFiles = require('./grunt/bs-raw-files-generator.js');
var generateCommonJSModule = require('./grunt/bs-commonjs-generator.js');
var configBridge = grunt.file.readJSON('./grunt/configBridge.json', { encoding: 'utf8' });
Object.keys(configBridge.paths).forEach(function (key) {
configBridge.paths[key].forEach(function (val, i, arr) {
arr[i] = path.join('./docs/assets', val);
});
});
// Project configuration.
grunt.initConfig({
// Metadata.
pkg: grunt.file.readJSON('package.json'),
banner: '/*!\n' +
' * Bootstrap v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
' * Copyright 2011-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
' * Licensed under the <%= pkg.license %> license\n' +
' */\n',
jqueryCheck: configBridge.config.jqueryCheck.join('\n'),
jqueryVersionCheck: configBridge.config.jqueryVersionCheck.join('\n'),
// Task configuration.
clean: {
dist: 'dist',
docs: 'docs/dist'
},
jshint: {
options: {
jshintrc: 'js/.jshintrc'
},
grunt: {
options: {
jshintrc: 'grunt/.jshintrc'
},
src: ['Gruntfile.js', 'package.js', 'grunt/*.js']
},
core: {
src: 'js/*.js'
},
test: {
options: {
jshintrc: 'js/tests/unit/.jshintrc'
},
src: 'js/tests/unit/*.js'
},
assets: {
src: ['docs/assets/js/src/*.js', 'docs/assets/js/*.js', '!docs/assets/js/*.min.js']
}
},
jscs: {
options: {
config: 'js/.jscsrc'
},
grunt: {
src: '<%= jshint.grunt.src %>'
},
core: {
src: '<%= jshint.core.src %>'
},
test: {
src: '<%= jshint.test.src %>'
},
assets: {
options: {
requireCamelCaseOrUpperCaseIdentifiers: null
},
src: '<%= jshint.assets.src %>'
}
},
concat: {
options: {
banner: '<%= banner %>\n<%= jqueryCheck %>\n<%= jqueryVersionCheck %>',
stripBanners: false
},
bootstrap: {
src: [
'js/transition.js',
'js/alert.js',
'js/button.js',
'js/carousel.js',
'js/collapse.js',
'js/dropdown.js',
'js/modal.js',
'js/tooltip.js',
'js/popover.js',
'js/scrollspy.js',
'js/tab.js',
'js/affix.js'
],
dest: 'dist/js/<%= pkg.name %>.js'
}
},
uglify: {
options: {
compress: {
warnings: false
},
mangle: true,
preserveComments: 'some'
},
core: {
src: '<%= concat.bootstrap.dest %>',
dest: 'dist/js/<%= pkg.name %>.min.js'
},
customize: {
src: configBridge.paths.customizerJs,
dest: 'docs/assets/js/customize.min.js'
},
docsJs: {
src: configBridge.paths.docsJs,
dest: 'docs/assets/js/docs.min.js'
}
},
qunit: {
options: {
inject: 'js/tests/unit/phantom.js'
},
files: 'js/tests/index.html'
},
less: {
compileCore: {
options: {
strictMath: true,
sourceMap: true,
outputSourceFiles: true,
sourceMapURL: '<%= pkg.name %>.css.map',
sourceMapFilename: 'dist/css/<%= pkg.name %>.css.map'
},
src: 'less/bootstrap.less',
dest: 'dist/css/<%= pkg.name %>.css'
},
compileTheme: {
options: {
strictMath: true,
sourceMap: true,
outputSourceFiles: true,
sourceMapURL: '<%= pkg.name %>-theme.css.map',
sourceMapFilename: 'dist/css/<%= pkg.name %>-theme.css.map'
},
src: 'less/theme.less',
dest: 'dist/css/<%= pkg.name %>-theme.css'
}
},
autoprefixer: {
options: {
browsers: configBridge.config.autoprefixerBrowsers
},
core: {
options: {
map: true
},
src: 'dist/css/<%= pkg.name %>.css'
},
theme: {
options: {
map: true
},
src: 'dist/css/<%= pkg.name %>-theme.css'
},
docs: {
src: ['docs/assets/css/src/docs.css']
},
examples: {
expand: true,
cwd: 'docs/examples/',
src: ['**/*.css'],
dest: 'docs/examples/'
}
},
csslint: {
options: {
csslintrc: 'less/.csslintrc'
},
dist: [
'dist/css/bootstrap.css',
'dist/css/bootstrap-theme.css'
],
examples: [
'docs/examples/**/*.css'
],
docs: {
options: {
ids: false,
'overqualified-elements': false
},
src: 'docs/assets/css/src/docs.css'
}
},
cssmin: {
options: {
// TODO: disable `zeroUnits` optimization once clean-css 3.2 is released
// and then simplify the fix for https://github.com/twbs/bootstrap/issues/14837 accordingly
compatibility: 'ie8',
keepSpecialComments: '*',
sourceMap: true,
advanced: false
},
minifyCore: {
src: 'dist/css/<%= pkg.name %>.css',
dest: 'dist/css/<%= pkg.name %>.min.css'
},
minifyTheme: {
src: 'dist/css/<%= pkg.name %>-theme.css',
dest: 'dist/css/<%= pkg.name %>-theme.min.css'
},
docs: {
src: [
'docs/assets/css/ie10-viewport-bug-workaround.css',
'docs/assets/css/src/pygments-manni.css',
'docs/assets/css/src/docs.css'
],
dest: 'docs/assets/css/docs.min.css'
}
},
csscomb: {
options: {
config: 'less/.csscomb.json'
},
dist: {
expand: true,
cwd: 'dist/css/',
src: ['*.css', '!*.min.css'],
dest: 'dist/css/'
},
examples: {
expand: true,
cwd: 'docs/examples/',
src: '**/*.css',
dest: 'docs/examples/'
},
docs: {
src: 'docs/assets/css/src/docs.css',
dest: 'docs/assets/css/src/docs.css'
}
},
copy: {
fonts: {
expand: true,
src: 'fonts/*',
dest: 'dist/'
},
docs: {
expand: true,
cwd: 'dist/',
src: [
'**/*'
],
dest: 'docs/dist/'
}
},
connect: {
server: {
options: {
port: 3000,
base: '.'
}
}
},
jekyll: {
options: {
config: '_config.yml'
},
docs: {},
github: {
options: {
raw: 'github: true'
}
}
},
htmlmin: {
dist: {
options: {
collapseWhitespace: true,
conservativeCollapse: true,
minifyCSS: true,
minifyJS: true,
removeAttributeQuotes: true,
removeComments: true
},
expand: true,
cwd: '_gh_pages',
dest: '_gh_pages',
src: [
'**/*.html',
'!examples/**/*.html'
]
}
},
jade: {
options: {
pretty: true,
data: getLessVarsData
},
customizerVars: {
src: 'docs/_jade/customizer-variables.jade',
dest: 'docs/_includes/customizer-variables.html'
},
customizerNav: {
src: 'docs/_jade/customizer-nav.jade',
dest: 'docs/_includes/nav/customize.html'
}
},
htmllint: {
options: {
ignore: [
'Attribute "autocomplete" not allowed on element "button" at this point.',
'Attribute "autocomplete" is only allowed when the input type is "color", "date", "datetime", "datetime-local", "email", "month", "number", "password", "range", "search", "tel", "text", "time", "url", or "week".',
'Element "img" is missing required attribute "src".'
]
},
src: '_gh_pages/**/*.html'
},
watch: {
src: {
files: '<%= jshint.core.src %>',
tasks: ['jshint:core', 'qunit', 'concat']
},
test: {
files: '<%= jshint.test.src %>',
tasks: ['jshint:test', 'qunit']
},
less: {
files: 'less/**/*.less',
tasks: 'less'
}
},
sed: {
versionNumber: {
pattern: (function () {
var old = grunt.option('oldver');
return old ? RegExp.quote(old) : old;
})(),
replacement: grunt.option('newver'),
exclude: [
'dist/fonts',
'docs/assets',
'fonts',
'js/tests/vendor',
'node_modules',
'test-infra'
],
recursive: true
}
},
'saucelabs-qunit': {
all: {
options: {
build: process.env.TRAVIS_JOB_ID,
throttled: 10,
maxRetries: 3,
maxPollRetries: 4,
urls: ['http://127.0.0.1:3000/js/tests/index.html?hidepassed'],
browsers: grunt.file.readYAML('grunt/sauce_browsers.yml')
}
}
},
exec: {
npmUpdate: {
command: 'npm update'
}
},
compress: {
main: {
options: {
archive: 'bootstrap-<%= pkg.version %>-dist.zip',
mode: 'zip',
level: 9,
pretty: true
},
files: [
{
expand: true,
cwd: 'dist/',
src: ['**'],
dest: 'bootstrap-<%= pkg.version %>-dist'
}
]
}
}
});
// These plugins provide necessary tasks.
require('load-grunt-tasks')(grunt, { scope: 'devDependencies' });
require('time-grunt')(grunt);
// Docs HTML validation task
grunt.registerTask('validate-html', ['jekyll:docs', 'htmllint']);
var runSubset = function (subset) {
return !process.env.TWBS_TEST || process.env.TWBS_TEST === subset;
};
var isUndefOrNonZero = function (val) {
return val === undefined || val !== '0';
};
// Test task.
var testSubtasks = [];
// Skip core tests if running a different subset of the test suite
if (runSubset('core') &&
// Skip core tests if this is a Savage build
process.env.TRAVIS_REPO_SLUG !== 'twbs-savage/bootstrap') {
testSubtasks = testSubtasks.concat(['dist-css', 'dist-js', 'csslint:dist', 'test-js', 'docs']);
}
// Skip HTML validation if running a different subset of the test suite
if (runSubset('validate-html') &&
// Skip HTML5 validator on Travis when [skip validator] is in the commit message
isUndefOrNonZero(process.env.TWBS_DO_VALIDATOR)) {
testSubtasks.push('validate-html');
}
// Only run Sauce Labs tests if there's a Sauce access key
if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined' &&
// Skip Sauce if running a different subset of the test suite
runSubset('sauce-js-unit') &&
// Skip Sauce on Travis when [skip sauce] is in the commit message
isUndefOrNonZero(process.env.TWBS_DO_SAUCE)) {
testSubtasks.push('connect');
testSubtasks.push('saucelabs-qunit');
}
grunt.registerTask('test', testSubtasks);
grunt.registerTask('test-js', ['jshint:core', 'jshint:test', 'jshint:grunt', 'jscs:core', 'jscs:test', 'jscs:grunt', 'qunit']);
// JS distribution task.
grunt.registerTask('dist-js', ['concat', 'uglify:core', 'commonjs']);
// CSS distribution task.
grunt.registerTask('less-compile', ['less:compileCore', 'less:compileTheme']);
grunt.registerTask('dist-css', ['less-compile', 'autoprefixer:core', 'autoprefixer:theme', 'csscomb:dist', 'cssmin:minifyCore', 'cssmin:minifyTheme']);
// Full distribution task.
grunt.registerTask('dist', ['clean:dist', 'dist-css', 'copy:fonts', 'dist-js']);
// Default task.
grunt.registerTask('default', ['clean:dist', 'copy:fonts', 'test']);
// Version numbering task.
// grunt change-version-number --oldver=A.B.C --newver=X.Y.Z
// This can be overzealous, so its changes should always be manually reviewed!
grunt.registerTask('change-version-number', 'sed');
grunt.registerTask('build-glyphicons-data', function () { generateGlyphiconsData.call(this, grunt); });
// task for building customizer
grunt.registerTask('build-customizer', ['build-customizer-html', 'build-raw-files']);
grunt.registerTask('build-customizer-html', 'jade');
grunt.registerTask('build-raw-files', 'Add scripts/less files to customizer.', function () {
var banner = grunt.template.process('<%= banner %>');
generateRawFiles(grunt, banner);
});
grunt.registerTask('commonjs', 'Generate CommonJS entrypoint module in dist dir.', function () {
var srcFiles = grunt.config.get('concat.bootstrap.src');
var destFilepath = 'dist/js/npm.js';
generateCommonJSModule(grunt, srcFiles, destFilepath);
});
// Docs task.
grunt.registerTask('docs-css', ['autoprefixer:docs', 'autoprefixer:examples', 'csscomb:docs', 'csscomb:examples', 'cssmin:docs']);
grunt.registerTask('lint-docs-css', ['csslint:docs', 'csslint:examples']);
grunt.registerTask('docs-js', ['uglify:docsJs', 'uglify:customize']);
grunt.registerTask('lint-docs-js', ['jshint:assets', 'jscs:assets']);
grunt.registerTask('docs', ['docs-css', 'lint-docs-css', 'docs-js', 'lint-docs-js', 'clean:docs', 'copy:docs', 'build-glyphicons-data', 'build-customizer']);
grunt.registerTask('prep-release', ['dist', 'docs', 'jekyll:github', 'htmlmin', 'compress']);
// Task for updating the cached npm packages used by the Travis build (which are controlled by test-infra/npm-shrinkwrap.json).
// This task should be run and the updated file should be committed whenever Bootstrap's dependencies change.
grunt.registerTask('update-shrinkwrap', ['exec:npmUpdate', '_update-shrinkwrap']);
grunt.registerTask('_update-shrinkwrap', function () {
var done = this.async();
npmShrinkwrap({ dev: true, dirname: __dirname }, function (err) {
if (err) {
grunt.fail.warn(err);
}
var dest = 'test-infra/npm-shrinkwrap.json';
fs.renameSync('npm-shrinkwrap.json', dest);
grunt.log.writeln('File ' + dest.cyan + ' updated.');
done();
});
});
};
var gulp = require('gulp');
var browserify = require('browserify');
var babelify = require('babelify');
var source = require('vinyl-source-stream');
gulp.task('build', function () {
return browserify({entries: './app.jsx', extensions: ['.jsx'], debug: true})
.transform('babelify', {presets: ['es2015', 'react']})
.bundle()
.pipe(source('bundle.js'))
.pipe(gulp.dest('dist'));
});
gulp.task('watch', ['build'], function () {
gulp.watch('*.jsx', ['build']);
});
gulp.task('default', ['watch']);
index.html
bundle.js
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>ReactJS and ES6</title>
</head>
<body>
<div id="content"></div>
<script src="dist/bundle.js"></script>
</body>
</html>The idea is to have Webpack bundle everything in one or more bundles, and bootstrap them from an index.html
index.html
bundle.js
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>ReactJS and ES6</title>
</head>
<body>
<div id="content"></div>
<script src="dist/vendor.js"></script>
<script src="dist/bundle.js"></script>
</body>
</html>You are not limited to a single bundle
vendor.js
index.html
bundle.js
<div>
<a href="#">Home</a>
<a href="#heavyPage">Heavy Page</a>
</div>You are not limited to a single bundle
vendor.js
if (location.hash === '#heavyPage') {
require.ensure([], function () {
var HeavyPage = require('./heavyPage.js');
React.render(HeavyPage(),
document.getElementById('app'));
});heavy.js
vendor.js
/
#heavyPage
import React from 'react';
class HelloWorld extends React.Component {
render() {
return <h1>Hello from {this.props.phrase}!</h1>;
}
}
export default HelloWorld;import React from 'react'
import { render } from 'react-dom'
import HelloWorld from './components/hello';
render(
<HelloWorld phrase="ES6"/>,
document.getElementById("content")
);
hello.js
index.js
var _componentsHello = __webpack_require__(160);
var _componentsHello2 = _interopRequireDefault(_componentsHello);
(0, _reactDom.render)(_react2['default'].createElement(_componentsHello2['default'], { phrase: 'ES6' }), document.getElementById("content"));
* return <div>Hello World</div>;
* return <div>Hello, {name}!</div>;
var HelloWorld = (function (_React$Component) {
_inherits(HelloWorld, _React$Component);
function HelloWorld() {
_classCallCheck(this, HelloWorld);
_get(Object.getPrototypeOf(HelloWorld.prototype), 'constructor', this).apply(this, arguments);
_createClass(HelloWorld, [{
'Hello from ',
return HelloWorld;
exports['default'] = HelloWorld;
So that clean, de-composed ES6 code-base gets converted and bundled by Webback into something your browser understands.
var path = require('path')
var webpack = require('webpack')
module.exports = {
entry: [
'./src/index'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.js$/,
loaders: [ 'babel' ],
exclude: /node_modules/,
include: __dirname
}
]
}
}
<img src={require('./images/exclamation.jpg')}/>-rw-r--r-- 1 ddewaele wheel 127 Feb 3 15:59 index.html
-rw-r--r-- 1 ddewaele wheel 781572 Feb 3 15:59 bundle.js
-rw-r--r-- 1 ddewaele wheel 25829 Feb 3 15:59 7d24ad3a8e552ad2b43cde283e3125af.jpg{
test: /\.gif$/,
loader: "url-loader?mimetype=image/png"
},{
test: /\.(jpg|png)$/,
loader: 'url-loader?limit=1000' ,
}<img class="img-remark img-responsive center-block"
src="data:image/jpeg;base64,iVBORw0KGgoAAAANS"
data-reactid=".0.0.1"> <img src="9e31d0e1e181d1960982465bda6b24e1.png"
data-reactid=".0.0.0">import './Board.css';{
test: /\.css?$/,
loader: "style-loader!css-loader",
include: __dirname
},{
test: /\.woff(2)?(\?v=[0-9].[0-9].[0-9])?$/,
loader: "url-loader?mimetype=application/font-woff",
include: __dirname
},{
test: /\.(ttf|eot|svg)(\?v=[0-9].[0-9].[0-9])?$/,
loader: "file-loader?name=[name].[ext]",
include: __dirname
} module: {
loaders: [
{
test: /\.js$/,
loaders: [ 'babel' ],
exclude: /node_modules/,
include: __dirname
}, {
test: /\.css?$/,
loader: "style-loader!css-loader",
include: __dirname
}, {
test: /\.json$/,
loader: 'json-loader',
include: __dirname
}, {
test: /\.txt$/,
loader: 'raw-loader',
include: __dirname
}, {
test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000'
}]
}
"devDependencies": {
"babel-loader": "^6.2.0",
"css-loader": "^0.23.1",
"file-loader": "^0.8.5",
"less-loader": "^2.2.2",
"raw-loader": "^0.5.1",
"style-loader": "^0.12.4",
"url-loader": "^0.5.7",
"webpack": "^1.12.9",
},package.json
webpack.config.json
Webpacks comes with a set of plugins to help you with
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({template: 'index.html'})
],
-rw-r--r-- 1 ddewaele staff 25829 Feb 7 19:06 7d24ad3a8e552ad2b43cde283e3125af.jpg
-rw-r--r-- 1 ddewaele staff 127 Feb 15 22:48 index.html
-rw-r--r-- 1 ddewaele staff 1102191 Feb 15 22:48 bundle.js
-rw-r--r-- 1 ddewaele staff 108738 Feb 15 22:48 89889688147bd7575d6327160d64e760.svgAll that's left todo is npm install your module
npm install @ixor/webapp1
> history@2.0.0 postinstall /home/user/node_modules/@ixor/webapp1/node_modules/history
> node ./npm-scripts/postinstall.js
@ixor/webapp1@0.0.15 node_modules/@ixor/webapp1But how do we integrate this javascript world with Java ?
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>0.0.24</version>
<configuration>
<workingDirectory>src/main/resources/static</workingDirectory>
</configuration>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v0.10.33</nodeVersion>
<npmVersion>1.3.8</npmVersion>
</configuration>
</execution>
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<execution>
<id>webpack build</id>
<goals>
<goal>webpack</goal>
</goals>
</execution>
</executions>
</plugin>Install Node / NPM
[INFO] --- frontend-maven-plugin:0.0.24:install-node-and-npm (install node and npm) @ react-and-spring-data-rest-basic ---
[INFO] Installing node version v0.10.33
[INFO] Creating temporary directory /Users/ddewaele/Projects/Node/maven/draft-tut-react-and-spring-data-rest/basic/node_tmp
[INFO] Downloading Node.js from http://nodejs.org/dist/v0.10.33/node-v0.10.33-darwin-x64.tar.gz to /Users/ddewaele/Projects/Node/maven/draft-tut-react-and-spring-data-rest/basic/node_tmp/node.tar.gz
[INFO] No proxy was configured, downloading directly
[INFO] Extracting Node.js files in node_tmp
[INFO] Unpacking /Users/ddewaele/Projects/Node/maven/draft-tut-react-and-spring-data-rest/basic/node_tmp/node.tar.gz into /Users/ddewaele/Projects/Node/maven/draft-tut-react-and-spring-data-rest/basic/node_tmp
[INFO] Moving node binary to /Users/ddewaele/Projects/Node/maven/draft-tut-react-and-spring-data-rest/basic/node/node
[INFO] Deleting temporary directory /Users/ddewaele/Projects/Node/maven/draft-tut-react-and-spring-data-rest/basic/node_tmp
[INFO] Installed node locally.
[INFO] Installing npm version 1.3.8
[INFO] Downloading NPM from http://registry.npmjs.org/npm/-/npm-1.3.8.tgz to /Users/ddewaele/Projects/Node/maven/draft-tut-react-and-spring-data-rest/basic/npm.tar.gz
[INFO] No proxy was configured, downloading directly
[INFO] Extracting NPM files in node/
[INFO] Unpacking /Users/ddewaele/Projects/Node/maven/draft-tut-react-and-spring-data-rest/basic/npm.tar.gz into /Users/ddewaele/Projects/Node/maven/draft-tut-react-and-spring-data-rest/basic/node
[INFO] Installed NPM locally.
[INFO]
[INFO] --- frontend-maven-plugin:0.0.24:npm (npm install) @ react-and-spring-data-rest-basic ---
[INFO] Running 'npm install --color=false' in /Users/ddewaele/Projects/Node/maven/draft-tu
Install Bower dependencies
[INFO] --- frontend-maven-plugin:0.0.24:bower (bower install) @ react-and-spring-data-rest-basic ---
[INFO] Running 'bower install' in /Users/ddewaele/Projects/Node/maven/draft-tut-react-and-spring-data-rest/basic/src/main/resources/static
[INFO] bower rest#~1.3.1 not-cached git://github.com/cujojs/rest.git#~1.3.1
[INFO] bower rest#~1.3.1 resolve git://github.com/cujojs/rest.git#~1.3.1
plugins {
id "com.moowork.node" version "0.11"
}
apply plugin: 'base'
version '0.0.1'
buildDir = 'dist'
node {
version = '5.2.0'
npmVersion = '3.3.12'
download = true
}
task bundle(type: NpmTask) {
inputs.dir(new File('assets'))
inputs.dir(new File('src'))
outputs.dir(new File('dist'))
args = ['run', 'bundle']
}
task test(type: NpmTask) {
args = ['test']
}
check.dependsOn(test)
bundle.dependsOn(npm_install)
assemble.dependsOn(bundle)
./gradlew bundle
:frontend:nodeSetup UP-TO-DATE
:frontend:npm_install
npm WARN EPACKAGEJSON boot-react-frontend@0.0.1 No repository field.
:frontend:bundle
> boot-react-frontend@0.0.1 bundle /Users/ddewaele/Projects/Node/grails/boot-react/frontend
> cross-env NODE_ENV=production webpack -p --optimize-dedupe
Hash: 2f3cebfa162062c6af48
Version: webpack 1.12.14
Time: 13058ms
Asset Size Chunks Chunk Names
bundle.js 376 kB 0 [emitted] main
styles.css 13.1 kB 0 [emitted] main
bundle.js.map 3.26 MB 0 [emitted] main
styles.css.map 87 bytes 0 [emitted] main
index.html 184 bytes [emitted]
[0] multi main 28 bytes {0} [built]
+ 591 hidden modules
Child html-webpack-plugin for "index.html":
+ 3 hidden modules
Child extract-text-webpack-plugin:
+ 6 hidden modules
BUILD SUCCESSFUL
Total time: 29.14 secs