🎨 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, es2021: true,
node: true, node: true,
}, },
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:unicorn/recommended'],
parser: '@typescript-eslint/parser', parser: '@typescript-eslint/parser',
parserOptions: { parserOptions: {
ecmaVersion: 'latest', ecmaVersion: 'latest',
sourceType: 'module', sourceType: 'module',
}, },
plugins: ['@typescript-eslint'], plugins: ['@typescript-eslint', 'unicorn'],
rules: {}, 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/eslint-plugin": "^5.22.0",
"@typescript-eslint/parser": "^5.22.0", "@typescript-eslint/parser": "^5.22.0",
"eslint": "^8.15.0", "eslint": "^8.15.0",
"eslint-plugin-unicorn": "^44.0.2",
"jest": "^27.4.5", "jest": "^27.4.5",
"prettier": "^2.2.1", "prettier": "^2.2.1",
"ts-jest": "^27.1.2", "ts-jest": "^27.1.2",

View file

@ -1,6 +1,9 @@
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
/* eslint-disable unicorn/no-await-expression-member */
import { Cmd } from './types' import { Cmd } from './types'
export const commands: Cmd[] = [ export const commands: Cmd[] = [
@ -132,7 +135,7 @@ export const commands: Cmd[] = [
cmd: 'setup-project', cmd: 'setup-project',
description: 'Sets up a gluon project for the first time', description: 'Sets up a gluon project for the first time',
requestController: async () => requestController: async () =>
(await import('./commands/setupProject')).setupProject, (await import('./commands/setup-project')).setupProject,
}, },
{ {
cmd: 'status', cmd: 'status',

View file

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

View file

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

View file

@ -2,8 +2,8 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import execa from 'execa' import execa from 'execa'
import { existsSync, statSync } from 'fs' import { existsSync, statSync } from 'node:fs'
import { resolve } from 'path' import { resolve } from 'node:path'
import { log } from '../log' import { log } from '../log'
import { ENGINE_DIR } from '../constants' import { ENGINE_DIR } from '../constants'
@ -17,7 +17,7 @@ export const discard = async (file: string): Promise<void> => {
try { try {
await execa('git', ['restore', file], { cwd: ENGINE_DIR }) await execa('git', ['restore', file], { cwd: ENGINE_DIR })
} catch (e) { } catch {
log.warning(`The file ${file} was not changed`) log.warning(`The file ${file} was not changed`)
} }
} }

View file

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

View file

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

View file

@ -1,26 +1,26 @@
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // 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/. // 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 { log } from '../log'
import { ENGINE_DIR } from '../constants' import { ENGINE_DIR } from '../constants'
import { dispatch } from '../utils' import { dispatch } from '../utils'
export const execute = async (cmd: string[]) => { export const execute = async (cmd: string[]) => {
if (existsSync(ENGINE_DIR)) { if (existsSync(ENGINE_DIR)) {
if (!cmd || cmd.length == 0) if (!cmd || cmd.length === 0)
log.error('You need to specify a command to run.') log.error('You need to specify a command to run.')
const bin = cmd[0] const bin = cmd[0]
const args = cmd const arguments_ = cmd
args.shift() arguments_.shift()
log.info( log.info(
`Executing \`${bin}${args.length !== 0 ? ` ` : ``}${args.join( `Executing \`${bin}${arguments_.length > 0 ? ` ` : ``}${arguments_.join(
' ' ' '
)}\` in \`src\`...` )}\` in \`src\`...`
) )
dispatch(bin, args, ENGINE_DIR) dispatch(bin, arguments_, ENGINE_DIR)
} else { } else {
log.error(`Unable to locate src directory.`) 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import execa from 'execa' import execa from 'execa'
import { existsSync, writeFileSync } from 'fs' import { existsSync, writeFileSync } from 'node:fs'
import { basename, resolve } from 'path' import { basename, resolve } from 'node:path'
import { log } from '../log' import { log } from '../log'
import { ENGINE_DIR, SRC_DIR } from '../constants' import { ENGINE_DIR, SRC_DIR } from '../constants'
import { delay, ensureDir } from '../utils' import { delay, ensureDirectory } from '../utils'
export const getPatchName = (file: string): string => export const getPatchName = (file: string): string =>
`${basename(file).replace(/\./g, '-')}.patch` `${basename(file).replace(/\./g, '-')}.patch`
@ -36,7 +36,7 @@ export const exportFile = async (file: string): Promise<void> => {
const name = getPatchName(file) const name = getPatchName(file)
const patchPath = file.replace(/\./g, '-').split('/').slice(0, -1) 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) { if (proc.stdout.length >= 8000) {
log.warning('') log.warning('')

View file

@ -2,8 +2,8 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import { Command } from 'commander' import { Command } from 'commander'
import { existsSync, readFileSync } from 'fs' import { existsSync, readFileSync } from 'node:fs'
import { resolve } from 'path' import { resolve } from 'node:path'
import { bin_name } from '..' import { bin_name } from '..'
import { log } from '../log' import { log } from '../log'
import { config, configDispatch } from '../utils' import { config, configDispatch } from '../utils'
@ -11,9 +11,9 @@ import { config, configDispatch } from '../utils'
export const init = async (directory: Command | string): Promise<void> => { export const init = async (directory: Command | string): Promise<void> => {
const cwd = process.cwd() 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( log.error(
`Directory "${directory}" not found.\nCheck the directory exists and run |${bin_name} init| again.` `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, '') 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') log.info('Initializing git, this may take some time')
await configDispatch('git', { await configDispatch('git', {
args: ['init'], args: ['init'],
cwd: dir, cwd: absoluteInitDirectory,
shell: 'unix', shell: 'unix',
}) })
await configDispatch('git', { await configDispatch('git', {
args: ['init'], args: ['init'],
cwd: dir, cwd: absoluteInitDirectory,
shell: 'unix', shell: 'unix',
}) })
await configDispatch('git', { await configDispatch('git', {
args: ['checkout', '--orphan', version], args: ['checkout', '--orphan', version],
cwd: dir, cwd: absoluteInitDirectory,
shell: 'unix', shell: 'unix',
}) })
await configDispatch('git', { await configDispatch('git', {
args: ['add', '-f', '.'], args: ['add', '-f', '.'],
cwd: dir, cwd: absoluteInitDirectory,
shell: 'unix', shell: 'unix',
}) })
@ -67,13 +68,13 @@ export const init = async (directory: Command | string): Promise<void> => {
await configDispatch('git', { await configDispatch('git', {
args: ['commit', '-aqm', `"Firefox ${version}"`], args: ['commit', '-aqm', `"Firefox ${version}"`],
cwd: dir, cwd: absoluteInitDirectory,
shell: 'unix', shell: 'unix',
}) })
await configDispatch('git', { await configDispatch('git', {
args: ['checkout', '-b', config.name.toLowerCase().replace(/\s/g, '_')], args: ['checkout', '-b', config.name.toLowerCase().replace(/\s/g, '_')],
cwd: dir, cwd: absoluteInitDirectory,
shell: 'unix', shell: 'unix',
}) })
} }

View file

@ -2,7 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // 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/. // 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' import { isValidLicense } from './license-check'
describe('isValidLicense', () => { describe('isValidLicense', () => {

View file

@ -1,12 +1,12 @@
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import { readFile, writeFile } from 'fs/promises' import { readFile, writeFile } from 'node:fs/promises'
import { join } from 'path' import { join } from 'node:path'
import { SRC_DIR } from '../constants' import { SRC_DIR } from '../constants'
import { walkDirectory } from '../utils/fs' 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 ignoredFiles = new RegExp('.*\\.(json|patch|md|jpeg|png|gif|tiff|ico)')
const licenseIgnore = new RegExp('(//|#) Ignore license in this file', 'g') const licenseIgnore = new RegExp('(//|#) Ignore license in this file', 'g')
@ -32,7 +32,7 @@ const fixableFiles = [
] ]
export async function isValidLicense(path: string): Promise<boolean> { 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') const contents = file.split('\n')
// We need to grab the top 5 lines just in case there are newlines in the // 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), skip: () => ignoredFiles.test(path),
name: path.replace(SRC_DIR, ''), name: path.replace(SRC_DIR, ''),
task: async () => { 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) const hasLicense = await isValidLicense(path)
if (hasLicense) { if (hasLicense) {
@ -73,11 +74,12 @@ export function createTask(path: string, noFix: boolean): Task {
) )
} }
const mpl = ( const mplHeader = // eslint-disable-next-line unicorn/prefer-module
await readFile(join(__dirname, 'license-check.txt')) await readFile(join(__dirname, 'license-check.txt'), {
).toString() encoding: 'utf8',
})
const { comment, commentOpen, commentClose } = fixable const { comment, commentOpen, commentClose } = fixable
let header = mpl let header = mplHeader
.split('\n') .split('\n')
.map((ln) => (comment || '') + ln) .map((ln) => (comment || '') + ln)
.join('\n') .join('\n')
@ -86,7 +88,7 @@ export function createTask(path: string, noFix: boolean): Task {
header = commentOpen + header + commentClose 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 // 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import { existsSync } from 'fs' import { existsSync } from 'node:fs'
import { copyFile, mkdir, readdir, unlink } from 'fs/promises' import { copyFile, mkdir, readdir, unlink } from 'node:fs/promises'
import { join, resolve } from 'path' import { join, resolve } from 'node:path'
import { bin_name, config } from '..' import { bin_name, config } from '..'
import { DIST_DIR, ENGINE_DIR, OBJ_DIR } from '../constants' 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}`) log.error(`Cannot locate the 'mach' binary within ${ENGINE_DIR}`)
} }
const args = ['package'] const arguments_ = ['package']
log.info( log.info(
`Packaging \`${config.binaryName}\` with args ${JSON.stringify( `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') log.info('Copying results up')
@ -53,31 +53,41 @@ export const gluonPackage = async () => {
if (!existsSync(DIST_DIR)) await mkdir(DIST_DIR, { recursive: true }) if (!existsSync(DIST_DIR)) await mkdir(DIST_DIR, { recursive: true })
log.debug('Indexing files to copy') 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()) .filter((entry) => entry.isFile())
.map((entry) => entry.name) .map((entry) => entry.name)
for (const file of files) { for (const file of files) {
const destFile = join(DIST_DIR, file) const destinationFile = join(DIST_DIR, file)
log.debug(`Copying ${file}`) log.debug(`Copying ${file}`)
if (existsSync(destFile)) await unlink(destFile) if (existsSync(destinationFile)) await unlink(destinationFile)
await copyFile(join(OBJ_DIR, 'dist', file), destFile) await copyFile(join(OBJ_DIR, 'dist', file), destinationFile)
} }
// Windows has some special dist files that are available within the dist // Windows has some special dist files that are available within the dist
// directory. // directory.
if (process.platform == 'win32') { 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( log.error(
`Could not find windows installer files located at '${installerDistDirectory}'` `Could not find windows installer files located at '${installerDistributionDirectory}'`
) )
} }
const windowsInstallerFiles = ( const installerDistributionDirectoryContents = await readdir(
await readdir(installerDistDirectory, { withFileTypes: true }) installerDistributionDirectory,
{ withFileTypes: true }
) )
const windowsInstallerFiles = installerDistributionDirectoryContents
.filter((entry) => entry.isFile()) .filter((entry) => entry.isFile())
.map((entry) => entry.name) .map((entry) => entry.name)
@ -96,10 +106,13 @@ export const gluonPackage = async () => {
} }
// Actually copy // Actually copy
const destFile = join(DIST_DIR, newFileName) const destinationFile = join(DIST_DIR, newFileName)
log.debug(`Copying ${file}`) log.debug(`Copying ${file}`)
if (existsSync(destFile)) await unlink(destFile) if (existsSync(destinationFile)) await unlink(destinationFile)
await copyFile(join(installerDistDirectory, file), destFile) await copyFile(
join(installerDistributionDirectory, file),
destinationFile
)
} }
} }
@ -137,18 +150,10 @@ async function createMarFile(version: string, channel: string) {
// On macos this should be // On macos this should be
// <obj dir>/dist/${binaryName}/${brandFullName}.app and on everything else, // <obj dir>/dist/${binaryName}/${brandFullName}.app and on everything else,
// the contents of the folder <obj dir>/dist/${binaryName} // the contents of the folder <obj dir>/dist/${binaryName}
let binary: string const binary =
process.platform == 'darwin'
if (process.platform == 'darwin') { ? join(OBJ_DIR, 'dist', config.binaryName, `${getCurrentBrandName()}.app`)
binary = join( : join(OBJ_DIR, 'dist', config.binaryName)
OBJ_DIR,
'dist',
config.binaryName,
`${getCurrentBrandName()}.app`
)
} else {
binary = join(OBJ_DIR, 'dist', config.binaryName)
}
const marPath = windowsPathToUnix(join(DIST_DIR, 'output.mar')) const marPath = windowsPathToUnix(join(DIST_DIR, 'output.mar'))
await configDispatch('./tools/update-packaging/make_full_update.sh', { await configDispatch('./tools/update-packaging/make_full_update.sh', {

View file

@ -10,10 +10,10 @@ import {
readFileSync, readFileSync,
writeFileSync, writeFileSync,
copyFileSync, copyFileSync,
} from 'fs' } from 'node:fs'
import { copyFile, readFile, rm, writeFile } from 'fs/promises' import { copyFile, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
import { every } from 'modern-async' import { every } from 'modern-async'
import { dirname, extname, join } from 'path' import { dirname, extname, join } from 'node:path'
import sharp from 'sharp' import sharp from 'sharp'
import pngToIco from 'png-to-ico' import pngToIco from 'png-to-ico'
import asyncIcns from 'async-icns' import asyncIcns from 'async-icns'
@ -31,7 +31,7 @@ import {
walkDirectory, walkDirectory,
windowsPathToUnix, windowsPathToUnix,
} from '../../utils' } from '../../utils'
import { templateDir } from '../setupProject' import { templateDirectory } from '../setup-project'
import { IMelonPatch } from './command' import { IMelonPatch } from './command'
// ============================================================================= // =============================================================================
@ -74,7 +74,7 @@ function constructConfig(name: string) {
brandingVendor: config.vendor, brandingVendor: config.vendor,
...defaultBrandsConfig, ...defaultBrandsConfig,
...(config.brands[name] || {}), ...config.brands[name],
} }
} }
@ -111,15 +111,15 @@ async function setupImages(configPath: string, outputPath: string) {
// TODO: Custom MacOS icon support // TODO: Custom MacOS icon support
if (process.platform == 'darwin') { if (process.platform == 'darwin') {
log.debug('Generating Mac Icons') 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({ asyncIcns.convert({
input: join(configPath, 'logo.png'), input: join(configPath, 'logo.png'),
output: join(outputPath, 'firefox.icns'), output: join(outputPath, 'firefox.icns'),
sizes: [16, 32, 64, 128, 256, 512], sizes: [16, 32, 64, 128, 256, 512],
tmpDirectory: tmp, tmpDirectory: temporary,
}) })
} }
@ -155,22 +155,32 @@ async function setupLocale(
brandingVendor: string brandingVendor: string
} }
) { ) {
// eslint-disable-next-line @typescript-eslint/no-extra-semi for (const file of await walkDirectory(
;(await walkDirectory(join(templateDir, 'branding.optional'))) join(templateDirectory, 'branding.optional')
.map((file) => )) {
windowsPathToUnix(file).replace( const fileContents = await readFile(windowsPathToUnix(file), {
windowsPathToUnix(join(templateDir, 'branding.optional') + '/'), 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)
) )
.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( async function copyMozFiles(
@ -184,7 +194,8 @@ async function copyMozFiles(
brandingVendor: string 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, ''))) (file) => !existsSync(join(outputPath, file.replace(BRANDING_FF, '')))
) )
@ -192,7 +203,7 @@ async function copyMozFiles(
const everythingElse = files.filter((file) => !css.includes(file)) const everythingElse = files.filter((file) => !css.includes(file))
css for (const [contents, path] of css
.map((filePath) => [ .map((filePath) => [
readFileSync(filePath).toString(), readFileSync(filePath).toString(),
join(outputPath, filePath.replace(BRANDING_FF, '')), join(outputPath, filePath.replace(BRANDING_FF, '')),
@ -201,17 +212,16 @@ async function copyMozFiles(
contents.replace(CSS_REPLACE_REGEX, 'var(--theme-bg)') + contents.replace(CSS_REPLACE_REGEX, 'var(--theme-bg)') +
`:root { --theme-bg: ${brandingConfig.backgroundColor} }`, `:root { --theme-bg: ${brandingConfig.backgroundColor} }`,
path, path,
]) ])) {
.forEach(([contents, path]) => {
mkdirSync(dirname(path), { recursive: true }) mkdirSync(dirname(path), { recursive: true })
writeFileSync(path, contents) writeFileSync(path, contents)
}) }
// Copy everything else from the default firefox branding directory // 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, '')))) mkdirpSync(dirname(join(outputPath, file.replace(BRANDING_FF, ''))))
copyFileSync(file, 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 // 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import { join } from 'path' import { join } from 'node:path'
import { existsSync } from 'fs' import { existsSync } from 'node:fs'
import glob from 'tiny-glob' import glob from 'tiny-glob'
import { ENGINE_DIR, SRC_DIR } from '../../constants' import { ENGINE_DIR, SRC_DIR } from '../../constants'
import * as gitPatch from './gitPatch' import * as gitPatch from './git-patch'
import * as copyPatch from './copyPatches' import * as copyPatch from './copy-patches'
import * as brandingPatch from './brandingPatch' import * as brandingPatch from './branding-patch'
import { patchCountFile } from '../../middleware/patch-check' import { patchCountFile } from '../../middleware/patch-check'
import { checkHash } from '../../utils' import { checkHash } from '../../utils'
import { templateDir } from '../setupProject' import { templateDirectory } from '../setup-project'
import { Task, TaskList } from '../../utils/taskList' import { Task, TaskList } from '../../utils/task-list'
import { writeFile } from 'fs/promises' import { writeFile } from 'node:fs/promises'
export interface IMelonPatch { export interface IMelonPatch {
name: string name: string
@ -23,7 +23,7 @@ export interface IMelonPatch {
function patchMethod<T extends IMelonPatch>( function patchMethod<T extends IMelonPatch>(
name: string, name: string,
patches: T[], patches: T[],
patchFn: (patch: T, index: number) => Promise<void> patchFunction: (patch: T, index: number) => Promise<void>
): Task { ): Task {
return { return {
name: `Apply ${patches.length} ${name} patches`, name: `Apply ${patches.length} ${name} patches`,
@ -32,7 +32,7 @@ function patchMethod<T extends IMelonPatch>(
new TaskList( new TaskList(
patches.map((patch, index) => ({ patches.map((patch, index) => ({
name: `Apply ${patch.name}`, name: `Apply ${patch.name}`,
task: () => patchFn(patch, index), task: () => patchFunction(patch, index),
skip: patch.skip, skip: patch.skip,
})) }))
), ),
@ -80,12 +80,11 @@ async function importFolders(): Promise<Task> {
} }
async function importGitPatch(): Promise<Task> { async function importGitPatch(): Promise<Task> {
const patches = ( let patches = await glob('**/*.patch', {
await glob('**/*.patch', {
filesOnly: true, filesOnly: true,
cwd: SRC_DIR, cwd: SRC_DIR,
}) })
).map((path) => join(SRC_DIR, path)) patches = patches.map((path) => join(SRC_DIR, path))
await writeFile(patchCountFile, patches.length.toString()) await writeFile(patchCountFile, patches.length.toString())
@ -97,19 +96,18 @@ async function importGitPatch(): Promise<Task> {
} }
async function importInternalPatch(): Promise<Task> { async function importInternalPatch(): Promise<Task> {
const patches = ( const patches = await glob('*.patch', {
await glob('*.patch', {
filesOnly: true, filesOnly: true,
cwd: join(templateDir, 'patches.optional'), cwd: join(templateDirectory, 'patches.optional'),
}) })
).map((path) => ({ const structuredPatches = patches.map((path) => ({
name: path, name: path,
path: join(templateDir, 'patches.optional', path), path: join(templateDirectory, 'patches.optional', path),
})) }))
return patchMethod<gitPatch.IGitPatch>( return patchMethod<gitPatch.IGitPatch>(
'gluon', 'gluon',
patches, structuredPatches,
async (patch) => await gitPatch.apply(patch.path) 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 // 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import { existsSync } from 'fs' import { existsSync } from 'node:fs'
import { lstatSync, readFileSync } from 'fs' import { lstatSync, readFileSync } from 'node:fs'
import { ensureSymlink, remove } from 'fs-extra' import { ensureSymlink, remove } from 'fs-extra'
import { copyFile } from 'fs/promises' import { copyFile } from 'node:fs/promises'
import { dirname, resolve } from 'path' import { dirname, resolve } from 'node:path'
import glob from 'tiny-glob' import glob from 'tiny-glob'
import { appendToFileSync, mkdirp } from '../../utils' import { appendToFileSync, mkdirp } from '../../utils'
@ -70,21 +70,20 @@ export interface ICopyPatch extends IMelonPatch {
// Exports // Exports
export async function get(): Promise<ICopyPatch[]> { export async function get(): Promise<ICopyPatch[]> {
const files = ( const allFilesInSource = await glob('**/*', {
await glob('**/*', {
filesOnly: true, filesOnly: true,
cwd: SRC_DIR, cwd: SRC_DIR,
}) })
).filter( const files = allFilesInSource.filter(
(f) => !(f.endsWith('.patch') || f.split('/').includes('node_modules')) (f) => !(f.endsWith('.patch') || f.split('/').includes('node_modules'))
) )
const manualPatches: ICopyPatch[] = [] const manualPatches: ICopyPatch[] = []
files.map((i) => { files.map((index) => {
const group = i.split('/')[0] const group = index.split('/')[0]
if (!manualPatches.find((m) => m.name == group)) { if (!manualPatches.some((m) => m.name == group)) {
manualPatches.push({ manualPatches.push({
name: group, name: group,
src: files.filter((f) => f.split('/')[0] == group), src: files.filter((f) => f.split('/')[0] == group),
@ -95,8 +94,8 @@ export async function get(): Promise<ICopyPatch[]> {
return manualPatches return manualPatches
} }
export async function apply(src: string[]): Promise<void> { export async function apply(source: string[]): Promise<void> {
for (const item of src) { for (const item of source) {
await copyManual(item) await copyManual(item)
} }
} }

View file

@ -1,8 +1,10 @@
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import execa from 'execa' import execa from 'execa'
import { PATCH_ARGS, ENGINE_DIR } from '../../constants' import { PATCH_ARGS, ENGINE_DIR } from '../../constants'
import { log } from '../../log'
import { IMelonPatch } from './command' import { IMelonPatch } from './command'
export interface IGitPatch extends IMelonPatch { 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], { await execa('git', ['apply', '-R', ...PATCH_ARGS, path], {
cwd: ENGINE_DIR, cwd: ENGINE_DIR,
}) })
} catch (_e) { } catch {
// If the patch has already been applied, we want to revert it. Because // 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 // there is no good way to check this we are just going to catch and
// discard the error // discard the error
null undefined
} }
const { stdout, exitCode } = await execa( const { stdout, exitCode } = await execa(
@ -27,5 +29,5 @@ export async function apply(path: string): Promise<void> {
{ cwd: ENGINE_DIR } { 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 // 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import { existsSync, readdirSync } from 'fs' import { existsSync, readdirSync } from 'node:fs'
import { resolve } from 'path' import { resolve } from 'node:path'
import { bin_name } from '..' import { bin_name } from '..'
import { ENGINE_DIR } from '../constants' import { ENGINE_DIR } from '../constants'
import { log } from '../log' import { log } from '../log'
import { config, dispatch } from '../utils' import { config, dispatch } from '../utils'
export const run = async (chrome?: string) => { export const run = async (chrome?: string) => {
const dirs = readdirSync(ENGINE_DIR) const directories = readdirSync(ENGINE_DIR)
const objDirname = dirs.find((dir) => dir.startsWith('obj-')) 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.`) 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( dispatch(
'./mach', './mach',
['run'].concat(chrome ? ['-chrome', chrome] : []), ['run', ...(chrome ? ['-chrome', chrome] : [])],
ENGINE_DIR, ENGINE_DIR,
true true
) )

View file

@ -1,9 +1,9 @@
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import { writeFileSync, existsSync, mkdirSync, readFileSync } from 'fs' import { writeFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs'
import { copyFile } from 'fs/promises' import { copyFile } from 'node:fs/promises'
import { join, dirname } from 'path' import { join, dirname } from 'node:path'
import prompts from 'prompts' import prompts from 'prompts'
@ -13,7 +13,7 @@ import {
configPath, configPath,
delay, delay,
getLatestFF, getLatestFF,
projectDir, projectDirectory,
SupportedProducts, SupportedProducts,
walkDirectory, walkDirectory,
} from '../utils' } from '../utils'
@ -48,7 +48,7 @@ export async function setupProject(): Promise<void> {
}, },
{ {
title: 'Firefox developer edition (Not recommended)', title: 'Firefox developer edition (Not recommended)',
value: SupportedProducts.FirefoxDev, value: SupportedProducts.FirefoxDevelopment,
}, },
{ {
title: 'Firefox beta (Not recommended)', title: 'Firefox beta (Not recommended)',
@ -136,10 +136,10 @@ export async function setupProject(): Promise<void> {
await copyOptional(['browser/themes']) await copyOptional(['browser/themes'])
} }
writeFileSync(configPath, JSON.stringify(config, null, 2)) writeFileSync(configPath, JSON.stringify(config, undefined, 2))
// Append important stuff to gitignore // Append important stuff to gitignore
const gitignore = join(projectDir, '.gitignore') const gitignore = join(projectDirectory, '.gitignore')
let gitignoreContents = '' let gitignoreContents = ''
if (existsSync(gitignore)) { if (existsSync(gitignore)) {
@ -149,48 +149,64 @@ export async function setupProject(): Promise<void> {
gitignoreContents += '\n.dotbuild/\nengine/\nfirefox-*/\nnode_modules/\n' gitignoreContents += '\n.dotbuild/\nengine/\nfirefox-*/\nnode_modules/\n'
writeFileSync(gitignore, gitignoreContents) writeFileSync(gitignore, gitignoreContents)
} catch (e) { } catch (error) {
console.log(e) console.log(error)
} }
} }
// ============================================================================= // =============================================================================
// Filesystem templating // 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[]) { async function copyOptional(files: string[]) {
await Promise.all( const directoryContents = await walkDirectory(templateDirectory)
(
await walkDirectory(templateDir) for (const file of directoryContents) {
if (
!file.includes('.optional') &&
!files
.map((induvidualFile) => file.includes(induvidualFile))
.some(Boolean)
) )
.filter((f) => f.includes('.optional')) continue
.filter((f) => files.map((file) => f.includes(file)).some((b) => b))
.map(async (file) => { const outLocation = join(
const out = join(projectDir, file.replace(templateDir, '')).replace( projectDirectory,
'.optional', file.replace(templateDirectory, '')
'' ).replace('.optional', '')
)
if (!existsSync(out)) { if (!existsSync(outLocation)) {
mkdirSync(dirname(out), { recursive: true }) mkdirSync(dirname(outLocation), { recursive: true })
await copyFile(file, out) await copyFile(file, outLocation)
}
} }
})
)
} }
/**
* Copy all non-optional files from the template directory
*/
async function copyRequired() { async function copyRequired() {
await Promise.all( const directoryContents = await walkDirectory(templateDirectory)
(
await walkDirectory(templateDir) 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) => { if (!existsSync(outLocation)) {
const out = join(projectDir, file.replace(templateDir, '')) mkdirSync(dirname(outLocation), { recursive: true })
if (!existsSync(out)) { await copyFile(file, outLocation)
mkdirSync(dirname(out), { recursive: true }) }
await copyFile(file, out)
} }
})
)
} }

View file

@ -1,7 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // 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/. // 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 { log } from '../log'
import { BIN_NAME, ENGINE_DIR } from '../constants' import { BIN_NAME, ENGINE_DIR } from '../constants'
import { dispatch, hasConfig } from '../utils' import { dispatch, hasConfig } from '../utils'

View file

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

View file

@ -1,8 +1,8 @@
import { existsSync } from 'fs' import { existsSync } from 'node:fs'
import { readFile, writeFile } from 'fs/promises' import { readFile, writeFile } from 'node:fs/promises'
import { parse } from 'ini' import { parse } from 'ini'
import { isAppleSilicon } from 'is-apple-silicon' import { isAppleSilicon } from 'is-apple-silicon'
import { dirname, join } from 'path' import { dirname, join } from 'node:path'
import { create } from 'xmlbuilder2' import { create } from 'xmlbuilder2'
import { bin_name, config } from '../..' import { bin_name, config } from '../..'
import { DIST_DIR, OBJ_DIR } from '../../constants' import { DIST_DIR, OBJ_DIR } from '../../constants'
@ -41,7 +41,8 @@ export async function getPlatformConfig() {
if (!existsSync(platformINI)) if (!existsSync(platformINI))
platformINI = join(OBJ_DIR, 'dist', 'bin', 'platform.ini') 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 { function getReleaseMarName(releaseInfo: ReleaseInfo): string | undefined {
@ -52,13 +53,16 @@ function getReleaseMarName(releaseInfo: ReleaseInfo): string | undefined {
} }
switch (process.platform) { switch (process.platform) {
case 'win32': case 'win32': {
return releaseInfo.x86?.windowsMar return releaseInfo.x86?.windowsMar
case 'darwin': }
case 'darwin': {
return releaseInfo.x86?.macosMar return releaseInfo.x86?.macosMar
case 'linux': }
case 'linux': {
return releaseInfo.x86?.linuxMar return releaseInfo.x86?.linuxMar
} }
}
} }
function getReleaseMarURL(releaseInfo: ReleaseInfo) { function getReleaseMarURL(releaseInfo: ReleaseInfo) {
@ -84,7 +88,7 @@ async function writeUpdateFileToDisk(
channel: string, channel: string,
updateObject: { updateObject: {
updates: { 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import execa from 'execa' import execa from 'execa'
import { existsSync, mkdirSync, readdirSync } from 'fs' import { existsSync, mkdirSync, readdirSync } from 'node:fs'
import { resolve } from 'path' import { resolve } from 'node:path'
import { log } from '../log' import { log } from '../log'
export const BIN_NAME = 'gluon' 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 // TODO: Remove this, it is unused
export const FTL_STRING_LINE_REGEX = 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 // 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 // All windows specific code should be located inside of this if statement
if (process.platform == 'win32') { if (process.platform == 'win32') {

View file

@ -3,14 +3,7 @@ import { config } from '..'
const otherBuildModes = `# You can change to other build modes by running: const otherBuildModes = `# You can change to other build modes by running:
# $ gluon set buildMode [dev|debug|release]` # $ gluon set buildMode [dev|debug|release]`
const platformOptimize = const platformOptimize = getPlatformOptimiseFlags()
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}`
export const internalMozconfg = ( export const internalMozconfg = (
brand: string, brand: string,
@ -20,25 +13,28 @@ export const internalMozconfg = (
// Get the specific build options for the current build mode // Get the specific build options for the current build mode
switch (buildMode) { switch (buildMode) {
case 'dev': case 'dev': {
buildOptions = `# Development build settings buildOptions = `# Development build settings
${otherBuildModes} ${otherBuildModes}
ac_add_options --disable-debug` ac_add_options --disable-debug`
break break
case 'debug': }
case 'debug': {
buildOptions = `# Debug build settings buildOptions = `# Debug build settings
${otherBuildModes} ${otherBuildModes}
ac_add_options --enable-debug ac_add_options --enable-debug
ac_add_options --disable-optimize` ac_add_options --disable-optimize`
break break
}
case 'release': case 'release': {
buildOptions = `# Release build settings buildOptions = `# Release build settings
ac_add_options --disable-debug ac_add_options --disable-debug
ac_add_options --enable-optimize ac_add_options --enable-optimize
${platformOptimize} # Taken from waterfox` ${platformOptimize} # Taken from waterfox`
break break
} }
}
return ` 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import commander, { Command } from 'commander' import commander, { Command } from 'commander'
import { existsSync, readFileSync } from 'fs' import { existsSync, readFileSync } from 'node:fs'
import { resolve } from 'path' import { resolve } from 'node:path'
import { errorHandler, config as configInited, versionFormatter } from './utils' import { errorHandler, config as configInited, versionFormatter } from './utils'
import { commands } from './cmds' import { commands } from './cmds'
import { BIN_NAME, ENGINE_DIR } from './constants' import { BIN_NAME, ENGINE_DIR } from './constants'
import { updateCheck } from './middleware/update-check' import { updateCheck } from './middleware/update-check'
import { registerCommand } from './middleware/registerCommand' import { registerCommand } from './middleware/register-command'
import { log } from './log' import { log } from './log'
// We have to use a dynamic require here, otherwise the typescript compiler // We have to use a dynamic require here, otherwise the typescript compiler
// mucks up the directory structure // 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') const { version: gluonVersion } = require('../package.json')
export const config = configInited export const config = configInited
@ -76,14 +76,13 @@ async function middleware(command: commander.Command) {
registerCommand(command.name()) registerCommand(command.name())
} }
commands.forEach((command) => { for (const command of commands) {
if (command.flags) {
if ( if (
command.flags &&
command.flags.platforms && command.flags.platforms &&
!command.flags.platforms.includes(process.platform) !command.flags.platforms.includes(process.platform)
) { ) {
return continue
}
} }
let buildCommand = program let buildCommand = program
@ -92,11 +91,11 @@ commands.forEach((command) => {
.aliases(command?.aliases || []) .aliases(command?.aliases || [])
// Register all of the required options // 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.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 // Start loading the controller in the background whilst middleware is
// executing // executing
const controller = command.requestController() const controller = command.requestController()
@ -107,12 +106,12 @@ commands.forEach((command) => {
// Finish loading the controller and execute it // Finish loading the controller and execute it
// eslint-disable-next-line @typescript-eslint/no-extra-semi // eslint-disable-next-line @typescript-eslint/no-extra-semi
;(await controller)(...args) ;(await controller)(...arguments_)
}) })
}) }
process process
.on('uncaughtException', errorHandler) .on('uncaughtException', errorHandler)
.on('unhandledException', (err) => errorHandler(err, true)) .on('unhandledException', (error) => errorHandler(error, true))
program.parse(process.argv) program.parse(process.argv)

View file

@ -4,6 +4,9 @@
import chalk from 'chalk' import chalk from 'chalk'
import prompts from 'prompts' import prompts from 'prompts'
const formatToDoubleDigit = (r: number) =>
r.toString().length == 1 ? `0${r}` : r
class Log { class Log {
private startTime: number private startTime: number
@ -26,15 +29,15 @@ class Log {
const mins = Math.floor((elapsedTime / (60 * 1000)) % 60) const mins = Math.floor((elapsedTime / (60 * 1000)) % 60)
const hours = Math.floor((elapsedTime / (60 * 60 * 1000)) % 24) const hours = Math.floor((elapsedTime / (60 * 60 * 1000)) % 24)
const format = (r: number) => (r.toString().length == 1 ? `0${r}` : r) return `${formatToDoubleDigit(hours)}:${formatToDoubleDigit(
mins
return `${format(hours)}:${format(mins)}:${format(secs)}` )}:${formatToDoubleDigit(secs)}`
} }
set isDebug(val: boolean) { set isDebug(value: boolean) {
log.debug(`Logger debug mode has been ${val ? 'enabled' : 'disabled'}`) log.debug(`Logger debug mode has been ${value ? 'enabled' : 'disabled'}`)
this._isDebug = val this._isDebug = value
log.debug(`Logger debug mode has been ${val ? 'enabled' : 'disabled'}`) log.debug(`Logger debug mode has been ${value ? 'enabled' : 'disabled'}`)
} }
get isDebug() { get isDebug() {
@ -46,8 +49,8 @@ class Log {
* *
* @param args The information you want to provide to the user * @param args The information you want to provide to the user
*/ */
debug(...args: unknown[]): void { debug(...arguments_: unknown[]): void {
if (this.isDebug) console.debug(...args) if (this.isDebug) console.debug(...arguments_)
} }
/** /**
@ -57,8 +60,8 @@ class Log {
* *
* @param args The information you want to provide to the user * @param args The information you want to provide to the user
*/ */
info(...args: unknown[]): void { info(...arguments_: unknown[]): void {
console.info(chalk.blueBright.bold(this.getDiff()), ...args) 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 * @param args The information you want to provide to the user
*/ */
warning(...args: unknown[]): void { warning(...arguments_: unknown[]): void {
console.warn(chalk.yellowBright.bold(' WARNING'), ...args) console.warn(chalk.yellowBright.bold(' WARNING'), ...arguments_)
} }
/** /**
@ -78,8 +81,8 @@ class Log {
* *
* @param args The information you want to provide to the user * @param args The information you want to provide to the user
*/ */
async hardWarning(...args: unknown[]): Promise<void> { async hardWarning(...arguments_: unknown[]): Promise<void> {
console.info('', chalk.bgRed.bold('WARNING'), ...args) console.info('', chalk.bgRed.bold('WARNING'), ...arguments_)
const { answer } = await prompts({ const { answer } = await prompts({
type: 'confirm', type: 'confirm',
@ -95,9 +98,12 @@ class Log {
* *
* @param args The information you want to provide to the user * @param args The information you want to provide to the user
*/ */
success(...args: unknown[]): void { success(...arguments_: unknown[]): void {
console.log() 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 * @param args The error you want to throw or a type that you want to convert to an error
*/ */
error(...args: (Error | unknown)[]): never { error(...arguments_: (Error | unknown)[]): never {
throw args[0] instanceof Error throw arguments_[0] instanceof Error
? args[0] ? arguments_[0]
: new Error( : new Error(
...args.map((a) => ...arguments_.map((a) =>
typeof a !== 'undefined' ? (a as object).toString() : 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 * Responsible for checking if all new patches have been applied
*/ */
import { existsSync, readFileSync, writeFileSync } from 'fs' import { existsSync, readFileSync, writeFileSync } from 'node:fs'
import { resolve } from 'path' import { resolve } from 'node:path'
import { log } from '../log' import { log } from '../log'
import { MELON_DIR, SRC_DIR } from '../constants' import { MELON_DIR, SRC_DIR } from '../constants'
import { walkDirectory } from '../utils' import { walkDirectory } from '../utils'
@ -14,14 +14,12 @@ import { walkDirectory } from '../utils'
export const patchCountFile = resolve(MELON_DIR, 'patchCount') export const patchCountFile = resolve(MELON_DIR, 'patchCount')
export const patchCheck = async (): Promise<void> => { export const patchCheck = async (): Promise<void> => {
const fileList = (await walkDirectory(resolve(SRC_DIR))).filter((file) => const directoryCotnents = await walkDirectory(resolve(SRC_DIR))
file.endsWith('.patch')
) const fileList = directoryCotnents.filter((file) => file.endsWith('.patch'))
const patchCount = fileList.length const patchCount = fileList.length
if (!existsSync(patchCountFile)) { if (!existsSync(patchCountFile)) writeFileSync(patchCountFile, '0')
writeFileSync(patchCountFile, '0')
}
const recordedPatchCount = Number(readFileSync(patchCountFile).toString()) 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 // 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import { writeFileSync } from 'fs' import { writeFileSync } from 'node:fs'
import { resolve } from 'path' import { resolve } from 'node:path'
import { MELON_DIR } from '../constants' import { MELON_DIR } from '../constants'
/** /**

View file

@ -15,9 +15,9 @@ export const updateCheck = async (): Promise<void> => {
log.warning( log.warning(
`Latest version of Firefox (${version}) does not match frozen version (${firefoxVersion}).` `Latest version of Firefox (${version}) does not match frozen version (${firefoxVersion}).`
) )
} catch (e) { } catch (error) {
log.warning(`Failed to check for updates.`) log.warning(`Failed to check for updates.`)
log.askForReport() 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 * writing, is getting a touch long
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
requestController: () => Promise<(...args: any) => void> requestController: () => Promise<(...arguments_: any) => void>
options?: CmdOption[] options?: CmdOption[]
aliases?: string[] aliases?: string[]

View file

View file

@ -1,8 +1,8 @@
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import { readFile } from 'fs/promises' import { readFile } from 'node:fs/promises'
import { createHash } from 'crypto' import { createHash } from 'node:crypto'
import { readItem, writeItem } from './store' import { readItem, writeItem } from './store'
/** /**

View file

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

View file

@ -1,7 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // 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/. // 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 { import {
configPath, configPath,
defaultConfig, defaultConfig,

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // 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/. // 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' import { readItem, removeItem } from './store'
describe('set', () => { describe('set', () => {

View file

@ -2,12 +2,12 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import chalk from 'chalk' import chalk from 'chalk'
import { readFileSync } from 'fs' import { readFileSync } from 'node:fs'
import { resolve } from 'path' import { resolve } from 'node:path'
import { MELON_DIR } from '../constants' import { MELON_DIR } from '../constants'
import { log } from '../log' 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() let cc = readFileSync(resolve(MELON_DIR, 'command')).toString()
cc = cc.replace(/(\r\n|\n|\r)/gm, '') cc = cc.replace(/(\r\n|\n|\r)/gm, '')
@ -21,11 +21,11 @@ export const errorHandler = (err: Error, isUnhandledRej: boolean): void => {
console.log( console.log(
`\n\t`, `\n\t`,
isUnhandledRej isUnhandledRej
? err.toString().replace(/\n/g, '\n\t ') ? error.toString().replace(/\n/g, '\n\t ')
: err.message.replace(/\n/g, '\n\t ') : error.message.replace(/\n/g, '\n\t ')
) )
if (err.stack || isUnhandledRej) { if (error.stack || isUnhandledRej) {
const stack: string[] | undefined = err.stack?.split('\n') const stack: string[] | undefined = error.stack?.split('\n')
if (!stack) return if (!stack) return

View file

@ -8,9 +8,9 @@ import {
openSync, openSync,
rmSync, rmSync,
writeSync, writeSync,
} from 'fs' } from 'node:fs'
import { mkdir, readdir, stat, symlink } from 'fs/promises' import { mkdir, readdir, stat } from 'node:fs/promises'
import { join, isAbsolute, dirname, relative } from 'path' import { join, isAbsolute } from 'node:path'
import { log } from '../log' import { log } from '../log'
@ -24,19 +24,19 @@ import { log } from '../log'
export const windowsPathToUnix = (path: string): string => export const windowsPathToUnix = (path: string): string =>
process.platform == 'win32' ? path.replace(/\\/g, '/') : path 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 = [] const output = []
if (!isAbsolute(dirName)) { if (!isAbsolute(directory)) {
log.askForReport() log.askForReport()
log.error('Please provide an absolute input to walkDirectory') log.error('Please provide an absolute input to walkDirectory')
} }
try { try {
const directoryContents = await readdir(dirName) const directoryContents = await readdir(directory)
for (const file of directoryContents) { for (const file of directoryContents) {
const fullPath = join(dirName, file) const fullPath = join(directory, file)
const fStat = await stat(fullPath) const fStat = await stat(fullPath)
if (fStat.isDirectory()) { if (fStat.isDirectory()) {
@ -47,9 +47,9 @@ export async function walkDirectory(dirName: string): Promise<string[]> {
output.push(fullPath) output.push(fullPath)
} }
} }
} catch (e) { } catch (error) {
log.askForReport() log.askForReport()
log.error(e) log.error(error)
} }
return output return output
@ -57,21 +57,21 @@ export async function walkDirectory(dirName: string): Promise<string[]> {
export type TreeType = { [property: string]: string[] | TreeType } 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 = {} const output: TreeType = {}
if (!isAbsolute(dirName)) { if (!isAbsolute(directory)) {
log.askForReport() log.askForReport()
log.error('Please provide an absolute input to walkDirectory') log.error('Please provide an absolute input to walkDirectory')
} }
try { try {
const directoryContents = await readdir(dirName) const directoryContents = await readdir(directory)
const currentOut = [] const currentOut = []
for (const file of directoryContents) { for (const file of directoryContents) {
const fullPath = join(dirName, file) const fullPath = join(directory, file)
const fStat = await stat(fullPath) const fStat = await stat(fullPath)
if (fStat.isDirectory()) { if (fStat.isDirectory()) {
@ -82,26 +82,26 @@ export async function walkDirectoryTree(dirName: string): Promise<TreeType> {
} }
output['.'] = currentOut output['.'] = currentOut
} catch (e) { } catch (error) {
log.askForReport() log.askForReport()
log.error(e) log.error(error)
} }
return output return output
} }
export async function ensureDir(dirName: string): Promise<void> { export async function ensureDirectory(directory: string): Promise<void> {
if (!existsSync(dirName)) { if (!existsSync(directory)) {
await mkdirp(dirName) await mkdirp(directory)
} }
} }
export function mkdirp(dirName: string): Promise<string | undefined> { export function mkdirp(directory: string): Promise<string | undefined> {
return mkdir(dirName, { recursive: true }) return mkdir(directory, { recursive: true })
} }
export function mkdirpSync(dirName: string): string | undefined { export function mkdirpSync(directory: string): string | undefined {
return mkdirSync(dirName, { recursive: true }) return mkdirSync(directory, { recursive: true })
} }
export function appendToFileSync(fileName: string, content: string): void { export function appendToFileSync(fileName: string, content: string): void {
@ -110,58 +110,6 @@ export function appendToFileSync(fileName: string, content: string): void {
closeSync(file) 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 { export function filesExist(files: string[]): boolean {
return files.every((file) => existsSync(file)) return files.every((file) => existsSync(file))
} }
@ -175,5 +123,6 @@ export function ensureEmpty(path: string) {
} }
export async function getSize(path: string): Promise<number> { 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 // 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
export * from './commandExists' export * from './command-exists'
export * from './changeTracking' export * from './change-tracking'
export * from './delay' export * from './delay'
export * from './dispatch' export * from './dispatch'
export * from './error-handler' export * from './error-handler'
export * from './version' export * from './version'
export * from './config' export * from './config'
export * from './stringTemplate' export * from './string-template'
export * from './fs' export * from './fs'
export * from './versionFormatter' export * from './version-formatter'
export * as dynamicConfig from './dynamicConfig' 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 // 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 // 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/. // 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 { join } from 'path' import { join } from 'node:path'
import { equip, None, OptionEquipped } from 'rustic' import { equip, None, OptionEquipped } from 'rustic'
import { MELON_DIR } from '../constants' import { MELON_DIR } from '../constants'
export const readItem = <T>(key: string): OptionEquipped<T> => { 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) return equip<T>(None)
} }
const data = readFileSync(dir).toString() const data = readFileSync(fileLocation).toString()
return equip(JSON.parse(data)) return equip(JSON.parse(data))
} }
export const writeItem = <T>(key: string, data: T) => { export const writeItem = <T>(key: string, data: T) => {
const dir = join(MELON_DIR, `${key}.json`) const fileLocation = join(MELON_DIR, `${key}.json`)
writeFileSync(dir, JSON.stringify(data, null, 2)) writeFileSync(fileLocation, JSON.stringify(data, undefined, 2))
} }
export const removeItem = (key: string) => { export const removeItem = (key: string) => {

View file

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

View file

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

View file

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

View file

@ -8,7 +8,7 @@ import { SupportedProducts } from './config'
const firefoxTargets = JSON.parse(`{ const firefoxTargets = JSON.parse(`{
"${SupportedProducts.Firefox}": "LATEST_FIREFOX_VERSION", "${SupportedProducts.Firefox}": "LATEST_FIREFOX_VERSION",
"${SupportedProducts.FirefoxBeta}": "LATEST_FIREFOX_DEVEL_VERSION", "${SupportedProducts.FirefoxBeta}": "LATEST_FIREFOX_DEVEL_VERSION",
"${SupportedProducts.FirefoxDev}": "FIREFOX_DEVEDITION", "${SupportedProducts.FirefoxDevelopment}": "FIREFOX_DEVEDITION",
"${SupportedProducts.FirefoxESR}": "FIREFOX_ESR", "${SupportedProducts.FirefoxESR}": "FIREFOX_ESR",
"${SupportedProducts.FirefoxNightly}": "FIREFOX_NIGHTLY" "${SupportedProducts.FirefoxNightly}": "FIREFOX_NIGHTLY"
}`) }`)
@ -22,9 +22,9 @@ export const getLatestFF = async (
) )
return data[firefoxTargets[product]] return data[firefoxTargets[product]]
} catch (e) { } catch (error) {
log.warning('Failed to get latest firefox version with error:') log.warning('Failed to get latest firefox version with error:')
log.error(e) log.error(error)
return '' 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" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== 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": "@babel/helper-validator-option@^7.18.6":
version "7.18.6" version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" 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" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190"
integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== 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": "@types/picomatch@^2.3.0":
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003" 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" base64-js "^1.3.1"
ieee754 "^1.1.13" 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: callsites@^3.0.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" 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" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128"
integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg== 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: cjs-module-lexer@^1.0.0:
version "1.2.2" version "1.2.2"
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40"
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== 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: cli-progress@^3.9.1:
version "3.11.2" version "3.11.2"
resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.11.2.tgz#f8c89bd157e74f3f2c43bcfb3505670b4d48fc77" resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.11.2.tgz#f8c89bd157e74f3f2c43bcfb3505670b4d48fc77"
@ -1863,6 +1890,26 @@ escodegen@^2.0.0:
optionalDependencies: optionalDependencies:
source-map "~0.6.1" 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: eslint-scope@^5.1.1:
version "5.1.1" version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
@ -2271,6 +2318,11 @@ has@^1.0.3:
dependencies: dependencies:
function-bind "^1.1.1" 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: html-encoding-sniffer@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" 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" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== 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: inflight@^1.0.4:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 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" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== 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: is-core-module@^2.9.0:
version "2.9.0" version "2.9.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" 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" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash@^4.7.0: lodash@^4.17.21, lodash@^4.7.0:
version "4.17.21" version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -3154,6 +3218,11 @@ min-document@^2.19.0:
dependencies: dependencies:
dom-walk "^0.1.0" 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: minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 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" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== 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: normalize-path@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 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" resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.5.tgz#069793f9356a54008571eb7f9761153e6c770da9"
integrity sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA== 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" version "5.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
@ -3416,6 +3495,11 @@ pkg-dir@^4.2.0:
dependencies: dependencies:
find-up "^4.0.0" 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: png-to-ico@^2.1.4:
version "2.1.4" version "2.1.4"
resolved "https://registry.yarnpkg.com/png-to-ico/-/png-to-ico-2.1.4.tgz#0f14674c79e23bbd4367696b5852693edf28dbee" 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" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== 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: readable-stream@^3.1.1, readable-stream@^3.4.0:
version "3.6.0" version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" 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" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== 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: regexpp@^3.2.0:
version "3.2.0" version "3.2.0"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" 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" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9"
integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==
resolve@^1.20.0: resolve@^1.10.0, resolve@^1.20.0:
version "1.22.1" version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== 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" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 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": "safer-buffer@>= 2.1.2 < 3":
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
@ -3636,6 +3751,11 @@ saxes@^5.0.1:
dependencies: dependencies:
xmlchars "^2.2.0" 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: semver@7.x, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7:
version "7.3.7" version "7.3.7"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" 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" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== 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: sprintf-js@~1.0.2:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 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" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== 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: strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" 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" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== 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: typedarray-to-buffer@^3.1.5:
version "3.1.5" version "3.1.5"
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" 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" convert-source-map "^1.6.0"
source-map "^0.7.3" 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: w3c-hr-time@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"