🎨 New, overkill linter

This commit is contained in:
TrickyPR 2022-10-20 22:08:27 +11:00
parent 7650c90c3a
commit d84e0941b5
49 changed files with 670 additions and 470 deletions

View file

@ -1,244 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
import { renderAsync } from '@resvg/resvg-js'
import {
readdirSync,
lstatSync,
existsSync,
mkdirSync,
readFileSync,
writeFileSync,
copyFileSync,
} from 'fs'
import { copyFile, readFile, rm, writeFile } from 'fs/promises'
import { every } from 'modern-async'
import { dirname, extname, join } from 'path'
import sharp from 'sharp'
import pngToIco from 'png-to-ico'
import asyncIcns from 'async-icns'
import { config } from '../..'
import { CONFIGS_DIR, ENGINE_DIR, MELON_TMP_DIR } from '../../constants'
import { log } from '../../log'
import {
addHash,
defaultBrandsConfig,
ensureEmpty,
filesExist,
mkdirpSync,
stringTemplate,
walkDirectory,
windowsPathToUnix,
} from '../../utils'
import { templateDir } from '../setupProject'
import { IMelonPatch } from './command'
// =============================================================================
// Pure constants
export const BRANDING_DIR = join(CONFIGS_DIR, 'branding')
const BRANDING_STORE = join(ENGINE_DIR, 'browser', 'branding')
const BRANDING_FF = join(BRANDING_STORE, 'unofficial')
const REQUIRED_FILES = ['logo.png']
const CSS_REPLACE_REGEX = new RegExp(
'#130829|hsla\\(235, 43%, 10%, .5\\)',
'gm'
)
// =============================================================================
// Utility Functions
function checkForFaults(name: string, configPath: string) {
if (!existsSync(configPath)) {
throw new Error(`Branding ${name} does not exist`)
}
const requiredFiles = REQUIRED_FILES.map((file) => join(configPath, file))
const requiredFilesExist = filesExist(requiredFiles)
if (!requiredFilesExist) {
throw new Error(
`Missing some of the required files: ${requiredFiles
.filter((file) => !existsSync(file))
.join(', ')}`
)
}
}
function constructConfig(name: string) {
return {
brandingGenericName: config.name,
brandingVendor: config.vendor,
...defaultBrandsConfig,
...(config.brands[name] || {}),
}
}
// =============================================================================
// Main code
async function setupImages(configPath: string, outputPath: string) {
log.debug('Generating icons')
// Firefox doesn't use 512 by 512, but we need it to generate ico files later
await every([16, 22, 24, 32, 48, 64, 128, 256, 512], async (size) => {
await sharp(join(configPath, 'logo.png'))
.resize(size, size)
.toFile(join(outputPath, `default${size}.png`))
await copyFile(
join(outputPath, `default${size}.png`),
join(configPath, `logo${size}.png`)
)
return true
})
log.debug('Generating Windows Icons')
writeFileSync(
join(outputPath, 'firefox.ico'),
await pngToIco([join(configPath, 'logo512.png')])
)
writeFileSync(
join(outputPath, 'firefox64.ico'),
await pngToIco([join(configPath, 'logo64.png')])
)
// TODO: Custom MacOS icon support
if (process.platform == 'darwin') {
log.debug('Generating Mac Icons')
const tmp = join(MELON_TMP_DIR, 'macos_icon_info.iconset')
if (existsSync(tmp)) await rm(tmp, { recursive: true })
asyncIcns.convert({
input: join(configPath, 'logo.png'),
output: join(outputPath, 'firefox.icns'),
sizes: [16, 32, 64, 128, 256, 512],
tmpDirectory: tmp,
})
}
mkdirSync(join(outputPath, 'content'), { recursive: true })
await sharp(join(configPath, 'logo.png'))
.resize(512, 512)
.toFile(join(outputPath, 'content', 'about-logo.png'))
await sharp(join(configPath, 'logo.png'))
.resize(1024, 1024)
.toFile(join(outputPath, 'content', 'about-logo@2x.png'))
// Register logo in cache
await addHash(join(configPath, 'logo.png'))
log.debug('Generating macos install')
const macosInstall = await renderAsync(
await readFile(join(configPath, 'MacOSInstaller.svg'))
)
await writeFile(join(outputPath, 'content', 'background.png'), macosInstall)
await addHash(join(configPath, 'MacOSInstaller.svg'))
}
async function setupLocale(
outputPath: string,
brandingConfig: {
backgroundColor: string
brandShorterName: string
brandShortName: string
brandFullName: string
brandingGenericName: string
brandingVendor: string
}
) {
// eslint-disable-next-line @typescript-eslint/no-extra-semi
;(await walkDirectory(join(templateDir, 'branding.optional')))
.map((file) =>
windowsPathToUnix(file).replace(
windowsPathToUnix(join(templateDir, 'branding.optional') + '/'),
''
)
)
.map((file) => [
readFileSync(join(templateDir, 'branding.optional', file)).toString(),
join(outputPath, file),
])
.forEach(([contents, path]) => {
mkdirSync(dirname(path), { recursive: true })
writeFileSync(path, stringTemplate(contents, brandingConfig))
})
}
async function copyMozFiles(
outputPath: string,
brandingConfig: {
backgroundColor: string
brandShorterName: string
brandShortName: string
brandFullName: string
brandingGenericName: string
brandingVendor: string
}
) {
const files = (await walkDirectory(BRANDING_FF)).filter(
(file) => !existsSync(join(outputPath, file.replace(BRANDING_FF, '')))
)
const css = files.filter((file) => extname(file).includes('css'))
const everythingElse = files.filter((file) => !css.includes(file))
css
.map((filePath) => [
readFileSync(filePath).toString(),
join(outputPath, filePath.replace(BRANDING_FF, '')),
])
.map(([contents, path]) => [
contents.replace(CSS_REPLACE_REGEX, 'var(--theme-bg)') +
`:root { --theme-bg: ${brandingConfig.backgroundColor} }`,
path,
])
.forEach(([contents, path]) => {
mkdirSync(dirname(path), { recursive: true })
writeFileSync(path, contents)
})
// Copy everything else from the default firefox branding directory
everythingElse.forEach((file) => {
mkdirpSync(dirname(join(outputPath, file.replace(BRANDING_FF, ''))))
copyFileSync(file, join(outputPath, file.replace(BRANDING_FF, '')))
})
}
// =============================================================================
// Exports
export interface IBrandingPatch extends IMelonPatch {
value: unknown
}
export function get(): string[] {
return readdirSync(BRANDING_DIR).filter((file) =>
lstatSync(join(BRANDING_DIR, file)).isDirectory()
)
}
export async function apply(name: string): Promise<void> {
const configPath = join(BRANDING_DIR, name)
const outputPath = join(BRANDING_STORE, name)
checkForFaults(name, configPath)
const brandingConfig = constructConfig(name)
// Remove the output path if it exists and recreate it
ensureEmpty(outputPath)
await setupImages(configPath, outputPath)
await setupLocale(outputPath, brandingConfig)
await copyMozFiles(outputPath, brandingConfig)
}