Jordane Grenat | @JoGrenat
Fat Arrow functions
Generators
Promises
(Classes)
...
...
...
...
export function name1() {
};
export const name2 = 'value';
export let name3 = 1;
export class MyClass {};
let name4 = () => {};
let name5 = 5;
export { name4, name5 };
export { name4 as arrowFunc, name5 as five };
export default 'value';
import theDefaultExport from 'lib';
import { name1, name2 } from 'lib';
import theDefaultExport, { name1 } from 'lib';
import { name1 as otherName } from 'lib';
import * as myLib from 'lib';
import 'lib';
System.import('lib')
.then(myLib => {
// Code
})
.catch(error => {
// Error
});
SystemJS.config({
packageConfigPaths: [
"npm:@*/*.json",
"npm:*.json",
"github:*/*.json"
],
transpiler: "plugin-babel",
map: {
"backbone": "npm:backbone@1.2.3",
"css": "github:systemjs/plugin-css@0.1.20",
"handlebars": "github:components/handlebars.js@4.0.5",
"hbs": "github:davis/plugin-hbs@1.2.1",
"jquery": "npm:jquery@2.2.1",
"json": "github:systemjs/plugin-json@0.1.0",
"plugin-babel": "npm:systemjs-plugin-babel@0.0.6",
"process": "github:jspm/nodelibs-process@0.2.0-alpha"
},
packages: {
"app": {
"main": "app.js"
},
"github:davis/plugin-hbs@1.2.1": {
"map": {
"handlebars": "github:components/handlebars.js@4.0.5"
}
},
"npm:backbone@1.2.3": {
"map": {
"underscore": "npm:underscore@1.8.3"
}
}
}
});
Polyfill for the
ES Module Loader
<script src="system.js"></script>
<script>
// loads relative to the current page URL
System.import('./local-module.js');
// load from an absolute URL directly
System.import('https://code.jquery.com/jquery.js');
</script>
SystemJS is an
universal module manager
It can load:
define(['jquery'], function($) {
return function() {};
});
AMD Modules
var $ = require('jquery');
exports.myModule = function () {};
CommonJS Modules
import $ from 'jquery';
export default () => {};
ES2015 Modules
window.myModule = function() {};
Global Modules
You don't even CARE what kind of module you're loading!
With plugins, you can also load:
Package manager
built on top of SystemJS
It can load packages
from any registry:
jspm install npm:aurelia
jspm install github:twbs/bootstrap
jspm install backbone
Generates SystemJS config automatically
Front MVP framework with
4 kinds of elements
Template management is left to the developper
We're gonna use
# One global install for the CLI
npm install -g jspm@beta
# One local install for the project
npm install --save-dev jspm@beta
jspm install backbone jquery
{
"quotes": [
{
"text": "I think the worst time to have a heart attack is during a game of charades.",
"author": "Demitri Martin"
}, {
"text": "When people ask me how many people work here, I say, about a third of them.",
"author": "Lisa Kennedy Montgomery"
}, {
"text": "Always and never are two words you should always remember never to use.",
"author": "Wendell Johnson"
}, {
"text": "Those who believe in telekinetics, raise my hand!",
"author": "Kurt Vonnegut"
}, {
"text": "Always go to other people's funerals, otherwise they won't come to yours.",
"author": "Yogi Berra"
}, {
"text": "Knowledge is knowing a tomato is a fruit; wisdom is not putting it in a fruit salad.",
"author": "Miles Kington"
}
]
}
src/config.json
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Random Quotes</title>
</head>
<body>
<div class="view-main"></div>
<script src="/jspm_packages/system.js"></script>
<script src="/jspm.browser.js"></script>
<script src="/jspm.config.js"></script>
<script>
System['import']('app/app.js');
</script>
</body>
</html>
index.html
1- Load SystemJS
2- Load our configs
3- Import our entry point
(use ['import'] for IE8 compatibility)
import Backbone from 'backbone';
export default Backbone.Model.extend({
defaults: {
text: '',
author: ''
}
});
src/quote/QuoteModel.js
import Backbone from 'backbone';
import config from 'app/config.json!';
import Quote from './QuoteModel.js';
export default Backbone.Collection.extend({
model: Quote,
initialize() {
this.set(config.quotes);
}
});
src/quote/QuoteCollection.js
jspm install json
Load JSON config file
<blockquote class="quote">
{{ quote.text }}
<cite class="quote-author">{{ quote.author}}</cite>
</blockquote>
<button class="js-newQuote quoteButton">Another quote!</button>
src/quote/quoteTemplate.hbs
jspm install handlebars
html, body {
height: 100%;
}
body {
background-color: #daf0f8;
}
.view-main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
}
.quote {
width: 60%;
margin: 0;
text-align: center;
font-family: Georgia, serif;
font-size: 3rem;
font-style: italic;
line-height: 1.45;
color: #383838;
}
@media (max-width: 1200px) {
.quote {
width: 90%;
}
}
.quote-author {
color: #999999;
font-size: 1.5rem;
display: block;
margin: 1rem 0;
text-align: center;
}
src/quote/quote.css
.quote-author:before, .quote-author:after {
content: "\2009 \2014 \2009";
}
.quoteButton {
position: relative;
margin-top: 2rem;
min-height: 3rem;
padding: 0.4rem 2rem;
border: 4px solid lightblue;
background: none;
border-radius: 20px;
font-size: 1.5rem;
font-family: Arial, serif;
font-style: italic;
}
.quoteButton:hover, .quoteButton:focus {
background-color: lightblue;
}
.quoteButton:active, .quoteButton:focus {
outline: none;
border: 4px solid lightblue;
}
.quoteButton:active:before, .quoteButton:focus:before {
content: ' ';
border-radius: 20px;
position: absolute;
border: 2px dashed white;
display: block;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
import Backbone from 'backbone';
import './quote.css!';
import renderTemplate from './quoteTemplate.hbs!';
import QuoteCollection from './QuoteCollection.js';
export default Backbone.View.extend({
el: '.view-main',
events: {
'click .js-newQuote': 'render'
},
initialize() {
this.quotes = new QuoteCollection();
},
render() {
let quote;
while((quote = this.quotes.sample()) && quote === this.quote);
this.quote = quote;
this.$el.html(renderTemplate({ quote: quote.toJSON() }));
}
});
src/quote/QuoteView.js
jspm install css
jspm install hbs=github:davis/plugin-hbs
1- Load CSS file
2- Load template
3- Select random quote
4- Render template
'quote/quote.css!css'
import Backbone from 'backbone';
import QuoteView from 'app/quote/QuoteView.js';
let AppRouter = Backbone.Router.extend({
routes: {
'': 'quote'
},
initialize() {
this.quoteView = new QuoteView();
},
quote() {
this.quoteView.render();
}
});
export default new AppRouter();
src/appRouters.js
import Backbone from 'backbone';
import './appRouter.js';
Backbone.history.start();
src/app.js
Very easy, we only need to install npm and JSPM modules
{
"scripts": {
"postinstall": "jspm install"
}
}
npm install
package.json
live-server
npm install --save-dev live-server
{
"scripts": {
"start": "live-server"
}
}
npm start
package.json
jspm bundle entryPoint destinationFile
build
or
Group several modules into a bundle
Can be injected into browser configuration
Don't need SystemJS
Can use tree shaking
No lazy-loading
--minify
command +
--separateCSS
command +
--skip-source-maps
command +
{
"bundles": {
"dist/app.js": [
"npm:underscore@1.8.3/underscore",
"github:components/jquery@2.1.4/jquery",
"npm:process@0.10.1/browser",
"quote/quote.css!github:systemjs/plugin-css@0.1.13",
"github:components/handlebars.js@3.0.3/handlebars",
"config.json!github:systemjs/plugin-json@0.1.0",
"quote/QuoteModel",
"npm:underscore@1.8.3",
"github:components/jquery@2.1.4",
"npm:process@0.10.1",
"github:components/handlebars.js@3.0.3",
"quote/QuoteCollection",
"github:jspm/nodelibs-process@0.1.1/index",
"quote/quoteTemplate.hbs!github:davis/plugin-hbs@1.0.0",
"github:jspm/nodelibs-process@0.1.1",
"quote/QuoteView",
"npm:backbone@1.2.1/backbone",
"appRouter",
"npm:backbone@1.2.1",
"startup"
]
}
}
ollup
= Dead Code Elimination
=> Use System.import() and create several bundles !
jspm bundle app/app.js dist/app.js
jspm bundle app/quote/QuoteView.js - app/app.js dist/quotes.js
quote() {
let promise;
if(this.quoteView) {
promise = Promise.resolve(this.quoteView);
} else {
promise = System.import('quote/QuoteView').then(QuoteView => {
this.quoteView = new QuoteView['default']();
return this.quoteView;
});
}
return promise.then(quoteView => quoteView.render());
}
src/appRouter.js
1.
2.
3.
4.
5.
All!
That's
And
Replace scripts in index.html
JSPM build
npm install --save-dev gulp gulp-html-replace
var gulp = require('gulp');
var htmlReplace = require('gulp-html-replace');
gulp.task('html', function () {
return gulp.src(['index.html'])
.pipe(htmlReplace({
js: 'app.js'
}))
.pipe(gulp.dest('dist'));
});
Gulpfile.js
<!-- build:js -->
<script src="/jspm_packages/system.js"></script>
<script src="/jspm.browser.js"></script>
<script src="/jspm.config.js"></script>
<script>
System['import']('app/app.js');
</script>
<!-- endbuild -->
index.html
{
"scripts": {
"build": "jspm build app/app.js dist/app.js --minify && gulp html"
}
}
npm run build
package.json
npm install --save-dev mocha babel-core
npm install --save-dev babel-preset-es2015
{
"presets": ["es2015"]
}
.babelrc
npm install --save-dev karma karma-mocha \
karma-jspm karma-chrome-launcher
module.exports = function(config) {
config.set({
proxies: {
'/base': '/base/src',
},
frameworks: ['jspm', 'mocha'],
jspm: {
config: "src/jspm-config.js",
packages: '/src/jspm_packages',
serveFiles: [
'src/**/*.js',
'src/**/*.json',
'src/**/*.hbs',
],
loadFiles: [
'src/**/tests/**/*.test.js'
]
},
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
concurrency: Infinity
})
}
karma.conf.js
import QuoteCollection from 'quote/QuoteCollection';
describe('Test', function() {
const quoteCollection = new QuoteCollection();
describe('#get()', function() {
it('should return 6 quotes', function() {
expect(quoteCollection.get().length).toEqual(6);
});
});
});
src/quote/tests/QuoteCollection.test.js
karma start karma.conf.js
{
"scripts": {
"test:unit": "karma start karma.conf.js"
}
}
npm run test:unit
package.json
npm install --save-dev codeceptjs
npm install -g codeceptjs
codeceptjs init
Scenario('I have a button to change the quote', (I) => {
I.amOnPage('/');
I.waitForText('Another quote!');
});
Feature('Random quotes');
Scenario('I can change the quote', function*(I) {
I.amOnPage('/');
I.waitForText('Another quote!');
const quote1 = yield I.grabTextFrom('.quote');
I.click('Another quote!');
const quote2 = yield I.grabTextFrom('.quote');
assert.notEqual(quote1, quote2);
});
Scenario('I never have the same quote when changing the quote', function*(I) {
I.amOnPage('/');
for(let i = 0; i < 30; i++) {
I.waitForText('Another quote!');
const quote1 = yield I.grabTextFrom('.quote');
I.click('Another quote!');
const quote2 = yield I.grabTextFrom('.quote');
assert.notEqual(quote1, quote2);
}
});
{
"scripts": {
"test:e2e": "codeceptjs run",
"test": "npm run test:unit && npm run test:e2e"
}
}
npm run test
package.json
{
"scripts": {
"postinstall": "jspm install",
"start": "live-server",
"build": "jspm build src/app.js dist/app.js --minify && gulp html",
"test:unit": "karma run karma.conf.js",
"test:e2e": "codeceptjs run",
"test": "npm run test:unit && npm run test:e2e"
}
}
package.json
Jordane Grenat | @JoGrenat