Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve icon generator, use icon*.NODE_ENV.png #172

Merged
merged 5 commits into from
Sep 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 86 additions & 27 deletions cli/plasmo/src/features/extension-devtools/generate-icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,97 @@ import { copy, ensureDir, existsSync } from "fs-extra"
import { resolve } from "path"
import sharp from "sharp"

import { vLog } from "@plasmo/utils"
import { vLog, wLog } from "@plasmo/utils"

import { getMd5RevHash } from "~features/helpers/crypto"

import type { CommonPath } from "./common-path"

export async function generateIcons(
{ assetsDirectory, genAssetsDirectory }: CommonPath,
iconName = "icon512.png"
) {
const image512Path = resolve(assetsDirectory, iconName)

if (existsSync(image512Path)) {
vLog("Make sure generated assets directory exists")
await ensureDir(genAssetsDirectory)

// TODO: hash check the image512 to skip this routine
vLog(`${iconName} found, create resized icons in gen-assets`)

const image512 = sharp(image512Path)
await Promise.all(
[128, 48, 16].map((width) => {
const iconFileName = `icon${width}.png`
const developerProvidedImagePath = resolve(
assetsDirectory,
iconFileName
)
const getIconNameVariants = (size = 512 as string | number, name = "icon") => [
`${name}${size}`,
`${name}-${size}`,
`${name}-${size}x${size}`
]

// Prefer icon -> medium -> large icon
const baseIconNames = [
"icon",
...getIconNameVariants(),
...getIconNameVariants(1024)
]

// We pick env based icon first, then plain icon
const getPrioritizedIconPaths = (iconNames = baseIconNames) =>
iconNames
.map((name) => [`${name}.${process.env.NODE_ENV}.png`, `${name}.png`])
.flat()

const iconNameList = getPrioritizedIconPaths()

const generatedIconPath = resolve(genAssetsDirectory, iconFileName)
// Use this to cache the path resolving result
const iconState = {
baseIconPaths: [] as string[],
baseIconHash: null as string,
devProvidedIcons: {} as Record<string, string[]>
}

return existsSync(developerProvidedImagePath)
? copy(developerProvidedImagePath, generatedIconPath)
: image512.resize({ width, height: width }).toFile(generatedIconPath)
})
/**
* Generate manifest icons from an icon in the assets directory
* - One icon will be picked in the set { `icon`, `icon512`, `icon-512`, `icon-512x512`, `icon1024`, `icon-1024`, `icon-1024x1024` }
* - Optionally, it will resolve `process.env.NODE_ENV` suffix, e.g: `icon.development.png`, `icon.production.png`
* - The suffix is prioritized. Thus, if `icon512.development.png` exists, it will be picked over `icon512.png`
* - Only png is supported
*/
export async function generateIcons({
assetsDirectory,
genAssetsDirectory
}: CommonPath) {
// Precalculate the base icon paths
if (iconState.baseIconPaths.length === 0) {
iconState.baseIconPaths = iconNameList.map((name) =>
resolve(assetsDirectory, name)
)
}

const baseIconPath = iconState.baseIconPaths.find(existsSync)

if (baseIconPath === undefined) {
wLog("No icon found in assets directory")
return
}

vLog("Make sure generated assets directory exists")
await ensureDir(genAssetsDirectory)

const baseIcon = sharp(baseIconPath)
const baseIconHash = getMd5RevHash(await baseIcon.toBuffer())

if (iconState.baseIconHash === baseIconHash) {
vLog("Icon hash matched, skip icon generation")
return
}

vLog(`${baseIconPath} found, create resized icons in gen-assets`)

iconState.baseIconHash = baseIconHash

await Promise.all(
[128, 48, 32, 16].map((width) => {
if (iconState.devProvidedIcons[width] === undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do we invalidate this cache?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just static path cache to avoid extra path resolution. We would invalidate it against the passed down assetDirectory, but that path doesn't change at runtime atm xD

vLog(`Caching dev provided icon paths for size: ${width}`)
const devIconPath = getPrioritizedIconPaths(getIconNameVariants(width))
iconState.devProvidedIcons[width] = devIconPath.map((name) =>
resolve(assetsDirectory, name)
)
}

const devProvidedIcon = iconState.devProvidedIcons[width].find(existsSync)

const generatedIconPath = resolve(genAssetsDirectory, `icon${width}.png`)

return devProvidedIcon !== undefined
? copy(devProvidedIcon, generatedIconPath)
: baseIcon.resize({ width, height: width }).toFile(generatedIconPath)
})
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ export const handleProjectFile = async (
return
}
case WatchReason.PackageJson: {
iLog("package.json changed, update manifest overides")
iLog(
"package.json changed, updating manifest overides. You might need to restart the dev server."
)
await plasmoManifest.updatePackageData()
return
}
Expand Down
9 changes: 9 additions & 0 deletions cli/plasmo/src/features/helpers/crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createHash } from "crypto"

/**
* Fast hash for local file revving
* DO NOT USE FOR SENSITIVE PURPOSES
* md5 is good enough for file-revving: https://github.com/sindresorhus/rev-hash
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can even do crc32 for file diff if we want to squeeze out perf

Copy link
Contributor

@louisgv louisgv Sep 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think even google was struggling at implementing usable crc32 for their storage lol, they had to defer it to another module las time I checked xd

*/
export const getMd5RevHash = (buff: Buffer) =>
createHash("md5").update(buff).digest("hex").slice(0, 18)
1 change: 1 addition & 0 deletions cli/plasmo/src/features/manifest-factory/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export abstract class BaseFactory<
this.data = {}
this.data.icons = {
"16": "./gen-assets/icon16.png",
"32": "./gen-assets/icon32.png",
"48": "./gen-assets/icon48.png",
"128": "./gen-assets/icon128.png"
}
Expand Down
1 change: 1 addition & 0 deletions cli/plasmo/src/features/manifest-factory/mv2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class PlasmoExtensionManifestMV2 extends BaseFactory<ExtensionManifestV2>
this.data.browser_action = {
default_icon: {
"16": "./gen-assets/icon16.png",
"32": "./gen-assets/icon32.png",
"48": "./gen-assets/icon48.png"
}
}
Expand Down
1 change: 1 addition & 0 deletions cli/plasmo/src/features/manifest-factory/mv3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class PlasmoExtensionManifestMV3 extends BaseFactory<ExtensionManifest> {
this.data.action = {
default_icon: {
"16": "./gen-assets/icon16.png",
"32": "./gen-assets/icon32.png",
"48": "./gen-assets/icon48.png"
}
}
Expand Down