Repository: arthurbergmz/webpack-pwa-manifest Branch: master Commit: aaf3c4199ee2 Files: 69 Total size: 55.6 KB Directory structure: gitextract_ds0fadw8/ ├── .babelrc ├── .eslintrc.js ├── .gitignore ├── .travis.yml ├── .vscode/ │ └── launch.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.d.ts ├── index.js ├── package.json ├── src/ │ ├── errors/ │ │ ├── IconError.js │ │ └── PresetError.js │ ├── generators/ │ │ ├── legacy.js │ │ └── tapable.js │ ├── helpers/ │ │ ├── except.js │ │ ├── fingerprint.js │ │ └── uri.js │ ├── icons/ │ │ └── index.js │ ├── index.js │ ├── injector/ │ │ └── index.js │ └── validators/ │ ├── colors.js │ ├── presets.js │ └── versioning.js └── tests/ ├── basic/ │ ├── .gitignore │ ├── app.js │ ├── build/ │ │ └── webpack.config.js │ └── test/ │ ├── index.html │ ├── main.5c6b192511a888bcb780.bundle.js │ └── manifest.b71cbf253213cc7cedb8b846c4e8ea0a.json ├── complex/ │ ├── .gitignore │ ├── app.js │ ├── build/ │ │ └── webpack.config.js │ └── test/ │ ├── index.html │ ├── main.a328bb93659dd721d1d6.bundle.js │ └── manifest.aa33c28d24e1121925cfa150e2ec52a9.json ├── fingerprints-false/ │ ├── .gitignore │ ├── app.js │ ├── build/ │ │ └── webpack.config.js │ └── test/ │ ├── index.html │ ├── main.bundle.js │ └── manifest.json ├── index.html ├── index.js ├── issue-84/ │ ├── .gitignore │ ├── app.js │ ├── build/ │ │ └── webpack.config.js │ └── test/ │ ├── index.html │ ├── main.159ccf53b959ede39a2e.bundle.js │ └── manifest.81200e1d24c270411245b5a63a4b91e7.json ├── issue-87/ │ ├── .gitignore │ ├── app.js │ ├── build/ │ │ └── webpack.config.js │ └── test/ │ ├── index.html │ ├── main.2f76de9afb439f1fcb16.bundle.js │ └── manifest.24abfacd4846fab249f8fe94967260b9.json ├── rgb-background/ │ ├── .gitignore │ ├── app.js │ ├── build/ │ │ └── webpack.config.js │ └── test/ │ ├── index.html │ ├── main.fcb65b68a23c92e495a7.bundle.js │ └── manifest.836308dd4105e7779e96d575bbcb3984.json ├── rgba-background/ │ ├── .gitignore │ ├── app.js │ ├── build/ │ │ └── webpack.config.js │ └── test/ │ ├── index.html │ ├── main.a772dd109d73b782cf7a.bundle.js │ └── manifest.82c3c467821f8c1b613bc6f741480697.json └── runTest.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": [ "@babel/preset-env" ] } ================================================ FILE: .eslintrc.js ================================================ module.exports = { root: true, parser: "babel-eslint", rules: { strict: 0 } } ================================================ FILE: .gitignore ================================================ .DS_Store node_modules/ npm-debug.log* dist/ ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - "8" - "10" - "node" ================================================ FILE: .vscode/launch.json ================================================ { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Run test debugging", "skipFiles": [ "/**" ], "program": "${workspaceFolder}/tests/index.js" } ] } ================================================ FILE: CHANGELOG.md ================================================ ## 3.6.0 (2018-02-27) - Added Webpack 4 support. ## 3.5.0 (2018-02-13) - Added filename template. Thanks to @kevinchappell ! See more information at [pull request #34](https://github.com/arthurbergmz/webpack-pwa-manifest/pull/34). - Added SVG support. Thanks to @acostalima ! See more information at [pull request #49](https://github.com/arthurbergmz/webpack-pwa-manifest/pull/49). - Fixed recursive filename. Thanks to @satazor ! See more information at [pull request #50](https://github.com/arthurbergmz/webpack-pwa-manifest/pull/50). ## 3.4.1 (2017-12-22) - Fixed file convention. See more information at [pull request #31](https://github.com/arthurbergmz/webpack-pwa-manifest/pull/31). ## 3.4.0 (2017-12-22) - Added `includeDirectory`. See more information at this [issue #30](https://github.com/arthurbergmz/webpack-pwa-manifest/issues/30) and this [pull request #29](https://github.com/arthurbergmz/webpack-pwa-manifest/pull/29). ## 3.3.1/3.3.2 (2017-09-18) - Fixed URI generation. See more information at [issue #23](https://github.com/arthurbergmz/webpack-pwa-manifest/issues/23). ## 3.3.0 (2017-09-12) - Added 'publicPath' option. See more information at [issue #20](https://github.com/arthurbergmz/webpack-pwa-manifest/issues/20). ## 3.2.0 (2017-08-18) - Added Apple's Web Application injection. See more information at [issue #13](https://github.com/arthurbergmz/webpack-pwa-manifest/issues/13); - Added `theme-color` injection. See more information at [issue #18](https://github.com/arthurbergmz/webpack-pwa-manifest/issues/18); - Added `ios` property into options. - Added `ios` property into icon options. - See README.md for detailed information about the new `ios` property. ## 3.1.6 (2017-07-28) - Fixed misbehavior with protocols' slashes. See more information at [issue #16](https://github.com/arthurbergmz/webpack-pwa-manifest/issues/16). ## 3.1.5 (2017-07-19) - Fixed excessive "forward" slashes and backslashes. See more information at [issue #15](https://github.com/arthurbergmz/webpack-pwa-manifest/issues/15). ## 3.1.4 (2017-07-19) - Fixed excessive slashes on output. See more information at [issue #15](https://github.com/arthurbergmz/webpack-pwa-manifest/issues/15). ## 3.1.2 (2017-07-13) - Fixed resources URI on Windows. See more information at [issue #14](https://github.com/arthurbergmz/webpack-pwa-manifest/issues/14). ## 3.1.1 (2017-07-12) - `useWebpackPublicPath` is now deprecated. See more information at [issue #12](https://github.com/arthurbergmz/webpack-pwa-manifest/issues/12). ## 3.1.0 (2017-07-03) - Added `useWebpackPublicPath` into options; - Removed _HtmlWebpackPlugin_ technical dependency (misbehavior). ## 3.0.0 (2017-06-13) - Refactored code; **(ES6+ ready)** - Added HTML injection support through [_HtmlWebpackPlugin_](https://github.com/jantimon/html-webpack-plugin); - Added file fingerprinting; - Faster hot reload support. ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2020 Arthur Arioli Bergamaschi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # webpack-pwa-manifest > Looking for people willing to help! [More info](https://github.com/arthurbergmz/webpack-pwa-manifest/issues/119) `webpack-pwa-manifest` is a webpack plugin that generates a 'manifest.json' for your Progressive Web Application, with auto icon resizing and fingerprinting support. If you are using `inject` on your configuration, ensure that [`HtmlWebpackPlugin`](https://github.com/jantimon/html-webpack-plugin) appears *before* `WebpackPwaManifest` in the `plugins` array! # features ✔ Auto icon resizing ✔ Icon fingerprinting ✔ Manifest fingerprinting ✔ Auto manifest injection on HTML ✔ Hot Reload support ✔ ES6+ ready # install ```javascript npm install --save-dev webpack-pwa-manifest ``` # usage In your `webpack.config.js`: ```javascript // ES6+ import WebpackPwaManifest from 'webpack-pwa-manifest' // ES5 var WebpackPwaManifest = require('webpack-pwa-manifest') ... plugins: [ new WebpackPwaManifest({ name: 'My Progressive Web App', short_name: 'MyPWA', description: 'My awesome Progressive Web App!', background_color: '#ffffff', crossorigin: 'use-credentials', //can be null, use-credentials or anonymous icons: [ { src: path.resolve('src/assets/icon.png'), sizes: [96, 128, 192, 256, 384, 512] // multiple sizes }, { src: path.resolve('src/assets/large-icon.png'), size: '1024x1024' // you can also use the specifications pattern }, { src: path.resolve('src/assets/maskable-icon.png'), size: '1024x1024', purpose: 'maskable' } ] }) ] ``` # output `manifest..json` ```json { "name": "My Progressive Web App", "orientation": "portrait", "display": "standalone", "start_url": ".", "short_name": "MyPWA", "description": "My awesome Progressive Web App!", "background_color": "#ffffff", "icons": [ { "src": "icon_1024x1024..png", "sizes": "1024x1024", "type": "image/png", "purpose": "maskable" }, { "src": "icon_1024x1024..png", "sizes": "1024x1024", "type": "image/png" }, { "src": "icon_512x512..png", "sizes": "512x512", "type": "image/png" }, { "src": "icon_384x384..png", "sizes": "384x384", "type": "image/png" }, { "src": "icon_256x256..png", "sizes": "256x256", "type": "image/png" }, { "src": "icon_192x192..png", "sizes": "192x192", "type": "image/png" }, { "src": "icon_128x128..png", "sizes": "128x128", "type": "image/png" }, { "src": "icon_96x96..png", "sizes": "96x96", "type": "image/png" } ] } ``` # API ### WebpackPwaManifest([options]) **options** Type: `object` You can follow the [Web App Manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest) [specification](https://w3c.github.io/manifest/). The difference here is that, when defining icons, you can specify one icon with multiple sizes, using an array of integers, just as the example above. You can also change the output's filename with the `filename` property. Presets of `options`: ```javascript { filename: "manifest.json", name: "App", orientation: "portrait", display: "standalone", start_url: ".", crossorigin: null, inject: true, fingerprints: true, ios: false, publicPath: null, includeDirectory: true } ``` By default, HTML injection and fingerprint generation are on. With `inject: false` and `fingerprints: false`, respectively, you can turn them off. If `inject: true` and `'theme-color'` property is not defined, it wil try to use `theme_color` as default. Otherwise, no `theme-color` meta tag will be injected. With `includeDirectory: true`, we will use `filename`'s directory to export the manifest file. With `orientation: 'omit'`, the orientation key will be omitted from the generated manifest file. When `inject: true` and `ios: true`, specific Apple meta tags will be injected to the HTML code when possible, as requested at [issue #13](https://github.com/arthurbergmz/webpack-pwa-manifest/issues/13). You can see Apple's [Configuring Web Application](https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html) for more information. Instead of using a boolean value, you can also use an object to specify certain link or meta tag, for instance: ```javascript ... ios: { 'apple-mobile-web-app-title': 'AppTitle', 'apple-mobile-web-app-status-bar-style': 'black' } ``` If `publicPath` option is not given, this plugin fallbacks to [Webpack's public path](https://webpack.js.org/configuration/output/#output-publicpath) definition. When defining an icon object, you can also specify its output directory using a property called `destination`. Using `ios: true` in an icon object makes it eligible to the `apple-touch-icon` meta tag injection. Using `ios: 'startup'` in an icon object makes it eligible to the `apple-touch-startup-image` meta tag injection. ```javascript ... icons: [ { src: path.resolve('src/assets/icons/ios-icon.png'), sizes: [120, 152, 167, 180, 1024], destination: path.join('icons', 'ios'), ios: true }, { src: path.resolve('src/assets/icons/ios-icon.png'), size: 1024, destination: path.join('icons', 'ios'), ios: 'startup' }, { src: path.resolve('src/assets/icons/android-icon.png'), sizes: [36, 48, 72, 96, 144, 192, 512], destination: path.join('icons', 'android') } ] } ``` If you specify a valid `crossorigin` property it will be added to the `` in the HTML document. This property determines if the request for the manifest includes CORS headers and is required if the manifest is located on a different domain or requires authentication. ================================================ FILE: index.d.ts ================================================ // Type definitions for webpack-pwa-manifest 2.0.4 // Project: https://github.com/arthurbergmz/webpack-pwa-manifest // Definitions by: Arthur A. Bergamaschi import { Plugin } from 'webpack'; export = WebpackPwaManifest declare class WebpackPwaManifest extends Plugin { constructor(options: WebpackPwaManifest.ManifestOptions); } declare namespace WebpackPwaManifest { type Direction = 'ltr' | 'rtl' | 'auto'; type Display = 'fullscreen' | 'standalone' | 'minimal-ui' | 'browser'; type Orientation = 'any' | 'natural' | 'landscape' | 'landscape-primary' | 'landscape-secondary' | 'portrait' | 'portrait-primary' | 'portrait-secondary'; type CrossOrigin = 'use-credentials' | 'anonymous'; interface ManifestOptions { background_color?: string; description?: string; dir?: Direction; display?: Display; fingerprints?: boolean; filename?: string; icons?: Icon | Icon[]; inject?: boolean; lang?: string; name: string; orientation?: Orientation; publicPath?: string; prefer_related_applications?: boolean; related_applications?: RelatedApplications[]; scope?: string; short_name?: string; start_url?: string; theme_color?: string; 'theme-color'?: string; ios?: boolean | IosOptions; crossorigin?: CrossOrigin; } interface RelatedApplications { platform?: string; url: string; id?: string; } interface IosOptions { 'apple-touch-icon'?: string | IosAppleTouchIcon; 'apple-touch-startup-image'?: string; 'apple-mobile-web-app-title'?: string; 'apple-mobile-web-app-capable'?: 'yes' | 'no' | boolean; 'apple-mobile-web-app-status-bar-style'?: 'default' | 'black' | 'black-translucent'; } interface IosAppleTouchIcon { sizes?: string | number; href: string; } interface Icon { src: string; size?: string | number; sizes?: number[]; destination?: string; ios?: boolean | 'default' | 'startup'; purpose?: string; type?: string; } } ================================================ FILE: index.js ================================================ module.exports = require('./dist/index.js') ================================================ FILE: package.json ================================================ { "name": "webpack-pwa-manifest", "version": "4.3.0", "description": "Progressive Web Application (PWA) Manifest Generator", "main": "index.js", "types": "index.d.ts", "repository": { "type": "git", "url": "https://github.com/arthurbergmz/webpack-pwa-manifest.git" }, "keywords": [ "pwa", "manifest", "gen", "generator", "webpack", "webapp", "manifest.json", "progressive", "web", "app", "application" ], "scripts": { "build": "npx babel src --out-dir dist", "clean": "rimraf -rf tests/*/output && rimraf -rf dist", "clean:build": "npm run clean && npm run build", "test": "npm run clean:build && node tests/index.js", "test:debug": "npm run clean:build && node --inspect-brk ./tests/index.js" }, "author": "Arthur A. Bergamaschi ", "license": "MIT", "dependencies": { "css-color-names": "1.0.1", "jimp": "0.16.1", "mime": "2.4.6" }, "devDependencies": { "@babel/cli": "7.12.1", "@babel/core": "7.12.3", "@babel/preset-env": "7.12.1", "babel-eslint": "10.1.0", "eslint": "7.13.0", "html-webpack-plugin": "4.5.0", "list-directory-contents": "0.0.3", "rimraf": "3.0.2", "webpack": "5.5.0", "webpack-cli": "4.2.0" }, "files": [ "dist", "CHANGELOG.md", "index.d.ts", "index.js", "LICENSE", "package-lock.json", "package.json", "README.md" ], "engines": { "node": ">=6.0.0" } } ================================================ FILE: src/errors/IconError.js ================================================ export default class IconError extends Error { constructor (msg) { super(msg) this.name = this.constructor.name } } ================================================ FILE: src/errors/PresetError.js ================================================ export default class PresetError extends Error { constructor (key, value) { super(`Unknown value of "${key}": ${value}`) this.name = this.constructor.name } } ================================================ FILE: src/generators/legacy.js ================================================ import { buildResources, injectResources, generateHtmlTags, generateAppleTags, generateMaskIconLink, applyTag } from '../injector' module.exports = function (that, compiler) { compiler.plugin('compilation', (compilation) => { compilation.plugin('html-webpack-plugin-before-html-processing', function (htmlPluginData, callback) { if (!that.htmlPlugin) that.htmlPlugin = true buildResources(that, that.options.publicPath || compilation.options.output.publicPath, () => { if (that.options.inject) { let tags = generateAppleTags(that.options, that.assets) const themeColorTag = { name: 'theme-color', content: that.options['theme-color'] || that.options.theme_color } if (themeColorTag.content) applyTag(tags, 'meta', themeColorTag) applyTag(tags, 'link', Object.assign({ rel: 'manifest', href: that.manifest.url }, !!that.options.crossorigin && { crossorigin: that.options.crossorigin })) tags = generateMaskIconLink(tags, that.assets) htmlPluginData.html = htmlPluginData.html.replace(/(<\/head>)/i, `${generateHtmlTags(tags)}`) } callback(null, htmlPluginData) }) }) }) compiler.plugin('emit', (compilation, callback) => { if (that.htmlPlugin) { injectResources(compilation, that.assets, callback) } else { buildResources(that, that.options.publicPath || compilation.options.output.publicPath, () => { injectResources(compilation, that.assets, callback) }) } }) } ================================================ FILE: src/generators/tapable.js ================================================ import { buildResources, injectResources, generateHtmlTags, generateAppleTags, generateMaskIconLink, applyTag } from '../injector' let HtmlWebpackPlugin; try { HtmlWebpackPlugin = require('html-webpack-plugin'); } catch (e) { if (process.env.NODE_ENV === 'development') { console.log('it seems like you are not using html-webpack-plugin'); } } finally {} function getBeforeProcessingHook(compilation) { if (!compilation.hooks || !compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing) { return (HtmlWebpackPlugin && HtmlWebpackPlugin.getHooks) ? HtmlWebpackPlugin.getHooks(compilation).beforeEmit : null; } return compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing; } module.exports = function (that, { hooks: { compilation: comp, emit } }) { comp.tap('webpack-pwa-manifest', (compilation) => { const beforeProcessingHook = getBeforeProcessingHook(compilation); if (!beforeProcessingHook) return; beforeProcessingHook.tapAsync('webpack-pwa-manifest', function(htmlPluginData, callback) { if (!that.htmlPlugin) that.htmlPlugin = true buildResources(that, that.options.publicPath || compilation.options.output.publicPath, () => { if (that.options.inject) { let tags = generateAppleTags(that.options, that.assets) const themeColorTag = { name: 'theme-color', content: that.options['theme-color'] || that.options.theme_color } if (themeColorTag.content) applyTag(tags, 'meta', themeColorTag) applyTag(tags, 'link', Object.assign({ rel: 'manifest', href: that.manifest.url }, !!that.options.crossorigin && { crossorigin: that.options.crossorigin })) tags = generateMaskIconLink(tags, that.assets) htmlPluginData.html = htmlPluginData.html.replace(/(<\/head>)/i, `${generateHtmlTags(tags)}`) } callback(null, htmlPluginData) }) }) }) emit.tapAsync('webpack-pwa-manifest', (compilation, callback) => { if (that.htmlPlugin) { injectResources(compilation, that.assets, callback) } else { buildResources(that, that.options.publicPath || compilation.options.output.publicPath, () => { injectResources(compilation, that.assets, callback) }) } }) } ================================================ FILE: src/helpers/except.js ================================================ export default function (obj, properties) { obj = obj || {} const response = {} if (typeof properties === 'string') properties = properties.split(/\s+/) for (const i in obj) if (properties.indexOf(i) === -1) response[i] = obj[i] return response } ================================================ FILE: src/helpers/fingerprint.js ================================================ import crypto from 'crypto' export default function (input) { return crypto.createHash('md5').update(input).digest('hex') } ================================================ FILE: src/helpers/uri.js ================================================ export function joinURI (...arr) { const first = arr[0] || '' const join = arr.join('/') return normalizeURI(join[0] === '/' && first[0] !== '/' ? join.substring(1) : join) } export function normalizeURI (uri) { return uri.replace(/(:\/\/)|(\\+|\/{2,})+/g, match => match === '://' ? '://' : '/') } ================================================ FILE: src/icons/index.js ================================================ import fs from 'fs' import jimp from 'jimp' import mime from 'mime' import { joinURI } from '../helpers/uri' import generateFingerprint from '../helpers/fingerprint' import IconError from '../errors/IconError' const supportedMimeTypes = [jimp.MIME_PNG, jimp.MIME_JPEG, jimp.MIME_BMP] function parseArray (i) { return i && !Array.isArray(i) ? [i] : i } function sanitizeIcon (iconSnippet) { if (!iconSnippet.src) throw new IconError('Unknown icon source.') const arr = parseArray(iconSnippet.size || iconSnippet.sizes) if (!arr) throw new IconError('Unknown icon sizes.') const sizes = [] for (let size of arr) sizes.push(+size || parseInt(size)) return { src: iconSnippet.src, sizes, destination: iconSnippet.destination, ios: iconSnippet.ios || false, color: iconSnippet.color, purpose: iconSnippet.purpose } } function processIcon (currentSize, icon, buffer, mimeType, publicPath, shouldFingerprint) { const dimensions = `${currentSize}x${currentSize}` const fileName = shouldFingerprint ? `icon_${dimensions}.${generateFingerprint(buffer)}.${mime.getExtension(mimeType)}` : `icon_${dimensions}.${mime.getExtension(mimeType)}` const iconOutputDir = icon.destination ? joinURI(icon.destination, fileName) : fileName const iconPublicUrl = joinURI(publicPath, iconOutputDir) return { manifestIcon: { src: iconPublicUrl, sizes: dimensions, type: mimeType, purpose: icon.purpose }, webpackAsset: { output: iconOutputDir, url: iconPublicUrl, source: buffer, size: buffer.length, ios: icon.ios ? { valid: icon.ios, size: dimensions, href: iconPublicUrl } : false, color: icon.color } } } function process (sizes, icon, cachedIconsCopy, icons, assets, fingerprint, publicPath, callback) { const processNext = function () { if (sizes.length > 0) { return process(sizes, icon, cachedIconsCopy, icons, assets, fingerprint, publicPath, callback) // next size } else if (cachedIconsCopy.length > 0) { const next = cachedIconsCopy.pop() return process(next.sizes, next, cachedIconsCopy, icons, assets, fingerprint, publicPath, callback) // next icon } else { return callback(null, { icons, assets }) // there are no more icons left } } const size = sizes.pop() if (size > 0) { const mimeType = mime.getType(icon.src) if (!supportedMimeTypes.includes(mimeType)) { let buffer try { buffer = fs.readFileSync(icon.src) } catch (err) { throw new IconError(`It was not possible to read '${icon.src}'.`) } const processedIcon = processIcon(size, icon, buffer, mimeType, publicPath, fingerprint) icons.push(processedIcon.manifestIcon) assets.push(processedIcon.webpackAsset) return processNext() } jimp.read(icon.src, (err, img) => { if (err) throw new IconError(`It was not possible to read '${icon.src}'.`) img.resize(size, size).getBuffer(mimeType, (err, buffer) => { if (err) throw new IconError(`It was not possible to retrieve buffer of '${icon.src}'.`) const processedIcon = processIcon(size, icon, buffer, mimeType, publicPath, fingerprint) icons.push(processedIcon.manifestIcon) assets.push(processedIcon.webpackAsset) return processNext() }) }) } } export function retrieveIcons (options) { const icons = parseArray(options.icon || options.icons) const response = [] if (icons) for (let icon of icons) response.push(sanitizeIcon(icon)) delete options.icon delete options.icons return response } export function parseIcons (fingerprint, publicPath, icons, callback) { if (icons.length === 0) { callback(null, {}) } else { const first = icons.pop() process(first.sizes, first, icons, [], [], fingerprint, publicPath, callback) } } ================================================ FILE: src/index.js ================================================ import validatePresets from './validators/presets' import validateColors from './validators/colors' import checkDeprecated from './validators/versioning' class WebpackPwaManifest { constructor (options = {}) { validatePresets(options, 'dir', 'display', 'orientation', 'crossorigin') validateColors(options, 'background_color', 'theme_color') checkDeprecated(options, 'useWebpackPublicPath') this._generator = null this.assets = null this.htmlPlugin = false const shortName = options.short_name || options.name || 'App' // fingerprints is true by default, but we want it to be false even if users // set it to undefined or null. if (!options.hasOwnProperty('fingerprints')) { options.fingerprints = true } this.options = Object.assign({ filename: options.fingerprints ? '[name].[hash].[ext]' : '[name].[ext]', name: 'App', short_name: shortName, orientation: 'portrait', display: 'standalone', start_url: '.', inject: true, fingerprints: true, ios: false, publicPath: null, includeDirectory: true, crossorigin: null }, options) } _acquireGenerator (hooks) { return hooks ? require('./generators/tapable') : require('./generators/legacy') } apply (compiler) { const { hooks } = compiler const generator = this._generator || (this._generator = this._acquireGenerator(hooks)) generator(this, compiler) } } module.exports = WebpackPwaManifest ================================================ FILE: src/injector/index.js ================================================ import path from 'path' import generateFingerprint from '../helpers/fingerprint' import { joinURI } from '../helpers/uri' import { retrieveIcons, parseIcons } from '../icons' import except from '../helpers/except' const voidTags = [ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr' ] const appleTags = { 'apple-touch-icon': 'link', 'apple-touch-startup-image': 'link', 'apple-mobile-web-app-title': 'meta', 'apple-mobile-web-app-capable': 'meta', 'apple-mobile-web-app-status-bar-style': 'meta' } function createFilename (filenameTemplate, json, shouldFingerprint) { const formatters = [{ pattern: /\[hash(:([1-9]|[1-2][0-9]|3[0-2]))?\]/gi, value: (match, limit = ':32') => { if (!shouldFingerprint) return '' const hash = generateFingerprint(json) return hash.substr(0, parseInt(limit.substr(1), 10)) } }, { pattern: /\[ext\]/gi, value: 'json' }, { pattern: /\[name\]/gi, value: 'manifest' }] return formatters.reduce((acc, curr) => acc.replace(curr.pattern, curr.value), filenameTemplate) } function manifest (options, publicPath, icons, callback) { const content = except(Object.assign({ icons }, options), ['filename', 'inject', 'fingerprints', 'ios', 'publicPath', 'icon', 'useWebpackPublicPath', 'includeDirectory', 'crossorigin']) if (options.orientation === 'omit') { delete content.orientation } const json = JSON.stringify(content, null, 2) const file = path.parse(options.filename) const filename = createFilename(file.base, json, options.fingerprints) const output = options.includeDirectory ? path.join(file.dir, filename) : filename callback(null, { output, url: joinURI(publicPath, output), source: json, size: json.length }) } export function buildResources (_this, publicPath, callback) { if (_this.assets && _this.options.inject) { // already cached and ready to inject callback() } else { publicPath = publicPath || '' parseIcons(_this.options.fingerprints, publicPath, retrieveIcons(_this.options), (err, result) => { if (err) return manifest(_this.options, publicPath, result.icons, (fail, manifest) => { if (fail) return _this.manifest = manifest _this.assets = [manifest, ...(result.assets || [])] callback() }) }) } } export function injectResources (compilation, assets, callback) { if (assets) { for (let asset of assets) { compilation.assets[asset.output] = { source: () => asset.source, size: () => asset.size } } } callback() } export function generateAppleTags (options, assets) { let tags = {} if (options.ios) { let apple = Object.assign({ 'apple-mobile-web-app-title': options.name, 'apple-mobile-web-app-capable': 'yes', 'apple-mobile-web-app-status-bar-style': 'default' }, typeof options.ios === 'object' ? options.ios : {}) for (let tag in apple) { const type = appleTags[tag] if (!type) continue // not a valid apple tag applyTag(tags, type, formatAppleTag(tag, apple[tag])) } if (assets) { for (let asset of assets) { if (asset.ios && asset.ios.valid) { if (asset.ios.valid === 'startup') { applyTag(tags, 'link', { rel: 'apple-touch-startup-image', sizes: asset.ios.size, href: asset.ios.href }) } else { applyTag(tags, 'link', { rel: 'apple-touch-icon', sizes: asset.ios.size, href: asset.ios.href }) } } } } } return tags } export function generateMaskIconLink (tags, assets) { const svgAsset = assets.find((asset) => /[^.]+$/.exec(asset.output)[0] === 'svg') if (svgAsset) { applyTag(tags, 'link', Object.assign({ rel: 'mask-icon', href: svgAsset.url }, !!svgAsset.color && { color: svgAsset.color })) } return tags } function formatAppleTag (tag, content) { if (tag === 'apple-touch-icon') { if (typeof content === 'string') { return { rel: tag, href: content } } else { let sizes = content.sizes sizes = +sizes || parseInt(sizes) return isNaN(sizes) ? { rel: tag, href: content.href } : { rel: tag, sizes, href: content.href } } } else if (tag === 'apple-touch-startup-image') { return { rel: tag, href: content } } else if (tag === 'apple-mobile-web-app-title') { return { name: tag, content } } else if (tag === 'apple-mobile-web-app-capable') { let value = content if (typeof content === 'boolean' || typeof content === 'number') value = content ? 'yes' : 'no' return { name: tag, content: value } } else if (tag === 'apple-mobile-web-app-status-bar-style') { return { name: tag, content } } return null } export function applyTag (obj, tag, content) { if (!content) return if (obj[tag]) { if (Array.isArray(obj[tag])) { obj[tag].push(content) } else { obj[tag] = [obj[tag], content] } } else { obj[tag] = content } } export function generateHtmlTags (tags) { let html = '' for (let tag in tags) { const attrs = tags[tag] if (Array.isArray(attrs)) { for (let a of attrs) { html = `${html}${generateHtmlTags({ [tag]: a })}` } } else { html = `${html}<${tag}` for (let attr in attrs) { html = `${html} ${attr}="${attrs[attr]}"` } html = voidTags.indexOf(tag) === -1 ? `${html}>` : `${html} />` } } return html } ================================================ FILE: src/validators/colors.js ================================================ // @ts-ignore import cssColorNames from 'css-color-names' import PresetError from '../errors/PresetError' function isHexColor (color) { return (/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/).test(color) } function isCssColor (color) { return typeof color === 'string' && cssColorNames[color] } function isRgbColor (color) { return (/rgb\([\d]{1,3}, [\d]{1,3}, [\d]{1,3}\)/).test(color) } function isRgbaColor (color) { return (/rgba\([\d]{1,3}, [\d]{1,3}, [\d]{1,3}, \d\.\d+\)/).test(color) } export default function (config, ...properties) { if (!config) return for (let property of properties) { let color = config[property] if (color && !(isHexColor(color) || isCssColor(color) || isRgbColor(color) || isRgbaColor(color))) throw new PresetError(property, color) } } ================================================ FILE: src/validators/presets.js ================================================ import PresetError from '../errors/PresetError' const presets = { dir: ['ltr', 'rtl', 'auto'], orientation: [ 'any', 'natural', 'landscape', 'landscape-primary', 'landscape-secondary', 'portrait', 'portrait-primary', 'portrait-secondary', 'omit' ], display: [ 'fullscreen', 'standalone', 'minimal-ui', 'browser' ], crossorigin: [ 'anonymous', 'use-credentials' ] } function hasPreset (key, value) { return presets[key].indexOf(value) >= 0 } export default function (config, ...properties) { if (!config) return for (let property of properties) { let value = config[property] if (value && !hasPreset(property, value)) throw new PresetError(property, value) } } ================================================ FILE: src/validators/versioning.js ================================================ const deprecated = { useWebpackPublicPath: 'https://github.com/arthurbergmz/webpack-pwa-manifest/issues/12' } export default function (options, ...properties) { for (const property of properties) { if (options[property]) { console.log(`"${property}" is a deprecated option. Read more at "${deprecated[property]}".`) } } } ================================================ FILE: tests/basic/.gitignore ================================================ .DS_Store output/ ================================================ FILE: tests/basic/app.js ================================================ ================================================ FILE: tests/basic/build/webpack.config.js ================================================ const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const WebpackPwaManifest = require('../../../dist') module.exports = { entry: path.join(__dirname, '../app.js'), output: { path: path.join(__dirname, '../output'), publicPath: '/', filename: '[name].[fullhash].bundle.js' }, plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', minify: { minifyCSS: true, minifyJS: true, collapseWhitespace: true, collapseInlineTagWhitespace: true, preserveLineBreaks: false, removeAttributeQuotes: true, removeComments: true } }), new WebpackPwaManifest({ name: 'My Progressive Web App', short_name: 'MyPWA', description: 'My awesome Progressive Web App!', background_color: '#ffffff', ios: true, icons: [ { src: path.resolve('./tests/icon.png'), sizes: 512, destination: 'icons' }, { src: path.resolve('./tests/icon.png'), size: 1024, destination: 'icons', ios: true }, { src: path.resolve('./tests/icon.svg'), sizes: 512, destination: 'icons', color: '#ffffff' } ] }) ] } ================================================ FILE: tests/basic/test/index.html ================================================ Webpack App ================================================ FILE: tests/basic/test/main.5c6b192511a888bcb780.bundle.js ================================================ ================================================ FILE: tests/basic/test/manifest.b71cbf253213cc7cedb8b846c4e8ea0a.json ================================================ { "icons": [ { "src": "/icons/icon_512x512.dd60b11e9762b6e25f6d4ce981a1eeae.svg", "sizes": "512x512", "type": "image/svg+xml" }, { "src": "/icons/icon_1024x1024.35f4891d98af6eb0c3dee2853ea0f190.png", "sizes": "1024x1024", "type": "image/png" }, { "src": "/icons/icon_512x512.d1f0ac39b9300e08b1a811d7919ab7e7.png", "sizes": "512x512", "type": "image/png" } ], "name": "My Progressive Web App", "short_name": "MyPWA", "orientation": "portrait", "display": "standalone", "start_url": ".", "description": "My awesome Progressive Web App!", "background_color": "#ffffff" } ================================================ FILE: tests/complex/.gitignore ================================================ .DS_Store output/ ================================================ FILE: tests/complex/app.js ================================================ ================================================ FILE: tests/complex/build/webpack.config.js ================================================ const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const WebpackPwaManifest = require('../../../dist') module.exports = { entry: path.join(__dirname, '../app.js'), output: { path: path.join(__dirname, '../output'), publicPath: '/', filename: '[name].[fullhash].bundle.js' }, plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', minify: { minifyCSS: true, minifyJS: true, collapseWhitespace: true, collapseInlineTagWhitespace: true, preserveLineBreaks: false, removeAttributeQuotes: true, removeComments: true } }), new WebpackPwaManifest({ name: 'My Progressive Web App', short_name: 'MyPWA', description: 'My awesome Progressive Web App!', background_color: '#ffffff', ios: true, crossorigin: 'use-credentials', icons: [ { src: path.resolve('./tests/icon.png'), sizes: [36, 48, 72, 96, 144, 192, 512], destination: path.join('icons', 'android') }, { src: path.resolve('./tests/icon.png'), size: [120, 152, 167, 180, 1024], destination: path.join('icons', 'ios'), ios: true }, { src: path.resolve('./tests/icon.png'), size: 1024, destination: path.join('icons', 'ios', 'startup'), ios: 'startup' } ] }) ] } ================================================ FILE: tests/complex/test/index.html ================================================ Webpack App ================================================ FILE: tests/complex/test/main.a328bb93659dd721d1d6.bundle.js ================================================ ================================================ FILE: tests/complex/test/manifest.aa33c28d24e1121925cfa150e2ec52a9.json ================================================ { "icons": [ { "src": "/icons/ios/startup/icon_1024x1024.35f4891d98af6eb0c3dee2853ea0f190.png", "sizes": "1024x1024", "type": "image/png" }, { "src": "/icons/ios/icon_1024x1024.35f4891d98af6eb0c3dee2853ea0f190.png", "sizes": "1024x1024", "type": "image/png" }, { "src": "/icons/ios/icon_180x180.ff39561a8e4bf67913868a5caa412712.png", "sizes": "180x180", "type": "image/png" }, { "src": "/icons/ios/icon_167x167.26d2d10196ca0a32493134fb5a4510ba.png", "sizes": "167x167", "type": "image/png" }, { "src": "/icons/ios/icon_152x152.8f7dadbad509ff949e7da5488f44b686.png", "sizes": "152x152", "type": "image/png" }, { "src": "/icons/ios/icon_120x120.463a6f38bcc879bee49facdf208464c4.png", "sizes": "120x120", "type": "image/png" }, { "src": "/icons/android/icon_512x512.d1f0ac39b9300e08b1a811d7919ab7e7.png", "sizes": "512x512", "type": "image/png" }, { "src": "/icons/android/icon_192x192.93b74abaa553cfd33becd35a5af58d64.png", "sizes": "192x192", "type": "image/png" }, { "src": "/icons/android/icon_144x144.d92dfff13d6f324a3929f6ab86b73333.png", "sizes": "144x144", "type": "image/png" }, { "src": "/icons/android/icon_96x96.58231f08c5682276b4fbc4fe6477ace8.png", "sizes": "96x96", "type": "image/png" }, { "src": "/icons/android/icon_72x72.4ec5c9cc751141d69642b825c980fc31.png", "sizes": "72x72", "type": "image/png" }, { "src": "/icons/android/icon_48x48.19f1520677130b1faeefbc6b932d439c.png", "sizes": "48x48", "type": "image/png" }, { "src": "/icons/android/icon_36x36.282e97d80d71ad0b5c87011e6e42cf87.png", "sizes": "36x36", "type": "image/png" } ], "name": "My Progressive Web App", "short_name": "MyPWA", "orientation": "portrait", "display": "standalone", "start_url": ".", "description": "My awesome Progressive Web App!", "background_color": "#ffffff" } ================================================ FILE: tests/fingerprints-false/.gitignore ================================================ .DS_Store output/ ================================================ FILE: tests/fingerprints-false/app.js ================================================ ================================================ FILE: tests/fingerprints-false/build/webpack.config.js ================================================ const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const WebpackPwaManifest = require('../../../dist') module.exports = { entry: path.join(__dirname, '../app.js'), output: { path: path.join(__dirname, '../output'), publicPath: '/', filename: '[name].bundle.js' }, plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', minify: { minifyCSS: true, minifyJS: true, collapseWhitespace: true, collapseInlineTagWhitespace: true, preserveLineBreaks: false, removeAttributeQuotes: true, removeComments: true } }), new WebpackPwaManifest({ name: 'My Progressive Web App', short_name: 'MyPWA', description: 'My awesome Progressive Web App!', background_color: '#ffffff', ios: true, icons: [ { src: path.resolve('./tests/icon.png'), sizes: 512, destination: 'icons' }, { src: path.resolve('./tests/icon.png'), size: 1024, destination: 'icons', ios: true }, { src: path.resolve('./tests/icon.svg'), sizes: 512, destination: 'icons', color: '#ffffff' } ], fingerprints: false }) ] } ================================================ FILE: tests/fingerprints-false/test/index.html ================================================ Webpack App ================================================ FILE: tests/fingerprints-false/test/main.bundle.js ================================================ ================================================ FILE: tests/fingerprints-false/test/manifest.json ================================================ { "icons": [ { "src": "/icons/icon_512x512.svg", "sizes": "512x512", "type": "image/svg+xml" }, { "src": "/icons/icon_1024x1024.png", "sizes": "1024x1024", "type": "image/png" }, { "src": "/icons/icon_512x512.png", "sizes": "512x512", "type": "image/png" } ], "name": "My Progressive Web App", "short_name": "MyPWA", "orientation": "portrait", "display": "standalone", "start_url": ".", "description": "My awesome Progressive Web App!", "background_color": "#ffffff" } ================================================ FILE: tests/index.html ================================================ Document

Hello, world!

================================================ FILE: tests/index.js ================================================ const runTest = require('./runTest') const tests = [ 'basic', 'complex', 'fingerprints-false', 'issue-84', 'issue-87', 'rgb-background', 'rgba-background' ] console.log('Running tests...') runTest(tests.shift(), tests) ================================================ FILE: tests/issue-84/.gitignore ================================================ .DS_Store output/ ================================================ FILE: tests/issue-84/app.js ================================================ ================================================ FILE: tests/issue-84/build/webpack.config.js ================================================ const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const WebpackPwaManifest = require('../../../dist') module.exports = { entry: path.join(__dirname, '../app.js'), output: { path: path.join(__dirname, '../output'), publicPath: '/', filename: '[name].[fullhash].bundle.js' }, plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', minify: { minifyCSS: true, minifyJS: true, collapseWhitespace: true, collapseInlineTagWhitespace: true, preserveLineBreaks: false, removeAttributeQuotes: true, removeComments: true } }), new WebpackPwaManifest({ name: 'My Progressive Web App', short_name: 'MyPWA', description: 'My awesome Progressive Web App!', background_color: '#ffffff', orientation: 'omit', ios: true, icons: [ { src: path.resolve('./tests/icon.png'), sizes: 512, destination: 'icons' }, { src: path.resolve('./tests/icon.png'), size: 1024, destination: 'icons', ios: true }, { src: path.resolve('./tests/icon.svg'), sizes: 512, destination: 'icons', color: '#ffffff' } ] }) ] } ================================================ FILE: tests/issue-84/test/index.html ================================================ Webpack App ================================================ FILE: tests/issue-84/test/main.159ccf53b959ede39a2e.bundle.js ================================================ ================================================ FILE: tests/issue-84/test/manifest.81200e1d24c270411245b5a63a4b91e7.json ================================================ { "icons": [ { "src": "/icons/icon_512x512.dd60b11e9762b6e25f6d4ce981a1eeae.svg", "sizes": "512x512", "type": "image/svg+xml" }, { "src": "/icons/icon_1024x1024.35f4891d98af6eb0c3dee2853ea0f190.png", "sizes": "1024x1024", "type": "image/png" }, { "src": "/icons/icon_512x512.d1f0ac39b9300e08b1a811d7919ab7e7.png", "sizes": "512x512", "type": "image/png" } ], "name": "My Progressive Web App", "short_name": "MyPWA", "display": "standalone", "start_url": ".", "description": "My awesome Progressive Web App!", "background_color": "#ffffff" } ================================================ FILE: tests/issue-87/.gitignore ================================================ .DS_Store output/ ================================================ FILE: tests/issue-87/app.js ================================================ ================================================ FILE: tests/issue-87/build/webpack.config.js ================================================ const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const WebpackPwaManifest = require('../../../dist') module.exports = { entry: path.join(__dirname, '../app.js'), output: { path: path.join(__dirname, '../output'), publicPath: '/', filename: '[name].[fullhash].bundle.js' }, plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', minify: { minifyCSS: true, minifyJS: true, collapseWhitespace: true, collapseInlineTagWhitespace: true, preserveLineBreaks: false, removeAttributeQuotes: true, removeComments: true } }), new WebpackPwaManifest({ name: 'My Progressive Web App', short_name: 'MyPWA', description: 'My awesome Progressive Web App!', background_color: '#ffffff', ios: true, icons: [ { ios: 'startup', src: path.resolve('./tests/icon.png'), destination: 'icons', sizes: '640x1136' }, { ios: 'startup', src: path.resolve('./tests/icon.png'), destination: 'icons', sizes: '750x1334' }, { ios: 'startup', src: path.resolve('./tests/icon.png'), destination: 'icons', sizes: '768x1024' } ] }) ] } ================================================ FILE: tests/issue-87/test/index.html ================================================ Webpack App ================================================ FILE: tests/issue-87/test/main.2f76de9afb439f1fcb16.bundle.js ================================================ ================================================ FILE: tests/issue-87/test/manifest.24abfacd4846fab249f8fe94967260b9.json ================================================ { "icons": [ { "src": "/icons/icon_768x768.f85b060e02c421c52e530e23fe1df50e.png", "sizes": "768x768", "type": "image/png" }, { "src": "/icons/icon_750x750.6d93014782b03254bb1f640a67ed0590.png", "sizes": "750x750", "type": "image/png" }, { "src": "/icons/icon_640x640.3e80720306149c1cca44f6fa41d3dffd.png", "sizes": "640x640", "type": "image/png" } ], "name": "My Progressive Web App", "short_name": "MyPWA", "orientation": "portrait", "display": "standalone", "start_url": ".", "description": "My awesome Progressive Web App!", "background_color": "#ffffff" } ================================================ FILE: tests/rgb-background/.gitignore ================================================ .DS_Store output/ ================================================ FILE: tests/rgb-background/app.js ================================================ ================================================ FILE: tests/rgb-background/build/webpack.config.js ================================================ const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const WebpackPwaManifest = require('../../../dist') module.exports = { entry: path.join(__dirname, '../app.js'), output: { path: path.join(__dirname, '../output'), publicPath: '/', filename: '[name].[fullhash].bundle.js' }, plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', minify: { minifyCSS: true, minifyJS: true, collapseWhitespace: true, collapseInlineTagWhitespace: true, preserveLineBreaks: false, removeAttributeQuotes: true, removeComments: true } }), new WebpackPwaManifest({ name: 'My Progressive Web App', short_name: 'MyPWA', description: 'My awesome Progressive Web App!', background_color: 'rgb(66, 134, 244)', ios: true, icons: [ { src: path.resolve('./tests/icon.png'), sizes: 512, destination: 'icons' }, { src: path.resolve('./tests/icon.png'), size: 1024, destination: 'icons', ios: true }, { src: path.resolve('./tests/icon.svg'), sizes: 512, destination: 'icons', color: '#ffffff' } ] }) ] } ================================================ FILE: tests/rgb-background/test/index.html ================================================ Webpack App ================================================ FILE: tests/rgb-background/test/main.fcb65b68a23c92e495a7.bundle.js ================================================ ================================================ FILE: tests/rgb-background/test/manifest.836308dd4105e7779e96d575bbcb3984.json ================================================ { "icons": [ { "src": "/icons/icon_512x512.dd60b11e9762b6e25f6d4ce981a1eeae.svg", "sizes": "512x512", "type": "image/svg+xml" }, { "src": "/icons/icon_1024x1024.35f4891d98af6eb0c3dee2853ea0f190.png", "sizes": "1024x1024", "type": "image/png" }, { "src": "/icons/icon_512x512.d1f0ac39b9300e08b1a811d7919ab7e7.png", "sizes": "512x512", "type": "image/png" } ], "name": "My Progressive Web App", "short_name": "MyPWA", "orientation": "portrait", "display": "standalone", "start_url": ".", "description": "My awesome Progressive Web App!", "background_color": "rgb(66, 134, 244)" } ================================================ FILE: tests/rgba-background/.gitignore ================================================ .DS_Store output/ ================================================ FILE: tests/rgba-background/app.js ================================================ ================================================ FILE: tests/rgba-background/build/webpack.config.js ================================================ const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const WebpackPwaManifest = require('../../../dist') module.exports = { entry: path.join(__dirname, '../app.js'), output: { path: path.join(__dirname, '../output'), publicPath: '/', filename: '[name].[fullhash].bundle.js' }, plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', minify: { minifyCSS: true, minifyJS: true, collapseWhitespace: true, collapseInlineTagWhitespace: true, preserveLineBreaks: false, removeAttributeQuotes: true, removeComments: true } }), new WebpackPwaManifest({ name: 'My Progressive Web App', short_name: 'MyPWA', description: 'My awesome Progressive Web App!', background_color: 'rgba(123, 23, 12, 0.3)', ios: true, icons: [ { src: path.resolve('./tests/icon.png'), sizes: 512, destination: 'icons' }, { src: path.resolve('./tests/icon.png'), size: 1024, destination: 'icons', ios: true }, { src: path.resolve('./tests/icon.svg'), sizes: 512, destination: 'icons', color: '#ffffff' } ] }) ] } ================================================ FILE: tests/rgba-background/test/index.html ================================================ Webpack App ================================================ FILE: tests/rgba-background/test/main.a772dd109d73b782cf7a.bundle.js ================================================ ================================================ FILE: tests/rgba-background/test/manifest.82c3c467821f8c1b613bc6f741480697.json ================================================ { "icons": [ { "src": "/icons/icon_512x512.dd60b11e9762b6e25f6d4ce981a1eeae.svg", "sizes": "512x512", "type": "image/svg+xml" }, { "src": "/icons/icon_1024x1024.35f4891d98af6eb0c3dee2853ea0f190.png", "sizes": "1024x1024", "type": "image/png" }, { "src": "/icons/icon_512x512.d1f0ac39b9300e08b1a811d7919ab7e7.png", "sizes": "512x512", "type": "image/png" } ], "name": "My Progressive Web App", "short_name": "MyPWA", "orientation": "portrait", "display": "standalone", "start_url": ".", "description": "My awesome Progressive Web App!", "background_color": "rgba(123, 23, 12, 0.3)" } ================================================ FILE: tests/runTest.js ================================================ const webpack = require('webpack') const path = require('path') const ls = require('list-directory-contents') const fs = require('fs') const assert = require('assert') function run (name, next) { if (!name) return console.log('End.') const config = require(path.resolve(`./tests/${name}/build/webpack.config.js`)) console.log(`"${name}": building...`) webpack(config, (err, stats) => { if (err) throw err console.log(`"${name}": testing...`) const testPath = path.join(__dirname, name, 'test') const testPathLength = testPath.length ls(testPath, (err, tree) => { if (err) throw err const testTree = tree.filter((i) => /.*\..+/.test(i)) const testContent = testTree.map((i) => i.substring(testPathLength)) const testContentLength = testContent.length const outputPath = path.join(__dirname, name, 'output') const outputPathLength = outputPath.length ls(outputPath, (err, outputTree) => { if (err) throw err const outputContent = outputTree.filter((i) => /.*\..+/.test(i)) const outputContentLength = outputContent.length // console.log('outputContent: ', outputContent) // console.log('outputContentLength: ', outputContentLength) if (testContentLength === outputContentLength) { const result = outputContent.reduce((successfulTests, outputToTest) => { const outputFilename = outputToTest.substring(outputPathLength) const testAgainst = testContent.indexOf(outputFilename) const success = testAgainst > -1 && fs.readFileSync(outputToTest).equals(fs.readFileSync(testTree[testAgainst])) // if (!success) { // console.log(`FAILED: "${outputFilename}" AGAINST "${testTree[testAgainst]}..."`) // } return success ? successfulTests + 1 : successfulTests }, 0) // console.log('RESULT: ', result) // console.log('TEST CONTENT LENGTH: ', testContentLength) if (result === testContentLength) { console.log(`Test "${name}" passed.`) run(next.shift(), next) } else { console.log('There are files missing or with different content.') console.log(`Test "${name}" failed on file ${outputContent}.`) assert(result === testContentLength) } } else { console.log(`Expected ${testContentLength} file(s).`) console.log(`Found ${outputContentLength} file(s).`) console.log(`Test "${name}" failed.`) assert(testContentLength === outputContentLength) } }) }) }) } module.exports = run