From e13e630cd6e4fc0b1ff92098a28a770794c7bb9a Mon Sep 17 00:00:00 2001
From: gabrhr <73925454+gabrhr@users.noreply.github.com>
Date: Wed, 20 Apr 2022 10:19:29 -0500
Subject: =?UTF-8?q?A=C3=B1adir=20plantilla?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Base para front
---
 front/odiparpack/internals/.DS_Store               | Bin 0 -> 6148 bytes
 front/odiparpack/internals/config.js               |  57 ++++++
 .../internals/generators/component/class.js.hbs    |  31 ++++
 .../internals/generators/component/index.js        | 109 +++++++++++
 .../internals/generators/component/loadable.js.hbs |  12 ++
 .../internals/generators/component/messages.js.hbs |  14 ++
 .../generators/component/stateless.js.hbs          |  28 +++
 .../internals/generators/component/test.js.hbs     |  10 +
 .../internals/generators/container/actions.js.hbs  |  13 ++
 .../generators/container/actions.test.js.hbs       |  13 ++
 .../internals/generators/container/class.js.hbs    |  93 ++++++++++
 .../generators/container/constants.js.hbs          |   7 +
 .../internals/generators/container/index.js        | 194 +++++++++++++++++++
 .../internals/generators/container/index.js.hbs    |  66 +++++++
 .../internals/generators/container/messages.js.hbs |  14 ++
 .../internals/generators/container/reducer.js.hbs  |  21 +++
 .../generators/container/reducer.test.js.hbs       |   8 +
 .../internals/generators/container/saga.js.hbs     |   6 +
 .../generators/container/saga.test.js.hbs          |  15 ++
 .../generators/container/selectors.js.hbs          |  23 +++
 .../generators/container/selectors.test.js.hbs     |   8 +
 .../generators/container/stateless.js.hbs          |  87 +++++++++
 .../internals/generators/container/test.js.hbs     |  10 +
 front/odiparpack/internals/generators/index.js     |  41 +++++
 .../generators/language/add-locale-data.hbs        |   1 +
 .../internals/generators/language/app-locale.hbs   |   1 +
 .../language/format-translation-messages.hbs       |   1 +
 .../internals/generators/language/index.js         |  92 +++++++++
 .../generators/language/intl-locale-data.hbs       |   1 +
 .../generators/language/polyfill-intl-locale.hbs   |   1 +
 .../generators/language/translation-messages.hbs   |   1 +
 .../generators/language/translations-json.hbs      |   1 +
 .../internals/generators/utils/componentExists.js  |  21 +++
 front/odiparpack/internals/mocks/cssModule.js      |   1 +
 front/odiparpack/internals/mocks/image.js          |   1 +
 front/odiparpack/internals/scripts/analyze.js      |  27 +++
 front/odiparpack/internals/scripts/clean.js        |  63 +++++++
 front/odiparpack/internals/scripts/dependencies.js |  52 ++++++
 front/odiparpack/internals/scripts/extract-intl.js | 187 +++++++++++++++++++
 .../scripts/generate-templates-for-linting.js      | 119 ++++++++++++
 .../internals/scripts/helpers/checkmark.js         |  11 ++
 .../internals/scripts/helpers/progress.js          |  25 +++
 .../odiparpack/internals/scripts/helpers/xmark.js  |  11 ++
 .../internals/scripts/npmcheckversion.js           |   8 +
 front/odiparpack/internals/testing/enzyme-setup.js |   4 +
 front/odiparpack/internals/testing/test-bundler.js |   3 +
 .../internals/webpack/webpack.base.babel.js        | 205 +++++++++++++++++++++
 .../internals/webpack/webpack.dev.babel.js         | 144 +++++++++++++++
 .../internals/webpack/webpack.dll.babel.js         |  58 ++++++
 .../internals/webpack/webpack.prod.babel.js        | 154 ++++++++++++++++
 50 files changed, 2073 insertions(+)
 create mode 100644 front/odiparpack/internals/.DS_Store
 create mode 100644 front/odiparpack/internals/config.js
 create mode 100644 front/odiparpack/internals/generators/component/class.js.hbs
 create mode 100644 front/odiparpack/internals/generators/component/index.js
 create mode 100644 front/odiparpack/internals/generators/component/loadable.js.hbs
 create mode 100644 front/odiparpack/internals/generators/component/messages.js.hbs
 create mode 100644 front/odiparpack/internals/generators/component/stateless.js.hbs
 create mode 100644 front/odiparpack/internals/generators/component/test.js.hbs
 create mode 100644 front/odiparpack/internals/generators/container/actions.js.hbs
 create mode 100644 front/odiparpack/internals/generators/container/actions.test.js.hbs
 create mode 100644 front/odiparpack/internals/generators/container/class.js.hbs
 create mode 100644 front/odiparpack/internals/generators/container/constants.js.hbs
 create mode 100644 front/odiparpack/internals/generators/container/index.js
 create mode 100644 front/odiparpack/internals/generators/container/index.js.hbs
 create mode 100644 front/odiparpack/internals/generators/container/messages.js.hbs
 create mode 100644 front/odiparpack/internals/generators/container/reducer.js.hbs
 create mode 100644 front/odiparpack/internals/generators/container/reducer.test.js.hbs
 create mode 100644 front/odiparpack/internals/generators/container/saga.js.hbs
 create mode 100644 front/odiparpack/internals/generators/container/saga.test.js.hbs
 create mode 100644 front/odiparpack/internals/generators/container/selectors.js.hbs
 create mode 100644 front/odiparpack/internals/generators/container/selectors.test.js.hbs
 create mode 100644 front/odiparpack/internals/generators/container/stateless.js.hbs
 create mode 100644 front/odiparpack/internals/generators/container/test.js.hbs
 create mode 100644 front/odiparpack/internals/generators/index.js
 create mode 100644 front/odiparpack/internals/generators/language/add-locale-data.hbs
 create mode 100644 front/odiparpack/internals/generators/language/app-locale.hbs
 create mode 100644 front/odiparpack/internals/generators/language/format-translation-messages.hbs
 create mode 100644 front/odiparpack/internals/generators/language/index.js
 create mode 100644 front/odiparpack/internals/generators/language/intl-locale-data.hbs
 create mode 100644 front/odiparpack/internals/generators/language/polyfill-intl-locale.hbs
 create mode 100644 front/odiparpack/internals/generators/language/translation-messages.hbs
 create mode 100644 front/odiparpack/internals/generators/language/translations-json.hbs
 create mode 100644 front/odiparpack/internals/generators/utils/componentExists.js
 create mode 100644 front/odiparpack/internals/mocks/cssModule.js
 create mode 100644 front/odiparpack/internals/mocks/image.js
 create mode 100644 front/odiparpack/internals/scripts/analyze.js
 create mode 100644 front/odiparpack/internals/scripts/clean.js
 create mode 100644 front/odiparpack/internals/scripts/dependencies.js
 create mode 100644 front/odiparpack/internals/scripts/extract-intl.js
 create mode 100644 front/odiparpack/internals/scripts/generate-templates-for-linting.js
 create mode 100644 front/odiparpack/internals/scripts/helpers/checkmark.js
 create mode 100644 front/odiparpack/internals/scripts/helpers/progress.js
 create mode 100644 front/odiparpack/internals/scripts/helpers/xmark.js
 create mode 100644 front/odiparpack/internals/scripts/npmcheckversion.js
 create mode 100644 front/odiparpack/internals/testing/enzyme-setup.js
 create mode 100644 front/odiparpack/internals/testing/test-bundler.js
 create mode 100644 front/odiparpack/internals/webpack/webpack.base.babel.js
 create mode 100644 front/odiparpack/internals/webpack/webpack.dev.babel.js
 create mode 100644 front/odiparpack/internals/webpack/webpack.dll.babel.js
 create mode 100644 front/odiparpack/internals/webpack/webpack.prod.babel.js
(limited to 'front/odiparpack/internals')
diff --git a/front/odiparpack/internals/.DS_Store b/front/odiparpack/internals/.DS_Store
new file mode 100644
index 0000000..acaf70c
Binary files /dev/null and b/front/odiparpack/internals/.DS_Store differ
diff --git a/front/odiparpack/internals/config.js b/front/odiparpack/internals/config.js
new file mode 100644
index 0000000..ffff2f0
--- /dev/null
+++ b/front/odiparpack/internals/config.js
@@ -0,0 +1,57 @@
+const { resolve } = require('path');
+const pullAll = require('lodash/pullAll');
+const uniq = require('lodash/uniq');
+
+const ReactBoilerplate = {
+  // This refers to the react-boilerplate version this project is based on.
+  version: '3.6.0',
+
+  /**
+   * The DLL Plugin provides a dramatic speed increase to webpack build and hot module reloading
+   * by caching the module metadata for all of our npm dependencies. We enable it by default
+   * in development.
+   *
+   *
+   * To disable the DLL Plugin, set this value to false.
+   */
+  dllPlugin: {
+    defaults: {
+      /**
+       * we need to exclude dependencies which are not intended for the browser
+       * by listing them here.
+       */
+      exclude: [
+        '@date-io/date-fns',
+        'chalk',
+        'compression',
+        'cross-env',
+        'express',
+        'ip',
+        'minimist',
+        'sanitize.css',
+      ],
+
+      /**
+       * Specify any additional dependencies here. We include core-js and lodash
+       * since a lot of our dependencies depend on them and they get picked up by webpack.
+       */
+      include: ['core-js', 'eventsource-polyfill', 'babel-polyfill', 'lodash'],
+
+      // The path where the DLL manifest and bundle will get built
+      path: resolve('../node_modules/react-boilerplate-dlls'),
+    },
+
+    entry(pkg) {
+      const dependencyNames = Object.keys(pkg.dependencies);
+      const exclude = pkg.dllPlugin.exclude || ReactBoilerplate.dllPlugin.defaults.exclude;
+      const include = pkg.dllPlugin.include || ReactBoilerplate.dllPlugin.defaults.include;
+      const includeDependencies = uniq(dependencyNames.concat(include));
+
+      return {
+        reactBoilerplateDeps: pullAll(includeDependencies, exclude),
+      };
+    },
+  },
+};
+
+module.exports = ReactBoilerplate;
diff --git a/front/odiparpack/internals/generators/component/class.js.hbs b/front/odiparpack/internals/generators/component/class.js.hbs
new file mode 100644
index 0000000..310239a
--- /dev/null
+++ b/front/odiparpack/internals/generators/component/class.js.hbs
@@ -0,0 +1,31 @@
+/**
+ *
+ * {{ properCase name }}
+ *
+ */
+
+import React from 'react';
+// import PropTypes from 'prop-types';
+// import styled from 'styled-components';
+
+{{#if wantMessages}}
+import { FormattedMessage } from 'react-intl';
+import messages from './messages';
+{{/if}}
+
+/* eslint-disable react/prefer-stateless-function */
+class {{ properCase name }} extends {{{ type }}} {
+  render() {
+    return (
+      
+      {{#if wantMessages}}
+        
+      {{/if}}
+      
+    );
+  }
+}
+
+{{ properCase name }}.propTypes = {};
+
+export default {{ properCase name }};
diff --git a/front/odiparpack/internals/generators/component/index.js b/front/odiparpack/internals/generators/component/index.js
new file mode 100644
index 0000000..4ac51d9
--- /dev/null
+++ b/front/odiparpack/internals/generators/component/index.js
@@ -0,0 +1,109 @@
+/**
+ * Component Generator
+ */
+
+/* eslint strict: ["off"] */
+
+'use strict';
+
+const componentExists = require('../utils/componentExists');
+
+module.exports = {
+  description: 'Add an unconnected component',
+  prompts: [
+    {
+      type: 'list',
+      name: 'type',
+      message: 'Select the type of component',
+      default: 'Stateless Function',
+      choices: () => [
+        'Stateless Function',
+        'React.PureComponent',
+        'React.Component',
+      ],
+    },
+    {
+      type: 'input',
+      name: 'name',
+      message: 'What should it be called?',
+      default: 'Button',
+      validate: value => {
+        if (/.+/.test(value)) {
+          return componentExists(value)
+            ? 'A component or container with this name already exists'
+            : true;
+        }
+
+        return 'The name is required';
+      },
+    },
+    {
+      type: 'confirm',
+      name: 'wantMessages',
+      default: true,
+      message: 'Do you want i18n messages (i.e. will this component use text)?',
+    },
+    {
+      type: 'confirm',
+      name: 'wantLoadable',
+      default: false,
+      message: 'Do you want to load the component asynchronously?',
+    },
+  ],
+  actions: data => {
+    // Generate index.js and index.test.js
+    let componentTemplate;
+
+    switch (data.type) {
+      case 'Stateless Function': {
+        componentTemplate = './component/stateless.js.hbs';
+        break;
+      }
+      default: {
+        componentTemplate = './component/class.js.hbs';
+      }
+    }
+
+    const actions = [
+      {
+        type: 'add',
+        path: '../../app/components/{{properCase name}}/index.js',
+        templateFile: componentTemplate,
+        abortOnFail: true,
+      },
+      {
+        type: 'add',
+        path: '../../app/components/{{properCase name}}/tests/index.test.js',
+        templateFile: './component/test.js.hbs',
+        abortOnFail: true,
+      },
+    ];
+
+    // If they want a i18n messages file
+    if (data.wantMessages) {
+      actions.push({
+        type: 'add',
+        path: '../../app/components/{{properCase name}}/messages.js',
+        templateFile: './component/messages.js.hbs',
+        abortOnFail: true,
+      });
+    }
+
+    // If want Loadable.js to load the component asynchronously
+    if (data.wantLoadable) {
+      actions.push({
+        type: 'add',
+        path: '../../app/components/{{properCase name}}/Loadable.js',
+        templateFile: './component/loadable.js.hbs',
+        abortOnFail: true,
+      });
+    }
+
+    actions.push({
+      type: 'prettify',
+      path: '/components/',
+    });
+
+    return actions;
+  },
+};
diff --git a/front/odiparpack/internals/generators/component/loadable.js.hbs b/front/odiparpack/internals/generators/component/loadable.js.hbs
new file mode 100644
index 0000000..889bbf6
--- /dev/null
+++ b/front/odiparpack/internals/generators/component/loadable.js.hbs
@@ -0,0 +1,12 @@
+/**
+ *
+ * Asynchronously loads the component for {{ properCase name }}
+ *
+ */
+
+import Loadable from 'react-loadable';
+
+export default Loadable({
+  loader: () => import('./index'),
+  loading: () => null,
+});
diff --git a/front/odiparpack/internals/generators/component/messages.js.hbs b/front/odiparpack/internals/generators/component/messages.js.hbs
new file mode 100644
index 0000000..f73ee90
--- /dev/null
+++ b/front/odiparpack/internals/generators/component/messages.js.hbs
@@ -0,0 +1,14 @@
+/*
+ * {{ properCase name }} Messages
+ *
+ * This contains all the text for the {{ properCase name }} component.
+ */
+
+import { defineMessages } from 'react-intl';
+
+export default defineMessages({
+  header: {
+    id: 'app.components.{{ properCase name }}.header',
+    defaultMessage: 'This is the {{ properCase name}} component !',
+  },
+});
diff --git a/front/odiparpack/internals/generators/component/stateless.js.hbs b/front/odiparpack/internals/generators/component/stateless.js.hbs
new file mode 100644
index 0000000..04ba918
--- /dev/null
+++ b/front/odiparpack/internals/generators/component/stateless.js.hbs
@@ -0,0 +1,28 @@
+/**
+ *
+ * {{ properCase name }}
+ *
+ */
+
+import React from 'react';
+// import PropTypes from 'prop-types';
+// import styled from 'styled-components';
+
+{{#if wantMessages}}
+import { FormattedMessage } from 'react-intl';
+import messages from './messages';
+{{/if}}
+
+function {{ properCase name }}() {
+  return (
+    
+    {{#if wantMessages}}
+      
+    {{/if}}
+    
+  );
+}
+
+{{ properCase name }}.propTypes = {};
+
+export default {{ properCase name }};
diff --git a/front/odiparpack/internals/generators/component/test.js.hbs b/front/odiparpack/internals/generators/component/test.js.hbs
new file mode 100644
index 0000000..42c0e37
--- /dev/null
+++ b/front/odiparpack/internals/generators/component/test.js.hbs
@@ -0,0 +1,10 @@
+// import React from 'react';
+// import { shallow } from 'enzyme';
+
+// import {{ properCase name }} from '../index';
+
+describe('<{{ properCase name }} />', () => {
+  it('Expect to have unit tests specified', () => {
+    expect(true).toEqual(false);
+  });
+});
diff --git a/front/odiparpack/internals/generators/container/actions.js.hbs b/front/odiparpack/internals/generators/container/actions.js.hbs
new file mode 100644
index 0000000..f48b10b
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/actions.js.hbs
@@ -0,0 +1,13 @@
+/*
+ *
+ * {{ properCase name }} actions
+ *
+ */
+
+import { DEFAULT_ACTION } from './constants';
+
+export function defaultAction() {
+  return {
+    type: DEFAULT_ACTION,
+  };
+}
diff --git a/front/odiparpack/internals/generators/container/actions.test.js.hbs b/front/odiparpack/internals/generators/container/actions.test.js.hbs
new file mode 100644
index 0000000..89ca2c7
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/actions.test.js.hbs
@@ -0,0 +1,13 @@
+import { defaultAction } from '../actions';
+import { DEFAULT_ACTION } from '../constants';
+
+describe('{{ properCase name }} actions', () => {
+  describe('Default Action', () => {
+    it('has a type of DEFAULT_ACTION', () => {
+      const expected = {
+        type: DEFAULT_ACTION,
+      };
+      expect(defaultAction()).toEqual(expected);
+    });
+  });
+});
diff --git a/front/odiparpack/internals/generators/container/class.js.hbs b/front/odiparpack/internals/generators/container/class.js.hbs
new file mode 100644
index 0000000..95e265c
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/class.js.hbs
@@ -0,0 +1,93 @@
+/**
+ *
+ * {{properCase name }}
+ *
+ */
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+{{#if wantHeaders}}
+import { Helmet } from 'react-helmet';
+{{/if}}
+{{#if wantMessages}}
+import { FormattedMessage } from 'react-intl';
+{{/if}}
+{{#if wantActionsAndReducer}}
+import { createStructuredSelector } from 'reselect';
+{{/if}}
+import { compose } from 'redux';
+
+{{#if wantSaga}}
+import injectSaga from 'utils/injectSaga';
+{{/if}}
+{{#if wantActionsAndReducer}}
+import injectReducer from 'utils/injectReducer';
+import makeSelect{{properCase name}} from './selectors';
+import reducer from './reducer';
+{{/if}}
+{{#if wantSaga}}
+import saga from './saga';
+{{/if}}
+{{#if wantMessages}}
+import messages from './messages';
+{{/if}}
+
+/* eslint-disable react/prefer-stateless-function */
+export class {{ properCase name }} extends {{{ type }}} {
+  render() {
+    return (
+      
+      {{#if wantHeaders}}
+        
+          {{properCase name}}
+          
+        
+      {{/if}}
+      {{#if wantMessages}}
+        
+      {{/if}}
+      
+    );
+  }
+}
+
+{{ properCase name }}.propTypes = {
+  dispatch: PropTypes.func.isRequired,
+};
+
+{{#if wantActionsAndReducer}}
+const mapStateToProps = createStructuredSelector({
+  {{ lowerCase name }}: makeSelect{{properCase name}}(),
+});
+{{/if}}
+
+function mapDispatchToProps(dispatch) {
+  return {
+    dispatch,
+  };
+}
+
+{{#if wantActionsAndReducer}}
+const withConnect = connect(
+  mapStateToProps,
+  mapDispatchToProps
+);
+
+const withReducer = injectReducer({ key: '{{ camelCase name }}', reducer });
+{{else}}
+const withConnect = connect(null, mapDispatchToProps);
+{{/if}}
+{{#if wantSaga}}
+const withSaga = injectSaga({ key: '{{ camelCase name }}', saga });
+{{/if}}
+
+export default compose(
+{{#if wantActionsAndReducer}}
+  withReducer,
+{{/if}}
+{{#if wantSaga}}
+  withSaga,
+{{/if}}
+  withConnect
+)({{ properCase name }});
diff --git a/front/odiparpack/internals/generators/container/constants.js.hbs b/front/odiparpack/internals/generators/container/constants.js.hbs
new file mode 100644
index 0000000..0a37bd1
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/constants.js.hbs
@@ -0,0 +1,7 @@
+/*
+ *
+ * {{ properCase name }} constants
+ *
+ */
+
+export const DEFAULT_ACTION = 'app/{{ properCase name }}/DEFAULT_ACTION';
diff --git a/front/odiparpack/internals/generators/container/index.js b/front/odiparpack/internals/generators/container/index.js
new file mode 100644
index 0000000..572a161
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/index.js
@@ -0,0 +1,194 @@
+/**
+ * Container Generator
+ */
+
+const componentExists = require('../utils/componentExists');
+
+module.exports = {
+  description: 'Add a container component',
+  prompts: [
+    {
+      type: 'list',
+      name: 'type',
+      message: 'Select the base component type:',
+      default: 'Stateless Function',
+      choices: () => [
+        'Stateless Function',
+        'React.PureComponent',
+        'React.Component',
+      ],
+    },
+    {
+      type: 'input',
+      name: 'name',
+      message: 'What should it be called?',
+      default: 'Form',
+      validate: value => {
+        if (/.+/.test(value)) {
+          return componentExists(value)
+            ? 'A component or container with this name already exists'
+            : true;
+        }
+
+        return 'The name is required';
+      },
+    },
+    {
+      type: 'confirm',
+      name: 'wantHeaders',
+      default: false,
+      message: 'Do you want headers?',
+    },
+    {
+      type: 'confirm',
+      name: 'wantActionsAndReducer',
+      default: true,
+      message:
+        'Do you want an actions/constants/selectors/reducer tuple for this container?',
+    },
+    {
+      type: 'confirm',
+      name: 'wantSaga',
+      default: true,
+      message: 'Do you want sagas for asynchronous flows? (e.g. fetching data)',
+    },
+    {
+      type: 'confirm',
+      name: 'wantMessages',
+      default: true,
+      message: 'Do you want i18n messages (i.e. will this component use text)?',
+    },
+    {
+      type: 'confirm',
+      name: 'wantLoadable',
+      default: true,
+      message: 'Do you want to load resources asynchronously?',
+    },
+  ],
+  actions: data => {
+    // Generate index.js and index.test.js
+    var componentTemplate; // eslint-disable-line no-var
+
+    switch (data.type) {
+      case 'Stateless Function': {
+        componentTemplate = './container/stateless.js.hbs';
+        break;
+      }
+      default: {
+        componentTemplate = './container/class.js.hbs';
+      }
+    }
+
+    const actions = [
+      {
+        type: 'add',
+        path: '../../app/containers/{{properCase name}}/index.js',
+        templateFile: componentTemplate,
+        abortOnFail: true,
+      },
+      {
+        type: 'add',
+        path: '../../app/containers/{{properCase name}}/tests/index.test.js',
+        templateFile: './container/test.js.hbs',
+        abortOnFail: true,
+      },
+    ];
+
+    // If component wants messages
+    if (data.wantMessages) {
+      actions.push({
+        type: 'add',
+        path: '../../app/containers/{{properCase name}}/messages.js',
+        templateFile: './container/messages.js.hbs',
+        abortOnFail: true,
+      });
+    }
+
+    // If they want actions and a reducer, generate actions.js, constants.js,
+    // reducer.js and the corresponding tests for actions and the reducer
+    if (data.wantActionsAndReducer) {
+      // Actions
+      actions.push({
+        type: 'add',
+        path: '../../app/containers/{{properCase name}}/actions.js',
+        templateFile: './container/actions.js.hbs',
+        abortOnFail: true,
+      });
+      actions.push({
+        type: 'add',
+        path: '../../app/containers/{{properCase name}}/tests/actions.test.js',
+        templateFile: './container/actions.test.js.hbs',
+        abortOnFail: true,
+      });
+
+      // Constants
+      actions.push({
+        type: 'add',
+        path: '../../app/containers/{{properCase name}}/constants.js',
+        templateFile: './container/constants.js.hbs',
+        abortOnFail: true,
+      });
+
+      // Selectors
+      actions.push({
+        type: 'add',
+        path: '../../app/containers/{{properCase name}}/selectors.js',
+        templateFile: './container/selectors.js.hbs',
+        abortOnFail: true,
+      });
+      actions.push({
+        type: 'add',
+        path:
+          '../../app/containers/{{properCase name}}/tests/selectors.test.js',
+        templateFile: './container/selectors.test.js.hbs',
+        abortOnFail: true,
+      });
+
+      // Reducer
+      actions.push({
+        type: 'add',
+        path: '../../app/containers/{{properCase name}}/reducer.js',
+        templateFile: './container/reducer.js.hbs',
+        abortOnFail: true,
+      });
+      actions.push({
+        type: 'add',
+        path: '../../app/containers/{{properCase name}}/tests/reducer.test.js',
+        templateFile: './container/reducer.test.js.hbs',
+        abortOnFail: true,
+      });
+    }
+
+    // Sagas
+    if (data.wantSaga) {
+      actions.push({
+        type: 'add',
+        path: '../../app/containers/{{properCase name}}/saga.js',
+        templateFile: './container/saga.js.hbs',
+        abortOnFail: true,
+      });
+      actions.push({
+        type: 'add',
+        path: '../../app/containers/{{properCase name}}/tests/saga.test.js',
+        templateFile: './container/saga.test.js.hbs',
+        abortOnFail: true,
+      });
+    }
+
+    if (data.wantLoadable) {
+      actions.push({
+        type: 'add',
+        path: '../../app/containers/{{properCase name}}/Loadable.js',
+        templateFile: './component/loadable.js.hbs',
+        abortOnFail: true,
+      });
+    }
+
+    actions.push({
+      type: 'prettify',
+      path: '/containers/',
+    });
+
+    return actions;
+  },
+};
diff --git a/front/odiparpack/internals/generators/container/index.js.hbs b/front/odiparpack/internals/generators/container/index.js.hbs
new file mode 100644
index 0000000..39117a1
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/index.js.hbs
@@ -0,0 +1,66 @@
+/*
+ *
+ * {{properCase name }}
+ *
+ */
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+{{#if wantHeaders}}
+import { Helmet } from 'react-helmet';
+{{/if}}
+{{#if wantMessages}}
+import { FormattedMessage } from 'react-intl';
+{{/if}}
+{{#if wantActionsAndReducer}}
+import { createStructuredSelector } from 'reselect';
+import makeSelect{{properCase name}} from './selectors';
+{{/if}}
+{{#if wantMessages}}
+import messages from './messages';
+{{/if}}
+
+/* eslint-disable react/prefer-stateless-function */
+export class {{ properCase name }} extends React.{{{ component }}} {
+  render() {
+    return (
+      
+      {{#if wantHeaders}}
+        
+          {{properCase name}}
+          
+        
+      {{/if}}
+      {{#if wantMessages}}
+        
+      {{/if}}
+      
+    );
+  }
+}
+
+{{ properCase name }}.propTypes = {
+  dispatch: PropTypes.func.isRequired,
+};
+
+{{#if wantActionsAndReducer}}
+const mapStateToProps = createStructuredSelector({
+  {{name}}: makeSelect{{properCase name}}(),
+});
+{{/if}}
+
+function mapDispatchToProps(dispatch) {
+  return {
+    dispatch,
+  };
+}
+
+{{#if wantActionsAndReducer}}
+export default connect(mapStateToProps, mapDispatchToProps)({{ properCase name }});
+{{else}}
+export default connect(null, mapDispatchToProps)({{ properCase name }});
+{{/if}}
diff --git a/front/odiparpack/internals/generators/container/messages.js.hbs b/front/odiparpack/internals/generators/container/messages.js.hbs
new file mode 100644
index 0000000..9500820
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/messages.js.hbs
@@ -0,0 +1,14 @@
+/*
+ * {{properCase name }} Messages
+ *
+ * This contains all the text for the {{properCase name }} component.
+ */
+
+import { defineMessages } from 'react-intl';
+
+export default defineMessages({
+  header: {
+    id: 'app.containers.{{properCase name }}.header',
+    defaultMessage: 'This is {{properCase name}} container !',
+  },
+});
diff --git a/front/odiparpack/internals/generators/container/reducer.js.hbs b/front/odiparpack/internals/generators/container/reducer.js.hbs
new file mode 100644
index 0000000..5af9e12
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/reducer.js.hbs
@@ -0,0 +1,21 @@
+/*
+ *
+ * {{ properCase name }} reducer
+ *
+ */
+
+import { fromJS } from 'immutable';
+import { DEFAULT_ACTION } from './constants';
+
+export const initialState = fromJS({});
+
+function {{ camelCase name }}Reducer(state = initialState, action) {
+  switch (action.type) {
+    case DEFAULT_ACTION:
+      return state;
+    default:
+      return state;
+  }
+}
+
+export default {{ camelCase name }}Reducer;
diff --git a/front/odiparpack/internals/generators/container/reducer.test.js.hbs b/front/odiparpack/internals/generators/container/reducer.test.js.hbs
new file mode 100644
index 0000000..5ac10e3
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/reducer.test.js.hbs
@@ -0,0 +1,8 @@
+import { fromJS } from 'immutable';
+import {{ camelCase name }}Reducer from '../reducer';
+
+describe('{{ camelCase name }}Reducer', () => {
+  it('returns the initial state', () => {
+    expect({{ camelCase name }}Reducer(undefined, {})).toEqual(fromJS({}));
+  });
+});
diff --git a/front/odiparpack/internals/generators/container/saga.js.hbs b/front/odiparpack/internals/generators/container/saga.js.hbs
new file mode 100644
index 0000000..fc475fd
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/saga.js.hbs
@@ -0,0 +1,6 @@
+// import { take, call, put, select } from 'redux-saga/effects';
+
+// Individual exports for testing
+export default function* defaultSaga() {
+  // See example in containers/HomePage/saga.js
+}
diff --git a/front/odiparpack/internals/generators/container/saga.test.js.hbs b/front/odiparpack/internals/generators/container/saga.test.js.hbs
new file mode 100644
index 0000000..9fcf7f5
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/saga.test.js.hbs
@@ -0,0 +1,15 @@
+/**
+ * Test sagas
+ */
+
+/* eslint-disable redux-saga/yield-effects */
+// import { take, call, put, select } from 'redux-saga/effects';
+// import { defaultSaga } from '../saga';
+
+// const generator = defaultSaga();
+
+describe('defaultSaga Saga', () => {
+  it('Expect to have unit tests specified', () => {
+    expect(true).toEqual(false);
+  });
+});
diff --git a/front/odiparpack/internals/generators/container/selectors.js.hbs b/front/odiparpack/internals/generators/container/selectors.js.hbs
new file mode 100644
index 0000000..55e5a04
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/selectors.js.hbs
@@ -0,0 +1,23 @@
+import { createSelector } from 'reselect';
+import { initialState } from './reducer';
+
+/**
+ * Direct selector to the {{ camelCase name }} state domain
+ */
+
+const select{{ properCase name }}Domain = state =>
+  state.get('{{ camelCase name }}', initialState);
+
+/**
+ * Other specific selectors
+ */
+
+/**
+ * Default selector used by {{ properCase name }}
+ */
+
+const makeSelect{{ properCase name }} = () =>
+  createSelector(select{{ properCase name }}Domain, substate => substate.toJS());
+
+export default makeSelect{{ properCase name }};
+export { select{{ properCase name }}Domain };
diff --git a/front/odiparpack/internals/generators/container/selectors.test.js.hbs b/front/odiparpack/internals/generators/container/selectors.test.js.hbs
new file mode 100644
index 0000000..4ad7530
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/selectors.test.js.hbs
@@ -0,0 +1,8 @@
+// import { fromJS } from 'immutable';
+// import { select{{ properCase name }}Domain } from '../selectors';
+
+describe('select{{ properCase name }}Domain', () => {
+  it('Expect to have unit tests specified', () => {
+    expect(true).toEqual(false);
+  });
+});
diff --git a/front/odiparpack/internals/generators/container/stateless.js.hbs b/front/odiparpack/internals/generators/container/stateless.js.hbs
new file mode 100644
index 0000000..2f7994a
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/stateless.js.hbs
@@ -0,0 +1,87 @@
+/**
+ *
+ * {{properCase name }}
+ *
+ */
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+{{#if wantHeaders}}
+import { Helmet } from 'react-helmet';
+{{/if}}
+{{#if wantMessages}}
+import { FormattedMessage } from 'react-intl';
+{{/if}}
+{{#if wantActionsAndReducer}}
+import { createStructuredSelector } from 'reselect';
+{{/if}}
+import { compose } from 'redux';
+
+{{#if wantSaga}}
+import injectSaga from 'utils/injectSaga';
+{{/if}}
+{{#if wantActionsAndReducer}}
+import injectReducer from 'utils/injectReducer';
+import makeSelect{{properCase name}} from './selectors';
+import reducer from './reducer';
+{{/if}}
+{{#if wantSaga}}
+import saga from './saga';
+{{/if}}
+{{#if wantMessages}}
+import messages from './messages';
+{{/if}}
+
+function {{ properCase name }}() {
+  return (
+    
+    {{#if wantHeaders}}
+      
+        {{properCase name}}
+        
+      
+    {{/if}}
+    {{#if wantMessages}}
+      
+    {{/if}}
+    
+  );
+}
+
+{{ properCase name }}.propTypes = {
+  dispatch: PropTypes.func.isRequired,
+};
+
+{{#if wantActionsAndReducer}}
+const mapStateToProps = createStructuredSelector({
+  {{ lowerCase name }}: makeSelect{{properCase name}}(),
+});
+{{/if}}
+
+function mapDispatchToProps(dispatch) {
+  return {
+    dispatch,
+  };
+}
+
+{{#if wantActionsAndReducer}}
+const withConnect = connect(mapStateToProps, mapDispatchToProps);
+
+const withReducer = injectReducer({ key: '{{ lowerCase name }}', reducer });
+{{else}}
+const withConnect = connect(null, mapDispatchToProps);
+{{/if}}
+{{#if wantSaga}}
+const withSaga = injectSaga({ key: '{{ lowerCase name }}', saga });
+{{/if}}
+
+export default compose(
+{{#if wantActionsAndReducer}}
+  withReducer,
+{{/if}}
+{{#if wantSaga}}
+  withSaga,
+{{/if}}
+  withConnect,
+)({{ properCase name }});
diff --git a/front/odiparpack/internals/generators/container/test.js.hbs b/front/odiparpack/internals/generators/container/test.js.hbs
new file mode 100644
index 0000000..8427a4a
--- /dev/null
+++ b/front/odiparpack/internals/generators/container/test.js.hbs
@@ -0,0 +1,10 @@
+// import React from 'react';
+// import { shallow } from 'enzyme';
+
+// import { {{ properCase name }} } from '../index';
+
+describe('<{{ properCase name }} />', () => {
+  it('Expect to have unit tests specified', () => {
+    expect(true).toEqual(false);
+  });
+});
diff --git a/front/odiparpack/internals/generators/index.js b/front/odiparpack/internals/generators/index.js
new file mode 100644
index 0000000..e00eeea
--- /dev/null
+++ b/front/odiparpack/internals/generators/index.js
@@ -0,0 +1,41 @@
+/**
+ * generator/index.js
+ *
+ * Exports the generators so plop knows them
+ */
+
+const fs = require('fs');
+const path = require('path');
+const { exec } = require('child_process');
+const componentGenerator = require('./component/index.js');
+const containerGenerator = require('./container/index.js');
+const languageGenerator = require('./language/index.js');
+
+module.exports = plop => {
+  plop.setGenerator('component', componentGenerator);
+  plop.setGenerator('container', containerGenerator);
+  plop.setGenerator('language', languageGenerator);
+  plop.addHelper('directory', comp => {
+    try {
+      fs.accessSync(
+        path.join(__dirname, `../../app/containers/${comp}`),
+        fs.F_OK,
+      );
+      return `containers/${comp}`;
+    } catch (e) {
+      return `components/${comp}`;
+    }
+  });
+  plop.addHelper('curly', (object, open) => (open ? '{' : '}'));
+  plop.setActionType('prettify', (answers, config) => {
+    const folderPath = `${path.join(
+      __dirname,
+      '/../../app/',
+      config.path,
+      plop.getHelper('properCase')(answers.name),
+      '**.js',
+    )}`;
+    exec(`npm run prettify -- "${folderPath}"`);
+    return folderPath;
+  });
+};
diff --git a/front/odiparpack/internals/generators/language/add-locale-data.hbs b/front/odiparpack/internals/generators/language/add-locale-data.hbs
new file mode 100644
index 0000000..80727c7
--- /dev/null
+++ b/front/odiparpack/internals/generators/language/add-locale-data.hbs
@@ -0,0 +1 @@
+$1addLocaleData({{language}}LocaleData);
diff --git a/front/odiparpack/internals/generators/language/app-locale.hbs b/front/odiparpack/internals/generators/language/app-locale.hbs
new file mode 100644
index 0000000..08753eb
--- /dev/null
+++ b/front/odiparpack/internals/generators/language/app-locale.hbs
@@ -0,0 +1 @@
+$1  '{{language}}',
diff --git a/front/odiparpack/internals/generators/language/format-translation-messages.hbs b/front/odiparpack/internals/generators/language/format-translation-messages.hbs
new file mode 100644
index 0000000..143601f
--- /dev/null
+++ b/front/odiparpack/internals/generators/language/format-translation-messages.hbs
@@ -0,0 +1 @@
+$1  {{language}}: formatTranslationMessages('{{language}}', {{language}}TranslationMessages),
diff --git a/front/odiparpack/internals/generators/language/index.js b/front/odiparpack/internals/generators/language/index.js
new file mode 100644
index 0000000..aaf91e8
--- /dev/null
+++ b/front/odiparpack/internals/generators/language/index.js
@@ -0,0 +1,92 @@
+/**
+ * Language Generator
+ */
+const fs = require('fs');
+const { exec } = require('child_process');
+
+function languageIsSupported(language) {
+  try {
+    fs.accessSync(`app/translations/${language}.json`, fs.F_OK);
+    return true;
+  } catch (e) {
+    return false;
+  }
+}
+
+module.exports = {
+  description: 'Add a language',
+  prompts: [
+    {
+      type: 'input',
+      name: 'language',
+      message:
+        'What is the language you want to add i18n support for (e.g. "fr", "de")?',
+      default: 'fr',
+      validate: value => {
+        if (/.+/.test(value) && value.length === 2) {
+          return languageIsSupported(value)
+            ? `The language "${value}" is already supported.`
+            : true;
+        }
+
+        return '2 character language specifier is required';
+      },
+    },
+  ],
+
+  actions: () => {
+    const actions = [];
+    actions.push({
+      type: 'modify',
+      path: '../../app/i18n.js',
+      pattern: /(const ..LocaleData = require\('react-intl\/locale-data\/..'\);\n)+/g,
+      templateFile: './language/intl-locale-data.hbs',
+    });
+    actions.push({
+      type: 'modify',
+      path: '../../app/i18n.js',
+      pattern: /(\s+'[a-z]+',\n)(?!.*\s+'[a-z]+',)/g,
+      templateFile: './language/app-locale.hbs',
+    });
+    actions.push({
+      type: 'modify',
+      path: '../../app/i18n.js',
+      pattern: /(const ..TranslationMessages = require\('\.\/translations\/..\.json'\);\n)(?!const ..TranslationMessages = require\('\.\/translations\/..\.json'\);\n)/g,
+      templateFile: './language/translation-messages.hbs',
+    });
+    actions.push({
+      type: 'modify',
+      path: '../../app/i18n.js',
+      pattern: /(addLocaleData\([a-z]+LocaleData\);\n)(?!.*addLocaleData\([a-z]+LocaleData\);)/g,
+      templateFile: './language/add-locale-data.hbs',
+    });
+    actions.push({
+      type: 'modify',
+      path: '../../app/i18n.js',
+      pattern: /([a-z]+:\sformatTranslationMessages\('[a-z]+',\s[a-z]+TranslationMessages\),\n)(?!.*[a-z]+:\sformatTranslationMessages\('[a-z]+',\s[a-z]+TranslationMessages\),)/g,
+      templateFile: './language/format-translation-messages.hbs',
+    });
+    actions.push({
+      type: 'add',
+      path: '../../app/translations/{{language}}.json',
+      templateFile: './language/translations-json.hbs',
+      abortOnFail: true,
+    });
+    actions.push({
+      type: 'modify',
+      path: '../../app/app.js',
+      pattern: /(import\('intl\/locale-data\/jsonp\/[a-z]+\.js'\),\n)(?!.*import\('intl\/locale-data\/jsonp\/[a-z]+\.js'\),)/g,
+      templateFile: './language/polyfill-intl-locale.hbs',
+    });
+    actions.push(() => {
+      const cmd = 'npm run extract-intl';
+      exec(cmd, (err, result) => {
+        if (err) throw err;
+        process.stdout.write(result);
+      });
+      return 'modify translation messages';
+    });
+
+    return actions;
+  },
+};
diff --git a/front/odiparpack/internals/generators/language/intl-locale-data.hbs b/front/odiparpack/internals/generators/language/intl-locale-data.hbs
new file mode 100644
index 0000000..7114f82
--- /dev/null
+++ b/front/odiparpack/internals/generators/language/intl-locale-data.hbs
@@ -0,0 +1 @@
+$&const {{language}}LocaleData = require('react-intl/locale-data/{{language}}');
diff --git a/front/odiparpack/internals/generators/language/polyfill-intl-locale.hbs b/front/odiparpack/internals/generators/language/polyfill-intl-locale.hbs
new file mode 100644
index 0000000..139b74c
--- /dev/null
+++ b/front/odiparpack/internals/generators/language/polyfill-intl-locale.hbs
@@ -0,0 +1 @@
+$1        import('intl/locale-data/jsonp/{{language}}.js'),
diff --git a/front/odiparpack/internals/generators/language/translation-messages.hbs b/front/odiparpack/internals/generators/language/translation-messages.hbs
new file mode 100644
index 0000000..6764c6c
--- /dev/null
+++ b/front/odiparpack/internals/generators/language/translation-messages.hbs
@@ -0,0 +1 @@
+$1const {{language}}TranslationMessages = require('./translations/{{language}}.json');
diff --git a/front/odiparpack/internals/generators/language/translations-json.hbs b/front/odiparpack/internals/generators/language/translations-json.hbs
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/front/odiparpack/internals/generators/language/translations-json.hbs
@@ -0,0 +1 @@
+[]
diff --git a/front/odiparpack/internals/generators/utils/componentExists.js b/front/odiparpack/internals/generators/utils/componentExists.js
new file mode 100644
index 0000000..8cd13d9
--- /dev/null
+++ b/front/odiparpack/internals/generators/utils/componentExists.js
@@ -0,0 +1,21 @@
+/**
+ * componentExists
+ *
+ * Check whether the given component exist in either the components or containers directory
+ */
+
+const fs = require('fs');
+const path = require('path');
+const pageComponents = fs.readdirSync(
+  path.join(__dirname, '../../../app/components'),
+);
+const pageContainers = fs.readdirSync(
+  path.join(__dirname, '../../../app/containers'),
+);
+const components = pageComponents.concat(pageContainers);
+
+function componentExists(comp) {
+  return components.indexOf(comp) >= 0;
+}
+
+module.exports = componentExists;
diff --git a/front/odiparpack/internals/mocks/cssModule.js b/front/odiparpack/internals/mocks/cssModule.js
new file mode 100644
index 0000000..52cb086
--- /dev/null
+++ b/front/odiparpack/internals/mocks/cssModule.js
@@ -0,0 +1 @@
+module.exports = 'CSS_MODULE';
diff --git a/front/odiparpack/internals/mocks/image.js b/front/odiparpack/internals/mocks/image.js
new file mode 100644
index 0000000..a566a76
--- /dev/null
+++ b/front/odiparpack/internals/mocks/image.js
@@ -0,0 +1 @@
+module.exports = 'IMAGE_MOCK';
diff --git a/front/odiparpack/internals/scripts/analyze.js b/front/odiparpack/internals/scripts/analyze.js
new file mode 100644
index 0000000..2144e5a
--- /dev/null
+++ b/front/odiparpack/internals/scripts/analyze.js
@@ -0,0 +1,27 @@
+#!/usr/bin/env node
+
+const shelljs = require('shelljs');
+const animateProgress = require('./helpers/progress');
+const chalk = require('chalk');
+const addCheckMark = require('./helpers/checkmark');
+
+const progress = animateProgress('Generating stats');
+
+// Generate stats.json file with webpack
+shelljs.exec(
+  'webpack --config internals/webpack/webpack.prod.babel.js --profile --json > stats.json',
+  addCheckMark.bind(null, callback), // Output a checkmark on completion
+);
+
+// Called after webpack has finished generating the stats.json file
+function callback() {
+  clearInterval(progress);
+  process.stdout.write(
+    '\n\nOpen ' +
+      chalk.magenta('http://webpack.github.io/analyse/') +
+      ' in your browser and upload the stats.json file!' +
+      chalk.blue(
+        '\n(Tip: ' + chalk.italic('CMD + double-click') + ' the link!)\n\n',
+      ),
+  );
+}
diff --git a/front/odiparpack/internals/scripts/clean.js b/front/odiparpack/internals/scripts/clean.js
new file mode 100644
index 0000000..52a93e0
--- /dev/null
+++ b/front/odiparpack/internals/scripts/clean.js
@@ -0,0 +1,63 @@
+const shell = require('shelljs');
+const addCheckMark = require('./helpers/checkmark.js');
+
+if (!shell.which('git')) {
+  shell.echo('Sorry, this script requires git');
+  shell.exit(1);
+}
+
+if (!shell.test('-e', 'internals/templates')) {
+  shell.echo('The example is deleted already.');
+  shell.exit(1);
+}
+
+process.stdout.write('Cleanup started...');
+
+// Reuse existing LanguageProvider and i18n tests
+shell.mv(
+  'app/containers/LanguageProvider/tests',
+  'internals/templates/containers/LanguageProvider',
+);
+shell.cp('app/tests/i18n.test.js', 'internals/templates/tests/i18n.test.js');
+
+// Cleanup components/
+shell.rm('-rf', 'app/components/*');
+
+// Handle containers/
+shell.rm('-rf', 'app/containers');
+shell.mv('internals/templates/containers', 'app');
+
+// Handle tests/
+shell.mv('internals/templates/tests', 'app');
+
+// Handle translations/
+shell.rm('-rf', 'app/translations');
+shell.mv('internals/templates/translations', 'app');
+
+// Handle utils/
+shell.rm('-rf', 'app/utils');
+shell.mv('internals/templates/utils', 'app');
+
+// Replace the files in the root app/ folder
+shell.cp('internals/templates/app.js', 'app/app.js');
+shell.cp('internals/templates/global-styles.js', 'app/global-styles.js');
+shell.cp('internals/templates/i18n.js', 'app/i18n.js');
+shell.cp('internals/templates/index.html', 'app/index.html');
+shell.cp('internals/templates/reducers.js', 'app/reducers.js');
+shell.cp('internals/templates/configureStore.js', 'app/configureStore.js');
+
+// Remove the templates folder
+shell.rm('-rf', 'internals/templates');
+
+addCheckMark();
+
+// Commit the changes
+if (
+  shell.exec('git add . --all && git commit -qm "Remove default example"')
+    .code !== 0
+) {
+  shell.echo('\nError: Git commit failed');
+  shell.exit(1);
+}
+
+shell.echo('\nCleanup done. Happy Coding!!!');
diff --git a/front/odiparpack/internals/scripts/dependencies.js b/front/odiparpack/internals/scripts/dependencies.js
new file mode 100644
index 0000000..4f9f1ed
--- /dev/null
+++ b/front/odiparpack/internals/scripts/dependencies.js
@@ -0,0 +1,52 @@
+// No need to build the DLL in production
+if (process.env.NODE_ENV === 'production') {
+  process.exit(0);
+}
+
+require('shelljs/global');
+
+const path = require('path');
+const fs = require('fs');
+const exists = fs.existsSync;
+const writeFile = fs.writeFileSync;
+
+const defaults = require('lodash/defaultsDeep');
+const pkg = require(path.join(process.cwd(), 'package.json'));
+const config = require('../config');
+const dllConfig = defaults(pkg.dllPlugin, config.dllPlugin.defaults);
+const outputPath = path.join(process.cwd(), dllConfig.path);
+const dllManifestPath = path.join(outputPath, 'package.json');
+
+/**
+ * I use node_modules/react-boilerplate-dlls by default just because
+ * it isn't going to be version controlled and babel wont try to parse it.
+ */
+mkdir('-p', outputPath);
+
+echo('Building the Webpack DLL...');
+
+/**
+ * Create a manifest so npm install doesn't warn us
+ */
+if (!exists(dllManifestPath)) {
+  writeFile(
+    dllManifestPath,
+    JSON.stringify(
+      defaults({
+        name: 'react-boilerplate-dlls',
+        private: true,
+        author: pkg.author,
+        repository: pkg.repository,
+        version: pkg.version,
+      }),
+      null,
+      2,
+    ),
+    'utf8',
+  );
+}
+
+// the BUILDING_DLL env var is set to avoid confusing the development environment
+exec(
+  'cross-env BUILDING_DLL=true webpack --display-chunks --color --config internals/webpack/webpack.dll.babel.js --hide-modules',
+);
diff --git a/front/odiparpack/internals/scripts/extract-intl.js b/front/odiparpack/internals/scripts/extract-intl.js
new file mode 100644
index 0000000..087b04e
--- /dev/null
+++ b/front/odiparpack/internals/scripts/extract-intl.js
@@ -0,0 +1,187 @@
+/* eslint-disable */
+/**
+ * This script will extract the internationalization messages from all components
+   and package them in the translation json files in the translations file.
+ */
+const fs = require('fs');
+const nodeGlob = require('glob');
+const transform = require('babel-core').transform;
+
+const animateProgress = require('./helpers/progress');
+const addCheckmark = require('./helpers/checkmark');
+
+const pkg = require('../../package.json');
+const presets = pkg.babel.presets;
+const plugins = pkg.babel.plugins || [];
+
+const i18n = require('../../app/i18n');
+
+const DEFAULT_LOCALE = i18n.DEFAULT_LOCALE;
+
+require('shelljs/global');
+
+// Glob to match all js files except test files
+const FILES_TO_PARSE = 'app/**/!(*.test).js';
+const locales = i18n.appLocales;
+
+const newLine = () => process.stdout.write('\n');
+
+// Progress Logger
+let progress;
+const task = message => {
+  progress = animateProgress(message);
+  process.stdout.write(message);
+
+  return error => {
+    if (error) {
+      process.stderr.write(error);
+    }
+    clearTimeout(progress);
+    return addCheckmark(() => newLine());
+  };
+};
+
+// Wrap async functions below into a promise
+const glob = pattern =>
+  new Promise((resolve, reject) => {
+    nodeGlob(
+      pattern,
+      (error, value) => (error ? reject(error) : resolve(value)),
+    );
+  });
+
+const readFile = fileName =>
+  new Promise((resolve, reject) => {
+    fs.readFile(
+      fileName,
+      (error, value) => (error ? reject(error) : resolve(value)),
+    );
+  });
+
+const writeFile = (fileName, data) =>
+  new Promise((resolve, reject) => {
+    fs.writeFile(
+      fileName,
+      data,
+      (error, value) => (error ? reject(error) : resolve(value)),
+    );
+  });
+
+// Store existing translations into memory
+const oldLocaleMappings = [];
+const localeMappings = [];
+
+// Loop to run once per locale
+for (const locale of locales) {
+  oldLocaleMappings[locale] = {};
+  localeMappings[locale] = {};
+  // File to store translation messages into
+  const translationFileName = `app/translations/${locale}.json`;
+  try {
+    // Parse the old translation message JSON files
+    const messages = JSON.parse(fs.readFileSync(translationFileName));
+    const messageKeys = Object.keys(messages);
+    for (const messageKey of messageKeys) {
+      oldLocaleMappings[locale][messageKey] = messages[messageKey];
+    }
+  } catch (error) {
+    if (error.code !== 'ENOENT') {
+      process.stderr.write(
+        `There was an error loading this translation file: ${translationFileName}
+        \n${error}`,
+      );
+    }
+  }
+}
+
+/* push `react-intl` plugin to the existing plugins that are already configured in `package.json`
+   Example:
+   ```
+  "babel": {
+    "plugins": [
+      ["transform-object-rest-spread", { "useBuiltIns": true }]
+    ],
+    "presets": [
+      "env",
+      "react"
+    ]
+  }
+  ```
+*/
+plugins.push(['react-intl']);
+
+const extractFromFile = fileName => {
+  return readFile(fileName)
+    .then(code => {
+      // Use babel plugin to extract instances where react-intl is used
+      const { metadata: result } = transform(code, { presets, plugins });
+
+      for (const message of result['react-intl'].messages) {
+        for (const locale of locales) {
+          const oldLocaleMapping = oldLocaleMappings[locale][message.id];
+          // Merge old translations into the babel extracted instances where react-intl is used
+          const newMsg =
+            locale === DEFAULT_LOCALE ? message.defaultMessage : '';
+          localeMappings[locale][message.id] = oldLocaleMapping
+            ? oldLocaleMapping
+            : newMsg;
+        }
+      }
+    })
+    .catch(error => {
+      process.stderr.write(`Error transforming file: ${fileName}\n${error}`);
+    });
+};
+
+const memoryTask = glob(FILES_TO_PARSE);
+const memoryTaskDone = task('Storing language files in memory');
+
+memoryTask.then(files => {
+  memoryTaskDone();
+
+  const extractTask = Promise.all(
+    files.map(fileName => extractFromFile(fileName)),
+  );
+  const extractTaskDone = task('Run extraction on all files');
+  // Run extraction on all files that match the glob on line 16
+  extractTask.then(result => {
+    extractTaskDone();
+
+    // Make the directory if it doesn't exist, especially for first run
+    mkdir('-p', 'app/translations');
+
+    let localeTaskDone;
+    let translationFileName;
+
+    for (const locale of locales) {
+      translationFileName = `app/translations/${locale}.json`;
+      localeTaskDone = task(
+        `Writing translation messages for ${locale} to: ${translationFileName}`,
+      );
+
+      // Sort the translation JSON file so that git diffing is easier
+      // Otherwise the translation messages will jump around every time we extract
+      let messages = {};
+      Object.keys(localeMappings[locale])
+        .sort()
+        .forEach(function(key) {
+          messages[key] = localeMappings[locale][key];
+        });
+
+      // Write to file the JSON representation of the translation messages
+      const prettified = `${JSON.stringify(messages, null, 2)}\n`;
+
+      try {
+        fs.writeFileSync(translationFileName, prettified);
+        localeTaskDone();
+      } catch (error) {
+        localeTaskDone(
+          `There was an error saving this translation file: ${translationFileName}
+          \n${error}`,
+        );
+      }
+    }
+
+    process.exit();
+  });
+});
diff --git a/front/odiparpack/internals/scripts/generate-templates-for-linting.js b/front/odiparpack/internals/scripts/generate-templates-for-linting.js
new file mode 100644
index 0000000..cb3904a
--- /dev/null
+++ b/front/odiparpack/internals/scripts/generate-templates-for-linting.js
@@ -0,0 +1,119 @@
+/**
+ * This script is for internal `react-boilerplate`'s usage. The only purpose of generating all of these templates is
+ * to be able to lint them and detect critical errors. Every generated component's name has to start with
+ * 'RbGenerated' so it can be easily excluded from the test coverage reports.
+ */
+
+const nodePlop = require('node-plop');
+const path = require('path');
+const chalk = require('chalk');
+const rimraf = require('rimraf');
+
+const xmark = require('./helpers/xmark');
+
+process.chdir(path.join(__dirname, '../generators'));
+
+const prettyStringify = data => JSON.stringify(data, null, 2);
+
+const checkForErrors = result => {
+  if (Array.isArray(result.failures) && result.failures.length > 0) {
+    throw result.failures;
+  }
+};
+
+const reportErrorsFor = title => err => {
+  // TODO Replace with our own helpers/log that is guaranteed to be blocking?
+  xmark(() =>
+    console.error(
+      chalk.red(` ERROR generating '${title}': `),
+      prettyStringify(err),
+    ),
+  );
+  process.exit(1);
+};
+
+// Generated tests are designed to fail, which would in turn fail CI builds
+const removeTestsDirFrom = relativePath => () =>
+  rimraf.sync(path.join(__dirname, '/../../app/', relativePath, '/tests'));
+
+const plop = nodePlop('./index.js');
+
+const componentGen = plop.getGenerator('component');
+componentGen
+  .runActions({
+    name: 'RbGeneratedComponentEsclass',
+    type: 'React.Component',
+    wantMessages: true,
+    wantLoadable: true,
+  })
+  .then(checkForErrors)
+  .then(removeTestsDirFrom('components/RbGeneratedComponentEsclass'))
+  .catch(reportErrorsFor('component/React.Component'));
+
+componentGen
+  .runActions({
+    name: 'RbGeneratedComponentEsclasspure',
+    type: 'React.PureComponent',
+    wantMessages: true,
+    wantLoadable: true,
+  })
+  .then(checkForErrors)
+  .then(removeTestsDirFrom('components/RbGeneratedComponentEsclasspure'))
+  .catch(reportErrorsFor('component/React.PureComponent'));
+
+componentGen
+  .runActions({
+    name: 'RbGeneratedComponentStatelessfunction',
+    type: 'Stateless Function',
+    wantMessages: true,
+    wantLoadable: true,
+  })
+  .then(checkForErrors)
+  .then(removeTestsDirFrom('components/RbGeneratedComponentStatelessfunction'))
+  .catch(reportErrorsFor('component/Stateless Function'));
+
+const containerGen = plop.getGenerator('container');
+containerGen
+  .runActions({
+    name: 'RbGeneratedContainerPureComponent',
+    type: 'React.PureComponent',
+    wantHeaders: true,
+    wantActionsAndReducer: true,
+    wantSagas: true,
+    wantMessages: true,
+    wantLoadable: true,
+  })
+  .then(checkForErrors)
+  .then(removeTestsDirFrom('containers/RbGeneratedContainerPureComponent'))
+  .catch(reportErrorsFor('container/React.PureComponent'));
+
+containerGen
+  .runActions({
+    name: 'RbGeneratedContainerComponent',
+    type: 'React.Component',
+    wantHeaders: true,
+    wantActionsAndReducer: true,
+    wantSagas: true,
+    wantMessages: true,
+    wantLoadable: true,
+  })
+  .then(checkForErrors)
+  .then(removeTestsDirFrom('containers/RbGeneratedContainerComponent'))
+  .catch(reportErrorsFor('container/React.Component'));
+
+containerGen
+  .runActions({
+    name: 'RbGeneratedContainerStateless',
+    type: 'Stateless Function',
+    wantHeaders: true,
+    wantActionsAndReducer: true,
+    wantSagas: true,
+    wantMessages: true,
+    wantLoadable: true,
+  })
+  .then(checkForErrors)
+  .then(removeTestsDirFrom('containers/RbGeneratedContainerStateless'))
+  .catch(reportErrorsFor('container/Stateless'));
+
+const languageGen = plop.getGenerator('language');
+languageGen.runActions({ language: 'fr' }).catch(reportErrorsFor('language'));
diff --git a/front/odiparpack/internals/scripts/helpers/checkmark.js b/front/odiparpack/internals/scripts/helpers/checkmark.js
new file mode 100644
index 0000000..ac30dbc
--- /dev/null
+++ b/front/odiparpack/internals/scripts/helpers/checkmark.js
@@ -0,0 +1,11 @@
+const chalk = require('chalk');
+
+/**
+ * Adds mark check symbol
+ */
+function addCheckMark(callback) {
+  process.stdout.write(chalk.green(' ✓'));
+  if (callback) callback();
+}
+
+module.exports = addCheckMark;
diff --git a/front/odiparpack/internals/scripts/helpers/progress.js b/front/odiparpack/internals/scripts/helpers/progress.js
new file mode 100644
index 0000000..4353f20
--- /dev/null
+++ b/front/odiparpack/internals/scripts/helpers/progress.js
@@ -0,0 +1,25 @@
+'use strict';
+
+const readline = require('readline');
+
+/**
+ * Adds an animated progress indicator
+ *
+ * @param  {string} message      The message to write next to the indicator
+ * @param  {number} amountOfDots The amount of dots you want to animate
+ */
+function animateProgress(message, amountOfDots) {
+  if (typeof amountOfDots !== 'number') {
+    amountOfDots = 3;
+  }
+
+  let i = 0;
+  return setInterval(function() {
+    readline.cursorTo(process.stdout, 0);
+    i = (i + 1) % (amountOfDots + 1);
+    const dots = new Array(i + 1).join('.');
+    process.stdout.write(message + dots);
+  }, 500);
+}
+
+module.exports = animateProgress;
diff --git a/front/odiparpack/internals/scripts/helpers/xmark.js b/front/odiparpack/internals/scripts/helpers/xmark.js
new file mode 100644
index 0000000..59d137d
--- /dev/null
+++ b/front/odiparpack/internals/scripts/helpers/xmark.js
@@ -0,0 +1,11 @@
+const chalk = require('chalk');
+
+/**
+ * Adds mark cross symbol
+ */
+function addXMark(callback) {
+  process.stdout.write(chalk.red(' ✘'));
+  if (callback) callback();
+}
+
+module.exports = addXMark;
diff --git a/front/odiparpack/internals/scripts/npmcheckversion.js b/front/odiparpack/internals/scripts/npmcheckversion.js
new file mode 100644
index 0000000..e3ecd0c
--- /dev/null
+++ b/front/odiparpack/internals/scripts/npmcheckversion.js
@@ -0,0 +1,8 @@
+const exec = require('child_process').exec;
+exec('npm -v', function(err, stdout, stderr) {
+  if (err) throw err;
+  if (parseFloat(stdout) < 3) {
+    throw new Error('[ERROR: React Boilerplate] You need npm version @>=3');
+    process.exit(1);
+  }
+});
diff --git a/front/odiparpack/internals/testing/enzyme-setup.js b/front/odiparpack/internals/testing/enzyme-setup.js
new file mode 100644
index 0000000..82edfc9
--- /dev/null
+++ b/front/odiparpack/internals/testing/enzyme-setup.js
@@ -0,0 +1,4 @@
+import { configure } from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+
+configure({ adapter: new Adapter() });
diff --git a/front/odiparpack/internals/testing/test-bundler.js b/front/odiparpack/internals/testing/test-bundler.js
new file mode 100644
index 0000000..928d73e
--- /dev/null
+++ b/front/odiparpack/internals/testing/test-bundler.js
@@ -0,0 +1,3 @@
+// needed for regenerator-runtime
+// (ES7 generator support is required by redux-saga)
+import '@babel/polyfill';
diff --git a/front/odiparpack/internals/webpack/webpack.base.babel.js b/front/odiparpack/internals/webpack/webpack.base.babel.js
new file mode 100644
index 0000000..ca26c12
--- /dev/null
+++ b/front/odiparpack/internals/webpack/webpack.base.babel.js
@@ -0,0 +1,205 @@
+/**
+ * COMMON WEBPACK CONFIGURATION
+ */
+
+const path = require('path');
+const webpack = require('webpack');
+
+const HappyPack = require('happypack');
+const happyThreadPool = HappyPack.ThreadPool({ size: 5 });
+
+module.exports = options => ({
+  mode: options.mode,
+  entry: options.entry,
+  output: Object.assign(
+    {
+      // Compile into js/build.js
+      path: path.resolve(process.cwd(), 'build'),
+      publicPath: '/',
+    },
+    options.output,
+  ), // Merge with env dependent settings
+  devServer: {
+    inline: false,
+  },
+  optimization: options.optimization,
+  module: {
+    rules: [
+      /*
+        Disabled eslint by default.
+        You can enable it to maintain and keep clean your code.
+        NOTE: By enable eslint running app process at beginning will slower
+      */
+      //      {
+      //        enforce: 'pre',
+      //        test: /\.js?$/,
+      //        exclude: [/node_modules/],
+      //        loader: 'eslint-loader',
+      //        options: {
+      //          quiet: true
+      //        }
+      //      },
+      {
+        test: /\.jsx?$/, // Transform all .js files required somewhere with Babel
+        exclude: /node_modules/,
+        use: {
+          loader: 'happypack/loader?id=js',
+          options: options.babelQuery,
+        },
+      },
+      {
+        // Preprocess our own .css files
+        // This is the place to add your own loaders (e.g. sass/less etc.)
+        // for a list of loaders, see https://webpack.js.org/loaders/#styling
+        test: /\.css$/,
+        exclude: /node_modules/,
+        use: ['style-loader', 'css-loader'],
+      },
+      {
+        // Preprocess 3rd party .css files located in node_modules
+        test: /\.css$/,
+        include: /node_modules/,
+        use: ['style-loader', 'css-loader'],
+      },
+      {
+        test: /\.(eot|otf|ttf|woff|woff2)$/,
+        use: 'file-loader',
+      },
+      {
+        test: /\.(scss)$/,
+        use: [{
+          loader: 'style-loader'
+        },
+        {
+          loader: 'css-loader',
+          options:
+          {
+            sourceMap: false,
+            importLoaders: 2,
+            modules: true,
+            localIdentName: '[local]__[hash:base64:5]'
+          }
+        },
+        {
+          loader: 'postcss-loader',
+          options: {
+            sourceMap: false
+          }
+        },
+        {
+          loader: 'sass-loader',
+          options: {
+            outputStyle: 'expanded',
+            sourceMap: false
+          }
+        }],
+      },
+      {
+        test: /\.md$/,
+        use: 'raw-loader'
+      },
+      {
+        test: /\.(jpg|png|gif|svg)$/,
+        use: [
+          {
+            loader: 'url-loader',
+            options: {
+              // Inline files smaller than 10 kB
+              limit: 10 * 1024,
+            },
+          },
+          /*
+            Disabled image compression by default,
+            due error in windows 10 because libpng not available.
+            The libpng avaible on Linux and Mac system only.
+            NOTE: To enable this, first you need to install image-webpack-loader.
+            npm install -i image-webpack-loader --save
+          */
+          //  {
+          //    loader: 'image-webpack-loader',
+          //    options: {
+          //      mozjpeg: {
+          //        enabled: false,
+          //        // NOTE: mozjpeg is disabled as it causes errors in some Linux environments
+          //        // Try enabling it in your environment by switching the config to:
+          //        // enabled: true,
+          //        // progressive: true,
+          //      },
+          //      gifsicle: {
+          //        interlaced: false,
+          //      },
+          //      optipng: {
+          //        optimizationLevel: 7,
+          //      },
+          //      pngquant: {
+          //        quality: '65-90',
+          //        speed: 4,
+          //      },
+          //    },
+          //  },
+        ],
+      },
+      {
+        test: /\.html$/,
+        use: 'html-loader',
+      },
+      {
+        test: /\.(mp4|webm)$/,
+        use: {
+          loader: 'url-loader',
+          options: {
+            limit: 10000,
+          },
+        },
+      },
+    ],
+  },
+  node: {
+    fs: 'empty'
+  },
+  plugins: options.plugins.concat([
+    // Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
+    // inside your code for any environment checks; Terser will automatically
+    // drop any unreachable code.
+    new HappyPack({
+      id: 'js',
+      threadPool: happyThreadPool,
+      loaders: ['babel-loader?cacheDirectory=true']
+    }),
+    new webpack.DefinePlugin({
+      'process.env': {
+        NODE_ENV: JSON.stringify(process.env.NODE_ENV),
+      },
+    }),
+    new webpack.ContextReplacementPlugin(/^\.\/locale$/, context => {
+      if (!/\/moment\//.test(context.context)) {
+        return;
+      }
+      // context needs to be modified in place
+      Object.assign(context, {
+      // include only CJK
+        regExp: /^\.\/(ja|ko|zh)/,
+        // point to the locale data folder relative to moment's src/lib/locale
+        request: './locale'
+      });
+    })
+  ]),
+  resolve: {
+    modules: ['node_modules', 'app'],
+    extensions: ['.js', '.jsx', '.react.js'],
+    mainFields: ['browser', 'jsnext:main', 'main'],
+    alias: {
+      'ba-components': path.resolve(__dirname, '../../app/components/'),
+      'ba-containers': path.resolve(__dirname, '../../app/containers/'),
+      'ba-actions': path.resolve(__dirname, '../../app/actions/'),
+      'ba-styles': path.resolve(__dirname, '../../app/styles/components/'),
+      'ba-helpers': path.resolve(__dirname, '../../app/styles/helpers/'),
+      'ba-api': path.resolve(__dirname, '../../app/api/'),
+      'ba-images': path.resolve(__dirname, '../../public/images/'),
+      'ba-vendor': path.resolve(__dirname, '../../node_modules/'),
+    }
+  },
+  devtool: options.devtool,
+  target: 'web', // Make web variables accessible to webpack, e.g. window
+  performance: options.performance || {},
+});
diff --git a/front/odiparpack/internals/webpack/webpack.dev.babel.js b/front/odiparpack/internals/webpack/webpack.dev.babel.js
new file mode 100644
index 0000000..dda13b8
--- /dev/null
+++ b/front/odiparpack/internals/webpack/webpack.dev.babel.js
@@ -0,0 +1,144 @@
+/**
+ * DEVELOPMENT WEBPACK CONFIGURATION
+ */
+
+const path = require('path');
+const fs = require('fs');
+const glob = require('glob');
+const webpack = require('webpack');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
+const CircularDependencyPlugin = require('circular-dependency-plugin');
+const logger = require('../../server/logger');
+const pkg = require(path.resolve(process.cwd(), 'package.json')); // eslint-disable-line
+const { dllPlugin } = pkg;
+
+const plugins = [
+  new webpack.HotModuleReplacementPlugin(), // Tell webpack we want hot reloading
+  new HtmlWebpackPlugin({
+    inject: true, // Inject all files that are generated by webpack, e.g. bundle.js
+    template: 'app/index.html',
+  }),
+  new CircularDependencyPlugin({
+    exclude: /a\.js|node_modules/, // exclude node_modules
+    failOnError: false, // show a warning when there is a circular dependency
+  }),
+];
+
+if (dllPlugin) {
+  glob.sync(`${dllPlugin.path}/*.dll.js`).forEach(dllPath => {
+    plugins.push(
+      new AddAssetHtmlPlugin({
+        filepath: dllPath,
+        includeSourcemap: false,
+      }),
+    );
+  });
+}
+
+module.exports = require('./webpack.base.babel')({
+  mode: 'development',
+
+  // Add hot reloading in development
+  entry: [
+    'eventsource-polyfill', // Necessary for hot reloading with IE
+    'webpack-hot-middleware/client?reload=true',
+    path.join(process.cwd(), 'app/app.js'), // Start with js/app.js
+  ],
+
+  // Don't use hashes in dev mode for better performance
+  output: {
+    filename: '[name].js',
+    chunkFilename: '[name].chunk.js',
+  },
+
+  optimization: {
+    splitChunks: {
+      chunks: 'all',
+    },
+  },
+
+  // Add development plugins
+  plugins: dependencyHandlers().concat(plugins), // eslint-disable-line no-use-before-define
+
+  // Emit a source map for easier debugging
+  // See https://webpack.js.org/configuration/devtool/#devtool
+  devtool: 'eval-source-map',
+
+  performance: {
+    hints: false,
+  },
+});
+
+/**
+ * Select which plugins to use to optimize the bundle's handling of
+ * third party dependencies.
+ *
+ * If there is a dllPlugin key on the project's package.json, the
+ * Webpack DLL Plugin will be used.
+ *
+ */
+function dependencyHandlers() {
+  // Don't do anything during the DLL Build step
+  if (process.env.BUILDING_DLL) {
+    return [];
+  }
+
+  // Don't do anything if package.json does not have a dllPlugin property
+  // Code splitting now included by default in Webpack 4
+  if (!dllPlugin) {
+    return [];
+  }
+
+  const dllPath = path.resolve(
+    process.cwd(),
+    dllPlugin.path || 'node_modules/react-boilerplate-dlls',
+  );
+
+  /**
+   * If DLLs aren't explicitly defined, we assume all production dependencies listed in package.json
+   * Reminder: You need to exclude any server side dependencies by listing them in dllConfig.exclude
+   */
+  if (!dllPlugin.dlls) {
+    const manifestPath = path.resolve(dllPath, 'reactBoilerplateDeps.json');
+
+    if (!fs.existsSync(manifestPath)) {
+      logger.error(
+        'The DLL manifest is missing. Please run `npm run build:dll`',
+      );
+      process.exit(0);
+    }
+
+    return [
+      new webpack.DllReferencePlugin({
+        context: process.cwd(),
+        manifest: require(manifestPath), // eslint-disable-line
+      }),
+    ];
+  }
+
+  // If DLLs are explicitly defined, we automatically create a DLLReferencePlugin for each of them.
+  const dllManifests = Object.keys(dllPlugin.dlls)
+    .map(name => path.join(dllPath, `/${name}.json`));
+
+  return dllManifests.map(manifestPath => {
+    if (!fs.existsSync(path)) {
+      if (!fs.existsSync(manifestPath)) {
+        logger.error(
+          `The following Webpack DLL manifest is missing: ${path.basename(
+            manifestPath,
+          )}`,
+        );
+        logger.error(`Expected to find it in ${dllPath}`);
+        logger.error('Please run: npm run build:dll');
+
+        process.exit(0);
+      }
+    }
+
+    return new webpack.DllReferencePlugin({
+      context: process.cwd(),
+      manifest: require(manifestPath), // eslint-disable-line
+    });
+  });
+}
diff --git a/front/odiparpack/internals/webpack/webpack.dll.babel.js b/front/odiparpack/internals/webpack/webpack.dll.babel.js
new file mode 100644
index 0000000..d1ee114
--- /dev/null
+++ b/front/odiparpack/internals/webpack/webpack.dll.babel.js
@@ -0,0 +1,58 @@
+/**
+ * WEBPACK DLL GENERATOR
+ *
+ * This profile is used to cache webpack's module
+ * contexts for external library and framework type
+ * dependencies which will usually not change often enough
+ * to warrant building them from scratch every time we use
+ * the webpack process.
+ */
+
+const { join } = require('path');
+const defaults = require('lodash/defaultsDeep');
+const webpack = require('webpack');
+const pkg = require(join(process.cwd(), 'package.json')); // eslint-disable-line
+const { dllPlugin } = require('../config');
+
+if (!pkg.dllPlugin) {
+  process.exit(0);
+}
+
+const dllConfig = defaults(pkg.dllPlugin, dllPlugin.defaults);
+const outputPath = join(process.cwd(), dllConfig.path);
+
+module.exports = require('./webpack.base.babel')({
+  mode: 'development',
+  context: process.cwd(),
+  entry: dllConfig.dlls ? dllConfig.dlls : dllPlugin.entry(pkg),
+  optimization: {
+    minimize: false,
+  },
+  devtool: 'eval',
+  output: {
+    filename: '[name].dll.js',
+    path: outputPath,
+    library: '[name]',
+  },
+  plugins: [
+    new webpack.DllPlugin({
+      name: '[name]',
+      path: join(outputPath, '[name].json'),
+    }),
+    new webpack.ContextReplacementPlugin(/^\.\/locale$/, context => {
+      if (!/\/moment\//.test(context.context)) {
+        return;
+      }
+      // context needs to be modified in place
+      Object.assign(context, {
+      // include only CJK
+        regExp: /^\.\/(ja|ko|zh)/,
+        // point to the locale data folder relative to moment's src/lib/locale
+        request: './locale'
+      });
+    })
+  ],
+  performance: {
+    hints: false,
+  },
+});
diff --git a/front/odiparpack/internals/webpack/webpack.prod.babel.js b/front/odiparpack/internals/webpack/webpack.prod.babel.js
new file mode 100644
index 0000000..94ecac0
--- /dev/null
+++ b/front/odiparpack/internals/webpack/webpack.prod.babel.js
@@ -0,0 +1,154 @@
+// Important modules this config uses
+const path = require('path');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const WebpackPwaManifest = require('webpack-pwa-manifest');
+const OfflinePlugin = require('offline-plugin');
+const { HashedModuleIdsPlugin } = require('webpack');
+const TerserPlugin = require('terser-webpack-plugin');
+const CompressionPlugin = require('compression-webpack-plugin');
+
+module.exports = require('./webpack.base.babel')({
+  mode: 'production',
+
+  // In production, we skip all hot-reloading stuff
+  entry: [
+    require.resolve('react-app-polyfill/ie11'),
+    path.join(process.cwd(), 'app/app.js'),
+  ],
+
+  // Utilize long-term caching by adding content hashes (not compilation hashes) to compiled assets
+  output: {
+    filename: '[name].[chunkhash].js',
+    chunkFilename: '[name].[chunkhash].chunk.js',
+  },
+
+  optimization: {
+    minimize: true,
+    minimizer: [
+      new TerserPlugin({
+        terserOptions: {
+          warnings: false,
+          compress: {
+            comparisons: false,
+          },
+          parse: {},
+          mangle: true,
+          output: {
+            comments: false,
+            ascii_only: true,
+          },
+        },
+        parallel: true,
+        cache: true,
+        sourceMap: true,
+      }),
+    ],
+    nodeEnv: 'production',
+    sideEffects: true,
+    concatenateModules: true,
+    splitChunks: {
+      chunks: 'all',
+      minSize: 30000,
+      minChunks: 1,
+      maxAsyncRequests: 5,
+      maxInitialRequests: 3,
+      name: true,
+      cacheGroups: {
+        commons: {
+          test: /[\\/]node_modules[\\/]/,
+          name: 'vendor',
+          chunks: 'all',
+        },
+        main: {
+          chunks: 'all',
+          minChunks: 2,
+          reuseExistingChunk: true,
+          enforce: true,
+        },
+      },
+    },
+    runtimeChunk: true,
+  },
+
+  plugins: [
+    // Minify and optimize the index.html
+    new HtmlWebpackPlugin({
+      template: 'app/index.html',
+      minify: {
+        removeComments: true,
+        collapseWhitespace: true,
+        removeRedundantAttributes: true,
+        useShortDoctype: true,
+        removeEmptyAttributes: true,
+        removeStyleLinkTypeAttributes: true,
+        keepClosingSlash: true,
+        minifyJS: true,
+        minifyCSS: true,
+        minifyURLs: true,
+      },
+      inject: true,
+    }),
+
+    // Put it in the end to capture all the HtmlWebpackPlugin's
+    // assets manipulations and do leak its manipulations to HtmlWebpackPlugin
+    new OfflinePlugin({
+      relativePaths: false,
+      publicPath: '/',
+      appShell: '/',
+
+      // No need to cache .htaccess. See http://mxs.is/googmp,
+      // this is applied before any match in `caches` section
+      excludes: ['.htaccess'],
+
+      caches: {
+        main: [':rest:'],
+
+        // All chunks marked as `additional`, loaded after main section
+        // and do not prevent SW to install. Change to `optional` if
+        // do not want them to be preloaded at all (cached only when first loaded)
+        additional: ['*.chunk.js'],
+      },
+
+      // Removes warning for about `additional` section usage
+      safeToUseOptionalCaches: true,
+    }),
+
+    new CompressionPlugin({
+      algorithm: 'gzip',
+      test: /\.js$|\.css$|\.html$/,
+      threshold: 10240,
+      minRatio: 0.8,
+    }),
+
+    new WebpackPwaManifest({
+      name: 'React Boilerplate',
+      short_name: 'React BP',
+      description: 'My React Boilerplate-based project!',
+      background_color: '#fafafa',
+      theme_color: '#b1624d',
+      inject: true,
+      ios: true,
+      cons: [
+        {
+          src: path.resolve('public/images/logo.png'),
+          sizes: [72, 96, 128, 144, 192, 384, 512],
+        },
+        {
+          src: path.resolve('public/images/logo.png'),
+          sizes: [120, 152, 167, 180],
+          ios: true,
+        },
+      ],
+    }),
+
+    new HashedModuleIdsPlugin({
+      hashFunction: 'sha256',
+      hashDigest: 'hex',
+      hashDigestLength: 20,
+    }),
+  ],
+
+  performance: {
+    assetFilter: assetFilename => !/(\.map$)|(^(main\.|favicon\.))/.test(assetFilename),
+  },
+});
-- 
cgit v1.2.3