diff --git a/.gitignore b/.gitignore
index 76de67a..0c9f08a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,19 +1,20 @@
 # Sensible files
 includes/config.php
 
 # Data
 images/commons
 wikimedia/dev/feeds/project/*.xml
 wikimedia/dev/feeds/user/*.xml
 wikimedia/dev/feeds/user/index.tmp
 
 # Tools maintained as external packages
 wikimedia/write/sourcetemplatesgenerator
 
 # Composer
 vendor
 composer.lock
 
 # NPM
 node_modules/
 bundle.js
+package-lock.json
diff --git a/gadgets/generators/app.js b/gadgets/generators/app.js
index ee6c728..030f3df 100644
--- a/gadgets/generators/app.js
+++ b/gadgets/generators/app.js
@@ -1,284 +1,286 @@
 /** @jsx React.DOM */
 
 /*  -------------------------------------------------------------
     Generators - Craft sentences, expressions or generate names.
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     Author:         Dereckson
     Dependencies:   React, JSX, classnames, react-select
     Licence:        BSD
     -------------------------------------------------------------    */
 
 /*  -------------------------------------------------------------
     Dependencies
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -   */
 
-require('babel/polyfill');
+require('@babel/polyfill');
 
 var React = require('react');
 var Select = require('react-select');
 var Textarea = require('react-textarea-autosize');
 
+const createReactClass = require('create-react-class');
+
 /*  -------------------------------------------------------------
     Strings and Array prototype functions
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -   */
 
 String.prototype.capitalizeFirstLetter = function() {
     // http://stackoverflow.com/a/1026087/1930997
     return this.charAt(0).toUpperCase() + this.slice(1);
 }
 
 Array.prototype.randomElement = function () {
     // http://stackoverflow.com/a/7120353/1930997
     return this[Math.floor(Math.random() * this.length)]
 }
 
 /*  -------------------------------------------------------------
     Craft
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -   */
 
 var CraftBluePrint = {
 
     bluePrint: null,
     initialized: false,
 
     init: function (bluePrint) {
         this.bluePrint = bluePrint;
         this.initialized = true;
     },
 
     getSource: function () {
         if (!this.initialized) {
             return "";
         }
         return this.bluePrint.craft.source;
     },
 
     craftPickSeveralInOneGroup: function () {
         /**
          * In this mode, we have one group object array, and we select craft.amount items in this group.
          * We return these picked items, concatenated, optionally taking craft.separator in consideration.
          */
         var separator = "", start = "", end = "";
         if (this.bluePrint.craft.separator) {
             separator = this.bluePrint.craft.separator;
         }
         if (this.bluePrint.craft.prepend) {
             start = this.bluePrint.craft.prepend;
         }
         if (this.bluePrint.craft.append) {
             end = this.bluePrint.craft.append;
         }
         var items = [];
         for (var i = 0 ; i < this.bluePrint.craft.amount ; i++) {
             do {
                 candidate = this.bluePrint.group.content.randomElement();
             } while (items.indexOf(candidate) > -1)
             items[i] = candidate;
         };
         return start + items.join(separator) + end;
     },
 
     craftPickOnePerGroup: function () {
         /**
          * In this mode, we have a groups array, and we select one item in each group.
          * We return these picked items, concatenated, optionally taking craft.separator in consideration.
          */
         var separator = "";
         if (this.bluePrint.craft.separator) {
             separator = this.bluePrint.craft.separator;
         }
         var items = this.bluePrint.groups.map(function (group) {
             return group.content.randomElement();
         });
         return items.join(separator);
     },
 
     craft: function () {
         /* The blueprint should define a craft mode and other options:
            craft: { mode: "Quux", ... };
 
            We then call the mode method, for example here craftQuux().
         */
 
         var craftModeMethod = "craft" + this.bluePrint.craft.mode;
         var itemsAmount = 1;
         var itemsSeparator = "\n";
 
         if (this.bluePrint.craft.populate) {
             itemsAmount = this.bluePrint.craft.populate;
         }
         if (this.bluePrint.craft.populateSeparator) {
             itemsSeparator = this.bluePrint.craft.populateSeparator;
         }
 
         if (this[craftModeMethod]) {
             if (itemsAmount == 1) {
                 return this[craftModeMethod]();
             }
 
             var items = [];
             for (i = 0 ; i < itemsAmount ; i++) {
                 items[i] = this[craftModeMethod]();
             }
             return items.join(itemsSeparator);
         }
         return "[Craft exception] Craft mode unknown: " + craftModeMethod;
     },
 };
 
 /*  -------------------------------------------------------------
     Application UI
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -   */
 
-var App = React.createClass({
+var App = createReactClass({
     getDefaultProps: function () {
         return {
             searchable: true,
         };
     },
 
     getInitialState: function () {
         return {
             generator: null,
             generators: [],
             generatorResult: "(running generator...)",
         }
     },
 
     extractName: function (url) {
         var re = /^.*\/(.*).json$/;
         return re.exec(url)[1];
     },
 
     componentDidMount: function () {
         var self = this;
         var url = generators.datasource;
         $.getJSON(url, function (result) {
             if (!result || !result.length) {
                 return;
             }
             var generatorsList = result.map(function (item) {
                 return {
                     url: item,
                     name: self.extractName(item)
                 }
             });
             self.setState({
                 generators: generatorsList
             });
         });
     },
 
     updateGeneratorValue: function (newValue) {
         var url = newValue || null;
         this.setState({
             generator: url
         });
         if (url) {
             this.runGenerator(url);
         }
     },
 
     getOptions: function () {
         return this.state.generators.map(function (item) {
             return { value: item.url, label: item.name };
         });
     },
 
     renderSelector: function () {
         return (
             <div id="generator-selector">
                 <h2>Generator selection</h2>
                 <label htmlFor="generator">Generator to use:</label>
                 <Select
                     name="generator" id="generator"
                     options={this.getOptions()}
                     value={this.state.generator}
                     searchable={this.props.searchable}
                     onChange={this.updateGeneratorValue}
                 />
                 <span>Something else to craft? We'll be happy to add open datasources and provide the generator logic. <a href="https://devcentral.nasqueron.org/tag/tools/">Suggest it here.</a></span>
             </div>);
     },
 
     canRenderGenerator: function () {
         return this.state.generator != null;
     },
 
     getGeneratorTitle: function () {
         return this.extractName(this.state.generator).replace("-", " — ").capitalizeFirstLetter();
     },
 
     recraft: function () {
         this.setState({
             generatorResult: CraftBluePrint.craft()
         });
     },
 
     runGenerator: function (url) {
         var self = this;
         $.getJSON(url, function (result) {
             if (!result) {
                 return;
             }
             CraftBluePrint.init(result);
             self.setState({
                 generatorResult: CraftBluePrint.craft()
             });
         });
     },
 
     renderGenerator: function () {
         if (!this.canRenderGenerator()) {
             return "";
         }
 
         return <div id="generator-result">
             <h2>{this.getGeneratorTitle()}</h2>
             <Textarea id="generator-result" className="result" value={this.state.generatorResult} readOnly />
             <button className="button" onClick={this.recraft}>Roll again</button>
             <p id="generator-source"><strong>Source:</strong> {CraftBluePrint.getSource()}</p>
         </div>;
     },
 
     render: function () {
         return <div id="app">
             {this.renderSelector()}
             {this.renderGenerator()}
         </div>;
     }
 });
 
 /*  -------------------------------------------------------------
     Application entry point
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -   */
 
 var generators = {
     datasource: null,
     id: null,
 
     init: function (datasource, id) {
         this.datasource = datasource;
         this.id = id;
     },
 
     getAppElement: function () {
         return document.getElementById(this.id);
     },
 
     run: function () {
         console.log('Rendering interface...');
         React.render(
             <App/>,
             this.getAppElement()
         );
         console.log('... done.');
     }
 }
 
 /*  -------------------------------------------------------------
     Runs application
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -   */
 
 generators.init('list-generators.query', 'application');
 generators.run();
diff --git a/package.json b/package.json
index d8a6280..69e742e 100644
--- a/package.json
+++ b/package.json
@@ -1,31 +1,33 @@
 {
   "name": "nasqueron-tools",
   "version": "1.0.0",
   "description": "Small utilities, gadgets & scripts to perform daily tasks.",
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1"
   },
   "dependencies": {
-    "react-tools": ">= 0.13.3",
-    "react-select": ">= 0.4.9",
-    "react-textarea-autosize": ">= 2.3.1",
-    "babel": ">= 5.5.8",
-    "browserify": ">= 10.2.4",
-    "reactify": ">= 1.1.1",
-    "uglifyify": ">= 2.4.23"
+    "@babel/polyfill": "^7.12.1",
+    "babel-cli": "^6.26.0",
+    "browserify": "^10.2.4",
+    "create-react-class": "^15.7.0",
+    "react-select": "^0.4.9",
+    "react-textarea-autosize": "^2.3.1",
+    "react-tools": "^0.13.3",
+    "reactify": "^1.1.1",
+    "uglifyify": "^2.4.23"
   },
   "repository": {
     "type": "git",
     "url": "git+https://github.com/nasqueron/tools.nasqueron.org.git"
   },
   "keywords": [
     "nasqueron",
     "tools"
   ],
   "author": "Sébastien Santoro <dereckson@espace-win.org>",
   "license": "BSD",
   "bugs": {
     "url": "http://devcentral.nasqueron.org/tag/tools/"
   },
   "homepage": "https://tools.nasqueron.org/"
 }