mirror of
https://github.com/elyby/emails-renderer.git
synced 2024-12-22 05:09:44 +05:30
Upgrade project structure to webpack 4.
Remove unused dependencies. Rename all .jsx files into .js.
This commit is contained in:
parent
1f102b71b0
commit
cb84df8f96
9
.babelrc
9
.babelrc
@ -1,9 +0,0 @@
|
||||
{
|
||||
"presets": ["react", "es2015", "stage-0"],
|
||||
"plugins": ["transform-runtime", ["react-intl", {"messagesDir": "./dist/messages/"}]],
|
||||
"env": {
|
||||
"development": {
|
||||
"presets": ["react-hmre"]
|
||||
}
|
||||
}
|
||||
}
|
53
package.json
53
package.json
@ -9,52 +9,43 @@
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "SleepWalker <dev@udf.su>",
|
||||
"license": "private",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://gitlab.com/elyby/email-renderer/issues"
|
||||
},
|
||||
"homepage": "https://gitlab.com/elyby/email-renderer#README",
|
||||
"scripts": {
|
||||
"start": "rm -rf dist/ && webpack-dev-server --progress --colors",
|
||||
"start": "webpack-dev-server --mode=development --progress --colors",
|
||||
"lint": "eslint ./src",
|
||||
"i18n": "cd ./scripts && ../node_modules/.bin/babel-node i18n-collect.js",
|
||||
"build": "rm -rf dist/ && NODE_ENV=production webpack --progress --colors",
|
||||
"i18n:collect": "node --experimental-modules ./scripts/i18n-collect.mjs",
|
||||
"build": "rm -rf dist/ && webpack --mode=production --progress --colors",
|
||||
"i18n:pull": "cd ./scripts && ../node_modules/.bin/babel-node --presets es2015,stage-0 i18n-onesky.js pull"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.3.14",
|
||||
"classnames": "^2.1.3",
|
||||
"history": "^2.0.0",
|
||||
"intl": "^1.2.2",
|
||||
"intl-format-cache": "^2.0.4",
|
||||
"intl-messageformat": "^1.1.0",
|
||||
"react": "^15.0.0",
|
||||
"react-dom": "^15.0.0",
|
||||
"react": "^16.8.4",
|
||||
"react-dom": "^16.8.4",
|
||||
"react-intl": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.18.0",
|
||||
"babel-core": "^6.0.0",
|
||||
"@babel/core": "^7.3.4",
|
||||
"@babel/plugin-proposal-class-properties": "^7.3.4",
|
||||
"@babel/plugin-proposal-export-default-from": "^7.2.0",
|
||||
"@babel/preset-env": "^7.3.4",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@eoleo/image-size-loader": "^1.0.0",
|
||||
"babel-eslint": "^6.0.0",
|
||||
"babel-loader": "^6.0.0",
|
||||
"babel-plugin-react-intl": "^2.0.0",
|
||||
"babel-plugin-transform-runtime": "^6.3.13",
|
||||
"babel-preset-es2015": "^6.3.13",
|
||||
"babel-preset-react": "^6.3.13",
|
||||
"babel-preset-react-hmre": "^1.0.1",
|
||||
"babel-preset-stage-0": "^6.3.13",
|
||||
"babel-runtime": "^6.0.0",
|
||||
"circular-dependency-plugin": "^2.0.0",
|
||||
"babel-loader": "^8.0.5",
|
||||
"babel-plugin-react-intl": "^3.0.1",
|
||||
"babel-preset-react-hot": "^1.0.5",
|
||||
"eslint": "^3.1.1",
|
||||
"eslint-plugin-react": "^6.0.0",
|
||||
"extract-text-webpack-plugin": "^1.0.0",
|
||||
"file-loader": "^0.9.0",
|
||||
"html-webpack-plugin": "^2.0.0",
|
||||
"image-size-loader": "^0.7.0",
|
||||
"json-loader": "^0.5.4",
|
||||
"file-loader": "^3.0.1",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"intl-json-loader": "file:./webpack-utils/intl-json-loader",
|
||||
"prop-types": "^15.7.2",
|
||||
"scripts": "file:scripts",
|
||||
"webpack": "^1.12.9",
|
||||
"webpack-dev-server": "^1.14.0",
|
||||
"webpack-utils": "file:webpack-utils"
|
||||
"webpack": "^4.29.6",
|
||||
"webpack-cli": "^3.3.0",
|
||||
"webpack-dev-server": "^3.2.1"
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,19 @@
|
||||
/* eslint-env node */
|
||||
/* eslint-disable no-console */
|
||||
import fs from 'fs';
|
||||
import {sync as globSync} from 'glob';
|
||||
import {sync as mkdirpSync} from 'mkdirp';
|
||||
import path from 'path';
|
||||
import glob from 'glob';
|
||||
import mkdirp from 'mkdirp';
|
||||
import chalk from 'chalk';
|
||||
import prompt from 'prompt';
|
||||
|
||||
const MESSAGES_PATTERN = '../dist/messages/**/*.json';
|
||||
const LANG_DIR = '../src/i18n';
|
||||
// https://stackoverflow.com/a/50052194/5184751
|
||||
const __dirname = path.dirname(new URL(import.meta.url).pathname); // eslint-disable-line
|
||||
|
||||
const MESSAGES_PATTERN = path.resolve(__dirname, '../dist/messages/**/*.json');
|
||||
const LANG_DIR = path.resolve(__dirname, '../src/i18n');
|
||||
const DEFAULT_LOCALE = 'en';
|
||||
const SUPPORTED_LANGS = [DEFAULT_LOCALE].concat('ru', 'be', 'uk');
|
||||
const SUPPORTED_LANGS = Object.keys(JSON.parse(fs.readFileSync(path.join(LANG_DIR, 'index.json'))));
|
||||
|
||||
/**
|
||||
* Aggregates the default messages that were extracted from the app's
|
||||
@ -18,7 +23,7 @@ const SUPPORTED_LANGS = [DEFAULT_LOCALE].concat('ru', 'be', 'uk');
|
||||
*/
|
||||
let idToFileMap = {};
|
||||
let duplicateIds = [];
|
||||
const collectedMessages = globSync(MESSAGES_PATTERN)
|
||||
const collectedMessages = glob.sync(MESSAGES_PATTERN)
|
||||
.map((filename) => [filename, JSON.parse(fs.readFileSync(filename, 'utf8'))])
|
||||
.reduce((collection, [file, descriptors]) => {
|
||||
descriptors.forEach(({id, defaultMessage}) => {
|
||||
@ -37,7 +42,8 @@ if (duplicateIds.length) {
|
||||
console.log('\nFound duplicated ids:');
|
||||
duplicateIds.forEach((id) => console.log(`${chalk.yellow(id)}:\n - ${idToFileMap[id].join('\n - ')}\n`));
|
||||
console.log(chalk.red('Please correct the errors above to proceed further!'));
|
||||
return;
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
duplicateIds = null;
|
||||
@ -83,7 +89,8 @@ keysToUpdate = Object.entries(prevMessages).reduce((acc, [key, message]) =>
|
||||
});
|
||||
|
||||
if (!keysToAdd.length && !keysToRemove.length && !keysToUpdate.length && !keysToRename.length) {
|
||||
return console.log(chalk.green('Everything is up to date!'));
|
||||
console.log(chalk.green('Everything is up to date!'));
|
||||
process.exit();
|
||||
}
|
||||
|
||||
console.log(chalk.magenta(`The diff relative to default locale (${DEFAULT_LOCALE}) is:`));
|
||||
@ -135,7 +142,7 @@ prompt.get({
|
||||
|
||||
|
||||
function buildLocales() {
|
||||
mkdirpSync(LANG_DIR);
|
||||
mkdirp.sync(LANG_DIR);
|
||||
|
||||
SUPPORTED_LANGS.map((lang) => {
|
||||
const destPath = `${LANG_DIR}/${lang}.json`;
|
@ -1,150 +0,0 @@
|
||||
/* eslint-env node */
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import onesky from 'onesky-utils';
|
||||
import fs from 'fs';
|
||||
import ch from 'chalk';
|
||||
|
||||
const LANG_DIR = `${__dirname}/../src/i18n`;
|
||||
const SOURCE_LANG = 'en'; // Базовый язык, относительно которого будут формироваться все остальные переводы
|
||||
const SOURCE_FILE_NAME = 'i18n.json'; // Название файла с исходными строками внутри OneSky
|
||||
const INDEX_FILE_NAME = 'index.json'; // Название файла с информацией о переводах
|
||||
const MIN_RELEASE_PROGRESS = 80; // Какой процент локали перевода должен быть выполнен, чтобы локаль была опубликована
|
||||
|
||||
/**
|
||||
* Массив локалей для соответствия каноничному виду в OneSky и нашему представлению
|
||||
* о том, каким должны быть имена локалей
|
||||
*/
|
||||
const LOCALES_MAP = {
|
||||
ru: 'ru-RU',
|
||||
en: 'en-GB',
|
||||
sl: 'sl-SI',
|
||||
fr: 'fr-FR',
|
||||
el: 'el-GR',
|
||||
de: 'de-DE',
|
||||
sr: 'sr-RS',
|
||||
lt: 'lt-LT',
|
||||
};
|
||||
|
||||
// https://ely-translates.oneskyapp.com/admin/site/settings
|
||||
const defaultOptions = {
|
||||
apiKey: '5MaW9TYp0S3qdJgkZ5QLgEIDeabkFDzB',
|
||||
secret: 'qd075hUNpop4DItD6KOXKQnbqWPLZilf',
|
||||
projectId: 202784,
|
||||
};
|
||||
|
||||
/**
|
||||
* Переводит из кода языка в OneSky в наше представление
|
||||
*
|
||||
* @param {string} code
|
||||
* @return {string}
|
||||
*/
|
||||
function code2locale(code) {
|
||||
for (const locale in LOCALES_MAP) {
|
||||
if (code === LOCALES_MAP[locale]) {
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Переводит из нашего формата локалей в ожидаемое значение OneSky
|
||||
*
|
||||
* @param {string} locale
|
||||
* @return {string}
|
||||
*/
|
||||
function locale2code(locale) {
|
||||
return LOCALES_MAP[locale] || locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Форматирует входящий объект с переводами в итоговую строку в том формате, в каком они
|
||||
* хранятся в самом приложении
|
||||
*
|
||||
* @param {object} translates
|
||||
* @return {string}
|
||||
*/
|
||||
function formatTranslates(translates) {
|
||||
return JSON.stringify(sortByKeys(translates), null, 4) + '\n'; // eslint-disable-line prefer-template
|
||||
}
|
||||
|
||||
/**
|
||||
* http://stackoverflow.com/a/29622653/5184751
|
||||
*
|
||||
* @param {object} object
|
||||
* @return {object}
|
||||
*/
|
||||
function sortByKeys(object) {
|
||||
return Object.keys(object).sort().reduce((result, key) => {
|
||||
result[key] = object[key];
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
async function pullReadyLanguages() {
|
||||
const languages = JSON.parse(await onesky.getLanguages({...defaultOptions}));
|
||||
return languages.data
|
||||
.filter((elem) => elem.is_ready_to_publish || parseFloat(elem.translation_progress) > MIN_RELEASE_PROGRESS);
|
||||
}
|
||||
|
||||
async function pullTranslate(language) {
|
||||
const rawResponse = await onesky.getFile({...defaultOptions, language, fileName: SOURCE_FILE_NAME});
|
||||
const response = JSON.parse(rawResponse);
|
||||
fs.writeFileSync(`${LANG_DIR}/${code2locale(language)}.json`, formatTranslates(response));
|
||||
}
|
||||
|
||||
async function pull() {
|
||||
console.log('Pulling locales list...');
|
||||
const langs = await pullReadyLanguages();
|
||||
const langsList = langs.map((elem) => elem.custom_locale || elem.code);
|
||||
|
||||
console.log(ch.green('Pulled locales: ') + langsList.map((lang) => code2locale(lang)).join(', '));
|
||||
|
||||
console.log('Pulling translates...');
|
||||
await Promise.all(langsList.map(async (lang) => {
|
||||
await pullTranslate(lang);
|
||||
console.log(ch.green('Locale ') + ch.white.bold(code2locale(lang)) + ch.green(' successfully pulled'));
|
||||
}));
|
||||
|
||||
console.log('Writing an index file...');
|
||||
const mapFileContent = {};
|
||||
langs.map((elem) => {
|
||||
mapFileContent[elem.locale] = {
|
||||
name: elem.local_name.match(/^([^\(]+)/)[0].trim(), // Обрезаем значения в скобках
|
||||
progress: parseFloat(elem.translation_progress),
|
||||
};
|
||||
});
|
||||
fs.writeFileSync(`${LANG_DIR}/${INDEX_FILE_NAME}`, formatTranslates(mapFileContent));
|
||||
console.log(ch.green('The index file was successfully written'));
|
||||
}
|
||||
|
||||
async function publish() {
|
||||
console.log(`Publishing ${ch.bold(SOURCE_LANG)} translates file...`);
|
||||
await onesky.postFile({
|
||||
...defaultOptions,
|
||||
format: 'HIERARCHICAL_JSON',
|
||||
content: fs.readFileSync(`${LANG_DIR}/${SOURCE_LANG}.json`, 'utf8'),
|
||||
keepStrings: false,
|
||||
language: locale2code(SOURCE_LANG),
|
||||
fileName: SOURCE_FILE_NAME,
|
||||
});
|
||||
console.log(ch.green('Success'));
|
||||
}
|
||||
|
||||
try {
|
||||
const action = process.argv[2];
|
||||
switch (action) {
|
||||
case 'pull':
|
||||
pull();
|
||||
break;
|
||||
case 'publish':
|
||||
publish();
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown action ${action}`);
|
||||
}
|
||||
} catch (exception) {
|
||||
console.error(exception);
|
||||
}
|
@ -11,7 +11,8 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"chalk": "^1.1.3",
|
||||
"node-babel": "^0.1.2",
|
||||
"glob": "^7.1.3",
|
||||
"mkdirp": "^0.5.1",
|
||||
"prompt": "^1.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { PropTypes } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { IntlProvider, addLocaleData } from 'react-intl';
|
||||
|
||||
@ -32,12 +33,12 @@ addLocaleData(ptLocaleData);
|
||||
addLocaleData(ukLocaleData);
|
||||
addLocaleData(viLocaleData);
|
||||
|
||||
import { SUPPORTED_LANGUAGES, DEFAULT_LANGUAGE } from './constants';
|
||||
import { SUPPORTED_LANGUAGES, DEFAULT_LANGUAGE } from './params';
|
||||
|
||||
import BaseLayout from 'components/BaseLayout';
|
||||
|
||||
export default function App({type, payload = {}}) {
|
||||
let {locale} = payload;
|
||||
let { locale } = payload;
|
||||
|
||||
if (!locale || SUPPORTED_LANGUAGES.indexOf(locale) === -1) {
|
||||
locale = DEFAULT_LANGUAGE;
|
||||
@ -58,6 +59,6 @@ export default function App({type, payload = {}}) {
|
||||
App.propTypes = {
|
||||
type: PropTypes.string.isRequired,
|
||||
payload: PropTypes.shape({
|
||||
locale: PropTypes.string
|
||||
})
|
||||
locale: PropTypes.string,
|
||||
}),
|
||||
};
|
@ -1,3 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
import { Table } from 'components/table';
|
@ -1,3 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function Html(props) {
|
||||
return (
|
||||
<html>
|
@ -1,4 +1,5 @@
|
||||
import { PropTypes } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
|
||||
@ -6,7 +7,6 @@ import { colors, green } from 'components/ui/colors';
|
||||
import { Button, Input } from 'components/ui';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
import messages from './messages.intl.json';
|
||||
|
||||
export default function Code({code, link, label, color = green}) {
|
@ -1,3 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
export default function Content(props) {
|
@ -1,10 +1,10 @@
|
||||
import { Table } from 'components/table';
|
||||
import React from 'react';
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
|
||||
import { Table } from 'components/table';
|
||||
import { BitmapText } from 'components/text';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
import messages from './messages.intl.json';
|
||||
|
||||
export default function Footer() {
|
@ -1,9 +1,10 @@
|
||||
import { Table } from 'components/table';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
import { PropTypes } from 'react';
|
||||
|
||||
import { Table } from 'components/table';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
import messages from './messages.intl.json';
|
||||
|
||||
export default function Userbar({username, title}) {
|
@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Table } from 'components/table';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
import logoImage from './logo.png';
|
||||
|
||||
export default function Userbar() {
|
@ -1,3 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
export default function Table(props) {
|
@ -1,5 +1,5 @@
|
||||
import { PropTypes } from 'react';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
|
||||
export function BitmapText(props) {
|
||||
@ -19,18 +19,18 @@ export function BitmapText(props) {
|
||||
src = require(`emails/${componentPath}/images/${props.intl.locale}/${fileName}.png`);
|
||||
// TODO: we can improve this loader in future by adding an option to disable file emitting
|
||||
// because this thing is handled by url-loader
|
||||
size = require(`image-size!emails/${componentPath}/images/${props.intl.locale}/${fileName}.png`);
|
||||
size = require(`image-size-loader!emails/${componentPath}/images/${props.intl.locale}/${fileName}.png`);
|
||||
} catch (err) { // fallback to default locale
|
||||
src = require(`emails/${componentPath}/images/${props.intl.defaultLocale}/${fileName}.png`);
|
||||
size = require(`image-size!emails/${componentPath}/images/${props.intl.defaultLocale}/${fileName}.png`);
|
||||
size = require(`image-size-loader!emails/${componentPath}/images/${props.intl.defaultLocale}/${fileName}.png`);
|
||||
}
|
||||
} catch (err) { // try components
|
||||
try {
|
||||
src = require(`components/${componentPath}/images/${props.intl.locale}/${fileName}.png`);
|
||||
size = require(`image-size!components/${componentPath}/images/${props.intl.locale}/${fileName}.png`);
|
||||
size = require(`image-size-loader!components/${componentPath}/images/${props.intl.locale}/${fileName}.png`);
|
||||
} catch (err) { // fallback to default locale
|
||||
src = require(`components/${componentPath}/images/${props.intl.defaultLocale}/${fileName}.png`);
|
||||
size = require(`image-size!components/${componentPath}/images/${props.intl.defaultLocale}/${fileName}.png`);
|
||||
size = require(`image-size-loader!components/${componentPath}/images/${props.intl.defaultLocale}/${fileName}.png`);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Component, PropTypes } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './styles';
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { Component, PropTypes } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { colors, green } from 'components/ui/colors';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
export default class Input extends Component {
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
color: PropTypes.oneOf(Object.values(colors))
|
||||
color: PropTypes.oneOf(Object.values(colors)),
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
color: green
|
||||
color: green,
|
||||
};
|
||||
|
||||
render() {
|
||||
@ -29,5 +29,4 @@ export default class Input extends Component {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -1,19 +1,19 @@
|
||||
import { Component } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import App from 'App';
|
||||
|
||||
import List from './List';
|
||||
|
||||
import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from 'constants';
|
||||
import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from 'params';
|
||||
|
||||
const EVAILABLE_EMAILS = require.context('emails', true, /index\.js$/).keys().map((path) => path.split('/')[1]);
|
||||
const AVAILABLE_EMAILS = require.context('emails', true, /index\.js$/).keys().map((path) => path.split('/')[1]);
|
||||
|
||||
export default class DevApp extends Component {
|
||||
state = {
|
||||
locale: DEFAULT_LANGUAGE,
|
||||
type: EVAILABLE_EMAILS[0],
|
||||
type: AVAILABLE_EMAILS[0],
|
||||
fixture: 'default',
|
||||
isMinimized: false
|
||||
isMinimized: false,
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
@ -63,7 +63,7 @@ export default class DevApp extends Component {
|
||||
/>
|
||||
|
||||
<List label="Email"
|
||||
items={EVAILABLE_EMAILS}
|
||||
items={AVAILABLE_EMAILS}
|
||||
active={type}
|
||||
onChange={this.onTypeChange}
|
||||
/>
|
@ -1,4 +1,5 @@
|
||||
import { PropTypes } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function List({label, items, active, onChange}) {
|
||||
return (
|
@ -1,5 +1,5 @@
|
||||
import { PropTypes } from 'react';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
|
||||
import { Userbar, Header, Content, Footer } from 'components/layout';
|
@ -1,5 +1,5 @@
|
||||
import { PropTypes } from 'react';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
|
||||
import { Userbar, Header, Content, Footer } from 'components/layout';
|
||||
@ -128,5 +128,5 @@ export default function Register({username, link, code}) {
|
||||
Register.propTypes = {
|
||||
username: PropTypes.string,
|
||||
link: PropTypes.string,
|
||||
code: PropTypes.string
|
||||
code: PropTypes.string,
|
||||
};
|
@ -1,11 +1,9 @@
|
||||
import 'babel-polyfill';
|
||||
|
||||
import { Html } from 'components';
|
||||
|
||||
// NOTE: we are requiring with require(), to enable dynamic dependencies
|
||||
// depending on ENV, where App is running in.
|
||||
// This allows us better support of hmr and reduces bundle size
|
||||
|
||||
const React = require('react');
|
||||
|
||||
/* global process: false */
|
||||
// eslint-disable-next-line no-negated-condition
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
@ -18,11 +16,13 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
);
|
||||
} else {
|
||||
const ReactDOMServer = require('react-dom/server');
|
||||
const { Html } = require('components');
|
||||
const App = require('App').default;
|
||||
|
||||
module.exports = {
|
||||
default(props) {
|
||||
if (props.assetsHost) {
|
||||
// noinspection JSUnresolvedVariable
|
||||
__webpack_public_path__ = props.assetsHost.replace(/\/*$/, '/'); // eslint-disable-line
|
||||
|
||||
Reflect.deleteProperty(props, 'assetsHost');
|
||||
|
25
webpack-utils/intl-json-loader/index.js
Normal file
25
webpack-utils/intl-json-loader/index.js
Normal file
@ -0,0 +1,25 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function(input) {
|
||||
this.cacheable && this.cacheable();
|
||||
|
||||
const moduleId = this.context
|
||||
.replace(path.join(this.rootContext, 'src'), '')
|
||||
.replace(/^\/|\/$/g, '')
|
||||
.replace(/\//g, '.');
|
||||
|
||||
const json = JSON.parse(input);
|
||||
const result = JSON.stringify(Object.keys(json).reduce((translations, key) => {
|
||||
translations[key] = {
|
||||
id: `${moduleId}.${key}`,
|
||||
defaultMessage: json[key],
|
||||
};
|
||||
|
||||
return translations;
|
||||
}, {}));
|
||||
|
||||
return `
|
||||
import { defineMessages } from 'react-intl';
|
||||
export default defineMessages(${result})
|
||||
`;
|
||||
};
|
7
webpack-utils/intl-json-loader/package.json
Normal file
7
webpack-utils/intl-json-loader/package.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "intl-json-loader",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
module.exports = function() {
|
||||
this.cacheable && this.cacheable();
|
||||
|
||||
var moduleId = this.context
|
||||
.replace(this.options.resolve.root, '')
|
||||
.replace(/^\/|\/$/g, '')
|
||||
.replace(/\//g, '.');
|
||||
|
||||
var content = this.inputValue[0];
|
||||
content = JSON.stringify(Object.keys(content).reduce(function(translations, key) {
|
||||
translations[key] = {
|
||||
id: moduleId + '.' + key,
|
||||
defaultMessage: content[key]
|
||||
};
|
||||
|
||||
return translations;
|
||||
}, {}));
|
||||
|
||||
return 'import { defineMessages } from \'react-intl\';'
|
||||
+ 'export default defineMessages(' + content + ')';
|
||||
};
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "webpack-utils",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"dependencies": {
|
||||
"loader-utils": "^0.2.12"
|
||||
}
|
||||
}
|
@ -2,106 +2,114 @@
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const webpack = require('webpack');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const CircularDependencyPlugin = require('circular-dependency-plugin');
|
||||
|
||||
const rootPath = path.resolve('./src');
|
||||
module.exports = (env, { mode = 'development' }) => {
|
||||
const isProduction = mode === 'production';
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
return {
|
||||
devtool: isProduction ? false : 'source-map',
|
||||
|
||||
process.env.NODE_ENV = isProduction ? 'production' : 'development';
|
||||
entry: {
|
||||
app: path.join(__dirname, 'src'),
|
||||
},
|
||||
|
||||
const webpackConfig = {
|
||||
entry: {
|
||||
app: path.join(__dirname, 'src')
|
||||
},
|
||||
target: isProduction ? 'node' : 'web',
|
||||
|
||||
target: isProduction ? 'node' : 'web',
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
publicPath: '/',
|
||||
filename: isProduction ? '[name].js' : '[name].js?[hash]',
|
||||
libraryTarget: isProduction ? 'commonjs2' : undefined,
|
||||
},
|
||||
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
publicPath: '/',
|
||||
filename: isProduction ? '[name].js' : '[name].js?[hash]',
|
||||
libraryTarget: isProduction ? 'commonjs' : undefined
|
||||
},
|
||||
resolve: {
|
||||
modules: [
|
||||
path.join(__dirname, 'src'),
|
||||
path.join(__dirname, 'node_modules'),
|
||||
],
|
||||
extensions: ['.js', '.jsx'],
|
||||
},
|
||||
|
||||
resolve: {
|
||||
root: rootPath,
|
||||
extensions: ['', '.js', '.jsx']
|
||||
},
|
||||
|
||||
devServer: {
|
||||
host: 'localhost',
|
||||
port: 8080,
|
||||
hot: true,
|
||||
inline: true,
|
||||
historyApiFallback: true
|
||||
},
|
||||
|
||||
devtool: isProduction ? false : 'eval',
|
||||
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
|
||||
resolveLoader: {
|
||||
alias: {
|
||||
'image-size-loader': path.join(__dirname, 'node_modules/@eoleo/image-size-loader/dist/cjs.js'),
|
||||
},
|
||||
__DEV__: !isProduction,
|
||||
__PROD__: isProduction
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: 'src/index.ejs',
|
||||
favicon: 'src/favicon.ico',
|
||||
filename: 'index.html',
|
||||
inject: false
|
||||
}),
|
||||
new webpack.ProvidePlugin({
|
||||
React: 'react'
|
||||
})
|
||||
],
|
||||
},
|
||||
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel'
|
||||
},
|
||||
{
|
||||
test: /\.(png|gif|jpg|svg)$/,
|
||||
loader: 'file',
|
||||
query: {
|
||||
name: 'assets/[name]-[folder].[ext]?[hash]'
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.json$/,
|
||||
exclude: /(intl|font)\.json/,
|
||||
loader: 'json'
|
||||
},
|
||||
{
|
||||
test: /\.intl\.json$/,
|
||||
loader: 'babel!intl!json'
|
||||
}
|
||||
]
|
||||
},
|
||||
devServer: {
|
||||
host: 'localhost',
|
||||
port: 8080,
|
||||
hot: true,
|
||||
inline: true,
|
||||
historyApiFallback: true,
|
||||
},
|
||||
|
||||
resolveLoader: {
|
||||
alias: {
|
||||
intl: path.resolve('webpack-utils/intl-loader')
|
||||
}
|
||||
}
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
template: 'src/index.ejs',
|
||||
favicon: 'src/favicon.ico',
|
||||
filename: 'index.html',
|
||||
inject: false,
|
||||
}),
|
||||
],
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: isProduction ? {
|
||||
node: '8',
|
||||
} : {
|
||||
browsers: [
|
||||
'last 1 chrome version',
|
||||
'last 1 firefox version',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
'@babel/preset-react',
|
||||
{
|
||||
development: !isProduction,
|
||||
},
|
||||
],
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'@babel/plugin-proposal-export-default-from',
|
||||
// TODO: by unknown reasons react-intl plugins isn't working.
|
||||
// investigate later
|
||||
['react-intl', {
|
||||
messagesDir: path.join(__dirname, 'dist/messages/'),
|
||||
}],
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(png|gif|jpg|svg)$/,
|
||||
loader: 'file-loader',
|
||||
query: {
|
||||
name: 'assets/[name]-[folder].[ext]?[hash]',
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.intl\.json$/,
|
||||
loader: 'intl-json-loader',
|
||||
type: 'javascript/auto',
|
||||
},
|
||||
]
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
if (!isProduction) {
|
||||
webpackConfig.plugins.push(
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NoErrorsPlugin(),
|
||||
new CircularDependencyPlugin({
|
||||
exclude: /node_modules/,
|
||||
failOnError: true
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = webpackConfig;
|
||||
|
Loading…
Reference in New Issue
Block a user