mirror of
https://github.com/elyby/emails-renderer.git
synced 2024-12-22 13:19:45 +05:30
Hello World
This commit is contained in:
parent
7e8e198763
commit
7e618b23df
9
.babelrc
Normal file
9
.babelrc
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"presets": ["react", "es2015", "stage-0"],
|
||||||
|
"plugins": ["transform-runtime", ["react-intl", {"messagesDir": "./dist/messages/"}]],
|
||||||
|
"env": {
|
||||||
|
"development": {
|
||||||
|
"presets": ["react-hmre"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
213
.eslintrc.json
Normal file
213
.eslintrc.json
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
{
|
||||||
|
"parser": "babel-eslint",
|
||||||
|
"plugins": [
|
||||||
|
"react"
|
||||||
|
],
|
||||||
|
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true,
|
||||||
|
"modules": true,
|
||||||
|
"classes": true,
|
||||||
|
"defaultParams": true,
|
||||||
|
"destructuring": true,
|
||||||
|
"spread": true,
|
||||||
|
"arrowFunctions": true,
|
||||||
|
"blockBindings": true,
|
||||||
|
"objectLiteralComputedProperties": true,
|
||||||
|
"objectLiteralDuplicateProperties": true,
|
||||||
|
"objectLiteralShorthandMethods": true,
|
||||||
|
"objectLiteralShorthandProperties": true,
|
||||||
|
"restParams": true,
|
||||||
|
"superInFunctions": true,
|
||||||
|
"templateStrings": true,
|
||||||
|
"experimentalObjectRestSpread": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"commonjs": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
|
||||||
|
// @see: http://eslint.org/docs/rules/
|
||||||
|
"rules": {
|
||||||
|
// possible errors (including eslint:recommended)
|
||||||
|
"valid-jsdoc": ["warn", {
|
||||||
|
"requireParamDescription": false,
|
||||||
|
"requireReturn": false,
|
||||||
|
"requireReturnDescription": false,
|
||||||
|
"prefer": {
|
||||||
|
"returns": "return"
|
||||||
|
},
|
||||||
|
"preferType": {
|
||||||
|
"String": "string",
|
||||||
|
"Object": "object",
|
||||||
|
"Number": "number",
|
||||||
|
"Function": "function"
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
|
||||||
|
// best practice
|
||||||
|
"block-scoped-var": "error",
|
||||||
|
"curly": "error",
|
||||||
|
"default-case": "error",
|
||||||
|
"dot-location": ["error", "property"],
|
||||||
|
"dot-notation": "error",
|
||||||
|
"eqeqeq": ["error", "smart"],
|
||||||
|
"no-alert": "error",
|
||||||
|
"no-caller": "error",
|
||||||
|
"no-case-declarations": "error",
|
||||||
|
"no-div-regex": "error",
|
||||||
|
"no-else-return": "error",
|
||||||
|
"no-empty-pattern": "error",
|
||||||
|
"no-eq-null": "error",
|
||||||
|
"no-eval": "error",
|
||||||
|
"no-extend-native": "error",
|
||||||
|
"no-extra-bind": "warn",
|
||||||
|
"no-fallthrough": "error",
|
||||||
|
"no-floating-decimal": "warn",
|
||||||
|
"no-implied-eval": "error",
|
||||||
|
"no-invalid-this": "off",
|
||||||
|
"no-labels": "error",
|
||||||
|
"no-lone-blocks": "warn",
|
||||||
|
"no-loop-func": "error",
|
||||||
|
"no-multi-spaces": "error",
|
||||||
|
"no-multi-str": "error",
|
||||||
|
"no-native-reassign": "error",
|
||||||
|
"no-new-wrappers": "warn",
|
||||||
|
"no-new": "warn",
|
||||||
|
"no-octal-escape": "warn",
|
||||||
|
"no-octal": "error",
|
||||||
|
"no-proto": "error",
|
||||||
|
"no-redeclare": "warn",
|
||||||
|
"no-script-url": "error",
|
||||||
|
"no-self-compare": "error",
|
||||||
|
"no-sequences": "error",
|
||||||
|
"no-throw-literal": "error",
|
||||||
|
"no-unused-expressions": ["warn", {"allowShortCircuit": true, "allowTernary": true}],
|
||||||
|
"no-useless-call": "warn",
|
||||||
|
"no-useless-concat": "warn",
|
||||||
|
"no-void": "error",
|
||||||
|
"no-with": "error",
|
||||||
|
"radix": "error",
|
||||||
|
"wrap-iife": "error",
|
||||||
|
"yoda": "warn",
|
||||||
|
"no-constant-condition": "error",
|
||||||
|
|
||||||
|
// strict mode
|
||||||
|
"strict": ["warn", "never"], // babel все сделает за нас
|
||||||
|
|
||||||
|
// variables
|
||||||
|
"no-catch-shadow": "error",
|
||||||
|
"no-delete-var": "error",
|
||||||
|
"no-label-var": "error",
|
||||||
|
"no-shadow-restricted-names": "error",
|
||||||
|
"no-shadow": "off",
|
||||||
|
"no-undef-init": "error",
|
||||||
|
"no-undef": "error",
|
||||||
|
"no-use-before-define": ["warn", "nofunc"],
|
||||||
|
|
||||||
|
// CommonJS
|
||||||
|
"no-mixed-requires": "warn",
|
||||||
|
"no-path-concat": "warn",
|
||||||
|
|
||||||
|
// stylistic
|
||||||
|
"array-bracket-spacing": "error",
|
||||||
|
"block-spacing": ["error", "never"],
|
||||||
|
"brace-style": ["error", "1tbs", {"allowSingleLine": true}],
|
||||||
|
"comma-spacing": "error",
|
||||||
|
"comma-style": "error",
|
||||||
|
"comma-dangle": ["warn", "only-multiline"],
|
||||||
|
"computed-property-spacing": "error",
|
||||||
|
"consistent-this": ["error", "that"],
|
||||||
|
"camelcase": "warn",
|
||||||
|
"eol-last": "warn",
|
||||||
|
"id-length": ["error", {"min": 2, "exceptions": ["x", "y", "i", "$"]}],
|
||||||
|
"indent": ["error", 4, {"SwitchCase": 1}],
|
||||||
|
"jsx-quotes": "error",
|
||||||
|
"key-spacing": ["error", {"mode": "minimum"}],
|
||||||
|
"linebreak-style": "error",
|
||||||
|
"max-depth": "error",
|
||||||
|
"new-cap": "error",
|
||||||
|
"new-parens": "error",
|
||||||
|
"no-array-constructor": "warn",
|
||||||
|
"no-bitwise": "warn",
|
||||||
|
"no-lonely-if": "error",
|
||||||
|
"no-negated-condition": "warn",
|
||||||
|
"no-nested-ternary": "error",
|
||||||
|
"no-new-object": "error",
|
||||||
|
"no-spaced-func": "error",
|
||||||
|
"no-trailing-spaces": "warn",
|
||||||
|
"no-unneeded-ternary": "warn",
|
||||||
|
"one-var": ["error", "never"],
|
||||||
|
"operator-assignment": ["warn", "always"],
|
||||||
|
"operator-linebreak": ["error", "before"],
|
||||||
|
"padded-blocks": ["warn", "never"],
|
||||||
|
"quote-props": ["warn", "as-needed"],
|
||||||
|
"quotes": ["warn", "single"],
|
||||||
|
"semi": "error",
|
||||||
|
"semi-spacing": "error",
|
||||||
|
"keyword-spacing": "warn",
|
||||||
|
"space-before-blocks": "error",
|
||||||
|
"space-before-function-paren": ["error", "never"],
|
||||||
|
"space-in-parens": "warn",
|
||||||
|
"space-infix-ops": "error",
|
||||||
|
"space-unary-ops": "error",
|
||||||
|
"spaced-comment": "warn",
|
||||||
|
|
||||||
|
// es6
|
||||||
|
"arrow-body-style": "warn",
|
||||||
|
"arrow-parens": "error",
|
||||||
|
"arrow-spacing": "error",
|
||||||
|
"constructor-super": "error",
|
||||||
|
"generator-star-spacing": "warn",
|
||||||
|
"no-class-assign": "error",
|
||||||
|
"no-const-assign": "error",
|
||||||
|
"no-dupe-class-members": "error",
|
||||||
|
"no-this-before-super": "error",
|
||||||
|
"no-var": "warn",
|
||||||
|
"object-shorthand": "warn",
|
||||||
|
"prefer-arrow-callback": "warn",
|
||||||
|
"prefer-const": "warn",
|
||||||
|
"prefer-reflect": "warn",
|
||||||
|
"prefer-spread": "warn",
|
||||||
|
"prefer-template": "warn",
|
||||||
|
"require-yield": "error",
|
||||||
|
|
||||||
|
// react
|
||||||
|
"react/display-name": "warn",
|
||||||
|
"react/forbid-prop-types": "warn",
|
||||||
|
"react/jsx-boolean-value": "warn",
|
||||||
|
"react/jsx-closing-bracket-location": "warn",
|
||||||
|
"react/jsx-curly-spacing": "warn",
|
||||||
|
"react/jsx-handler-names": ["warn", {"eventHandlerPrefix": "on", "eventHandlerPropPrefix": "on"}],
|
||||||
|
"react/jsx-indent-props": "warn",
|
||||||
|
"react/jsx-key": "warn",
|
||||||
|
"react/jsx-max-props-per-line": ["warn", {"maximum": 3}],
|
||||||
|
"react/jsx-no-bind": "warn",
|
||||||
|
"react/jsx-no-duplicate-props": "warn",
|
||||||
|
"react/jsx-no-literals": "warn",
|
||||||
|
"react/jsx-no-undef": "warn",
|
||||||
|
"react/jsx-pascal-case": "warn",
|
||||||
|
"react/jsx-uses-react": "warn",
|
||||||
|
"react/jsx-uses-vars": "warn",
|
||||||
|
"react/jsx-no-comment-textnodes": "warn",
|
||||||
|
"react/jsx-wrap-multilines": "warn",
|
||||||
|
"react/no-deprecated": "warn",
|
||||||
|
"react/no-did-mount-set-state": "warn",
|
||||||
|
"react/no-did-update-set-state": "warn",
|
||||||
|
"react/no-direct-mutation-state": "warn",
|
||||||
|
"react/require-render-return": "warn",
|
||||||
|
"react/no-is-mounted": "warn",
|
||||||
|
"react/no-multi-comp": "warn",
|
||||||
|
"react/no-string-refs": "warn",
|
||||||
|
"react/no-unknown-property": "warn",
|
||||||
|
"react/prefer-es6-class": "warn",
|
||||||
|
"react/prop-types": "warn",
|
||||||
|
"react/react-in-jsx-scope": "warn",
|
||||||
|
"react/self-closing-comp": "warn",
|
||||||
|
"react/sort-comp": ["warn", {"order": ["lifecycle", "render", "everything-else"]}]
|
||||||
|
}
|
||||||
|
}
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
74
package.json
Normal file
74
package.json
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"name": "email-renderer",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+ssh://git@gitlab.com/elyby/email-renderer.git"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "SleepWalker <dev@udf.su>",
|
||||||
|
"license": "private",
|
||||||
|
"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",
|
||||||
|
"up": "npm update",
|
||||||
|
"test": "karma start ./karma.conf.js",
|
||||||
|
"lint": "eslint ./src",
|
||||||
|
"i18n": "cd ./scripts && ./node_modules/.bin/babel-node i18n-collect.js",
|
||||||
|
"build": "rm -rf dist/ && webpack --progress --colors -p",
|
||||||
|
"render": ""
|
||||||
|
},
|
||||||
|
"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-intl": "^2.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"babel-core": "^6.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",
|
||||||
|
"bundle-loader": "^0.5.4",
|
||||||
|
"css-loader": "^0.23.0",
|
||||||
|
"eslint": "^3.1.1",
|
||||||
|
"eslint-plugin-react": "^6.0.0",
|
||||||
|
"exports-loader": "^0.6.3",
|
||||||
|
"extract-text-webpack-plugin": "^1.0.0",
|
||||||
|
"file-loader": "^0.9.0",
|
||||||
|
"html-loader": "^0.4.3",
|
||||||
|
"html-webpack-plugin": "^2.0.0",
|
||||||
|
"imports-loader": "^0.6.5",
|
||||||
|
"json-loader": "^0.5.4",
|
||||||
|
"loader-utils": "^0.2.15",
|
||||||
|
"node-sass": "^3.4.2",
|
||||||
|
"postcss-import": "^8.1.2",
|
||||||
|
"postcss-loader": "^0.9.0",
|
||||||
|
"postcss-scss": "^0.1.8",
|
||||||
|
"postcss-url": "SleepWalker/postcss-url#switch-to-async-api",
|
||||||
|
"raw-loader": "^0.5.1",
|
||||||
|
"sass-loader": "^4.0.0",
|
||||||
|
"scripts": "file:scripts",
|
||||||
|
"style-loader": "^0.13.0",
|
||||||
|
"url-loader": "^0.5.7",
|
||||||
|
"webpack": "^1.12.9",
|
||||||
|
"webpack-dev-server": "^1.14.0",
|
||||||
|
"webpack-utils": "file:webpack-utils"
|
||||||
|
}
|
||||||
|
}
|
3
scripts/.babelrc
Normal file
3
scripts/.babelrc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"breakConfig": true
|
||||||
|
}
|
183
scripts/i18n-collect.js
Normal file
183
scripts/i18n-collect.js
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
import fs from 'fs';
|
||||||
|
import {sync as globSync} from 'glob';
|
||||||
|
import {sync as mkdirpSync} from 'mkdirp';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import prompt from 'prompt';
|
||||||
|
|
||||||
|
const MESSAGES_PATTERN = '../dist/messages/**/*.json';
|
||||||
|
const LANG_DIR = '../src/i18n';
|
||||||
|
const DEFAULT_LOCALE = 'en';
|
||||||
|
const SUPPORTED_LANGS = [DEFAULT_LOCALE].concat('ru', 'be', 'uk');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aggregates the default messages that were extracted from the app's
|
||||||
|
* React components via the React Intl Babel plugin. An error will be thrown if
|
||||||
|
* there are messages in different components that use the same `id`. The result
|
||||||
|
* is a flat collection of `id: message` pairs for the app's default locale.
|
||||||
|
*/
|
||||||
|
let idToFileMap = {};
|
||||||
|
let duplicateIds = [];
|
||||||
|
const collectedMessages = globSync(MESSAGES_PATTERN)
|
||||||
|
.map((filename) => [filename, JSON.parse(fs.readFileSync(filename, 'utf8'))])
|
||||||
|
.reduce((collection, [file, descriptors]) => {
|
||||||
|
descriptors.forEach(({id, defaultMessage}) => {
|
||||||
|
if (collection.hasOwnProperty(id)) {
|
||||||
|
duplicateIds.push(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
collection[id] = defaultMessage;
|
||||||
|
idToFileMap[id] = (idToFileMap[id] || []).concat(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
return collection;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicateIds = null;
|
||||||
|
idToFileMap = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Making a diff with the previous DEFAULT_LOCALE version
|
||||||
|
*/
|
||||||
|
const defaultMessagesPath = `${LANG_DIR}/${DEFAULT_LOCALE}.json`;
|
||||||
|
let keysToUpdate = [];
|
||||||
|
let keysToAdd = [];
|
||||||
|
let keysToRemove = [];
|
||||||
|
const keysToRename = [];
|
||||||
|
const isNotMarked = (value) => value.slice(0, 2) !== '--';
|
||||||
|
try {
|
||||||
|
const prevMessages = JSON.parse(fs.readFileSync(defaultMessagesPath, 'utf8'));
|
||||||
|
const prevMessagesMap = Object.entries(prevMessages).reduce((acc, [key, value]) => {
|
||||||
|
if (acc[value]) {
|
||||||
|
acc[value].push(key);
|
||||||
|
} else {
|
||||||
|
acc[value] = [key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
keysToAdd = Object.keys(collectedMessages).filter((key) => !prevMessages[key]);
|
||||||
|
keysToRemove = Object.keys(prevMessages).filter((key) => !collectedMessages[key]).filter(isNotMarked);
|
||||||
|
keysToUpdate = Object.entries(prevMessages).reduce((acc, [key, message]) =>
|
||||||
|
acc.concat(collectedMessages[key] && collectedMessages[key] !== message ? key : [])
|
||||||
|
, []);
|
||||||
|
|
||||||
|
// detect keys to rename, mutating keysToAdd and keysToRemove
|
||||||
|
[].concat(keysToAdd).forEach((toKey) => {
|
||||||
|
const keys = prevMessagesMap[collectedMessages[toKey]] || [];
|
||||||
|
const fromKey = keys.find((fromKey) => keysToRemove.indexOf(fromKey) > -1);
|
||||||
|
|
||||||
|
if (fromKey) {
|
||||||
|
keysToRename.push([fromKey, toKey]);
|
||||||
|
|
||||||
|
keysToRemove.splice(keysToRemove.indexOf(fromKey), 1);
|
||||||
|
keysToAdd.splice(keysToAdd.indexOf(toKey), 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.log(chalk.yellow(`Can not read ${defaultMessagesPath}. The new file will be created.`), err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keysToAdd.length && !keysToRemove.length && !keysToUpdate.length && !keysToRename.length) {
|
||||||
|
return console.log(chalk.green('Everything is up to date!'));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(chalk.magenta(`The diff relative to default locale (${DEFAULT_LOCALE}) is:`));
|
||||||
|
|
||||||
|
if (keysToRemove.length) {
|
||||||
|
console.log('The following keys will be removed:');
|
||||||
|
console.log([chalk.red('\n - '), keysToRemove.join(chalk.red('\n - ')), '\n'].join(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keysToAdd.length) {
|
||||||
|
console.log('The following keys will be added:');
|
||||||
|
console.log([chalk.green('\n + '), keysToAdd.join(chalk.green('\n + ')), '\n'].join(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keysToUpdate.length) {
|
||||||
|
console.log('The following keys will be updated:');
|
||||||
|
console.log([chalk.yellow('\n @ '), keysToUpdate.join(chalk.yellow('\n @ ')), '\n'].join(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keysToRename.length) {
|
||||||
|
console.log('The following keys will be renamed:\n');
|
||||||
|
console.log(keysToRename.reduce((str, pair) =>
|
||||||
|
[str, pair[0], chalk.yellow(' -> '), pair[1], '\n'].join('')
|
||||||
|
, ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt.start();
|
||||||
|
prompt.get({
|
||||||
|
properties: {
|
||||||
|
apply: {
|
||||||
|
description: 'Apply changes? [Y/n]',
|
||||||
|
pattern: /^y|n$/i,
|
||||||
|
message: 'Please enter "y" or "n"',
|
||||||
|
default: 'y',
|
||||||
|
before: (value) => value.toLowerCase() === 'y'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, (err, resp) => {
|
||||||
|
console.log('\n');
|
||||||
|
|
||||||
|
if (err || !resp.apply) {
|
||||||
|
return console.log(chalk.red('Aborted'));
|
||||||
|
}
|
||||||
|
|
||||||
|
buildLocales();
|
||||||
|
|
||||||
|
console.log(chalk.green('All locales was successfuly built'));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function buildLocales() {
|
||||||
|
mkdirpSync(LANG_DIR);
|
||||||
|
|
||||||
|
SUPPORTED_LANGS.map((lang) => {
|
||||||
|
const destPath = `${LANG_DIR}/${lang}.json`;
|
||||||
|
|
||||||
|
let newMessages = {};
|
||||||
|
try {
|
||||||
|
newMessages = JSON.parse(fs.readFileSync(destPath, 'utf8'));
|
||||||
|
} catch (err) {
|
||||||
|
console.log(chalk.yellow(`Can not read ${destPath}. The new file will be created.`), err);
|
||||||
|
}
|
||||||
|
|
||||||
|
keysToRename.forEach(([fromKey, toKey]) => {
|
||||||
|
newMessages[toKey] = newMessages[fromKey];
|
||||||
|
delete newMessages[fromKey];
|
||||||
|
});
|
||||||
|
keysToRemove.forEach((key) => {
|
||||||
|
delete newMessages[key];
|
||||||
|
});
|
||||||
|
keysToUpdate.forEach((key) => {
|
||||||
|
newMessages[`--${key}`] = newMessages[key];
|
||||||
|
newMessages[key] = collectedMessages[key];
|
||||||
|
});
|
||||||
|
keysToAdd.forEach((key) => {
|
||||||
|
newMessages[key] = collectedMessages[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortedKeys = Object.keys(newMessages).sort((key1, key2) => {
|
||||||
|
key1 = key1.replace(/^\-+/, '');
|
||||||
|
key2 = key2.replace(/^\-+/, '');
|
||||||
|
|
||||||
|
return key1 < key2 || !isNotMarked(key1) ? -1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortedNewMessages = sortedKeys.reduce((acc, key) => {
|
||||||
|
acc[key] = newMessages[key];
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
fs.writeFileSync(destPath, JSON.stringify(sortedNewMessages, null, 4) + '\n');
|
||||||
|
});
|
||||||
|
}
|
17
scripts/package.json
Normal file
17
scripts/package.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "scripts",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "i18n-build.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"chalk": "^1.1.3",
|
||||||
|
"node-babel": "^0.1.2",
|
||||||
|
"prompt": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
BIN
src/favicon.ico
Normal file
BIN
src/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 198 B |
24
src/index.ejs
Normal file
24
src/index.ejs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Ely.by - Email Renderer</title>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="msapplication-tap-highlight" content="no">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||||||
|
<% if (htmlWebpackPlugin.files.favicon) { %>
|
||||||
|
<link rel="shortcut icon" href="<%= htmlWebpackPlugin.files.favicon %>">
|
||||||
|
<% } %>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="app" class="app"></div>
|
||||||
|
|
||||||
|
<% for (var css in htmlWebpackPlugin.files.css) { %>
|
||||||
|
<link href="<%= htmlWebpackPlugin.files.css[css] %>" rel="stylesheet">
|
||||||
|
<% } %>
|
||||||
|
<% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
|
||||||
|
<script src="<%= htmlWebpackPlugin.files.chunks[chunk].entry %>"></script>
|
||||||
|
<% } %>
|
||||||
|
</body>
|
||||||
|
</html>
|
13
src/index.js
Normal file
13
src/index.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import 'babel-polyfill';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
|
import { IntlProvider } from 'react-intl';
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<IntlProvider locale="en" messages={{}}>
|
||||||
|
<div>Hello world</div>
|
||||||
|
</IntlProvider>,
|
||||||
|
document.getElementById('app')
|
||||||
|
);
|
36
webpack-utils/cssUrl.js
Normal file
36
webpack-utils/cssUrl.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// при использовании sass-loader теряется контекст в импортированных модулях
|
||||||
|
// из-за чего css-loader не может правильно обработать относительные url
|
||||||
|
//
|
||||||
|
// препроцессим урлы перед тем, как пропускать их через sass-loader
|
||||||
|
// урлы, начинающиеся с / будут оставлены как есть
|
||||||
|
|
||||||
|
const cssUrl = require('postcss-url');
|
||||||
|
const loaderUtils = require('loader-utils');
|
||||||
|
|
||||||
|
// /#.+$/ - strip #hash part of svg font url
|
||||||
|
const urlToRequest = (url) => loaderUtils.urlToRequest(url.replace(/\??#.+$/, ''), true);
|
||||||
|
const urlPostfix = (url) => {
|
||||||
|
var idx = url.indexOf('?#');
|
||||||
|
|
||||||
|
if (idx < 0) {
|
||||||
|
idx = url.indexOf('#');
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx >= 0 ? url.slice(idx) : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = function(loader) {
|
||||||
|
return cssUrl({
|
||||||
|
url: (url, decl, from, dirname, to, options, result) =>
|
||||||
|
new Promise((resolve, reject) =>
|
||||||
|
loaderUtils.isUrlRequest(url) ? loader.loadModule(urlToRequest(url), (err, source) =>
|
||||||
|
err ? reject(err) : resolve(
|
||||||
|
loader.exec(`
|
||||||
|
var __webpack_public_path__ = '${loader.options.output.publicPath}';
|
||||||
|
${source}
|
||||||
|
`) + urlPostfix(url)
|
||||||
|
)
|
||||||
|
) : resolve(url)
|
||||||
|
)
|
||||||
|
});
|
||||||
|
};
|
21
webpack-utils/intl-loader.js
Normal file
21
webpack-utils/intl-loader.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
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 + ')';
|
||||||
|
};
|
10
webpack-utils/package.json
Normal file
10
webpack-utils/package.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "webpack-utils",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"dependencies": {
|
||||||
|
"loader-utils": "^0.2.12"
|
||||||
|
}
|
||||||
|
}
|
232
webpack.config.js
Normal file
232
webpack.config.js
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
/* eslint-env node */
|
||||||
|
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
var webpack = require('webpack');
|
||||||
|
var loaderUtils = require('loader-utils');
|
||||||
|
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
|
var HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
var cssUrl = require('webpack-utils/cssUrl');
|
||||||
|
var cssImport = require('postcss-import');
|
||||||
|
|
||||||
|
var vendor = Object.keys(require('./package.json').dependencies);
|
||||||
|
|
||||||
|
const rootPath = path.resolve('./src');
|
||||||
|
|
||||||
|
const isProduction = process.argv.some((arg) => arg === '-p');
|
||||||
|
|
||||||
|
process.env.NODE_ENV = isProduction ? 'production' : 'development';
|
||||||
|
|
||||||
|
const CSS_CLASS_TEMPLATE = isProduction ? '[hash:base64:5]' : '[path][name]-[local]';
|
||||||
|
|
||||||
|
const fileCache = {};
|
||||||
|
|
||||||
|
|
||||||
|
const cssLoaderQuery = {
|
||||||
|
modules: true,
|
||||||
|
importLoaders: 2,
|
||||||
|
url: false,
|
||||||
|
localIdentName: CSS_CLASS_TEMPLATE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cssnano options
|
||||||
|
*/
|
||||||
|
sourcemap: !isProduction,
|
||||||
|
autoprefixer: {
|
||||||
|
add: true,
|
||||||
|
remove: true,
|
||||||
|
browsers: ['last 2 versions']
|
||||||
|
},
|
||||||
|
safe: true,
|
||||||
|
// отключаем минификацию цветов, что бы она не ломала такие выражения:
|
||||||
|
// composes: black from './buttons.scss';
|
||||||
|
colormin: false,
|
||||||
|
discardComments: {
|
||||||
|
removeAll: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var webpackConfig = {
|
||||||
|
entry: {
|
||||||
|
app: path.join(__dirname, 'src'),
|
||||||
|
vendor: vendor
|
||||||
|
},
|
||||||
|
|
||||||
|
output: {
|
||||||
|
path: path.join(__dirname, 'dist'),
|
||||||
|
publicPath: '/',
|
||||||
|
filename: '[name].js?[hash]'
|
||||||
|
},
|
||||||
|
|
||||||
|
resolve: {
|
||||||
|
root: rootPath,
|
||||||
|
extensions: ['', '.js', '.jsx']
|
||||||
|
},
|
||||||
|
|
||||||
|
devServer: {
|
||||||
|
host: 'localhost',
|
||||||
|
port: 8080,
|
||||||
|
// proxy: {
|
||||||
|
// '/api*': {
|
||||||
|
// headers: {
|
||||||
|
// host: config.apiHost.replace(/https?:|\//g, '')
|
||||||
|
// },
|
||||||
|
// target: config.apiHost
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
hot: true,
|
||||||
|
inline: true,
|
||||||
|
historyApiFallback: true
|
||||||
|
},
|
||||||
|
|
||||||
|
devtool: 'eval',
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env': {
|
||||||
|
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
|
||||||
|
},
|
||||||
|
__DEV__: !isProduction,
|
||||||
|
__PROD__: isProduction
|
||||||
|
}),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: 'src/index.ejs',
|
||||||
|
favicon: 'src/favicon.ico',
|
||||||
|
hash: isProduction,
|
||||||
|
filename: 'index.html',
|
||||||
|
inject: false,
|
||||||
|
minify: {
|
||||||
|
collapseWhitespace: isProduction
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
// window.fetch polyfill
|
||||||
|
fetch: 'imports?this=>self!exports?self.fetch!whatwg-fetch'
|
||||||
|
}),
|
||||||
|
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js?[hash]')
|
||||||
|
].concat(isProduction ? [
|
||||||
|
new webpack.optimize.DedupePlugin(),
|
||||||
|
new webpack.optimize.UglifyJsPlugin()
|
||||||
|
] : []),
|
||||||
|
|
||||||
|
module: {
|
||||||
|
loaders: [
|
||||||
|
{
|
||||||
|
test: /\.scss$/,
|
||||||
|
extractInProduction: true,
|
||||||
|
loader: 'style!css?' + JSON.stringify(cssLoaderQuery) + '!sass!postcss?syntax=postcss-scss'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.jsx?$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'babel'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|gif|jpg|svg)$/,
|
||||||
|
loader: 'file',
|
||||||
|
query: {
|
||||||
|
name: 'assets/[name].[ext]?[hash]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(woff|woff2|ttf)$/,
|
||||||
|
loader: 'file',
|
||||||
|
query: {
|
||||||
|
name: 'assets/fonts/[name].[ext]?[hash]'
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.json$/,
|
||||||
|
exclude: /(intl|font)\.json/,
|
||||||
|
loader: 'json'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.html$/,
|
||||||
|
loader: 'html'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.intl\.json$/,
|
||||||
|
loader: 'babel!intl!json'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.font\.(js|json)$/,
|
||||||
|
loader: 'raw!fontgen'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
resolveLoader: {
|
||||||
|
alias: {
|
||||||
|
intl: path.resolve('webpack-utils/intl-loader')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
postcss() {
|
||||||
|
return [
|
||||||
|
cssImport({
|
||||||
|
path: rootPath,
|
||||||
|
addDependencyTo: webpack,
|
||||||
|
|
||||||
|
resolve: ((defaultResolve) =>
|
||||||
|
(url, basedir, importOptions) =>
|
||||||
|
defaultResolve(loaderUtils.urlToRequest(url), basedir, importOptions)
|
||||||
|
)(require('postcss-import/lib/resolve-id')),
|
||||||
|
|
||||||
|
load: ((defaultLoad) =>
|
||||||
|
(filename, importOptions) => {
|
||||||
|
if (/\.font.(js|json)$/.test(filename)) {
|
||||||
|
if (!fileCache[filename] || !isProduction) {
|
||||||
|
// do not execute loader on the same file twice
|
||||||
|
// this is an overcome for a bug with ExtractTextPlugin, for isProduction === true
|
||||||
|
// when @imported files may be processed mutiple times
|
||||||
|
fileCache[filename] = new Promise((resolve, reject) =>
|
||||||
|
this.loadModule(filename, (err, source) =>
|
||||||
|
err ? reject(err) : resolve(this.exec(source))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileCache[filename];
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultLoad(filename, importOptions);
|
||||||
|
}
|
||||||
|
)(require('postcss-import/lib/load-content'))
|
||||||
|
}),
|
||||||
|
|
||||||
|
cssUrl(this)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isProduction) {
|
||||||
|
webpackConfig.module.loaders.forEach((loader) => {
|
||||||
|
if (loader.extractInProduction) {
|
||||||
|
// remove style-loader from chain and pass through ExtractTextPlugin
|
||||||
|
const parts = loader.loader.split('!');
|
||||||
|
|
||||||
|
loader.loader = ExtractTextPlugin.extract(
|
||||||
|
parts[0], // style-loader
|
||||||
|
parts.slice(1) // css-loader and rest
|
||||||
|
.join('!')
|
||||||
|
.replace(/[&?]sourcemap/, '')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
webpackConfig.plugins.push(new ExtractTextPlugin('styles.css', {
|
||||||
|
allChunks: true
|
||||||
|
}));
|
||||||
|
|
||||||
|
webpackConfig.devtool = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isProduction) {
|
||||||
|
webpackConfig.plugins.push(
|
||||||
|
new webpack.HotModuleReplacementPlugin(),
|
||||||
|
new webpack.NoErrorsPlugin()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = webpackConfig;
|
Loading…
Reference in New Issue
Block a user