diff --git a/.editorconfig b/.editorconfig index 541fc2d..1e7b83a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,3 +10,7 @@ charset = utf-8 end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true + +[*.json] +indent_style = space +tab_width = 2 diff --git a/.gitignore b/.gitignore index 25fbf5a..45c1ef8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -node_modules/ +.nyc_output coverage/ +node_modules/ diff --git a/README.md b/README.md index 46789a4..09a07cb 100644 --- a/README.md +++ b/README.md @@ -152,9 +152,16 @@ Examples: ``` ```js -// Clone 'https://github.com/cksource/foo.git' (via HTTPS) and check out to branch tag 'v1.2.3'. +// Clone 'https://github.com/cksource/foo.git' (via HTTPS) and check out to tag 'v1.2.3'. { - "foo": "https://github.com/cksource/foo.git#v1.2.3" + "foo": "https://github.com/cksource/foo.git@v1.2.3" +} +``` + +```js +// Clone 'cksource/foo' and check out to the latest available tag. +{ + "foo": "cksource/foo@latest" } ``` @@ -212,8 +219,9 @@ You can also use full HTTPS URLs to configure `dependencies` in your `mrgit.json When you call `mrgit sync` or `mrgit co`, mrgit will use the following algorithm to determine the branch to which each repository should be checked out: 1. If a branch is defined in `mrgit.json`, use it. A branch can be defined after `#` in a repository URL. For example: `"@cksource/foo": "cksource/foo#dev"`. -2. If the root repository (assuming, it is a repository) is on one of the "base branches", use that branch name. -3. Otherwise, use `master`. +2. If a tag is defined in `mrgit.json`, use it. A tag can be defined after `@` in a repository URL. Its either a specific tag name, such as `@v30.0.0`, or `@latest` tag that will look for the latest available tag. +3. If the root repository (assuming, it is a repository) is on one of the "base branches", use that branch name. +4. Otherwise, use `master` branch. You can define the base branches as follows: @@ -241,7 +249,7 @@ $ mrgit [command] --help ### sync -Updates dependencies. Switches repositories to correct branches (specified in `mrgit.json`) and pulls changes. +Updates dependencies. Switches repositories to correct branches or tags (specified in `mrgit.json`) and pulls changes. If any dependency is missing, the command will install this dependency as well. @@ -258,8 +266,7 @@ mrgit sync --recursive ### pull -Pulls changes in existing repositories. It does not change branches in the repositories and pull the changes even if -the repository contains uncommitted changes. +Pulls changes in existing repositories. It does not change branches in the repositories. It does not pull the changes if the repository contains uncommitted changes. It skips repositories that are in detached head mode (are checked out on a tag). Examples: @@ -269,7 +276,7 @@ mrgit pull ### push -Pushes changes in existing repositories. +Pushes changes in existing repositories. It skips repositories that are in detached head mode (are checked out on a tag). Examples: @@ -310,8 +317,7 @@ mrgit exec 'echo `pwd`' ### commit (alias: `ci`) -For every repository that contains changes which can be committed, makes a commit with these files. -You need to specify the message for the commit. +For every repository that contains changes which can be committed, makes a commit with these files. You need to specify the message for the commit. It skips repositories that are in detached head mode (are checked out on a tag). Example: @@ -324,10 +330,7 @@ mrgit commit --message 'Introduce PULL_REQUEST_TEMPLATE.md.' ### close -Requires a second argument which is a branch name that will be merged to current one. You can also specify the message -which will be added to the default git-merge message. - -Repositories which do not have specified branch will be ignored. +Requires a second argument which is a branch name that will be merged to current one. You can also specify the message which will be added to the default git-merge message. Repositories which do not have specified branch will be ignored. It skips repositories that are in detached head mode (are checked out on a tag). After merging, the merged branch will be removed from the remote and the local registry. diff --git a/index.js b/index.js index 72ee406..fa5d537 100755 --- a/index.js +++ b/index.js @@ -34,14 +34,14 @@ function handleCli() { }; const logo = ` - _ _ - (_) | - _ __ ___ _ __ __ _ _| |_ + _ _ + (_) | + _ __ ___ _ __ __ _ _| |_ | '_ \` _ \\| '__| / _\` | | __| - | | | | | | | _ | (_| | | |_ + | | | | | | | _ | (_| | | |_ |_| |_| |_|_|(_) \\__, |_|\\__| - __/ | - |___/ + __/ | + |___/ `; const { @@ -60,7 +60,7 @@ function handleCli() { ${ c( 'checkout' ) } Changes branches in repositories according to the configuration file. ${ c( 'close' ) } Merges specified branch with the current one and remove merged branch from the remote. ${ c( 'commit' ) } Commits all changes. A shorthand for "mrgit exec 'git commit -a'". - ${ c( 'diff' ) } Prints changes from packages where something has changed. + ${ c( 'diff' ) } Prints changes from packages where something has changed. ${ c( 'exec' ) } Executes shell command in each package. ${ c( 'fetch' ) } Fetches existing repositories. ${ c( 'pull' ) } Pulls changes in existing repositories. @@ -68,7 +68,7 @@ function handleCli() { ${ c( 'save' ) } Saves hashes of packages in mrgit.json. It allows to easily fix project to a specific state. ${ c( 'status' ) } Prints a table which contains useful information about the status of repositories. ${ c( 'sync' ) } Updates packages to the latest versions or install missing ones. - + ${ u( 'Options:' ) } ${ y( '--branch' ) } For "${ u( 'save' ) }" command: whether to save branch names. @@ -81,7 +81,7 @@ function handleCli() { Will ignore all packages which names start from "foo". ${ g( 'Default: null' ) } - + ${ y( '--message' ) } Message that will be used as an option for git command. Required for "${ u( 'commit' ) }" command but it is also used by "${ u( 'close' ) }" command (append the message to the default). @@ -140,7 +140,7 @@ function handleCli() { return; } - // Specified command is is available, displays the command's help. + // Specified command is available, displays the command's help. console.log( logo ); console.log( ` ${ u( 'Command:' ) } ${ c( commandInstance.name || commandName ) } ` ); console.log( commandInstance.helpMessage ); diff --git a/lib/commands/close.js b/lib/commands/close.js index a90d78b..f759f39 100644 --- a/lib/commands/close.js +++ b/lib/commands/close.js @@ -27,9 +27,9 @@ module.exports = { The merge commit will be made using following message: "${ i( 'Merge branch \'branch-name\'' ) }". After merging, specified branch will be removed from the remote and local registry. - + ${ u( 'Options:' ) } - ${ m( '--message' ) } (-m) An additional description for merge commit. It will be + ${ m( '--message' ) } (-m) An additional description for merge commit. It will be appended to the default message. E.g.: ${ g( '> mrgit merge develop -- -m "Some description about merged changes."' ) } `; @@ -51,7 +51,7 @@ module.exports = { const branch = data.arguments[ 0 ]; return execCommand.execute( getExecData( `git branch --list ${ branch }` ) ) - .then( execResponse => { + .then( async execResponse => { const branchExists = Boolean( execResponse.logs.info[ 0 ] ); if ( !branchExists ) { @@ -62,6 +62,17 @@ module.exports = { }; } + const commandResponse = await execCommand.execute( getExecData( 'git branch --show-current' ) ); + const detachedHead = !commandResponse.logs.info[ 0 ]; + + if ( detachedHead ) { + log.info( 'This repository is currently in detached head mode - skipping.' ); + + return { + logs: log.all() + }; + } + const mergeMessage = this._getMergeMessage( data.toolOptions, data.arguments ); const commitTitle = `Merge branch '${ branch }'`; diff --git a/lib/commands/commit.js b/lib/commands/commit.js index 5c5d760..ebb8ec7 100644 --- a/lib/commands/commit.js +++ b/lib/commands/commit.js @@ -24,14 +24,14 @@ module.exports = { return ` ${ u( 'Description:' ) } - Makes a commit in every repository that contains tracked files that have changed. + Makes a commit in every repository that contains tracked files that have changed. This command is a shorthand for: "${ i( 'mrgit exec \'git commit -a\'' ) }". - + ${ u( 'Options:' ) } ${ y( '--message' ) } (-m) Required. A message for the commit. It can be specified more then once, e.g.: ${ g( '> mrgit commit --message "Title of the commit." --message "Additional description."' ) } - ${ u( 'Git Options:' ) } + ${ u( 'Git Options:' ) } ${ m( '--no-verify' ) } (-n) Whether to skip pre-commit and commit-msg hooks. ${ g( '> mrgit commit -m "Title of the commit." -- -n' ) } `; @@ -62,6 +62,14 @@ module.exports = { .then( execResponse => { const status = gitStatusParser( execResponse.logs.info[ 0 ] ); + if ( status.detachedHead ) { + log.info( 'This repository is currently in detached head mode - skipping.' ); + + return { + logs: log.all() + }; + } + if ( !status.anythingToCommit ) { log.info( 'Nothing to commit.' ); diff --git a/lib/commands/exec.js b/lib/commands/exec.js index 04c6079..27d1674 100644 --- a/lib/commands/exec.js +++ b/lib/commands/exec.js @@ -21,7 +21,7 @@ module.exports = { return ` ${ u( 'Description:' ) } Requires a command that will be executed on all repositories. E.g. "${ g( 'mrgit exec pwd' ) }" will execute "${ i( 'pwd' ) }" - command in every repository. Commands that contain spaces must be wrapped in quotation marks, + command in every repository. Commands that contain spaces must be wrapped in quotation marks, e.g.: "${ g( 'mrgit exec "git remote"' ) }". `; }, diff --git a/lib/commands/pull.js b/lib/commands/pull.js index 85c8c67..eebda27 100644 --- a/lib/commands/pull.js +++ b/lib/commands/pull.js @@ -29,7 +29,7 @@ module.exports = { * @param {CommandData} data * @returns {Promise} */ - execute( data ) { + async execute( data ) { const execCommand = require( './exec' ); const destinationPath = path.join( data.toolOptions.packages, data.repository.directory ); @@ -39,6 +39,18 @@ module.exports = { return Promise.resolve( {} ); } + const commandResponse = await execCommand.execute( getExecData( 'git branch --show-current' ) ); + const currentlyOnBranch = Boolean( commandResponse.logs.info[ 0 ] ); + + if ( !currentlyOnBranch ) { + return Promise.resolve( { + logs: { + error: [], + info: [ 'This repository is currently in detached head mode - skipping.' ] + } + } ); + } + return execCommand.execute( getExecData( 'git pull' ) ); function getExecData( command ) { diff --git a/lib/commands/push.js b/lib/commands/push.js index 62d4cca..5d36037 100644 --- a/lib/commands/push.js +++ b/lib/commands/push.js @@ -25,9 +25,9 @@ module.exports = { For cloned repositories this command is a shorthand for: "${ i( 'mrgit exec \'git push\'' ) }". ${ u( 'Git Options:' ) } - All options accepted by "${ i( 'git push' ) }" are supported by mrgit. Everything specified after "--" is passed directly to the + All options accepted by "${ i( 'git push' ) }" are supported by mrgit. Everything specified after "--" is passed directly to the "${ i( 'git push' ) }" command. - + E.g.: "${ g( 'mrgit push -- --verbose --all' ) }" will execute "${ i( 'git push --verbose --all' ) }" `; }, @@ -36,7 +36,7 @@ module.exports = { * @param {CommandData} data * @returns {Promise} */ - execute( data ) { + async execute( data ) { const execCommand = require( './exec' ); const destinationPath = path.join( data.toolOptions.packages, data.repository.directory ); @@ -46,6 +46,18 @@ module.exports = { return Promise.resolve( {} ); } + const commandResponse = await execCommand.execute( getExecData( 'git branch --show-current' ) ); + const currentlyOnBranch = Boolean( commandResponse.logs.info[ 0 ] ); + + if ( !currentlyOnBranch ) { + return Promise.resolve( { + logs: { + error: [], + info: [ 'This repository is currently in detached head mode - skipping.' ] + } + } ); + } + const pushCommand = ( 'git push ' + data.arguments.join( ' ' ) ).trim(); return execCommand.execute( getExecData( pushCommand ) ); diff --git a/lib/commands/save.js b/lib/commands/save.js index 02cb1cf..04c3216 100644 --- a/lib/commands/save.js +++ b/lib/commands/save.js @@ -20,10 +20,10 @@ module.exports = { return ` ${ u( 'Description:' ) } - Saves hashes of commits or branches which repositories are checked out in "mrgit.json" file. + Saves hashes of commits or branches which repositories are checked out in "mrgit.json" file. ${ u( 'Options:' ) } - ${ y( '--hash' ) } Whether to save hashes (id of last commit) on current branch. + ${ y( '--hash' ) } Whether to save hashes (id of last commit) on current branch. ${ g( 'Default: true' ) } ${ y( '--branch' ) } (-b) Whether to save names of current branches instead of commit ids. ${ g( 'Default: false' ) } @@ -104,9 +104,13 @@ module.exports = { const cwd = require( '../utils/getcwd' )(); const mrgitJsonPath = path.join( cwd, 'mrgit.json' ); + const tagPattern = /@([^ ~^:?*\\]*?)$/; + updateJsonFile( mrgitJsonPath, json => { for ( const response of commandResponses.values() ) { - const repository = json.dependencies[ response.packageName ].split( '#' )[ 0 ]; + const repository = json.dependencies[ response.packageName ] + .replace( tagPattern, '' ) + .split( '#' )[ 0 ]; // If returned branch is equal to 'master', save only the repository path. if ( response.branch && response.data === 'master' ) { diff --git a/lib/commands/status.js b/lib/commands/status.js index b3be6ce..1b4cef4 100644 --- a/lib/commands/status.js +++ b/lib/commands/status.js @@ -42,22 +42,29 @@ module.exports = { const promises = [ execCommand.execute( getExecData( 'git rev-parse HEAD' ) ), - execCommand.execute( getExecData( 'git status --branch --porcelain' ) ) + execCommand.execute( getExecData( 'git status --branch --porcelain' ) ), + execCommand.execute( getExecData( 'git describe --abbrev=0 --tags' ) ), + execCommand.execute( getExecData( 'git log --tags --simplify-by-decoration --pretty="%S"' ) ) ]; return Promise.all( promises ) - .then( ( [ hashResponse, statusResponse ] ) => { + .then( ( [ hashResponse, currentBranchStatusResponse, currentTagStatusResponse, latestTagStatusResponse ] ) => { let packageName = data.packageName; + const currentTag = currentTagStatusResponse.logs.info[ 0 ]; + const latestTag = latestTagStatusResponse.logs.info[ 0 ].trim().split( '\n' ).shift(); + for ( const packagePrefix of data.toolOptions.packagesPrefix ) { packageName = packageName.replace( new RegExp( '^' + packagePrefix ), '' ); } const commandResponse = { packageName, - status: gitStatusParser( statusResponse.logs.info[ 0 ] ), + status: gitStatusParser( currentBranchStatusResponse.logs.info[ 0 ], currentTag ), commit: hashResponse.logs.info[ 0 ].slice( 0, 7 ), // Short version of the commit hash. - mrgitBranch: data.repository.branch + mrgitBranch: data.repository.branch, + mrgitTag: data.repository.tag, + latestTag }; return { response: commandResponse }; @@ -81,8 +88,11 @@ module.exports = { return; } + let shouldDisplayLatestHint = false; + let shouldDisplaySyncHint = false; + const table = new Table( { - head: [ 'Package', 'Branch', 'Commit', 'Status' ], + head: [ 'Package', 'Branch/Tag', 'Commit', 'Status' ], style: { compact: true } @@ -106,69 +116,106 @@ module.exports = { } console.log( table.toString() ); - console.log( getLegend() ); + displayLegend(); + displayHints( shouldDisplayLatestHint, shouldDisplaySyncHint ); - function createSingleRow( { packageName, status, mrgitBranch, commit } ) { - const wholeRow = []; + function createSingleRow( data ) { + const { packageName, status, commit, mrgitBranch, mrgitTag, latestTag } = data; const statusColumn = []; - const wholeRowColor = status.branch !== 'master' ? 'magenta' : null; - let branch = status.branch; + const shouldUseTag = mrgitTag !== undefined; + const shouldUseLatestTag = mrgitTag === 'latest'; + let branchOrTag = !status.detachedHead ? status.branch : status.tag; // Unmerged files are also modified so we should print the number of them out. const modifiedFiles = [ status.modified, status.unmerged ] .reduce( ( sum, item ) => sum + item.length, 0 ); - if ( mrgitBranch !== status.branch ) { - branch = `${ color( 'cyan', '!' ) } ${ branch }`; + if ( shouldUseTag && shouldUseLatestTag && status.detachedHead && status.tag === latestTag ) { + branchOrTag = `${ chalk.green( 'L' ) } ${ branchOrTag }`; + shouldDisplayLatestHint = true; + } + + if ( shouldUseTag && shouldUseLatestTag && ( !status.detachedHead || status.tag !== latestTag ) ) { + branchOrTag = `${ chalk.cyan( '!' ) } ${ branchOrTag }`; + shouldDisplaySyncHint = true; + } + + if ( shouldUseTag && !shouldUseLatestTag && status.tag !== mrgitTag ) { + branchOrTag = `${ chalk.cyan( '!' ) } ${ branchOrTag }`; + shouldDisplaySyncHint = true; + } + + if ( !shouldUseTag && status.branch !== mrgitBranch ) { + branchOrTag = `${ chalk.cyan( '!' ) } ${ branchOrTag }`; + shouldDisplaySyncHint = true; } if ( status.ahead ) { - branch += color( 'yellow', ` ↑${ status.ahead }` ); + branchOrTag += chalk.yellow( ` ↑${ status.ahead }` ); } if ( status.behind ) { - branch += color( 'yellow', ` ↓${ status.behind }` ); + branchOrTag += chalk.yellow( ` ↓${ status.behind }` ); } if ( status.staged.length ) { - statusColumn.push( color( 'green', `+${ status.staged.length }` ) ); + statusColumn.push( chalk.green( `+${ status.staged.length }` ) ); } if ( modifiedFiles ) { - statusColumn.push( color( 'red', `M${ modifiedFiles }` ) ); + statusColumn.push( chalk.red( `M${ modifiedFiles }` ) ); } if ( status.untracked.length ) { - statusColumn.push( color( 'blue', `?${ status.untracked.length }` ) ); + statusColumn.push( chalk.blue( `?${ status.untracked.length }` ) ); } - wholeRow.push( color( wholeRowColor, packageName ) ); - wholeRow.push( color( wholeRowColor, branch ) ); - wholeRow.push( color( wholeRowColor, commit ) ); - wholeRow.push( statusColumn.join( ' ' ) ); - - return wholeRow; + return [ + packageName, + branchOrTag, + commit, + statusColumn.join( ' ' ) + ]; } - function getLegend() { + function displayLegend() { const legend = [ - `${ color( 'yellow', '↑' ) } branch is ahead ${ color( 'yellow', '↓' ) } or behind`, - `${ color( 'green', '+' ) } staged files`, - `${ color( 'red', 'M' ) } modified files`, - `${ color( 'blue', '?' ) } untracked files`, - `\n${ color( 'cyan', '!' ) } not on a branch specified in "mrgit.json"` + `${ chalk.yellow( '↑' ) } branch is ahead ${ chalk.yellow( '↓' ) } or behind`, + `${ chalk.green( '+' ) } staged files`, + `${ chalk.red( 'M' ) } modified files`, + `${ chalk.blue( '?' ) } untracked files`, + `\n${ chalk.green( 'L' ) } latest tag`, + `${ chalk.cyan( '!' ) } not on a branch or a tag specified in "mrgit.json"` ]; - return `${ chalk.bold( 'Legend:' ) }\n${ legend.join( ', ' ) }.`; + console.log( `${ chalk.bold( 'Legend:' ) }\n${ legend.join( ', ' ) }.` ); } - function color( colorName, value ) { - if ( !colorName ) { - return value; + function displayHints( shouldDisplayLatestHint, shouldDisplaySyncHint ) { + const hints = []; + + if ( shouldDisplayLatestHint ) { + hints.push( [ + chalk.green( 'L' ), + 'This is the latest local tag. To ensure having latest remote tag, execute', + chalk.blue( 'mrgit sync' ) + ].join( ' ' ) + '.' ); + } + + if ( shouldDisplaySyncHint ) { + hints.push( [ + chalk.cyan( '!' ), + 'In order to bring your repositories up to date, execute', + chalk.blue( 'mrgit sync' ) + ].join( ' ' ) + '.' ); + } + + if ( !hints.length ) { + return; } - return chalk[ colorName ]( value ); + console.log( `\n${ chalk.bold( 'Hints:' ) }\n${ hints.join( '\n' ) }` ); } } }; diff --git a/lib/commands/sync.js b/lib/commands/sync.js index b6c31b0..82a80e3 100644 --- a/lib/commands/sync.js +++ b/lib/commands/sync.js @@ -20,19 +20,19 @@ module.exports = { return ` ${ u( 'Description:' ) } - Updates all packages. For packages that contain uncommitted changes, the update process is aborted. + Updates all packages. For packages that contain uncommitted changes, the update process is aborted. If some package is missed, it will be installed automatically. - + The update process executes following commands: * Checks whether repository can be updated. If the repository contains uncommitted changes, the process is aborted. * Fetches changes from the remote. * Checks out on the branch or particular commit that is specified in "mrgit.json" file. - * Pulls the changes if the repository is not detached at some commit. - + * Pulls the changes if the repository is not detached at some commit. + ${ u( 'Options:' ) } - ${ m( '--recursive' ) } (-r) Whether to install dependencies recursively. Only packages matching these + ${ m( '--recursive' ) } (-r) Whether to install dependencies recursively. Only packages matching these patterns will be cloned recursively. ${ g( 'Default: false' ) } `; @@ -56,12 +56,13 @@ module.exports = { path: destinationPath, name: data.packageName, url: data.repository.url, - branch: data.repository.branch + branch: data.repository.branch, + tag: data.repository.tag }, data.toolOptions, { log } ); } return execCommand.execute( getExecData( 'git status -s' ) ) - .then( response => { + .then( async response => { const stdout = response.logs.info.join( '\n' ).trim(); if ( stdout ) { @@ -73,8 +74,23 @@ module.exports = { .then( response => { log.concat( response.logs ); } ) - .then( () => { - return execCommand.execute( getExecData( `git checkout ${ data.repository.branch }` ) ); + .then( async () => { + let checkoutValue; + + if ( !data.repository.tag ) { + checkoutValue = data.repository.branch; + } else if ( data.repository.tag === 'latest' ) { + const commandOutput = await execCommand.execute( + getExecData( 'git log --tags --simplify-by-decoration --pretty="%S"' ) + ); + const latestTag = commandOutput.logs.info[ 0 ].trim().split( '\n' ).shift(); + + checkoutValue = 'tags/' + latestTag.trim(); + } else { + checkoutValue = 'tags/' + data.repository.tag; + } + + return execCommand.execute( getExecData( `git checkout "${ checkoutValue }"` ) ); } ) .then( response => { log.concat( response.logs ); @@ -93,7 +109,7 @@ module.exports = { return { logs: log.all() }; } - return execCommand.execute( getExecData( `git pull origin ${ data.repository.branch }` ) ) + return execCommand.execute( getExecData( `git pull origin "${ data.repository.branch }"` ) ) .then( response => { log.concat( response.logs ); @@ -188,16 +204,28 @@ module.exports = { _clonePackage( packageDetails, toolOptions, options ) { const log = options.log; - const command = [ - `git clone --progress "${ packageDetails.url }" "${ packageDetails.path }"`, - `cd "${ packageDetails.path }"`, - `git checkout --quiet ${ packageDetails.branch }` - ].join( ' && ' ); - - return shell( command ) - .then( output => { + return shell( `git clone --progress "${ packageDetails.url }" "${ packageDetails.path }"` ) + .then( async output => { log.info( output ); + let checkoutValue; + + if ( !packageDetails.tag ) { + checkoutValue = packageDetails.branch; + } else if ( packageDetails.tag === 'latest' ) { + const commandOutput = await shell( + `cd "${ packageDetails.path }" && git log --tags --simplify-by-decoration --pretty="%S"` + ); + const latestTag = commandOutput.trim().split( '\n' ).shift(); + + checkoutValue = 'tags/' + latestTag.trim(); + } else { + checkoutValue = 'tags/' + packageDetails.tag; + } + + return shell( `cd "${ packageDetails.path }" && git checkout --quiet "${ checkoutValue }"` ); + } ) + .then( () => { const commandOutput = { logs: log.all() }; diff --git a/lib/utils/gitstatusparser.js b/lib/utils/gitstatusparser.js index 032c12f..6a395a9 100644 --- a/lib/utils/gitstatusparser.js +++ b/lib/utils/gitstatusparser.js @@ -25,10 +25,11 @@ const ADDED_STAGED_SYMBOL = 'A '; const UNTRACKED_SYMBOL = '??'; /** - * @param {String} response An output returned by `git status -sb` command. + * @param {String} branchStatus An output returned by `git status --branch --porcelain` command. + * @param {String} currentTag An output returned by `git describe --abbrev=0 --tags` command. * @returns {Object} data * @returns {Boolean} data.anythingToCommit Returns true if any changed file could be committed using command `git commit -a`. - * @returns {String} data.branch Current branch. + * @returns {String} data.branchOrTag Current branch or tag. * @returns {Number|null} data.behind Number of commits that branch is behind the remote upstream. * @returns {Number|null} data.ahead Number of commits that branch is ahead the remote upstream. * @returns {Array.} data.added List of files created files (untracked files are tracked now). @@ -39,11 +40,13 @@ const UNTRACKED_SYMBOL = '??'; * @returns {Array.} data.untracked List of untracked files which won't be committed using command `git commit -a`. * @returns {Array.} data.staged List of files that their changes are ready to commit. */ -module.exports = function gitStatusParser( response ) { - const responseAsArray = response.split( '\n' ); +module.exports = function gitStatusParser( branchStatus, currentTag ) { + const responseAsArray = branchStatus.split( '\n' ); const branchData = responseAsArray.shift(); const branch = branchData.split( '...' )[ 0 ].match( /## (.*)$/ )[ 1 ]; + const tag = currentTag; + const detachedHead = branch === 'HEAD (no branch)'; const added = filterFiles( [ ADDED_STAGED_SYMBOL ] ); const modified = filterFiles( [ MODIFIED_NOT_STAGED_SYMBOL, MODIFIED_STAGED_AND_NOT_STAGED_SYMBOL, DELETE_NOT_STAGED_SYMBOL ] ); const deleted = filterFiles( [ DELETE_STAGED_SYMBOL, DELETE_NOT_STAGED_SYMBOL ] ); @@ -75,6 +78,8 @@ module.exports = function gitStatusParser( response ) { }, branch, + tag, + detachedHead, behind, ahead, added, diff --git a/lib/utils/log.js b/lib/utils/log.js index 717e4cb..7027894 100644 --- a/lib/utils/log.js +++ b/lib/utils/log.js @@ -70,7 +70,7 @@ module.exports = function log() { /** * @typedef {Object} Logs * - * @propery {Array.} error An error messages. + * @property {Array.} error An error messages. * - * @propery {Array.} info An information messages. + * @property {Array.} info An information messages. */ diff --git a/lib/utils/parserepositoryurl.js b/lib/utils/parserepositoryurl.js index 4b3b1a3..be416f4 100644 --- a/lib/utils/parserepositoryurl.js +++ b/lib/utils/parserepositoryurl.js @@ -7,6 +7,10 @@ const url = require( 'url' ); +// Limitations on git tag names: +// https://git-scm.com/docs/git-check-ref-format#_description +const tagPattern = /@([^ ~^:?*\\]*?)$/; + /** * Parses repository URL taken from `mrgit.json`'s dependencies and returns * it as an object containing repository URL and branch name. @@ -17,13 +21,22 @@ const url = require( 'url' ); * Used if `repositoryUrl` defines only `'/'`. * @param {String} [options.defaultBranch='master'] The default branch name to be used if the * repository URL doesn't specify it. - * @param {Array.>} [options.baseBranches=[]] Name of branches that are allowed to check out + * @param {Array.} [options.baseBranches=[]] Name of branches that are allowed to check out * based on the value specified as `options.cwdPackageBranch`. * @param {String} [options.cwdPackageBranch] A name of a branch that the main repository is checked out. * @returns {Repository} */ module.exports = function parseRepositoryUrl( repositoryUrl, options = {} ) { + let tag = undefined; + + if ( tagPattern.test( repositoryUrl ) ) { + tag = repositoryUrl.match( tagPattern )[ 1 ]; + + repositoryUrl = repositoryUrl.replace( tagPattern, '' ); + } + const parsedUrl = url.parse( repositoryUrl ); + const branch = getBranch( parsedUrl, { defaultBranch: options.defaultBranch, baseBranches: options.baseBranches || [], @@ -45,7 +58,8 @@ module.exports = function parseRepositoryUrl( repositoryUrl, options = {} ) { return { url: repoUrl, branch, - directory: repoUrl.replace( /\.git$/, '' ).match( /[:/]([^/]+)\/?$/ )[ 1 ] + directory: repoUrl.replace( /\.git$/, '' ).match( /[:/]([^/]+)\/?$/ )[ 1 ], + tag }; }; diff --git a/package.json b/package.json index aad5e29..3a09ce5 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,10 @@ "eslint": "^7.7.0", "eslint-config-ckeditor5": "^4.0.0", "husky": "^8.0.2", - "istanbul": "^0.4.5", "lint-staged": "^10.2.11", "mocha": "^9.0.0", "mockery": "^2.1.0", + "nyc": "^15.1.0", "sinon": "^9.0.3" }, "repository": { @@ -52,8 +52,8 @@ "scripts": { "postinstall": "node ./scripts/postinstall.js", "test": "mocha tests --recursive", - "coverage": "istanbul cover _mocha tests -- --recursive", - "lint": "eslint --quiet '**/*.js'", + "coverage": "nyc --reporter=lcov --reporter=text-summary yarn run test", + "lint": "eslint --quiet \"**/*.js\"", "changelog": "node ./scripts/changelog.js", "release:bump-version": "node ./scripts/bump-version.js", "release:publish": "node ./scripts/publish.js" diff --git a/tests/commands/close.js b/tests/commands/close.js index bd6bb5c..0c4b9d3 100644 --- a/tests/commands/close.js +++ b/tests/commands/close.js @@ -104,7 +104,7 @@ describe( 'commands/close', () => { stubs.execCommand.execute.onCall( 1 ).resolves( { logs: { info: [ - 'Merge made by the \'recursive\' strategy.' + 'develop' ], error: [] } @@ -113,13 +113,22 @@ describe( 'commands/close', () => { stubs.execCommand.execute.onCall( 2 ).resolves( { logs: { info: [ - 'Deleted branch develop (was e6bda2e9).' + 'Merge made by the \'recursive\' strategy.' ], error: [] } } ); stubs.execCommand.execute.onCall( 3 ).resolves( { + logs: { + info: [ + 'Deleted branch develop (was e6bda2e9).' + ], + error: [] + } + } ); + + stubs.execCommand.execute.onCall( 4 ).resolves( { logs: { info: [ 'To github.com:foo/bar.git\n' + @@ -131,7 +140,7 @@ describe( 'commands/close', () => { return closeCommand.execute( commandData ) .then( commandResponse => { - expect( stubs.execCommand.execute.callCount ).to.equal( 4 ); + expect( stubs.execCommand.execute.callCount ).to.equal( 5 ); expect( stubs.execCommand.execute.getCall( 0 ).args[ 0 ] ).to.deep.equal( { repository: { @@ -145,7 +154,7 @@ describe( 'commands/close', () => { repository: { branch: 'master' }, - arguments: [ 'git merge develop --no-ff -m "Merge branch \'develop\'"' ], + arguments: [ 'git branch --show-current' ], toolOptions } ); @@ -153,11 +162,19 @@ describe( 'commands/close', () => { repository: { branch: 'master' }, - arguments: [ 'git branch -d develop' ], + arguments: [ 'git merge develop --no-ff -m "Merge branch \'develop\'"' ], toolOptions } ); expect( stubs.execCommand.execute.getCall( 3 ).args[ 0 ] ).to.deep.equal( { + repository: { + branch: 'master' + }, + arguments: [ 'git branch -d develop' ], + toolOptions + } ); + + expect( stubs.execCommand.execute.getCall( 4 ).args[ 0 ] ).to.deep.equal( { repository: { branch: 'master' }, @@ -198,7 +215,7 @@ describe( 'commands/close', () => { stubs.execCommand.execute.onCall( 1 ).resolves( { logs: { info: [ - 'Merge made by the \'recursive\' strategy.' + 'develop' ], error: [] } @@ -207,13 +224,22 @@ describe( 'commands/close', () => { stubs.execCommand.execute.onCall( 2 ).resolves( { logs: { info: [ - 'Deleted branch develop (was e6bda2e9).' + 'Merge made by the \'recursive\' strategy.' ], error: [] } } ); stubs.execCommand.execute.onCall( 3 ).resolves( { + logs: { + info: [ + 'Deleted branch develop (was e6bda2e9).' + ], + error: [] + } + } ); + + stubs.execCommand.execute.onCall( 4 ).resolves( { logs: { info: [ 'To github.com:foo/bar.git\n' + @@ -225,7 +251,7 @@ describe( 'commands/close', () => { return closeCommand.execute( commandData ) .then( commandResponse => { - expect( stubs.execCommand.execute.callCount ).to.equal( 4 ); + expect( stubs.execCommand.execute.callCount ).to.equal( 5 ); expect( stubs.execCommand.execute.getCall( 0 ).args[ 0 ] ).to.deep.equal( { repository: { @@ -239,7 +265,7 @@ describe( 'commands/close', () => { repository: { branch: 'master' }, - arguments: [ 'git merge develop --no-ff -m "Merge branch \'develop\'" -m "Test."' ], + arguments: [ 'git branch --show-current' ], toolOptions } ); @@ -247,11 +273,19 @@ describe( 'commands/close', () => { repository: { branch: 'master' }, - arguments: [ 'git branch -d develop' ], + arguments: [ 'git merge develop --no-ff -m "Merge branch \'develop\'" -m "Test."' ], toolOptions } ); expect( stubs.execCommand.execute.getCall( 3 ).args[ 0 ] ).to.deep.equal( { + repository: { + branch: 'master' + }, + arguments: [ 'git branch -d develop' ], + toolOptions + } ); + + expect( stubs.execCommand.execute.getCall( 4 ).args[ 0 ] ).to.deep.equal( { repository: { branch: 'master' }, @@ -292,7 +326,7 @@ describe( 'commands/close', () => { stubs.execCommand.execute.onCall( 1 ).resolves( { logs: { info: [ - 'Merge made by the \'recursive\' strategy.' + 'develop' ], error: [] } @@ -301,13 +335,22 @@ describe( 'commands/close', () => { stubs.execCommand.execute.onCall( 2 ).resolves( { logs: { info: [ - 'Deleted branch develop (was e6bda2e9).' + 'Merge made by the \'recursive\' strategy.' ], error: [] } } ); stubs.execCommand.execute.onCall( 3 ).resolves( { + logs: { + info: [ + 'Deleted branch develop (was e6bda2e9).' + ], + error: [] + } + } ); + + stubs.execCommand.execute.onCall( 4 ).resolves( { logs: { info: [ 'To github.com:foo/bar.git\n' + @@ -319,7 +362,7 @@ describe( 'commands/close', () => { return closeCommand.execute( commandData ) .then( commandResponse => { - expect( stubs.execCommand.execute.callCount ).to.equal( 4 ); + expect( stubs.execCommand.execute.callCount ).to.equal( 5 ); expect( stubs.execCommand.execute.getCall( 0 ).args[ 0 ] ).to.deep.equal( { repository: { @@ -333,7 +376,7 @@ describe( 'commands/close', () => { repository: { branch: 'master' }, - arguments: [ 'git merge develop --no-ff -m "Merge branch \'develop\'" -m "Test."' ], + arguments: [ 'git branch --show-current' ], toolOptions } ); @@ -341,11 +384,19 @@ describe( 'commands/close', () => { repository: { branch: 'master' }, - arguments: [ 'git branch -d develop' ], + arguments: [ 'git merge develop --no-ff -m "Merge branch \'develop\'" -m "Test."' ], toolOptions } ); expect( stubs.execCommand.execute.getCall( 3 ).args[ 0 ] ).to.deep.equal( { + repository: { + branch: 'master' + }, + arguments: [ 'git branch -d develop' ], + toolOptions + } ); + + expect( stubs.execCommand.execute.getCall( 4 ).args[ 0 ] ).to.deep.equal( { repository: { branch: 'master' }, @@ -399,5 +450,52 @@ describe( 'commands/close', () => { ] ); } ); } ); + + it( 'does not merge branch if in detached head mode', () => { + commandData.arguments.push( 'develop' ); + + stubs.execCommand.execute.onCall( 0 ).resolves( { + logs: { + info: [ + '* develop' + ], + error: [] + } + } ); + + stubs.execCommand.execute.onCall( 1 ).resolves( { + logs: { + info: [ + '' + ], + error: [] + } + } ); + + return closeCommand.execute( commandData ) + .then( commandResponse => { + expect( stubs.execCommand.execute.callCount ).to.equal( 2 ); + + expect( stubs.execCommand.execute.getCall( 0 ).args[ 0 ] ).to.deep.equal( { + repository: { + branch: 'master' + }, + arguments: [ 'git branch --list develop' ], + toolOptions + } ); + + expect( stubs.execCommand.execute.getCall( 1 ).args[ 0 ] ).to.deep.equal( { + repository: { + branch: 'master' + }, + arguments: [ 'git branch --show-current' ], + toolOptions + } ); + + expect( commandResponse.logs.info ).to.deep.equal( [ + 'This repository is currently in detached head mode - skipping.' + ] ); + } ); + } ); } ); } ); diff --git a/tests/commands/commit.js b/tests/commands/commit.js index 1170ef6..9d6ea59 100644 --- a/tests/commands/commit.js +++ b/tests/commands/commit.js @@ -380,5 +380,44 @@ describe( 'commands/commit', () => { ] ); } ); } ); + + it( 'does not commit if repository is in detached head mode', () => { + toolOptions.message = 'Test.'; + + stubs.execCommand.execute.onFirstCall().resolves( { + logs: { + info: [ + 'Response returned by "git status" command.' + ] + } + } ); + + stubs.execCommand.execute.onSecondCall().resolves( { + logs: { + info: [ + '[master a89f9ee] Test.' + ] + } + } ); + + stubs.gitStatusParser.returns( { anythingToCommit: true, detachedHead: true } ); + + return commitCommand.execute( commandData ) + .then( commandResponse => { + expect( stubs.execCommand.execute.callCount ).to.equal( 1 ); + + expect( stubs.execCommand.execute.firstCall.args[ 0 ] ).to.deep.equal( { + repository: { + branch: 'master' + }, + arguments: [ 'git status --branch --porcelain' ], + toolOptions + } ); + + expect( commandResponse.logs.info ).to.deep.equal( [ + 'This repository is currently in detached head mode - skipping.' + ] ); + } ); + } ); } ); } ); diff --git a/tests/commands/pull.js b/tests/commands/pull.js index b2eac3f..33e50c9 100644 --- a/tests/commands/pull.js +++ b/tests/commands/pull.js @@ -81,19 +81,43 @@ describe( 'commands/pull', () => { } ); } ); + it( 'skips a package if its in detached head mode', () => { + stubs.fs.existsSync.returns( true ); + + const exec = stubs.execCommand.execute; + + exec.onCall( 0 ).returns( Promise.resolve( { + logs: getCommandLogs( '' ) + } ) ); + + return pullCommand.execute( commandData ) + .then( response => { + expect( exec.callCount ).to.equal( 1 ); + expect( exec.getCall( 0 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git branch --show-current' ); + + expect( response.logs.info ).to.deep.equal( [ + 'This repository is currently in detached head mode - skipping.' + ] ); + } ); + } ); + it( 'resolves promise after pulling the changes', () => { stubs.fs.existsSync.returns( true ); const exec = stubs.execCommand.execute; - exec.returns( Promise.resolve( { + exec.onCall( 0 ).returns( Promise.resolve( { + logs: getCommandLogs( 'master' ) + } ) ); + exec.onCall( 1 ).returns( Promise.resolve( { logs: getCommandLogs( 'Already up-to-date.' ) } ) ); return pullCommand.execute( commandData ) .then( response => { - expect( exec.callCount ).to.equal( 1 ); - expect( exec.firstCall.args[ 0 ].arguments[ 0 ] ).to.equal( 'git pull' ); + expect( exec.callCount ).to.equal( 2 ); + expect( exec.getCall( 0 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git branch --show-current' ); + expect( exec.getCall( 1 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git pull' ); expect( response.logs.info ).to.deep.equal( [ 'Already up-to-date.' diff --git a/tests/commands/push.js b/tests/commands/push.js index f380383..1db95f7 100644 --- a/tests/commands/push.js +++ b/tests/commands/push.js @@ -77,19 +77,43 @@ describe( 'commands/push', () => { } ); } ); + it( 'skips a package if its in detached head mode', () => { + stubs.fs.existsSync.returns( true ); + + const exec = stubs.execCommand.execute; + + exec.onCall( 0 ).returns( Promise.resolve( { + logs: getCommandLogs( '' ) + } ) ); + + return pushCommand.execute( commandData ) + .then( response => { + expect( exec.callCount ).to.equal( 1 ); + expect( exec.getCall( 0 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git branch --show-current' ); + + expect( response.logs.info ).to.deep.equal( [ + 'This repository is currently in detached head mode - skipping.' + ] ); + } ); + } ); + it( 'resolves promise after pushing the changes', () => { stubs.fs.existsSync.returns( true ); const exec = stubs.execCommand.execute; - exec.returns( Promise.resolve( { + exec.onCall( 0 ).returns( Promise.resolve( { + logs: getCommandLogs( 'master' ) + } ) ); + exec.onCall( 1 ).returns( Promise.resolve( { logs: getCommandLogs( 'Everything up-to-date' ) } ) ); return pushCommand.execute( commandData ) .then( response => { - expect( exec.callCount ).to.equal( 1 ); - expect( exec.firstCall.args[ 0 ].arguments[ 0 ] ).to.equal( 'git push' ); + expect( exec.callCount ).to.equal( 2 ); + expect( exec.getCall( 0 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git branch --show-current' ); + expect( exec.getCall( 1 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git push' ); expect( response.logs.info ).to.deep.equal( [ 'Everything up-to-date' @@ -110,8 +134,9 @@ describe( 'commands/push', () => { return pushCommand.execute( commandData ) .then( response => { - expect( exec.callCount ).to.equal( 1 ); - expect( exec.firstCall.args[ 0 ].arguments[ 0 ] ).to.equal( 'git push --verbose --all' ); + expect( exec.callCount ).to.equal( 2 ); + expect( exec.getCall( 0 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git branch --show-current' ); + expect( exec.getCall( 1 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git push --verbose --all' ); expect( response.logs.info ).to.deep.equal( [ 'Everything up-to-date' diff --git a/tests/commands/save.js b/tests/commands/save.js index e0d6d2d..f329915 100644 --- a/tests/commands/save.js +++ b/tests/commands/save.js @@ -7,7 +7,6 @@ 'use strict'; -const path = require( 'upath' ); const sinon = require( 'sinon' ); const mockery = require( 'mockery' ); const expect = require( 'chai' ).expect; @@ -15,6 +14,8 @@ const expect = require( 'chai' ).expect; describe( 'commands/save', () => { let saveCommand, stubs, commandData, toolOptions, mrgitJsonPath, updateFunction; + const normalizedDirname = __dirname.replace( /\\/g, '/' ); + beforeEach( () => { mockery.enable( { useCleanCache: true, @@ -27,7 +28,7 @@ describe( 'commands/save', () => { execute: sinon.stub() }, path: { - join: sinon.stub( path, 'join' ).callsFake( ( ...chunks ) => chunks.join( '/' ) ) + join: sinon.stub().callsFake( ( ...chunks ) => chunks.join( '/' ) ) }, gitStatusParser: sinon.stub() }; @@ -205,7 +206,7 @@ describe( 'commands/save', () => { } }; - expect( mrgitJsonPath ).to.equal( __dirname + '/mrgit.json' ); + expect( mrgitJsonPath ).to.equal( normalizedDirname + '/mrgit.json' ); expect( updateFunction ).to.be.a( 'function' ); json = updateFunction( json ); @@ -247,7 +248,7 @@ describe( 'commands/save', () => { } }; - expect( mrgitJsonPath ).to.equal( __dirname + '/mrgit.json' ); + expect( mrgitJsonPath ).to.equal( normalizedDirname + '/mrgit.json' ); expect( updateFunction ).to.be.a( 'function' ); json = updateFunction( json ); @@ -280,7 +281,7 @@ describe( 'commands/save', () => { } }; - expect( mrgitJsonPath ).to.equal( __dirname + '/mrgit.json' ); + expect( mrgitJsonPath ).to.equal( normalizedDirname + '/mrgit.json' ); expect( updateFunction ).to.be.a( 'function' ); json = updateFunction( json ); @@ -289,5 +290,45 @@ describe( 'commands/save', () => { 'test-package': 'organization/test-package' } ); } ); + + it( 'overwrites tags defined in "mrgit.json"', () => { + const processedPackages = new Set(); + const commandResponses = new Set(); + + processedPackages.add( 'test-package' ); + processedPackages.add( 'package-test' ); + + commandResponses.add( { + packageName: 'test-package', + data: 'develop', + hash: false, + branch: true + } ); + commandResponses.add( { + packageName: 'package-test', + data: 'develop', + hash: false, + branch: true + } ); + + saveCommand.afterExecute( processedPackages, commandResponses ); + + let json = { + dependencies: { + 'test-package': 'organization/test-package@v30.0.0', + 'package-test': 'organization/package-test@latest' + } + }; + + expect( mrgitJsonPath ).to.equal( normalizedDirname + '/mrgit.json' ); + expect( updateFunction ).to.be.a( 'function' ); + + json = updateFunction( json ); + + expect( json.dependencies ).to.deep.equal( { + 'test-package': 'organization/test-package#develop', + 'package-test': 'organization/package-test#develop' + } ); + } ); } ); } ); diff --git a/tests/commands/status.js b/tests/commands/status.js index b650ff3..5f1a804 100644 --- a/tests/commands/status.js +++ b/tests/commands/status.js @@ -135,45 +135,50 @@ describe( 'commands/status', () => { } ); it( 'returns a response with status of the repository', () => { - stubs.execCommand.execute.onFirstCall().resolves( { - logs: { - info: [ '6bfd379a56a32c9f8b6e58bf08e39c124cdbae10' ] - } + stubs.execCommand.execute.onCall( 0 ).resolves( { + logs: { info: [ '6bfd379a56a32c9f8b6e58bf08e39c124cdbae10' ] } } ); - stubs.execCommand.execute.onSecondCall().resolves( { - logs: { - info: [ 'Response returned by "git status" command.' ] - } + stubs.execCommand.execute.onCall( 1 ).resolves( { + logs: { info: [ 'Response returned by "git status" command.' ] } + } ); + stubs.execCommand.execute.onCall( 2 ).resolves( { + logs: { info: [ 'Response returned by "git describe" command.' ] } + } ); + stubs.execCommand.execute.onCall( 3 ).resolves( { + logs: { info: [ '\nv35.3.2\nv35.3.1\nv35.3.0\nv35.2.1\nv35.2.0' ] } } ); stubs.gitStatusParser.returns( { response: 'Parsed response.' } ); return statusCommand.execute( commandData ) .then( statusResponse => { - expect( stubs.execCommand.execute.calledTwice ).to.equal( true ); - expect( stubs.execCommand.execute.firstCall.args[ 0 ] ).to.deep.equal( + expect( stubs.execCommand.execute.callCount ).to.equal( 4 ); + expect( stubs.execCommand.execute.getCall( 0 ).args[ 0 ] ).to.deep.equal( getCommandArguments( 'git rev-parse HEAD' ) ); - expect( stubs.execCommand.execute.secondCall.args[ 0 ] ).to.deep.equal( + expect( stubs.execCommand.execute.getCall( 1 ).args[ 0 ] ).to.deep.equal( getCommandArguments( 'git status --branch --porcelain' ) ); + expect( stubs.execCommand.execute.getCall( 2 ).args[ 0 ] ).to.deep.equal( + getCommandArguments( 'git describe --abbrev=0 --tags' ) + ); + expect( stubs.execCommand.execute.getCall( 3 ).args[ 0 ] ).to.deep.equal( + getCommandArguments( 'git log --tags --simplify-by-decoration --pretty="%S"' ) + ); expect( stubs.gitStatusParser.calledOnce ).to.equal( true ); expect( stubs.gitStatusParser.firstCall.args[ 0 ] ).to.equal( 'Response returned by "git status" command.' ); + expect( stubs.gitStatusParser.firstCall.args[ 1 ] ).to.equal( 'Response returned by "git describe" command.' ); expect( statusResponse.response ).to.deep.equal( { packageName: 'test-package', - commit: '6bfd379', status: { response: 'Parsed response.' }, - mrgitBranch: 'master' + commit: '6bfd379', + mrgitBranch: 'master', + mrgitTag: undefined, + latestTag: 'v35.3.2' } ); } ); - - function getCommandArguments( command ) { - return Object.assign( {}, commandData, { - arguments: [ command ] - } ); - } } ); it( 'modifies the package name if "packagesPrefix" is an array', () => { @@ -182,74 +187,107 @@ describe( 'commands/status', () => { '@ckeditor/ckeditor5-' ]; - stubs.execCommand.execute.onFirstCall().resolves( { - logs: { - info: [ '6bfd379a56a32c9f8b6e58bf08e39c124cdbae10' ] - } + stubs.execCommand.execute.onCall( 0 ).resolves( { + logs: { info: [ '6bfd379a56a32c9f8b6e58bf08e39c124cdbae10' ] } } ); - stubs.execCommand.execute.onSecondCall().resolves( { - logs: { - info: [ 'Response returned by "git status" command.' ] - } + stubs.execCommand.execute.onCall( 1 ).resolves( { + logs: { info: [ 'Response returned by "git status" command.' ] } + } ); + stubs.execCommand.execute.onCall( 2 ).resolves( { + logs: { info: [ 'Response returned by "git describe" command.' ] } + } ); + stubs.execCommand.execute.onCall( 3 ).resolves( { + logs: { info: [ 'v35.3.2\nv35.3.1\nv35.3.0\nv35.2.1\nv35.2.0\n' ] } } ); stubs.gitStatusParser.returns( { response: 'Parsed response.' } ); return statusCommand.execute( commandData ) .then( statusResponse => { - expect( stubs.execCommand.execute.calledTwice ).to.equal( true ); - expect( stubs.execCommand.execute.firstCall.args[ 0 ] ).to.deep.equal( + expect( stubs.execCommand.execute.callCount ).to.equal( 4 ); + expect( stubs.execCommand.execute.getCall( 0 ).args[ 0 ] ).to.deep.equal( getCommandArguments( 'git rev-parse HEAD' ) ); - expect( stubs.execCommand.execute.secondCall.args[ 0 ] ).to.deep.equal( + expect( stubs.execCommand.execute.getCall( 1 ).args[ 0 ] ).to.deep.equal( getCommandArguments( 'git status --branch --porcelain' ) ); + expect( stubs.execCommand.execute.getCall( 2 ).args[ 0 ] ).to.deep.equal( + getCommandArguments( 'git describe --abbrev=0 --tags' ) + ); + expect( stubs.execCommand.execute.getCall( 3 ).args[ 0 ] ).to.deep.equal( + getCommandArguments( 'git log --tags --simplify-by-decoration --pretty="%S"' ) + ); expect( stubs.gitStatusParser.calledOnce ).to.equal( true ); expect( stubs.gitStatusParser.firstCall.args[ 0 ] ).to.equal( 'Response returned by "git status" command.' ); + expect( stubs.gitStatusParser.firstCall.args[ 1 ] ).to.equal( 'Response returned by "git describe" command.' ); expect( statusResponse.response ).to.deep.equal( { packageName: 'test-package', - commit: '6bfd379', status: { response: 'Parsed response.' }, - mrgitBranch: 'master' + commit: '6bfd379', + mrgitBranch: 'master', + mrgitTag: undefined, + latestTag: 'v35.3.2' } ); } ); - - function getCommandArguments( command ) { - return Object.assign( {}, commandData, { - arguments: [ command ] - } ); - } } ); it( 'does not modify the package name if "packagesPrefix" option is not specified', () => { // mrgit resolves this option to be an empty array if it isn't specified. commandData.toolOptions.packagesPrefix = []; - stubs.execCommand.execute.onFirstCall().resolves( { - logs: { - info: [ '6bfd379a56a32c9f8b6e58bf08e39c124cdbae10' ] - } + stubs.execCommand.execute.onCall( 0 ).resolves( { + logs: { info: [ '6bfd379a56a32c9f8b6e58bf08e39c124cdbae10' ] } } ); - stubs.execCommand.execute.onSecondCall().resolves( { - logs: { - info: [ 'Response returned by "git status" command.' ] - } + stubs.execCommand.execute.onCall( 1 ).resolves( { + logs: { info: [ 'Response returned by "git status" command.' ] } + } ); + stubs.execCommand.execute.onCall( 2 ).resolves( { + logs: { info: [ 'Response returned by "git describe" command.' ] } + } ); + stubs.execCommand.execute.onCall( 3 ).resolves( { + logs: { info: [ '\nv35.3.2\nv35.3.1\nv35.3.0\nv35.2.1\nv35.2.0' ] } } ); stubs.gitStatusParser.returns( { response: 'Parsed response.' } ); return statusCommand.execute( commandData ) .then( statusResponse => { + expect( stubs.execCommand.execute.callCount ).to.equal( 4 ); + expect( stubs.execCommand.execute.getCall( 0 ).args[ 0 ] ).to.deep.equal( + getCommandArguments( 'git rev-parse HEAD' ) + ); + expect( stubs.execCommand.execute.getCall( 1 ).args[ 0 ] ).to.deep.equal( + getCommandArguments( 'git status --branch --porcelain' ) + ); + expect( stubs.execCommand.execute.getCall( 2 ).args[ 0 ] ).to.deep.equal( + getCommandArguments( 'git describe --abbrev=0 --tags' ) + ); + expect( stubs.execCommand.execute.getCall( 3 ).args[ 0 ] ).to.deep.equal( + getCommandArguments( 'git log --tags --simplify-by-decoration --pretty="%S"' ) + ); + + expect( stubs.gitStatusParser.calledOnce ).to.equal( true ); + expect( stubs.gitStatusParser.firstCall.args[ 0 ] ).to.equal( 'Response returned by "git status" command.' ); + expect( stubs.gitStatusParser.firstCall.args[ 1 ] ).to.equal( 'Response returned by "git describe" command.' ); + expect( statusResponse.response ).to.deep.equal( { packageName: '@ckeditor/ckeditor5-test-package', - commit: '6bfd379', status: { response: 'Parsed response.' }, - mrgitBranch: 'master' + commit: '6bfd379', + mrgitBranch: 'master', + mrgitTag: undefined, + latestTag: 'v35.3.2' } ); } ); } ); + + function getCommandArguments( command ) { + return Object.assign( {}, commandData, { + arguments: [ command ] + } ); + } } ); describe( 'afterExecute()', () => { @@ -294,6 +332,8 @@ describe( 'commands/status', () => { packageName: 'foo', status: { branch: 'master', + tag: undefined, + detachedHead: false, ahead: 0, behind: 2, staged: [], @@ -301,14 +341,19 @@ describe( 'commands/status', () => { untracked: [], unmerged: [] }, + commit: 'abcd123', mrgitBranch: 'master', - commit: 'abcd123' + mrgitTag: undefined, + currentTag: 'v35.3.2', + latestTag: 'v35.3.2' } ); commandResponses.add( { packageName: 'bar', status: { branch: 't/1', + tag: undefined, + detachedHead: false, ahead: 3, behind: 0, staged: [ 'gulpfile.js' ], @@ -316,8 +361,11 @@ describe( 'commands/status', () => { untracked: [ 'CHANGELOG.md' ], unmerged: [] }, + commit: 'ef45678', mrgitBranch: 't/1', - commit: 'ef45678' + mrgitTag: undefined, + currentTag: 'v35.3.2', + latestTag: 'v35.3.2' } ); stubs.table.toString.returns( '┻━┻' ); @@ -325,7 +373,7 @@ describe( 'commands/status', () => { statusCommand.afterExecute( processedPackages, commandResponses ); expect( stubs.table.constructor.firstCall.args[ 0 ] ).to.deep.equal( { - head: [ 'Package', 'Branch', 'Commit', 'Status' ], + head: [ 'Package', 'Branch/Tag', 'Commit', 'Status' ], style: { compact: true } @@ -348,18 +396,20 @@ describe( 'commands/status', () => { logStub.restore(); } ); - it( 'highlights a row if current branch is other than master', () => { + it( 'adds green "L" before the tag name to indicate latest tag being up to date', () => { const logStub = sinon.stub( console, 'log' ); const processedPackages = new Set(); const commandResponses = new Set(); - processedPackages.add( '@ckeditor/ckeditor5-foo' ); + processedPackages.add( '@ckeditor/ckeditor5-bar' ); commandResponses.add( { - packageName: 'foo', + packageName: 'bar', status: { - branch: 't/ckeditor5-bar/1', + branch: 'HEAD (no branch)', + tag: 'v35.3.2', + detachedHead: true, ahead: 0, behind: 0, staged: [], @@ -367,28 +417,138 @@ describe( 'commands/status', () => { untracked: [], unmerged: [] }, + commit: 'ef45678', mrgitBranch: 'master', - commit: 'abcd123' + mrgitTag: 'latest', + currentTag: 'v35.3.2', + latestTag: 'v35.3.2' } ); statusCommand.afterExecute( processedPackages, commandResponses ); - expect( stubs.chalk.magenta.called ).to.equal( true ); + + expect( stubs.table.push.firstCall.args[ 0 ] ).to.deep.equal( + [ 'bar', 'L v35.3.2', 'ef45678', '' ] + ); + + expect( stubs.chalk.green.callCount ).to.equal( 4 ); + + // Table + expect( stubs.chalk.green.getCall( 0 ).args[ 0 ] ).to.equal( 'L' ); + // Legend + expect( stubs.chalk.green.getCall( 1 ).args[ 0 ] ).to.equal( '+' ); + expect( stubs.chalk.green.getCall( 2 ).args[ 0 ] ).to.equal( 'L' ); + // Hints + expect( stubs.chalk.green.getCall( 3 ).args[ 0 ] ).to.equal( 'L' ); logStub.restore(); } ); - it( 'does not highlight a row if current branch is equal to master', () => { + it( 'adds "!" before the branch name if current branch is other than defined in "mrgit.json"', () => { const logStub = sinon.stub( console, 'log' ); const processedPackages = new Set(); const commandResponses = new Set(); - processedPackages.add( '@ckeditor/ckeditor5-foo' ); + processedPackages.add( '@ckeditor/ckeditor5-bar' ); commandResponses.add( { - packageName: 'foo', + packageName: 'bar', + status: { + branch: 't/1', + tag: undefined, + detachedHead: false, + ahead: 0, + behind: 0, + staged: [], + modified: [], + untracked: [], + unmerged: [] + }, + commit: 'ef45678', + mrgitBranch: 'master', + mrgitTag: undefined, + currentTag: 'v35.3.2', + latestTag: 'v35.3.2' + } ); + + statusCommand.afterExecute( processedPackages, commandResponses ); + + expect( stubs.table.push.firstCall.args[ 0 ] ).to.deep.equal( + [ 'bar', '! t/1', 'ef45678', '' ] + ); + + expect( stubs.chalk.cyan.callCount ).to.equal( 3 ); + + // Table + expect( stubs.chalk.cyan.getCall( 0 ).args[ 0 ] ).to.equal( '!' ); + // Legend + expect( stubs.chalk.cyan.getCall( 1 ).args[ 0 ] ).to.equal( '!' ); + // Hints + expect( stubs.chalk.cyan.getCall( 2 ).args[ 0 ] ).to.equal( '!' ); + + logStub.restore(); + } ); + + it( 'adds "!" before the tag name to indicate latest tag not being up to date', () => { + const logStub = sinon.stub( console, 'log' ); + + const processedPackages = new Set(); + const commandResponses = new Set(); + + processedPackages.add( '@ckeditor/ckeditor5-bar' ); + + commandResponses.add( { + packageName: 'bar', + status: { + branch: 'HEAD (no branch)', + tag: 'v30.0.0', + detachedHead: true, + ahead: 0, + behind: 0, + staged: [], + modified: [], + untracked: [], + unmerged: [] + }, + commit: 'ef45678', + mrgitBranch: 'master', + mrgitTag: 'latest', + currentTag: 'v30.0.0', + latestTag: 'v35.3.2' + } ); + + statusCommand.afterExecute( processedPackages, commandResponses ); + + expect( stubs.table.push.firstCall.args[ 0 ] ).to.deep.equal( + [ 'bar', '! v30.0.0', 'ef45678', '' ] + ); + + expect( stubs.chalk.cyan.callCount ).to.equal( 3 ); + + // Table + expect( stubs.chalk.cyan.getCall( 0 ).args[ 0 ] ).to.equal( '!' ); + // Legend + expect( stubs.chalk.cyan.getCall( 1 ).args[ 0 ] ).to.equal( '!' ); + // Hints + expect( stubs.chalk.cyan.getCall( 2 ).args[ 0 ] ).to.equal( '!' ); + + logStub.restore(); + } ); + + it( 'adds "!" before the branch name if the latest tag should be checked out instead', () => { + const logStub = sinon.stub( console, 'log' ); + + const processedPackages = new Set(); + const commandResponses = new Set(); + + processedPackages.add( '@ckeditor/ckeditor5-bar' ); + + commandResponses.add( { + packageName: 'bar', status: { branch: 'master', + tag: undefined, + detachedHead: false, ahead: 0, behind: 0, staged: [], @@ -396,17 +556,32 @@ describe( 'commands/status', () => { untracked: [], unmerged: [] }, + commit: 'ef45678', mrgitBranch: 'master', - commit: 'abcd123' + mrgitTag: 'latest', + currentTag: 'v35.3.2', + latestTag: 'v35.3.2' } ); statusCommand.afterExecute( processedPackages, commandResponses ); - expect( stubs.chalk.magenta.called ).to.equal( false ); + + expect( stubs.table.push.firstCall.args[ 0 ] ).to.deep.equal( + [ 'bar', '! master', 'ef45678', '' ] + ); + + expect( stubs.chalk.cyan.callCount ).to.equal( 3 ); + + // Table + expect( stubs.chalk.cyan.getCall( 0 ).args[ 0 ] ).to.equal( '!' ); + // Legend + expect( stubs.chalk.cyan.getCall( 1 ).args[ 0 ] ).to.equal( '!' ); + // Hints + expect( stubs.chalk.cyan.getCall( 2 ).args[ 0 ] ).to.equal( '!' ); logStub.restore(); } ); - it( 'adds "!" before the branch name if current branch is other than defined in "mrgit.json"', () => { + it( 'adds "!" before the branch name if specific tag should be checked out instead', () => { const logStub = sinon.stub( console, 'log' ); const processedPackages = new Set(); @@ -417,7 +592,9 @@ describe( 'commands/status', () => { commandResponses.add( { packageName: 'bar', status: { - branch: 't/1', + branch: 'master', + tag: undefined, + detachedHead: false, ahead: 0, behind: 0, staged: [], @@ -425,23 +602,32 @@ describe( 'commands/status', () => { untracked: [], unmerged: [] }, + commit: 'ef45678', mrgitBranch: 'master', - commit: 'ef45678' + mrgitTag: 'v30.0.0', + currentTag: 'v35.3.2', + latestTag: 'v35.3.2' } ); statusCommand.afterExecute( processedPackages, commandResponses ); expect( stubs.table.push.firstCall.args[ 0 ] ).to.deep.equal( - [ 'bar', '! t/1', 'ef45678', '' ] + [ 'bar', '! master', 'ef45678', '' ] ); - // First - in the table, second - in the legend. - expect( stubs.chalk.cyan.calledTwice ).to.equal( true ); + expect( stubs.chalk.cyan.callCount ).to.equal( 3 ); + + // Table + expect( stubs.chalk.cyan.getCall( 0 ).args[ 0 ] ).to.equal( '!' ); + // Legend + expect( stubs.chalk.cyan.getCall( 1 ).args[ 0 ] ).to.equal( '!' ); + // Hints + expect( stubs.chalk.cyan.getCall( 2 ).args[ 0 ] ).to.equal( '!' ); logStub.restore(); } ); - it( 'sorts packages by alphabetically', () => { + it( 'sorts packages alphabetically', () => { const logStub = sinon.stub( console, 'log' ); const processedPackages = new Set(); @@ -471,6 +657,8 @@ describe( 'commands/status', () => { packageName, status: { branch: 'master', + tag: undefined, + detachedHead: false, ahead: 0, behind: 0, staged: [], @@ -478,8 +666,11 @@ describe( 'commands/status', () => { untracked: [], unmerged: [] }, + commit, mrgitBranch: 'master', - commit + mrgitTag: undefined, + currentTag: 'v35.3.2', + latestTag: 'v35.3.2' }; } } ); @@ -496,6 +687,8 @@ describe( 'commands/status', () => { packageName: 'foo', status: { branch: 'master', + tag: undefined, + detachedHead: false, ahead: 0, behind: 2, staged: [], @@ -503,8 +696,11 @@ describe( 'commands/status', () => { untracked: [], unmerged: [ '.travis.yml' ] }, + commit: 'abcd123', mrgitBranch: 'master', - commit: 'abcd123' + mrgitTag: undefined, + currentTag: 'v35.3.2', + latestTag: 'v35.3.2' } ); stubs.table.toString.returns( '┻━┻' ); @@ -512,7 +708,7 @@ describe( 'commands/status', () => { statusCommand.afterExecute( processedPackages, commandResponses ); expect( stubs.table.constructor.firstCall.args[ 0 ] ).to.deep.equal( { - head: [ 'Package', 'Branch', 'Commit', 'Status' ], + head: [ 'Package', 'Branch/Tag', 'Commit', 'Status' ], style: { compact: true } @@ -544,6 +740,8 @@ describe( 'commands/status', () => { packageName: 'foo', status: { branch: 'master', + tag: undefined, + detachedHead: false, ahead: 0, behind: 2, staged: [], @@ -551,8 +749,11 @@ describe( 'commands/status', () => { untracked: [], unmerged: [ '.travis.yml' ] }, + commit: 'abcd123', mrgitBranch: 'master', - commit: 'abcd123' + mrgitTag: undefined, + currentTag: 'v35.3.2', + latestTag: 'v35.3.2' } ); stubs.table.toString.returns( '┻━┻' ); @@ -560,7 +761,7 @@ describe( 'commands/status', () => { statusCommand.afterExecute( processedPackages, commandResponses ); expect( stubs.table.constructor.firstCall.args[ 0 ] ).to.deep.equal( { - head: [ 'Package', 'Branch', 'Commit', 'Status' ], + head: [ 'Package', 'Branch/Tag', 'Commit', 'Status' ], style: { compact: true } diff --git a/tests/commands/sync.js b/tests/commands/sync.js index 067604a..7349d56 100644 --- a/tests/commands/sync.js +++ b/tests/commands/sync.js @@ -80,24 +80,79 @@ describe( 'commands/sync', () => { describe( 'execute()', () => { describe( 'first call on a package', () => { - it( 'clones the package if is not available', () => { + it( 'clones the repository if is not available', () => { stubs.fs.existsSync.returns( false ); stubs.shell.returns( Promise.resolve( 'Git clone log.' ) ); return syncCommand.execute( commandData ) .then( response => { - expect( stubs.shell.calledOnce ).to.equal( true ); + expect( stubs.shell.callCount ).to.equal( 2 ); - const cloneCommand = stubs.shell.firstCall.args[ 0 ].split( ' && ' ); + // Clone the repository. + expect( stubs.shell.getCall( 0 ).args[ 0 ] ).to.equal( + 'git clone --progress "git@github.com/organization/test-package.git" "/tmp/packages/test-package"' + ); + // Change the directory to cloned package and check out to proper branch. + expect( stubs.shell.getCall( 1 ).args[ 0 ] ).to.equal( + 'cd "/tmp/packages/test-package" && git checkout --quiet "master"' + ); + + expect( response.logs.info ).to.deep.equal( [ + 'Package "test-package" was not found. Cloning...', + 'Git clone log.' + ] ); + } ); + } ); + + it( 'clones the repository and checks out a specific tag', () => { + commandData.repository.tag = 'v30.0.0'; + + stubs.fs.existsSync.returns( false ); + stubs.shell.returns( Promise.resolve( 'Git clone log.' ) ); + + return syncCommand.execute( commandData ) + .then( response => { + expect( stubs.shell.callCount ).to.equal( 2 ); // Clone the repository. - expect( cloneCommand[ 0 ] ).to.equal( + expect( stubs.shell.getCall( 0 ).args[ 0 ] ).to.equal( 'git clone --progress "git@github.com/organization/test-package.git" "/tmp/packages/test-package"' ); - // Change the directory to cloned package. - expect( cloneCommand[ 1 ] ).to.equal( 'cd "/tmp/packages/test-package"' ); - // And check out to proper branch. - expect( cloneCommand[ 2 ] ).to.equal( 'git checkout --quiet master' ); + // Change the directory to cloned package and check out to proper branch. + expect( stubs.shell.getCall( 1 ).args[ 0 ] ).to.equal( + 'cd "/tmp/packages/test-package" && git checkout --quiet "tags/v30.0.0"' + ); + + expect( response.logs.info ).to.deep.equal( [ + 'Package "test-package" was not found. Cloning...', + 'Git clone log.' + ] ); + } ); + } ); + + it( 'clones the repository and checks the latest tag', () => { + commandData.repository.tag = 'latest'; + + const command = 'cd "/tmp/packages/test-package" && git log --tags --simplify-by-decoration --pretty="%S"'; + + stubs.fs.existsSync.returns( false ); + stubs.shell.returns( Promise.resolve( 'Git clone log.' ) ); + stubs.shell.withArgs( command ).returns( Promise.resolve( 'v35.3.2\nv35.3.1\nv35.3.0\nv35.2.1\nv35.2.0' ) ); + + return syncCommand.execute( commandData ) + .then( response => { + expect( stubs.shell.callCount ).to.equal( 3 ); + + // Clone the repository. + expect( stubs.shell.getCall( 0 ).args[ 0 ] ).to.equal( + 'git clone --progress "git@github.com/organization/test-package.git" "/tmp/packages/test-package"' + ); + // Look for the latest tag. + expect( stubs.shell.getCall( 1 ).args[ 0 ] ).to.equal( command ); + // Change the directory to cloned package and check out to proper branch. + expect( stubs.shell.getCall( 2 ).args[ 0 ] ).to.equal( + 'cd "/tmp/packages/test-package" && git checkout --quiet "tags/v35.3.2"' + ); expect( response.logs.info ).to.deep.equal( [ 'Package "test-package" was not found. Cloning...', @@ -139,8 +194,6 @@ describe( 'commands/sync', () => { describe( 'repeats installation process', function() { this.timeout( 5500 ); - const cloneCommand = 'git clone --progress "git@github.com/organization/test-package.git" "/tmp/packages/test-package"'; - it( 'for errors with capital letters', () => { stubs.fs.existsSync.returns( false ); @@ -159,27 +212,23 @@ describe( 'commands/sync', () => { return syncCommand.execute( commandData ) .then( response => { - expect( stubs.shell.calledTwice ).to.equal( true ); - - const firstCommand = stubs.shell.firstCall.args[ 0 ].split( ' && ' ); - - // Clone the repository for the first time. It failed. - expect( firstCommand[ 0 ] ) - .to.equal( cloneCommand ); - // Change the directory to cloned package. - expect( firstCommand[ 1 ] ).to.equal( 'cd "/tmp/packages/test-package"' ); - // And check out to proper branch. - expect( firstCommand[ 2 ] ).to.equal( 'git checkout --quiet master' ); - - const secondCommand = stubs.shell.secondCall.args[ 0 ].split( ' && ' ); - - // Clone the repository for the second time. It succeed. - expect( secondCommand[ 0 ] ) - .to.equal( cloneCommand ); - // Change the directory to cloned package. - expect( secondCommand[ 1 ] ).to.equal( 'cd "/tmp/packages/test-package"' ); - // And check out to proper branch. - expect( secondCommand[ 2 ] ).to.equal( 'git checkout --quiet master' ); + expect( stubs.shell.callCount ).to.equal( 3 ); + + // First attempt. + // Clone the repository. + expect( stubs.shell.getCall( 0 ).args[ 0 ] ).to.equal( + 'git clone --progress "git@github.com/organization/test-package.git" "/tmp/packages/test-package"' + ); + + // Second attempt. + // Clone the repository. + expect( stubs.shell.getCall( 1 ).args[ 0 ] ).to.equal( + 'git clone --progress "git@github.com/organization/test-package.git" "/tmp/packages/test-package"' + ); + // Change the directory to cloned package and check out to proper branch. + expect( stubs.shell.getCall( 2 ).args[ 0 ] ).to.equal( + 'cd "/tmp/packages/test-package" && git checkout --quiet "master"' + ); expect( response.logs.info ).to.deep.equal( [ 'Package "test-package" was not found. Cloning...', @@ -206,27 +255,23 @@ describe( 'commands/sync', () => { return syncCommand.execute( commandData ) .then( response => { - expect( stubs.shell.calledTwice ).to.equal( true ); - - const firstCommand = stubs.shell.firstCall.args[ 0 ].split( ' && ' ); - - // Clone the repository for the first time. It failed. - expect( firstCommand[ 0 ] ) - .to.equal( cloneCommand ); - // Change the directory to cloned package. - expect( firstCommand[ 1 ] ).to.equal( 'cd "/tmp/packages/test-package"' ); - // And check out to proper branch. - expect( firstCommand[ 2 ] ).to.equal( 'git checkout --quiet master' ); - - const secondCommand = stubs.shell.secondCall.args[ 0 ].split( ' && ' ); - - // Clone the repository for the second time. It succeed. - expect( secondCommand[ 0 ] ) - .to.equal( cloneCommand ); - // Change the directory to cloned package. - expect( secondCommand[ 1 ] ).to.equal( 'cd "/tmp/packages/test-package"' ); - // And check out to proper branch. - expect( secondCommand[ 2 ] ).to.equal( 'git checkout --quiet master' ); + expect( stubs.shell.callCount ).to.equal( 3 ); + + // First attempt. + // Clone the repository. + expect( stubs.shell.getCall( 0 ).args[ 0 ] ).to.equal( + 'git clone --progress "git@github.com/organization/test-package.git" "/tmp/packages/test-package"' + ); + + // Second attempt. + // Clone the repository. + expect( stubs.shell.getCall( 1 ).args[ 0 ] ).to.equal( + 'git clone --progress "git@github.com/organization/test-package.git" "/tmp/packages/test-package"' + ); + // Change the directory to cloned package and check out to proper branch. + expect( stubs.shell.getCall( 2 ).args[ 0 ] ).to.equal( + 'cd "/tmp/packages/test-package" && git checkout --quiet "master"' + ); expect( response.logs.info ).to.deep.equal( [ 'Package "test-package" was not found. Cloning...', @@ -277,7 +322,7 @@ describe( 'commands/sync', () => { } ); } ); - describe( 'the package is already installed', () => { + describe( 'the repository is already installed', () => { it( 'resolves promise after pulling the changes', () => { stubs.fs.existsSync.returns( true ); @@ -307,9 +352,9 @@ describe( 'commands/sync', () => { .then( response => { expect( exec.getCall( 0 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git status -s' ); expect( exec.getCall( 1 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git fetch' ); - expect( exec.getCall( 2 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git checkout master' ); + expect( exec.getCall( 2 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git checkout "master"' ); expect( exec.getCall( 3 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git branch' ); - expect( exec.getCall( 4 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git pull origin master' ); + expect( exec.getCall( 4 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git pull origin "master"' ); expect( response.logs.info ).to.deep.equal( [ 'Already on \'master\'.', @@ -320,6 +365,107 @@ describe( 'commands/sync', () => { } ); } ); + it( 'checks out specific tag', () => { + commandData.repository.tag = 'v35.3.0'; + + stubs.fs.existsSync.returns( true ); + + const exec = stubs.execCommand.execute; + + exec.onCall( 0 ).returns( Promise.resolve( { + logs: getCommandLogs( '' ) + } ) ); + + exec.onCall( 1 ).returns( Promise.resolve( { + logs: getCommandLogs( '' ) + } ) ); + + exec.onCall( 2 ).returns( Promise.resolve( { + logs: getCommandLogs( 'Note: checking out \'tags/v35.3.0\'.' ) + } ) ); + + exec.onCall( 3 ).returns( Promise.resolve( { + logs: getCommandLogs( [ + '* (HEAD detached at 1a0ff0a)', + ' master', + ' remotes/origin/master' + ].join( '\n' ) ) + } ) ); + + exec.onCall( 5 ).returns( Promise.resolve( { + logs: getCommandLogs( 'Already up-to-date.' ) + } ) ); + + return syncCommand.execute( commandData ) + .then( response => { + expect( exec.getCall( 0 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git status -s' ); + expect( exec.getCall( 1 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git fetch' ); + expect( exec.getCall( 2 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git checkout "tags/v35.3.0"' ); + expect( exec.getCall( 3 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git branch' ); + + expect( response.logs.info ).to.deep.equal( [ + 'Note: checking out \'tags/v35.3.0\'.', + 'Package "test-package" is on a detached commit.' + ] ); + + expect( exec.callCount ).to.equal( 4 ); + } ); + } ); + + it( 'checks out the latest tag', () => { + commandData.repository.tag = 'latest'; + + stubs.fs.existsSync.returns( true ); + + const exec = stubs.execCommand.execute; + + exec.onCall( 0 ).returns( Promise.resolve( { + logs: getCommandLogs( '' ) + } ) ); + + exec.onCall( 1 ).returns( Promise.resolve( { + logs: getCommandLogs( '' ) + } ) ); + + exec.onCall( 2 ).returns( Promise.resolve( { + logs: getCommandLogs( 'v35.3.2\nv35.3.1\nv35.3.0\nv35.2.1\nv35.3.0' ) + } ) ); + + exec.onCall( 3 ).returns( Promise.resolve( { + logs: getCommandLogs( 'Note: checking out \'tags/v35.3.2\'.' ) + } ) ); + + exec.onCall( 4 ).returns( Promise.resolve( { + logs: getCommandLogs( [ + '* (HEAD detached at 1a0ff0a)', + ' master', + ' remotes/origin/master' + ].join( '\n' ) ) + } ) ); + + exec.onCall( 5 ).returns( Promise.resolve( { + logs: getCommandLogs( 'Already up-to-date.' ) + } ) ); + + return syncCommand.execute( commandData ) + .then( response => { + expect( exec.getCall( 0 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git status -s' ); + expect( exec.getCall( 1 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git fetch' ); + expect( exec.getCall( 2 ).args[ 0 ].arguments[ 0 ] ).to.equal( + 'git log --tags --simplify-by-decoration --pretty="%S"' + ); + expect( exec.getCall( 3 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git checkout "tags/v35.3.2"' ); + expect( exec.getCall( 4 ).args[ 0 ].arguments[ 0 ] ).to.equal( 'git branch' ); + + expect( response.logs.info ).to.deep.equal( [ + 'Note: checking out \'tags/v35.3.2\'.', + 'Package "test-package" is on a detached commit.' + ] ); + + expect( exec.callCount ).to.equal( 5 ); + } ); + } ); + it( 'aborts if package has uncommitted changes', () => { stubs.fs.existsSync.returns( true ); diff --git a/tests/default-resolver.js b/tests/default-resolver.js index 3850487..e51855d 100644 --- a/tests/default-resolver.js +++ b/tests/default-resolver.js @@ -24,6 +24,7 @@ describe( 'default resolver()', () => { expect( resolver( 'simple-package', options ) ).to.deep.equal( { url: 'git@github.com:a/b.git', branch: 'master', + tag: undefined, directory: 'b' } ); } ); @@ -32,6 +33,7 @@ describe( 'default resolver()', () => { expect( resolver( 'package-with-branch', options ) ).to.deep.equal( { url: 'git@github.com:a/b.git', branch: 'dev', + tag: undefined, directory: 'b' } ); } ); @@ -40,6 +42,7 @@ describe( 'default resolver()', () => { expect( resolver( '@scoped/package', options ) ).to.deep.equal( { url: 'git@github.com:c/d.git', branch: 'master', + tag: undefined, directory: 'd' } ); } ); @@ -48,6 +51,7 @@ describe( 'default resolver()', () => { expect( resolver( 'full-url-git', options ) ).to.deep.equal( { url: 'git@github.com:cksource/mrgit.git', branch: 'master', + tag: undefined, directory: 'mrgit' } ); } ); @@ -56,6 +60,7 @@ describe( 'default resolver()', () => { expect( resolver( 'full-url-git-with-branch', options ) ).to.deep.equal( { url: 'git@github.com:cksource/mrgit.git', branch: 'xyz', + tag: undefined, directory: 'mrgit' } ); } ); @@ -64,9 +69,28 @@ describe( 'default resolver()', () => { expect( resolver( 'full-url-https', options ) ).to.deep.equal( { url: 'https://github.com/cksource/mrgit.git', branch: 'master', + tag: undefined, directory: 'mrgit' } ); } ); + + it( 'returns specific tag', () => { + expect( resolver( 'package-with-specific-tag', options ) ).to.deep.equal( { + url: 'git@github.com:a/b.git', + branch: 'master', + tag: 'v30.0.0', + directory: 'b' + } ); + } ); + + it( 'returns the "latest" tag', () => { + expect( resolver( 'package-with-latest-tag', options ) ).to.deep.equal( { + url: 'git@github.com:a/b.git', + branch: 'master', + tag: 'latest', + directory: 'b' + } ); + } ); } ); describe( 'with options.resolverUrlTempalate', () => { @@ -78,6 +102,7 @@ describe( 'default resolver()', () => { expect( resolver( 'simple-package', options ) ).to.deep.equal( { url: 'custom@path:a/b.git', branch: 'master', + tag: undefined, directory: 'b' } ); } ); @@ -86,6 +111,7 @@ describe( 'default resolver()', () => { expect( resolver( 'full-url-git', options ) ).to.deep.equal( { url: 'git@github.com:cksource/mrgit.git', branch: 'master', + tag: undefined, directory: 'mrgit' } ); } ); @@ -96,18 +122,20 @@ describe( 'default resolver()', () => { resolverDefaultBranch: 'major' }, cwd ); - it( 'returns the default branch if dependency URL does not specify it', () => { + it( 'returns the default branch if dependency URL does not specify it (simple package)', () => { expect( resolver( 'simple-package', options ) ).to.deep.equal( { url: 'git@github.com:a/b.git', branch: 'major', + tag: undefined, directory: 'b' } ); } ); - it( 'returns the default branch if dependency URL does not specify it', () => { + it( 'returns the default branch if dependency URL does not specify it (package with branch)', () => { expect( resolver( 'package-with-branch', options ) ).to.deep.equal( { url: 'git@github.com:a/b.git', branch: 'dev', + tag: undefined, directory: 'b' } ); } ); @@ -122,6 +150,7 @@ describe( 'default resolver()', () => { expect( resolver( 'simple-package', options ) ).to.deep.equal( { url: 'git@github.com:a/b.git', branch: 'master', + tag: undefined, directory: 'simple-package' } ); } ); @@ -130,6 +159,7 @@ describe( 'default resolver()', () => { expect( resolver( '@scoped/package', options ) ).to.deep.equal( { url: 'git@github.com:c/d.git', branch: 'master', + tag: undefined, directory: '@scoped/package' } ); } ); @@ -142,6 +172,7 @@ describe( 'default resolver()', () => { expect( resolver( 'override-directory', options ) ).to.deep.equal( { url: 'git@github.com:foo/bar.git', branch: 'master', + tag: undefined, directory: 'custom-directory' } ); } ); @@ -154,6 +185,7 @@ describe( 'default resolver()', () => { expect( resolver( 'override-directory', options ) ).to.deep.equal( { url: 'git@github.com:foo/bar.git', branch: 'master', + tag: undefined, directory: 'override-directory' } ); } ); diff --git a/tests/fixtures/project-a/mrgit.json b/tests/fixtures/project-a/mrgit.json index 045266c..8653c18 100644 --- a/tests/fixtures/project-a/mrgit.json +++ b/tests/fixtures/project-a/mrgit.json @@ -2,6 +2,8 @@ "dependencies": { "simple-package": "a/b", "package-with-branch": "a/b#dev", + "package-with-specific-tag": "a/b@v30.0.0", + "package-with-latest-tag": "a/b@latest", "@scoped/package": "c/d", "full-url-git": "git@github.com:cksource/mrgit.git", "full-url-git-with-branch": "git@github.com:cksource/mrgit.git#xyz", diff --git a/tests/utils/gitstatusparser.js b/tests/utils/gitstatusparser.js index 251aba2..d0d21a3 100644 --- a/tests/utils/gitstatusparser.js +++ b/tests/utils/gitstatusparser.js @@ -84,6 +84,12 @@ describe( 'utils', () => { expect( status.branch ).to.equal( 'master' ); } ); + it( 'returns tag name if its available and the repository is in detached head mode', () => { + const status = gitStatusParser( '## HEAD (no branch)', 'v30.0.0' ); + + expect( status.tag ).to.equal( 'v30.0.0' ); + } ); + it( 'returns number of commits being behind the remote branch', () => { const status = gitStatusParser( '## master [behind 3 ahead 6]' ); diff --git a/tests/utils/parserepositoryurl.js b/tests/utils/parserepositoryurl.js index ec39d1b..0a27bcd 100644 --- a/tests/utils/parserepositoryurl.js +++ b/tests/utils/parserepositoryurl.js @@ -20,6 +20,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'https://github.com/foo/bar.git', branch: 'master', + tag: undefined, directory: 'bar' } ); } ); @@ -32,6 +33,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'https://github.com/foo/bar.git', branch: 'stable', + tag: undefined, directory: 'bar' } ); } ); @@ -45,6 +47,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'https://github.com/foo/bar.git', branch: 'stable', + tag: undefined, directory: 'bar' } ); } ); @@ -55,6 +58,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'http://github.com/foo/bar.git', branch: 'master', + tag: undefined, directory: 'bar' } ); } ); @@ -65,6 +69,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'https://github.com/foo/bar.git', branch: 'master', + tag: undefined, directory: 'bar' } ); } ); @@ -75,6 +80,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'file:///Users/Workspace/Projects/foo/bar', branch: 'master', + tag: undefined, directory: 'bar' } ); } ); @@ -85,6 +91,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'file://c/Users/Workspace/Projects/foo/bar', branch: 'master', + tag: undefined, directory: 'bar' } ); } ); @@ -95,6 +102,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'git@github.com:foo/bar.git', branch: 'master', + tag: undefined, directory: 'bar' } ); } ); @@ -105,6 +113,46 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'https://github.com/foo/bar.git', branch: 'stable', + tag: undefined, + directory: 'bar' + } ); + } ); + + it( 'returns specific tag that contains semantic version number', () => { + const repository = parseRepositoryUrl( 'foo/bar@v30.0.0', { + urlTemplate: 'https://github.com/${ path }.git' + } ); + + expect( repository ).to.deep.equal( { + url: 'https://github.com/foo/bar.git', + branch: 'master', + tag: 'v30.0.0', + directory: 'bar' + } ); + } ); + + it( 'returns specific tag that does not contain semantic version number', () => { + const repository = parseRepositoryUrl( 'foo/bar@customTagName', { + urlTemplate: 'https://github.com/${ path }.git' + } ); + + expect( repository ).to.deep.equal( { + url: 'https://github.com/foo/bar.git', + branch: 'master', + tag: 'customTagName', + directory: 'bar' + } ); + } ); + + it( 'returns the "latest" tag', () => { + const repository = parseRepositoryUrl( 'foo/bar@latest', { + urlTemplate: 'https://github.com/${ path }.git' + } ); + + expect( repository ).to.deep.equal( { + url: 'https://github.com/foo/bar.git', + branch: 'master', + tag: 'latest', directory: 'bar' } ); } ); @@ -114,12 +162,14 @@ describe( 'utils', () => { const repository = parseRepositoryUrl( 'foo/bar', { urlTemplate: 'https://github.com/${ path }.git', defaultBranch: 'develop', + tag: undefined, cwdPackageBranch: 'master' } ); expect( repository ).to.deep.equal( { url: 'https://github.com/foo/bar.git', branch: 'develop', + tag: undefined, directory: 'bar' } ); } ); @@ -133,6 +183,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'https://github.com/foo/bar.git', branch: 'develop', + tag: undefined, directory: 'bar' } ); } ); @@ -146,6 +197,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'https://github.com/foo/bar.git', branch: 'master', + tag: undefined, directory: 'bar' } ); } ); @@ -154,6 +206,7 @@ describe( 'utils', () => { const repository = parseRepositoryUrl( 'foo/bar', { urlTemplate: 'https://github.com/${ path }.git', defaultBranch: 'develop', + tag: undefined, baseBranches: [], cwdPackageBranch: 'master' } ); @@ -161,6 +214,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'https://github.com/foo/bar.git', branch: 'develop', + tag: undefined, directory: 'bar' } ); } ); @@ -169,6 +223,7 @@ describe( 'utils', () => { const repository = parseRepositoryUrl( 'foo/bar', { urlTemplate: 'https://github.com/${ path }.git', defaultBranch: 'develop', + tag: undefined, baseBranches: [ 'stable' ], cwdPackageBranch: 'master' } ); @@ -176,6 +231,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'https://github.com/foo/bar.git', branch: 'develop', + tag: undefined, directory: 'bar' } ); } ); @@ -184,6 +240,7 @@ describe( 'utils', () => { const repository = parseRepositoryUrl( 'foo/bar', { urlTemplate: 'https://github.com/${ path }.git', defaultBranch: 'develop', + tag: undefined, baseBranches: [ 'stable', 'master' ], cwdPackageBranch: 'stable' } ); @@ -191,6 +248,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'https://github.com/foo/bar.git', branch: 'stable', + tag: undefined, directory: 'bar' } ); } ); @@ -199,6 +257,7 @@ describe( 'utils', () => { const repository = parseRepositoryUrl( 'foo/bar#mrgit', { urlTemplate: 'https://github.com/${ path }.git', defaultBranch: 'develop', + tag: undefined, baseBranches: [ 'stable' ], cwdPackageBranch: 'master' } ); @@ -206,6 +265,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'https://github.com/foo/bar.git', branch: 'mrgit', + tag: undefined, directory: 'bar' } ); } ); @@ -214,6 +274,7 @@ describe( 'utils', () => { const repository = parseRepositoryUrl( 'foo/bar#mrgit', { urlTemplate: 'https://github.com/${ path }.git', defaultBranch: 'develop', + tag: undefined, baseBranches: [ 'master' ], cwdPackageBranch: 'master' } ); @@ -221,6 +282,7 @@ describe( 'utils', () => { expect( repository ).to.deep.equal( { url: 'https://github.com/foo/bar.git', branch: 'mrgit', + tag: undefined, directory: 'bar' } ); } ); diff --git a/yarn.lock b/yarn.lock index b1d2804..4a85e40 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,14 @@ # yarn lockfile v1 +"@ampproject/remapping@^2.1.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== + dependencies: + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" + "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" @@ -16,15 +24,51 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/generator@^7.20.1": - version "7.20.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.4.tgz#4d9f8f0c30be75fd90a0562099a26e5839602ab8" - integrity sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA== +"@babel/compat-data@^7.20.0": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.5.tgz#86f172690b093373a933223b4745deeb6049e733" + integrity sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g== + +"@babel/core@^7.7.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.5.tgz#45e2114dc6cd4ab167f81daf7820e8fa1250d113" + integrity sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ== dependencies: - "@babel/types" "^7.20.2" + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-module-transforms" "^7.20.2" + "@babel/helpers" "^7.20.5" + "@babel/parser" "^7.20.5" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + +"@babel/generator@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.5.tgz#cb25abee3178adf58d6814b68517c62bdbfdda95" + integrity sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA== + dependencies: + "@babel/types" "^7.20.5" "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/helper-compilation-targets@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" + integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== + dependencies: + "@babel/compat-data" "^7.20.0" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.21.3" + semver "^6.3.0" + "@babel/helper-environment-visitor@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" @@ -45,6 +89,34 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-module-transforms@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" + integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.2" + +"@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + "@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" @@ -62,6 +134,20 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + +"@babel/helpers@^7.20.5": + version "7.20.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.6.tgz#e64778046b70e04779dfbdf924e7ebb45992c763" + integrity sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w== + dependencies: + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" @@ -71,10 +157,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.18.10", "@babel/parser@^7.18.9", "@babel/parser@^7.20.1": - version "7.20.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2" - integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg== +"@babel/parser@^7.18.10", "@babel/parser@^7.18.9", "@babel/parser@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8" + integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA== "@babel/template@^7.18.10": version "7.18.10" @@ -85,26 +171,26 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/traverse@^7.18.9": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.1.tgz#9b15ccbf882f6d107eeeecf263fbcdd208777ec8" - integrity sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA== +"@babel/traverse@^7.18.9", "@babel/traverse@^7.20.1", "@babel/traverse@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.5.tgz#78eb244bea8270fdda1ef9af22a5d5e5b7e57133" + integrity sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.1" + "@babel/generator" "^7.20.5" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.1" - "@babel/types" "^7.20.0" + "@babel/parser" "^7.20.5" + "@babel/types" "^7.20.5" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.2.tgz#67ac09266606190f496322dbaff360fdaa5e7842" - integrity sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog== +"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84" + integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg== dependencies: "@babel/helper-string-parser" "^7.19.4" "@babel/helper-validator-identifier" "^7.19.1" @@ -218,6 +304,30 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" @@ -232,7 +342,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.1": +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== @@ -410,9 +520,9 @@ "@octokit/openapi-types" "^12.11.0" "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.5.tgz#e280c94c95f206dcfd5aca00a43f2156b758c764" - integrity sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA== + version "1.8.6" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" + integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== dependencies: type-detect "4.0.8" @@ -519,13 +629,13 @@ integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== "@typescript-eslint/eslint-plugin@^5.26.0": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.44.0.tgz#105788f299050c917eb85c4d9fd04b089e3740de" - integrity sha512-j5ULd7FmmekcyWeArx+i8x7sdRHzAtXTkmDPthE4amxZOWKFK7bomoJ4r7PJ8K7PoMzD16U8MmuZFAonr1ERvw== + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.0.tgz#ffa505cf961d4844d38cfa19dcec4973a6039e41" + integrity sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA== dependencies: - "@typescript-eslint/scope-manager" "5.44.0" - "@typescript-eslint/type-utils" "5.44.0" - "@typescript-eslint/utils" "5.44.0" + "@typescript-eslint/scope-manager" "5.45.0" + "@typescript-eslint/type-utils" "5.45.0" + "@typescript-eslint/utils" "5.45.0" debug "^4.3.4" ignore "^5.2.0" natural-compare-lite "^1.4.0" @@ -534,71 +644,71 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.26.0": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.44.0.tgz#99e2c710a2252191e7a79113264f438338b846ad" - integrity sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA== + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.45.0.tgz#b18a5f6b3cf1c2b3e399e9d2df4be40d6b0ddd0e" + integrity sha512-brvs/WSM4fKUmF5Ot/gEve6qYiCMjm6w4HkHPfS6ZNmxTS0m0iNN4yOChImaCkqc1hRwFGqUyanMXuGal6oyyQ== dependencies: - "@typescript-eslint/scope-manager" "5.44.0" - "@typescript-eslint/types" "5.44.0" - "@typescript-eslint/typescript-estree" "5.44.0" + "@typescript-eslint/scope-manager" "5.45.0" + "@typescript-eslint/types" "5.45.0" + "@typescript-eslint/typescript-estree" "5.45.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.44.0": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.44.0.tgz#988c3f34b45b3474eb9ff0674c18309dedfc3e04" - integrity sha512-2pKml57KusI0LAhgLKae9kwWeITZ7IsZs77YxyNyIVOwQ1kToyXRaJLl+uDEXzMN5hnobKUOo2gKntK9H1YL8g== +"@typescript-eslint/scope-manager@5.45.0": + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.45.0.tgz#7a4ac1bfa9544bff3f620ab85947945938319a96" + integrity sha512-noDMjr87Arp/PuVrtvN3dXiJstQR1+XlQ4R1EvzG+NMgXi8CuMCXpb8JqNtFHKceVSQ985BZhfRdowJzbv4yKw== dependencies: - "@typescript-eslint/types" "5.44.0" - "@typescript-eslint/visitor-keys" "5.44.0" + "@typescript-eslint/types" "5.45.0" + "@typescript-eslint/visitor-keys" "5.45.0" -"@typescript-eslint/type-utils@5.44.0": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.44.0.tgz#bc5a6e8a0269850714a870c9268c038150dfb3c7" - integrity sha512-A1u0Yo5wZxkXPQ7/noGkRhV4J9opcymcr31XQtOzcc5nO/IHN2E2TPMECKWYpM3e6olWEM63fq/BaL1wEYnt/w== +"@typescript-eslint/type-utils@5.45.0": + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.45.0.tgz#aefbc954c40878fcebeabfb77d20d84a3da3a8b2" + integrity sha512-DY7BXVFSIGRGFZ574hTEyLPRiQIvI/9oGcN8t1A7f6zIs6ftbrU0nhyV26ZW//6f85avkwrLag424n+fkuoJ1Q== dependencies: - "@typescript-eslint/typescript-estree" "5.44.0" - "@typescript-eslint/utils" "5.44.0" + "@typescript-eslint/typescript-estree" "5.45.0" + "@typescript-eslint/utils" "5.45.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.44.0": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.44.0.tgz#f3f0b89aaff78f097a2927fe5688c07e786a0241" - integrity sha512-Tp+zDnHmGk4qKR1l+Y1rBvpjpm5tGXX339eAlRBDg+kgZkz9Bw+pqi4dyseOZMsGuSH69fYfPJCBKBrbPCxYFQ== +"@typescript-eslint/types@5.45.0": + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.45.0.tgz#794760b9037ee4154c09549ef5a96599621109c5" + integrity sha512-QQij+u/vgskA66azc9dCmx+rev79PzX8uDHpsqSjEFtfF2gBUTRCpvYMh2gw2ghkJabNkPlSUCimsyBEQZd1DA== -"@typescript-eslint/typescript-estree@5.44.0": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.44.0.tgz#0461b386203e8d383bb1268b1ed1da9bc905b045" - integrity sha512-M6Jr+RM7M5zeRj2maSfsZK2660HKAJawv4Ud0xT+yauyvgrsHu276VtXlKDFnEmhG+nVEd0fYZNXGoAgxwDWJw== +"@typescript-eslint/typescript-estree@5.45.0": + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.0.tgz#f70a0d646d7f38c0dfd6936a5e171a77f1e5291d" + integrity sha512-maRhLGSzqUpFcZgXxg1qc/+H0bT36lHK4APhp0AEUVrpSwXiRAomm/JGjSG+kNUio5kAa3uekCYu/47cnGn5EQ== dependencies: - "@typescript-eslint/types" "5.44.0" - "@typescript-eslint/visitor-keys" "5.44.0" + "@typescript-eslint/types" "5.45.0" + "@typescript-eslint/visitor-keys" "5.45.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.44.0": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.44.0.tgz#d733da4d79d6c30f1a68b531cdda1e0c1f00d52d" - integrity sha512-fMzA8LLQ189gaBjS0MZszw5HBdZgVwxVFShCO3QN+ws3GlPkcy9YuS3U4wkT6su0w+Byjq3mS3uamy9HE4Yfjw== +"@typescript-eslint/utils@5.45.0": + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.45.0.tgz#9cca2996eee1b8615485a6918a5c763629c7acf5" + integrity sha512-OUg2JvsVI1oIee/SwiejTot2OxwU8a7UfTFMOdlhD2y+Hl6memUSL4s98bpUTo8EpVEr0lmwlU7JSu/p2QpSvA== dependencies: "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.44.0" - "@typescript-eslint/types" "5.44.0" - "@typescript-eslint/typescript-estree" "5.44.0" + "@typescript-eslint/scope-manager" "5.45.0" + "@typescript-eslint/types" "5.45.0" + "@typescript-eslint/typescript-estree" "5.45.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.44.0": - version "5.44.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.44.0.tgz#10740dc28902bb903d12ee3a005cc3a70207d433" - integrity sha512-a48tLG8/4m62gPFbJ27FxwCOqPKxsb8KC3HkmYoq2As/4YyjQl1jDbRr1s63+g4FS/iIehjmN3L5UjmKva1HzQ== +"@typescript-eslint/visitor-keys@5.45.0": + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.0.tgz#e0d160e9e7fdb7f8da697a5b78e7a14a22a70528" + integrity sha512-jc6Eccbn2RtQPr1s7th6jJWQHBHI6GBVQkCHoJFQ5UreaKm59Vxw+ynQUPPY2u2Amquc+7tmEoC2G52ApsGNNg== dependencies: - "@typescript-eslint/types" "5.44.0" + "@typescript-eslint/types" "5.45.0" eslint-visitor-keys "^3.3.0" "@ungap/promise-all-settled@1.1.2": @@ -857,6 +967,18 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +append-transform@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-2.0.0.tgz#99d9d29c7b38391e6f428d28ce136551f0b77e12" + integrity sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg== + dependencies: + default-require-extensions "^3.0.0" + +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -949,7 +1071,7 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.21.4: +browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.21.3, browserslist@^4.21.4: version "4.21.4" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== @@ -988,6 +1110,16 @@ cacache@^15.0.5: tar "^6.0.2" unique-filename "^1.1.1" +caching-transform@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" + integrity sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA== + dependencies: + hasha "^5.0.0" + make-dir "^3.0.0" + package-hash "^4.0.0" + write-file-atomic "^3.0.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1007,7 +1139,7 @@ camelcase-keys@^6.2.2: map-obj "^4.0.0" quick-lru "^4.0.1" -camelcase@^5.3.1: +camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== @@ -1142,6 +1274,15 @@ cli-width@^3.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -1269,6 +1410,11 @@ conventional-commits-parser@^3.1.0: split2 "^3.0.0" through2 "^4.0.0" +convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + core-js@^3.22.4: version "3.26.1" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.26.1.tgz#7a9816dabd9ee846c1c0fe0e8fcad68f3709134e" @@ -1296,7 +1442,7 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.2: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1454,6 +1600,13 @@ deep-is@^0.1.3, deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +default-require-extensions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-3.0.1.tgz#bfae00feeaeada68c2ae256c62540f60b80625bd" + integrity sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw== + dependencies: + strip-bom "^4.0.0" + del@^5.0.0, del@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/del/-/del-5.1.0.tgz#d9487c94e367410e6eff2925ee58c0c84a75b3a7" @@ -1593,6 +1746,11 @@ es-module-lexer@^0.9.0: resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== +es6-error@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -1888,7 +2046,7 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-cache-dir@^3.3.1: +find-cache-dir@^3.2.0, find-cache-dir@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== @@ -1931,6 +2089,19 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^3.0.2" + +fromentries@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" + integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg== + fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -1982,7 +2153,12 @@ generic-pool@^3.7.1: resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4" integrity sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g== -get-caller-file@^2.0.5: +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -1997,6 +2173,11 @@ get-own-enumerable-property-symbols@^3.0.0: resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -2107,7 +2288,7 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: +graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== @@ -2156,6 +2337,14 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hasha@^5.0.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1" + integrity sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ== + dependencies: + is-stream "^2.0.0" + type-fest "^0.8.0" + he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -2173,6 +2362,11 @@ hosted-git-info@^4.0.1: dependencies: lru-cache "^6.0.0" +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + human-signals@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" @@ -2363,11 +2557,21 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -2378,6 +2582,66 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + +istanbul-lib-hook@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz#8f84c9434888cc6b1d0a9d7092a76d239ebf0cc6" + integrity sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ== + dependencies: + append-transform "^2.0.0" + +istanbul-lib-instrument@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-processinfo@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz#366d454cd0dcb7eb6e0e419378e60072c8626169" + integrity sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg== + dependencies: + archy "^1.0.0" + cross-spawn "^7.0.3" + istanbul-lib-coverage "^3.2.0" + p-map "^3.0.0" + rimraf "^3.0.0" + uuid "^8.3.2" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.0.2: + version "3.1.5" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" + integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + istanbul@^0.4.5: version "0.4.5" resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" @@ -2471,7 +2735,7 @@ json-stringify-safe@^5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== -json5@^2.1.2: +json5@^2.1.2, json5@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== @@ -2601,6 +2865,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.flattendeep@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" + integrity sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ== + lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -2673,7 +2942,7 @@ macos-release@^2.2.0: resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.0.tgz#067c2c88b5f3fb3c56a375b2ec93826220fa1ff2" integrity sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g== -make-dir@^3.0.2: +make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -2949,6 +3218,13 @@ node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" +node-preload@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/node-preload/-/node-preload-0.2.1.tgz#c03043bb327f417a18fee7ab7ee57b408a144301" + integrity sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ== + dependencies: + process-on-spawn "^1.0.0" + node-releases@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" @@ -3012,6 +3288,39 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" +nyc@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.1.0.tgz#1335dae12ddc87b6e249d5a1994ca4bdaea75f02" + integrity sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A== + dependencies: + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + caching-transform "^4.0.0" + convert-source-map "^1.7.0" + decamelize "^1.2.0" + find-cache-dir "^3.2.0" + find-up "^4.1.0" + foreground-child "^2.0.0" + get-package-type "^0.1.0" + glob "^7.1.6" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-hook "^3.0.0" + istanbul-lib-instrument "^4.0.0" + istanbul-lib-processinfo "^2.0.2" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + make-dir "^3.0.0" + node-preload "^0.2.1" + p-map "^3.0.0" + process-on-spawn "^1.0.0" + resolve-from "^5.0.0" + rimraf "^3.0.0" + signal-exit "^3.0.2" + spawn-wrap "^2.0.0" + test-exclude "^6.0.0" + yargs "^15.0.2" + once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -3115,6 +3424,16 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-hash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506" + integrity sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ== + dependencies: + graceful-fs "^4.1.15" + hasha "^5.0.0" + lodash.flattendeep "^4.4.0" + release-zalgo "^1.0.0" + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -3492,6 +3811,13 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== +process-on-spawn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-on-spawn/-/process-on-spawn-1.0.0.tgz#95b05a23073d30a17acfdc92a440efd2baefdc93" + integrity sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg== + dependencies: + fromentries "^1.2.0" + progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -3607,6 +3933,13 @@ regexpp@^3.1.0, regexpp@^3.2.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== +release-zalgo@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" + integrity sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA== + dependencies: + es6-error "^4.0.1" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -3617,11 +3950,21 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" @@ -3716,7 +4059,7 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0: +semver@^6.0.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -3742,6 +4085,11 @@ serialize-javascript@^5.0.1: dependencies: randombytes "^2.1.0" +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -3845,6 +4193,18 @@ source-map@~0.2.0: dependencies: amdefine ">=0.0.4" +spawn-wrap@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-2.0.0.tgz#103685b8b8f9b79771318827aa78650a610d457e" + integrity sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg== + dependencies: + foreground-child "^2.0.0" + is-windows "^1.0.2" + make-dir "^3.0.0" + rimraf "^3.0.0" + signal-exit "^3.0.2" + which "^2.0.1" + spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" @@ -3939,6 +4299,11 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -4092,6 +4457,15 @@ terser@^5.14.1, terser@^5.3.4: commander "^2.20.0" source-map-support "~0.5.20" +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + text-extensions@^1.0.0: version "1.9.0" resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" @@ -4217,11 +4591,18 @@ type-fest@^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: +type-fest@^0.8.0, type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -4298,6 +4679,11 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" @@ -4383,6 +4769,11 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== + which@2.0.2, which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -4442,6 +4833,21 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" @@ -4462,6 +4868,14 @@ yargs-parser@20.2.4: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" @@ -4490,6 +4904,23 @@ yargs@16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yargs@^15.0.2: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"