From fb569a1d6fcc794ed736ab57012cf2fec08c92ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96nder=20Ceylan?= Date: Mon, 13 Apr 2020 17:43:25 +0200 Subject: [PATCH] feat(meta): added xhtml option to introduce self-closing meta tags Added xhtml option to introduce self-closing xhtml tags. This will be especially handy in JSX context. fix #192 --- README.md | 3 + src/__snapshots__/main.test.ts.snap | 359 ++++++++++++++++++++++++++++ src/cli.ts | 3 + src/config/constants.ts | 31 ++- src/helpers/meta.ts | 7 +- src/main.test.ts | 164 ++++++++----- src/models/options.ts | 7 + 7 files changed, 505 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 8046f87a..f49393cd 100644 --- a/README.md +++ b/README.md @@ -86,12 +86,14 @@ $ pwa-asset-generator --help -r --portrait-only Only generate portrait splash screens [default: false] -d --dark-mode Generate iOS splash screen meta with (prefers-color-scheme: dark) media attr [default: false] -u --single-quotes Generate HTML meta tags with single quotes [default: false] + -x --xhtml Generate HTML meta tags by self-closing the tags [default: false] -g --log Logs the steps of the library process [default: true] Examples $ pwa-asset-generator logo.html $ pwa-asset-generator logo.svg -i ./index.html -m ./manifest.json $ pwa-asset-generator https://your-cdn-server.com/assets/logo.png ./ -t jpg -q 90 --splash-only --portrait-only + $ pwa-asset-generator logo.svg ./assets --splash-only --xhtml --single-quotes $ pwa-asset-generator logo.svg ./assets --scrape false --icon-only --path "%PUBLIC_URL%" $ pwa-asset-generator logo.svg ./assets --icon-only --favicon --opaque false --maskable false $ pwa-asset-generator logo.svg ./assets --dark-mode --background dimgrey --splash-only --type jpeg --quality 80 @@ -117,6 +119,7 @@ $ pwa-asset-generator --help --portrait-only --dark-mode --single-quotes + --xhtml --log false ``` diff --git a/src/__snapshots__/main.test.ts.snap b/src/__snapshots__/main.test.ts.snap index 4cdaf30a..71e00a73 100644 --- a/src/__snapshots__/main.test.ts.snap +++ b/src/__snapshots__/main.test.ts.snap @@ -1545,6 +1545,365 @@ Object { } `; +exports[`generates meta saving meta to index.html creating a favicon meta with xhtml output 1`] = ` +Object { + "htmlMeta": Object { + "appleMobileWebAppCapable": " +", + "appleTouchIcon": " + + + +", + "favicon": " +", + }, + "manifestJsonContent": Array [ + Object { + "purpose": "maskable any", + "sizes": "192x192", + "src": "temp/manifest-icon-192.png", + "type": "image/png", + }, + Object { + "purpose": "maskable any", + "sizes": "512x512", + "src": "temp/manifest-icon-512.png", + "type": "image/png", + }, + ], + "savedImages": Array [ + Object { + "height": 180, + "name": "apple-icon-180", + "orientation": null, + "path": "temp/apple-icon-180.png", + "scaleFactor": undefined, + "width": 180, + }, + Object { + "height": 167, + "name": "apple-icon-167", + "orientation": null, + "path": "temp/apple-icon-167.png", + "scaleFactor": undefined, + "width": 167, + }, + Object { + "height": 152, + "name": "apple-icon-152", + "orientation": null, + "path": "temp/apple-icon-152.png", + "scaleFactor": undefined, + "width": 152, + }, + Object { + "height": 120, + "name": "apple-icon-120", + "orientation": null, + "path": "temp/apple-icon-120.png", + "scaleFactor": undefined, + "width": 120, + }, + Object { + "height": 192, + "name": "manifest-icon-192", + "orientation": null, + "path": "temp/manifest-icon-192.png", + "scaleFactor": undefined, + "width": 192, + }, + Object { + "height": 512, + "name": "manifest-icon-512", + "orientation": null, + "path": "temp/manifest-icon-512.png", + "scaleFactor": undefined, + "width": 512, + }, + Object { + "height": 196, + "name": "favicon-196", + "orientation": null, + "path": "temp/favicon-196.png", + "scaleFactor": undefined, + "width": 196, + }, + ], +} +`; + +exports[`generates meta saving meta to index.html creating apple icons meta with xhtml output 1`] = ` +Object { + "htmlMeta": Object { + "appleMobileWebAppCapable": " +", + "appleTouchIcon": " + + + +", + }, + "manifestJsonContent": Array [ + Object { + "purpose": "maskable any", + "sizes": "192x192", + "src": "temp/manifest-icon-192.png", + "type": "image/png", + }, + Object { + "purpose": "maskable any", + "sizes": "512x512", + "src": "temp/manifest-icon-512.png", + "type": "image/png", + }, + ], + "savedImages": Array [ + Object { + "height": 180, + "name": "apple-icon-180", + "orientation": null, + "path": "temp/apple-icon-180.png", + "scaleFactor": undefined, + "width": 180, + }, + Object { + "height": 167, + "name": "apple-icon-167", + "orientation": null, + "path": "temp/apple-icon-167.png", + "scaleFactor": undefined, + "width": 167, + }, + Object { + "height": 152, + "name": "apple-icon-152", + "orientation": null, + "path": "temp/apple-icon-152.png", + "scaleFactor": undefined, + "width": 152, + }, + Object { + "height": 120, + "name": "apple-icon-120", + "orientation": null, + "path": "temp/apple-icon-120.png", + "scaleFactor": undefined, + "width": 120, + }, + Object { + "height": 192, + "name": "manifest-icon-192", + "orientation": null, + "path": "temp/manifest-icon-192.png", + "scaleFactor": undefined, + "width": 192, + }, + Object { + "height": 512, + "name": "manifest-icon-512", + "orientation": null, + "path": "temp/manifest-icon-512.png", + "scaleFactor": undefined, + "width": 512, + }, + ], +} +`; + +exports[`generates meta saving meta to index.html creating splash screens meta with xhtml output 1`] = ` +Object { + "htmlMeta": Object { + "appleLaunchImage": " + + + + + + + + + + + + + + + + + + + +", + "appleMobileWebAppCapable": " +", + }, + "manifestJsonContent": Array [], + "savedImages": Array [ + Object { + "height": 2732, + "name": "apple-splash-2048-2732", + "orientation": "portrait", + "path": "temp/apple-splash-2048-2732.png", + "scaleFactor": 2, + "width": 2048, + }, + Object { + "height": 2048, + "name": "apple-splash-2732-2048", + "orientation": "landscape", + "path": "temp/apple-splash-2732-2048.png", + "scaleFactor": 2, + "width": 2732, + }, + Object { + "height": 2388, + "name": "apple-splash-1668-2388", + "orientation": "portrait", + "path": "temp/apple-splash-1668-2388.png", + "scaleFactor": 2, + "width": 1668, + }, + Object { + "height": 1668, + "name": "apple-splash-2388-1668", + "orientation": "landscape", + "path": "temp/apple-splash-2388-1668.png", + "scaleFactor": 2, + "width": 2388, + }, + Object { + "height": 2224, + "name": "apple-splash-1668-2224", + "orientation": "portrait", + "path": "temp/apple-splash-1668-2224.png", + "scaleFactor": 2, + "width": 1668, + }, + Object { + "height": 1668, + "name": "apple-splash-2224-1668", + "orientation": "landscape", + "path": "temp/apple-splash-2224-1668.png", + "scaleFactor": 2, + "width": 2224, + }, + Object { + "height": 2048, + "name": "apple-splash-1536-2048", + "orientation": "portrait", + "path": "temp/apple-splash-1536-2048.png", + "scaleFactor": 2, + "width": 1536, + }, + Object { + "height": 1536, + "name": "apple-splash-2048-1536", + "orientation": "landscape", + "path": "temp/apple-splash-2048-1536.png", + "scaleFactor": 2, + "width": 2048, + }, + Object { + "height": 2688, + "name": "apple-splash-1242-2688", + "orientation": "portrait", + "path": "temp/apple-splash-1242-2688.png", + "scaleFactor": 3, + "width": 1242, + }, + Object { + "height": 1242, + "name": "apple-splash-2688-1242", + "orientation": "landscape", + "path": "temp/apple-splash-2688-1242.png", + "scaleFactor": 3, + "width": 2688, + }, + Object { + "height": 2436, + "name": "apple-splash-1125-2436", + "orientation": "portrait", + "path": "temp/apple-splash-1125-2436.png", + "scaleFactor": 3, + "width": 1125, + }, + Object { + "height": 1125, + "name": "apple-splash-2436-1125", + "orientation": "landscape", + "path": "temp/apple-splash-2436-1125.png", + "scaleFactor": 3, + "width": 2436, + }, + Object { + "height": 1792, + "name": "apple-splash-828-1792", + "orientation": "portrait", + "path": "temp/apple-splash-828-1792.png", + "scaleFactor": 2, + "width": 828, + }, + Object { + "height": 828, + "name": "apple-splash-1792-828", + "orientation": "landscape", + "path": "temp/apple-splash-1792-828.png", + "scaleFactor": 2, + "width": 1792, + }, + Object { + "height": 2208, + "name": "apple-splash-1242-2208", + "orientation": "portrait", + "path": "temp/apple-splash-1242-2208.png", + "scaleFactor": 3, + "width": 1242, + }, + Object { + "height": 1242, + "name": "apple-splash-2208-1242", + "orientation": "landscape", + "path": "temp/apple-splash-2208-1242.png", + "scaleFactor": 3, + "width": 2208, + }, + Object { + "height": 1334, + "name": "apple-splash-750-1334", + "orientation": "portrait", + "path": "temp/apple-splash-750-1334.png", + "scaleFactor": 2, + "width": 750, + }, + Object { + "height": 750, + "name": "apple-splash-1334-750", + "orientation": "landscape", + "path": "temp/apple-splash-1334-750.png", + "scaleFactor": 2, + "width": 1334, + }, + Object { + "height": 1136, + "name": "apple-splash-640-1136", + "orientation": "portrait", + "path": "temp/apple-splash-640-1136.png", + "scaleFactor": 2, + "width": 640, + }, + Object { + "height": 640, + "name": "apple-splash-1136-640", + "orientation": "landscape", + "path": "temp/apple-splash-1136-640.png", + "scaleFactor": 2, + "width": 1136, + }, + ], +} +`; + exports[`generates meta saving meta to index.html using a path override 1`] = ` Object { "htmlMeta": Object { diff --git a/src/cli.ts b/src/cli.ts index 5286a955..6035f6cf 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -31,12 +31,14 @@ $ pwa-asset-generator --help -r --portrait-only Only generate portrait splash screens [default: false] -d --dark-mode Generate iOS splash screen meta with (prefers-color-scheme: dark) media attr [default: false] -u --single-quotes Generate HTML meta tags with single quotes [default: false] + -x --xhtml Generate HTML meta tags by self-closing the tags [default: false] -g --log Logs the steps of the library process [default: true] Examples $ pwa-asset-generator logo.html $ pwa-asset-generator logo.svg -i ./index.html -m ./manifest.json $ pwa-asset-generator https://your-cdn-server.com/assets/logo.png ./ -t jpg -q 90 --splash-only --portrait-only + $ pwa-asset-generator logo.svg ./assets --splash-only --xhtml --single-quotes $ pwa-asset-generator logo.svg ./assets --scrape false --icon-only --path "%PUBLIC_URL%" $ pwa-asset-generator logo.svg ./assets --icon-only --favicon --opaque false --maskable false $ pwa-asset-generator logo.svg ./assets --dark-mode --background dimgrey --splash-only --type jpeg --quality 80 @@ -62,6 +64,7 @@ $ pwa-asset-generator --help --portrait-only --dark-mode --single-quotes + --xhtml --log false `, // TODO: remove when inferred meow types are corrected diff --git a/src/config/constants.ts b/src/config/constants.ts index 8f81ea46..885fff37 100644 --- a/src/config/constants.ts +++ b/src/config/constants.ts @@ -105,6 +105,11 @@ export default { alias: 'u', default: false, }, + xhtml: { + type: 'boolean', + alias: 'x', + default: false, + }, favicon: { type: 'boolean', alias: 'f', @@ -177,8 +182,15 @@ export default { WAIT_FOR_SELECTOR_TIMEOUT: 1000, BROWSER_TIMEOUT: 10000, - FAVICON_META_HTML: (size: number, url: string, mimeType: string): string => - ` + FAVICON_META_HTML: ( + size: number, + url: string, + mimeType: string, + xhtml: boolean, + ): string => + ` `, SHELL_HTML_FOR_LOGO: ( @@ -211,8 +223,14 @@ export default { `, - APPLE_TOUCH_ICON_META_HTML: (size: number, url: string): string => - ` + APPLE_TOUCH_ICON_META_HTML: ( + size: number, + url: string, + xhtml: boolean, + ): string => + ` `, APPLE_LAUNCH_SCREEN_META_HTML: ( @@ -222,17 +240,18 @@ export default { scaleFactor: number, orientation: Orientation, darkMode: boolean, + xhtml: boolean, ): string => { /* eslint-disable */ if (orientation === 'portrait') { return `\ - + `; } // As weird as it gets, Apple expects same device width and height values from portrait orientation, for landscape return `\ - + `; /* eslint-enable */ }, diff --git a/src/helpers/meta.ts b/src/helpers/meta.ts index 389620c1..819c6f08 100644 --- a/src/helpers/meta.ts +++ b/src/helpers/meta.ts @@ -68,6 +68,7 @@ const generateAppleTouchIconHtml = ( constants.APPLE_TOUCH_ICON_META_HTML( width, generateOutputPath(options, name, path), + options.xhtml, ), ) .join(''); @@ -84,6 +85,7 @@ const generateFaviconHtml = ( width, generateOutputPath(options, name, path), lookup(path) as string, + options.xhtml, ), ) .join(''); @@ -106,6 +108,7 @@ const generateAppleLaunchImageHtml = ( scaleFactor as number, orientation, darkMode, + options.xhtml, ), ) .join(''); @@ -116,7 +119,9 @@ const generateHtmlForIndexPage = ( options: Options, ): HTMLMeta => { const htmlMeta: HTMLMeta = { - [HTMLMetaNames.appleMobileWebAppCapable]: ` + [HTMLMetaNames.appleMobileWebAppCapable]: ` `, }; diff --git a/src/main.test.ts b/src/main.test.ts index 1941f209..dab67aa8 100644 --- a/src/main.test.ts +++ b/src/main.test.ts @@ -260,86 +260,126 @@ describe('generates meta', () => { await saveIndex(); }); - test('creating a favicon meta', async () => { - const result = await generateTempImages({ - scrape: false, - favicon: true, - iconOnly: true, - log: false, - index: './temp/index.html', + describe('creating a favicon meta', () => { + test('with html output', async () => { + const result = await generateTempImages({ + scrape: false, + favicon: true, + iconOnly: true, + log: false, + index: './temp/index.html', + }); + + const savedIndex = await readIndex(); + + expect(savedIndex).toContain( + result.htmlMeta[HTMLMetaNames.appleMobileWebAppCapable], + ); + expect(savedIndex).toContain(result.htmlMeta[HTMLMetaNames.favicon]); }); - const savedIndex = await readIndex(); + test('with xhtml output', async () => { + const result = await generateTempImages({ + scrape: false, + favicon: true, + iconOnly: true, + log: false, + xhtml: true, + }); - expect(savedIndex).toContain( - result.htmlMeta[HTMLMetaNames.appleMobileWebAppCapable], - ); - expect(savedIndex).toContain(result.htmlMeta[HTMLMetaNames.favicon]); + expect(result).toMatchSnapshot(); + }); }); - test('creating apple icons meta', async () => { - const result = await generateTempImages({ - scrape: false, - iconOnly: true, - log: false, - index: './temp/index.html', + describe('creating apple icons meta', () => { + test('with html output', async () => { + const result = await generateTempImages({ + scrape: false, + iconOnly: true, + log: false, + index: './temp/index.html', + }); + + const savedIndex = await readIndex(); + + expect(savedIndex).toContain( + result.htmlMeta[HTMLMetaNames.appleMobileWebAppCapable], + ); + expect(savedIndex).toContain( + result.htmlMeta[HTMLMetaNames.appleTouchIcon], + ); }); - const savedIndex = await readIndex(); + test('with xhtml output', async () => { + const result = await generateTempImages({ + scrape: false, + iconOnly: true, + log: false, + xhtml: true, + }); - expect(savedIndex).toContain( - result.htmlMeta[HTMLMetaNames.appleMobileWebAppCapable], - ); - expect(savedIndex).toContain( - result.htmlMeta[HTMLMetaNames.appleTouchIcon], - ); + expect(result).toMatchSnapshot(); + }); }); - test('creating splash screens meta', async () => { - const result = await generateTempImages({ - scrape: false, - splashOnly: true, - log: false, - index: './temp/index.html', + describe('creating splash screens meta', () => { + test('with default html output', async () => { + const result = await generateTempImages({ + scrape: false, + splashOnly: true, + log: false, + index: './temp/index.html', + }); + + const savedIndex = await readIndex(); + + expect(savedIndex).toContain( + result.htmlMeta[HTMLMetaNames.appleMobileWebAppCapable], + ); + expect(savedIndex).toContain( + result.htmlMeta[HTMLMetaNames.appleLaunchImage], + ); }); - const savedIndex = await readIndex(); + test('with dark mode html output', async () => { + const resultLight = await generateTempImages({ + scrape: false, + splashOnly: true, + log: false, + index: './temp/index.html', + }); - expect(savedIndex).toContain( - result.htmlMeta[HTMLMetaNames.appleMobileWebAppCapable], - ); - expect(savedIndex).toContain( - result.htmlMeta[HTMLMetaNames.appleLaunchImage], - ); - }); + const resultDark = await generateTempImages({ + scrape: false, + splashOnly: true, + darkMode: true, + log: false, + index: './temp/index.html', + }); - test('creating splash screens meta with dark mode enabled', async () => { - const resultLight = await generateTempImages({ - scrape: false, - splashOnly: true, - log: false, - index: './temp/index.html', - }); + const savedIndex = await readIndex(); - const resultDark = await generateTempImages({ - scrape: false, - splashOnly: true, - darkMode: true, - log: false, - index: './temp/index.html', + expect(savedIndex).toContain( + resultDark.htmlMeta[HTMLMetaNames.appleMobileWebAppCapable], + ); + expect(savedIndex).toContain( + resultLight.htmlMeta[HTMLMetaNames.appleLaunchImage], + ); + expect(savedIndex).toContain( + resultDark.htmlMeta[HTMLMetaNames.appleLaunchImageDarkMode], + ); }); - const savedIndex = await readIndex(); + test('with xhtml output', async () => { + const result = await generateTempImages({ + scrape: false, + splashOnly: true, + log: false, + xhtml: true, + }); - expect(savedIndex).toContain( - resultDark.htmlMeta[HTMLMetaNames.appleMobileWebAppCapable], - ); - expect(savedIndex).toContain( - resultLight.htmlMeta[HTMLMetaNames.appleLaunchImage], - ); - expect(savedIndex).toContain( - resultDark.htmlMeta[HTMLMetaNames.appleLaunchImageDarkMode], - ); + expect(result).toMatchSnapshot(); + }); }); test('using a path override', async () => { diff --git a/src/models/options.ts b/src/models/options.ts index f86eaa5c..4598384f 100644 --- a/src/models/options.ts +++ b/src/models/options.ts @@ -115,6 +115,13 @@ export interface Options { */ readonly singleQuotes: boolean; + /** + Generate HTML meta tags by self-closing the tags + + @default false + */ + readonly xhtml: boolean; + /** Generate favicon images and HTML meta tags