Возможность импортить классы иконок через sass

This commit is contained in:
SleepWalker 2016-01-08 11:42:25 +02:00
parent f48533b70f
commit 86e691491a
8 changed files with 279 additions and 21 deletions

View File

@ -44,7 +44,6 @@
"eslint": "^1.10.3",
"eslint-plugin-react": "^3.13.1",
"extract-text-webpack-plugin": "^0.9.1",
"fontgen-loader": "^0.1.9",
"html-webpack-plugin": "^1.7.0",
"imports-loader": "^0.6.4",
"karma": "*",

View File

@ -1,12 +0,0 @@
import icons from 'icons.font.json';
const baseClass = 'icon';
export default Object.keys(icons)
.filter((icon) => icon !== baseClass)
.reduce((acc, icon) => {
acc[icon.replace(`${baseClass}-`, '')] = `${icons[baseClass]} ${icons[icon]}`;
return acc;
}, {})
;

View File

@ -0,0 +1,10 @@
@import '~icons.font.json';
.arrowLeft {
composes: arrow;
display: inline-block;
transform: rotate(90deg);
font-size: 24px;
}

25
src/icons.css.hbs Normal file
View File

@ -0,0 +1,25 @@
@font-face {
font-family: "{{fontName}}";
src: {{{src}}};
}
.{{baseClass}} {
line-height: 1;
}
.{{baseClass}}:before {
font-family: {{fontName}} !important;
font-style: normal;
font-weight: normal !important;
vertical-align: top;
}
{{#each codepoints}}
.{{../classPrefix}}{{@key}} {
composes: {{../baseClass}};
&:before {
content: "\\{{this}}";
}
}
{{/each}}

View File

@ -1,6 +1,9 @@
{
"files": ["icons/webfont/*"],
"fileName": "[fontname][ext]",
"fontName": "ely-account-icons",
"cssTemplate": "icons.css.hbs",
"classPrefix": "",
"fixedWidth": true,
"normalize": true
}

View File

@ -1,10 +1,12 @@
/* eslint-env node */
var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var cssnano = require('cssnano');
var iconfontImporter = require('./webpack/node-sass-iconfont-importer');
const API_HOST = 'http://account.l';
@ -21,8 +23,6 @@ var isTest = process.argv.some(function(arg) {
return arg.indexOf('karma') !== -1;
});
const CSS_LOADER = 'style!css?modules&importLoaders=2&localIdentName=[path][name]-[local]!postcss';
var webpackConfig = {
entry: {
app: path.join(__dirname, 'src'),
@ -66,6 +66,7 @@ var webpackConfig = {
devtool: isTest ? 'inline-source-map' : 'eval',
plugins: [
new iconfontImporter.Plugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(isProduction ? 'production' : 'dev')
@ -96,7 +97,7 @@ var webpackConfig = {
{
test: /\.scss$/,
extractInProduction: true,
loader: CSS_LOADER + '!sass'
loader: 'style!css?modules&localIdentName=[path][name]-[local]!postcss!sass'
},
{
test: /\.jsx?$/,
@ -106,15 +107,17 @@ var webpackConfig = {
presets: ['react', 'es2015', 'stage-0'],
plugins: ['transform-runtime', ['react-intl', {messagesDir: './dist/messages/'}]]
}
},
{
test: /\.font.(js|json)$/,
extractInProduction: true,
loader: CSS_LOADER + '!fontgen?types=woff,eot,ttf'
}
]
},
sassLoader: {
importer: iconfontImporter({
test: /\.font.(js|json)$/,
types: ['woff', 'eot', 'ttf']
})
},
postcss: [
cssnano({
sourcemap: !isProduction,

View File

@ -0,0 +1,217 @@
var path = require("path");
var glob = require('glob');
var loaderUtils = require("loader-utils");
var fontgen = require("webfonts-generator");
module.exports = function createImporter(options) {
return function(url, fileContext, done) {
if (options.test.test(url)) {
var context = this.options.includePaths;
var request = loaderUtils.urlToRequest(url);
compiler.resolvers.normal.resolve(context, request, function(err, resourcePath) {
if (err) return done(new Error(err));
var context = path.dirname(resourcePath);
var config = require(resourcePath);
generate(
config,
options,
resourcePath,
context,
function(err, content) {
if (err) return done(new Error(err));
done({
contents: content
});
}
);
});
} else {
done(false);
}
};
}
var Plugin = module.exports.Plugin = function() {};
Plugin.prototype.apply = function(compiler) {
setCompiler(compiler); // inject compiler to use in importer
compiler.plugin("emit", function(compilation, callback) {
Object.keys(emitQueue).forEach(function(url) {
compilation.assets[url] = emitQueue[url];
});
callback();
});
};
var compiler;
function setCompiler(instance) {
// inject compiler instance for importer function
compiler = instance;
}
var emitQueue = {};
var RawSource = require('webpack-sources').RawSource;
function emitFile(url, content) {
// TODO: support multiple plugin instances?
emitQueue[url] = new RawSource(content);
}
/**
* Partially modified code of fontgen goes next
*/
var mimeTypes = {
'eot': 'application/vnd.ms-fontobject',
'svg': 'image/svg+xml',
'ttf': 'application/x-font-ttf',
'woff': 'application/font-woff'
};
function absolute(from, to) {
if (arguments.length < 2) {
return function (to) {
return path.resolve(from, to);
};
}
return path.resolve(from, to);
}
function getFilesAndDeps(patterns, context) {
var files = [];
var filesDeps = [];
var directoryDeps = [];
function addFile(file) {
filesDeps.push(file);
files.push(absolute(context, file));
}
function addByGlob(globExp) {
var globOptions = {cwd: context};
var foundFiles = glob.sync(globExp, globOptions);
files = files.concat(foundFiles.map(absolute(context)));
var globDirs = glob.sync(path.dirname(globExp) + '/', globOptions);
directoryDeps = directoryDeps.concat(globDirs.map(absolute(context)));
}
// Re-work the files array.
patterns.forEach(function (pattern) {
if (glob.hasMagic(pattern)) {
addByGlob(pattern);
}
else {
addFile(pattern);
}
});
return {
files: files,
dependencies: {
directories: directoryDeps,
files: filesDeps
}
};
}
function generate(config, params, resourcePath, context, cb) {
config.__dirname = path.dirname(resourcePath);
var filesAndDeps = getFilesAndDeps(config.files, context);
config.files = filesAndDeps.files;
// With everything set up, let's make an ACTUAL config.
var formats = params.types || ['eot', 'woff', 'ttf', 'svg'];
if (formats.constructor !== Array) {
formats = [formats];
}
var fontconf = {
files: config.files,
fontName: config.fontName,
types: formats,
order: formats,
fontHeight: config.fontHeight || 1000, // Fixes conversion issues with small svgs
templateOptions: {
baseClass: config.baseClass || "icon",
classPrefix: 'classPrefix' in config ? config.classPrefix : "icon-"
},
rename: (typeof config.rename == "function" ? config.rename : function (f) {
return path.basename(f, ".svg");
}),
dest: "",
writeFiles: false
};
if (config.cssTemplate) {
fontconf.cssTemplate = absolute(context, config.cssTemplate);
}
for (var option in config.templateOptions) {
if (config.templateOptions.hasOwnProperty(option)) {
fontconf.templateOptions[option] = config.templateOptions[option];
}
}
// svgicons2svgfont stuff
var keys = [
"fixedWidth",
"centerHorizontally",
"normalize",
"fontHeight",
"round",
"descent"
];
for (var x in keys) {
if (typeof config[keys[x]] != "undefined") {
fontconf[keys[x]] = config[keys[x]];
}
}
var pub = compiler.options.output.publicPath || '/';
var embed = !!params.embed;
fontgen(fontconf, function (err, res) {
if (err) {
return cb(err);
}
var urls = {};
for (var i in formats) {
var format = formats[i];
if (!embed) {
var filename = config.fileName || params.fileName || "[hash]-[fontname][ext]";
filename = filename
.replace("[fontname]", fontconf.fontName)
.replace("[ext]", "." + format);
var url = loaderUtils.interpolateName(this,
filename,
{
context: params.context || this.context,
content: res[format]
}
);
urls[format] = path.join(pub, url).replace(/\\/g, '/');
emitFile(url, res[format]);
} else {
urls[format] = 'data:'
+ mimeTypes[format]
+ ';charset=utf-8;base64,'
+ (new Buffer(res[format]).toString('base64'));
}
}
cb(null, res.generateCss(urls));
});
};

13
webpack/package.json Normal file
View File

@ -0,0 +1,13 @@
{
"name": "webpack",
"version": "1.0.0",
"description": "",
"keywords": [],
"author": "",
"dependencies": {
"glob": "^6.0.3",
"loader-utils": "^0.2.12",
"webfonts-generator": "^0.3.1",
"webpack-sources": "^0.1.0"
}
}