summaryrefslogtreecommitdiffstats
path: root/front/odiparpack/internals/generators
diff options
context:
space:
mode:
Diffstat (limited to 'front/odiparpack/internals/generators')
-rw-r--r--front/odiparpack/internals/generators/component/class.js.hbs31
-rw-r--r--front/odiparpack/internals/generators/component/index.js109
-rw-r--r--front/odiparpack/internals/generators/component/loadable.js.hbs12
-rw-r--r--front/odiparpack/internals/generators/component/messages.js.hbs14
-rw-r--r--front/odiparpack/internals/generators/component/stateless.js.hbs28
-rw-r--r--front/odiparpack/internals/generators/component/test.js.hbs10
-rw-r--r--front/odiparpack/internals/generators/container/actions.js.hbs13
-rw-r--r--front/odiparpack/internals/generators/container/actions.test.js.hbs13
-rw-r--r--front/odiparpack/internals/generators/container/class.js.hbs93
-rw-r--r--front/odiparpack/internals/generators/container/constants.js.hbs7
-rw-r--r--front/odiparpack/internals/generators/container/index.js194
-rw-r--r--front/odiparpack/internals/generators/container/index.js.hbs66
-rw-r--r--front/odiparpack/internals/generators/container/messages.js.hbs14
-rw-r--r--front/odiparpack/internals/generators/container/reducer.js.hbs21
-rw-r--r--front/odiparpack/internals/generators/container/reducer.test.js.hbs8
-rw-r--r--front/odiparpack/internals/generators/container/saga.js.hbs6
-rw-r--r--front/odiparpack/internals/generators/container/saga.test.js.hbs15
-rw-r--r--front/odiparpack/internals/generators/container/selectors.js.hbs23
-rw-r--r--front/odiparpack/internals/generators/container/selectors.test.js.hbs8
-rw-r--r--front/odiparpack/internals/generators/container/stateless.js.hbs87
-rw-r--r--front/odiparpack/internals/generators/container/test.js.hbs10
-rw-r--r--front/odiparpack/internals/generators/index.js41
-rw-r--r--front/odiparpack/internals/generators/language/add-locale-data.hbs1
-rw-r--r--front/odiparpack/internals/generators/language/app-locale.hbs1
-rw-r--r--front/odiparpack/internals/generators/language/format-translation-messages.hbs1
-rw-r--r--front/odiparpack/internals/generators/language/index.js92
-rw-r--r--front/odiparpack/internals/generators/language/intl-locale-data.hbs1
-rw-r--r--front/odiparpack/internals/generators/language/polyfill-intl-locale.hbs1
-rw-r--r--front/odiparpack/internals/generators/language/translation-messages.hbs1
-rw-r--r--front/odiparpack/internals/generators/language/translations-json.hbs1
-rw-r--r--front/odiparpack/internals/generators/utils/componentExists.js21
31 files changed, 943 insertions, 0 deletions
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 (
+ <div>
+ {{#if wantMessages}}
+ <FormattedMessage {...messages.header} />
+ {{/if}}
+ </div>
+ );
+ }
+}
+
+{{ 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 (
+ <div>
+ {{#if wantMessages}}
+ <FormattedMessage {...messages.header} />
+ {{/if}}
+ </div>
+ );
+}
+
+{{ 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 (
+ <div>
+ {{#if wantHeaders}}
+ <Helmet>
+ <title>{{properCase name}}</title>
+ <meta name="description" content="Description of {{properCase name}}" />
+ </Helmet>
+ {{/if}}
+ {{#if wantMessages}}
+ <FormattedMessage {...messages.header} />
+ {{/if}}
+ </div>
+ );
+ }
+}
+
+{{ 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 (
+ <div>
+ {{#if wantHeaders}}
+ <Helmet>
+ <title>{{properCase name}}</title>
+ <meta
+ name="description"
+ content="Description of {{properCase name}}"
+ />
+ </Helmet>
+ {{/if}}
+ {{#if wantMessages}}
+ <FormattedMessage {...messages.header} />
+ {{/if}}
+ </div>
+ );
+ }
+}
+
+{{ 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 (
+ <div>
+ {{#if wantHeaders}}
+ <Helmet>
+ <title>{{properCase name}}</title>
+ <meta name="description" content="Description of {{properCase name}}" />
+ </Helmet>
+ {{/if}}
+ {{#if wantMessages}}
+ <FormattedMessage {...messages.header} />
+ {{/if}}
+ </div>
+ );
+}
+
+{{ 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;