diff --git a/lib/index.js b/lib/index.js index abb9d85..e061a00 100644 --- a/lib/index.js +++ b/lib/index.js @@ -10,9 +10,15 @@ const RawSource = require('webpack-sources').RawSource const FONT_REGEX = /\.(eot|ttf|svg|woff|woff2)$/ const TEXT_REGEX = /\.(js|css|html)$/ const GLYPH_REGEX = /content\s*:[^};]*?('|")(.*?)\s*('|"|;)/g -const UNICODE_REGEX = /\\(\w{4})/ +const UNICODE_REGEX = /\\([0-9a-f]{4,6})/i const FONTMIN_EXTENSIONS = ['eot', 'woff', 'svg'] +function getSurrogatePair(astralCodePoint) { + let highSurrogate = Math.floor((astralCodePoint - 0x10000) / 0x400) + 0xd800 + let lowSurrogate = ((astralCodePoint - 0x10000) % 0x400) + 0xdc00 + return [highSurrogate, lowSurrogate] +} + class FontminPlugin { constructor(options) { this._options = _.assign( @@ -105,7 +111,15 @@ class FontminPlugin { return matches .map(match => { const unicodeMatch = match.match(UNICODE_REGEX) - return unicodeMatch ? String.fromCharCode(parseInt(unicodeMatch[1], 16)) : false + if (!unicodeMatch) return false + const unicodeHex = unicodeMatch[1] + const numericValue = parseInt(unicodeHex, 16) + const character = String.fromCharCode(numericValue) + if (unicodeHex.length === 4) return character + const multiCharacter = getSurrogatePair(numericValue) + .map(v => String.fromCharCode(v)) + .join('') + return multiCharacter }) .filter(Boolean) }) @@ -173,13 +187,15 @@ class FontminPlugin { const glyphs = this.computeFinalGlyphs(glyphsInCss) return minifiableFonts.reduce((prev, group) => { - return prev.then(() => this.minifyFontGroup(group, glyphs)).then(files => { - files.forEach(file => { - if (file.buffer.length > file.minified.length) { - compilation.assets[file.asset] = new RawSource(file.minified) - } + return prev + .then(() => this.minifyFontGroup(group, glyphs)) + .then(files => { + files.forEach(file => { + if (file.buffer.length > file.minified.length) { + compilation.assets[file.asset] = new RawSource(file.minified) + } + }) }) - }) }, Promise.resolve()) } diff --git a/package.json b/package.json index 371676c..06edd62 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "webpack": "5.x" }, "devDependencies": { + "@mdi/font": "^5.9.55", "@patrickhulce/lint": "^3.0.2", "chai": "^4.3.0", "css-loader": "^5.0.2", diff --git a/test/fixtures/entry-utf32.js b/test/fixtures/entry-utf32.js new file mode 100644 index 0000000..0f2df0c --- /dev/null +++ b/test/fixtures/entry-utf32.js @@ -0,0 +1 @@ +require('@mdi/font/css/materialdesignicons.min.css') diff --git a/test/fixtures/webpack.unicode.config.js b/test/fixtures/webpack.unicode.config.js new file mode 100644 index 0000000..976ef0b --- /dev/null +++ b/test/fixtures/webpack.unicode.config.js @@ -0,0 +1,14 @@ +const FontminPlugin = require('../../lib') + +module.exports = { + entry: `${__dirname}/entry-utf32.js`, + output: {filename: 'out.js', path: `${__dirname}/dist`, publicPath: '/test/fixtures/dist/'}, + module: { + rules: [ + {test: /\.(woff|woff2)(\?v=.+)?$/, use: ['file-loader']}, + {test: /\.(svg|ttf|eot|png)(\?v=.+)?$/, use: ['file-loader']}, + {test: /\.css$/, use: ['style-loader', 'css-loader']}, + ], + }, + plugins: [new FontminPlugin()], +} diff --git a/test/index.test.js b/test/index.test.js index 1a19818..19777e7 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -14,6 +14,7 @@ describe('FontminPlugin', () => { let fontStats const baseConfig = require('./fixtures/webpack.config.js') const baseExtractConfig = require('./fixtures/webpack.extract-text.config.js') + const baseUnicodeConfig = require('./fixtures/webpack.unicode.config.js') const originalStats = collectFontStats(FONT_AWESOME_FOLDER + '/fonts', { 'fontawesome-webfont.eot': true, 'fontawesome-webfont.ttf': true, @@ -60,7 +61,7 @@ describe('FontminPlugin', () => { } describe('FontAwesome micro', () => { - it('should run successfully', function (done) { + it('should run successfully', function(done) { this.timeout(10000) const plugin = new Plugin({autodetect: false, glyphs: '\uF0C7'}) const config = _.cloneDeep(baseConfig) @@ -109,7 +110,7 @@ describe('FontminPlugin', () => { }) describe('FontAwesome inferred', () => { - it('should run successfully', function (done) { + it('should run successfully', function(done) { this.timeout(60000) testWithConfig(baseConfig, done) }) @@ -127,7 +128,7 @@ describe('FontminPlugin', () => { }) describe('FontAwesome full', () => { - it('should run successfully', function (done) { + it('should run successfully', function(done) { this.timeout(60000) const plugin = new Plugin({autodetect: false}) const config = _.cloneDeep(baseConfig) @@ -144,7 +145,7 @@ describe('FontminPlugin', () => { }) describe('FontAwesome with ExtractTextPlugin', () => { - it('should run successfully', function (done) { + it('should run successfully', function(done) { this.timeout(60000) testWithConfig(baseExtractConfig, done) }) @@ -195,4 +196,13 @@ describe('FontminPlugin', () => { expect(glyphs).to.contain('remove') }) }) + + describe('FontAwesome with multi-byte unicode characters', () => { + it('should run successfully', function(done) { + this.timeout(60000) + testWithConfig(baseUnicodeConfig, done) + }) + + after(done => rimraf(DIST_FOLDER, done)) + }) }) diff --git a/yarn.lock b/yarn.lock index 17d8fae..2561f24 100644 --- a/yarn.lock +++ b/yarn.lock @@ -63,6 +63,11 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752" integrity sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg== +"@mdi/font@^5.9.55": + version "5.9.55" + resolved "https://registry.yarnpkg.com/@mdi/font/-/font-5.9.55.tgz#41acd50b88073ded7095fc3029d8712b6e12f38e" + integrity sha512-jswRF6q3eq8NWpWiqct6q+6Fg/I7nUhrxYJfiEM8JJpap0wVJLQdbKtyS65GdlK7S7Ytnx3TTi/bmw+tBhkGmg== + "@patrickhulce/lint@^3.0.2": version "3.0.2" resolved "https://registry.yarnpkg.com/@patrickhulce/lint/-/lint-3.0.2.tgz#6b4fd8030bfad70df33dcf19a88a0176f23ff83d"