├── .gitignore ├── npm_icon.png ├── images └── promo.gif ├── .vscodeignore ├── tsconfig.json ├── .vscode ├── tasks.json └── launch.json ├── CHANGELOG.md ├── LICENSE ├── eslint.config.mjs ├── README.md ├── package.json └── src └── extension.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode-test/ 3 | .vsix 4 | -------------------------------------------------------------------------------- /npm_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herrmannplatz/npm-dependency-links/HEAD/npm_icon.png -------------------------------------------------------------------------------- /images/promo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herrmannplatz/npm-dependency-links/HEAD/images/promo.gif -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .gitignore 3 | tsconfig.json 4 | vsc-extension-quickstart.md 5 | eslint.config.mjs 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2020", 5 | "lib": ["es2020"], 6 | "outDir": "out", 7 | "sourceMap": true, 8 | "strict": true, 9 | "rootDir": "src" 10 | }, 11 | "exclude": ["node_modules", ".vscode-test"] 12 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to the "npm-dependency-links" extension will be documented in this file. 3 | 4 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 5 | 6 | ## 0.0.1 7 | - Initial release 8 | 9 | ## 0.0.2 10 | - Support peerDependencies 11 | 12 | ## 0.0.3 13 | - Project cleanup 14 | 15 | ## 1.0.0 16 | - Improve parsing 17 | - Fix links getting marked multiple times 18 | 19 | ## 1.1.0 20 | - Configuration for registry url 21 | 22 | ## 1.2.0 23 | - Add support for arbitrary registry URLs 24 | 25 | ## 1.3.0 26 | - Improve performance 27 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": ["--extensionDevelopmentPath=${workspaceFolder}"], 14 | "outFiles": ["${workspaceFolder}/out/**/*.js"], 15 | "preLaunchTask": "npm: watch" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 René Herrmann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * ESLint configuration for the project. 3 | * 4 | * See https://eslint.style and https://typescript-eslint.io for additional linting options. 5 | */ 6 | // @ts-check 7 | import js from "@eslint/js"; 8 | import tseslint from "typescript-eslint"; 9 | import stylistic from "@stylistic/eslint-plugin"; 10 | 11 | export default tseslint.config( 12 | { 13 | ignores: [".vscode-test", "out"], 14 | }, 15 | js.configs.recommended, 16 | ...tseslint.configs.recommended, 17 | ...tseslint.configs.stylistic, 18 | { 19 | plugins: { 20 | "@stylistic": stylistic, 21 | }, 22 | rules: { 23 | curly: "warn", 24 | "@stylistic/semi": ["warn", "always"], 25 | "@typescript-eslint/no-empty-function": "off", 26 | "@typescript-eslint/naming-convention": [ 27 | "warn", 28 | { 29 | selector: "import", 30 | format: ["camelCase", "PascalCase"], 31 | }, 32 | ], 33 | "@typescript-eslint/no-unused-vars": [ 34 | "error", 35 | { 36 | argsIgnorePattern: "^_", 37 | }, 38 | ], 39 | }, 40 | } 41 | ); 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # npm-dependency-links 2 | 3 | ![Visual Studio Marketplace Version](https://img.shields.io/visual-studio-marketplace/v/herrmannplatz.npm-dependency-links) ![Visual Studio Marketplace Downloads](https://img.shields.io/visual-studio-marketplace/d/herrmannplatz.npm-dependency-links) ![Visual Studio Marketplace Rating](https://img.shields.io/visual-studio-marketplace/r/herrmannplatz.npm-dependency-links.svg) 4 | 5 | Links the depedencies in your package.json to their npmjs.com profile. 6 | 7 | ## Features 8 | 9 | Visit the npmjs.com profile of your dependencies by simply `CMD/Ctrl+click` on the package name in your package.json. 10 | 11 | ![link example](images/promo.gif) 12 | 13 | ## Custom Registry URL 14 | To change the URL, you can set the configuration value in `settings.json` as below: 15 | 16 | `"npmDependencyLinks.registryUrlPattern": "http://myCustomRegistry/{{pkg}}/package?queryparams"` 17 | 18 | Note that `{{pkg}}` is the only part of the pattern that will be set by this extension. E.g., If your pattern is `http://registry.mysite.com/package?name={{pkg}}?version=latest`. If you click on `npm-dependency-links`, then the resulting URL will be `http://registry.mysite.com/package?name=npm-dependency-links?version=latest` 19 | 20 | ## Custom Registry URL (Legacy) 21 | You can also pass custom registry url. For example, if you are using verdaccio. Default value is `https://www.npmjs.com/package/`. 22 | 23 | To change the url, you need to set this configuration value in `settings.json` 24 | 25 | `"npmDependencyLinks.registryUrl": "http://myCustomRegistry/"` 26 | 27 | _Note: Don't forget to put `"/"` in the end._ 28 | 29 | ## Requirements 30 | 31 | Visual Studio Code > 1.74.0 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm-dependency-links", 3 | "displayName": "npm Dependency Links", 4 | "description": "Go to npm site of your dependencies", 5 | "version": "1.3.0", 6 | "publisher": "herrmannplatz", 7 | "icon": "npm_icon.png", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/herrmannplatz/npm-dependency-links.git" 11 | }, 12 | "engines": { 13 | "vscode": "^1.74.0" 14 | }, 15 | "categories": [ 16 | "Other" 17 | ], 18 | "activationEvents": [ 19 | "onLanguage:json" 20 | ], 21 | "main": "./out/extension.js", 22 | "scripts": { 23 | "vscode:prepublish": "npm run compile", 24 | "compile": "tsc -p ./", 25 | "lint": "eslint", 26 | "watch": "tsc -watch -p ./" 27 | }, 28 | "contributes": { 29 | "configuration": { 30 | "type": "object", 31 | "title": "NPM Dependency Links Configuration", 32 | "properties": { 33 | "npmDependencyLinks.registryUrl": { 34 | "type": "string", 35 | "default": "https://www.npmjs.com/package/", 36 | "description": "Registry URL to be used for package link." 37 | }, 38 | "npmDependencyLinks.registryUrlPattern": { 39 | "type": "string", 40 | "default": "", 41 | "markdownDescription": "Registry URL pattern to be used for package link.\n Example: `http://myCustomRegistry/{{pkg}}/package?queryparams`. \nTakes priority over `#npmDependencyLinks.registryUrl#` if set." 42 | } 43 | } 44 | } 45 | }, 46 | "devDependencies": { 47 | "@eslint/js": "^9.13.0", 48 | "@stylistic/eslint-plugin": "^2.9.0", 49 | "@types/node": "^20", 50 | "@types/vscode": "^1.73.0", 51 | "eslint": "^9.13.0", 52 | "typescript": "^5.7.2", 53 | "typescript-eslint": "^8.16.0" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | function buildLinkFromPattern(line: vscode.TextLine, lineIndex: number, packageName: string) { 4 | const startCharacter = line.text.indexOf(packageName); 5 | const endCharacter = startCharacter + packageName.length; 6 | const linkRange = new vscode.Range(lineIndex, startCharacter, lineIndex, endCharacter); 7 | const registryUrlPattern = vscode.workspace.getConfiguration('npmDependencyLinks').registryUrlPattern; 8 | const registryUrl = registryUrlPattern.replace('{{pkg}}', packageName); 9 | const linkUri = vscode.Uri.parse(registryUrl); 10 | 11 | return new vscode.DocumentLink(linkRange, linkUri); 12 | } 13 | 14 | function shouldUseUrlPattern() { 15 | return !!vscode.workspace.getConfiguration('npmDependencyLinks').registryUrlPattern; 16 | } 17 | 18 | function buildLink(line: vscode.TextLine, lineIndex: number, packageName: string) { 19 | if (shouldUseUrlPattern()) { 20 | return buildLinkFromPattern(line, lineIndex, packageName); 21 | } 22 | 23 | const startCharacter = line.text.indexOf(packageName); 24 | const endCharacter = startCharacter + packageName.length; 25 | const linkRange = new vscode.Range(lineIndex, startCharacter, lineIndex, endCharacter); 26 | const registryUrl = vscode.workspace.getConfiguration('npmDependencyLinks').registryUrl; 27 | const linkUri = vscode.Uri.parse(`${registryUrl}${packageName}`); 28 | return new vscode.DocumentLink(linkRange, linkUri); 29 | } 30 | 31 | exports.activate = function (context: vscode.ExtensionContext) { 32 | const disposable = vscode.languages.registerDocumentLinkProvider({ language: 'json', pattern: '**/package.json' }, { 33 | provideDocumentLinks(document) { 34 | const links = []; 35 | let lineIndex = 0; 36 | let shouldCheckForDependency = false; 37 | 38 | while (lineIndex < document.lineCount) { 39 | const line = document.lineAt(lineIndex); 40 | 41 | if (shouldCheckForDependency) { 42 | // no need to check for dependencies if block ended 43 | if (line.text.includes('}')) { 44 | shouldCheckForDependency = false; 45 | } else { 46 | // find dependency 47 | const matches = line.text.match(/"(.*?)"/); 48 | 49 | if (matches) { 50 | links.push(buildLink(line, lineIndex, matches[1])); 51 | } 52 | } 53 | 54 | } else { 55 | // check if we are in a dependencies block 56 | shouldCheckForDependency = /"(.*?)dependencies"/i.test(line.text); 57 | } 58 | 59 | lineIndex += 1; 60 | } 61 | 62 | return links; 63 | } 64 | }); 65 | 66 | context.subscriptions.push(disposable); 67 | }; 68 | --------------------------------------------------------------------------------