From d3367ad8b7cd9cc0737e8348338e17ff8ceb373e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96nder=20Ceylan?= Date: Mon, 2 Sep 2019 00:14:53 +0200 Subject: [PATCH 1/9] feat(main): exposed private API via main.js Added generateImages function to make use of the lib functionality via node scripting fix #5 --- cli.js | 84 ++---------------------------------------------- helpers/flags.js | 29 +++++++++++++++++ main.js | 72 +++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 4 files changed, 105 insertions(+), 82 deletions(-) create mode 100644 helpers/flags.js create mode 100644 main.js diff --git a/cli.js b/cli.js index 4b95b836..4dbb70bb 100755 --- a/cli.js +++ b/cli.js @@ -1,10 +1,10 @@ #!/usr/bin/env node const meow = require('meow'); -const puppets = require('./puppets'); -const pwa = require('./helpers/pwa'); const preLogger = require('./helpers/logger'); +const main = require('./main'); +const logger = preLogger('cli'); const cli = meow( ` $ pwa-asset-generator --help @@ -112,87 +112,9 @@ $ pwa-asset-generator --help }, ); -const source = cli.input[0]; -let output = cli.input[1]; -let options = cli.flags; -const logger = preLogger('cli'); - -const normalizeOnlyFlagPairs = (flag1Key, flag2Key, opts) => { - const stripOnly = key => key.replace('Only', ''); - if (opts[flag1Key] && opts[flag2Key]) { - logger.warn( - `Hmm, you want to _only_ generate both ${stripOnly( - flag1Key, - )} and ${stripOnly( - flag2Key, - )} set. Ignoring --x-only settings as this is default behavior`, - ); - return { - ...opts, - [flag1Key]: false, - [flag2Key]: false, - }; - } - return opts; -}; - -if (!source) { - logger.error('Please specify a URL or file path as a source'); - process.exit(1); -} - -options = normalizeOnlyFlagPairs('splashOnly', 'iconOnly', options); -options = normalizeOnlyFlagPairs('landscapeOnly', 'portraitOnly', options); - -if (!output) { - output = '.'; -} - (async () => { try { - const savedImages = await puppets.generateImages(source, output, options); - const manifestJsonContent = pwa.generateIconsContentForManifest( - savedImages, - options.manifest, - ); - const htmlContent = pwa.generateHtmlForIndexPage( - savedImages, - options.index, - ); - - if (!options.splashOnly) { - if (options.manifest) { - await pwa.addIconsToManifest(manifestJsonContent, options.manifest); - logger.success( - `Icons are saved to Web App Manifest file ${options.manifest}`, - ); - } else if (!options.splashOnly) { - logger.warn( - 'Web App Manifest file is not specified, printing out the content to console instead', - ); - logger.success( - 'Below is the icons content for your manifest.json file. You can copy/paste it manually', - ); - process.stdout.write( - `\n${JSON.stringify(manifestJsonContent, null, 2)}\n\n`, - ); - } - } - - if (options.index) { - await pwa.addMetaTagsToIndexPage(htmlContent, options.index); - logger.success( - `iOS meta tags are saved to index html file ${options.index}`, - ); - } else { - logger.warn( - 'Index html file is not specified, printing out the content to console instead', - ); - logger.success( - 'Below is the iOS meta tags content for your index.html file. You can copy/paste it manually', - ); - process.stdout.write(`\n${htmlContent}\n`); - } + await main.generateImages(cli.input[0], cli.input[1], cli.flags, logger); } catch (e) { logger.error(e); process.exit(1); diff --git a/helpers/flags.js b/helpers/flags.js new file mode 100644 index 00000000..ee6b545f --- /dev/null +++ b/helpers/flags.js @@ -0,0 +1,29 @@ +const normalizeOnlyFlagPairs = (flag1Key, flag2Key, opts, logger) => { + const stripOnly = key => key.replace('Only', ''); + if (opts[flag1Key] && opts[flag2Key]) { + logger.warn( + `Hmm, you want to _only_ generate both ${stripOnly( + flag1Key, + )} and ${stripOnly( + flag2Key, + )} set. Ignoring --x-only settings as this is default behavior`, + ); + return { + [flag1Key]: false, + [flag2Key]: false, + }; + } + return {}; +}; + +const normalizeOutput = output => { + if (!output) { + return '.'; + } + return output; +}; + +module.exports = { + normalizeOnlyFlagPairs, + normalizeOutput, +}; diff --git a/main.js b/main.js new file mode 100644 index 00000000..612d4add --- /dev/null +++ b/main.js @@ -0,0 +1,72 @@ +const pwa = require('./helpers/pwa'); +const puppets = require('./puppets'); +const flags = require('./helpers/flags'); +const preLogger = require('./helpers/logger'); + +const generateImages = async (source, _output, _options, loggerInjection) => { + const logger = loggerInjection || preLogger(generateImages.name); + + if (!source) { + throw Error('Please specify a URL or file path as a source'); + } + + const options = { + ..._options, + ...flags.normalizeOnlyFlagPairs('splashOnly', 'iconOnly', _options, logger), + ...flags.normalizeOnlyFlagPairs( + 'landscapeOnly', + 'portraitOnly', + _options, + logger, + ), + }; + + const output = flags.normalizeOutput(_output); + + const savedImages = await puppets.generateImages(source, output, options); + const manifestJsonContent = pwa.generateIconsContentForManifest( + savedImages, + options.manifest, + ); + const htmlContent = pwa.generateHtmlForIndexPage(savedImages, options.index); + + if (!options.splashOnly) { + if (options.manifest) { + await pwa.addIconsToManifest(manifestJsonContent, options.manifest); + logger.success( + `Icons are saved to Web App Manifest file ${options.manifest}`, + ); + } else if (!options.splashOnly) { + logger.warn( + 'Web App Manifest file is not specified, printing out the content to console instead', + ); + logger.success( + 'Below is the icons content for your manifest.json file. You can copy/paste it manually', + ); + process.stdout.write( + `\n${JSON.stringify(manifestJsonContent, null, 2)}\n\n`, + ); + } + } + + if (options.index) { + await pwa.addMetaTagsToIndexPage(htmlContent, options.index); + logger.success( + `iOS meta tags are saved to index html file ${options.index}`, + ); + } else { + logger.warn( + 'Index html file is not specified, printing out the content to console instead', + ); + logger.success( + 'Below is the iOS meta tags content for your index.html file. You can copy/paste it manually', + ); + process.stdout.write(`\n${htmlContent}\n`); + } + + return savedImages; +}; + +module.exports = { + generateImages, +}; diff --git a/package.json b/package.json index a1044f93..48256afe 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "pwa-asset-generator", "version": "1.1.7", "description": "PWA asset generator based on Puppeteer. Automatically generates icons and splash screens guided by Web App Manifest specs and Apple Human Interface guidelines. Updates manifest.json and index.html files with the generated images.", - "main": "cli.js", + "main": "main.js", "bin": { "pwa-asset-generator": "cli.js" }, From cbcd4ebc17598968bb29f86a8fc61a8426543860 Mon Sep 17 00:00:00 2001 From: Dima Snisarenko Date: Tue, 3 Sep 2019 09:15:01 +0200 Subject: [PATCH 2/9] chore(git): check git commit message against conventional commit format --- commitlint.config.js | 1 + package.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 commitlint.config.js diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 00000000..422b1944 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1 @@ +module.exports = { extends: ['@commitlint/config-conventional'] }; diff --git a/package.json b/package.json index 48256afe..7af30dbf 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "slash": "^3.0.0" }, "devDependencies": { + "@commitlint/cli": "^8.1.0", "@jedmao/semantic-release-npm-github-config": "^1.0.6", "@types/jest": "^24.0.18", "cz-conventional-changelog": "^3.0.2", @@ -74,7 +75,7 @@ "husky": { "hooks": { "pre-commit": "lint-staged", - "prepare-commit-msg": "exec < /dev/tty && git cz --hook" + "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" } }, "lint-staged": { From 8db0558b4cc370741c0da737c33f7fc0738f32a0 Mon Sep 17 00:00:00 2001 From: Dima Snisarenko Date: Tue, 3 Sep 2019 09:19:43 +0200 Subject: [PATCH 3/9] chore(git): add config-conventional dependency --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 7af30dbf..fb4c2820 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ }, "devDependencies": { "@commitlint/cli": "^8.1.0", + "@commitlint/config-conventional": "^8.1.0", "@jedmao/semantic-release-npm-github-config": "^1.0.6", "@types/jest": "^24.0.18", "cz-conventional-changelog": "^3.0.2", From f62eb1b3b851de14d0fd2ca586fe1527c572272c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96nder=20Ceylan?= Date: Tue, 3 Sep 2019 10:26:15 +0200 Subject: [PATCH 4/9] docs(contr): updated commit instructions Updated docs to reflect latest commit flow --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 49727b37..11cda7e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,12 +20,12 @@ Please note that if any of the issues has an assignee or is `In Progress` within ## Commit messages and continuous deployment -This project uses [husky](https://github.com/typicode/husky) w [commitizen](https://github.com/commitizen/cz-cli) and [semantic-release](https://github.com/semantic-release/semantic-release) to commit message standardization and continuous deployment. +This project uses [husky](https://github.com/typicode/husky) w [commitizen](https://github.com/commitizen/cz-cli) and [semantic-release](https://github.com/semantic-release/semantic-release) for conventional changelog and continuous deployment. When you run `npm install` before introducing any change, all the necessary packages for this workflow will be installed on your local clone. -You should commit your changes without providing a custom message via `git commit -m` ❌, but instead using `git commit` ✅. -After running `git commit`, commitizen CLI will initialize and it will help you through. +You should commit your changes without providing a custom message via `git commit -m` ❌, but instead using `npm run commit` ✅. +After running `npm run commit`, commitizen CLI will initialize and it will help you through. All you commit messages will be linted by `commitlint` to make sure they're conventional. Please note that your commit message has a direct impact on the deployment of a new release. When your PR is merged to `master`, any changes in your PR with; From 1ab1c3e2c813039ec5f0d5caf9cd78ed559b4720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96nder=20Ceylan?= Date: Wed, 11 Sep 2019 15:11:24 +0200 Subject: [PATCH 5/9] feat(main): refactored main and added index.d.ts Refactored main.js file to return saved images array, html content of meta tags and manifest json icons array. Added interfaces and docs via index.d.ts and updated readme. fix #5 --- .eslintrc | 1 + .npmignore | 2 + README.md | 27 +++- __snapshots__/cli.test.js.snap | 7 + cli.js | 72 +-------- cli.test.js | 10 +- config/constants.js | 66 ++++++++ helpers/file.js | 8 +- helpers/flags.js | 13 ++ helpers/logger.js | 21 ++- helpers/url.js | 3 +- index.d.ts | 286 +++++++++++++++++++++++++++++++++ main.js | 13 +- package.json | 1 + puppets.js | 18 +-- 15 files changed, 452 insertions(+), 96 deletions(-) create mode 100644 index.d.ts diff --git a/.eslintrc b/.eslintrc index 3187d18f..418663d8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -18,5 +18,6 @@ "ecmaVersion": 2018 }, "rules": { + "no-prototype-builtins": "off" } } diff --git a/.npmignore b/.npmignore index e1a60b2b..5aeff52d 100644 --- a/.npmignore +++ b/.npmignore @@ -10,4 +10,6 @@ __snapshots__/* .prettierrc .travis.yml .releaserc +.idea +.github *.test.js diff --git a/README.md b/README.md index 169cf916..b023b3c2 100644 --- a/README.md +++ b/README.md @@ -66,15 +66,16 @@ $ pwa-asset-generator --help -b --background Page background to use when image source is provided: css value [default: transparent] -o --opaque Making screenshots to be saved with a background color [default: true] -p --padding Padding to use when image source provided: css value [default: "10%"] - -s --scrape Scraping Apple Human Interface Guidelines to fetch splash screen specs [default: true] - -m --manifest Web app manifest file path to automatically update manifest file with the generated images - -i --index Index html file path to automatically put splash screen meta tags in + -s --scrape Scraping Apple Human Interface guidelines to fetch splash screen specs [default: true] + -m --manifest Web app manifest file path to automatically update manifest file with the generated icons + -i --index Index html file path to automatically put splash screen and icon meta tags in -t --type Image type: png|jpeg [default: png] -q --quality Image quality: 0...100 (Only for JPEG) [default: 100] -h --splash-only Only generate splash screens [default: false] -c --icon-only Only generate icons [default: false] -l --landscape-only Only generate landscape splash screens [default: false] -r --portrait-only Only generate portrait splash screens [default: false] + -g --log Logs the steps of the library process [default: true] Examples $ pwa-asset-generator logo.html . @@ -95,6 +96,26 @@ $ pwa-asset-generator --help --icon-only --landscape-only --portrait-only + --log=false +``` + +### Module + +```javascript +const pwaAssetGenerator = require('pwa-asset-generator'); + +(async () => { + const { savedImages, htmlContent, manifestJsonContent } = await pwaAssetGenerator.generateImages( + 'https://raw.githubusercontent.com/onderceylan/pwa-asset-generator/HEAD/static/logo.png', + './temp', + { + scrape: false, + background: "linear-gradient(to right, #fa709a 0%, #fee140 100%)", + splashOnly: true, + portraitOnly: true, + log: false + }); +})(); ``` ## Troubleshooting diff --git a/__snapshots__/cli.test.js.snap b/__snapshots__/cli.test.js.snap index 54157b32..c7a588a0 100644 --- a/__snapshots__/cli.test.js.snap +++ b/__snapshots__/cli.test.js.snap @@ -16,6 +16,7 @@ exports[`generates icons and splash screens when both only flags exist 1`] = ` ] + @@ -82,6 +83,7 @@ exports[`generates icons and splash screens when both only flags exist 1`] = ` + " `; @@ -101,12 +103,14 @@ exports[`generates icons only 1`] = ` ] + + " `; @@ -144,6 +148,7 @@ exports[`generates landscape splash screens only 1`] = ` + " `; @@ -181,6 +186,7 @@ exports[`generates portrait splash screens only 1`] = ` + " `; @@ -248,5 +254,6 @@ exports[`generates splash screens only 1`] = ` + " `; diff --git a/cli.js b/cli.js index 4dbb70bb..d08d7f7d 100755 --- a/cli.js +++ b/cli.js @@ -3,8 +3,8 @@ const meow = require('meow'); const preLogger = require('./helpers/logger'); const main = require('./main'); +const { FLAGS: flags } = require('./config/constants'); -const logger = preLogger('cli'); const cli = meow( ` $ pwa-asset-generator --help @@ -18,15 +18,16 @@ $ pwa-asset-generator --help -b --background Page background to use when image source is provided: css value [default: transparent] -o --opaque Making screenshots to be saved with a background color [default: true] -p --padding Padding to use when image source provided: css value [default: "10%"] - -s --scrape Scraping Apple Human Interface Guidelines to fetch splash screen specs [default: true] - -m --manifest Web app manifest file path to automatically update manifest file with the generated images - -i --index Index html file path to automatically put splash screen meta tags in + -s --scrape Scraping Apple Human Interface guidelines to fetch splash screen specs [default: true] + -m --manifest Web app manifest file path to automatically update manifest file with the generated icons + -i --index Index html file path to automatically put splash screen and icon meta tags in -t --type Image type: png|jpeg [default: png] -q --quality Image quality: 0...100 (Only for JPEG) [default: 100] -h --splash-only Only generate splash screens [default: false] -c --icon-only Only generate icons [default: false] -l --landscape-only Only generate landscape splash screens [default: false] -r --portrait-only Only generate portrait splash screens [default: false] + -g --log Logs the steps of the library process [default: true] Examples $ pwa-asset-generator logo.html . @@ -47,70 +48,13 @@ $ pwa-asset-generator --help --icon-only --landscape-only --portrait-only + --log=false `, { - flags: { - background: { - type: 'string', - alias: 'b', - default: 'transparent', - }, - manifest: { - type: 'string', - alias: 'm', - }, - index: { - type: 'string', - alias: 'i', - }, - opaque: { - type: 'boolean', - alias: 'o', - default: true, - }, - scrape: { - type: 'boolean', - alias: 's', - default: true, - }, - padding: { - type: 'string', - alias: 'p', - default: '10%', - }, - type: { - type: 'string', - alias: 't', - default: 'png', - }, - quality: { - type: 'number', - alias: 'q', - default: 100, - }, - splashOnly: { - type: 'boolean', - alias: 'h', - default: false, - }, - iconOnly: { - type: 'boolean', - alias: 'c', - default: false, - }, - landscapeOnly: { - type: 'boolean', - alias: 'l', - default: false, - }, - portraitOnly: { - type: 'boolean', - alias: 'r', - default: false, - }, - }, + flags, }, ); +const logger = preLogger('cli', cli.flags); (async () => { try { diff --git a/cli.test.js b/cli.test.js index 96c4f508..50ae51a4 100644 --- a/cli.test.js +++ b/cli.test.js @@ -29,7 +29,7 @@ test('generates icons only', async () => { const { stdout } = await execa( './cli.js', ['./static/logo.png', './temp', '-s=false', '--icon-only'], - { env: { PAG_NO_TRACE: '1' } }, + { env: { PAG_TEST_MODE: '1' } }, ); expect(stdout).toMatchSnapshot(); @@ -41,7 +41,7 @@ test('generates splash screens only', async () => { const { stdout } = await execa( './cli.js', ['./static/logo.png', './temp', '-s=false', '--splash-only'], - { env: { PAG_NO_TRACE: '1' } }, + { env: { PAG_TEST_MODE: '1' } }, ); expect(stdout).toMatchSnapshot(); @@ -59,7 +59,7 @@ test('generates portrait splash screens only', async () => { '--splash-only', '--portrait-only', ], - { env: { PAG_NO_TRACE: '1' } }, + { env: { PAG_TEST_MODE: '1' } }, ); expect(stdout).toMatchSnapshot(); @@ -77,7 +77,7 @@ test('generates landscape splash screens only', async () => { '--splash-only', '--landscape-only', ], - { env: { PAG_NO_TRACE: '1' } }, + { env: { PAG_TEST_MODE: '1' } }, ); expect(stdout).toMatchSnapshot(); @@ -89,7 +89,7 @@ test('generates icons and splash screens when both only flags exist', async () = const { stdout } = await execa( './cli.js', ['./static/logo.png', './temp', '-s=false', '--splash-only', '--icon-only'], - { env: { PAG_NO_TRACE: '1' } }, + { env: { PAG_TEST_MODE: '1' } }, ); expect(stdout).toMatchSnapshot(); diff --git a/config/constants.js b/config/constants.js index 42ccbcd9..a5d8a7f8 100644 --- a/config/constants.js +++ b/config/constants.js @@ -1,4 +1,70 @@ module.exports = { + FLAGS: { + background: { + type: 'string', + alias: 'b', + default: 'transparent', + }, + manifest: { + type: 'string', + alias: 'm', + }, + index: { + type: 'string', + alias: 'i', + }, + opaque: { + type: 'boolean', + alias: 'o', + default: true, + }, + scrape: { + type: 'boolean', + alias: 's', + default: true, + }, + padding: { + type: 'string', + alias: 'p', + default: '10%', + }, + type: { + type: 'string', + alias: 't', + default: 'png', + }, + quality: { + type: 'number', + alias: 'q', + default: 100, + }, + splashOnly: { + type: 'boolean', + alias: 'h', + default: false, + }, + iconOnly: { + type: 'boolean', + alias: 'c', + default: false, + }, + landscapeOnly: { + type: 'boolean', + alias: 'l', + default: false, + }, + portraitOnly: { + type: 'boolean', + alias: 'r', + default: false, + }, + log: { + type: 'boolean', + alias: 'g', + default: true, + }, + }, + PUPPETEER_LAUNCH_ARGS: [ '--log-level=3', // Fatal only '--no-default-browser-check', diff --git a/helpers/file.js b/helpers/file.js index 35dba969..35efe348 100644 --- a/helpers/file.js +++ b/helpers/file.js @@ -32,7 +32,13 @@ const isHtmlFile = file => { }; const getAppDir = () => { - return path.dirname(require.main.filename); + let appPath; + try { + appPath = require.resolve('pwa-asset-generator'); + } catch (e) { + appPath = require.main.filename; + } + return path.dirname(appPath); }; const getShellHtmlFilePath = () => { diff --git a/helpers/flags.js b/helpers/flags.js index ee6b545f..ca3718ba 100644 --- a/helpers/flags.js +++ b/helpers/flags.js @@ -1,3 +1,5 @@ +const constants = require('../config/constants'); + const normalizeOnlyFlagPairs = (flag1Key, flag2Key, opts, logger) => { const stripOnly = key => key.replace('Only', ''); if (opts[flag1Key] && opts[flag2Key]) { @@ -23,7 +25,18 @@ const normalizeOutput = output => { return output; }; +const getDefaultOptions = () => { + const { FLAGS: flags } = constants; + + return Object.keys(flags) + .filter(flagKey => flags[flagKey].hasOwnProperty('default')) + .reduce((acc, curr) => { + return { ...acc, [curr]: flags[curr].default }; + }, {}); +}; + module.exports = { normalizeOnlyFlagPairs, normalizeOutput, + getDefaultOptions, }; diff --git a/helpers/logger.js b/helpers/logger.js index d3eced84..00300e04 100644 --- a/helpers/logger.js +++ b/helpers/logger.js @@ -1,8 +1,11 @@ const chalk = require('chalk'); -const noTrace = !!+process.env.PAG_NO_TRACE; +const testMode = !!+process.env.PAG_TEST_MODE; + +const logger = (prefix, options) => { + const isLogEnabled = + options && options.hasOwnProperty('log') ? options.log : true; -const logger = prefix => { const getTime = () => { return chalk.inverse(new Date().toLocaleTimeString()); }; @@ -12,18 +15,23 @@ const logger = prefix => { }; /* eslint-disable no-console */ + const raw = (...args) => { + if (!isLogEnabled) return; + console.log(...args); + }; + const log = (...args) => { - if (noTrace) return; + if (testMode || !isLogEnabled) return; console.log(getTime(), getPrefix(), ...args); }; const warn = (...args) => { - if (noTrace) return; + if (testMode || !isLogEnabled) return; console.warn(getTime(), getPrefix(), chalk.yellow(...args), '🤔'); }; const trace = (...args) => { - if (noTrace) return; + if (testMode || !isLogEnabled) return; console.trace(getTime(), getPrefix(), ...args); }; @@ -32,12 +40,13 @@ const logger = prefix => { }; const success = (...args) => { - if (noTrace) return; + if (testMode || !isLogEnabled) return; console.log(getTime(), getPrefix(), chalk.green(...args), '🙌'); }; /* eslint-enable no-console */ return { + raw, log, warn, trace, diff --git a/helpers/url.js b/helpers/url.js index a61b1e5f..11a3849f 100644 --- a/helpers/url.js +++ b/helpers/url.js @@ -25,12 +25,13 @@ const isUrlExists = source => { }; const getAddress = async (source, options) => { - const logger = preLogger(getAddress.name); + const logger = preLogger(getAddress.name, options); const useShell = async (isSourceUrl = false) => { try { await file.saveHtmlShell(source, options, isSourceUrl); } catch (e) { + logger.error(e); throw Error('Failed saving html shell'); } diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 00000000..cf419d7b --- /dev/null +++ b/index.d.ts @@ -0,0 +1,286 @@ +declare namespace PwaAssetGenerator { + + interface Options { + /** + Page background to use when image source is provided + Same as css background property + + @default transparent + */ + readonly background?: string; + + /** + Making screenshots to be saved with a background color + Uses white background when background option is not provided + + @default true + */ + readonly opaque?: boolean; + + /** + Padding to use when image source provided + Same as css padding property + + @default "10%" + */ + readonly padding?: string; + + /** + Scraping Apple Human Interface guidelines to fetch splash screen specs + + @default true + */ + readonly scrape?: boolean; + + /** + Web app manifest file path to automatically update manifest file with the generated icons + */ + readonly manifest?: string; + + /** + Index html file path to automatically put splash screen and icon meta tags in + */ + readonly index?: string; + + /** + Image type + + @default png + */ + readonly type?: 'png' | 'jpeg'; + + /** + Image quality: 0...100 + Enabled only for jpeg image type + + @default 100 + */ + readonly quality?: number; + + /** + Only generate splash screens + + @default false + */ + readonly splashOnly?: boolean; + + /** + Only generate icons + + @default false + */ + readonly iconOnly?: boolean; + + /** + Only generate landscape splash screens + Disabled when iconOnly option is provided + + @default false + */ + readonly landscapeOnly?: boolean; + + /** + Only generate portrait splash screens + Disabled when iconOnly option is provided + + @default false + */ + readonly portraitOnly?: boolean; + } + + interface SavedImage { + /** + Name of the saved image file, without file extension + */ + name: string; + + /** + Image width in pixels + */ + width: number; + + /** + Image height in pixels + */ + height: number; + + /** + Device scale factor used for generating HTML meta tags for iOS splash screens + Defaults to null for icons + + @default null + */ + scaleFactor: number | null; + + /** + Saved image path + Path is relative to execution folder + */ + path: string; + + /** + Device orientation used for generating HTML meta tags for iOS splash screens + Defaults to null for icons + + @default null + */ + orientation: 'landscape' | 'portrait' | null; + } + + interface ManifestJsonIcon { + /** + A URL from which a user agent can fetch the image's data + + @tutorial https://www.w3.org/TR/appmanifest/#dom-imageresource-src + @example //icons.example.com/lowres + */ + src: string; + + /** + A string consisting of an unordered set of unique space-separated tokens + + @tutorial https://www.w3.org/TR/appmanifest/#sizes-member + @example 192x192 + */ + sizes?: string; + + /** + Media type of the image + The purpose of this member is to allow a user agent to ignore images of media types it does not support + + @tutorial https://www.w3.org/TR/appmanifest/#dom-imageresource-type + @example image/png + */ + type?: string; + + /** + When an ImageResource is used as an icon, a developer can hint that + the image is intended to serve some special purpose in the context of the host OS + + @tutorial https://www.w3.org/TR/appmanifest/#dfn-icon-purposes + @default any + */ + purpose?: 'badge' | 'maskable' | 'any'; + + /** + The platform member represents the platform to which a containing object applies + + @tutorial https://github.com/w3c/manifest/wiki/Platforms + */ + platform?: 'chrome_web_store' | 'play' | 'itunes' | 'windows'; + } + + interface Result { + /** + Saved images array that keeps both splash screens and icons, with image properties + + @example + ```javascript + [{ + name: 'apple-splash-1136-640', + width: 1136, + height: 640, + scaleFactor: 2, + path: 'temp/apple-splash-1136-640.png', + orientation: 'landscape' + }, + { + name: 'apple-icon-180', + width: 180, + height: 180, + scaleFactor: null, + path: 'temp/apple-icon-180.png', + orientation: null + }] + ``` + */ + savedImages: SavedImage[]; + + /** + Meta tags to be added to index.html file + + @example + ```html + + + + + ``` + */ + htmlContent: string; + + /** + Icons to be added to manifest.json's icons property + + @example + ```json + [{ + "src": "assets/pwa/manifest-icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "assets/pwa/manifest-icon-512.png", + "sizes": "512x512", + "type": "image/png" + }] + ``` + */ + manifestJsonContent: ManifestJsonIcon[]; + } + + /** + Logger function to print out steps of the lib + + @param prefix - Shows the origin of the log, e.g. function name + @param options - Option flags of the library in an object + */ + interface Logger { + (prefix: string, options?: Options): { + raw(): string; + log(): string; + warn(): string; + trace(): string; + error(): string; + success(): string; + } + } +} + +declare const pwaAssetGenerator: { + /** + Generates PWA assets based on a source input and saves generated images in the output folder provided + + @param source - A local image file, a local HTML file, a remote image or remote HTML file path + @param outputFolderPath - The path of the folder to save the images in + @param options - Option flags of the library in an object, keeps default values + @param logger - An optional logger function to log the output + @returns A promise of result object that resolves when all images are generated and file updates are finalized + + @example + ```javascript + import pwaAssetGenerator = require('pwa-asset-generator'); + + (async () => { + const { savedImages, htmlContent, manifestJsonContent } = await pwaAssetGenerator.generateImages( + 'https://raw.githubusercontent.com/onderceylan/pwa-asset-generator/HEAD/static/logo.png', + './temp', + { + scrape: false, + background: "linear-gradient(to right, #fa709a 0%, #fee140 100%)", + splashOnly: true, + portraitOnly: true, + log: false + }); + })(); + ``` + */ + generateImages( + source: string, + outputFolderPath: string, + options?: PwaAssetGenerator.Options, + logger?: PwaAssetGenerator.Logger, + ): Promise; +}; + +export = pwaAssetGenerator; diff --git a/main.js b/main.js index 612d4add..ee575d41 100644 --- a/main.js +++ b/main.js @@ -3,14 +3,15 @@ const puppets = require('./puppets'); const flags = require('./helpers/flags'); const preLogger = require('./helpers/logger'); -const generateImages = async (source, _output, _options, loggerInjection) => { - const logger = loggerInjection || preLogger(generateImages.name); +const generateImages = async (source, _output, _options, loggerFn) => { + const logger = loggerFn || preLogger(generateImages.name, _options); if (!source) { throw Error('Please specify a URL or file path as a source'); } const options = { + ...flags.getDefaultOptions(), ..._options, ...flags.normalizeOnlyFlagPairs('splashOnly', 'iconOnly', _options, logger), ...flags.normalizeOnlyFlagPairs( @@ -43,9 +44,7 @@ const generateImages = async (source, _output, _options, loggerInjection) => { logger.success( 'Below is the icons content for your manifest.json file. You can copy/paste it manually', ); - process.stdout.write( - `\n${JSON.stringify(manifestJsonContent, null, 2)}\n\n`, - ); + logger.raw(`\n${JSON.stringify(manifestJsonContent, null, 2)}\n\n`); } } @@ -61,10 +60,10 @@ const generateImages = async (source, _output, _options, loggerInjection) => { logger.success( 'Below is the iOS meta tags content for your index.html file. You can copy/paste it manually', ); - process.stdout.write(`\n${htmlContent}\n`); + logger.raw(`\n${htmlContent}\n`); } - return savedImages; + return { savedImages, htmlContent, manifestJsonContent }; }; module.exports = { diff --git a/package.json b/package.json index fb4c2820..9f036f5c 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "1.1.7", "description": "PWA asset generator based on Puppeteer. Automatically generates icons and splash screens guided by Web App Manifest specs and Apple Human Interface guidelines. Updates manifest.json and index.html files with the generated images.", "main": "main.js", + "types": "index.d.ts", "bin": { "pwa-asset-generator": "cli.js" }, diff --git a/puppets.js b/puppets.js index 1467d449..92e57179 100644 --- a/puppets.js +++ b/puppets.js @@ -6,8 +6,8 @@ const file = require('./helpers/file'); const images = require('./helpers/images'); const preLogger = require('./helpers/logger'); -const getAppleSplashScreenData = async browser => { - const logger = preLogger(getAppleSplashScreenData.name); +const getAppleSplashScreenData = async (browser, options) => { + const logger = preLogger(getAppleSplashScreenData.name, options); const page = await browser.newPage(); await page.setUserAgent(constants.EMULATED_USER_AGENT); logger.log( @@ -86,8 +86,8 @@ const getAppleSplashScreenData = async browser => { return splashScreenData; }; -const getDeviceScaleFactorData = async browser => { - const logger = preLogger(getDeviceScaleFactorData.name); +const getDeviceScaleFactorData = async (browser, options) => { + const logger = preLogger(getDeviceScaleFactorData.name, options); const page = await browser.newPage(); await page.setUserAgent(constants.EMULATED_USER_AGENT); logger.log( @@ -156,7 +156,7 @@ const getDeviceScaleFactorData = async browser => { }; const getSplashScreenMetaData = async options => { - const logger = preLogger(getSplashScreenMetaData.name); + const logger = preLogger(getSplashScreenMetaData.name, options); if (!options.scrape) { logger.log( @@ -178,8 +178,8 @@ const getSplashScreenMetaData = async options => { let splashScreenUniformMetaData; try { - const splashScreenData = await getAppleSplashScreenData(browser); - const scaleFactorData = await getDeviceScaleFactorData(browser); + const splashScreenData = await getAppleSplashScreenData(browser, options); + const scaleFactorData = await getDeviceScaleFactorData(browser, options); splashScreenUniformMetaData = images.getSplashScreenScaleFactorUnionData( splashScreenData, scaleFactorData, @@ -199,7 +199,7 @@ const getSplashScreenMetaData = async options => { }; const saveImages = async (imageList, source, output, options) => { - const logger = preLogger(saveImages.name); + const logger = preLogger(saveImages.name, options); logger.log('Initialising puppeteer to take screenshots', '🤖'); const address = await url.getAddress(source, options); @@ -243,7 +243,7 @@ const saveImages = async (imageList, source, output, options) => { }; const generateImages = async (source, output, options) => { - const logger = preLogger(generateImages.name); + const logger = preLogger(generateImages.name, options); const splashScreenMetaData = await getSplashScreenMetaData(options); const allImages = [ ...(!options.iconOnly From 1e18466b20a97b9b156901e94a4c1394827af734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96nder=20Ceylan?= Date: Thu, 12 Sep 2019 07:44:20 +0200 Subject: [PATCH 6/9] feat(main): added option to provide path prefix to generated href links fix #31 --- README.md | 22 +++++---- __snapshots__/cli.test.js.snap | 87 ++++++++++++++++++++++++++++++++++ cli.js | 22 +++++---- cli.test.js | 12 +++++ config/constants.js | 4 ++ helpers/pwa.js | 30 +++++++++--- index.d.ts | 7 +++ main.js | 6 ++- 8 files changed, 162 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index b023b3c2..316553b7 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ $ pwa-asset-generator --help -s --scrape Scraping Apple Human Interface guidelines to fetch splash screen specs [default: true] -m --manifest Web app manifest file path to automatically update manifest file with the generated icons -i --index Index html file path to automatically put splash screen and icon meta tags in + -a --path Path prefix to prepend for href links generated for meta tags -t --type Image type: png|jpeg [default: png] -q --quality Image quality: 0...100 (Only for JPEG) [default: 100] -h --splash-only Only generate splash screens [default: false] @@ -80,23 +81,24 @@ $ pwa-asset-generator --help Examples $ pwa-asset-generator logo.html . $ pwa-asset-generator https://your-cdn-server.com/assets/logo.png . -t jpeg -q 90 --splash-only --portrait-only - $ pwa-asset-generator logo.svg ./assets --scrape false --icon-only + $ pwa-asset-generator logo.svg ./assets --scrape false --icon-only --path "%PUBLIC_URL%" $ pwa-asset-generator https://raw.githubusercontent.com/onderceylan/pwa-asset-generator/HEAD/static/logo.png -p "15%" -b "linear-gradient(to right, #fa709a 0%, #fee140 100%)" Flag examples - --background="rgba(255, 255, 255, .5)" - --opaque=false - --padding="10px" - --scrape=false - --manifest=./src/manifest.json - --index=./src/index.html - --type=jpeg - --quality=80 + --background "rgba(255, 255, 255, .5)" + --opaque false + --padding "10px" + --scrape false + --manifest ./src/manifest.json + --index ./src/index.html + --path "%PUBLIC_URL%" + --type jpeg + --quality 80 --splash-only --icon-only --landscape-only --portrait-only - --log=false + --log false ``` ### Module diff --git a/__snapshots__/cli.test.js.snap b/__snapshots__/cli.test.js.snap index c7a588a0..781e2eb7 100644 --- a/__snapshots__/cli.test.js.snap +++ b/__snapshots__/cli.test.js.snap @@ -87,6 +87,93 @@ exports[`generates icons and splash screens when both only flags exist 1`] = ` " `; +exports[`generates icons and splash screens with path prefix 1`] = ` +" +[ + { + \\"src\\": \\"temp/manifest-icon-192.png\\", + \\"sizes\\": \\"192x192\\", + \\"type\\": \\"image/png\\" + }, + { + \\"src\\": \\"temp/manifest-icon-512.png\\", + \\"sizes\\": \\"512x512\\", + \\"type\\": \\"image/png\\" + } +] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" +`; + exports[`generates icons only 1`] = ` " [ diff --git a/cli.js b/cli.js index d08d7f7d..64871af9 100755 --- a/cli.js +++ b/cli.js @@ -21,6 +21,7 @@ $ pwa-asset-generator --help -s --scrape Scraping Apple Human Interface guidelines to fetch splash screen specs [default: true] -m --manifest Web app manifest file path to automatically update manifest file with the generated icons -i --index Index html file path to automatically put splash screen and icon meta tags in + -a --path Path prefix to prepend for href links generated for meta tags -t --type Image type: png|jpeg [default: png] -q --quality Image quality: 0...100 (Only for JPEG) [default: 100] -h --splash-only Only generate splash screens [default: false] @@ -32,23 +33,24 @@ $ pwa-asset-generator --help Examples $ pwa-asset-generator logo.html . $ pwa-asset-generator https://your-cdn-server.com/assets/logo.png . -t jpeg -q 90 --splash-only --portrait-only - $ pwa-asset-generator logo.svg ./assets --scrape false --icon-only + $ pwa-asset-generator logo.svg ./assets --scrape false --icon-only --path "%PUBLIC_URL%" $ pwa-asset-generator https://raw.githubusercontent.com/onderceylan/pwa-asset-generator/HEAD/static/logo.png -p "15%" -b "linear-gradient(to right, #fa709a 0%, #fee140 100%)" Flag examples - --background="rgba(255, 255, 255, .5)" - --opaque=false - --padding="10px" - --scrape=false - --manifest=./src/manifest.json - --index=./src/index.html - --type=jpeg - --quality=80 + --background "rgba(255, 255, 255, .5)" + --opaque false + --padding "10px" + --scrape false + --manifest ./src/manifest.json + --index ./src/index.html + --path "%PUBLIC_URL%" + --type jpeg + --quality 80 --splash-only --icon-only --landscape-only --portrait-only - --log=false + --log false `, { flags, diff --git a/cli.test.js b/cli.test.js index 50ae51a4..777c6129 100644 --- a/cli.test.js +++ b/cli.test.js @@ -94,3 +94,15 @@ test('generates icons and splash screens when both only flags exist', async () = expect(stdout).toMatchSnapshot(); }); + +test('generates icons and splash screens with path prefix', async () => { + jest.setTimeout(timeout); + + const { stdout } = await execa( + './cli.js', + ['./static/logo.png', './temp', '-s=false', '--path=%PUBLIC_URL%'], + { env: { PAG_TEST_MODE: '1' } }, + ); + + expect(stdout).toMatchSnapshot(); +}); diff --git a/config/constants.js b/config/constants.js index a5d8a7f8..62b03c86 100644 --- a/config/constants.js +++ b/config/constants.js @@ -13,6 +13,10 @@ module.exports = { type: 'string', alias: 'i', }, + path: { + type: 'string', + alias: 'a', + }, opaque: { type: 'boolean', alias: 'o', diff --git a/helpers/pwa.js b/helpers/pwa.js index dbcbc51b..8a6d118c 100644 --- a/helpers/pwa.js +++ b/helpers/pwa.js @@ -14,7 +14,11 @@ const generateIconsContentForManifest = (savedImages, manifestJsonPath) => { })); }; -const generateAppleTouchIconHtml = (savedImages, indexHtmlPath) => { +const generateAppleTouchIconHtml = ( + savedImages, + indexHtmlPath, + pathPrefix = '', +) => { return savedImages .filter(image => image.name.startsWith(constants.APPLE_ICON_FILENAME_PREFIX), @@ -22,13 +26,17 @@ const generateAppleTouchIconHtml = (savedImages, indexHtmlPath) => { .map(({ width, path }) => constants.APPLE_TOUCH_ICON_META_HTML( width, - file.getRelativeImagePath(indexHtmlPath, path), + pathPrefix + file.getRelativeImagePath(indexHtmlPath, path), ), ) .join(''); }; -const generateAppleLaunchImageHtml = (savedImages, indexHtmlPath) => { +const generateAppleLaunchImageHtml = ( + savedImages, + indexHtmlPath, + pathPrefix = '', +) => { return savedImages .filter(image => image.name.startsWith(constants.APPLE_SPLASH_FILENAME_PREFIX), @@ -37,7 +45,7 @@ const generateAppleLaunchImageHtml = (savedImages, indexHtmlPath) => { constants.APPLE_LAUNCH_SCREEN_META_HTML( width, height, - file.getRelativeImagePath(indexHtmlPath, path), + pathPrefix + file.getRelativeImagePath(indexHtmlPath, path), scaleFactor, orientation, ), @@ -45,11 +53,19 @@ const generateAppleLaunchImageHtml = (savedImages, indexHtmlPath) => { .join(''); }; -const generateHtmlForIndexPage = (savedImages, indexHtmlPath) => { +const getPathPrefix = pathPrefix => { + if (pathPrefix) { + return `${pathPrefix}/`; + } + return ''; +}; + +const generateHtmlForIndexPage = (savedImages, indexHtmlPath, pathPrefix) => { + const prependPath = getPathPrefix(pathPrefix); return `\ -${generateAppleTouchIconHtml(savedImages, indexHtmlPath)} +${generateAppleTouchIconHtml(savedImages, indexHtmlPath, prependPath)} -${generateAppleLaunchImageHtml(savedImages, indexHtmlPath)}`; +${generateAppleLaunchImageHtml(savedImages, indexHtmlPath, prependPath)}`; }; const addIconsToManifest = async (manifestContent, manifestJsonFilePath) => { diff --git a/index.d.ts b/index.d.ts index cf419d7b..ea17859b 100644 --- a/index.d.ts +++ b/index.d.ts @@ -42,6 +42,13 @@ declare namespace PwaAssetGenerator { */ readonly index?: string; + /** + Path prefix to prepend for href links generated for meta tags + + @example %PUBLIC_URL% + */ + readonly path?: string; + /** Image type diff --git a/main.js b/main.js index ee575d41..703855e1 100644 --- a/main.js +++ b/main.js @@ -29,7 +29,11 @@ const generateImages = async (source, _output, _options, loggerFn) => { savedImages, options.manifest, ); - const htmlContent = pwa.generateHtmlForIndexPage(savedImages, options.index); + const htmlContent = pwa.generateHtmlForIndexPage( + savedImages, + options.index, + options.path, + ); if (!options.splashOnly) { if (options.manifest) { From afce4af5ca78f7a105e615cb5abab0a4269deeeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96nder=20Ceylan?= Date: Mon, 2 Sep 2019 00:14:53 +0200 Subject: [PATCH 7/9] feat(main): exposed private API via main.js Added generateImages function to make use of the lib functionality via node scripting fix #5 --- cli.js | 84 ++---------------------------------------------- helpers/flags.js | 29 +++++++++++++++++ main.js | 72 +++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 4 files changed, 105 insertions(+), 82 deletions(-) create mode 100644 helpers/flags.js create mode 100644 main.js diff --git a/cli.js b/cli.js index 4b95b836..4dbb70bb 100755 --- a/cli.js +++ b/cli.js @@ -1,10 +1,10 @@ #!/usr/bin/env node const meow = require('meow'); -const puppets = require('./puppets'); -const pwa = require('./helpers/pwa'); const preLogger = require('./helpers/logger'); +const main = require('./main'); +const logger = preLogger('cli'); const cli = meow( ` $ pwa-asset-generator --help @@ -112,87 +112,9 @@ $ pwa-asset-generator --help }, ); -const source = cli.input[0]; -let output = cli.input[1]; -let options = cli.flags; -const logger = preLogger('cli'); - -const normalizeOnlyFlagPairs = (flag1Key, flag2Key, opts) => { - const stripOnly = key => key.replace('Only', ''); - if (opts[flag1Key] && opts[flag2Key]) { - logger.warn( - `Hmm, you want to _only_ generate both ${stripOnly( - flag1Key, - )} and ${stripOnly( - flag2Key, - )} set. Ignoring --x-only settings as this is default behavior`, - ); - return { - ...opts, - [flag1Key]: false, - [flag2Key]: false, - }; - } - return opts; -}; - -if (!source) { - logger.error('Please specify a URL or file path as a source'); - process.exit(1); -} - -options = normalizeOnlyFlagPairs('splashOnly', 'iconOnly', options); -options = normalizeOnlyFlagPairs('landscapeOnly', 'portraitOnly', options); - -if (!output) { - output = '.'; -} - (async () => { try { - const savedImages = await puppets.generateImages(source, output, options); - const manifestJsonContent = pwa.generateIconsContentForManifest( - savedImages, - options.manifest, - ); - const htmlContent = pwa.generateHtmlForIndexPage( - savedImages, - options.index, - ); - - if (!options.splashOnly) { - if (options.manifest) { - await pwa.addIconsToManifest(manifestJsonContent, options.manifest); - logger.success( - `Icons are saved to Web App Manifest file ${options.manifest}`, - ); - } else if (!options.splashOnly) { - logger.warn( - 'Web App Manifest file is not specified, printing out the content to console instead', - ); - logger.success( - 'Below is the icons content for your manifest.json file. You can copy/paste it manually', - ); - process.stdout.write( - `\n${JSON.stringify(manifestJsonContent, null, 2)}\n\n`, - ); - } - } - - if (options.index) { - await pwa.addMetaTagsToIndexPage(htmlContent, options.index); - logger.success( - `iOS meta tags are saved to index html file ${options.index}`, - ); - } else { - logger.warn( - 'Index html file is not specified, printing out the content to console instead', - ); - logger.success( - 'Below is the iOS meta tags content for your index.html file. You can copy/paste it manually', - ); - process.stdout.write(`\n${htmlContent}\n`); - } + await main.generateImages(cli.input[0], cli.input[1], cli.flags, logger); } catch (e) { logger.error(e); process.exit(1); diff --git a/helpers/flags.js b/helpers/flags.js new file mode 100644 index 00000000..ee6b545f --- /dev/null +++ b/helpers/flags.js @@ -0,0 +1,29 @@ +const normalizeOnlyFlagPairs = (flag1Key, flag2Key, opts, logger) => { + const stripOnly = key => key.replace('Only', ''); + if (opts[flag1Key] && opts[flag2Key]) { + logger.warn( + `Hmm, you want to _only_ generate both ${stripOnly( + flag1Key, + )} and ${stripOnly( + flag2Key, + )} set. Ignoring --x-only settings as this is default behavior`, + ); + return { + [flag1Key]: false, + [flag2Key]: false, + }; + } + return {}; +}; + +const normalizeOutput = output => { + if (!output) { + return '.'; + } + return output; +}; + +module.exports = { + normalizeOnlyFlagPairs, + normalizeOutput, +}; diff --git a/main.js b/main.js new file mode 100644 index 00000000..612d4add --- /dev/null +++ b/main.js @@ -0,0 +1,72 @@ +const pwa = require('./helpers/pwa'); +const puppets = require('./puppets'); +const flags = require('./helpers/flags'); +const preLogger = require('./helpers/logger'); + +const generateImages = async (source, _output, _options, loggerInjection) => { + const logger = loggerInjection || preLogger(generateImages.name); + + if (!source) { + throw Error('Please specify a URL or file path as a source'); + } + + const options = { + ..._options, + ...flags.normalizeOnlyFlagPairs('splashOnly', 'iconOnly', _options, logger), + ...flags.normalizeOnlyFlagPairs( + 'landscapeOnly', + 'portraitOnly', + _options, + logger, + ), + }; + + const output = flags.normalizeOutput(_output); + + const savedImages = await puppets.generateImages(source, output, options); + const manifestJsonContent = pwa.generateIconsContentForManifest( + savedImages, + options.manifest, + ); + const htmlContent = pwa.generateHtmlForIndexPage(savedImages, options.index); + + if (!options.splashOnly) { + if (options.manifest) { + await pwa.addIconsToManifest(manifestJsonContent, options.manifest); + logger.success( + `Icons are saved to Web App Manifest file ${options.manifest}`, + ); + } else if (!options.splashOnly) { + logger.warn( + 'Web App Manifest file is not specified, printing out the content to console instead', + ); + logger.success( + 'Below is the icons content for your manifest.json file. You can copy/paste it manually', + ); + process.stdout.write( + `\n${JSON.stringify(manifestJsonContent, null, 2)}\n\n`, + ); + } + } + + if (options.index) { + await pwa.addMetaTagsToIndexPage(htmlContent, options.index); + logger.success( + `iOS meta tags are saved to index html file ${options.index}`, + ); + } else { + logger.warn( + 'Index html file is not specified, printing out the content to console instead', + ); + logger.success( + 'Below is the iOS meta tags content for your index.html file. You can copy/paste it manually', + ); + process.stdout.write(`\n${htmlContent}\n`); + } + + return savedImages; +}; + +module.exports = { + generateImages, +}; diff --git a/package.json b/package.json index 455fc94b..fb4c2820 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "pwa-asset-generator", "version": "1.1.7", "description": "PWA asset generator based on Puppeteer. Automatically generates icons and splash screens guided by Web App Manifest specs and Apple Human Interface guidelines. Updates manifest.json and index.html files with the generated images.", - "main": "cli.js", + "main": "main.js", "bin": { "pwa-asset-generator": "cli.js" }, From 72a7fe406410f149e277fd5b1255a078f4b50d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96nder=20Ceylan?= Date: Wed, 11 Sep 2019 15:11:24 +0200 Subject: [PATCH 8/9] feat(main): refactored main and added index.d.ts Refactored main.js file to return saved images array, html content of meta tags and manifest json icons array. Added interfaces and docs via index.d.ts and updated readme. fix #5 --- .eslintrc | 1 + .npmignore | 2 + README.md | 27 +++- __snapshots__/cli.test.js.snap | 7 + cli.js | 72 +-------- cli.test.js | 10 +- config/constants.js | 66 ++++++++ helpers/file.js | 8 +- helpers/flags.js | 13 ++ helpers/logger.js | 21 ++- helpers/url.js | 3 +- index.d.ts | 286 +++++++++++++++++++++++++++++++++ main.js | 13 +- package.json | 1 + puppets.js | 18 +-- 15 files changed, 452 insertions(+), 96 deletions(-) create mode 100644 index.d.ts diff --git a/.eslintrc b/.eslintrc index 3187d18f..418663d8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -18,5 +18,6 @@ "ecmaVersion": 2018 }, "rules": { + "no-prototype-builtins": "off" } } diff --git a/.npmignore b/.npmignore index e1a60b2b..5aeff52d 100644 --- a/.npmignore +++ b/.npmignore @@ -10,4 +10,6 @@ __snapshots__/* .prettierrc .travis.yml .releaserc +.idea +.github *.test.js diff --git a/README.md b/README.md index 169cf916..b023b3c2 100644 --- a/README.md +++ b/README.md @@ -66,15 +66,16 @@ $ pwa-asset-generator --help -b --background Page background to use when image source is provided: css value [default: transparent] -o --opaque Making screenshots to be saved with a background color [default: true] -p --padding Padding to use when image source provided: css value [default: "10%"] - -s --scrape Scraping Apple Human Interface Guidelines to fetch splash screen specs [default: true] - -m --manifest Web app manifest file path to automatically update manifest file with the generated images - -i --index Index html file path to automatically put splash screen meta tags in + -s --scrape Scraping Apple Human Interface guidelines to fetch splash screen specs [default: true] + -m --manifest Web app manifest file path to automatically update manifest file with the generated icons + -i --index Index html file path to automatically put splash screen and icon meta tags in -t --type Image type: png|jpeg [default: png] -q --quality Image quality: 0...100 (Only for JPEG) [default: 100] -h --splash-only Only generate splash screens [default: false] -c --icon-only Only generate icons [default: false] -l --landscape-only Only generate landscape splash screens [default: false] -r --portrait-only Only generate portrait splash screens [default: false] + -g --log Logs the steps of the library process [default: true] Examples $ pwa-asset-generator logo.html . @@ -95,6 +96,26 @@ $ pwa-asset-generator --help --icon-only --landscape-only --portrait-only + --log=false +``` + +### Module + +```javascript +const pwaAssetGenerator = require('pwa-asset-generator'); + +(async () => { + const { savedImages, htmlContent, manifestJsonContent } = await pwaAssetGenerator.generateImages( + 'https://raw.githubusercontent.com/onderceylan/pwa-asset-generator/HEAD/static/logo.png', + './temp', + { + scrape: false, + background: "linear-gradient(to right, #fa709a 0%, #fee140 100%)", + splashOnly: true, + portraitOnly: true, + log: false + }); +})(); ``` ## Troubleshooting diff --git a/__snapshots__/cli.test.js.snap b/__snapshots__/cli.test.js.snap index 54157b32..c7a588a0 100644 --- a/__snapshots__/cli.test.js.snap +++ b/__snapshots__/cli.test.js.snap @@ -16,6 +16,7 @@ exports[`generates icons and splash screens when both only flags exist 1`] = ` ] + @@ -82,6 +83,7 @@ exports[`generates icons and splash screens when both only flags exist 1`] = ` + " `; @@ -101,12 +103,14 @@ exports[`generates icons only 1`] = ` ] + + " `; @@ -144,6 +148,7 @@ exports[`generates landscape splash screens only 1`] = ` + " `; @@ -181,6 +186,7 @@ exports[`generates portrait splash screens only 1`] = ` + " `; @@ -248,5 +254,6 @@ exports[`generates splash screens only 1`] = ` + " `; diff --git a/cli.js b/cli.js index 4dbb70bb..d08d7f7d 100755 --- a/cli.js +++ b/cli.js @@ -3,8 +3,8 @@ const meow = require('meow'); const preLogger = require('./helpers/logger'); const main = require('./main'); +const { FLAGS: flags } = require('./config/constants'); -const logger = preLogger('cli'); const cli = meow( ` $ pwa-asset-generator --help @@ -18,15 +18,16 @@ $ pwa-asset-generator --help -b --background Page background to use when image source is provided: css value [default: transparent] -o --opaque Making screenshots to be saved with a background color [default: true] -p --padding Padding to use when image source provided: css value [default: "10%"] - -s --scrape Scraping Apple Human Interface Guidelines to fetch splash screen specs [default: true] - -m --manifest Web app manifest file path to automatically update manifest file with the generated images - -i --index Index html file path to automatically put splash screen meta tags in + -s --scrape Scraping Apple Human Interface guidelines to fetch splash screen specs [default: true] + -m --manifest Web app manifest file path to automatically update manifest file with the generated icons + -i --index Index html file path to automatically put splash screen and icon meta tags in -t --type Image type: png|jpeg [default: png] -q --quality Image quality: 0...100 (Only for JPEG) [default: 100] -h --splash-only Only generate splash screens [default: false] -c --icon-only Only generate icons [default: false] -l --landscape-only Only generate landscape splash screens [default: false] -r --portrait-only Only generate portrait splash screens [default: false] + -g --log Logs the steps of the library process [default: true] Examples $ pwa-asset-generator logo.html . @@ -47,70 +48,13 @@ $ pwa-asset-generator --help --icon-only --landscape-only --portrait-only + --log=false `, { - flags: { - background: { - type: 'string', - alias: 'b', - default: 'transparent', - }, - manifest: { - type: 'string', - alias: 'm', - }, - index: { - type: 'string', - alias: 'i', - }, - opaque: { - type: 'boolean', - alias: 'o', - default: true, - }, - scrape: { - type: 'boolean', - alias: 's', - default: true, - }, - padding: { - type: 'string', - alias: 'p', - default: '10%', - }, - type: { - type: 'string', - alias: 't', - default: 'png', - }, - quality: { - type: 'number', - alias: 'q', - default: 100, - }, - splashOnly: { - type: 'boolean', - alias: 'h', - default: false, - }, - iconOnly: { - type: 'boolean', - alias: 'c', - default: false, - }, - landscapeOnly: { - type: 'boolean', - alias: 'l', - default: false, - }, - portraitOnly: { - type: 'boolean', - alias: 'r', - default: false, - }, - }, + flags, }, ); +const logger = preLogger('cli', cli.flags); (async () => { try { diff --git a/cli.test.js b/cli.test.js index 96c4f508..50ae51a4 100644 --- a/cli.test.js +++ b/cli.test.js @@ -29,7 +29,7 @@ test('generates icons only', async () => { const { stdout } = await execa( './cli.js', ['./static/logo.png', './temp', '-s=false', '--icon-only'], - { env: { PAG_NO_TRACE: '1' } }, + { env: { PAG_TEST_MODE: '1' } }, ); expect(stdout).toMatchSnapshot(); @@ -41,7 +41,7 @@ test('generates splash screens only', async () => { const { stdout } = await execa( './cli.js', ['./static/logo.png', './temp', '-s=false', '--splash-only'], - { env: { PAG_NO_TRACE: '1' } }, + { env: { PAG_TEST_MODE: '1' } }, ); expect(stdout).toMatchSnapshot(); @@ -59,7 +59,7 @@ test('generates portrait splash screens only', async () => { '--splash-only', '--portrait-only', ], - { env: { PAG_NO_TRACE: '1' } }, + { env: { PAG_TEST_MODE: '1' } }, ); expect(stdout).toMatchSnapshot(); @@ -77,7 +77,7 @@ test('generates landscape splash screens only', async () => { '--splash-only', '--landscape-only', ], - { env: { PAG_NO_TRACE: '1' } }, + { env: { PAG_TEST_MODE: '1' } }, ); expect(stdout).toMatchSnapshot(); @@ -89,7 +89,7 @@ test('generates icons and splash screens when both only flags exist', async () = const { stdout } = await execa( './cli.js', ['./static/logo.png', './temp', '-s=false', '--splash-only', '--icon-only'], - { env: { PAG_NO_TRACE: '1' } }, + { env: { PAG_TEST_MODE: '1' } }, ); expect(stdout).toMatchSnapshot(); diff --git a/config/constants.js b/config/constants.js index 42ccbcd9..a5d8a7f8 100644 --- a/config/constants.js +++ b/config/constants.js @@ -1,4 +1,70 @@ module.exports = { + FLAGS: { + background: { + type: 'string', + alias: 'b', + default: 'transparent', + }, + manifest: { + type: 'string', + alias: 'm', + }, + index: { + type: 'string', + alias: 'i', + }, + opaque: { + type: 'boolean', + alias: 'o', + default: true, + }, + scrape: { + type: 'boolean', + alias: 's', + default: true, + }, + padding: { + type: 'string', + alias: 'p', + default: '10%', + }, + type: { + type: 'string', + alias: 't', + default: 'png', + }, + quality: { + type: 'number', + alias: 'q', + default: 100, + }, + splashOnly: { + type: 'boolean', + alias: 'h', + default: false, + }, + iconOnly: { + type: 'boolean', + alias: 'c', + default: false, + }, + landscapeOnly: { + type: 'boolean', + alias: 'l', + default: false, + }, + portraitOnly: { + type: 'boolean', + alias: 'r', + default: false, + }, + log: { + type: 'boolean', + alias: 'g', + default: true, + }, + }, + PUPPETEER_LAUNCH_ARGS: [ '--log-level=3', // Fatal only '--no-default-browser-check', diff --git a/helpers/file.js b/helpers/file.js index 35dba969..35efe348 100644 --- a/helpers/file.js +++ b/helpers/file.js @@ -32,7 +32,13 @@ const isHtmlFile = file => { }; const getAppDir = () => { - return path.dirname(require.main.filename); + let appPath; + try { + appPath = require.resolve('pwa-asset-generator'); + } catch (e) { + appPath = require.main.filename; + } + return path.dirname(appPath); }; const getShellHtmlFilePath = () => { diff --git a/helpers/flags.js b/helpers/flags.js index ee6b545f..ca3718ba 100644 --- a/helpers/flags.js +++ b/helpers/flags.js @@ -1,3 +1,5 @@ +const constants = require('../config/constants'); + const normalizeOnlyFlagPairs = (flag1Key, flag2Key, opts, logger) => { const stripOnly = key => key.replace('Only', ''); if (opts[flag1Key] && opts[flag2Key]) { @@ -23,7 +25,18 @@ const normalizeOutput = output => { return output; }; +const getDefaultOptions = () => { + const { FLAGS: flags } = constants; + + return Object.keys(flags) + .filter(flagKey => flags[flagKey].hasOwnProperty('default')) + .reduce((acc, curr) => { + return { ...acc, [curr]: flags[curr].default }; + }, {}); +}; + module.exports = { normalizeOnlyFlagPairs, normalizeOutput, + getDefaultOptions, }; diff --git a/helpers/logger.js b/helpers/logger.js index d3eced84..00300e04 100644 --- a/helpers/logger.js +++ b/helpers/logger.js @@ -1,8 +1,11 @@ const chalk = require('chalk'); -const noTrace = !!+process.env.PAG_NO_TRACE; +const testMode = !!+process.env.PAG_TEST_MODE; + +const logger = (prefix, options) => { + const isLogEnabled = + options && options.hasOwnProperty('log') ? options.log : true; -const logger = prefix => { const getTime = () => { return chalk.inverse(new Date().toLocaleTimeString()); }; @@ -12,18 +15,23 @@ const logger = prefix => { }; /* eslint-disable no-console */ + const raw = (...args) => { + if (!isLogEnabled) return; + console.log(...args); + }; + const log = (...args) => { - if (noTrace) return; + if (testMode || !isLogEnabled) return; console.log(getTime(), getPrefix(), ...args); }; const warn = (...args) => { - if (noTrace) return; + if (testMode || !isLogEnabled) return; console.warn(getTime(), getPrefix(), chalk.yellow(...args), '🤔'); }; const trace = (...args) => { - if (noTrace) return; + if (testMode || !isLogEnabled) return; console.trace(getTime(), getPrefix(), ...args); }; @@ -32,12 +40,13 @@ const logger = prefix => { }; const success = (...args) => { - if (noTrace) return; + if (testMode || !isLogEnabled) return; console.log(getTime(), getPrefix(), chalk.green(...args), '🙌'); }; /* eslint-enable no-console */ return { + raw, log, warn, trace, diff --git a/helpers/url.js b/helpers/url.js index a61b1e5f..11a3849f 100644 --- a/helpers/url.js +++ b/helpers/url.js @@ -25,12 +25,13 @@ const isUrlExists = source => { }; const getAddress = async (source, options) => { - const logger = preLogger(getAddress.name); + const logger = preLogger(getAddress.name, options); const useShell = async (isSourceUrl = false) => { try { await file.saveHtmlShell(source, options, isSourceUrl); } catch (e) { + logger.error(e); throw Error('Failed saving html shell'); } diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 00000000..cf419d7b --- /dev/null +++ b/index.d.ts @@ -0,0 +1,286 @@ +declare namespace PwaAssetGenerator { + + interface Options { + /** + Page background to use when image source is provided + Same as css background property + + @default transparent + */ + readonly background?: string; + + /** + Making screenshots to be saved with a background color + Uses white background when background option is not provided + + @default true + */ + readonly opaque?: boolean; + + /** + Padding to use when image source provided + Same as css padding property + + @default "10%" + */ + readonly padding?: string; + + /** + Scraping Apple Human Interface guidelines to fetch splash screen specs + + @default true + */ + readonly scrape?: boolean; + + /** + Web app manifest file path to automatically update manifest file with the generated icons + */ + readonly manifest?: string; + + /** + Index html file path to automatically put splash screen and icon meta tags in + */ + readonly index?: string; + + /** + Image type + + @default png + */ + readonly type?: 'png' | 'jpeg'; + + /** + Image quality: 0...100 + Enabled only for jpeg image type + + @default 100 + */ + readonly quality?: number; + + /** + Only generate splash screens + + @default false + */ + readonly splashOnly?: boolean; + + /** + Only generate icons + + @default false + */ + readonly iconOnly?: boolean; + + /** + Only generate landscape splash screens + Disabled when iconOnly option is provided + + @default false + */ + readonly landscapeOnly?: boolean; + + /** + Only generate portrait splash screens + Disabled when iconOnly option is provided + + @default false + */ + readonly portraitOnly?: boolean; + } + + interface SavedImage { + /** + Name of the saved image file, without file extension + */ + name: string; + + /** + Image width in pixels + */ + width: number; + + /** + Image height in pixels + */ + height: number; + + /** + Device scale factor used for generating HTML meta tags for iOS splash screens + Defaults to null for icons + + @default null + */ + scaleFactor: number | null; + + /** + Saved image path + Path is relative to execution folder + */ + path: string; + + /** + Device orientation used for generating HTML meta tags for iOS splash screens + Defaults to null for icons + + @default null + */ + orientation: 'landscape' | 'portrait' | null; + } + + interface ManifestJsonIcon { + /** + A URL from which a user agent can fetch the image's data + + @tutorial https://www.w3.org/TR/appmanifest/#dom-imageresource-src + @example //icons.example.com/lowres + */ + src: string; + + /** + A string consisting of an unordered set of unique space-separated tokens + + @tutorial https://www.w3.org/TR/appmanifest/#sizes-member + @example 192x192 + */ + sizes?: string; + + /** + Media type of the image + The purpose of this member is to allow a user agent to ignore images of media types it does not support + + @tutorial https://www.w3.org/TR/appmanifest/#dom-imageresource-type + @example image/png + */ + type?: string; + + /** + When an ImageResource is used as an icon, a developer can hint that + the image is intended to serve some special purpose in the context of the host OS + + @tutorial https://www.w3.org/TR/appmanifest/#dfn-icon-purposes + @default any + */ + purpose?: 'badge' | 'maskable' | 'any'; + + /** + The platform member represents the platform to which a containing object applies + + @tutorial https://github.com/w3c/manifest/wiki/Platforms + */ + platform?: 'chrome_web_store' | 'play' | 'itunes' | 'windows'; + } + + interface Result { + /** + Saved images array that keeps both splash screens and icons, with image properties + + @example + ```javascript + [{ + name: 'apple-splash-1136-640', + width: 1136, + height: 640, + scaleFactor: 2, + path: 'temp/apple-splash-1136-640.png', + orientation: 'landscape' + }, + { + name: 'apple-icon-180', + width: 180, + height: 180, + scaleFactor: null, + path: 'temp/apple-icon-180.png', + orientation: null + }] + ``` + */ + savedImages: SavedImage[]; + + /** + Meta tags to be added to index.html file + + @example + ```html + + + + + ``` + */ + htmlContent: string; + + /** + Icons to be added to manifest.json's icons property + + @example + ```json + [{ + "src": "assets/pwa/manifest-icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "assets/pwa/manifest-icon-512.png", + "sizes": "512x512", + "type": "image/png" + }] + ``` + */ + manifestJsonContent: ManifestJsonIcon[]; + } + + /** + Logger function to print out steps of the lib + + @param prefix - Shows the origin of the log, e.g. function name + @param options - Option flags of the library in an object + */ + interface Logger { + (prefix: string, options?: Options): { + raw(): string; + log(): string; + warn(): string; + trace(): string; + error(): string; + success(): string; + } + } +} + +declare const pwaAssetGenerator: { + /** + Generates PWA assets based on a source input and saves generated images in the output folder provided + + @param source - A local image file, a local HTML file, a remote image or remote HTML file path + @param outputFolderPath - The path of the folder to save the images in + @param options - Option flags of the library in an object, keeps default values + @param logger - An optional logger function to log the output + @returns A promise of result object that resolves when all images are generated and file updates are finalized + + @example + ```javascript + import pwaAssetGenerator = require('pwa-asset-generator'); + + (async () => { + const { savedImages, htmlContent, manifestJsonContent } = await pwaAssetGenerator.generateImages( + 'https://raw.githubusercontent.com/onderceylan/pwa-asset-generator/HEAD/static/logo.png', + './temp', + { + scrape: false, + background: "linear-gradient(to right, #fa709a 0%, #fee140 100%)", + splashOnly: true, + portraitOnly: true, + log: false + }); + })(); + ``` + */ + generateImages( + source: string, + outputFolderPath: string, + options?: PwaAssetGenerator.Options, + logger?: PwaAssetGenerator.Logger, + ): Promise; +}; + +export = pwaAssetGenerator; diff --git a/main.js b/main.js index 612d4add..ee575d41 100644 --- a/main.js +++ b/main.js @@ -3,14 +3,15 @@ const puppets = require('./puppets'); const flags = require('./helpers/flags'); const preLogger = require('./helpers/logger'); -const generateImages = async (source, _output, _options, loggerInjection) => { - const logger = loggerInjection || preLogger(generateImages.name); +const generateImages = async (source, _output, _options, loggerFn) => { + const logger = loggerFn || preLogger(generateImages.name, _options); if (!source) { throw Error('Please specify a URL or file path as a source'); } const options = { + ...flags.getDefaultOptions(), ..._options, ...flags.normalizeOnlyFlagPairs('splashOnly', 'iconOnly', _options, logger), ...flags.normalizeOnlyFlagPairs( @@ -43,9 +44,7 @@ const generateImages = async (source, _output, _options, loggerInjection) => { logger.success( 'Below is the icons content for your manifest.json file. You can copy/paste it manually', ); - process.stdout.write( - `\n${JSON.stringify(manifestJsonContent, null, 2)}\n\n`, - ); + logger.raw(`\n${JSON.stringify(manifestJsonContent, null, 2)}\n\n`); } } @@ -61,10 +60,10 @@ const generateImages = async (source, _output, _options, loggerInjection) => { logger.success( 'Below is the iOS meta tags content for your index.html file. You can copy/paste it manually', ); - process.stdout.write(`\n${htmlContent}\n`); + logger.raw(`\n${htmlContent}\n`); } - return savedImages; + return { savedImages, htmlContent, manifestJsonContent }; }; module.exports = { diff --git a/package.json b/package.json index fb4c2820..9f036f5c 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "1.1.7", "description": "PWA asset generator based on Puppeteer. Automatically generates icons and splash screens guided by Web App Manifest specs and Apple Human Interface guidelines. Updates manifest.json and index.html files with the generated images.", "main": "main.js", + "types": "index.d.ts", "bin": { "pwa-asset-generator": "cli.js" }, diff --git a/puppets.js b/puppets.js index 1467d449..92e57179 100644 --- a/puppets.js +++ b/puppets.js @@ -6,8 +6,8 @@ const file = require('./helpers/file'); const images = require('./helpers/images'); const preLogger = require('./helpers/logger'); -const getAppleSplashScreenData = async browser => { - const logger = preLogger(getAppleSplashScreenData.name); +const getAppleSplashScreenData = async (browser, options) => { + const logger = preLogger(getAppleSplashScreenData.name, options); const page = await browser.newPage(); await page.setUserAgent(constants.EMULATED_USER_AGENT); logger.log( @@ -86,8 +86,8 @@ const getAppleSplashScreenData = async browser => { return splashScreenData; }; -const getDeviceScaleFactorData = async browser => { - const logger = preLogger(getDeviceScaleFactorData.name); +const getDeviceScaleFactorData = async (browser, options) => { + const logger = preLogger(getDeviceScaleFactorData.name, options); const page = await browser.newPage(); await page.setUserAgent(constants.EMULATED_USER_AGENT); logger.log( @@ -156,7 +156,7 @@ const getDeviceScaleFactorData = async browser => { }; const getSplashScreenMetaData = async options => { - const logger = preLogger(getSplashScreenMetaData.name); + const logger = preLogger(getSplashScreenMetaData.name, options); if (!options.scrape) { logger.log( @@ -178,8 +178,8 @@ const getSplashScreenMetaData = async options => { let splashScreenUniformMetaData; try { - const splashScreenData = await getAppleSplashScreenData(browser); - const scaleFactorData = await getDeviceScaleFactorData(browser); + const splashScreenData = await getAppleSplashScreenData(browser, options); + const scaleFactorData = await getDeviceScaleFactorData(browser, options); splashScreenUniformMetaData = images.getSplashScreenScaleFactorUnionData( splashScreenData, scaleFactorData, @@ -199,7 +199,7 @@ const getSplashScreenMetaData = async options => { }; const saveImages = async (imageList, source, output, options) => { - const logger = preLogger(saveImages.name); + const logger = preLogger(saveImages.name, options); logger.log('Initialising puppeteer to take screenshots', '🤖'); const address = await url.getAddress(source, options); @@ -243,7 +243,7 @@ const saveImages = async (imageList, source, output, options) => { }; const generateImages = async (source, output, options) => { - const logger = preLogger(generateImages.name); + const logger = preLogger(generateImages.name, options); const splashScreenMetaData = await getSplashScreenMetaData(options); const allImages = [ ...(!options.iconOnly From 268c6715e29e1fcee1aa3aa2fbdb2d144ca47677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96nder=20Ceylan?= Date: Thu, 12 Sep 2019 07:44:20 +0200 Subject: [PATCH 9/9] feat(main): added option to provide path prefix to generated href links fix #31 --- README.md | 22 +++++---- __snapshots__/cli.test.js.snap | 87 ++++++++++++++++++++++++++++++++++ cli.js | 22 +++++---- cli.test.js | 12 +++++ config/constants.js | 4 ++ helpers/pwa.js | 30 +++++++++--- index.d.ts | 7 +++ main.js | 6 ++- 8 files changed, 162 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index b023b3c2..316553b7 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ $ pwa-asset-generator --help -s --scrape Scraping Apple Human Interface guidelines to fetch splash screen specs [default: true] -m --manifest Web app manifest file path to automatically update manifest file with the generated icons -i --index Index html file path to automatically put splash screen and icon meta tags in + -a --path Path prefix to prepend for href links generated for meta tags -t --type Image type: png|jpeg [default: png] -q --quality Image quality: 0...100 (Only for JPEG) [default: 100] -h --splash-only Only generate splash screens [default: false] @@ -80,23 +81,24 @@ $ pwa-asset-generator --help Examples $ pwa-asset-generator logo.html . $ pwa-asset-generator https://your-cdn-server.com/assets/logo.png . -t jpeg -q 90 --splash-only --portrait-only - $ pwa-asset-generator logo.svg ./assets --scrape false --icon-only + $ pwa-asset-generator logo.svg ./assets --scrape false --icon-only --path "%PUBLIC_URL%" $ pwa-asset-generator https://raw.githubusercontent.com/onderceylan/pwa-asset-generator/HEAD/static/logo.png -p "15%" -b "linear-gradient(to right, #fa709a 0%, #fee140 100%)" Flag examples - --background="rgba(255, 255, 255, .5)" - --opaque=false - --padding="10px" - --scrape=false - --manifest=./src/manifest.json - --index=./src/index.html - --type=jpeg - --quality=80 + --background "rgba(255, 255, 255, .5)" + --opaque false + --padding "10px" + --scrape false + --manifest ./src/manifest.json + --index ./src/index.html + --path "%PUBLIC_URL%" + --type jpeg + --quality 80 --splash-only --icon-only --landscape-only --portrait-only - --log=false + --log false ``` ### Module diff --git a/__snapshots__/cli.test.js.snap b/__snapshots__/cli.test.js.snap index c7a588a0..781e2eb7 100644 --- a/__snapshots__/cli.test.js.snap +++ b/__snapshots__/cli.test.js.snap @@ -87,6 +87,93 @@ exports[`generates icons and splash screens when both only flags exist 1`] = ` " `; +exports[`generates icons and splash screens with path prefix 1`] = ` +" +[ + { + \\"src\\": \\"temp/manifest-icon-192.png\\", + \\"sizes\\": \\"192x192\\", + \\"type\\": \\"image/png\\" + }, + { + \\"src\\": \\"temp/manifest-icon-512.png\\", + \\"sizes\\": \\"512x512\\", + \\"type\\": \\"image/png\\" + } +] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" +`; + exports[`generates icons only 1`] = ` " [ diff --git a/cli.js b/cli.js index d08d7f7d..64871af9 100755 --- a/cli.js +++ b/cli.js @@ -21,6 +21,7 @@ $ pwa-asset-generator --help -s --scrape Scraping Apple Human Interface guidelines to fetch splash screen specs [default: true] -m --manifest Web app manifest file path to automatically update manifest file with the generated icons -i --index Index html file path to automatically put splash screen and icon meta tags in + -a --path Path prefix to prepend for href links generated for meta tags -t --type Image type: png|jpeg [default: png] -q --quality Image quality: 0...100 (Only for JPEG) [default: 100] -h --splash-only Only generate splash screens [default: false] @@ -32,23 +33,24 @@ $ pwa-asset-generator --help Examples $ pwa-asset-generator logo.html . $ pwa-asset-generator https://your-cdn-server.com/assets/logo.png . -t jpeg -q 90 --splash-only --portrait-only - $ pwa-asset-generator logo.svg ./assets --scrape false --icon-only + $ pwa-asset-generator logo.svg ./assets --scrape false --icon-only --path "%PUBLIC_URL%" $ pwa-asset-generator https://raw.githubusercontent.com/onderceylan/pwa-asset-generator/HEAD/static/logo.png -p "15%" -b "linear-gradient(to right, #fa709a 0%, #fee140 100%)" Flag examples - --background="rgba(255, 255, 255, .5)" - --opaque=false - --padding="10px" - --scrape=false - --manifest=./src/manifest.json - --index=./src/index.html - --type=jpeg - --quality=80 + --background "rgba(255, 255, 255, .5)" + --opaque false + --padding "10px" + --scrape false + --manifest ./src/manifest.json + --index ./src/index.html + --path "%PUBLIC_URL%" + --type jpeg + --quality 80 --splash-only --icon-only --landscape-only --portrait-only - --log=false + --log false `, { flags, diff --git a/cli.test.js b/cli.test.js index 50ae51a4..777c6129 100644 --- a/cli.test.js +++ b/cli.test.js @@ -94,3 +94,15 @@ test('generates icons and splash screens when both only flags exist', async () = expect(stdout).toMatchSnapshot(); }); + +test('generates icons and splash screens with path prefix', async () => { + jest.setTimeout(timeout); + + const { stdout } = await execa( + './cli.js', + ['./static/logo.png', './temp', '-s=false', '--path=%PUBLIC_URL%'], + { env: { PAG_TEST_MODE: '1' } }, + ); + + expect(stdout).toMatchSnapshot(); +}); diff --git a/config/constants.js b/config/constants.js index a5d8a7f8..62b03c86 100644 --- a/config/constants.js +++ b/config/constants.js @@ -13,6 +13,10 @@ module.exports = { type: 'string', alias: 'i', }, + path: { + type: 'string', + alias: 'a', + }, opaque: { type: 'boolean', alias: 'o', diff --git a/helpers/pwa.js b/helpers/pwa.js index dbcbc51b..8a6d118c 100644 --- a/helpers/pwa.js +++ b/helpers/pwa.js @@ -14,7 +14,11 @@ const generateIconsContentForManifest = (savedImages, manifestJsonPath) => { })); }; -const generateAppleTouchIconHtml = (savedImages, indexHtmlPath) => { +const generateAppleTouchIconHtml = ( + savedImages, + indexHtmlPath, + pathPrefix = '', +) => { return savedImages .filter(image => image.name.startsWith(constants.APPLE_ICON_FILENAME_PREFIX), @@ -22,13 +26,17 @@ const generateAppleTouchIconHtml = (savedImages, indexHtmlPath) => { .map(({ width, path }) => constants.APPLE_TOUCH_ICON_META_HTML( width, - file.getRelativeImagePath(indexHtmlPath, path), + pathPrefix + file.getRelativeImagePath(indexHtmlPath, path), ), ) .join(''); }; -const generateAppleLaunchImageHtml = (savedImages, indexHtmlPath) => { +const generateAppleLaunchImageHtml = ( + savedImages, + indexHtmlPath, + pathPrefix = '', +) => { return savedImages .filter(image => image.name.startsWith(constants.APPLE_SPLASH_FILENAME_PREFIX), @@ -37,7 +45,7 @@ const generateAppleLaunchImageHtml = (savedImages, indexHtmlPath) => { constants.APPLE_LAUNCH_SCREEN_META_HTML( width, height, - file.getRelativeImagePath(indexHtmlPath, path), + pathPrefix + file.getRelativeImagePath(indexHtmlPath, path), scaleFactor, orientation, ), @@ -45,11 +53,19 @@ const generateAppleLaunchImageHtml = (savedImages, indexHtmlPath) => { .join(''); }; -const generateHtmlForIndexPage = (savedImages, indexHtmlPath) => { +const getPathPrefix = pathPrefix => { + if (pathPrefix) { + return `${pathPrefix}/`; + } + return ''; +}; + +const generateHtmlForIndexPage = (savedImages, indexHtmlPath, pathPrefix) => { + const prependPath = getPathPrefix(pathPrefix); return `\ -${generateAppleTouchIconHtml(savedImages, indexHtmlPath)} +${generateAppleTouchIconHtml(savedImages, indexHtmlPath, prependPath)} -${generateAppleLaunchImageHtml(savedImages, indexHtmlPath)}`; +${generateAppleLaunchImageHtml(savedImages, indexHtmlPath, prependPath)}`; }; const addIconsToManifest = async (manifestContent, manifestJsonFilePath) => { diff --git a/index.d.ts b/index.d.ts index cf419d7b..ea17859b 100644 --- a/index.d.ts +++ b/index.d.ts @@ -42,6 +42,13 @@ declare namespace PwaAssetGenerator { */ readonly index?: string; + /** + Path prefix to prepend for href links generated for meta tags + + @example %PUBLIC_URL% + */ + readonly path?: string; + /** Image type diff --git a/main.js b/main.js index ee575d41..703855e1 100644 --- a/main.js +++ b/main.js @@ -29,7 +29,11 @@ const generateImages = async (source, _output, _options, loggerFn) => { savedImages, options.manifest, ); - const htmlContent = pwa.generateHtmlForIndexPage(savedImages, options.index); + const htmlContent = pwa.generateHtmlForIndexPage( + savedImages, + options.index, + options.path, + ); if (!options.splashOnly) { if (options.manifest) {