🎨 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

@ -3,12 +3,17 @@ module.exports = {
es2021: true,
node: true,
},
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:unicorn/recommended'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['@typescript-eslint'],
rules: {},
plugins: ['@typescript-eslint', 'unicorn'],
rules: {
'unicorn/no-process-exit': 0,
// We are currently using commonjs. If / when it becomes viable for us to
// switch to ESModules, we should consider enabling this rule
'unicorn/prefer-module': 0,
},
}

View file

@ -73,6 +73,7 @@
"@typescript-eslint/eslint-plugin": "^5.22.0",
"@typescript-eslint/parser": "^5.22.0",
"eslint": "^8.15.0",
"eslint-plugin-unicorn": "^44.0.2",
"jest": "^27.4.5",
"prettier": "^2.2.1",
"ts-jest": "^27.1.2",

View file

@ -1,6 +1,9 @@
// 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/.
/* eslint-disable unicorn/no-await-expression-member */
import { Cmd } from './types'
export const commands: Cmd[] = [
@ -132,7 +135,7 @@ export const commands: Cmd[] = [
cmd: 'setup-project',
description: 'Sets up a gluon project for the first time',
requestController: async () =>
(await import('./commands/setupProject')).setupProject,
(await import('./commands/setup-project')).setupProject,
},
{
cmd: 'status',

View file

@ -10,11 +10,11 @@ import { configDispatch } from '../utils'
export const bootstrap = async () => {
log.info(`Bootstrapping ${config.name}...`)
const args = ['--application-choice', 'browser']
const arguments_ = ['--application-choice', 'browser']
console.debug(`Passing through to |mach bootstrap|`)
await configDispatch('./mach', {
args: ['bootstrap', ...args],
args: ['bootstrap', ...arguments_],
cwd: ENGINE_DIR,
})
}

View file

@ -2,8 +2,8 @@
// 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 execa from 'execa'
import { existsSync, readFileSync, writeFileSync } from 'fs'
import { join, resolve } from 'path'
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { bin_name, config } from '..'
import { BUILD_TARGETS, CONFIGS_DIR, ENGINE_DIR } from '../constants'
import { internalMozconfg } from '../constants/mozconfig'
@ -28,7 +28,7 @@ const applyConfig = async (os: string) => {
// Retrieve changeset
const { stdout } = await execa('git', ['rev-parse', 'HEAD'])
changeset = stdout.trim()
} catch (e) {
} catch (error) {
log.warning(
'Gluon expects that you are building your browser with git as your version control'
)
@ -38,7 +38,7 @@ const applyConfig = async (os: string) => {
log.warning('Otherwise, you can setup git in this folder by running:')
log.warning(' |git init|')
throw e
throw error
}
const templateOptions = {

View file

@ -2,8 +2,8 @@
// 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 execa from 'execa'
import { existsSync, statSync } from 'fs'
import { resolve } from 'path'
import { existsSync, statSync } from 'node:fs'
import { resolve } from 'node:path'
import { log } from '../log'
import { ENGINE_DIR } from '../constants'
@ -17,7 +17,7 @@ export const discard = async (file: string): Promise<void> => {
try {
await execa('git', ['restore', file], { cwd: ENGINE_DIR })
} catch (e) {
} catch {
log.warning(`The file ${file} was not changed`)
}
}

View file

@ -1,5 +1,5 @@
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'fs'
import { join } from 'path'
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import { isMatch } from 'picomatch'
import { config } from '../..'
@ -10,7 +10,7 @@ import {
AddonInfo,
configDispatch,
delay,
ensureDir,
ensureDirectory,
walkDirectoryTree,
windowsPathToUnix,
} from '../../utils'
@ -29,34 +29,36 @@ export async function resolveAddonDownloadUrl(
addon: AddonInfo
): Promise<string> {
switch (addon.platform) {
case 'url':
case 'url': {
return addon.url
}
case 'amo':
case 'amo': {
try {
return (
await axios.get(
`https://addons.mozilla.org/api/v4/addons/addon/${addon.amoId}/versions/`
)
).data.results[0].files[0].url
} catch (e) {
const mozillaData = await axios.get(
`https://addons.mozilla.org/api/v4/addons/addon/${addon.amoId}/versions/`
)
return mozillaData.data.results[0].files[0].url
} catch (error) {
log.warning(
'The following error occured whilst fetching amo addon metadata'
)
log.error(e)
log.error(error)
return ''
}
}
case 'github':
case 'github': {
try {
const githubData = await axios.get(
`https://api.github.com/repos/${addon.repo}/releases/tags/${addon.version}`
)
return (
(
((
await axios.get(
`https://api.github.com/repos/${addon.repo}/releases/tags/${addon.version}`
)
).data.assets as {
(githubData.data.assets as {
url: string
browser_download_url: string
name: string
@ -64,14 +66,15 @@ export async function resolveAddonDownloadUrl(
).find((asset) => isMatch(asset.name, addon.fileGlob))
?.browser_download_url || 'failed'
)
} catch (e) {
} catch (error) {
log.warning(
'The following error occured whilst fetching github metadata'
)
log.error(e)
log.error(error)
return ''
}
}
}
}
@ -79,7 +82,7 @@ export async function downloadAddon(
url: string,
addon: AddonInfo & { name: string }
): Promise<string> {
const tempFile = join(MELON_TMP_DIR, addon.name + '.xpi')
const temporaryFile = join(MELON_TMP_DIR, addon.name + '.xpi')
log.info(`Download addon from ${url}`)
@ -91,23 +94,23 @@ export async function downloadAddon(
// it
} else {
const cache = extensionCache.unwrap()
if (cache.url == url && existsSync(tempFile)) {
if (cache.url == url && existsSync(temporaryFile)) {
log.info(`Using cached version of ${addon.name}`)
return tempFile
return temporaryFile
}
}
}
if (existsSync(tempFile)) {
unlinkSync(tempFile)
if (existsSync(temporaryFile)) {
unlinkSync(temporaryFile)
}
await downloadFileToLocation(url, tempFile)
await downloadFileToLocation(url, temporaryFile)
// I do not know why, but this delay causes unzip to work reliably
await delay(200)
return tempFile
return temporaryFile
}
export async function unpackAddon(
@ -128,7 +131,7 @@ export async function unpackAddon(
// I do not know why, but this delay causes unzip to work reliably
await delay(200)
ensureDir(outPath)
ensureDirectory(outPath)
await configDispatch('unzip', {
args: [windowsPathToUnix(path), '-d', windowsPathToUnix(outPath)],

View file

@ -1,12 +1,12 @@
import execa from 'execa'
import { existsSync } from 'fs'
import { dirname, resolve } from 'path'
import { existsSync } from 'node:fs'
import { dirname, resolve } from 'node:path'
import { bin_name } from '../..'
import { BASH_PATH, ENGINE_DIR, MELON_TMP_DIR } from '../../constants'
import { log } from '../../log'
import { commandExistsSync } from '../../utils/commandExists'
import { commandExistsSync } from '../../utils/command-exists'
import { downloadFileToLocation } from '../../utils/download'
import { ensureDir, windowsPathToUnix } from '../../utils/fs'
import { ensureDirectory, windowsPathToUnix } from '../../utils/fs'
import { init } from '../init'
export function shouldSetupFirefoxSource() {
@ -30,7 +30,7 @@ export async function setupFirefoxSource(version: string) {
async function unpackFirefoxSource(name: string): Promise<void> {
log.info(`Unpacking Firefox...`)
ensureDir(ENGINE_DIR)
ensureDirectory(ENGINE_DIR)
let tarExec = 'tar'
@ -56,12 +56,12 @@ async function unpackFirefoxSource(name: string): Promise<void> {
tarExec,
[
'--strip-components=1',
process.platform == 'win32' ? '--force-local' : null,
process.platform == 'win32' ? '--force-local' : undefined,
'-xf',
windowsPathToUnix(resolve(MELON_TMP_DIR, name)),
'-C',
windowsPathToUnix(ENGINE_DIR),
].filter((x) => x) as string[],
].filter(Boolean) as string[],
{
// HACK: Use bash shell on windows to get a sane version of tar that works
shell: BASH_PATH || false,
@ -80,7 +80,7 @@ async function downloadFirefoxSource(version: string) {
log.info(`Locating Firefox release ${version}...`)
await ensureDir(dirname(fsSaveLocation))
await ensureDirectory(dirname(fsSaveLocation))
if (existsSync(fsSaveLocation)) {
log.info('Using cached download')

View file

@ -1,26 +1,26 @@
// 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 { existsSync } from 'fs'
import { existsSync } from 'node:fs'
import { log } from '../log'
import { ENGINE_DIR } from '../constants'
import { dispatch } from '../utils'
export const execute = async (cmd: string[]) => {
if (existsSync(ENGINE_DIR)) {
if (!cmd || cmd.length == 0)
if (!cmd || cmd.length === 0)
log.error('You need to specify a command to run.')
const bin = cmd[0]
const args = cmd
args.shift()
const arguments_ = cmd
arguments_.shift()
log.info(
`Executing \`${bin}${args.length !== 0 ? ` ` : ``}${args.join(
`Executing \`${bin}${arguments_.length > 0 ? ` ` : ``}${arguments_.join(
' '
)}\` in \`src\`...`
)
dispatch(bin, args, ENGINE_DIR)
dispatch(bin, arguments_, ENGINE_DIR)
} else {
log.error(`Unable to locate src directory.`)
}

View file

@ -2,11 +2,11 @@
// 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 execa from 'execa'
import { existsSync, writeFileSync } from 'fs'
import { basename, resolve } from 'path'
import { existsSync, writeFileSync } from 'node:fs'
import { basename, resolve } from 'node:path'
import { log } from '../log'
import { ENGINE_DIR, SRC_DIR } from '../constants'
import { delay, ensureDir } from '../utils'
import { delay, ensureDirectory } from '../utils'
export const getPatchName = (file: string): string =>
`${basename(file).replace(/\./g, '-')}.patch`
@ -36,7 +36,7 @@ export const exportFile = async (file: string): Promise<void> => {
const name = getPatchName(file)
const patchPath = file.replace(/\./g, '-').split('/').slice(0, -1)
await ensureDir(resolve(SRC_DIR, ...patchPath))
await ensureDirectory(resolve(SRC_DIR, ...patchPath))
if (proc.stdout.length >= 8000) {
log.warning('')

View file

@ -2,8 +2,8 @@
// 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 { Command } from 'commander'
import { existsSync, readFileSync } from 'fs'
import { resolve } from 'path'
import { existsSync, readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { bin_name } from '..'
import { log } from '../log'
import { config, configDispatch } from '../utils'
@ -11,9 +11,9 @@ import { config, configDispatch } from '../utils'
export const init = async (directory: Command | string): Promise<void> => {
const cwd = process.cwd()
const dir = resolve(cwd as string, directory.toString())
const absoluteInitDirectory = resolve(cwd as string, directory.toString())
if (!existsSync(dir)) {
if (!existsSync(absoluteInitDirectory)) {
log.error(
`Directory "${directory}" not found.\nCheck the directory exists and run |${bin_name} init| again.`
)
@ -36,30 +36,31 @@ export const init = async (directory: Command | string): Promise<void> => {
version = version.trim().replace(/\\n/g, '')
// TODO: Use bash on windows, this may significantly improve performance. Still needs testing though
// TODO: Use bash on windows, this may significantly improve performance.
// Still needs testing though
log.info('Initializing git, this may take some time')
await configDispatch('git', {
args: ['init'],
cwd: dir,
cwd: absoluteInitDirectory,
shell: 'unix',
})
await configDispatch('git', {
args: ['init'],
cwd: dir,
cwd: absoluteInitDirectory,
shell: 'unix',
})
await configDispatch('git', {
args: ['checkout', '--orphan', version],
cwd: dir,
cwd: absoluteInitDirectory,
shell: 'unix',
})
await configDispatch('git', {
args: ['add', '-f', '.'],
cwd: dir,
cwd: absoluteInitDirectory,
shell: 'unix',
})
@ -67,13 +68,13 @@ export const init = async (directory: Command | string): Promise<void> => {
await configDispatch('git', {
args: ['commit', '-aqm', `"Firefox ${version}"`],
cwd: dir,
cwd: absoluteInitDirectory,
shell: 'unix',
})
await configDispatch('git', {
args: ['checkout', '-b', config.name.toLowerCase().replace(/\s/g, '_')],
cwd: dir,
cwd: absoluteInitDirectory,
shell: 'unix',
})
}

View file

@ -2,7 +2,7 @@
// 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 { join } from 'path'
import { join } from 'node:path'
import { isValidLicense } from './license-check'
describe('isValidLicense', () => {

View file

@ -1,12 +1,12 @@
// 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 { readFile, writeFile } from 'fs/promises'
import { join } from 'path'
import { readFile, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import { SRC_DIR } from '../constants'
import { walkDirectory } from '../utils/fs'
import { Task, TaskList } from '../utils/taskList'
import { Task, TaskList } from '../utils/task-list'
const ignoredFiles = new RegExp('.*\\.(json|patch|md|jpeg|png|gif|tiff|ico)')
const licenseIgnore = new RegExp('(//|#) Ignore license in this file', 'g')
@ -32,7 +32,7 @@ const fixableFiles = [
]
export async function isValidLicense(path: string): Promise<boolean> {
const file = (await readFile(path)).toString()
const file = await readFile(path, { encoding: 'utf8' })
const contents = file.split('\n')
// We need to grab the top 5 lines just in case there are newlines in the
@ -58,7 +58,8 @@ export function createTask(path: string, noFix: boolean): Task {
skip: () => ignoredFiles.test(path),
name: path.replace(SRC_DIR, ''),
task: async () => {
const contents = (await readFile(path)).toString().split('\n')
const contents = await readFile(path, { encoding: 'utf8' })
const contentsSplitNewline = contents.split('\n')
const hasLicense = await isValidLicense(path)
if (hasLicense) {
@ -73,11 +74,12 @@ export function createTask(path: string, noFix: boolean): Task {
)
}
const mpl = (
await readFile(join(__dirname, 'license-check.txt'))
).toString()
const mplHeader = // eslint-disable-next-line unicorn/prefer-module
await readFile(join(__dirname, 'license-check.txt'), {
encoding: 'utf8',
})
const { comment, commentOpen, commentClose } = fixable
let header = mpl
let header = mplHeader
.split('\n')
.map((ln) => (comment || '') + ln)
.join('\n')
@ -86,7 +88,7 @@ export function createTask(path: string, noFix: boolean): Task {
header = commentOpen + header + commentClose
}
await writeFile(path, header + '\n' + contents.join('\n'))
await writeFile(path, header + '\n' + contentsSplitNewline.join('\n'))
},
}
}

View file

@ -1,9 +1,9 @@
// 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 { existsSync } from 'fs'
import { copyFile, mkdir, readdir, unlink } from 'fs/promises'
import { join, resolve } from 'path'
import { existsSync } from 'node:fs'
import { copyFile, mkdir, readdir, unlink } from 'node:fs/promises'
import { join, resolve } from 'node:path'
import { bin_name, config } from '..'
import { DIST_DIR, ENGINE_DIR, OBJ_DIR } from '../constants'
@ -37,15 +37,15 @@ export const gluonPackage = async () => {
log.error(`Cannot locate the 'mach' binary within ${ENGINE_DIR}`)
}
const args = ['package']
const arguments_ = ['package']
log.info(
`Packaging \`${config.binaryName}\` with args ${JSON.stringify(
args.slice(1, 0)
arguments_.slice(1, 0)
)}...`
)
await dispatch(machPath, args, ENGINE_DIR, true)
await dispatch(machPath, arguments_, ENGINE_DIR, true)
log.info('Copying results up')
@ -53,31 +53,41 @@ export const gluonPackage = async () => {
if (!existsSync(DIST_DIR)) await mkdir(DIST_DIR, { recursive: true })
log.debug('Indexing files to copy')
const files = (await readdir(join(OBJ_DIR, 'dist'), { withFileTypes: true }))
const filesInMozillaDistrobution = await readdir(join(OBJ_DIR, 'dist'), {
withFileTypes: true,
})
const files = filesInMozillaDistrobution
.filter((entry) => entry.isFile())
.map((entry) => entry.name)
for (const file of files) {
const destFile = join(DIST_DIR, file)
const destinationFile = join(DIST_DIR, file)
log.debug(`Copying ${file}`)
if (existsSync(destFile)) await unlink(destFile)
await copyFile(join(OBJ_DIR, 'dist', file), destFile)
if (existsSync(destinationFile)) await unlink(destinationFile)
await copyFile(join(OBJ_DIR, 'dist', file), destinationFile)
}
// Windows has some special dist files that are available within the dist
// directory.
if (process.platform == 'win32') {
const installerDistDirectory = join(OBJ_DIR, 'dist', 'install', 'sea')
const installerDistributionDirectory = join(
OBJ_DIR,
'dist',
'install',
'sea'
)
if (!existsSync(installerDistDirectory)) {
if (!existsSync(installerDistributionDirectory)) {
log.error(
`Could not find windows installer files located at '${installerDistDirectory}'`
`Could not find windows installer files located at '${installerDistributionDirectory}'`
)
}
const windowsInstallerFiles = (
await readdir(installerDistDirectory, { withFileTypes: true })
const installerDistributionDirectoryContents = await readdir(
installerDistributionDirectory,
{ withFileTypes: true }
)
const windowsInstallerFiles = installerDistributionDirectoryContents
.filter((entry) => entry.isFile())
.map((entry) => entry.name)
@ -96,10 +106,13 @@ export const gluonPackage = async () => {
}
// Actually copy
const destFile = join(DIST_DIR, newFileName)
const destinationFile = join(DIST_DIR, newFileName)
log.debug(`Copying ${file}`)
if (existsSync(destFile)) await unlink(destFile)
await copyFile(join(installerDistDirectory, file), destFile)
if (existsSync(destinationFile)) await unlink(destinationFile)
await copyFile(
join(installerDistributionDirectory, file),
destinationFile
)
}
}
@ -137,18 +150,10 @@ async function createMarFile(version: string, channel: string) {
// On macos this should be
// <obj dir>/dist/${binaryName}/${brandFullName}.app and on everything else,
// the contents of the folder <obj dir>/dist/${binaryName}
let binary: string
if (process.platform == 'darwin') {
binary = join(
OBJ_DIR,
'dist',
config.binaryName,
`${getCurrentBrandName()}.app`
)
} else {
binary = join(OBJ_DIR, 'dist', config.binaryName)
}
const binary =
process.platform == 'darwin'
? join(OBJ_DIR, 'dist', config.binaryName, `${getCurrentBrandName()}.app`)
: join(OBJ_DIR, 'dist', config.binaryName)
const marPath = windowsPathToUnix(join(DIST_DIR, 'output.mar'))
await configDispatch('./tools/update-packaging/make_full_update.sh', {

View file

@ -10,10 +10,10 @@ import {
readFileSync,
writeFileSync,
copyFileSync,
} from 'fs'
import { copyFile, readFile, rm, writeFile } from 'fs/promises'
} from 'node:fs'
import { copyFile, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
import { every } from 'modern-async'
import { dirname, extname, join } from 'path'
import { dirname, extname, join } from 'node:path'
import sharp from 'sharp'
import pngToIco from 'png-to-ico'
import asyncIcns from 'async-icns'
@ -31,7 +31,7 @@ import {
walkDirectory,
windowsPathToUnix,
} from '../../utils'
import { templateDir } from '../setupProject'
import { templateDirectory } from '../setup-project'
import { IMelonPatch } from './command'
// =============================================================================
@ -74,7 +74,7 @@ function constructConfig(name: string) {
brandingVendor: config.vendor,
...defaultBrandsConfig,
...(config.brands[name] || {}),
...config.brands[name],
}
}
@ -111,15 +111,15 @@ async function setupImages(configPath: string, outputPath: string) {
// TODO: Custom MacOS icon support
if (process.platform == 'darwin') {
log.debug('Generating Mac Icons')
const tmp = join(MELON_TMP_DIR, 'macos_icon_info.iconset')
const temporary = join(MELON_TMP_DIR, 'macos_icon_info.iconset')
if (existsSync(tmp)) await rm(tmp, { recursive: true })
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: tmp,
tmpDirectory: temporary,
})
}
@ -155,22 +155,32 @@ async function setupLocale(
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))
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(
@ -184,7 +194,8 @@ async function copyMozFiles(
brandingVendor: string
}
) {
const files = (await walkDirectory(BRANDING_FF)).filter(
const firefoxBrandingDirectoryContents = await walkDirectory(BRANDING_FF)
const files = firefoxBrandingDirectoryContents.filter(
(file) => !existsSync(join(outputPath, file.replace(BRANDING_FF, '')))
)
@ -192,7 +203,7 @@ async function copyMozFiles(
const everythingElse = files.filter((file) => !css.includes(file))
css
for (const [contents, path] of css
.map((filePath) => [
readFileSync(filePath).toString(),
join(outputPath, filePath.replace(BRANDING_FF, '')),
@ -201,17 +212,16 @@ async function copyMozFiles(
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)
})
])) {
mkdirSync(dirname(path), { recursive: true })
writeFileSync(path, contents)
}
// Copy everything else from the default firefox branding directory
everythingElse.forEach((file) => {
for (const file of everythingElse) {
mkdirpSync(dirname(join(outputPath, file.replace(BRANDING_FF, ''))))
copyFileSync(file, join(outputPath, file.replace(BRANDING_FF, '')))
})
}
}
// =============================================================================

View file

@ -1,19 +1,19 @@
// 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 { join } from 'path'
import { existsSync } from 'fs'
import { join } from 'node:path'
import { existsSync } from 'node:fs'
import glob from 'tiny-glob'
import { ENGINE_DIR, SRC_DIR } from '../../constants'
import * as gitPatch from './gitPatch'
import * as copyPatch from './copyPatches'
import * as brandingPatch from './brandingPatch'
import * as gitPatch from './git-patch'
import * as copyPatch from './copy-patches'
import * as brandingPatch from './branding-patch'
import { patchCountFile } from '../../middleware/patch-check'
import { checkHash } from '../../utils'
import { templateDir } from '../setupProject'
import { Task, TaskList } from '../../utils/taskList'
import { writeFile } from 'fs/promises'
import { templateDirectory } from '../setup-project'
import { Task, TaskList } from '../../utils/task-list'
import { writeFile } from 'node:fs/promises'
export interface IMelonPatch {
name: string
@ -23,7 +23,7 @@ export interface IMelonPatch {
function patchMethod<T extends IMelonPatch>(
name: string,
patches: T[],
patchFn: (patch: T, index: number) => Promise<void>
patchFunction: (patch: T, index: number) => Promise<void>
): Task {
return {
name: `Apply ${patches.length} ${name} patches`,
@ -32,7 +32,7 @@ function patchMethod<T extends IMelonPatch>(
new TaskList(
patches.map((patch, index) => ({
name: `Apply ${patch.name}`,
task: () => patchFn(patch, index),
task: () => patchFunction(patch, index),
skip: patch.skip,
}))
),
@ -80,12 +80,11 @@ async function importFolders(): Promise<Task> {
}
async function importGitPatch(): Promise<Task> {
const patches = (
await glob('**/*.patch', {
filesOnly: true,
cwd: SRC_DIR,
})
).map((path) => join(SRC_DIR, path))
let patches = await glob('**/*.patch', {
filesOnly: true,
cwd: SRC_DIR,
})
patches = patches.map((path) => join(SRC_DIR, path))
await writeFile(patchCountFile, patches.length.toString())
@ -97,19 +96,18 @@ async function importGitPatch(): Promise<Task> {
}
async function importInternalPatch(): Promise<Task> {
const patches = (
await glob('*.patch', {
filesOnly: true,
cwd: join(templateDir, 'patches.optional'),
})
).map((path) => ({
const patches = await glob('*.patch', {
filesOnly: true,
cwd: join(templateDirectory, 'patches.optional'),
})
const structuredPatches = patches.map((path) => ({
name: path,
path: join(templateDir, 'patches.optional', path),
path: join(templateDirectory, 'patches.optional', path),
}))
return patchMethod<gitPatch.IGitPatch>(
'gluon',
patches,
structuredPatches,
async (patch) => await gitPatch.apply(patch.path)
)
}

View file

@ -1,11 +1,11 @@
// 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 { existsSync } from 'fs'
import { lstatSync, readFileSync } from 'fs'
import { existsSync } from 'node:fs'
import { lstatSync, readFileSync } from 'node:fs'
import { ensureSymlink, remove } from 'fs-extra'
import { copyFile } from 'fs/promises'
import { dirname, resolve } from 'path'
import { copyFile } from 'node:fs/promises'
import { dirname, resolve } from 'node:path'
import glob from 'tiny-glob'
import { appendToFileSync, mkdirp } from '../../utils'
@ -70,21 +70,20 @@ export interface ICopyPatch extends IMelonPatch {
// Exports
export async function get(): Promise<ICopyPatch[]> {
const files = (
await glob('**/*', {
filesOnly: true,
cwd: SRC_DIR,
})
).filter(
const allFilesInSource = await glob('**/*', {
filesOnly: true,
cwd: SRC_DIR,
})
const files = allFilesInSource.filter(
(f) => !(f.endsWith('.patch') || f.split('/').includes('node_modules'))
)
const manualPatches: ICopyPatch[] = []
files.map((i) => {
const group = i.split('/')[0]
files.map((index) => {
const group = index.split('/')[0]
if (!manualPatches.find((m) => m.name == group)) {
if (!manualPatches.some((m) => m.name == group)) {
manualPatches.push({
name: group,
src: files.filter((f) => f.split('/')[0] == group),
@ -95,8 +94,8 @@ export async function get(): Promise<ICopyPatch[]> {
return manualPatches
}
export async function apply(src: string[]): Promise<void> {
for (const item of src) {
export async function apply(source: string[]): Promise<void> {
for (const item of source) {
await copyManual(item)
}
}

View file

@ -1,8 +1,10 @@
// 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 execa from 'execa'
import { PATCH_ARGS, ENGINE_DIR } from '../../constants'
import { log } from '../../log'
import { IMelonPatch } from './command'
export interface IGitPatch extends IMelonPatch {
@ -14,11 +16,11 @@ export async function apply(path: string): Promise<void> {
await execa('git', ['apply', '-R', ...PATCH_ARGS, path], {
cwd: ENGINE_DIR,
})
} catch (_e) {
} catch {
// If the patch has already been applied, we want to revert it. Because
// there is no good way to check this we are just going to catch and
// discard the error
null
undefined
}
const { stdout, exitCode } = await execa(
@ -27,5 +29,5 @@ export async function apply(path: string): Promise<void> {
{ cwd: ENGINE_DIR }
)
if (exitCode != 0) throw stdout
if (exitCode != 0) log.error(stdout)
}

View file

@ -1,27 +1,29 @@
// 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 { existsSync, readdirSync } from 'fs'
import { resolve } from 'path'
import { existsSync, readdirSync } from 'node:fs'
import { resolve } from 'node:path'
import { bin_name } from '..'
import { ENGINE_DIR } from '../constants'
import { log } from '../log'
import { config, dispatch } from '../utils'
export const run = async (chrome?: string) => {
const dirs = readdirSync(ENGINE_DIR)
const objDirname = dirs.find((dir) => dir.startsWith('obj-'))
const directories = readdirSync(ENGINE_DIR)
const objectDirname = directories.find((directory) =>
directory.startsWith('obj-')
)
if (!objDirname) {
if (!objectDirname) {
throw new Error(`${config.name} needs to be built before you can do this.`)
}
const objDir = resolve(ENGINE_DIR, objDirname)
const objectDirectory = resolve(ENGINE_DIR, objectDirname)
if (existsSync(objDir)) {
if (existsSync(objectDirectory)) {
dispatch(
'./mach',
['run'].concat(chrome ? ['-chrome', chrome] : []),
['run', ...(chrome ? ['-chrome', chrome] : [])],
ENGINE_DIR,
true
)

View file

@ -1,9 +1,9 @@
// 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 { writeFileSync, existsSync, mkdirSync, readFileSync } from 'fs'
import { copyFile } from 'fs/promises'
import { join, dirname } from 'path'
import { writeFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs'
import { copyFile } from 'node:fs/promises'
import { join, dirname } from 'node:path'
import prompts from 'prompts'
@ -13,7 +13,7 @@ import {
configPath,
delay,
getLatestFF,
projectDir,
projectDirectory,
SupportedProducts,
walkDirectory,
} from '../utils'
@ -48,7 +48,7 @@ export async function setupProject(): Promise<void> {
},
{
title: 'Firefox developer edition (Not recommended)',
value: SupportedProducts.FirefoxDev,
value: SupportedProducts.FirefoxDevelopment,
},
{
title: 'Firefox beta (Not recommended)',
@ -136,10 +136,10 @@ export async function setupProject(): Promise<void> {
await copyOptional(['browser/themes'])
}
writeFileSync(configPath, JSON.stringify(config, null, 2))
writeFileSync(configPath, JSON.stringify(config, undefined, 2))
// Append important stuff to gitignore
const gitignore = join(projectDir, '.gitignore')
const gitignore = join(projectDirectory, '.gitignore')
let gitignoreContents = ''
if (existsSync(gitignore)) {
@ -149,48 +149,64 @@ export async function setupProject(): Promise<void> {
gitignoreContents += '\n.dotbuild/\nengine/\nfirefox-*/\nnode_modules/\n'
writeFileSync(gitignore, gitignoreContents)
} catch (e) {
console.log(e)
} catch (error) {
console.log(error)
}
}
// =============================================================================
// Filesystem templating
export const templateDir = join(__dirname, '../..', 'template')
// eslint-disable-next-line unicorn/prefer-module
export const templateDirectory = join(__dirname, '../..', 'template')
/**
* Copy files from the template directory that have .optional in their path,
* based on the function parameters
*
* @param files The files that should be coppied
*/
async function copyOptional(files: string[]) {
await Promise.all(
(
await walkDirectory(templateDir)
const directoryContents = await walkDirectory(templateDirectory)
for (const file of directoryContents) {
if (
!file.includes('.optional') &&
!files
.map((induvidualFile) => file.includes(induvidualFile))
.some(Boolean)
)
.filter((f) => f.includes('.optional'))
.filter((f) => files.map((file) => f.includes(file)).some((b) => b))
.map(async (file) => {
const out = join(projectDir, file.replace(templateDir, '')).replace(
'.optional',
''
)
if (!existsSync(out)) {
mkdirSync(dirname(out), { recursive: true })
await copyFile(file, out)
}
})
)
continue
const outLocation = join(
projectDirectory,
file.replace(templateDirectory, '')
).replace('.optional', '')
if (!existsSync(outLocation)) {
mkdirSync(dirname(outLocation), { recursive: true })
await copyFile(file, outLocation)
}
}
}
/**
* Copy all non-optional files from the template directory
*/
async function copyRequired() {
await Promise.all(
(
await walkDirectory(templateDir)
const directoryContents = await walkDirectory(templateDirectory)
for (const file of directoryContents) {
if (file.includes('.optional')) continue
const outLocation = join(
projectDirectory,
file.replace(templateDirectory, '')
)
.filter((f) => !f.includes('.optional'))
.map(async (file) => {
const out = join(projectDir, file.replace(templateDir, ''))
if (!existsSync(out)) {
mkdirSync(dirname(out), { recursive: true })
await copyFile(file, out)
}
})
)
if (!existsSync(outLocation)) {
mkdirSync(dirname(outLocation), { recursive: true })
await copyFile(file, outLocation)
}
}
}

View file

@ -1,7 +1,7 @@
// 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 { existsSync } from 'fs'
import { existsSync } from 'node:fs'
import { log } from '../log'
import { BIN_NAME, ENGINE_DIR } from '../constants'
import { dispatch, hasConfig } from '../utils'

View file

@ -1,8 +1,13 @@
import { stat, writeFile } from 'fs/promises'
import { dirname, join } from 'path'
import { writeFile } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import { create } from 'xmlbuilder2'
import { DIST_DIR } from '../../constants'
import { dynamicConfig, ensureDir, generateHash, getSize } from '../../utils'
import {
dynamicConfig,
ensureDirectory,
generateHash,
getSize,
} from '../../utils'
import {
downloadAddon,
getAddons,
@ -45,6 +50,6 @@ export async function generateAddonUpdateFiles() {
'update.xml'
)
await ensureDir(dirname(path))
await ensureDirectory(dirname(path))
await writeFile(path, root.end({ prettyPrint: true }))
}

View file

@ -1,8 +1,8 @@
import { existsSync } from 'fs'
import { readFile, writeFile } from 'fs/promises'
import { existsSync } from 'node:fs'
import { readFile, writeFile } from 'node:fs/promises'
import { parse } from 'ini'
import { isAppleSilicon } from 'is-apple-silicon'
import { dirname, join } from 'path'
import { dirname, join } from 'node:path'
import { create } from 'xmlbuilder2'
import { bin_name, config } from '../..'
import { DIST_DIR, OBJ_DIR } from '../../constants'
@ -41,7 +41,8 @@ export async function getPlatformConfig() {
if (!existsSync(platformINI))
platformINI = join(OBJ_DIR, 'dist', 'bin', 'platform.ini')
return parse((await readFile(platformINI)).toString())
const iniContents = await readFile(platformINI)
return parse(iniContents.toString())
}
function getReleaseMarName(releaseInfo: ReleaseInfo): string | undefined {
@ -52,12 +53,15 @@ function getReleaseMarName(releaseInfo: ReleaseInfo): string | undefined {
}
switch (process.platform) {
case 'win32':
case 'win32': {
return releaseInfo.x86?.windowsMar
case 'darwin':
}
case 'darwin': {
return releaseInfo.x86?.macosMar
case 'linux':
}
case 'linux': {
return releaseInfo.x86?.linuxMar
}
}
}
@ -84,7 +88,7 @@ async function writeUpdateFileToDisk(
channel: string,
updateObject: {
updates: {
update: Record<string, any>
update: Record<string, string | undefined>
}
}
) {

View file

@ -2,8 +2,8 @@
// 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 execa from 'execa'
import { existsSync, mkdirSync, readdirSync } from 'fs'
import { resolve } from 'path'
import { existsSync, mkdirSync, readdirSync } from 'node:fs'
import { resolve } from 'node:path'
import { log } from '../log'
export const BIN_NAME = 'gluon'
@ -63,13 +63,13 @@ export const OBJ_DIR = resolve(ENGINE_DIR, `obj-${CONFIG_GUESS}`)
// TODO: Remove this, it is unused
export const FTL_STRING_LINE_REGEX =
/(([a-zA-Z0-9-]*|\.[a-z-]*) =(.*|\.)|\[[a-zA-Z0-9]*\].*(\n\s?\s?})?|\*\[[a-zA-Z0-9]*\] .*(\n\s?\s?})?)/gm
/(([\dA-Za-z-]*|\.[a-z-]*) =(.*|\.)|\[[\dA-Za-z]*].*(\n\s?\s?})?|\*\[[\dA-Za-z]*] .*(\n\s?\s?})?)/gm
// =================
// Windows constants
// =================
export let BASH_PATH: string | null = null
export let BASH_PATH: string | undefined
// All windows specific code should be located inside of this if statement
if (process.platform == 'win32') {

View file

@ -3,14 +3,7 @@ import { config } from '..'
const otherBuildModes = `# You can change to other build modes by running:
# $ gluon set buildMode [dev|debug|release]`
const platformOptimize =
process.platform == 'darwin'
? 'ac_add_options --enable-optimize="-O3 -march=nehalem -mtune=haswell -w"'
: process.platform == 'linux'
? 'ac_add_options --enable-optimize="-O3 -march=haswell -mtune=haswell -w"'
: process.platform == 'win32'
? 'ac_add_options --enable-optimize="-O2 -Qvec -w -clang:-ftree-vectorize -clang:-msse3 -clang:-mssse3 -clang:-msse4.1 -clang:-mtune=haswell"'
: `# Unknown platform ${process.platform}`
const platformOptimize = getPlatformOptimiseFlags()
export const internalMozconfg = (
brand: string,
@ -20,24 +13,27 @@ export const internalMozconfg = (
// Get the specific build options for the current build mode
switch (buildMode) {
case 'dev':
case 'dev': {
buildOptions = `# Development build settings
${otherBuildModes}
ac_add_options --disable-debug`
break
case 'debug':
}
case 'debug': {
buildOptions = `# Debug build settings
${otherBuildModes}
ac_add_options --enable-debug
ac_add_options --disable-optimize`
break
}
case 'release':
case 'release': {
buildOptions = `# Release build settings
ac_add_options --disable-debug
ac_add_options --enable-optimize
${platformOptimize} # Taken from waterfox`
break
}
}
return `
@ -61,3 +57,24 @@ export MOZ_APPUPDATE_HOST=${
}
`
}
function getPlatformOptimiseFlags(): string {
let optimiseFlags = `# Unknown platform ${process.platform}`
switch (process.platform) {
case 'linux': {
optimiseFlags = `ac_add_options --enable-optimize="-O3 -march=haswell -mtune=haswell -w"`
break
}
case 'darwin': {
optimiseFlags = `ac_add_options --enable-optimize="-O3 -march=nehalem -mtune=haswell -w"`
break
}
case 'win32': {
optimiseFlags = `ac_add_options --enable-optimize="-O2 -Qvec -w -clang:-ftree-vectorize -clang:-msse3 -clang:-mssse3 -clang:-msse4.1 -clang:-mtune=haswell"`
break
}
}
return optimiseFlags
}

View file

@ -4,19 +4,19 @@
// 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 commander, { Command } from 'commander'
import { existsSync, readFileSync } from 'fs'
import { resolve } from 'path'
import { existsSync, readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { errorHandler, config as configInited, versionFormatter } from './utils'
import { commands } from './cmds'
import { BIN_NAME, ENGINE_DIR } from './constants'
import { updateCheck } from './middleware/update-check'
import { registerCommand } from './middleware/registerCommand'
import { registerCommand } from './middleware/register-command'
import { log } from './log'
// We have to use a dynamic require here, otherwise the typescript compiler
// mucks up the directory structure
// eslint-disable-next-line @typescript-eslint/no-var-requires
// eslint-disable-next-line @typescript-eslint/no-var-requires, unicorn/prefer-module
const { version: gluonVersion } = require('../package.json')
export const config = configInited
@ -76,14 +76,13 @@ async function middleware(command: commander.Command) {
registerCommand(command.name())
}
commands.forEach((command) => {
if (command.flags) {
if (
command.flags.platforms &&
!command.flags.platforms.includes(process.platform)
) {
return
}
for (const command of commands) {
if (
command.flags &&
command.flags.platforms &&
!command.flags.platforms.includes(process.platform)
) {
continue
}
let buildCommand = program
@ -92,11 +91,11 @@ commands.forEach((command) => {
.aliases(command?.aliases || [])
// Register all of the required options
command?.options?.forEach((opt) => {
for (const opt of command?.options || []) {
buildCommand = buildCommand.option(opt.arg, opt.description)
})
}
buildCommand = buildCommand.action(async (...args) => {
buildCommand = buildCommand.action(async (...arguments_) => {
// Start loading the controller in the background whilst middleware is
// executing
const controller = command.requestController()
@ -107,12 +106,12 @@ commands.forEach((command) => {
// Finish loading the controller and execute it
// eslint-disable-next-line @typescript-eslint/no-extra-semi
;(await controller)(...args)
;(await controller)(...arguments_)
})
})
}
process
.on('uncaughtException', errorHandler)
.on('unhandledException', (err) => errorHandler(err, true))
.on('unhandledException', (error) => errorHandler(error, true))
program.parse(process.argv)

View file

@ -4,6 +4,9 @@
import chalk from 'chalk'
import prompts from 'prompts'
const formatToDoubleDigit = (r: number) =>
r.toString().length == 1 ? `0${r}` : r
class Log {
private startTime: number
@ -26,15 +29,15 @@ class Log {
const mins = Math.floor((elapsedTime / (60 * 1000)) % 60)
const hours = Math.floor((elapsedTime / (60 * 60 * 1000)) % 24)
const format = (r: number) => (r.toString().length == 1 ? `0${r}` : r)
return `${format(hours)}:${format(mins)}:${format(secs)}`
return `${formatToDoubleDigit(hours)}:${formatToDoubleDigit(
mins
)}:${formatToDoubleDigit(secs)}`
}
set isDebug(val: boolean) {
log.debug(`Logger debug mode has been ${val ? 'enabled' : 'disabled'}`)
this._isDebug = val
log.debug(`Logger debug mode has been ${val ? 'enabled' : 'disabled'}`)
set isDebug(value: boolean) {
log.debug(`Logger debug mode has been ${value ? 'enabled' : 'disabled'}`)
this._isDebug = value
log.debug(`Logger debug mode has been ${value ? 'enabled' : 'disabled'}`)
}
get isDebug() {
@ -46,8 +49,8 @@ class Log {
*
* @param args The information you want to provide to the user
*/
debug(...args: unknown[]): void {
if (this.isDebug) console.debug(...args)
debug(...arguments_: unknown[]): void {
if (this.isDebug) console.debug(...arguments_)
}
/**
@ -57,8 +60,8 @@ class Log {
*
* @param args The information you want to provide to the user
*/
info(...args: unknown[]): void {
console.info(chalk.blueBright.bold(this.getDiff()), ...args)
info(...arguments_: unknown[]): void {
console.info(chalk.blueBright.bold(this.getDiff()), ...arguments_)
}
/**
@ -68,8 +71,8 @@ class Log {
*
* @param args The information you want to provide to the user
*/
warning(...args: unknown[]): void {
console.warn(chalk.yellowBright.bold(' WARNING'), ...args)
warning(...arguments_: unknown[]): void {
console.warn(chalk.yellowBright.bold(' WARNING'), ...arguments_)
}
/**
@ -78,8 +81,8 @@ class Log {
*
* @param args The information you want to provide to the user
*/
async hardWarning(...args: unknown[]): Promise<void> {
console.info('', chalk.bgRed.bold('WARNING'), ...args)
async hardWarning(...arguments_: unknown[]): Promise<void> {
console.info('', chalk.bgRed.bold('WARNING'), ...arguments_)
const { answer } = await prompts({
type: 'confirm',
@ -95,9 +98,12 @@ class Log {
*
* @param args The information you want to provide to the user
*/
success(...args: unknown[]): void {
success(...arguments_: unknown[]): void {
console.log()
console.log(`\n${chalk.greenBright.bold('SUCCESS')}`, args.join('\n\t'))
console.log(
`\n${chalk.greenBright.bold('SUCCESS')}`,
arguments_.join('\n\t')
)
}
/**
@ -105,11 +111,11 @@ class Log {
*
* @param args The error you want to throw or a type that you want to convert to an error
*/
error(...args: (Error | unknown)[]): never {
throw args[0] instanceof Error
? args[0]
error(...arguments_: (Error | unknown)[]): never {
throw arguments_[0] instanceof Error
? arguments_[0]
: new Error(
...args.map((a) =>
...arguments_.map((a) =>
typeof a !== 'undefined' ? (a as object).toString() : a
)
)

View file

@ -5,8 +5,8 @@
* Responsible for checking if all new patches have been applied
*/
import { existsSync, readFileSync, writeFileSync } from 'fs'
import { resolve } from 'path'
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { log } from '../log'
import { MELON_DIR, SRC_DIR } from '../constants'
import { walkDirectory } from '../utils'
@ -14,14 +14,12 @@ import { walkDirectory } from '../utils'
export const patchCountFile = resolve(MELON_DIR, 'patchCount')
export const patchCheck = async (): Promise<void> => {
const fileList = (await walkDirectory(resolve(SRC_DIR))).filter((file) =>
file.endsWith('.patch')
)
const directoryCotnents = await walkDirectory(resolve(SRC_DIR))
const fileList = directoryCotnents.filter((file) => file.endsWith('.patch'))
const patchCount = fileList.length
if (!existsSync(patchCountFile)) {
writeFileSync(patchCountFile, '0')
}
if (!existsSync(patchCountFile)) writeFileSync(patchCountFile, '0')
const recordedPatchCount = Number(readFileSync(patchCountFile).toString())

View file

@ -1,8 +1,8 @@
// 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 { writeFileSync } from 'fs'
import { resolve } from 'path'
import { writeFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { MELON_DIR } from '../constants'
/**

View file

@ -15,9 +15,9 @@ export const updateCheck = async (): Promise<void> => {
log.warning(
`Latest version of Firefox (${version}) does not match frozen version (${firefoxVersion}).`
)
} catch (e) {
} catch (error) {
log.warning(`Failed to check for updates.`)
log.askForReport()
log.error(e)
log.error(error)
}
}

2
src/types.d.ts vendored
View file

@ -11,7 +11,7 @@ export interface Cmd {
* writing, is getting a touch long
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
requestController: () => Promise<(...args: any) => void>
requestController: () => Promise<(...arguments_: any) => void>
options?: CmdOption[]
aliases?: string[]

View file

View file

@ -1,8 +1,8 @@
// 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 { readFile } from 'fs/promises'
import { createHash } from 'crypto'
import { readFile } from 'node:fs/promises'
import { createHash } from 'node:crypto'
import { readItem, writeItem } from './store'
/**

View file

@ -21,9 +21,9 @@
// Adapted from the `command-exists` node module
// https://github.com/mathisonian/command-exists
import { execSync } from 'child_process'
import { accessSync, constants } from 'fs'
import path from 'path'
import { execSync } from 'node:child_process'
import { accessSync, constants } from 'node:fs'
import path from 'node:path'
const onWindows = process.platform == 'win32'
@ -31,7 +31,7 @@ const fileNotExistsSync = (commandName: string): boolean => {
try {
accessSync(commandName, constants.F_OK)
return false
} catch (e) {
} catch {
return true
}
}
@ -40,7 +40,7 @@ const localExecutableSync = (commandName: string): boolean => {
try {
accessSync(commandName, constants.F_OK | constants.X_OK)
return true
} catch (e) {
} catch {
return false
}
}
@ -60,7 +60,7 @@ const commandExistsUnixSync = function (
'; exit 0; }'
)
return !!stdout
} catch (error) {
} catch {
return false
}
}
@ -73,7 +73,7 @@ const commandExistsWindowsSync = function (
): boolean {
// Regex from Julio from: https://stackoverflow.com/questions/51494579/regex-windows-path-validator
if (
!/^(?!(?:.*\s|.*\.|\W+)$)(?:[a-zA-Z]:)?(?:(?:[^<>:"|?*\n])+(?:\/\/|\/|\\\\|\\)?)+$/m.test(
!/^(?!(?:.*\s|.*\.|\W+)$)(?:[A-Za-z]:)?(?:[^\n"*:<>?|]+(?:\/\/|\/|\\\\|\\)?)+$/m.test(
commandName
)
) {
@ -82,7 +82,7 @@ const commandExistsWindowsSync = function (
try {
const stdout = execSync('where ' + cleanedCommandName, { stdio: [] })
return !!stdout
} catch (error) {
} catch {
return false
}
}
@ -91,7 +91,7 @@ function cleanInput(toBeCleaned: string): string {
// Windows has a different cleaning process to Unix, so we should go through
// that process first
if (onWindows) {
const isPathName = /[\\]/.test(toBeCleaned)
const isPathName = /\\/.test(toBeCleaned)
if (isPathName) {
const dirname = '"' + path.dirname(toBeCleaned) + '"'
const basename = '"' + path.basename(toBeCleaned) + '"'
@ -102,7 +102,7 @@ function cleanInput(toBeCleaned: string): string {
}
// Otherwise go through the unix cleaning process
if (/[^A-Za-z0-9_\\/:=-]/.test(toBeCleaned)) {
if (/[^\w/:=\\-]/.test(toBeCleaned)) {
toBeCleaned = "'" + toBeCleaned.replace(/'/g, "'\\''") + "'"
toBeCleaned = toBeCleaned
.replace(/^(?:'')+/g, '') // unduplicate single-quote at the beginning
@ -114,9 +114,5 @@ function cleanInput(toBeCleaned: string): string {
export function commandExistsSync(commandName: string): boolean {
const cleanedCommandName = cleanInput(commandName)
if (onWindows) {
return commandExistsWindowsSync(commandName, cleanedCommandName)
} else {
return commandExistsUnixSync(commandName, cleanedCommandName)
}
return onWindows ? commandExistsWindowsSync(commandName, cleanedCommandName) : commandExistsUnixSync(commandName, cleanedCommandName);
}

View file

@ -1,7 +1,7 @@
// 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 { existsSync, readFileSync, unlinkSync, writeFileSync } from 'fs'
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
import {
configPath,
defaultConfig,

View file

@ -5,21 +5,21 @@
* Responsible for loading, parsing and checking the config file for melon
*/
import { existsSync, readFileSync, writeFileSync } from 'fs'
import { join } from 'path'
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import { BIN_NAME } from '../constants'
import { log } from '../log'
export const projectDir = process.cwd()
export const configPath = join(projectDir, 'gluon.json')
export const projectDirectory = process.cwd()
export const configPath = join(projectDirectory, 'gluon.json')
let hasWarnedAboutConfig = false
export enum SupportedProducts {
Firefox = 'firefox',
FirefoxESR = 'firefox-esr',
FirefoxDev = 'firefox-dev',
FirefoxDevelopment = 'firefox-dev',
FirefoxBeta = 'firefox-beta',
FirefoxNightly = 'firefox-nightly',
}
@ -27,7 +27,7 @@ export enum SupportedProducts {
export const validProducts = [
SupportedProducts.Firefox,
SupportedProducts.FirefoxESR,
SupportedProducts.FirefoxDev,
SupportedProducts.FirefoxDevelopment,
SupportedProducts.FirefoxBeta,
SupportedProducts.FirefoxNightly,
]
@ -216,10 +216,10 @@ export function getConfig(): Config {
try {
// Try to parse the contents of the file. May not be valid JSON
fileParsed = JSON.parse(fileContents)
} catch (e) {
} catch (error) {
// Report the error to the user
log.error(`Error parsing ${BIN_NAME} config file located at ${configPath}`)
log.error(e)
log.error(error)
process.exit(1)
}
@ -295,7 +295,7 @@ export function getConfig(): Config {
}
export function saveConfig() {
writeFileSync(configPath, JSON.stringify(config, null, 2))
writeFileSync(configPath, JSON.stringify(config, undefined, 2))
}
export const config = getConfig()

View file

@ -36,26 +36,29 @@ export const configDispatch = (
if (config?.shell) {
switch (config.shell) {
// Don't change anything if we are using the default shell
case 'default':
case 'default': {
break
}
case 'unix':
case 'unix': {
// Bash path provides a unix shell on windows
shell = BASH_PATH || false
break
}
default:
default: {
log.error(`dispatch() does not understand the shell '${shell}'`)
break
}
}
}
const handle = (data: string | Error, killOnError?: boolean) => {
const d = data.toString()
const dataAsString = data.toString()
d.split('\n').forEach((line: string) => {
if (line.length !== 0) logger(removeTimestamp(line))
})
for (const line of dataAsString.split('\n')) {
if (line.length > 0) logger(removeTimestamp(line))
}
if (killOnError) {
log.error('Command failed. See error above.')
@ -67,7 +70,7 @@ export const configDispatch = (
cwd: config?.cwd || process.cwd(),
shell: shell,
env: {
...(config?.env || {}),
...config?.env,
...process.env,
},
})
@ -89,13 +92,13 @@ export const configDispatch = (
*/
export const dispatch = (
cmd: string,
args?: string[],
arguments_?: string[],
cwd?: string,
killOnError?: boolean,
logger = (data: string) => log.info(data)
): Promise<boolean> => {
return configDispatch(cmd, {
args: args,
args: arguments_,
cwd: cwd,
killOnError: killOnError,
logger: logger,

View file

@ -1,11 +1,11 @@
// 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 { createWriteStream } from 'fs'
import { createWriteStream } from 'node:fs'
import axios from 'axios'
import cliProgress from 'cli-progress'
import { Duplex } from 'stream'
import { Duplex } from 'node:stream'
import { log } from '../log'
export async function downloadFileToLocation(
@ -44,11 +44,11 @@ export async function downloadFileToLocation(
receivedBytes += chunk.length
})
data.pipe(writer)
data.on('error', (err: unknown) => {
data.on('error', (error: unknown) => {
log.warning(
`An error occured whilst downloading ${url}. It might be ignored`
)
reject(err)
reject(error)
})
const progressInterval = setInterval(

View file

@ -2,7 +2,7 @@
// 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 * as dynamicConfig from './dynamicConfig'
import * as dynamicConfig from './dynamic-config'
import { readItem, removeItem } from './store'
describe('set', () => {

View file

@ -2,12 +2,12 @@
// 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 chalk from 'chalk'
import { readFileSync } from 'fs'
import { resolve } from 'path'
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { MELON_DIR } from '../constants'
import { log } from '../log'
export const errorHandler = (err: Error, isUnhandledRej: boolean): void => {
export const errorHandler = (error: Error, isUnhandledRej: boolean): never => {
let cc = readFileSync(resolve(MELON_DIR, 'command')).toString()
cc = cc.replace(/(\r\n|\n|\r)/gm, '')
@ -21,11 +21,11 @@ export const errorHandler = (err: Error, isUnhandledRej: boolean): void => {
console.log(
`\n\t`,
isUnhandledRej
? err.toString().replace(/\n/g, '\n\t ')
: err.message.replace(/\n/g, '\n\t ')
? error.toString().replace(/\n/g, '\n\t ')
: error.message.replace(/\n/g, '\n\t ')
)
if (err.stack || isUnhandledRej) {
const stack: string[] | undefined = err.stack?.split('\n')
if (error.stack || isUnhandledRej) {
const stack: string[] | undefined = error.stack?.split('\n')
if (!stack) return

View file

@ -8,9 +8,9 @@ import {
openSync,
rmSync,
writeSync,
} from 'fs'
import { mkdir, readdir, stat, symlink } from 'fs/promises'
import { join, isAbsolute, dirname, relative } from 'path'
} from 'node:fs'
import { mkdir, readdir, stat } from 'node:fs/promises'
import { join, isAbsolute } from 'node:path'
import { log } from '../log'
@ -24,19 +24,19 @@ import { log } from '../log'
export const windowsPathToUnix = (path: string): string =>
process.platform == 'win32' ? path.replace(/\\/g, '/') : path
export async function walkDirectory(dirName: string): Promise<string[]> {
export async function walkDirectory(directory: string): Promise<string[]> {
const output = []
if (!isAbsolute(dirName)) {
if (!isAbsolute(directory)) {
log.askForReport()
log.error('Please provide an absolute input to walkDirectory')
}
try {
const directoryContents = await readdir(dirName)
const directoryContents = await readdir(directory)
for (const file of directoryContents) {
const fullPath = join(dirName, file)
const fullPath = join(directory, file)
const fStat = await stat(fullPath)
if (fStat.isDirectory()) {
@ -47,9 +47,9 @@ export async function walkDirectory(dirName: string): Promise<string[]> {
output.push(fullPath)
}
}
} catch (e) {
} catch (error) {
log.askForReport()
log.error(e)
log.error(error)
}
return output
@ -57,21 +57,21 @@ export async function walkDirectory(dirName: string): Promise<string[]> {
export type TreeType = { [property: string]: string[] | TreeType }
export async function walkDirectoryTree(dirName: string): Promise<TreeType> {
export async function walkDirectoryTree(directory: string): Promise<TreeType> {
const output: TreeType = {}
if (!isAbsolute(dirName)) {
if (!isAbsolute(directory)) {
log.askForReport()
log.error('Please provide an absolute input to walkDirectory')
}
try {
const directoryContents = await readdir(dirName)
const directoryContents = await readdir(directory)
const currentOut = []
for (const file of directoryContents) {
const fullPath = join(dirName, file)
const fullPath = join(directory, file)
const fStat = await stat(fullPath)
if (fStat.isDirectory()) {
@ -82,26 +82,26 @@ export async function walkDirectoryTree(dirName: string): Promise<TreeType> {
}
output['.'] = currentOut
} catch (e) {
} catch (error) {
log.askForReport()
log.error(e)
log.error(error)
}
return output
}
export async function ensureDir(dirName: string): Promise<void> {
if (!existsSync(dirName)) {
await mkdirp(dirName)
export async function ensureDirectory(directory: string): Promise<void> {
if (!existsSync(directory)) {
await mkdirp(directory)
}
}
export function mkdirp(dirName: string): Promise<string | undefined> {
return mkdir(dirName, { recursive: true })
export function mkdirp(directory: string): Promise<string | undefined> {
return mkdir(directory, { recursive: true })
}
export function mkdirpSync(dirName: string): string | undefined {
return mkdirSync(dirName, { recursive: true })
export function mkdirpSync(directory: string): string | undefined {
return mkdirSync(directory, { recursive: true })
}
export function appendToFileSync(fileName: string, content: string): void {
@ -110,58 +110,6 @@ export function appendToFileSync(fileName: string, content: string): void {
closeSync(file)
}
export async function createSymlink(
srcPath: string,
destPath: string,
type?: string
): Promise<void> {
if (existsSync(destPath)) return
const { toDest: src } = symlinkPaths(srcPath, destPath)
const dir = dirname(destPath)
const exists = existsSync(dir)
if (exists) return await symlink(src, destPath, type)
await mkdirp(dir)
return await symlink(src, destPath, type)
}
/**
* Adapted from fs-extra
* @param srcPath
* @param destPath
* @returns
*/
export function symlinkPaths(
srcPath: string,
destPath: string
): { toCwd: string; toDest: string } {
if (isAbsolute(srcPath)) {
if (!existsSync(srcPath)) throw new Error('absolute srcpath does not exist')
return {
toCwd: srcPath,
toDest: srcPath,
}
} else {
const dstdir = dirname(destPath)
const relativeToDst = join(dstdir, srcPath)
if (existsSync(relativeToDst))
return {
toCwd: relativeToDst,
toDest: srcPath,
}
else {
if (!existsSync(srcPath))
throw new Error('relative srcpath does not exist')
return {
toCwd: srcPath,
toDest: relative(dstdir, srcPath),
}
}
}
}
export function filesExist(files: string[]): boolean {
return files.every((file) => existsSync(file))
}
@ -175,5 +123,6 @@ export function ensureEmpty(path: string) {
}
export async function getSize(path: string): Promise<number> {
return (await stat(path)).size
const fileInfo = await stat(path)
return fileInfo.size
}

View file

@ -1,14 +1,14 @@
// 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/.
export * from './commandExists'
export * from './changeTracking'
export * from './command-exists'
export * from './change-tracking'
export * from './delay'
export * from './dispatch'
export * from './error-handler'
export * from './version'
export * from './config'
export * from './stringTemplate'
export * from './string-template'
export * from './fs'
export * from './versionFormatter'
export * as dynamicConfig from './dynamicConfig'
export * from './version-formatter'
export * as dynamicConfig from './dynamic-config'

View file

@ -1,27 +1,27 @@
// 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 { existsSync, readFileSync, unlinkSync, writeFileSync } from 'fs'
import { join } from 'path'
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import { equip, None, OptionEquipped } from 'rustic'
import { MELON_DIR } from '../constants'
export const readItem = <T>(key: string): OptionEquipped<T> => {
const dir = join(MELON_DIR, `${key}.json`)
const fileLocation = join(MELON_DIR, `${key}.json`)
if (!existsSync(dir)) {
if (!existsSync(fileLocation)) {
return equip<T>(None)
}
const data = readFileSync(dir).toString()
const data = readFileSync(fileLocation).toString()
return equip(JSON.parse(data))
}
export const writeItem = <T>(key: string, data: T) => {
const dir = join(MELON_DIR, `${key}.json`)
writeFileSync(dir, JSON.stringify(data, null, 2))
const fileLocation = join(MELON_DIR, `${key}.json`)
writeFileSync(fileLocation, JSON.stringify(data, undefined, 2))
}
export const removeItem = (key: string) => {

View file

@ -8,15 +8,15 @@ export function stringTemplate(
template: string,
variables: { [key: string]: string | number }
): string {
let temp = template
let temporary = template
for (const variable in variables) {
// Replace only replaces the first instance of a string. We want to
// replace all instances
while (temp.includes(`\${${variable}}`)) {
temp = temp.replace(`\${${variable}}`, variables[variable].toString())
while (temporary.includes(`\${${variable}}`)) {
temporary = temporary.replace(`\${${variable}}`, variables[variable].toString())
}
}
return temp
return temporary
}

View file

@ -15,10 +15,10 @@ export interface Task {
export class TaskList {
tasks: Task[]
loggingStyle: LoggingMode = 'normal'
loggingIndentation: string = ''
loggingIndentation = ''
loggingOnError: LoggingErrorMode = 'throw'
error: Error | null = null
error?: Error
constructor(tasks: Task[]) {
this.tasks = tasks
@ -49,22 +49,27 @@ export class TaskList {
const prefixTemplate = `[${type.toUpperCase()}]`
switch (type) {
case 'start':
case 'start': {
prefix += kleur.bold().gray(prefixTemplate)
break
case 'finish':
}
case 'finish': {
prefix += kleur.bold().green(prefixTemplate)
break
case 'fail':
}
case 'fail': {
prefix += kleur.bold().red(prefixTemplate)
break
case 'skip':
}
case 'skip': {
prefix += kleur.bold().yellow(prefixTemplate)
break
case 'info':
}
case 'info': {
prefix += ' '
prefix += kleur.bold().cyan(prefixTemplate)
break
}
}
console.log(`${prefix} ${name}`)
@ -94,17 +99,17 @@ export class TaskList {
await result.indent(this.loggingIndentation + ' ').run()
}
} catch (e) {
} catch (error) {
if (this.loggingOnError == 'throw') {
this.log('fail', task.name)
throw e
throw error
}
if (this.loggingOnError == 'inline') {
this.log('fail', `${task.name}: ${e}`)
this.log('fail', `${task.name}: ${error}`)
}
this.error = e as Error
this.error = error as Error
}
this.log('finish', task.name)

View file

@ -7,8 +7,8 @@ export const versionFormatter = (
options: ({ name: string; value: string } | null | string)[]
): string => {
const spacesValue = Math.max(
...options.map((arg) =>
typeof arg === 'string' ? 0 : arg?.value?.length || 0
...options.map((argument) =>
typeof argument === 'string' ? 0 : argument?.value?.length || 0
)
)

View file

@ -8,7 +8,7 @@ import { SupportedProducts } from './config'
const firefoxTargets = JSON.parse(`{
"${SupportedProducts.Firefox}": "LATEST_FIREFOX_VERSION",
"${SupportedProducts.FirefoxBeta}": "LATEST_FIREFOX_DEVEL_VERSION",
"${SupportedProducts.FirefoxDev}": "FIREFOX_DEVEDITION",
"${SupportedProducts.FirefoxDevelopment}": "FIREFOX_DEVEDITION",
"${SupportedProducts.FirefoxESR}": "FIREFOX_ESR",
"${SupportedProducts.FirefoxNightly}": "FIREFOX_NIGHTLY"
}`)
@ -22,9 +22,9 @@ export const getLatestFF = async (
)
return data[firefoxTargets[product]]
} catch (e) {
} catch (error) {
log.warning('Failed to get latest firefox version with error:')
log.error(e)
log.error(error)
return ''
}

177
yarn.lock
View file

@ -127,6 +127,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==
"@babel/helper-validator-identifier@^7.19.1":
version "7.19.1"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
"@babel/helper-validator-option@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
@ -1114,6 +1119,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190"
integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==
"@types/normalize-package-data@^2.4.0":
version "2.4.1"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==
"@types/picomatch@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003"
@ -1525,6 +1535,11 @@ buffer@^5.2.0, buffer@^5.5.0:
base64-js "^1.3.1"
ieee754 "^1.1.13"
builtin-modules@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
callsites@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
@ -1577,11 +1592,23 @@ ci-info@^3.2.0:
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128"
integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==
ci-info@^3.4.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.5.0.tgz#bfac2a29263de4c829d806b1ab478e35091e171f"
integrity sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==
cjs-module-lexer@^1.0.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40"
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
clean-regexp@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7"
integrity sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==
dependencies:
escape-string-regexp "^1.0.5"
cli-progress@^3.9.1:
version "3.11.2"
resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.11.2.tgz#f8c89bd157e74f3f2c43bcfb3505670b4d48fc77"
@ -1863,6 +1890,26 @@ escodegen@^2.0.0:
optionalDependencies:
source-map "~0.6.1"
eslint-plugin-unicorn@^44.0.2:
version "44.0.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-44.0.2.tgz#6324a001c0a5e2ac00fb51b30db27d14c6c36ab3"
integrity sha512-GLIDX1wmeEqpGaKcnMcqRvMVsoabeF0Ton0EX4Th5u6Kmf7RM9WBl705AXFEsns56ESkEs0uyelLuUTvz9Tr0w==
dependencies:
"@babel/helper-validator-identifier" "^7.19.1"
ci-info "^3.4.0"
clean-regexp "^1.0.0"
eslint-utils "^3.0.0"
esquery "^1.4.0"
indent-string "^4.0.0"
is-builtin-module "^3.2.0"
lodash "^4.17.21"
pluralize "^8.0.0"
read-pkg-up "^7.0.1"
regexp-tree "^0.1.24"
safe-regex "^2.1.1"
semver "^7.3.7"
strip-indent "^3.0.0"
eslint-scope@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
@ -2271,6 +2318,11 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
hosted-git-info@^2.1.4:
version "2.8.9"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
html-encoding-sniffer@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3"
@ -2350,6 +2402,11 @@ imurmurhash@^0.1.4:
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
indent-string@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@ -2387,6 +2444,13 @@ is-arrayish@^0.3.1:
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
is-builtin-module@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.0.tgz#bb0310dfe881f144ca83f30100ceb10cf58835e0"
integrity sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==
dependencies:
builtin-modules "^3.3.0"
is-core-module@^2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"
@ -3071,7 +3135,7 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash@^4.7.0:
lodash@^4.17.21, lodash@^4.7.0:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -3154,6 +3218,11 @@ min-document@^2.19.0:
dependencies:
dom-walk "^0.1.0"
min-indent@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
@ -3228,6 +3297,16 @@ node-releases@^2.0.6:
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==
normalize-package-data@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
dependencies:
hosted-git-info "^2.1.4"
resolve "^1.10.0"
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"
normalize-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
@ -3342,7 +3421,7 @@ parse-headers@^2.0.0:
resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.5.tgz#069793f9356a54008571eb7f9761153e6c770da9"
integrity sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==
parse-json@^5.2.0:
parse-json@^5.0.0, parse-json@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
@ -3416,6 +3495,11 @@ pkg-dir@^4.2.0:
dependencies:
find-up "^4.0.0"
pluralize@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==
png-to-ico@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/png-to-ico/-/png-to-ico-2.1.4.tgz#0f14674c79e23bbd4367696b5852693edf28dbee"
@ -3523,6 +3607,25 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
read-pkg-up@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507"
integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==
dependencies:
find-up "^4.1.0"
read-pkg "^5.2.0"
type-fest "^0.8.1"
read-pkg@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc"
integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==
dependencies:
"@types/normalize-package-data" "^2.4.0"
normalize-package-data "^2.5.0"
parse-json "^5.0.0"
type-fest "^0.6.0"
readable-stream@^3.1.1, readable-stream@^3.4.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
@ -3537,6 +3640,11 @@ regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
regexp-tree@^0.1.24, regexp-tree@~0.1.1:
version "0.1.24"
resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.24.tgz#3d6fa238450a4d66e5bc9c4c14bb720e2196829d"
integrity sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==
regexpp@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
@ -3569,7 +3677,7 @@ resolve.exports@^1.1.0:
resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9"
integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==
resolve@^1.20.0:
resolve@^1.10.0, resolve@^1.20.0:
version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
@ -3619,6 +3727,13 @@ safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-regex@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-2.1.1.tgz#f7128f00d056e2fe5c11e81a1324dd974aadced2"
integrity sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==
dependencies:
regexp-tree "~0.1.1"
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
@ -3636,6 +3751,11 @@ saxes@^5.0.1:
dependencies:
xmlchars "^2.2.0"
"semver@2 || 3 || 4 || 5":
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
semver@7.x, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7:
version "7.3.7"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
@ -3728,6 +3848,32 @@ source-map@^0.7.3:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
spdx-correct@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==
dependencies:
spdx-expression-parse "^3.0.0"
spdx-license-ids "^3.0.0"
spdx-exceptions@^2.1.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d"
integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==
spdx-expression-parse@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==
dependencies:
spdx-exceptions "^2.1.0"
spdx-license-ids "^3.0.0"
spdx-license-ids@^3.0.0:
version "3.0.12"
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz#69077835abe2710b65f03969898b6637b505a779"
integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@ -3781,6 +3927,13 @@ strip-final-newline@^2.0.0:
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
strip-indent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==
dependencies:
min-indent "^1.0.0"
strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
@ -3991,6 +4144,16 @@ type-fest@^0.21.3:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
type-fest@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==
type-fest@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
typedarray-to-buffer@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
@ -4054,6 +4217,14 @@ v8-to-istanbul@^8.1.0:
convert-source-map "^1.6.0"
source-map "^0.7.3"
validate-npm-package-license@^3.0.1:
version "3.0.4"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
dependencies:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"
w3c-hr-time@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"