29 | );
30 | }
31 |
32 | export default {{ properCase name }};
33 |
--------------------------------------------------------------------------------
/internals/generators/component/styles.css.hbs:
--------------------------------------------------------------------------------
1 | .{{ camelCase name }} { /* stylelint-ignore */
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/internals/generators/component/test.js.hbs:
--------------------------------------------------------------------------------
1 | // import {{ properCase name }} from '../index';
2 |
3 | import expect from 'expect';
4 | // import { shallow } from 'enzyme';
5 | // import React from 'react';
6 |
7 | describe('<{{ properCase name }} />', () => {
8 | it('Expect to have unit tests specified', () => {
9 | expect(true).toEqual(false);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/internals/generators/container/actions.js.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * {{ properCase name }} actions
4 | *
5 | */
6 |
7 | import {
8 | DEFAULT_ACTION,
9 | } from './constants';
10 |
11 | export function defaultAction() {
12 | return {
13 | type: DEFAULT_ACTION,
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/internals/generators/container/actions.test.js.hbs:
--------------------------------------------------------------------------------
1 | import expect from 'expect';
2 | import {
3 | defaultAction,
4 | } from '../actions';
5 | import {
6 | DEFAULT_ACTION,
7 | } from '../constants';
8 |
9 | describe('{{ properCase name }} actions', () => {
10 | describe('Default Action', () => {
11 | it('has a type of DEFAULT_ACTION', () => {
12 | const expected = {
13 | type: DEFAULT_ACTION,
14 | };
15 | expect(defaultAction()).toEqual(expected);
16 | });
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/internals/generators/container/constants.js.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * {{ properCase name }} constants
4 | *
5 | */
6 |
7 | export const DEFAULT_ACTION = 'app/{{ properCase name }}/DEFAULT_ACTION';
8 |
--------------------------------------------------------------------------------
/internals/generators/container/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Container Generator
3 | */
4 |
5 | const componentExists = require('../utils/componentExists');
6 |
7 | module.exports = {
8 | description: 'Add a container component',
9 | prompts: [{
10 | type: 'input',
11 | name: 'name',
12 | message: 'What should it be called?',
13 | default: 'Form',
14 | validate: value => {
15 | if ((/.+/).test(value)) {
16 | return componentExists(value) ? 'A component or container with this name already exists' : true;
17 | }
18 |
19 | return 'The name is required';
20 | },
21 | }, {
22 | type: 'confirm',
23 | name: 'wantHeaders',
24 | default: false,
25 | message: 'Do you want headers?',
26 | }, {
27 | type: 'confirm',
28 | name: 'wantCSS',
29 | default: false,
30 | message: 'Does it have styling?',
31 | }, {
32 | type: 'confirm',
33 | name: 'wantActionsAndReducer',
34 | default: true,
35 | message: 'Do you want an actions/constants/selectors/reducer tupel for this container?',
36 | }, {
37 | type: 'confirm',
38 | name: 'wantSagas',
39 | default: true,
40 | message: 'Do you want sagas for asynchronous flows? (e.g. fetching data)',
41 | }, {
42 | type: 'confirm',
43 | name: 'wantMessages',
44 | default: true,
45 | message: 'Do you want i18n messages (i.e. will this component use text)?',
46 | }],
47 | actions: data => {
48 | // Generate index.js and index.test.js
49 | const actions = [{
50 | type: 'add',
51 | path: '../../app/containers/{{properCase name}}/index.js',
52 | templateFile: './container/index.js.hbs',
53 | abortOnFail: true,
54 | }, {
55 | type: 'add',
56 | path: '../../app/containers/{{properCase name}}/tests/index.test.js',
57 | templateFile: './container/test.js.hbs',
58 | abortOnFail: true,
59 | }];
60 |
61 | // If they want a CSS file, add styles.css
62 | if (data.wantCSS) {
63 | actions.push({
64 | type: 'add',
65 | path: '../../app/containers/{{properCase name}}/styles.css',
66 | templateFile: './container/styles.css.hbs',
67 | abortOnFail: true,
68 | });
69 | }
70 |
71 | // If component wants messages
72 | if (data.wantMessages) {
73 | actions.push({
74 | type: 'add',
75 | path: '../../app/containers/{{properCase name}}/messages.js',
76 | templateFile: './container/messages.js.hbs',
77 | abortOnFail: true,
78 | });
79 | }
80 |
81 | // If they want actions and a reducer, generate actions.js, constants.js,
82 | // reducer.js and the corresponding tests for actions and the reducer
83 | if (data.wantActionsAndReducer) {
84 | // Actions
85 | actions.push({
86 | type: 'add',
87 | path: '../../app/containers/{{properCase name}}/actions.js',
88 | templateFile: './container/actions.js.hbs',
89 | abortOnFail: true,
90 | });
91 | actions.push({
92 | type: 'add',
93 | path: '../../app/containers/{{properCase name}}/tests/actions.test.js',
94 | templateFile: './container/actions.test.js.hbs',
95 | abortOnFail: true,
96 | });
97 |
98 | // Constants
99 | actions.push({
100 | type: 'add',
101 | path: '../../app/containers/{{properCase name}}/constants.js',
102 | templateFile: './container/constants.js.hbs',
103 | abortOnFail: true,
104 | });
105 |
106 | // Selectors
107 | actions.push({
108 | type: 'add',
109 | path: '../../app/containers/{{properCase name}}/selectors.js',
110 | templateFile: './container/selectors.js.hbs',
111 | abortOnFail: true,
112 | });
113 | actions.push({
114 | type: 'add',
115 | path: '../../app/containers/{{properCase name}}/tests/selectors.test.js',
116 | templateFile: './container/selectors.test.js.hbs',
117 | abortOnFail: true,
118 | });
119 |
120 | // Reducer
121 | actions.push({
122 | type: 'add',
123 | path: '../../app/containers/{{properCase name}}/reducer.js',
124 | templateFile: './container/reducer.js.hbs',
125 | abortOnFail: true,
126 | });
127 | actions.push({
128 | type: 'add',
129 | path: '../../app/containers/{{properCase name}}/tests/reducer.test.js',
130 | templateFile: './container/reducer.test.js.hbs',
131 | abortOnFail: true,
132 | });
133 | }
134 |
135 | // Sagas
136 | if (data.wantSagas) {
137 | actions.push({
138 | type: 'add',
139 | path: '../../app/containers/{{properCase name}}/sagas.js',
140 | templateFile: './container/sagas.js.hbs',
141 | abortOnFail: true,
142 | });
143 | actions.push({
144 | type: 'add',
145 | path: '../../app/containers/{{properCase name}}/tests/sagas.test.js',
146 | templateFile: './container/sagas.test.js.hbs',
147 | abortOnFail: true,
148 | });
149 | }
150 |
151 | return actions;
152 | },
153 | };
154 |
--------------------------------------------------------------------------------
/internals/generators/container/index.js.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * {{properCase name }}
4 | *
5 | */
6 |
7 | import React from 'react';
8 | import { connect } from 'react-redux';
9 | {{#if wantHeaders}}
10 | import Helmet from 'react-helmet';
11 | {{/if}}
12 | {{#if wantActionsAndReducer}}
13 | import select{{properCase name}} from './selectors';
14 | {{/if}}
15 | {{#if wantMessages}}
16 | import { FormattedMessage } from 'react-intl';
17 | import messages from './messages';
18 | {{/if}}
19 | {{#if wantCSS}}
20 | import styles from './styles.css';
21 | {{/if}}
22 |
23 | export class {{ properCase name }} extends React.Component { // eslint-disable-line react/prefer-stateless-function
24 | render() {
25 | return (
26 | {{#if wantCSS}}
27 |
28 | {{else}}
29 |
30 | {{/if}}
31 | {{#if wantHeaders}}
32 |
38 | {{/if}}
39 | {{#if wantMessages}}
40 |
41 | {{/if}}
42 |
43 | );
44 | }
45 | }
46 |
47 | {{#if wantActionsAndReducer}}
48 | const mapStateToProps = select{{properCase name}}();
49 | {{/if}}
50 |
51 | function mapDispatchToProps(dispatch) {
52 | return {
53 | dispatch,
54 | };
55 | }
56 |
57 | {{#if wantActionsAndReducer}}
58 | export default connect(mapStateToProps, mapDispatchToProps)({{ properCase name }});
59 | {{else}}
60 | export default connect(mapDispatchToProps)({{ properCase name }});
61 | {{/if}}
62 |
--------------------------------------------------------------------------------
/internals/generators/container/messages.js.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | * {{properCase name }} Messages
3 | *
4 | * This contains all the text for the {{properCase name }} component.
5 | */
6 | import { defineMessages } from 'react-intl';
7 |
8 | export default defineMessages({
9 | header: {
10 | id: 'app.containers.{{properCase name }}.header',
11 | defaultMessage: 'This is {{properCase name}} container !',
12 | },
13 | });
14 |
--------------------------------------------------------------------------------
/internals/generators/container/reducer.js.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * {{ properCase name }} reducer
4 | *
5 | */
6 |
7 | import { fromJS } from 'immutable';
8 | import {
9 | DEFAULT_ACTION,
10 | } from './constants';
11 |
12 | const initialState = fromJS({});
13 |
14 | function {{ camelCase name }}Reducer(state = initialState, action) {
15 | switch (action.type) {
16 | case DEFAULT_ACTION:
17 | return state;
18 | default:
19 | return state;
20 | }
21 | }
22 |
23 | export default {{ camelCase name }}Reducer;
24 |
--------------------------------------------------------------------------------
/internals/generators/container/reducer.test.js.hbs:
--------------------------------------------------------------------------------
1 | import expect from 'expect';
2 | import {{ camelCase name }}Reducer from '../reducer';
3 | import { fromJS } from 'immutable';
4 |
5 | describe('{{ camelCase name }}Reducer', () => {
6 | it('returns the initial state', () => {
7 | expect({{ camelCase name }}Reducer(undefined, {})).toEqual(fromJS({}));
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/internals/generators/container/sagas.js.hbs:
--------------------------------------------------------------------------------
1 | // import { take, call, put, select } from 'redux-saga/effects';
2 |
3 | // Individual exports for testing
4 | export function* defaultSaga() {
5 | return;
6 | }
7 |
8 | // All sagas to be loaded
9 | export default [
10 | defaultSaga,
11 | ];
12 |
--------------------------------------------------------------------------------
/internals/generators/container/sagas.test.js.hbs:
--------------------------------------------------------------------------------
1 | /**
2 | * Test sagas
3 | */
4 |
5 | import expect from 'expect';
6 | // import { take, call, put, select } from 'redux-saga/effects';
7 | // import { defaultSaga } from '../sagas';
8 |
9 | // const generator = defaultSaga();
10 |
11 | describe('defaultSaga Saga', () => {
12 | it('Expect to have unit tests specified', () => {
13 | expect(true).toEqual(false);
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/internals/generators/container/selectors.js.hbs:
--------------------------------------------------------------------------------
1 | import { createSelector } from 'reselect';
2 |
3 | /**
4 | * Direct selector to the {{ camelCase name }} state domain
5 | */
6 | const select{{ properCase name }}Domain = () => state => state.get('{{ camelCase name }}');
7 |
8 | /**
9 | * Other specific selectors
10 | */
11 |
12 |
13 | /**
14 | * Default selector used by {{ properCase name }}
15 | */
16 |
17 | const select{{ properCase name }} = () => createSelector(
18 | select{{ properCase name }}Domain(),
19 | (substate) => substate.toJS()
20 | );
21 |
22 | export default select{{ properCase name }};
23 | export {
24 | select{{ properCase name }}Domain,
25 | };
26 |
--------------------------------------------------------------------------------
/internals/generators/container/selectors.test.js.hbs:
--------------------------------------------------------------------------------
1 | // import { select{{ properCase name }}Domain } from '../selectors';
2 | // import { fromJS } from 'immutable';
3 | import expect from 'expect';
4 |
5 | // const selector = select{{ properCase name}}Domain();
6 |
7 | describe('select{{ properCase name }}Domain', () => {
8 | it('Expect to have unit tests specified', () => {
9 | expect('Test case').toEqual(false);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/internals/generators/container/styles.css.hbs:
--------------------------------------------------------------------------------
1 | .{{ camelCase name }} { /* stylelint-disable */
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/internals/generators/container/test.js.hbs:
--------------------------------------------------------------------------------
1 | // import {{ properCase name }} from '../index';
2 |
3 | import expect from 'expect';
4 | // import { shallow } from 'enzyme';
5 | // import React from 'react';
6 |
7 | describe('<{{ properCase name }} />', () => {
8 | it('Expect to have unit tests specified', () => {
9 | expect(true).toEqual(false);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/internals/generators/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * generator/index.js
3 | *
4 | * Exports the generators so plop knows them
5 | */
6 |
7 | const fs = require('fs');
8 | const componentGenerator = require('./component/index.js');
9 | const containerGenerator = require('./container/index.js');
10 | const routeGenerator = require('./route/index.js');
11 | const languageGenerator = require('./language/index.js');
12 |
13 | module.exports = (plop) => {
14 | plop.setGenerator('component', componentGenerator);
15 | plop.setGenerator('container', containerGenerator);
16 | plop.setGenerator('route', routeGenerator);
17 | plop.setGenerator('language', languageGenerator);
18 | plop.addHelper('directory', (comp) => {
19 | try {
20 | fs.accessSync(`app/containers/${comp}`, fs.F_OK);
21 | return `containers/${comp}`;
22 | } catch (e) {
23 | return `components/${comp}`;
24 | }
25 | });
26 | plop.addHelper('curly', (object, open) => (open ? '{' : '}'));
27 | };
28 |
--------------------------------------------------------------------------------
/internals/generators/language/add-locale-data.hbs:
--------------------------------------------------------------------------------
1 | $1addLocaleData({{language}}LocaleData);
2 |
--------------------------------------------------------------------------------
/internals/generators/language/app-locale.hbs:
--------------------------------------------------------------------------------
1 | $1
2 | '{{language}}',
3 |
--------------------------------------------------------------------------------
/internals/generators/language/format-translation-messages.hbs:
--------------------------------------------------------------------------------
1 | $1 {{language}}: formatTranslationMessages({{language}}TranslationMessages),
2 |
--------------------------------------------------------------------------------
/internals/generators/language/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Language Generator
3 | */
4 | const exec = require('child_process').exec;
5 |
6 | module.exports = {
7 | description: 'Add a langauge',
8 | prompts: [{
9 | type: 'input',
10 | name: 'language',
11 | message: 'What is the language you want to add i18n support for (e.g. "fr", "de")?',
12 | default: 'fr',
13 | validate: value => {
14 | if ((/.+/).test(value) && value.length === 2) {
15 | return true;
16 | }
17 |
18 | return '2 character language specifier is required';
19 | },
20 | }],
21 |
22 | actions: () => {
23 | const actions = [];
24 | actions.push({
25 | type: 'modify',
26 | path: '../../app/i18n.js',
27 | pattern: /('react-intl\/locale-data\/[a-z]+';\n)(?!.*'react-intl\/locale-data\/[a-z]+';)/g,
28 | templateFile: './language/intl-locale-data.hbs',
29 | });
30 | actions.push({
31 | type: 'modify',
32 | path: '../../app/i18n.js',
33 | pattern: /([\n\s'[a-z]+',)(?!.*[\n\s'[a-z]+',)/g,
34 | templateFile: './language/app-locale.hbs',
35 | });
36 | actions.push({
37 | type: 'modify',
38 | path: '../../app/i18n.js',
39 | pattern: /(from\s'.\/translations\/[a-z]+.json';\n)(?!.*from\s'.\/translations\/[a-z]+.json';)/g,
40 | templateFile: './language/translation-messages.hbs',
41 | });
42 | actions.push({
43 | type: 'modify',
44 | path: '../../app/i18n.js',
45 | pattern: /(addLocaleData\([a-z]+LocaleData\);\n)(?!.*addLocaleData\([a-z]+LocaleData\);)/g,
46 | templateFile: './language/add-locale-data.hbs',
47 | });
48 | actions.push({
49 | type: 'modify',
50 | path: '../../app/i18n.js',
51 | pattern: /([a-z]+:\sformatTranslationMessages\([a-z]+TranslationMessages\),\n)(?!.*[a-z]+:\sformatTranslationMessages\([a-z]+TranslationMessages\),)/g,
52 | templateFile: './language/format-translation-messages.hbs',
53 | });
54 | actions.push({
55 | type: 'add',
56 | path: '../../app/translations/{{language}}.json',
57 | templateFile: './language/translations-json.hbs',
58 | abortOnFail: true,
59 | });
60 | actions.push({
61 | type: 'modify',
62 | path: '../../app/app.js',
63 | pattern: /(System\.import\('intl\/locale-data\/jsonp\/[a-z]+\.js'\),\n)(?!.*System\.import\('intl\/locale-data\/jsonp\/[a-z]+\.js'\),)/g,
64 | templateFile: './language/polyfill-intl-locale.hbs',
65 | });
66 | actions.push(
67 | () => {
68 | const cmd = 'npm run extract-intl';
69 | exec(cmd, (err, result, stderr) => {
70 | if (err || stderr) {
71 | throw err || stderr;
72 | }
73 | process.stdout.write(result);
74 | });
75 | }
76 | );
77 |
78 | return actions;
79 | },
80 | };
81 |
--------------------------------------------------------------------------------
/internals/generators/language/intl-locale-data.hbs:
--------------------------------------------------------------------------------
1 | $1import {{language}}LocaleData from 'react-intl/locale-data/{{language}}';
2 |
--------------------------------------------------------------------------------
/internals/generators/language/polyfill-intl-locale.hbs:
--------------------------------------------------------------------------------
1 | $1 System.import('intl/locale-data/jsonp/{{language}}.js'),
2 |
--------------------------------------------------------------------------------
/internals/generators/language/translation-messages.hbs:
--------------------------------------------------------------------------------
1 | $1import {{language}}TranslationMessages from './translations/{{language}}.json';
2 |
--------------------------------------------------------------------------------
/internals/generators/language/translations-json.hbs:
--------------------------------------------------------------------------------
1 | []
2 |
--------------------------------------------------------------------------------
/internals/generators/route/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Route Generator
3 | */
4 | const fs = require('fs');
5 | const componentExists = require('../utils/componentExists');
6 |
7 | function reducerExists(comp) {
8 | try {
9 | fs.accessSync(`app/containers/${comp}/reducer.js`, fs.F_OK);
10 | return true;
11 | } catch (e) {
12 | return false;
13 | }
14 | }
15 |
16 | function sagasExists(comp) {
17 | try {
18 | fs.accessSync(`app/containers/${comp}/sagas.js`, fs.F_OK);
19 | return true;
20 | } catch (e) {
21 | return false;
22 | }
23 | }
24 |
25 | function trimTemplateFile(template) {
26 | // Loads the template file and trims the whitespace and then returns the content as a string.
27 | return fs.readFileSync(`internals/generators/route/${template}`, 'utf8').replace(/\s*$/, '');
28 | }
29 |
30 | module.exports = {
31 | description: 'Add a route',
32 | prompts: [{
33 | type: 'input',
34 | name: 'component',
35 | message: 'Which component should the route show?',
36 | validate: value => {
37 | if ((/.+/).test(value)) {
38 | return componentExists(value) ? true : `"${value}" doesn't exist.`;
39 | }
40 |
41 | return 'The path is required';
42 | },
43 | }, {
44 | type: 'input',
45 | name: 'path',
46 | message: 'Enter the path of the route.',
47 | default: '/about',
48 | validate: value => {
49 | if ((/.+/).test(value)) {
50 | return true;
51 | }
52 |
53 | return 'path is required';
54 | },
55 | }],
56 |
57 | // Add the route to the routes.js file above the error route
58 | // TODO smarter route adding
59 | actions: data => {
60 | const actions = [];
61 | if (reducerExists(data.component)) {
62 | data.useSagas = sagasExists(data.component); // eslint-disable-line no-param-reassign
63 | actions.push({
64 | type: 'modify',
65 | path: '../../app/routes.js',
66 | pattern: /(\s{\n\s{0,}path: '\*',)/g,
67 | template: trimTemplateFile('routeWithReducer.hbs'),
68 | });
69 | } else {
70 | actions.push({
71 | type: 'modify',
72 | path: '../../app/routes.js',
73 | pattern: /(\s{\n\s{0,}path: '\*',)/g,
74 | template: trimTemplateFile('route.hbs'),
75 | });
76 | }
77 |
78 | return actions;
79 | },
80 | };
81 |
--------------------------------------------------------------------------------
/internals/generators/route/route.hbs:
--------------------------------------------------------------------------------
1 | {
2 | path: '{{ path }}',
3 | name: '{{ camelCase component }}',
4 | getComponent(location, cb) {
5 | System.import('{{{directory (properCase component)}}}')
6 | .then(loadModule(cb))
7 | .catch(errorLoading);
8 | },
9 | },$1
10 |
--------------------------------------------------------------------------------
/internals/generators/route/routeWithReducer.hbs:
--------------------------------------------------------------------------------
1 | {
2 | path: '{{ path }}',
3 | name: '{{ camelCase component }}',
4 | getComponent(nextState, cb) {
5 | const importModules = Promise.all([
6 | System.import('containers/{{ properCase component }}/reducer'),
7 | {{#if useSagas}}
8 | System.import('containers/{{ properCase component }}/sagas'),
9 | {{/if}}
10 | System.import('containers/{{ properCase component }}'),
11 | ]);
12 |
13 | const renderRoute = loadModule(cb);
14 |
15 | importModules.then(([reducer,{{#if useSagas}} sagas,{{/if}} component]) => {
16 | injectReducer('{{ camelCase component }}', reducer.default);
17 | {{#if useSagas}}
18 | injectSagas(sagas.default);
19 | {{/if}}
20 | renderRoute(component);
21 | });
22 |
23 | importModules.catch(errorLoading);
24 | },
25 | },$1
26 |
--------------------------------------------------------------------------------
/internals/generators/utils/componentExists.js:
--------------------------------------------------------------------------------
1 | /**
2 | * componentExists
3 | *
4 | * Check whether the given component exist in either the components or containers directory
5 | */
6 |
7 | const fs = require('fs');
8 | const pageComponents = fs.readdirSync('app/components');
9 | const pageContainers = fs.readdirSync('app/containers');
10 | const components = pageComponents.concat(pageContainers);
11 |
12 | function componentExists(comp) {
13 | return components.indexOf(comp) >= 0;
14 | }
15 |
16 | module.exports = componentExists;
17 |
--------------------------------------------------------------------------------
/internals/scripts/analyze.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var shelljs = require('shelljs');
4 | var animateProgress = require('./helpers/progress');
5 | var chalk = require('chalk');
6 | var addCheckMark = require('./helpers/checkmark');
7 |
8 | var progress = animateProgress('Generating stats');
9 |
10 | // Generate stats.json file with webpack
11 | shelljs.exec(
12 | 'webpack --config internals/webpack/webpack.prod.babel.js --profile --json > stats.json',
13 | addCheckMark.bind(null, callback) // Output a checkmark on completion
14 | );
15 |
16 | // Called after webpack has finished generating the stats.json file
17 | function callback() {
18 | clearInterval(progress);
19 | process.stdout.write(
20 | '\n\nOpen ' + chalk.magenta('http://webpack.github.io/analyse/') + ' in your browser and upload the stats.json file!' +
21 | chalk.blue('\n(Tip: ' + chalk.italic('CMD + double-click') + ' the link!)\n\n')
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/internals/scripts/clean.js:
--------------------------------------------------------------------------------
1 | require('shelljs/global');
2 |
3 | /**
4 | * Adds mark check symbol
5 | */
6 | function addCheckMark(callback) {
7 | process.stdout.write(' ✓');
8 | callback();
9 | }
10 |
11 | if (!which('git')) {
12 | echo('Sorry, this script requires git');
13 | exit(1);
14 | }
15 |
16 | if (!test('-e', 'internals/templates')) {
17 | echo('The example is deleted already.');
18 | exit(1);
19 | }
20 |
21 | process.stdout.write('Cleanup started...');
22 |
23 | // Cleanup components folder
24 | rm('-rf', 'app/components/*');
25 |
26 | // Cleanup containers folder
27 | rm('-rf', 'app/containers/*');
28 | mkdir('-p', 'app/containers/App');
29 | mkdir('-p', 'app/containers/NotFoundPage');
30 | mkdir('-p', 'app/containers/HomePage');
31 | cp('internals/templates/appContainer.js', 'app/containers/App/index.js');
32 | cp('internals/templates/notFoundPage/notFoundPage.js', 'app/containers/NotFoundPage/index.js');
33 | cp('internals/templates/notFoundPage/messages.js', 'app/containers/NotFoundPage/messages.js');
34 | cp('internals/templates/homePage/homePage.js', 'app/containers/HomePage/index.js');
35 | cp('internals/templates/homePage/messages.js', 'app/containers/HomePage/messages.js');
36 |
37 | // Handle Translations
38 | mkdir('-p', 'app/translations');
39 | cp('internals/templates/translations/en.json',
40 | 'app/translations/en.json');
41 |
42 | // move i18n file
43 | cp('internals/templates/i18n.js',
44 | 'app/i18n.js');
45 |
46 | // Copy LanguageProvider
47 | mkdir('-p', 'app/containers/LanguageProvider');
48 | mkdir('-p', 'app/containers/LanguageProvider/tests');
49 | cp('internals/templates/languageProvider/actions.js',
50 | 'app/containers/LanguageProvider/actions.js');
51 | cp('internals/templates/languageProvider/constants.js',
52 | 'app/containers/LanguageProvider/constants.js');
53 | cp('internals/templates/languageProvider/languageProvider.js',
54 | 'app/containers/LanguageProvider/index.js');
55 | cp('internals/templates/languageProvider/reducer.js',
56 | 'app/containers/LanguageProvider/reducer.js');
57 | cp('internals/templates/languageProvider/selectors.js',
58 | 'app/containers/LanguageProvider/selectors.js');
59 | cp('internals/templates/styles.css', 'app/containers/App/styles.css');
60 |
61 | // Copy selectors
62 | mkdir('app/containers/App/tests');
63 | cp('internals/templates/selectors.js',
64 | 'app/containers/App/selectors.js');
65 | cp('internals/templates/selectors.test.js',
66 | 'app/containers/App/tests/selectors.test.js');
67 |
68 | // Utils
69 | rm('-rf', 'app/utils');
70 | mkdir('app/utils');
71 | mkdir('app/utils/tests');
72 | cp('internals/templates/asyncInjectors.js',
73 | 'app/utils/asyncInjectors.js');
74 | cp('internals/templates/asyncInjectors.test.js',
75 | 'app/utils/tests/asyncInjectors.test.js');
76 |
77 | // Replace the files in the root app/ folder
78 | cp('internals/templates/app.js', 'app/app.js');
79 | cp('internals/templates/index.html', 'app/index.html');
80 | cp('internals/templates/reducers.js', 'app/reducers.js');
81 | cp('internals/templates/routes.js', 'app/routes.js');
82 | cp('internals/templates/store.js', 'app/store.js');
83 | cp('internals/templates/store.test.js', 'app/store.test.js');
84 |
85 | // Remove the templates folder
86 | rm('-rf', 'internals/templates');
87 |
88 | process.stdout.write(' ✓');
89 |
90 | // Commit the changes
91 | if (exec('git add . --all && git commit -qm "Remove default example"').code !== 0) {
92 | echo('\nError: Git commit failed');
93 | exit(1);
94 | }
95 |
96 | echo('\nCleanup done. Happy Coding!!!');
97 |
--------------------------------------------------------------------------------
/internals/scripts/dependencies.js:
--------------------------------------------------------------------------------
1 | /*eslint-disable*/
2 |
3 | // No need to build the DLL in production
4 | if (process.env.NODE_ENV === 'production') {
5 | process.exit(0)
6 | }
7 |
8 | require('shelljs/global')
9 |
10 | const path = require('path')
11 | const fs = require('fs')
12 | const exists = fs.existsSync
13 | const writeFile = fs.writeFileSync
14 |
15 | const defaults = require('lodash/defaultsDeep')
16 | const pkg = require(path.join(process.cwd(), 'package.json'))
17 | const config = require('../config')
18 | const dllConfig = defaults(pkg.dllPlugin, config.dllPlugin.defaults)
19 | const outputPath = path.join(process.cwd(), dllConfig.path)
20 | const dllManifestPath = path.join(outputPath, 'package.json')
21 |
22 | /**
23 | * I use node_modules/react-boilerplate-dlls by default just because
24 | * it isn't going to be version controlled and babel wont try to parse it.
25 | */
26 | mkdir('-p', outputPath)
27 |
28 | echo('Building the Webpack DLL...')
29 |
30 | /**
31 | * Create a manifest so npm install doesnt warn us
32 | */
33 | if (!exists(dllManifestPath)) {
34 | writeFile(
35 | dllManifestPath,
36 | JSON.stringify(defaults({
37 | name: 'react-boilerplate-dlls',
38 | private: true,
39 | author: pkg.author,
40 | repository: pkg.repository,
41 | version: pkg.version
42 | }), null, 2),
43 |
44 | 'utf8'
45 | )
46 | }
47 |
48 | // the BUILDING_DLL env var is set to avoid confusing the development environment
49 | exec('cross-env BUILDING_DLL=true webpack --display-chunks --color --config internals/webpack/webpack.dll.babel.js')
50 |
--------------------------------------------------------------------------------
/internals/scripts/helpers/checkmark.js:
--------------------------------------------------------------------------------
1 | var chalk = require('chalk');
2 |
3 | /**
4 | * Adds mark check symbol
5 | */
6 | function addCheckMark(callback) {
7 | process.stdout.write(chalk.green(' ✓'));
8 | callback();
9 | }
10 |
11 | module.exports = addCheckMark;
12 |
--------------------------------------------------------------------------------
/internals/scripts/helpers/progress.js:
--------------------------------------------------------------------------------
1 | var readline = require('readline');
2 |
3 | /**
4 | * Adds an animated progress indicator
5 | *
6 | * @param {string} message The message to write next to the indicator
7 | * @param {number} amountOfDots The amount of dots you want to animate
8 | */
9 | function animateProgress(message, amountOfDots) {
10 | if (typeof amountOfDots !== 'number') {
11 | amountOfDots = 3;
12 | }
13 |
14 | var i = 0;
15 | return setInterval(function () {
16 | readline.cursorTo(process.stdout, 0);
17 | i = (i + 1) % (amountOfDots + 1);
18 | var dots = new Array(i + 1).join('.');
19 | process.stdout.write(message + dots);
20 | }, 500);
21 | }
22 |
23 | module.exports = animateProgress;
24 |
--------------------------------------------------------------------------------
/internals/scripts/npmcheckversion.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | var exec = require('child_process').exec;
3 | exec('npm -v', function (err, stdout, stderr) {
4 | if (err) throw err;
5 | if (parseFloat(stdout) < 3) {
6 | throw new Error('[ERROR: React Boilerplate] You need npm version @>=3');
7 | process.exit(1);
8 | }
9 | });
10 |
--------------------------------------------------------------------------------
/internals/scripts/pagespeed.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | process.stdin.resume();
4 | process.stdin.setEncoding('utf8');
5 |
6 | var ngrok = require('ngrok');
7 | var psi = require('psi');
8 | var chalk = require('chalk');
9 |
10 | log('\nStarting ngrok tunnel');
11 |
12 | startTunnel(runPsi);
13 |
14 | function runPsi(url) {
15 | log('\nStarting PageSpeed Insights');
16 | psi.output(url).then(function (err) {
17 | process.exit(0);
18 | });
19 | }
20 |
21 | function startTunnel(cb) {
22 | ngrok.connect(3000, function (err, url) {
23 | if (err) {
24 | log(chalk.red('\nERROR\n' + err));
25 | process.exit(0);
26 | }
27 |
28 | log('\nServing tunnel from: ' + chalk.magenta(url));
29 | cb(url);
30 | });
31 | }
32 |
33 | function log(string) {
34 | process.stdout.write(string);
35 | }
36 |
--------------------------------------------------------------------------------
/internals/testing/karma.conf.js:
--------------------------------------------------------------------------------
1 | const webpackConfig = require('../webpack/webpack.test.babel');
2 | const argv = require('minimist')(process.argv.slice(2));
3 | const path = require('path');
4 |
5 | module.exports = (config) => {
6 | config.set({
7 | frameworks: ['mocha'],
8 | reporters: ['coverage', 'mocha'],
9 | browsers: process.env.TRAVIS // eslint-disable-line no-nested-ternary
10 | ? ['ChromeTravis']
11 | : process.env.APPVEYOR
12 | ? ['IE'] : ['Chrome'],
13 |
14 | autoWatch: false,
15 | singleRun: true,
16 |
17 | client: {
18 | mocha: {
19 | grep: argv.grep,
20 | },
21 | },
22 |
23 | files: [
24 | {
25 | pattern: './test-bundler.js',
26 | watched: false,
27 | served: true,
28 | included: true,
29 | },
30 | ],
31 |
32 | preprocessors: {
33 | ['./test-bundler.js']: ['webpack', 'sourcemap'], // eslint-disable-line no-useless-computed-key
34 | },
35 |
36 | webpack: webpackConfig,
37 |
38 | // make Webpack bundle generation quiet
39 | webpackMiddleware: {
40 | noInfo: true,
41 | stats: 'errors-only',
42 | },
43 |
44 | customLaunchers: {
45 | ChromeTravis: {
46 | base: 'Chrome',
47 | flags: ['--no-sandbox'],
48 | },
49 | },
50 |
51 | coverageReporter: {
52 | dir: path.join(process.cwd(), 'coverage'),
53 | reporters: [
54 | { type: 'lcov', subdir: 'lcov' },
55 | { type: 'html', subdir: 'html' },
56 | { type: 'text-summary' },
57 | ],
58 | },
59 |
60 | });
61 | };
62 |
--------------------------------------------------------------------------------
/internals/testing/test-bundler.js:
--------------------------------------------------------------------------------
1 | // needed for regenerator-runtime
2 | // (ES7 generator support is required by redux-saga)
3 | import 'babel-polyfill';
4 |
5 | // If we need to use Chai, we'll have already chaiEnzyme loaded
6 | import chai from 'chai';
7 | import chaiEnzyme from 'chai-enzyme';
8 | chai.use(chaiEnzyme());
9 |
10 | // Include all .js files under `app`, except app.js, reducers.js, routes.js and
11 | // store.js. This is for isparta code coverage
12 | const context = require.context('../../app', true, /^^((?!(app|reducers|routes|store)).)*\.js$/);
13 | context.keys().forEach(context);
14 |
--------------------------------------------------------------------------------
/internals/webpack/webpack.base.babel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * COMMON WEBPACK CONFIGURATION
3 | */
4 |
5 | const path = require('path');
6 | const webpack = require('webpack');
7 |
8 | module.exports = (options) => ({
9 | entry: options.entry,
10 | output: Object.assign({ // Compile into js/build.js
11 | path: path.resolve(process.cwd(), 'build'),
12 | publicPath: '/',
13 | }, options.output), // Merge with env dependent settings
14 | module: {
15 | loaders: [{
16 | test: /\.js$/, // Transform all .js files required somewhere with Babel
17 | loader: 'babel',
18 | exclude: /node_modules/,
19 | query: options.babelQuery,
20 | }, {
21 | // Transform our own .css files with PostCSS and CSS-modules
22 | test: /\.css$/,
23 | exclude: /node_modules/,
24 | loader: options.cssLoaders,
25 | }, {
26 | // Do not transform vendor's CSS with CSS-modules
27 | // The point is that they remain in global scope.
28 | // Since we require these CSS files in our JS or CSS files,
29 | // they will be a part of our compilation either way.
30 | // So, no need for ExtractTextPlugin here.
31 | test: /\.css$/,
32 | include: /node_modules/,
33 | loaders: ['style-loader', 'css-loader'],
34 | }, {
35 | test: /\.(eot|svg|ttf|woff|woff2)$/,
36 | loader: 'file-loader',
37 | }, {
38 | test: /\.(jpg|png|gif)$/,
39 | loaders: [
40 | 'file-loader',
41 | 'image-webpack?{progressive:true, optimizationLevel: 7, interlaced: false, pngquant:{quality: "65-90", speed: 4}}',
42 | ],
43 | }, {
44 | test: /\.html$/,
45 | loader: 'html-loader',
46 | }, {
47 | test: /\.json$/,
48 | loader: 'json-loader',
49 | }, {
50 | test: /\.(mp4|webm)$/,
51 | loader: 'url-loader?limit=10000',
52 | }],
53 | },
54 | plugins: options.plugins.concat([
55 | new webpack.ProvidePlugin({
56 | // make fetch available
57 | fetch: 'exports?self.fetch!whatwg-fetch',
58 | }),
59 |
60 | // Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
61 | // inside your code for any environment checks; UglifyJS will automatically
62 | // drop any unreachable code.
63 | new webpack.DefinePlugin({
64 | 'process.env': {
65 | NODE_ENV: JSON.stringify(process.env.NODE_ENV),
66 | },
67 | }),
68 | ]),
69 | postcss: () => options.postcssPlugins,
70 | resolve: {
71 | modules: ['app', 'node_modules'],
72 | extensions: [
73 | '',
74 | '.js',
75 | '.jsx',
76 | '.react.js',
77 | ],
78 | mainFields: [
79 | 'jsnext:main',
80 | 'main',
81 | ],
82 | },
83 | devtool: options.devtool,
84 | target: 'web', // Make web variables accessible to webpack, e.g. window
85 | stats: false, // Don't show stats in the console
86 | progress: true,
87 | });
88 |
--------------------------------------------------------------------------------
/internals/webpack/webpack.dll.babel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WEBPACK DLL GENERATOR
3 | *
4 | * This profile is used to cache webpack's module
5 | * contexts for external library and framework type
6 | * dependencies which will usually not change often enough
7 | * to warrant building them from scratch every time we use
8 | * the webpack process.
9 | */
10 |
11 | const { join } = require('path');
12 | const defaults = require('lodash/defaultsDeep');
13 | const webpack = require('webpack');
14 | const pkg = require(join(process.cwd(), 'package.json'));
15 | const dllPlugin = require('../config').dllPlugin;
16 |
17 | if (!pkg.dllPlugin) { process.exit(0); }
18 |
19 | const dllConfig = defaults(pkg.dllPlugin, dllPlugin.defaults);
20 | const outputPath = join(process.cwd(), dllConfig.path);
21 |
22 | module.exports = {
23 | context: process.cwd(),
24 | entry: dllConfig.dlls ? dllConfig.dlls : dllPlugin.entry(pkg),
25 | devtool: 'eval',
26 | output: {
27 | filename: '[name].dll.js',
28 | path: outputPath,
29 | library: '[name]',
30 | },
31 | plugins: [
32 | new webpack.DllPlugin({ name: '[name]', path: join(outputPath, '[name].json') }), // eslint-disable-line no-new
33 | ],
34 | };
35 |
--------------------------------------------------------------------------------
/internals/webpack/webpack.prod.babel.js:
--------------------------------------------------------------------------------
1 | // Important modules this config uses
2 | const path = require('path');
3 | const webpack = require('webpack');
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
6 | const OfflinePlugin = require('offline-plugin');
7 |
8 | // PostCSS plugins
9 | const cssnext = require('postcss-cssnext');
10 | const postcssFocus = require('postcss-focus');
11 | const postcssReporter = require('postcss-reporter');
12 |
13 | module.exports = require('./webpack.base.babel')({
14 | // In production, we skip all hot-reloading stuff
15 | entry: [
16 | path.join(process.cwd(), 'app/app.js'),
17 | ],
18 |
19 | // Utilize long-term caching by adding content hashes (not compilation hashes) to compiled assets
20 | output: {
21 | filename: '[name].[chunkhash].js',
22 | chunkFilename: '[name].[chunkhash].chunk.js',
23 | },
24 |
25 | // We use ExtractTextPlugin so we get a seperate CSS file instead
26 | // of the CSS being in the JS and injected as a style tag
27 | cssLoaders: ExtractTextPlugin.extract(
28 | 'style-loader',
29 | 'css-loader?modules&-autoprefixer&importLoaders=1!postcss-loader'
30 | ),
31 |
32 | // In production, we minify our CSS with cssnano
33 | postcssPlugins: [
34 | postcssFocus(),
35 | cssnext({
36 | browsers: ['last 2 versions', 'IE > 10'],
37 | }),
38 | postcssReporter({
39 | clearMessages: true,
40 | }),
41 | ],
42 | plugins: [
43 | new webpack.optimize.CommonsChunkPlugin({
44 | name: 'vendor',
45 | children: true,
46 | minChunks: 2,
47 | async: true,
48 | }),
49 |
50 | // OccurrenceOrderPlugin is needed for long-term caching to work properly.
51 | // See http://mxs.is/googmv
52 | new webpack.optimize.OccurrenceOrderPlugin(true),
53 |
54 | // Merge all duplicate modules
55 | new webpack.optimize.DedupePlugin(),
56 |
57 | // Minify and optimize the JavaScript
58 | new webpack.optimize.UglifyJsPlugin({
59 | compress: {
60 | warnings: false, // ...but do not show warnings in the console (there is a lot of them)
61 | },
62 | }),
63 |
64 | // Minify and optimize the index.html
65 | new HtmlWebpackPlugin({
66 | template: 'app/index.html',
67 | minify: {
68 | removeComments: true,
69 | collapseWhitespace: true,
70 | removeRedundantAttributes: true,
71 | useShortDoctype: true,
72 | removeEmptyAttributes: true,
73 | removeStyleLinkTypeAttributes: true,
74 | keepClosingSlash: true,
75 | minifyJS: true,
76 | minifyCSS: true,
77 | minifyURLs: true,
78 | },
79 | inject: true,
80 | }),
81 |
82 | // Extract the CSS into a seperate file
83 | new ExtractTextPlugin('[name].[contenthash].css'),
84 |
85 | // Put it in the end to capture all the HtmlWebpackPlugin's
86 | // assets manipulations and do leak its manipulations to HtmlWebpackPlugin
87 | new OfflinePlugin({
88 | relativePaths: false,
89 | publicPath: '/',
90 |
91 | // No need to cache .htaccess. See http://mxs.is/googmp,
92 | // this is applied before any match in `caches` section
93 | excludes: ['.htaccess'],
94 |
95 | caches: {
96 | main: [':rest:'],
97 |
98 | // All chunks marked as `additional`, loaded after main section
99 | // and do not prevent SW to install. Change to `optional` if
100 | // do not want them to be preloaded at all (cached only when first loaded)
101 | additional: ['*.chunk.js'],
102 | },
103 |
104 | // Removes warning for about `additional` section usage
105 | safeToUseOptionalCaches: true,
106 |
107 | AppCache: false,
108 | }),
109 | ],
110 | });
111 |
--------------------------------------------------------------------------------
/internals/webpack/webpack.test.babel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * TEST WEBPACK CONFIGURATION
3 | */
4 |
5 | const path = require('path');
6 | const webpack = require('webpack');
7 | const modules = [
8 | 'app',
9 | 'node_modules',
10 | ];
11 |
12 | module.exports = {
13 | devtool: 'inline-source-map',
14 | isparta: {
15 | babel: {
16 | presets: ['es2015', 'react', 'stage-0'],
17 | },
18 | },
19 | module: {
20 | // Some libraries don't like being run through babel.
21 | // If they gripe, put them here.
22 | noParse: [
23 | /node_modules(\\|\/)sinon/,
24 | /node_modules(\\|\/)acorn/,
25 | ],
26 | preLoaders: [
27 | { test: /\.js$/,
28 | loader: 'isparta',
29 | include: path.resolve('app/'),
30 | },
31 | ],
32 | loaders: [
33 | { test: /\.json$/, loader: 'json-loader' },
34 | { test: /\.css$/, loader: 'null-loader' },
35 |
36 | // sinon.js--aliased for enzyme--expects/requires global vars.
37 | // imports-loader allows for global vars to be injected into the module.
38 | // See https://github.com/webpack/webpack/issues/304
39 | { test: /sinon(\\|\/)pkg(\\|\/)sinon\.js/,
40 | loader: 'imports?define=>false,require=>false',
41 | },
42 | { test: /\.js$/,
43 | loader: 'babel',
44 | exclude: [/node_modules/],
45 | },
46 | { test: /\.jpe?g$|\.gif$|\.png$/i,
47 | loader: 'null-loader',
48 | },
49 | ],
50 | },
51 |
52 | plugins: [
53 |
54 | // Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
55 | // inside your code for any environment checks; UglifyJS will automatically
56 | // drop any unreachable code.
57 | new webpack.DefinePlugin({
58 | 'process.env': {
59 | NODE_ENV: JSON.stringify(process.env.NODE_ENV),
60 | },
61 | })],
62 |
63 | // Some node_modules pull in Node-specific dependencies.
64 | // Since we're running in a browser we have to stub them out. See:
65 | // https://webpack.github.io/docs/configuration.html#node
66 | // https://github.com/webpack/node-libs-browser/tree/master/mock
67 | // https://github.com/webpack/jade-loader/issues/8#issuecomment-55568520
68 | node: {
69 | fs: 'empty',
70 | child_process: 'empty',
71 | net: 'empty',
72 | tls: 'empty',
73 | },
74 |
75 | // required for enzyme to work properly
76 | externals: {
77 | jsdom: 'window',
78 | 'react/addons': true,
79 | 'react/lib/ExecutionEnvironment': true,
80 | 'react/lib/ReactContext': 'window',
81 | },
82 | resolve: {
83 | modulesDirectories: modules,
84 | modules,
85 | alias: {
86 | // required for enzyme to work properly
87 | sinon: 'sinon/pkg/sinon',
88 | },
89 | },
90 | };
91 |
--------------------------------------------------------------------------------
/media/messages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litdevelopers/tinder/6eb771d682ef7f5ea537105556f3733ee1019502/media/messages.png
--------------------------------------------------------------------------------
/media/profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litdevelopers/tinder/6eb771d682ef7f5ea537105556f3733ee1019502/media/profile.png
--------------------------------------------------------------------------------
/media/recommendations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litdevelopers/tinder/6eb771d682ef7f5ea537105556f3733ee1019502/media/recommendations.png
--------------------------------------------------------------------------------
/server/api/facebookAuth.js:
--------------------------------------------------------------------------------
1 | const request = require('superagent');
2 |
3 | function getFBToken(data) {
4 | return new Promise((resolve, reject) => {
5 | const tokenPattern = /access_token=(.*)&/;
6 | const token = data.match(tokenPattern)[1];
7 | if (token) {
8 | resolve(token);
9 | } else {
10 | reject('Token error');
11 | }
12 | // Keep code in case we want to start storing statically
13 | // const expirationPattern = /expires_in=(.*)/;
14 | // const expiration = parseInt(result.match(expirationPattern)[1]);
15 | // const now = Date.now();
16 | // const expiryTime = new Date(now + (1000 * expiration));
17 | });
18 | }
19 |
20 | // Keep code in case in future we need fbid again
21 | function getFBUserId(token) {
22 | return new Promise((resolve, reject) => {
23 | const graphUrl = `https://graph.facebook.com/me?access_token=${token}`;
24 | request.get(graphUrl)
25 | .end((err, res) => {
26 | if (err || !res.ok) {
27 | reject('Superagent error');
28 | } else {
29 | resolve(JSON.parse(res.text).id);
30 | }
31 | });
32 | });
33 | }
34 |
35 | function returnFacebook(data) {
36 | const token = getFBToken(data);
37 | const fbid = token.then((fbtoken) => getFBUserId(fbtoken));
38 | return Promise.all([token, fbid]);
39 | }
40 | module.exports = {
41 | getFBToken,
42 | getFBUserId,
43 | returnFacebook,
44 | };
45 |
--------------------------------------------------------------------------------
/server/api/shelvedApi.txt:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | NEW AUTH FLOW BECAUSE I BROKE THE OLD ONE:
4 | 1) Go to Facebook.com.
5 | 2) If I can find an element for my profile picture, I know im in the right place.
6 | 2 - 1) If I can, I will go to the FACEBOOK_AUTH_URL
7 | 2 - 2) Done
8 | 3) If I cannot find my profile picture, I will go to facebook.com and login
9 | 3 - 1) I will then retrieve my profile id
10 | 3 - 2) I will THEN go on the website for oauth
11 |
12 |
13 |
14 | */
15 | const FACEBOOK_AUTH_URL = 'https://www.facebook.com/dialog/oauth?client_id=464891386855067&redirect_uri=https://www.facebook.com/connect/login_success.html&scope=basic_info,email,public_profile,user_about_me,user_activities,user_birthday,user_education_history,user_friends,user_interests,user_likes,user_location,user_photos,user_relationship_details&response_type=token';
16 | const NEW_FACEBOOK = 'https://www.facebook.com/v2.6/dialog/oauth?redirect_uri=fb464891386855067%3A%2F%2Fauthorize%2F&state=%7B%22challenge%22%3A%22q1WMwhvSfbWHvd8xz5PT6lk6eoA%253D%22%2C%220_auth_logger_id%22%3A%2254783C22-558A-4E54-A1EE-BB9E357CC11F%22%2C%22com.facebook.sdk_client_state%22%3Atrue%2C%223_method%22%3A%22sfvc_auth%22%7D&scope=user_birthday%2Cuser_photos%2Cuser_education_history%2Cemail%2Cuser_relationship_details%2Cuser_friends%2Cuser_work_history%2Cuser_likes&response_type=token%2Csigned_request&default_audience=friends&return_scopes=true&auth_type=rerequest&client_id=464891386855067&ret=login&sdk=ios&logger_id=54783C22-558A-4E54-A1EE-BB9E357CC11F#_=_';
17 | const FACEBOOK_URL = 'https://facebook.com';
18 |
19 |
20 | const Phantasma = require('phantasma'); // eslint-disable-line global-require
21 | const ph = new Phantasma({ webSecurity: false });
22 | ph
23 | .open(NEW_FACEBOOK)
24 | .type('input[name="email"]', req.body.login)
25 | .type('input[name="pass"]', req.body.password)
26 | .click('button[id="loginbutton"]')
27 | .wait()
28 | .click('button[name="__CONFIRM__"]')
29 | .finally(() => {
30 | console.log('Done');
31 | })
32 | .on('onResourceReceived', (data) => {
33 | console.log(data);
34 | });
35 |
36 | // const n = nightmare({ show: true, typeInterval: 20, webPreferences: { partition: 'noflexzone' } });
37 | // n
38 | // .on('onResourceReceived', (event) => {
39 | // console.log(event);
40 | // })
41 | // .goto(NEW_FACEBOOK)
42 | // .type('input[name="email"]', req.body.login)
43 | // .type('input[name="pass"]', req.body.password)
44 | // .click('button[id="loginbutton"]')
45 | // .wait('button[name="__CONFIRM__"]')
46 | // .click('button[name="__CONFIRM__"]')
47 | // .then((profileId) => {
48 | // // n
49 | // // .goto(FACEBOOK_AUTH_URL)
50 | // // .click('button[tabindex="0"]')
51 | // // .wait(200)
52 | // // .url()
53 | // // .then((profileURL) => {
54 | // // res.status(200).json({
55 | // // token: profileURL.split(/access_token=|&expires_in/)[1],
56 | // // id: profileId,
57 | // // });
58 | // // });
59 | // });
60 |
61 | // n
62 | // .goto(FACEBOOK_AUTH_URL)
63 | // .exists('form[id="login_form"]')
64 | // .then((formExists) => {
65 | // if (!formExists) {
66 | // n
67 | // .url()
68 | // .then((noLoginUrl) => {
69 | // n
70 | // .goto(FACEBOOK_URL)
71 | // .evaluate(() => document.querySelector('a[title="Profile"] img').id.split('_').reverse()[0])
72 | // .end()
73 | // .then((profileId) => {
74 | // res.status(200).json({
75 | // token: noLoginUrl.split(/access_token=|&expires_in/)[1],
76 | // id: profileId,
77 | // });
78 | // });
79 | // });
80 | // } else {
81 | // n
82 | // .type('input[name="email"]', req.body.login)
83 | // .type('input[name="pass"]', req.body.password)
84 | // .click('button[id="loginbutton"]')
85 | // .wait(200)
86 | // .url()
87 | // .then((loginURL) => {
88 | // n
89 | // .goto(FACEBOOK_URL)
90 | // .type('input[name="email"]', req.body.login)
91 | // .type('input[name="pass"]', req.body.password)
92 | // .click('input[type="submit"]')
93 | // .evaluate(() => document.querySelector('a[title="Profile"] img').id.split('_').reverse()[0])
94 | // .end()
95 | // .then((profileId) => {
96 | // res.status(200).json({
97 | // token: loginURL.split(/access_token=|&expires_in/)[1],
98 | // id: profileId,
99 | // });
100 | // });
101 | // });
102 | // }
103 | // });
104 |
105 |
106 |
107 |
108 | locale=en_US; datr=IKNqV4kYw97DR_kJW19lVZwr; pl=y; lu=gjnJdOlL5gaFCMSUxSN30F0A; p=-2; act=1470853084631%2F11; c_user=1350111674; xs=211%3AxrSoDHyBQsH0qg%3A2%3A1470849618%3A1182; fr=0orQBCHDfsnX7AKsj.AWWJJFJxJJHkNL0_gQrA1jUy7oM.BXaqMg.gb.AAA.1.0.BXq3CT.AWXdsrci; csm=2; s=Aa5ZG1nJAyw-GXz8.BXq2JS; sb=IqNqVxlTCu2S8h-L1PBlP1q1; presence=EDvF3EtimeF1470854328EuserFA21350111674A2EstateFDt2F_5bDiFA2user_3a1042457287A2ErF1C_5dElm2FA2user_3a1042457287A2Euct2F1470852922411EtrFnullEtwF2197376950EatF1470854311724G470854328608CEchFDp_5f1350111674F88CC
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | /* eslint consistent-return:0 */
2 |
3 | const express = require('express');
4 | const logger = require('./logger');
5 |
6 | const argv = require('minimist')(process.argv.slice(2));
7 | const setup = require('./middlewares/frontendMiddleware');
8 | const isDev = process.env.NODE_ENV !== 'production';
9 | const ngrok = (isDev && process.env.ENABLE_TUNNEL) || argv.tunnel ? require('ngrok') : false;
10 | const resolve = require('path').resolve;
11 | const app = express();
12 | const bodyParser = require('body-parser');
13 | const api = require('./api/api.js');
14 |
15 | // json middleware
16 | app.use(bodyParser.urlencoded({ extended: false }));
17 | app.use(bodyParser.json());
18 |
19 |
20 | // If you need a backend, e.g. an API, add your custom backend-specific middleware here
21 | app.use('/api', api);
22 |
23 | // In production we need to pass these values in instead of relying on webpack
24 | setup(app, {
25 | outputPath: resolve(process.cwd(), 'build'),
26 | publicPath: '/',
27 | });
28 |
29 | // get the intended port number, use port 3000 if not provided
30 | const port = argv.port || process.env.PORT || 3000;
31 |
32 | // Start your app.
33 | app.listen(port, (err) => {
34 | if (err) {
35 | return logger.error(err.message);
36 | }
37 |
38 | // Connect to ngrok in dev mode
39 | if (ngrok) {
40 | ngrok.connect(port, (innerErr, url) => {
41 | if (innerErr) {
42 | return logger.error(innerErr);
43 | }
44 |
45 | logger.appStarted(port, url);
46 | });
47 | } else {
48 | logger.appStarted(port);
49 | }
50 | });
51 |
--------------------------------------------------------------------------------
/server/logger.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | const chalk = require('chalk');
4 | const ip = require('ip');
5 |
6 | const divider = chalk.gray('\n-----------------------------------');
7 |
8 | /**
9 | * Logger middleware, you can customize it to make messages more personal
10 | */
11 | const logger = {
12 |
13 | // Called whenever there's an error on the server we want to print
14 | error: err => {
15 | console.error(chalk.red(err));
16 | },
17 |
18 | // Called when express.js app starts on given port w/o errors
19 | appStarted: (port, tunnelStarted) => {
20 | console.log(`Server started ${chalk.green('✓')}`);
21 |
22 | // If the tunnel started, log that and the URL it's available at
23 | if (tunnelStarted) {
24 | console.log(`Tunnel initialised ${chalk.green('✓')}`);
25 | }
26 |
27 | console.log(`
28 | ${chalk.bold('Access URLs:')}${divider}
29 | Localhost: ${chalk.magenta(`http://localhost:${port}`)}
30 | LAN: ${chalk.magenta(`http://${ip.address()}:${port}`) +
31 | (tunnelStarted ? `\n Proxy: ${chalk.magenta(tunnelStarted)}` : '')}${divider}
32 | ${chalk.blue(`Press ${chalk.italic('CTRL-C')} to stop`)}
33 | `);
34 | },
35 | };
36 |
37 | module.exports = logger;
38 |
--------------------------------------------------------------------------------
/server/middlewares/frontendMiddleware.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable global-require */
2 | const express = require('express');
3 | const path = require('path');
4 | const compression = require('compression');
5 | const pkg = require(path.resolve(process.cwd(), 'package.json'));
6 |
7 | // Dev middleware
8 | const addDevMiddlewares = (app, webpackConfig) => {
9 | const webpack = require('webpack');
10 | const webpackDevMiddleware = require('webpack-dev-middleware');
11 | const webpackHotMiddleware = require('webpack-hot-middleware');
12 | const compiler = webpack(webpackConfig);
13 | const middleware = webpackDevMiddleware(compiler, {
14 | noInfo: true,
15 | publicPath: webpackConfig.output.publicPath,
16 | silent: true,
17 | stats: 'errors-only',
18 | });
19 |
20 | app.use(middleware);
21 | app.use(webpackHotMiddleware(compiler));
22 |
23 | // Since webpackDevMiddleware uses memory-fs internally to store build
24 | // artifacts, we use it instead
25 | const fs = middleware.fileSystem;
26 |
27 | if (pkg.dllPlugin) {
28 | app.get(/\.dll\.js$/, (req, res) => {
29 | const filename = req.path.replace(/^\//, '');
30 | res.sendFile(path.join(process.cwd(), pkg.dllPlugin.path, filename));
31 | });
32 | }
33 |
34 | app.get('*', (req, res) => {
35 | fs.readFile(path.join(compiler.outputPath, 'index.html'), (err, file) => {
36 | if (err) {
37 | res.sendStatus(404);
38 | } else {
39 | res.send(file.toString());
40 | }
41 | });
42 | });
43 | };
44 |
45 | // Production middlewares
46 | const addProdMiddlewares = (app, options) => {
47 | const publicPath = options.publicPath || '/';
48 | const outputPath = options.outputPath || path.resolve(process.cwd(), 'build');
49 |
50 | // compression middleware compresses your server responses which makes them
51 | // smaller (applies also to assets). You can read more about that technique
52 | // and other good practices on official Express.js docs http://mxs.is/googmy
53 | app.use(compression());
54 | app.use(publicPath, express.static(outputPath));
55 |
56 | app.get('*', (req, res) => res.sendFile(path.resolve(outputPath, 'index.html')));
57 | };
58 |
59 | /**
60 | * Front-end middleware
61 | */
62 | module.exports = (app, options) => {
63 | const isProd = process.env.NODE_ENV === 'production';
64 |
65 | if (isProd) {
66 | addProdMiddlewares(app, options);
67 | } else {
68 | const webpackConfig = require('../../internals/webpack/webpack.dev.babel');
69 | addDevMiddlewares(app, webpackConfig);
70 | }
71 |
72 | return app;
73 | };
74 |
--------------------------------------------------------------------------------