mirror of
https://github.com/elyby/emails-renderer.git
synced 2025-05-31 14:12:05 +05:30
Implemented font image renderer
This commit is contained in:
Binary file not shown.
120
webpack-utils/extended-translations-loader/index.js
Normal file
120
webpack-utils/extended-translations-loader/index.js
Normal file
@@ -0,0 +1,120 @@
|
||||
/* eslint-env node */
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const glob = require('glob');
|
||||
const { stringify } = require('qs');
|
||||
|
||||
const localFontPath = path.join(__dirname, 'RobotoCondensed-Regular.ttf');
|
||||
const localFontName = 'RobotoCondensed';
|
||||
const scale = 2;
|
||||
|
||||
module.exports = async function(content) {
|
||||
this.cacheable && this.cacheable();
|
||||
|
||||
const callback = this.async();
|
||||
|
||||
const { publicPath } = this._compiler.options.output;
|
||||
const ROOT_PATH = path.join(this.rootContext, 'src');
|
||||
const localeName = path.basename(this.resourcePath, `.${this.resourcePath.split('.').pop()}`);
|
||||
|
||||
const renderText2Png = (key, { text, size, color }) => new Promise((resolve, reject) => {
|
||||
if (!text || !size || !color) {
|
||||
reject(new Error('text, size and color params are required'));
|
||||
return;
|
||||
}
|
||||
|
||||
const fileName = `${key.replace(/\./g, '_')}_${localeName}.png`;
|
||||
const args = {
|
||||
localFontPath,
|
||||
localFontName,
|
||||
text,
|
||||
color,
|
||||
font: `${size * scale}px RobotoCondensed`, // eslint-disable-line generator-star-spacing
|
||||
};
|
||||
const renderTextRequest = `text2png-loader?${stringify(args)}!`;
|
||||
const emitFileRequest = `image-size-loader?name=assets${path.sep}${fileName}?[hash]!${renderTextRequest}`;
|
||||
this.loadModule(emitFileRequest, (err, module) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
global.__webpack_public_path__ = publicPath; // eslint-disable-line camelcase
|
||||
const { src, width, height } = this.exec(module, fileName);
|
||||
Reflect.deleteProperty(global, '__webpack_public_path__');
|
||||
|
||||
const targetWidth = Math.ceil(width / scale);
|
||||
const targetHeight = Math.ceil(height / scale);
|
||||
|
||||
resolve(`<img src="${src}" alt="${text}" width="${targetWidth}" height="${targetHeight}" style="vertical-align: middle" />`);
|
||||
});
|
||||
});
|
||||
|
||||
const examine = (key, value) => new Promise((resolve, reject) => {
|
||||
const pathParts = key.split('.');
|
||||
const id = pathParts.pop();
|
||||
const pattern = path.join(ROOT_PATH, pathParts.join('/'), '*.intl.json');
|
||||
// glob always uses linux separators
|
||||
glob(pattern.replace(/\\/g, '/'), (err, matches) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (matches.length === 0) {
|
||||
this.emitWarning(`Unable to find corresponding intl file for ${key} key`);
|
||||
resolve(value);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const path of matches) {
|
||||
const json = JSON.parse(fs.readFileSync(path));
|
||||
const descriptor = json[id];
|
||||
if (!descriptor) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.addDependency(path);
|
||||
|
||||
if (typeof descriptor === 'string') {
|
||||
resolve(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof descriptor !== 'object') {
|
||||
this.emitWarning('Unknown value type');
|
||||
continue;
|
||||
}
|
||||
|
||||
const { type } = descriptor;
|
||||
if (type !== 'text2png') {
|
||||
this.emitWarning(`Unsupported object key type "${type}"`);
|
||||
continue;
|
||||
}
|
||||
|
||||
renderText2Png(key, {
|
||||
...descriptor,
|
||||
text: value,
|
||||
}).then(resolve).catch(reject);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(value);
|
||||
});
|
||||
});
|
||||
|
||||
const json = JSON.parse(content);
|
||||
const result = JSON.stringify(await Object.keys(json).reduce(async (translationsPromise, key) => {
|
||||
const translations = await translationsPromise;
|
||||
translations[key] = await examine(key, json[key]);
|
||||
|
||||
return translations;
|
||||
}, Promise.resolve({})));
|
||||
|
||||
callback(null, `
|
||||
import { defineMessages } from 'react-intl';
|
||||
export default defineMessages(${result});
|
||||
`);
|
||||
};
|
13
webpack-utils/extended-translations-loader/package.json
Normal file
13
webpack-utils/extended-translations-loader/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "extended-translations-loader",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@lesechos/image-size-loader": "file:./../image-size-loader",
|
||||
"glob": "^7.1.4",
|
||||
"loader-utils": "^1.2.3",
|
||||
"text2png-loader": "file:./../text2png-loader",
|
||||
"tmp": "^0.0.33",
|
||||
"qs": "^6.0.0"
|
||||
}
|
||||
}
|
108
webpack-utils/image-size-loader/index.js
Normal file
108
webpack-utils/image-size-loader/index.js
Normal file
@@ -0,0 +1,108 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const sizeOf = require('image-size');
|
||||
const loaderUtils = require('loader-utils');
|
||||
const validateOptions = require('schema-utils');
|
||||
|
||||
const schema = require('./options.json');
|
||||
|
||||
function imageToString(image) {
|
||||
return `
|
||||
module.exports = {
|
||||
src: ${image.src},
|
||||
width: ${JSON.stringify(image.width)},
|
||||
height: ${JSON.stringify(image.height)},
|
||||
bytes: ${JSON.stringify(image.bytes)},
|
||||
type: ${JSON.stringify(image.type)},
|
||||
};
|
||||
// For requires from CSS when used with webpack css-loader,
|
||||
// outputting an Object doesn't make sense,
|
||||
// So overriding the toString method to output just the URL
|
||||
module.exports.toString = function() {
|
||||
return ${image.src};
|
||||
};
|
||||
`;
|
||||
}
|
||||
|
||||
module.exports = function(content) {
|
||||
if (!this.emitFile) {
|
||||
throw new Error('File Loader\n\nemitFile is required from module system');
|
||||
}
|
||||
|
||||
const options = loaderUtils.getOptions(this) || {};
|
||||
|
||||
validateOptions(schema, options, 'File Loader');
|
||||
|
||||
const context = options.context || this.rootContext || (this.options && this.options.context);
|
||||
|
||||
const url = loaderUtils.interpolateName(this, options.name, {
|
||||
context,
|
||||
content,
|
||||
regExp: options.regExp,
|
||||
});
|
||||
|
||||
let image;
|
||||
if (this.resourcePath) {
|
||||
image = sizeOf(this.resourcePath);
|
||||
image.bytes = fs.statSync(this.resourcePath).size;
|
||||
} else {
|
||||
image = sizeOf(content);
|
||||
image.bytes = content.byteLength;
|
||||
}
|
||||
|
||||
let outputPath = url;
|
||||
|
||||
if (options.outputPath) {
|
||||
if (typeof options.outputPath === 'function') {
|
||||
outputPath = options.outputPath(url);
|
||||
} else {
|
||||
outputPath = path.posix.join(options.outputPath, url);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.useRelativePath) {
|
||||
const filePath = this.resourcePath;
|
||||
|
||||
const issuer = options.context ? context : this._module && this._module.issuer && this._module.issuer.context;
|
||||
|
||||
const relativeUrl = issuer && path
|
||||
.relative(issuer, filePath)
|
||||
.split(path.sep)
|
||||
.join('/');
|
||||
|
||||
const relativePath = relativeUrl && `${path.dirname(relativeUrl)}/`;
|
||||
// eslint-disable-next-line no-bitwise
|
||||
if (~relativePath.indexOf('../')) {
|
||||
outputPath = path.posix.join(outputPath, relativePath, url);
|
||||
} else {
|
||||
outputPath = path.posix.join(relativePath, url);
|
||||
}
|
||||
}
|
||||
|
||||
let publicPath = `__webpack_public_path__ + ${JSON.stringify(outputPath)}`;
|
||||
|
||||
if (options.publicPath) {
|
||||
if (typeof options.publicPath === 'function') {
|
||||
publicPath = options.publicPath(url);
|
||||
} else if (options.publicPath.endsWith('/')) {
|
||||
publicPath = options.publicPath + url;
|
||||
} else {
|
||||
publicPath = `${options.publicPath}/${url}`;
|
||||
}
|
||||
|
||||
publicPath = JSON.stringify(publicPath);
|
||||
}
|
||||
|
||||
image.src = publicPath;
|
||||
|
||||
// eslint-disable-next-line no-undefined
|
||||
if (options.emitFile === undefined || options.emitFile) {
|
||||
this.emitFile(outputPath, content);
|
||||
}
|
||||
|
||||
return imageToString(image);
|
||||
};
|
||||
|
||||
module.exports.raw = true;
|
19
webpack-utils/image-size-loader/options.json
Normal file
19
webpack-utils/image-size-loader/options.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {},
|
||||
"regExp": {},
|
||||
"context": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicPath": {},
|
||||
"outputPath": {},
|
||||
"useRelativePath": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"emitFile": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
13
webpack-utils/image-size-loader/package.json
Normal file
13
webpack-utils/image-size-loader/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@lesechos/image-size-loader",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"peerDependencies": {
|
||||
"webpack": "^2.0.0 || ^3.0.0 || ^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"image-size": "^0.6.3",
|
||||
"loader-utils": "^1.1.0",
|
||||
"schema-utils": "^1.0.0"
|
||||
}
|
||||
}
|
@@ -10,16 +10,25 @@ module.exports = function(input) {
|
||||
|
||||
const json = JSON.parse(input);
|
||||
const result = JSON.stringify(Object.keys(json).reduce((translations, key) => {
|
||||
translations[key] = {
|
||||
id: `${moduleId}.${key}`,
|
||||
defaultMessage: json[key],
|
||||
};
|
||||
const value = json[key];
|
||||
const id = `${moduleId}.${key}`;
|
||||
if (typeof value === 'object') {
|
||||
translations[key] = {
|
||||
...value,
|
||||
id,
|
||||
};
|
||||
} else {
|
||||
translations[key] = {
|
||||
id,
|
||||
defaultMessage: value,
|
||||
};
|
||||
}
|
||||
|
||||
return translations;
|
||||
}, {}));
|
||||
|
||||
return `
|
||||
import { defineMessages } from 'react-intl';
|
||||
export default defineMessages(${result})
|
||||
export default defineMessages(${result});
|
||||
`;
|
||||
};
|
||||
|
16
webpack-utils/text2png-loader/index.js
Normal file
16
webpack-utils/text2png-loader/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/* eslint-env node */
|
||||
|
||||
const { getOptions } = require('loader-utils');
|
||||
const text2png = require('text2png');
|
||||
|
||||
module.exports = function() {
|
||||
this.cacheable && this.cacheable();
|
||||
|
||||
const { text, ...options } = getOptions(this);
|
||||
if (!text) {
|
||||
this.emitError('The text param is required');
|
||||
return '';
|
||||
}
|
||||
|
||||
return text2png(text, options);
|
||||
};
|
9
webpack-utils/text2png-loader/package.json
Normal file
9
webpack-utils/text2png-loader/package.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "text2png-loader",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"text2png": "^2.1.0",
|
||||
"loader-utils": "^1.2.3"
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user