diff --git a/package.json b/package.json index ea3efbb..321f5dc 100644 --- a/package.json +++ b/package.json @@ -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": "*", diff --git a/src/components/ui/icons.js b/src/components/ui/icons.js deleted file mode 100644 index 077c740..0000000 --- a/src/components/ui/icons.js +++ /dev/null @@ -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; - }, {}) - ; diff --git a/src/components/ui/icons.scss b/src/components/ui/icons.scss new file mode 100644 index 0000000..58793c8 --- /dev/null +++ b/src/components/ui/icons.scss @@ -0,0 +1,10 @@ +@import '~icons.font.json'; + +.arrowLeft { + composes: arrow; + + display: inline-block; + transform: rotate(90deg); + + font-size: 24px; +} diff --git a/src/icons.css.hbs b/src/icons.css.hbs new file mode 100644 index 0000000..8867089 --- /dev/null +++ b/src/icons.css.hbs @@ -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}} diff --git a/src/icons.font.json b/src/icons.font.json index 5f3c2ed..813c9f3 100644 --- a/src/icons.font.json +++ b/src/icons.font.json @@ -1,6 +1,9 @@ { "files": ["icons/webfont/*"], + "fileName": "[fontname][ext]", "fontName": "ely-account-icons", + "cssTemplate": "icons.css.hbs", + "classPrefix": "", "fixedWidth": true, "normalize": true } diff --git a/webpack.config.js b/webpack.config.js index 5de2781..6ad1215 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -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, diff --git a/webpack/node-sass-iconfont-importer.js b/webpack/node-sass-iconfont-importer.js new file mode 100644 index 0000000..b82b2cd --- /dev/null +++ b/webpack/node-sass-iconfont-importer.js @@ -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)); + }); +}; diff --git a/webpack/package.json b/webpack/package.json new file mode 100644 index 0000000..49d5370 --- /dev/null +++ b/webpack/package.json @@ -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" + } +}