Merge branch 'development' into releases/2.6.2

This commit is contained in:
Markus Olsson 2021-01-18 14:51:28 +01:00 committed by GitHub
commit c184eb25c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 318 additions and 221 deletions

View file

@ -36,7 +36,7 @@ install GitHub Desktop:
- Windows users can install using [Chocolatey](https://chocolatey.org/) package manager:
`c:\> choco install github-desktop`
- macOS users can install using [Homebrew](https://brew.sh/) package manager:
`$ brew cask install github`
`$ brew install --cask github`
Installers for various Linux distributions can be found on the
[`shiftkey/desktop`](https://github.com/shiftkey/desktop) fork.

View file

@ -47,7 +47,7 @@ function getBundleIdentifiers(editor: ExternalEditor): ReadonlyArray<string> {
case ExternalEditor.VSCodium:
return ['com.visualstudio.code.oss']
case ExternalEditor.SublimeText:
return ['com.sublimetext.3']
return ['com.sublimetext.4', 'com.sublimetext.3', 'com.sublimetext.2']
case ExternalEditor.BBEdit:
return ['com.barebones.bbedit']
case ExternalEditor.PhpStorm:

View file

@ -1,4 +1,4 @@
import { git, GitError, parseCommitSHA } from './core'
import { git, parseCommitSHA } from './core'
import { stageFiles } from './update-index'
import { Repository } from '../../models/repository'
import { WorkingDirectoryFileChange } from '../../models/status'
@ -15,7 +15,7 @@ export async function createCommit(
repository: Repository,
message: string,
files: ReadonlyArray<WorkingDirectoryFileChange>
): Promise<string | undefined> {
): Promise<string> {
// Clear the staging area, our diffs reflect the difference between the
// working directory and the last commit (if any) so our commits should
// do the same thing.
@ -23,20 +23,15 @@ export async function createCommit(
await stageFiles(repository, files)
try {
const result = await git(
['commit', '-F', '-'],
repository.path,
'createCommit',
{
stdin: message,
}
)
return parseCommitSHA(result)
} catch (e) {
logCommitError(e)
return undefined
}
const result = await git(
['commit', '-F', '-'],
repository.path,
'createCommit',
{
stdin: message,
}
)
return parseCommitSHA(result)
}
/**
@ -51,80 +46,54 @@ export async function createMergeCommit(
repository: Repository,
files: ReadonlyArray<WorkingDirectoryFileChange>,
manualResolutions: ReadonlyMap<string, ManualConflictResolution> = new Map()
): Promise<string | undefined> {
try {
// apply manual conflict resolutions
for (const [path, resolution] of manualResolutions) {
const file = files.find(f => f.path === path)
if (file !== undefined) {
await stageManualConflictResolution(repository, file, resolution)
} else {
log.error(
`couldn't find file ${path} even though there's a manual resolution for it`
)
}
): Promise<string> {
// apply manual conflict resolutions
for (const [path, resolution] of manualResolutions) {
const file = files.find(f => f.path === path)
if (file !== undefined) {
await stageManualConflictResolution(repository, file, resolution)
} else {
log.error(
`couldn't find file ${path} even though there's a manual resolution for it`
)
}
const otherFiles = files.filter(f => !manualResolutions.has(f.path))
await stageFiles(repository, otherFiles)
const result = await git(
[
'commit',
// no-edit here ensures the app does not accidentally invoke the user's editor
'--no-edit',
// By default Git merge commits do not contain any commentary (which
// are lines prefixed with `#`). This works because the Git CLI will
// prompt the user to edit the file in `.git/COMMIT_MSG` before
// committing, and then it will run `--cleanup=strip`.
//
// This clashes with our use of `--no-edit` above as Git will now change
// it's behavior to invoke `--cleanup=whitespace` as it did not ask
// the user to edit the COMMIT_MSG as part of creating a commit.
//
// From the docs on git-commit (https://git-scm.com/docs/git-commit) I'll
// quote the relevant section:
// --cleanup=<mode>
// strip
// Strip leading and trailing empty lines, trailing whitespace,
// commentary and collapse consecutive empty lines.
// whitespace
// Same as `strip` except #commentary is not removed.
// default
// Same as `strip` if the message is to be edited. Otherwise `whitespace`.
//
// We should emulate the behavior in this situation because we don't
// let the user view or change the commit message before making the
// commit.
'--cleanup=strip',
],
repository.path,
'createMergeCommit'
)
return parseCommitSHA(result)
} catch (e) {
logCommitError(e)
return undefined
}
}
/**
* Commit failures could come from a pre-commit hook rejection.
* So display a bit more context than we otherwise would,
* then re-raise the error.
*/
function logCommitError(e: Error): void {
if (e instanceof GitError) {
const output = e.result.stderr.trim()
const otherFiles = files.filter(f => !manualResolutions.has(f.path))
const standardError = output.length > 0 ? `, with output: '${output}'` : ''
const { exitCode } = e.result
const error = new Error(
`Commit failed - exit code ${exitCode} received${standardError}`
)
error.name = 'commit-failed'
throw error
} else {
throw e
}
await stageFiles(repository, otherFiles)
const result = await git(
[
'commit',
// no-edit here ensures the app does not accidentally invoke the user's editor
'--no-edit',
// By default Git merge commits do not contain any commentary (which
// are lines prefixed with `#`). This works because the Git CLI will
// prompt the user to edit the file in `.git/COMMIT_MSG` before
// committing, and then it will run `--cleanup=strip`.
//
// This clashes with our use of `--no-edit` above as Git will now change
// it's behavior to invoke `--cleanup=whitespace` as it did not ask
// the user to edit the COMMIT_MSG as part of creating a commit.
//
// From the docs on git-commit (https://git-scm.com/docs/git-commit) I'll
// quote the relevant section:
// --cleanup=<mode>
// strip
// Strip leading and trailing empty lines, trailing whitespace,
// commentary and collapse consecutive empty lines.
// whitespace
// Same as `strip` except #commentary is not removed.
// default
// Same as `strip` if the message is to be edited. Otherwise `whitespace`.
//
// We should emulate the behavior in this situation because we don't
// let the user view or change the commit message before making the
// commit.
'--cleanup=strip',
],
repository.path,
'createMergeCommit'
)
return parseCommitSHA(result)
}

View file

@ -15,6 +15,9 @@ import * as Path from 'path'
import { Repository } from '../../models/repository'
import { getConfigValue, getGlobalConfigValue } from './config'
import { isErrnoException } from '../errno-exception'
import { ChildProcess } from 'child_process'
import { Readable } from 'stream'
import split2 from 'split2'
/**
* An extension of the execution options in dugite that
@ -54,6 +57,9 @@ export interface IGitResult extends DugiteResult {
/** The human-readable error description, based on `gitError`. */
readonly gitErrorDescription: string | null
/** Both stdout and stderr combined. */
readonly combinedOutput: string
/**
* The path that the Git command was executed from, i.e. the
* process working directory (not to be confused with the Git
@ -61,22 +67,6 @@ export interface IGitResult extends DugiteResult {
*/
readonly path: string
}
function getResultMessage(result: IGitResult) {
const description = result.gitErrorDescription
if (description) {
return description
}
if (result.stderr.length) {
return result.stderr
} else if (result.stdout.length) {
return result.stdout
} else {
return 'Unknown error'
}
}
export class GitError extends Error {
/** The result from the failed command. */
public readonly result: IGitResult
@ -84,12 +74,35 @@ export class GitError extends Error {
/** The args for the failed command. */
public readonly args: ReadonlyArray<string>
/**
* Whether or not the error message is just the raw output of the git command.
*/
public readonly isRawMessage: boolean
public constructor(result: IGitResult, args: ReadonlyArray<string>) {
super(getResultMessage(result))
let rawMessage = true
let message
if (result.gitErrorDescription) {
message = result.gitErrorDescription
rawMessage = false
} else if (result.combinedOutput.length > 0) {
message = result.combinedOutput
} else if (result.stderr.length) {
message = result.stderr
} else if (result.stdout.length) {
message = result.stdout
} else {
message = 'Unknown error'
rawMessage = false
}
super(message)
this.name = 'GitError'
this.result = result
this.args = args
this.isRawMessage = rawMessage
}
}
@ -123,8 +136,24 @@ export async function git(
expectedErrors: new Set(),
}
let combinedOutput = ''
const opts = { ...defaultOptions, ...options }
opts.processCallback = (process: ChildProcess) => {
options?.processCallback?.(process)
const combineOutput = (readable: Readable | null) => {
if (readable) {
readable.pipe(split2()).on('data', (line: string) => {
combinedOutput += line + '\n'
})
}
}
combineOutput(process.stderr)
combineOutput(process.stdout)
}
// Explicitly set TERM to 'dumb' so that if Desktop was launched
// from a terminal or if the system environment variables
// have TERM set Git won't consider us as a smart terminal.
@ -160,7 +189,13 @@ export async function git(
}
const gitErrorDescription = gitError ? getDescriptionForError(gitError) : null
const gitResult = { ...result, gitError, gitErrorDescription, path }
const gitResult = {
...result,
gitError,
gitErrorDescription,
combinedOutput,
path,
}
let acceptableError = true
if (gitError && opts.expectedErrors) {

View file

@ -22,6 +22,7 @@ import {
} from '../local-storage'
import { PushOptions } from '../git'
import { getShowSideBySideDiff } from '../../ui/lib/diff-mode'
import { remote } from 'electron'
const StatsEndpoint = 'https://central.github.com/api/usage/desktop'
@ -312,6 +313,12 @@ interface ICalculatedStats {
* default) diff view mode
*/
readonly diffMode: 'split' | 'unified'
/**
* Whether the app was launched from the Applications folder or not. This is
* only relevant on macOS, null will be sent otherwise.
*/
readonly launchedFromApplicationsFolder: boolean | null
}
type DailyStats = ICalculatedStats &
@ -481,6 +488,10 @@ export class StatsStore implements IStatsStore {
).length
const diffMode = getShowSideBySideDiff() ? 'split' : 'unified'
// isInApplicationsFolder is undefined when not running on Darwin
const launchedFromApplicationsFolder =
remote.app.isInApplicationsFolder?.() ?? null
return {
eventType: 'usage',
version: getVersion(),
@ -497,6 +508,7 @@ export class StatsStore implements IStatsStore {
...repositoryCounts,
repositoriesCommittedInWithoutWriteAccess,
diffMode,
launchedFromApplicationsFolder,
}
}

View file

@ -2376,90 +2376,106 @@ export class AppStore extends TypedBaseStore<IAppState> {
const gitStore = this.gitStoreCache.get(repository)
const result = await this.isCommitting(repository, () => {
return gitStore.performFailableOperation(async () => {
return this.withIsCommitting(repository, async () => {
const result = await gitStore.performFailableOperation(async () => {
const message = await formatCommitMessage(repository, context)
return createCommit(repository, message, selectedFiles)
})
if (result !== undefined) {
await this._recordCommitStats(
gitStore,
repository,
state,
context,
files
)
await this._refreshRepository(repository)
await this.refreshChangesSection(repository, {
includingStatus: true,
clearPartialState: true,
})
}
return result !== undefined
})
}
if (result) {
this.statsStore.recordCommit()
private async _recordCommitStats(
gitStore: GitStore,
repository: Repository,
repositoryState: IRepositoryState,
context: ICommitContext,
files: readonly WorkingDirectoryFileChange[]
) {
this.statsStore.recordCommit()
const includedPartialSelections = files.some(
file => file.selection.getSelectionType() === DiffSelectionType.Partial
)
if (includedPartialSelections) {
this.statsStore.recordPartialCommit()
}
const { trailers } = context
if (trailers !== undefined && trailers.some(isCoAuthoredByTrailer)) {
this.statsStore.recordCoAuthoredCommit()
}
const account = getAccountForRepository(this.accounts, repository)
if (repository.gitHubRepository !== null) {
if (account !== null) {
if (account.endpoint === getDotComAPIEndpoint()) {
this.statsStore.recordCommitToDotcom()
} else {
this.statsStore.recordCommitToEnterprise()
}
const { commitAuthor } = state
if (commitAuthor !== null) {
const commitEmail = commitAuthor.email.toLowerCase()
const attributableEmails = getAttributableEmailsFor(account)
const commitEmailMatchesAccount = attributableEmails.some(
email => email.toLowerCase() === commitEmail
)
if (!commitEmailMatchesAccount) {
this.statsStore.recordUnattributedCommit()
}
}
}
const branchProtectionsFound = await this.repositoriesStore.hasBranchProtectionsConfigured(
repository.gitHubRepository
)
if (branchProtectionsFound) {
this.statsStore.recordCommitToRepositoryWithBranchProtections()
}
const branchName = findRemoteBranchName(
gitStore.tip,
gitStore.currentRemote,
repository.gitHubRepository
)
if (branchName !== null) {
const { changesState } = this.repositoryStateCache.get(repository)
if (changesState.currentBranchProtected) {
this.statsStore.recordCommitToProtectedBranch()
}
}
if (
repository.gitHubRepository !== null &&
!hasWritePermission(repository.gitHubRepository)
) {
this.statsStore.recordCommitToRepositoryWithoutWriteAccess()
this.statsStore.recordRepositoryCommitedInWithoutWriteAccess(
repository.gitHubRepository.dbID
)
}
}
await this._refreshRepository(repository)
await this.refreshChangesSection(repository, {
includingStatus: true,
clearPartialState: true,
})
const includedPartialSelections = files.some(
file => file.selection.getSelectionType() === DiffSelectionType.Partial
)
if (includedPartialSelections) {
this.statsStore.recordPartialCommit()
}
return result || false
const { trailers } = context
if (trailers !== undefined && trailers.some(isCoAuthoredByTrailer)) {
this.statsStore.recordCoAuthoredCommit()
}
const account = getAccountForRepository(this.accounts, repository)
if (repository.gitHubRepository !== null) {
if (account !== null) {
if (account.endpoint === getDotComAPIEndpoint()) {
this.statsStore.recordCommitToDotcom()
} else {
this.statsStore.recordCommitToEnterprise()
}
const { commitAuthor } = repositoryState
if (commitAuthor !== null) {
const commitEmail = commitAuthor.email.toLowerCase()
const attributableEmails = getAttributableEmailsFor(account)
const commitEmailMatchesAccount = attributableEmails.some(
email => email.toLowerCase() === commitEmail
)
if (!commitEmailMatchesAccount) {
this.statsStore.recordUnattributedCommit()
}
}
}
const branchProtectionsFound = await this.repositoriesStore.hasBranchProtectionsConfigured(
repository.gitHubRepository
)
if (branchProtectionsFound) {
this.statsStore.recordCommitToRepositoryWithBranchProtections()
}
const branchName = findRemoteBranchName(
gitStore.tip,
gitStore.currentRemote,
repository.gitHubRepository
)
if (branchName !== null) {
const { changesState } = this.repositoryStateCache.get(repository)
if (changesState.currentBranchProtected) {
this.statsStore.recordCommitToProtectedBranch()
}
}
if (
repository.gitHubRepository !== null &&
!hasWritePermission(repository.gitHubRepository)
) {
this.statsStore.recordCommitToRepositoryWithoutWriteAccess()
this.statsStore.recordRepositoryCommitedInWithoutWriteAccess(
repository.gitHubRepository.dbID
)
}
}
}
/** This shouldn't be called directly. See `Dispatcher`. */
@ -3562,14 +3578,14 @@ export class AppStore extends TypedBaseStore<IAppState> {
})
}
private async isCommitting(
private async withIsCommitting(
repository: Repository,
fn: () => Promise<string | undefined>
): Promise<boolean | undefined> {
fn: () => Promise<boolean>
): Promise<boolean> {
const state = this.repositoryStateCache.get(repository)
// ensure the user doesn't try and commit again
if (state.isCommitting) {
return
return false
}
this.repositoryStateCache.update(repository, () => ({
@ -3578,8 +3594,7 @@ export class AppStore extends TypedBaseStore<IAppState> {
this.emitUpdate()
try {
const sha = await fn()
return sha !== undefined
return await fn()
} finally {
this.repositoryStateCache.update(repository, () => ({
isCommitting: false,

View file

@ -108,16 +108,13 @@ export class AppError extends React.Component<IAppErrorProps, IAppErrorState> {
}
private renderErrorMessage(error: Error) {
const e = error instanceof ErrorWithMetadata ? error.underlyingError : error
const e = getUnderlyingError(error)
if (e instanceof GitError) {
// See getResultMessage in core.ts
// If the error message is the same as stderr or stdout then we know
// it's output from git and we'll display it in fixed-width font
if (e.message === e.result.stderr || e.message === e.result.stdout) {
const formattedMessage = this.formatGitErrorMessage(e.message)
return <p className="monospace">{formattedMessage}</p>
}
// If the error message is just the raw git output, display it in
// fixed-width font
if (isRawGitError(e)) {
const formattedMessage = this.formatGitErrorMessage(e.message)
return <p className="monospace">{formattedMessage}</p>
}
return <p>{e.message}</p>
@ -148,6 +145,9 @@ export class AppError extends React.Component<IAppErrorProps, IAppErrorState> {
onSubmit={this.onDismissed}
onDismissed={this.onDismissed}
disabled={this.state.disabled}
className={
isRawGitError(this.state.error) ? 'raw-git-error' : undefined
}
>
<DialogContent onRef={this.onDialogContentRef}>
{this.renderErrorMessage(error)}
@ -187,10 +187,8 @@ export class AppError extends React.Component<IAppErrorProps, IAppErrorState> {
const e = getUnderlyingError(this.state.error)
if (isGitError(e)) {
if (e.message === e.result.stderr || e.message === e.result.stdout) {
this.dialogContent.scrollTop = this.dialogContent.scrollHeight
}
if (isRawGitError(e)) {
this.dialogContent.scrollTop = this.dialogContent.scrollHeight
}
}
@ -285,6 +283,14 @@ function isGitError(error: Error): error is GitError {
return error instanceof GitError
}
function isRawGitError(error: Error | null) {
if (!error) {
return false
}
const e = getUnderlyingError(error)
return e instanceof GitError && e.isRawMessage
}
function isCloneError(error: Error) {
if (!isErrorWithMetaData(error)) {
return false

View file

@ -193,6 +193,11 @@ export class AppMenuBarButton extends React.Component<
dropdownState={dropDownState}
onDropdownStateChanged={this.onDropdownStateChanged}
dropdownContentRenderer={this.dropDownContentRenderer}
// Disable the dropdown focus trap for menus. Items in the menus are not
// "tabbable", so the app crashes when this prop is set to true and the
// user opens a menu (on Windows).
// Besides, we use a custom "focus trap" for menus anyway.
enableFocusTrap={false}
showDisclosureArrow={false}
onMouseEnter={this.onMouseEnter}
onKeyDown={this.onKeyDown}

View file

@ -776,7 +776,10 @@ export class ChangesList extends React.Component<
selectedRows={this.state.selectedRows}
selectionMode="multi"
onSelectionChanged={this.props.onFileSelectionChanged}
invalidationProps={this.props.workingDirectory}
invalidationProps={{
workingDirectory: this.props.workingDirectory,
isCommitting: this.props.isCommitting,
}}
onRowClick={this.props.onRowClick}
onScroll={this.onScroll}
setScrollTop={this.props.changesListScrollTop}

View file

@ -4,6 +4,8 @@ import { assertNever } from '../../lib/fatal-error'
import { ToolbarButton, ToolbarButtonStyle } from './button'
import { rectEquals } from '../lib/rect'
import classNames from 'classnames'
import FocusTrap from 'focus-trap-react'
import { Options as FocusTrapOptions } from 'focus-trap'
export type DropdownState = 'open' | 'closed'
@ -73,6 +75,9 @@ export interface IToolbarDropdownProps {
/** The button's style. Defaults to `ToolbarButtonStyle.Standard`. */
readonly style?: ToolbarButtonStyle
/** Wether the dropdown will trap focus or not. Defaults to true. */
readonly enableFocusTrap?: boolean
/**
* Sets the styles for the dropdown's foldout. Useful for custom positioning
* and sizes.
@ -142,10 +147,20 @@ export class ToolbarDropdown extends React.Component<
IToolbarDropdownState
> {
private innerButton: ToolbarButton | null = null
private focusTrapOptions: FocusTrapOptions
public constructor(props: IToolbarDropdownProps) {
super(props)
this.state = { clientRect: null }
this.focusTrapOptions = {
allowOutsideClick: true,
// Explicitly disable deactivation from the FocusTrap, since in that case
// we would lose the "source" of the event (keyboard vs pointer).
clickOutsideDeactivates: false,
escapeDeactivates: false,
}
}
private get isOpen() {
@ -273,21 +288,26 @@ export class ToolbarDropdown extends React.Component<
// bar to instantly close before even receiving the onDropdownStateChanged
// event from us.
return (
<div id="foldout-container" style={this.getFoldoutContainerStyle()}>
<div
className="overlay"
tabIndex={-1}
onClick={this.handleOverlayClick}
/>
<div
className="foldout"
style={this.getFoldoutStyle()}
tabIndex={-1}
onKeyDown={this.onFoldoutKeyDown}
>
{this.props.dropdownContentRenderer()}
<FocusTrap
active={this.props.enableFocusTrap ?? true}
focusTrapOptions={this.focusTrapOptions}
>
<div id="foldout-container" style={this.getFoldoutContainerStyle()}>
<div
className="overlay"
tabIndex={-1}
onClick={this.handleOverlayClick}
/>
<div
className="foldout"
style={this.getFoldoutStyle()}
tabIndex={-1}
onKeyDown={this.onFoldoutKeyDown}
>
{this.props.dropdownContentRenderer()}
</div>
</div>
</div>
</FocusTrap>
)
}

View file

@ -321,6 +321,12 @@ dialog {
}
&#app-error {
// Use wider dialogs for raw git errors, to make sure we can display lines
// up to 80 characters long without wrapping them.
&.raw-git-error {
max-width: 700px;
}
.dialog-content {
p {
-webkit-user-select: text;

View file

@ -727,7 +727,7 @@ describe('git/commit', () => {
const status = await getStatusOrThrow(repository)
expect(
createMergeCommit(repository, status.workingDirectory.files)
).rejects.toThrow(/Commit failed/i)
).rejects.toThrow('There are no changes to commit.')
})
})
})

View file

@ -112,6 +112,7 @@ describe('git/core', () => {
gitErrorDescription: null,
stderr,
stdout: '',
combinedOutput: stderr,
}
}

View file

@ -166,7 +166,7 @@ describe('git/tag', () => {
await checkoutBranch(repository, account, branch!)
const commitSha = await createCommit(repository, 'a commit', files)
await createTag(repository, 'my-new-tag', commitSha!)
await createTag(repository, 'my-new-tag', commitSha)
expect(
await fetchTagsToPush(repository, account, originRemote, 'master')

View file

@ -10,6 +10,15 @@
"[Fixed] Avoid bright flash for users of the dark theme when launching the app maximized - #5631. Thanks @AndreiMaga!",
"[Fixed] VSCodium is now detected as an editor on Windows - #11252. Thanks @KallePM!"
],
"2.6.2-beta4": [
"[Improved] Show full output of Git hooks on errors - #6403",
"[Improved] Keep focus in dropdown when pressing tab multiple times - #11278",
"[Improved] Upgrade embedded Git LFS to 2.13.2 - #11394",
"[Improved] Add detection of Sublime Text 4 and 2 as editor on macOS. - #11263. Thanks @yurikoles!",
"[Fixed] Fork behavior changes are now reflected in the app immediately - #11327",
"[Fixed] Commit message remains in text box until user chooses to commit - #7251"
],
"2.6.2-beta3": ["[Removed] Release removed in favor of 2.6.2-beta4"],
"2.6.2-beta2": [
"[Fixed] Forked repository remotes are no longer removed when there's local branches tracking them - #11266",
"[Fixed] Avoid bright flash for users of the dark theme when launching the app maximized - #5631. Thanks @AndreiMaga!",

View file

@ -101,6 +101,7 @@
"sass-loader": "^10.0.3",
"semver": "^5.5.0",
"spectron": "^11.1.0",
"split2": "^3.2.2",
"stop-build": "^1.1.0",
"style-loader": "^0.21.0",
"to-camel-case": "^1.0.0",
@ -154,6 +155,7 @@
"@types/request": "^2.0.9",
"@types/semver": "^5.5.0",
"@types/source-map-support": "^0.5.2",
"@types/split2": "^2.1.6",
"@types/strip-ansi": "^3.0.0",
"@types/temp": "^0.8.29",
"@types/textarea-caret": "^3.0.0",

View file

@ -681,6 +681,13 @@
resolved "https://registry.yarnpkg.com/@types/source-map/-/source-map-0.5.1.tgz#7e74db5d06ab373a712356eebfaea2fad0ea2367"
integrity sha512-/GVAjL1Y8puvZab63n8tsuBiYwZt1bApMdx58/msQ9ID5T05ov+wm/ZV1DvYC/DKKEygpTJViqQvkh5Rhrl4CA==
"@types/split2@^2.1.6":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@types/split2/-/split2-2.1.6.tgz#b095c9e064853824b22c67993d99b066777402b1"
integrity sha512-ddaFSOMuy2Rp97l6q/LEteQygvTQJuEZ+SRhxFKR0uXGsdbFDqX/QF2xoGcOqLQ8XV91v01SnAv2vpgihNgW/Q==
dependencies:
"@types/node" "*"
"@types/stack-utils@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
@ -8746,7 +8753,7 @@ readable-stream@1.0:
isarray "0.0.1"
string_decoder "~0.10.x"
readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@ -9670,6 +9677,13 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies:
extend-shallow "^2.0.1"
split2@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f"
integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==
dependencies:
readable-stream "^3.0.0"
split@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"