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 3 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
114 changes: 87 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,98 @@ 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)
})
/**
* optionally handle other icon name variant too like icon-512 or icon-512x512
* optionally, handle larger icon such as icon-1024 or just icon
* optionally resolve icon512.${process.env.NODE_ENV}.png as well
// process.env.NODE_ENV === "development"
* We stick to png for now
*/
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)

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

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

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

iconState.baseIconHash = baseIconHash

await Promise.all(
[128, 48, 32, 16].map((width) => {
// Cache the dev provided icon paths for each 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

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, update manifest overides. You might need to restart the dev server."
louisgv marked this conversation as resolved.
Show resolved Hide resolved
)
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