mirror of
https://github.com/zen-browser/surfer.git
synced 2025-07-07 17:05:33 +02:00
🎨 New, overkill linter
This commit is contained in:
parent
7650c90c3a
commit
d84e0941b5
49 changed files with 670 additions and 470 deletions
254
src/commands/patches/branding-patch.ts
Normal file
254
src/commands/patches/branding-patch.ts
Normal file
|
@ -0,0 +1,254 @@
|
|||
// 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 'node:fs'
|
||||
import { copyFile, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
|
||||
import { every } from 'modern-async'
|
||||
import { dirname, extname, join } from 'node: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 { templateDirectory } from '../setup-project'
|
||||
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 temporary = join(MELON_TMP_DIR, 'macos_icon_info.iconset')
|
||||
|
||||
if (existsSync(temporary)) await rm(temporary, { recursive: true })
|
||||
|
||||
asyncIcns.convert({
|
||||
input: join(configPath, 'logo.png'),
|
||||
output: join(outputPath, 'firefox.icns'),
|
||||
sizes: [16, 32, 64, 128, 256, 512],
|
||||
tmpDirectory: temporary,
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
) {
|
||||
for (const file of await walkDirectory(
|
||||
join(templateDirectory, 'branding.optional')
|
||||
)) {
|
||||
const fileContents = await readFile(windowsPathToUnix(file), {
|
||||
encoding: 'utf8',
|
||||
})
|
||||
|
||||
const universalPath =
|
||||
// We want to avoid the pain that windows is going to throw at us with its
|
||||
// weird paths
|
||||
windowsPathToUnix(file)
|
||||
// We want to remove all of the extra folders that surround this from the
|
||||
// template folder
|
||||
.replace(
|
||||
windowsPathToUnix(join(templateDirectory, 'branding.optional') + '/'),
|
||||
''
|
||||
)
|
||||
|
||||
const sourceFolderPath = join(outputPath, universalPath)
|
||||
|
||||
await mkdir(dirname(sourceFolderPath), { recursive: true })
|
||||
await writeFile(
|
||||
sourceFolderPath,
|
||||
stringTemplate(fileContents, brandingConfig)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function copyMozFiles(
|
||||
outputPath: string,
|
||||
brandingConfig: {
|
||||
backgroundColor: string
|
||||
brandShorterName: string
|
||||
brandShortName: string
|
||||
brandFullName: string
|
||||
brandingGenericName: string
|
||||
brandingVendor: string
|
||||
}
|
||||
) {
|
||||
const firefoxBrandingDirectoryContents = await walkDirectory(BRANDING_FF)
|
||||
const files = firefoxBrandingDirectoryContents.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))
|
||||
|
||||
for (const [contents, path] of 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,
|
||||
])) {
|
||||
mkdirSync(dirname(path), { recursive: true })
|
||||
writeFileSync(path, contents)
|
||||
}
|
||||
|
||||
// Copy everything else from the default firefox branding directory
|
||||
for (const file of everythingElse) {
|
||||
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)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue