Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for publicPath == /, allow override in loader query string #5

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
330 changes: 167 additions & 163 deletions lib/font-loader.js
Original file line number Diff line number Diff line change
@@ -1,191 +1,195 @@

var _ = require('lodash'),
Promise = require('bluebird'),
path = require('path'),
fs = require('fs'),
loaderUtils = require('loader-utils'),
multiplex = require('option-multiplexer'),
ttf2eot = require('ttf2eot'),
ttf2woff = require('ttf2woff'),
svg2ttf = require('svg2ttf');
Promise = require('bluebird'),
path = require('path'),
fs = require('fs'),
loaderUtils = require('loader-utils'),
multiplex = require('option-multiplexer'),
ttf2eot = require('ttf2eot'),
ttf2woff = require('ttf2woff'),
svg2ttf = require('svg2ttf');

var template = _.template(fs.readFileSync(path.join(
__dirname, '..', 'share', 'font.template'
__dirname, '..', 'share', 'font.template'
)));

var extensions = {
'.woff': 'woff',
'.ttf': 'truetype',
'.eot': 'embedded-opentype',
'.svg': 'svg',
'.otf': 'opentype'
'.woff': 'woff',
'.ttf': 'truetype',
'.eot': 'embedded-opentype',
'.svg': 'svg',
'.otf': 'opentype'
};

var convertors = {
'svg': {
'truetype': function(font, data) {
return svg2ttf(data, { }).buffer;
}
},
'truetype': {
'woff': function(font, data) {
return ttf2woff(data, { }).buffer;
},
'embedded-opentype': function(font, data) {
return ttf2eot(data, { }).buffer;
},
'opentype': function(font, data) {
return data;
}
},
'opentype': {
'woff': function(font, data) {
return ttf2woff(data, { }).buffer;
},
'embedded-opentype': function(font, data) {
return ttf2eot(data, { }).buffer;
},
'truetype': function(font, data) {
return data;
}
}
'svg': {
'truetype': function(font, data) {
return svg2ttf(data, { }).buffer;
}
},
'truetype': {
'woff': function(font, data) {
return ttf2woff(data, { }).buffer;
},
'embedded-opentype': function(font, data) {
return ttf2eot(data, { }).buffer;
},
'opentype': function(font, data) {
return data;
}
},
'opentype': {
'woff': function(font, data) {
return ttf2woff(data, { }).buffer;
},
'embedded-opentype': function(font, data) {
return ttf2eot(data, { }).buffer;
},
'truetype': function(font, data) {
return data;
}
}
};

var formats = _.invert(extensions);

function getDefaultFormat(ext) {
return extensions[ext];
return extensions[ext];
}

function getExtension(format) {
return formats[format];
return formats[format];
}

function createTargets(source, options) {
options = _.defaults(_.pick(options, 'weight', 'style', 'format'), {
weight: _.chain(source).pluck('weight').uniq().value(),
style: _.chain(source).pluck('style').uniq().value(),
format: _.chain(source).pluck('format').uniq().value()
});
return multiplex(options);
options = _.defaults(_.pick(options, 'weight', 'style', 'format'), {
weight: _.chain(source).pluck('weight').uniq().value(),
style: _.chain(source).pluck('style').uniq().value(),
format: _.chain(source).pluck('format').uniq().value()
});
return multiplex(options);
}

function groupFaces(meta, fonts) {
return _.chain(fonts)
.groupBy(function(font) {
return JSON.stringify(_.pick(font, 'weight', 'style'))
}).map(function(members, key) {
var props = JSON.parse(key);
return _.assign(props, {
name: meta.name,
files: members
});
})
.value();
return _.chain(fonts)
.groupBy(function(font) {
return JSON.stringify(_.pick(font, 'weight', 'style'))
}).map(function(members, key) {
var props = JSON.parse(key);
return _.assign(props, {
name: meta.name,
files: members
});
})
.value();
}

module.exports = function(input) {


var _this = this,
globalQuery = loaderUtils.parseQuery(this.query),
localQuery = loaderUtils.parseQuery(this.resourceQuery),
query = _.assign({ }, globalQuery, localQuery),
base = this.context,
callback = this.async();

// Since queries are strings, need to turn weights to ints to get them
// matched properly
if (query.weight) {
if (!_.isArray(query.weight)) {
query.weight = [ query.weight ];
}
query.weight = _.map(query.weight, function(value) {
return parseInt(value, 10);
});
}

if (query.style) {
if (!_.isArray(query.style)) {
query.style = [ query.style ];
}
}

function interpolateName(font) {
var name = [
_.kebabCase(meta.name),
font.style,
font.weight
].join('-') + '.[hash:8]' + getExtension(font.format);

// TODO: Should this be globalQuery or localQuery?
return loaderUtils.interpolateName(_this, name, {
context: globalQuery.context || _this.options.context,
content: font.data,
regExp: globalQuery.regExp
});
}

function emit(font) {
var name = interpolateName(font);
_this.emitFile(name, font.data);
return name;
}

this.cacheable();

// WOW THIS IS HACKY
if (/\.(css|sass|scss)$/.test(this.resourcePath)) {
callback(null, input);
return;
}

var meta = JSON.parse(input);
var targets, results;

function defaults(file) {
_.defaults(file, {
weight: 500,
format: getDefaultFormat(path.extname(file.file)),
style: 'regular',
data: new Promise(function filePromise(resolve, reject) {
fs.readFile(path.join(base, file.file), function fileLoaded(err, data) {
_this.dependency(file.file);
return err ? reject(err) : resolve(data);
});
})
});
}

_.forEach(meta.files, defaults);
targets = createTargets(meta.files, query);

results = _.map(targets, function processTarget(target) {
var search = _.pick(target, 'weight', 'style'),
source = _.find(meta.files, search);

if (!source) {
return Promise.reject('No matching source to ' + query + '.');
}
return source.data.then(function dataLoaded(data) {
return _.assign({
data: source.format === target.format ?
data :
convertors[source.format][target.format](target, data)
}, target);
}).then(function emitFont(font) {
font.file = emit(font);
return font;
});
});

Promise.all(results).then(function fontsGenerated(fonts) {
var faces = groupFaces(meta, fonts);
callback(null, template({
faces: faces,
publicPath: _this.options.output.publicPath
}));
}).catch(function errored(err) {
callback(err);
});
var _this = this,
globalQuery = loaderUtils.parseQuery(this.query),
localQuery = loaderUtils.parseQuery(this.resourceQuery),
query = _.assign({ }, globalQuery, localQuery),
base = this.context,
callback = this.async();

// Since queries are strings, need to turn weights to ints to get them
// matched properly
if (query.weight) {
if (!_.isArray(query.weight)) {
query.weight = [ query.weight ];
}
query.weight = _.map(query.weight, function(value) {
return parseInt(value, 10);
});
}

if (query.style) {
if (!_.isArray(query.style)) {
query.style = [ query.style ];
}
}

function interpolateName(font) {
var name = [
_.kebabCase(meta.name),
font.style,
font.weight
].join('-') + '.[hash:8]' + getExtension(font.format);

// TODO: Should this be globalQuery or localQuery?
return loaderUtils.interpolateName(_this, name, {
context: globalQuery.context || _this.options.context,
content: font.data,
regExp: globalQuery.regExp
});
}

function emit(font) {
var name = interpolateName(font);
_this.emitFile(name, font.data);
return name;
}

this.cacheable();

// WOW THIS IS HACKY
if (/\.(css|sass|scss)$/.test(this.resourcePath)) {
callback(null, input);
return;
}

var meta = JSON.parse(input);
var targets, results;

function defaults(file) {
_.defaults(file, {
weight: 500,
format: getDefaultFormat(path.extname(file.file)),
style: 'regular',
data: new Promise(function filePromise(resolve, reject) {
var filePath = path.join(base, file.file);

_this.addDependency(filePath);

fs.readFile(filePath, function fileLoaded(err, data) {
return err ? reject(err) : resolve(data);
});
})
});
}

_.forEach(meta.files, defaults);
targets = createTargets(meta.files, query);

results = _.map(targets, function processTarget(target) {
var search = _.pick(target, 'weight', 'style');
var source = _.find(meta.files, search);

if(!source) {
return Promise.reject('No matching source to ' + query + '.');
}

return source.data.then(function dataLoaded(data) {
return _.assign({
data: source.format === target.format ?
data :
convertors[source.format][target.format](target, data)
}, target);
}).then(function emitFont(font) {
font.file = emit(font);
return font;
});
});

Promise.all(results).then(function fontsGenerated(fonts) {
var faces = groupFaces(meta, fonts);
var publicPath = _this.options.output.publicPath || query.basePath || '/';

callback(null, template({
faces: faces,
publicPath: publicPath
}));
}).catch(function errored(err) {
callback(err);
});
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "font-loader",
"version": "0.1.2",
"version": "0.1.3",
"description": "Fonts with webpack.",
"author": "Izaak Schroeder <izaak.schroeder@gmail.com>",
"main": "lib/font-loader.js",
Expand Down
10 changes: 5 additions & 5 deletions share/font.template
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<% _.forEach(faces, function(face) { %>
@font-face {
font-family: "<%= face.name %>";
font-weight: <%= face.weight %>;
font-style: <%= face.style %>;
<% if (eot = _.find(face.files, { format: 'embedded-opentype' })) { %>src: url("/<%= eot.file %>");<% } %>
src: <% _.forEach(face.files, function(file, i, files) { %>url("<%= publicPath %>/<%= file.file %>") format("<%= file.format %>")<%if (i+1 < files.length) {%>, <%}%><% }); %>;
font-family: "<%= face.name %>";
font-weight: <%= face.weight %>;
font-style: <%= face.style %>;
<% if (eot = _.find(face.files, { format: 'embedded-opentype' })) { %>src: url("/<%= eot.file %>");<% } %>
src: <% _.forEach(face.files, function(file, i, files) { %>url("<%= publicPath %><%= file.file %>") format("<%= file.format %>")<%if (i+1 < files.length) {%>, <%}%><% }); %>;
}
<% }); %>