diff --git a/lib/url-plugin.js b/lib/url-plugin.js index aabe3b09..e3f3494b 100644 --- a/lib/url-plugin.js +++ b/lib/url-plugin.js @@ -12,9 +12,11 @@ */ import { promises as fs } from 'fs'; import { basename } from 'path'; +import { imageSize } from 'image-size'; const defaultOpts = { prefix: 'url', + imagePrefix: 'img-url', }; export default function urlPlugin(opts) { @@ -24,6 +26,7 @@ export default function urlPlugin(opts) { let assetIdToSourceBuffer; const prefix = opts.prefix + ':'; + const imagePrefix = opts.imagePrefix + ':'; return { name: 'url-plugin', buildStart() { @@ -48,28 +51,49 @@ export default function urlPlugin(opts) { return combinedBuffer; }, async resolveId(id, importer) { - if (!id.startsWith(prefix)) return; - const realId = id.slice(prefix.length); + const idPrefix = [prefix, imagePrefix].find((prefix) => + id.startsWith(prefix), + ); + if (!idPrefix) return; + + const realId = id.slice(idPrefix.length); const resolveResult = await this.resolve(realId, importer); if (!resolveResult) { throw Error(`Cannot find ${realId}`); } // Add an additional .js to the end so it ends up with .js at the end in the _virtual folder. - return prefix + resolveResult.id + '.js'; + return idPrefix + resolveResult.id + '.js'; }, async load(id) { - if (!id.startsWith(prefix)) return; - const realId = id.slice(prefix.length, -'.js'.length); + const idPrefix = [prefix, imagePrefix].find((prefix) => + id.startsWith(prefix), + ); + if (!idPrefix) return; + + const realId = id.slice(idPrefix.length, -'.js'.length); const source = await fs.readFile(realId); assetIdToSourceBuffer.set(id, source); this.addWatchFile(realId); - return `export default import.meta.ROLLUP_FILE_URL_${this.emitFile({ - type: 'asset', - source, - name: basename(realId), - })}`; + let imgSizeExport = ''; + + if (idPrefix === imagePrefix) { + const imgInfo = imageSize(source); + imgSizeExport = [ + `export const width = ${JSON.stringify(imgInfo.width)};`, + `export const height = ${JSON.stringify(imgInfo.height)};`, + ].join('\n'); + } + + return [ + `export default import.meta.ROLLUP_FILE_URL_${this.emitFile({ + type: 'asset', + source, + name: basename(realId), + })};`, + imgSizeExport, + ].join('\n'); }, }; } diff --git a/missing-types.d.ts b/missing-types.d.ts index 13721dbd..0444a321 100644 --- a/missing-types.d.ts +++ b/missing-types.d.ts @@ -22,6 +22,13 @@ declare module 'url:*' { export default value; } +declare module 'img-url:*' { + const value: string; + export default value; + export const width: number; + export const height: number; +} + declare module 'omt:*' { const value: string; export default value; diff --git a/package-lock.json b/package-lock.json index 1a69b7ef..9a4add25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -204,6 +204,12 @@ "@types/node": "*" } }, + "@types/mime-types": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.0.tgz", + "integrity": "sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM=", + "dev": true + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -2071,6 +2077,15 @@ "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true }, + "image-size": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.9.3.tgz", + "integrity": "sha512-5SakFa79uhUVSjKeQE30GVzzLJ0QNzB53+I+/VD1vIesD6GP6uatWIlgU0uisFNLt1u0d6kBydp7yfk+lLJhLQ==", + "dev": true, + "requires": { + "queue": "6.0.1" + } + }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -2551,12 +2566,20 @@ "dev": true }, "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.28", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", + "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", "dev": true, "requires": { - "mime-db": "1.44.0" + "mime-db": "1.45.0" + }, + "dependencies": { + "mime-db": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", + "dev": true + } } }, "mimic-fn": { @@ -6090,6 +6113,15 @@ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", "dev": true }, + "queue": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.1.tgz", + "integrity": "sha512-AJBQabRCCNr9ANq8v77RJEv73DPbn55cdTb+Giq4X0AVnNVZvMHlYp7XlQiN+1npCZj1DuSmaA2hYVUUDgxFDg==", + "dev": true, + "requires": { + "inherits": "~2.0.3" + } + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", diff --git a/package.json b/package.json index a42a91d5..c2a84ad8 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@rollup/plugin-replace": "^2.3.4", "@surma/rollup-plugin-off-main-thread": "^1.4.2", "@types/dedent": "^0.7.0", + "@types/mime-types": "^2.1.0", "@types/node": "^14.14.7", "comlink": "^4.3.0", "cssnano": "^4.1.10", @@ -24,10 +25,11 @@ "file-drop-element": "^1.0.1", "husky": "^4.3.0", "idb-keyval": "^3.2.0", + "image-size": "^0.9.3", "linkstate": "^2.0.0", "lint-staged": "^10.5.1", "lodash.camelcase": "^4.3.0", - "mime-types": "^2.1.27", + "mime-types": "^2.1.28", "npm-run-all": "^4.1.5", "pointer-tracker": "^2.4.0", "postcss": "^7.0.35", diff --git a/src/static-build/assets/screenshot1.png b/src/static-build/assets/screenshot1.png new file mode 100644 index 00000000..53c76947 Binary files /dev/null and b/src/static-build/assets/screenshot1.png differ diff --git a/src/static-build/assets/screenshot2.jpg b/src/static-build/assets/screenshot2.jpg new file mode 100644 index 00000000..35b54540 Binary files /dev/null and b/src/static-build/assets/screenshot2.jpg differ diff --git a/src/static-build/assets/screenshot3.jpg b/src/static-build/assets/screenshot3.jpg new file mode 100644 index 00000000..8459bf5f Binary files /dev/null and b/src/static-build/assets/screenshot3.jpg differ diff --git a/src/static-build/index.tsx b/src/static-build/index.tsx index 50ac718f..9c18b79b 100644 --- a/src/static-build/index.tsx +++ b/src/static-build/index.tsx @@ -14,9 +14,16 @@ import { h } from 'preact'; import { renderPage, writeFiles } from './utils'; import IndexPage from './pages/index'; -import iconLargeMaskable from 'url:static-build/assets/icon-large-maskable.png'; -import iconLarge from 'url:static-build/assets/icon-large.png'; +import * as iconLargeMaskable from 'img-url:static-build/assets/icon-large-maskable.png'; +import * as iconLarge from 'img-url:static-build/assets/icon-large.png'; +import * as screenshot1 from 'img-url:static-build/assets/screenshot1.png'; +import * as screenshot2 from 'img-url:static-build/assets/screenshot2.jpg'; +import * as screenshot3 from 'img-url:static-build/assets/screenshot3.jpg'; import dedent from 'dedent'; +import { lookup as lookupMime } from 'mime-types'; + +const manifestSize = ({ width, height }: { width: number; height: number }) => + `${width}x${height}`; // Set by Netlify const branch = process.env.BRANCH; @@ -49,17 +56,38 @@ const toOutput: Output = { theme_color: '#ff3385', icons: [ { - src: iconLarge, - type: 'image/png', - sizes: '1024x1024', + src: iconLarge.default, + type: lookupMime(iconLarge.default), + sizes: manifestSize(iconLarge), }, { - src: iconLargeMaskable, - type: 'image/png', - sizes: '1024x1024', + src: iconLargeMaskable.default, + type: lookupMime(iconLargeMaskable.default), + sizes: manifestSize(iconLargeMaskable), purpose: 'maskable', }, ], + description: + 'Compress and compare images with different codecs, right in your browser.', + lang: 'en', + categories: ['photo', 'productivity', 'utilities'], + screenshots: [ + { + src: screenshot1.default, + type: lookupMime(screenshot1.default), + sizes: manifestSize(screenshot1), + }, + { + src: screenshot2.default, + type: lookupMime(screenshot2.default), + sizes: manifestSize(screenshot2), + }, + { + src: screenshot3.default, + type: lookupMime(screenshot3.default), + sizes: manifestSize(screenshot3), + }, + ], share_target: { action: '/?utm_medium=PWA&utm_source=share-target&share-target', method: 'POST',