README. Clean up. Images usage demo

This commit is contained in:
SleepWalker 2016-09-04 19:24:50 +03:00
parent fc87c8aa80
commit 4e7980a175
5 changed files with 27 additions and 179 deletions

View File

@ -1 +1,21 @@
# Hello World This package allows rendering of React components into plain HTML to use it in emails. Each directory in `emails` dir corresponds to separate email template. Each email component will receive payload, passed from command line:
```
node cli --type=<email_dir_name> --payload=<json_encoded_in_base64>
```
Try `php example.php` for demo.
# Email component structure
* `index.js` — required. This file should export the main component, wich will receive payload.
* `fixtures.js` — an optional file exports hash `{featureId: payload, featureId2: payload}`. Use this to create data samples for testing in dev mode.
* `styles.js` — an optional file, that will hold style objects for email components to allow style inlining.
* `messages.intl.json` — an optional file, that exports hash with `{messageId: defaultMessage}` for `react-intl`.
# Available npm scripts
* `npm start` — starts app in dev mode.
* `npm run i18n` — collects translations and places in `src/i18n`.
* `npm run build` — builds app for usage in `cli.js`.
* `npm run eslint` — lints source files.

View File

@ -16,12 +16,9 @@
"homepage": "https://gitlab.com/elyby/email-renderer#README", "homepage": "https://gitlab.com/elyby/email-renderer#README",
"scripts": { "scripts": {
"start": "rm -rf dist/ && webpack-dev-server --progress --colors", "start": "rm -rf dist/ && webpack-dev-server --progress --colors",
"up": "npm update",
"test": "karma start ./karma.conf.js",
"lint": "eslint ./src", "lint": "eslint ./src",
"i18n": "cd ./scripts && ../node_modules/.bin/babel-node i18n-collect.js", "i18n": "cd ./scripts && ../node_modules/.bin/babel-node i18n-collect.js",
"build": "rm -rf dist/ && NODE_ENV=production webpack --progress --colors", "build": "rm -rf dist/ && NODE_ENV=production webpack --progress --colors"
"render": ""
}, },
"dependencies": { "dependencies": {
"babel-polyfill": "^6.3.14", "babel-polyfill": "^6.3.14",
@ -45,27 +42,13 @@
"babel-preset-react-hmre": "^1.0.1", "babel-preset-react-hmre": "^1.0.1",
"babel-preset-stage-0": "^6.3.13", "babel-preset-stage-0": "^6.3.13",
"babel-runtime": "^6.0.0", "babel-runtime": "^6.0.0",
"bundle-loader": "^0.5.4",
"css-loader": "^0.23.0",
"eslint": "^3.1.1", "eslint": "^3.1.1",
"eslint-plugin-react": "^6.0.0", "eslint-plugin-react": "^6.0.0",
"exports-loader": "^0.6.3",
"extract-text-webpack-plugin": "^1.0.0", "extract-text-webpack-plugin": "^1.0.0",
"file-loader": "^0.9.0", "file-loader": "^0.9.0",
"html-loader": "^0.4.3",
"html-webpack-plugin": "^2.0.0", "html-webpack-plugin": "^2.0.0",
"imports-loader": "^0.6.5",
"json-loader": "^0.5.4", "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", "scripts": "file:scripts",
"style-loader": "^0.13.0",
"url-loader": "^0.5.7", "url-loader": "^0.5.7",
"webpack": "^1.12.9", "webpack": "^1.12.9",
"webpack-dev-server": "^1.14.0", "webpack-dev-server": "^1.14.0",

View File

@ -2,6 +2,8 @@ export default function Activation() {
return ( return (
<div> <div>
Activated! Activated!
<br />
<img src={require('./images/kitty.jpg')} />
</div> </div>
); );
} }

View File

@ -1,36 +0,0 @@
// при использовании 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)
)
});
};

View File

@ -3,11 +3,7 @@
const path = require('path'); const path = require('path');
const webpack = require('webpack'); const webpack = require('webpack');
const loaderUtils = require('loader-utils');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const cssUrl = require('webpack-utils/cssUrl');
const cssImport = require('postcss-import');
const rootPath = path.resolve('./src'); const rootPath = path.resolve('./src');
@ -15,36 +11,7 @@ const isProduction = process.env.NODE_ENV === 'production';
process.env.NODE_ENV = isProduction ? 'production' : 'development'; process.env.NODE_ENV = isProduction ? 'production' : 'development';
const CSS_CLASS_TEMPLATE = isProduction ? '[hash:base64:5]' : '[path][name]-[local]'; const webpackConfig = {
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: { entry: {
app: path.join(__dirname, 'src') app: path.join(__dirname, 'src')
}, },
@ -66,20 +33,12 @@ var webpackConfig = {
devServer: { devServer: {
host: 'localhost', host: 'localhost',
port: 8080, port: 8080,
// proxy: {
// '/api*': {
// headers: {
// host: config.apiHost.replace(/https?:|\//g, '')
// },
// target: config.apiHost
// }
// },
hot: true, hot: true,
inline: true, inline: true,
historyApiFallback: true historyApiFallback: true
}, },
devtool: 'eval', devtool: isProduction ? 'eval' : false,
plugins: [ plugins: [
new webpack.DefinePlugin({ new webpack.DefinePlugin({
@ -102,11 +61,6 @@ var webpackConfig = {
module: { module: {
loaders: [ loaders: [
{
test: /\.scss$/,
extractInProduction: true,
loader: 'style!css?' + JSON.stringify(cssLoaderQuery) + '!sass!postcss?syntax=postcss-scss'
},
{ {
test: /\.jsx?$/, test: /\.jsx?$/,
exclude: /node_modules/, exclude: /node_modules/,
@ -114,35 +68,19 @@ var webpackConfig = {
}, },
{ {
test: /\.(png|gif|jpg|svg)$/, test: /\.(png|gif|jpg|svg)$/,
loader: 'file', loader: 'url',
query: { query: {
name: 'assets/[name].[ext]?[hash]' name: 'assets/[name].[ext]?[hash]'
} }
}, },
{
test: /\.(woff|woff2|ttf)$/,
loader: 'file',
query: {
name: 'assets/fonts/[name].[ext]?[hash]'
}
},
{ {
test: /\.json$/, test: /\.json$/,
exclude: /(intl|font)\.json/, exclude: /(intl|font)\.json/,
loader: 'json' loader: 'json'
}, },
{
test: /\.html$/,
loader: 'html'
},
{ {
test: /\.intl\.json$/, test: /\.intl\.json$/,
loader: 'babel!intl!json' loader: 'babel!intl!json'
},
{
test: /\.font\.(js|json)$/,
loader: 'raw!fontgen'
} }
] ]
}, },
@ -151,68 +89,9 @@ var webpackConfig = {
alias: { alias: {
intl: path.resolve('webpack-utils/intl-loader') 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) { if (!isProduction) {
webpackConfig.plugins.push( webpackConfig.plugins.push(
new webpack.HotModuleReplacementPlugin(), new webpack.HotModuleReplacementPlugin(),