From 9ac68f6445a1a39abe2c7be13afdaaeb675fa935 Mon Sep 17 00:00:00 2001 From: Dmitry Maganov Date: Fri, 13 Sep 2019 06:50:11 +0300 Subject: [PATCH] feat: add rule to get generated html tags from js --- README.md | 28 ++ src/index.js | 9 + src/rule.js | 36 +++ test/fixtures/rule.js | 1 + test/rule.test.js | 58 +++++ test/snapshots/rule.test.js.md | 434 +++++++++++++++++++++++++++++++ test/snapshots/rule.test.js.snap | Bin 0 -> 4107 bytes test/util.js | 2 +- 8 files changed, 567 insertions(+), 1 deletion(-) create mode 100644 src/rule.js create mode 100644 test/fixtures/rule.js create mode 100644 test/rule.test.js create mode 100644 test/snapshots/rule.test.js.md create mode 100644 test/snapshots/rule.test.js.snap diff --git a/README.md b/README.md index 465def27..4359f25d 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,34 @@ plugins: [ ], ``` +### Rule + +A Webpack loader rule is also provided to make the generated HTML tags available to your JS app. + +```js +const plugin = new WebappWebpackPlugin({ logo: '/path/to/logo.png' }); + +... + +plugins: [ + plugin +] +module: { + rules: [ + plugin.rule() + ], +} +``` + +```js +// now inside Webpack bundle +// you can require logo path +// and get an array of strings with html tags +const favicons = require( '/path/to/logo.png' ); +``` + +> **Note**: `logo` must be an absolute path for `rule` to work. + ### Compilation Modes Modes allow you to choose a very fast simplified favicon compilation or a production ready favicon compilation diff --git a/src/index.js b/src/index.js index 8f668351..6c17b196 100644 --- a/src/index.js +++ b/src/index.js @@ -2,6 +2,7 @@ const assert = require('assert'); const child = require('./compiler'); const Oracle = require('./oracle'); const { tap, tapHtml, getAssetPath } = require('./compat'); +const rule = require('./rule'); const path = require('path'); const crypto = require('crypto'); @@ -17,6 +18,12 @@ module.exports = class FaviconsWebpackPlugin { favicons: {}, prefix: 'assets/', }, options); + + this.tags = rule.tags(); + } + + rule() { + return rule.rule(this); } apply(compiler) { @@ -70,6 +77,8 @@ module.exports = class FaviconsWebpackPlugin { faviconCompilation = this.generateFaviconsWebapp(compiler, compilation); } + faviconCompilation.then(this.tags.resolve, this.tags.reject); + // Hook into the html-webpack-plugin processing and add the html tapHtml(compilation, 'FaviconsWebpackPlugin', (htmlPluginData, htmlWebpackPluginCallback) => { faviconCompilation.then((tags) => { diff --git a/src/rule.js b/src/rule.js new file mode 100644 index 00000000..15ce2c6a --- /dev/null +++ b/src/rule.js @@ -0,0 +1,36 @@ +const assert = require('assert'); +const path = require('path'); + +module.exports = function loader() { + const callback = this.async(); + const { plugin } = this.query; + + plugin.tags.promise.then((tags) => { + callback(null, `module.exports = ['${tags.join("', '")}'];`); + }, callback); +}; + +module.exports.tags = () => { + const tags = {}; + + tags.promise = new Promise((resolve, reject) => { + tags.resolve = resolve; + tags.reject = reject; + }); + + tags.promise.catch(() => {}); + + return tags; +}; + +module.exports.rule = (plugin) => { + assert(path.isAbsolute(plugin.options.logo), '`logo` must be an absolute path'); + + const rule = { + include: plugin.options.logo, + loader: __filename, + options: { plugin }, + }; + + return rule; +}; diff --git a/test/fixtures/rule.js b/test/fixtures/rule.js new file mode 100644 index 00000000..9504ffb1 --- /dev/null +++ b/test/fixtures/rule.js @@ -0,0 +1 @@ +module.exports = require('./logo.png'); diff --git a/test/rule.test.js b/test/rule.test.js new file mode 100644 index 00000000..afb613d6 --- /dev/null +++ b/test/rule.test.js @@ -0,0 +1,58 @@ +const test = require('ava'); +const path = require('path'); +const fs = require('fs-extra'); +const FaviconsWebpackPlugin = require('../'); + +const { logo, mkdir, generate, snapshotCompilationAssets } = require('./util'); + +test.beforeEach(async t => t.context.root = await mkdir()); + +test('should fail to generate rule with relative logo', t => { + const plugin = new FaviconsWebpackPlugin({ logo: './path' }); + try { + plugin.rule(); + } catch (err) { + t.is(err.message, '`logo` must be an absolute path'); + } +}); + +test('should fail to generate rule with module logo', t => { + const plugin = new FaviconsWebpackPlugin({ logo: 'path' }); + try { + plugin.rule(); + } catch (err) { + t.is(err.message, '`logo` must be an absolute path'); + } +}); + +test('should succeed to generate rule with absolute logo', t => { + const plugin = new FaviconsWebpackPlugin({ logo: '/path' }); + const rule = plugin.rule(); + t.pass(); +}); + +test('should generate working rule for getting favicon tags', async t => { + const dist = path.join(t.context.root, 'dist'); + const plugin = new FaviconsWebpackPlugin({ logo }); + const rule = plugin.rule(); + + const compilationStats = await generate({ + entry: path.resolve(__dirname, 'fixtures/rule.js'), + context: t.context.root, + output: { + path: dist, + filename: 'main.jsx', + libraryTarget: 'commonjs2', + }, + plugins: [ + plugin, + ], + module: { + rules: [rule], + }, + }); + + snapshotCompilationAssets(t, compilationStats); +}); + +test.afterEach(t => fs.remove(t.context.root)); diff --git a/test/snapshots/rule.test.js.md b/test/snapshots/rule.test.js.md new file mode 100644 index 00000000..34655f38 --- /dev/null +++ b/test/snapshots/rule.test.js.md @@ -0,0 +1,434 @@ +# Snapshot report for `test/rule.test.js` + +The actual snapshot is saved in `rule.test.js.snap`. + +Generated by [AVA](https://ava.li). + +## should generate working rule for getting favicon tags + +> Snapshot 1 + + [ + 'assets/android-chrome-144x144.png', + 'assets/android-chrome-192x192.png', + 'assets/android-chrome-256x256.png', + 'assets/android-chrome-36x36.png', + 'assets/android-chrome-384x384.png', + 'assets/android-chrome-48x48.png', + 'assets/android-chrome-512x512.png', + 'assets/android-chrome-72x72.png', + 'assets/android-chrome-96x96.png', + 'assets/apple-touch-icon-1024x1024.png', + 'assets/apple-touch-icon-114x114.png', + 'assets/apple-touch-icon-120x120.png', + 'assets/apple-touch-icon-144x144.png', + 'assets/apple-touch-icon-152x152.png', + 'assets/apple-touch-icon-167x167.png', + 'assets/apple-touch-icon-180x180.png', + 'assets/apple-touch-icon-57x57.png', + 'assets/apple-touch-icon-60x60.png', + 'assets/apple-touch-icon-72x72.png', + 'assets/apple-touch-icon-76x76.png', + 'assets/apple-touch-icon-precomposed.png', + 'assets/apple-touch-icon.png', + 'assets/apple-touch-startup-image-1182x2208.png', + 'assets/apple-touch-startup-image-1242x2148.png', + 'assets/apple-touch-startup-image-1496x2048.png', + 'assets/apple-touch-startup-image-1536x2008.png', + 'assets/apple-touch-startup-image-320x460.png', + 'assets/apple-touch-startup-image-640x1096.png', + 'assets/apple-touch-startup-image-640x920.png', + 'assets/apple-touch-startup-image-748x1024.png', + 'assets/apple-touch-startup-image-750x1294.png', + 'assets/apple-touch-startup-image-768x1004.png', + 'assets/browserconfig.xml', + 'assets/coast-228x228.png', + 'assets/favicon-16x16.png', + 'assets/favicon-32x32.png', + 'assets/favicon.ico', + 'assets/firefox_app_128x128.png', + 'assets/firefox_app_512x512.png', + 'assets/firefox_app_60x60.png', + 'assets/manifest.json', + 'assets/manifest.webapp', + 'assets/mstile-144x144.png', + 'assets/mstile-150x150.png', + 'assets/mstile-310x150.png', + 'assets/mstile-310x310.png', + 'assets/mstile-70x70.png', + 'assets/yandex-browser-50x50.png', + 'assets/yandex-browser-manifest.json', + 'main.jsx', + ] + +> Snapshot 2 + + [ + { + assetName: 'assets/android-chrome-144x144.png', + content: 'binary', + }, + { + assetName: 'assets/android-chrome-192x192.png', + content: 'binary', + }, + { + assetName: 'assets/android-chrome-256x256.png', + content: 'binary', + }, + { + assetName: 'assets/android-chrome-36x36.png', + content: 'binary', + }, + { + assetName: 'assets/android-chrome-384x384.png', + content: 'binary', + }, + { + assetName: 'assets/android-chrome-48x48.png', + content: 'binary', + }, + { + assetName: 'assets/android-chrome-512x512.png', + content: 'binary', + }, + { + assetName: 'assets/android-chrome-72x72.png', + content: 'binary', + }, + { + assetName: 'assets/android-chrome-96x96.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-icon-1024x1024.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-icon-114x114.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-icon-120x120.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-icon-144x144.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-icon-152x152.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-icon-167x167.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-icon-180x180.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-icon-57x57.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-icon-60x60.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-icon-72x72.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-icon-76x76.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-icon-precomposed.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-icon.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-startup-image-1182x2208.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-startup-image-1242x2148.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-startup-image-1496x2048.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-startup-image-1536x2008.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-startup-image-320x460.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-startup-image-640x1096.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-startup-image-640x920.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-startup-image-748x1024.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-startup-image-750x1294.png', + content: 'binary', + }, + { + assetName: 'assets/apple-touch-startup-image-768x1004.png', + content: 'binary', + }, + { + assetName: 'assets/browserconfig.xml', + content: 'binary', + }, + { + assetName: 'assets/coast-228x228.png', + content: 'binary', + }, + { + assetName: 'assets/favicon-16x16.png', + content: 'binary', + }, + { + assetName: 'assets/favicon-32x32.png', + content: 'binary', + }, + { + assetName: 'assets/favicon.ico', + content: 'binary', + }, + { + assetName: 'assets/firefox_app_128x128.png', + content: 'binary', + }, + { + assetName: 'assets/firefox_app_512x512.png', + content: 'binary', + }, + { + assetName: 'assets/firefox_app_60x60.png', + content: 'binary', + }, + { + assetName: 'assets/manifest.json', + content: `{␊ + "name": null,␊ + "short_name": null,␊ + "description": null,␊ + "dir": "auto",␊ + "lang": "en-US",␊ + "display": "standalone",␊ + "orientation": "any",␊ + "start_url": "/?homescreen=1",␊ + "background_color": "#fff",␊ + "theme_color": "#fff",␊ + "icons": [␊ + {␊ + "src": "/assets/android-chrome-36x36.png",␊ + "sizes": "36x36",␊ + "type": "image/png"␊ + },␊ + {␊ + "src": "/assets/android-chrome-48x48.png",␊ + "sizes": "48x48",␊ + "type": "image/png"␊ + },␊ + {␊ + "src": "/assets/android-chrome-72x72.png",␊ + "sizes": "72x72",␊ + "type": "image/png"␊ + },␊ + {␊ + "src": "/assets/android-chrome-96x96.png",␊ + "sizes": "96x96",␊ + "type": "image/png"␊ + },␊ + {␊ + "src": "/assets/android-chrome-144x144.png",␊ + "sizes": "144x144",␊ + "type": "image/png"␊ + },␊ + {␊ + "src": "/assets/android-chrome-192x192.png",␊ + "sizes": "192x192",␊ + "type": "image/png"␊ + },␊ + {␊ + "src": "/assets/android-chrome-256x256.png",␊ + "sizes": "256x256",␊ + "type": "image/png"␊ + },␊ + {␊ + "src": "/assets/android-chrome-384x384.png",␊ + "sizes": "384x384",␊ + "type": "image/png"␊ + },␊ + {␊ + "src": "/assets/android-chrome-512x512.png",␊ + "sizes": "512x512",␊ + "type": "image/png"␊ + }␊ + ]␊ + }`, + }, + { + assetName: 'assets/manifest.webapp', + content: 'binary', + }, + { + assetName: 'assets/mstile-144x144.png', + content: 'binary', + }, + { + assetName: 'assets/mstile-150x150.png', + content: 'binary', + }, + { + assetName: 'assets/mstile-310x150.png', + content: 'binary', + }, + { + assetName: 'assets/mstile-310x310.png', + content: 'binary', + }, + { + assetName: 'assets/mstile-70x70.png', + content: 'binary', + }, + { + assetName: 'assets/yandex-browser-50x50.png', + content: 'binary', + }, + { + assetName: 'assets/yandex-browser-manifest.json', + content: `{␊ + "version": "1.0",␊ + "api_version": 1,␊ + "layout": {␊ + "logo": "/assets/yandex-browser-50x50.png",␊ + "color": "#fff",␊ + "show_title": true␊ + }␊ + }`, + }, + { + assetName: 'main.jsx', + content: `module.exports =␊ + /******/ (function(modules) { // webpackBootstrap␊ + /******/ // The module cache␊ + /******/ var installedModules = {};␊ + /******/␊ + /******/ // The require function␊ + /******/ function __webpack_require__(moduleId) {␊ + /******/␊ + /******/ // Check if module is in cache␊ + /******/ if(installedModules[moduleId]) {␊ + /******/ return installedModules[moduleId].exports;␊ + /******/ }␊ + /******/ // Create a new module (and put it into the cache)␊ + /******/ var module = installedModules[moduleId] = {␊ + /******/ i: moduleId,␊ + /******/ l: false,␊ + /******/ exports: {}␊ + /******/ };␊ + /******/␊ + /******/ // Execute the module function␊ + /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);␊ + /******/␊ + /******/ // Flag the module as loaded␊ + /******/ module.l = true;␊ + /******/␊ + /******/ // Return the exports of the module␊ + /******/ return module.exports;␊ + /******/ }␊ + /******/␊ + /******/␊ + /******/ // expose the modules object (__webpack_modules__)␊ + /******/ __webpack_require__.m = modules;␊ + /******/␊ + /******/ // expose the module cache␊ + /******/ __webpack_require__.c = installedModules;␊ + /******/␊ + /******/ // define getter function for harmony exports␊ + /******/ __webpack_require__.d = function(exports, name, getter) {␊ + /******/ if(!__webpack_require__.o(exports, name)) {␊ + /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });␊ + /******/ }␊ + /******/ };␊ + /******/␊ + /******/ // define __esModule on exports␊ + /******/ __webpack_require__.r = function(exports) {␊ + /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {␊ + /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });␊ + /******/ }␊ + /******/ Object.defineProperty(exports, '__esModule', { value: true });␊ + /******/ };␊ + /******/␊ + /******/ // create a fake namespace object␊ + /******/ // mode & 1: value is a module id, require it␊ + /******/ // mode & 2: merge all properties of value into the ns␊ + /******/ // mode & 4: return value when already ns object␊ + /******/ // mode & 8|1: behave like require␊ + /******/ __webpack_require__.t = function(value, mode) {␊ + /******/ if(mode & 1) value = __webpack_require__(value);␊ + /******/ if(mode & 8) return value;␊ + /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;␊ + /******/ var ns = Object.create(null);␊ + /******/ __webpack_require__.r(ns);␊ + /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });␊ + /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));␊ + /******/ return ns;␊ + /******/ };␊ + /******/␊ + /******/ // getDefaultExport function for compatibility with non-harmony modules␊ + /******/ __webpack_require__.n = function(module) {␊ + /******/ var getter = module && module.__esModule ?␊ + /******/ function getDefault() { return module['default']; } :␊ + /******/ function getModuleExports() { return module; };␊ + /******/ __webpack_require__.d(getter, 'a', getter);␊ + /******/ return getter;␊ + /******/ };␊ + /******/␊ + /******/ // Object.prototype.hasOwnProperty.call␊ + /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };␊ + /******/␊ + /******/ // __webpack_public_path__␊ + /******/ __webpack_require__.p = "";␊ + /******/␊ + /******/␊ + /******/ // Load entry module and return exports␊ + /******/ return __webpack_require__(__webpack_require__.s = 0);␊ + /******/ })␊ + /************************************************************************/␊ + /******/ ([␊ + /* 0 */␊ + /***/ (function(module, exports, __webpack_require__) {␊ + ␊ + module.exports = __webpack_require__(1);␊ + ␊ + ␊ + /***/ }),␊ + /* 1 */␊ + /***/ (function(module, exports) {␊ + ␊ + module.exports = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''];␊ + ␊ + /***/ })␊ + /******/ ]);`, + }, + ] diff --git a/test/snapshots/rule.test.js.snap b/test/snapshots/rule.test.js.snap new file mode 100644 index 0000000000000000000000000000000000000000..f1b564c375b8d41c152c543ff6f1c647bebf1571 GIT binary patch literal 4107 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 { const assetNames = Object.keys(assets).sort(); // Check if all files are generated correctly t.snapshot(assetNames); - const textFiles = /\.(json|html?)$/; + const textFiles = /\.(json|html?|jsx)$/; // CSS and JS files are not touched by this plugin // therefore those files are excluded from snapshots const ignoredFiles = /\.(js|css)$/;