├── .gitignore ├── output.gif ├── bin └── mvn-search ├── package.json ├── README.md └── src └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /output.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erosb/mvn-search/HEAD/output.gif -------------------------------------------------------------------------------- /bin/mvn-search: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import yargs from 'yargs'; 3 | import { search } from "../src/index.js" 4 | const args = yargs 5 | .scriptName('mvn-search') 6 | .usage('$0 [args] ', 'search for maven dependency', (yargs) => { 7 | yargs.positional('query-string', { 8 | describe: 'the dependency you search for', 9 | type: 'string' 10 | }) 11 | }) 12 | .option('format', { 13 | alias: 'f', 14 | description: 'Define in which format to print dependency. (gradle, gradlekts, gradlegroovy, maven, sbt)', 15 | type: 'string', 16 | default: 'maven' 17 | }) 18 | .help() 19 | .alias('help', 'h') 20 | .argv; 21 | 22 | search(args['query-string'], args.format); 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@erosb/mvn-search", 3 | "version": "0.0.5", 4 | "description": "CLI to search the Maven dependencies", 5 | "main": "src/index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "bin": { 11 | "@erosb/mvn-search": "bin/mvn-search", 12 | "mvn-search": "bin/mvn-search" 13 | }, 14 | "publishConfig": { 15 | "access": "public" 16 | }, 17 | "keywords": [ 18 | "cli", 19 | "mvn", 20 | "search", 21 | "java", 22 | "dependency" 23 | ], 24 | "repository": { 25 | "url": "https://github.com/erosb/mvn-search" 26 | }, 27 | "author": "Bence Eros", 28 | "license": "MIT", 29 | "dependencies": { 30 | "clipboardy": "^2.3.0", 31 | "esm": "^3.2.25", 32 | "inquirer": "^7.3.3", 33 | "yargs": "^15.4.1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `mvn-search` - command-line tool for searching maven central 2 | 3 | This is a utility for developers who prefer searching maven dependencies in the command line instead of the web UI of 4 | search.maven.org 5 | 6 | ![Searching demo](output.gif "Searching demo") 7 | 8 | ## Installation 9 | 10 | Prerequisite: node `10.9.0` or newer should be installed. 11 | 12 | 17 | 18 | ```bash 19 | npm install -g @erosb/mvn-search 20 | ``` 21 | 22 | ## Usage: `mvn-search ` 23 | 24 | This will list the found artifacts with their latest version numbers. After selecting the coordinates the tool displays 25 | the maven `` tag to be pasted into the `pom.xml`. 26 | 27 | 28 | ### Examples: 29 | 30 | * `mvn-search hibernate-validator` 31 | * `mvn-search g:org.slf4j` 32 | 33 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import inquirer from "inquirer"; 2 | import clipboardy from "clipboardy"; 3 | 4 | let lastSearchResults; 5 | let searchTerm; 6 | let dependencyFormat; 7 | 8 | function formatDependency(dependency) { 9 | switch (dependencyFormat.toLowerCase()) { 10 | case 'gradlekts': 11 | case 'gradle': 12 | return formatDependencyGradleKotlin(dependency); 13 | case 'gradlegroovy': 14 | return formatDependencyGradleGroovy(dependency); 15 | case 'sbt': 16 | return formatDependencySbt(dependency); 17 | case 'maven': 18 | default: 19 | return formatDependencyXml(dependency); 20 | } 21 | } 22 | 23 | function formatDependencyXml(dependency) { 24 | return ` 25 | 26 | ${dependency.groupId} 27 | ${dependency.artifactId} 28 | ${dependency.versions[0]} 29 | 30 | `; 31 | } 32 | 33 | function formatDependencyGradleKotlin(dependency) { 34 | return ` 35 | implementation("${dependency.groupId}:${dependency.artifactId}:${dependency.versions[0]}") 36 | `; 37 | } 38 | 39 | function formatDependencyGradleGroovy(dependency) { 40 | return ` 41 | implementation '${dependency.groupId}:${dependency.artifactId}:${dependency.versions[0]}' 42 | `; 43 | } 44 | 45 | function formatDependencySbt(dependency) { 46 | return ` 47 | libraryDependencies += "${dependency.groupId}" % "${dependency.artifactId}" % "${dependency.versions[0]}" 48 | `; 49 | } 50 | 51 | function versionSearchResponseArrived(resp) { 52 | const hits = JSON.parse(resp).components; 53 | if (hits.length === 0) { 54 | console.log("no result"); 55 | return; 56 | } 57 | console.log(hits.map(v => v.version)) 58 | } 59 | 60 | function mvnSearchResponseArrived(resp) { 61 | const hits = JSON.parse(resp).components; 62 | 63 | if (hits.length === 0) { 64 | console.error(`no results`) 65 | newSearch(); 66 | return; 67 | } 68 | 69 | lastSearchResults = hits.map(val => { 70 | return { 71 | groupId: val.namespace, 72 | artifactId: val.name, 73 | packaging: val.packaging, 74 | versions: [val.latestVersionInfo.version] 75 | }; 76 | }); 77 | 78 | const choices = lastSearchResults.map(result => { 79 | return { 80 | name: result.groupId + ":" + result.artifactId + ":" + result.versions[0], 81 | value: result 82 | }; 83 | 84 | }); 85 | inquirer.prompt({ 86 | type: "list", 87 | name: "coordinates", 88 | "pageSize": 30, 89 | choices 90 | }).then(answers => { 91 | const ans = answers.coordinates; 92 | console.log(formatDependency(ans)); 93 | inquirer.prompt([ 94 | { 95 | "type": "list", 96 | "name": "action", 97 | "choices": [ 98 | { 99 | "name": "Copy to clipboard", 100 | "value": "copyToClipboard" 101 | }, 102 | { 103 | "name": "Search older versions", 104 | "value": "searchOlderVersions" 105 | }, 106 | { 107 | "name": "Start a new search", 108 | "value": "newSearch" 109 | }, 110 | { 111 | "name": "quit", 112 | "value": "quit" 113 | } 114 | ] 115 | } 116 | ]).then(answers => { 117 | const action = answers.action; 118 | switch (action) { 119 | case "quit": 120 | break; 121 | case "newSearch": 122 | newSearch(); 123 | break; 124 | case "copyToClipboard": 125 | clipboardy.writeSync(formatDependency(ans)) 126 | break; 127 | case "searchOlderVersions": 128 | fetch(`https://central.sonatype.com/api/internal/browse/component/versions?sortField=normalizedVersion&sortDirection=desc&page=0&size=12&filter=namespace%3A${ans.groupId}%2Cname%3A${ans.artifactId}`) 129 | .then(resp => resp.text()) 130 | .then(versionSearchResponseArrived); 131 | break; 132 | } 133 | }); 134 | }); 135 | } 136 | 137 | function newSearch() { 138 | inquirer.prompt([ 139 | { 140 | "type": "input", 141 | "name": "search term" 142 | } 143 | ]).then(answer => startSearch(answer["search term"])); 144 | } 145 | 146 | function startSearch(searchTerm) { 147 | fetch("https://central.sonatype.com/api/internal/browse/components", { 148 | method: "post", 149 | headers: { 150 | "content-type": "application/json" 151 | }, 152 | body: JSON.stringify({ 153 | page: 0, 154 | size: 20, 155 | searchTerm: searchTerm 156 | }) 157 | }).then(resp => resp.text()) 158 | .then(mvnSearchResponseArrived); 159 | } 160 | 161 | export function search(term, format) { 162 | searchTerm = term || ''; 163 | dependencyFormat = format || 'maven'; 164 | if (searchTerm.trim() === "") { 165 | newSearch(); 166 | } else { 167 | startSearch(searchTerm); 168 | } 169 | } 170 | --------------------------------------------------------------------------------