├── .DS_Store ├── .eslintrc.js ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── SECURITY.md ├── assets ├── build.png ├── install.png ├── start.png └── test.png ├── build └── pipeline.yml ├── images └── touchbar-support.png ├── issues.github-issues ├── npm_icon.png ├── package-lock.json ├── package.json ├── src └── main.ts ├── test ├── extension.test.ts ├── index.ts ├── test-includeDirectories │ ├── .vscode │ │ ├── settings.json │ │ └── tasks.json │ ├── folder1 │ │ ├── package-lock.json │ │ ├── package.json │ │ └── test.ts │ ├── folder2 │ │ ├── package-lock.json │ │ └── package.json │ ├── package-lock.json │ ├── package.json │ └── test.md └── test-multiroot │ ├── folder1 │ ├── package-lock.json │ └── package.json │ └── test-multiroot.code-workspace ├── tsconfig.json ├── typings └── tree-kill.d.ts └── webpack.config.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-npm-scripts/11ad6e3f34dbde4560794c3ceea58f8e3fd795d8/.DS_Store -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "parser": "@typescript-eslint/parser", 8 | "parserOptions": { 9 | "sourceType": "module" 10 | }, 11 | "plugins": [ 12 | "@typescript-eslint" 13 | ], 14 | "rules": { 15 | "@typescript-eslint/class-name-casing": "error", 16 | "@typescript-eslint/member-delimiter-style": [ 17 | "error", 18 | { 19 | "multiline": { 20 | "delimiter": "semi", 21 | "requireLast": true 22 | }, 23 | "singleline": { 24 | "delimiter": "semi", 25 | "requireLast": false 26 | } 27 | } 28 | ], 29 | "@typescript-eslint/semi": [ 30 | "error", 31 | "always" 32 | ], 33 | "curly": "error", 34 | "eqeqeq": [ 35 | "error", 36 | "always" 37 | ], 38 | "no-redeclare": "error", 39 | "no-unused-expressions": "error", 40 | "prefer-const": "error" 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | out 4 | assets/.DS_Store -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.1.0", 4 | "configurations": [ 5 | { 6 | "name": "Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 11 | "stopOnEntry": false, 12 | "sourceMaps": true, 13 | "outFiles": [ 14 | "${workspaceFolder}/dist/**/*.js" 15 | ], 16 | "preLaunchTask": "npm: webpack" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | "editor.tabSize": 4, 10 | "editor.insertSpaces": false, 11 | "files.trimTrailingWhitespace": true, 12 | "files.autoSave": "afterDelay"} -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "webpack", 7 | "problemMatcher": "$tsc-watch", 8 | "isBackground": true, 9 | "presentation": { 10 | "reveal": "never" 11 | }, 12 | "group": { 13 | "kind": "build", 14 | "isDefault": true 15 | } 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | typings/** 3 | out/test/** 4 | test/** 5 | build/** 6 | src/** 7 | **/*.map 8 | .gitignore 9 | tsconfig.json 10 | vsc-extension-quickstart.md 11 | issues.github-issues -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # vscode-npm-scripts Changelog 2 | - 0.3.29 Address github reported vulnerabilities. 3 | - 0.3.28 Deprecate the extension and announce it in the README 4 | - 0.3.26 5 | - [#171](https://github.com/Microsoft/vscode-npm-scripts/issues/171) Change the keybindings to not use the R as their chording character 6 | - [#172](https://github.com/Microsoft/vscode-npm-scripts/issues/172) Remove support for module validation 7 | - [#174](https://github.com/Microsoft/vscode-npm-scripts/issues/174) The terminal's cmd+k keybinding doesn't work 8 | - 0.3.25 Address github reported vulnerabilities. 9 | - 0.3.24 Update the `thirdpartynotices.txt` file. 10 | - 0.3.23 Address github reported vulnerabilities. 11 | - 0.3.22 Address github reported vulnerabilities. 12 | - 0.3.21 Address github reported vulnerabilities. 13 | - 0.3.20 Define the required trust level. 14 | - 0.3.19 Address github reported vulnerabilities. 15 | - 0.3.18 Add the `npm outdated` command, thanks to https://github.com/joet3ch. 16 | - 0.3.17 Address github reported vulnerabilities. 17 | - 0.3.16 Update dependencies and fix the CVE link below. 18 | - 0.3.15 Fix [CVE-2021-26700](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-26700) 19 | - 0.3.14 Add badges to README.md 20 | - 0.3.13 Fix for [#129](https://github.com/Microsoft/vscode-npm-scripts/issues/129) Add !terminalFocus to keyboard extensions that use Ctrl + R 21 | - 0.3.12 Address github reported vulnerabilities. 22 | - 0.3.11 Address github reported vulnerabilities. Migrate from tslint to eslint. 23 | - 0.3.10 Address github reported vulnerabilities. 24 | - 0.3.9 Address github reported vulnerabilities. 25 | - 0.3.8 Address github reported vulnerabilities. 26 | - 0.3.7 Add setting `npm.enableTouchbar` to control whether npm commands should be shown in the touchbar. 27 | - 0.3.6 Add Macbook-Pro's touch bar support 28 | - 0.3.5 add support for running `npm audit` [#62](https://github.com/Microsoft/vscode-npm-scripts/issues/62) Handle no scripts found gracefully 29 | - 0.3.4 fix for [#60](https://github.com/Microsoft/vscode-npm-scripts/issues/60) Handle no scripts found gracefully 30 | - 0.3.3 fix for [#46](https://github.com/Microsoft/vscode-npm-scripts/issues/46) Cannot read property 'uri' of undefined 31 | - 0.3.1 fix for [#44](https://github.com/Microsoft/vscode-npm-scripts/issues/44) Exception when the workspace contains a root folder with another scheme than 'file' 32 | - 0.3.0 support multiple root folders 33 | - 0.2.1 fix for [#32716](https://github.com/Microsoft/vscode/issues/32716) Duplicate setting npm.runSilent 34 | - 0.2.0 fix for [#38](https://github.com/Microsoft/vscode-npm-scripts/issues/38) After closing npm in terminal I can't run command anymore 35 | - 0.1.9 bug fixes do not validate `yarn` managed modules with `npm` [#34](https://github.com/Microsoft/vscode-npm-scripts/issues/34) 36 | - 0.1.7/0.1.8 bug fixes 37 | - 0.1.6 Fully support the `includeDirectories` setting, issue [#24](https://github.com/Microsoft/vscode-npm-scripts/issues/24) 38 | - 0.1.4/0.1.5 Handle the case that npm is not installed more gracefully 39 | - 0.1.3 Guard against invalid package.json files issue [#77](https://github.com/Microsoft/vscode-npm-scripts/issues/77) 40 | - 0.1.0 added support for validating module dependencies 41 | - 0.0.21 added command to run `npm start`. 42 | - 0.0.20 when commands are run in the terminal, then the **integrated terminal** is used. 43 | - 0.0.16 added `npm install` to the context menu on `package.json` in the explorer. 44 | - 0.0.15 added setting to run npm commands with `--silent`. 45 | - 0.0.15 tweaks to the README so that the extension is found when searching for node. 46 | - 0.0.14 added command to terminate a running script 47 | - 0.0.13 save workspace before running scripts, added command to run `npm run build` 48 | - 0.0.12 added support for `npm.useRootDirectory` 49 | - 0.0.11 added command to run `npm test`. 50 | - 0.0.7 adding an icon and changed the display name to 'npm Script Runner'. 51 | - 0.0.4 the keybinding was changed from `R` to `N` to avoid conflicts with the default `workbench.action.files.newUntitledFile` command. 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Microsoft 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node npm 2 | 3 | ![vscode version](https://vsmarketplacebadge.apphb.com/version/eg2.vscode-npm-script.svg) 4 | ![number of installs](https://vsmarketplacebadge.apphb.com/installs/eg2.vscode-npm-script.svg) 5 | ![average user rating](https://vsmarketplacebadge.apphb.com/rating/eg2.vscode-npm-script.svg) 6 | ![license](https://img.shields.io/github/license/microsoft/vscode-npm-scripts.svg) 7 | 8 | > **❗IMPORTANT**: This extension has been deprecated. Support for running npm scripts is now provided by VS Code. You can run npm scripts as tasks using [task auto detection](https://code.visualstudio.com/Docs/editor/tasks#_task-autodetection) or from the [npm scripts explorer](https://code.visualstudio.com/docs/getstarted/tips-and-tricks#_run-npm-scripts-as-tasks-from-the-explorer). 9 | 10 | This extension supports running npm scripts defined in the `package.json` file. 11 | ## Commands 12 | 13 | Commands for running scripts are available the `npm` category. 14 | 15 | - Run a script (`npm run-script`) defined in the `package.json` by picking a script 16 | defined in the `scripts` section of the `package.json`. 17 | - Rerun the last npm script you have executed using this extension. 18 | - Run npm install, also available in the context menu of the explorer when the `package.json` file 19 | - Terminate a running script 20 | 21 | The scripts can be run either in the integrated terminal or an output window. 22 | 23 | ## Touch bar 24 | 25 | Support for Macbook Pro touch bar. You can run the following commands: 26 | 27 | - npm install 28 | - npm start 29 | - npm test 30 | - npm build 31 | 32 | ![touch bar support](images/touchbar-support.png) 33 | 34 | ## Settings 35 | 36 | - `npm.runInTerminal` defines whether the command is run 37 | in a terminal window or whether the output form the command is shown in the `Output` window. The default is to show the output in the terminal. 38 | - `npm.includeDirectories` define additional directories that include a `package.json`. 39 | - `npm.useRootDirectory` define whether the root directory of the workspace should be ignored, the default is `false`. 40 | - `npm.runSilent` run npm commands with the `--silent` option, the default is `false`. 41 | - `npm.bin` custom npm bin name, the default is `npm`. 42 | - `npm.enableTouchbar` Enable the npm scripts on macOS touchbar. 43 | - `npm.oldKeybindings.enable` Enable the original npm keybindings that start with `cmd/ctrl R` 44 | 45 | ### Example 46 | 47 | ```javascript 48 | { 49 | "npm.runInTerminal": false, 50 | "npm.includeDirectories": [ 51 | "subdir1/path", 52 | "subdir2/path" 53 | ] 54 | } 55 | ``` 56 | 57 | ## Keyboard Shortcuts 58 | 59 | This extension originally defined a chording keyboard shortcut for the `R` key. This has resulted in conflicts with the keybindings provided by VS Code and has caused frustration. To avoid these conflicts the keybindings have been changed to use the existing chording shortcut starting with the `K` key. The following table shows the default key bindings that can always be changed, see the [customization](https://code.visualstudio.com/docs/customization/keybindings) documentation. 60 | 61 | | Command | Old | New | 62 | | ----------- | ----------- |-----------| 63 | | Rerun last script | `CMD+R R` | `CMD+K L` | 64 | | Select a script to run | `CMD+R SHIFT+R` | `CMD+K SHIFT+R` | 65 | | Terminate the running script | `CMD+R SHIFT+X` | `CMD+K SHIFT+X` | 66 | | Run the test script | `CMD+R T` | `CMD+K T` | 67 | 68 | If you prefer the old keybindings starting with `R` you can define the setting `npm.oldKeybindings.enable` to `true`. 69 | 70 | [vs-url]: https://marketplace.visualstudio.com/items?itemName=eg2.vscode-npm-script 71 | [vs-image]: https://vsmarketplacebadge.apphb.com/version/eg2.vscode-npm-script.svg 72 | [install-url]: https://vsmarketplacebadge.apphb.com/installs/eg2.vscode-npm-script.svg 73 | [rate-url]: https://vsmarketplacebadge.apphb.com/rating/eg2.vscode-npm-script.svg 74 | [license-url]: https://img.shields.io/github/license/microsoft/vscode-npm-scripts.svg 75 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /assets/build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-npm-scripts/11ad6e3f34dbde4560794c3ceea58f8e3fd795d8/assets/build.png -------------------------------------------------------------------------------- /assets/install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-npm-scripts/11ad6e3f34dbde4560794c3ceea58f8e3fd795d8/assets/install.png -------------------------------------------------------------------------------- /assets/start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-npm-scripts/11ad6e3f34dbde4560794c3ceea58f8e3fd795d8/assets/start.png -------------------------------------------------------------------------------- /assets/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-npm-scripts/11ad6e3f34dbde4560794c3ceea58f8e3fd795d8/assets/test.png -------------------------------------------------------------------------------- /build/pipeline.yml: -------------------------------------------------------------------------------- 1 | name: $(Date:yyyyMMdd)$(Rev:.r) 2 | 3 | trigger: 4 | branches: 5 | include: 6 | - main 7 | pr: none 8 | 9 | resources: 10 | repositories: 11 | - repository: templates 12 | type: github 13 | name: microsoft/vscode-engineering 14 | ref: main 15 | endpoint: Monaco 16 | 17 | parameters: 18 | - name: publishExtension 19 | displayName: 🚀 Publish Extension 20 | type: boolean 21 | default: false 22 | 23 | extends: 24 | template: azure-pipelines/extension/stable.yml@templates 25 | parameters: 26 | buildSteps: 27 | - script: npm ci 28 | displayName: Install dependencies 29 | 30 | tsa: 31 | config: 32 | areaPath: 'Visual Studio Code Miscellaneous Extensions' 33 | serviceTreeID: 'c8cb03c6-176e-40dd-90a5-518de08666dc' 34 | enabled: true 35 | 36 | publishExtension: ${{ parameters.publishExtension }} 37 | -------------------------------------------------------------------------------- /images/touchbar-support.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-npm-scripts/11ad6e3f34dbde4560794c3ceea58f8e3fd795d8/images/touchbar-support.png -------------------------------------------------------------------------------- /issues.github-issues: -------------------------------------------------------------------------------- 1 | [{"kind":2,"language":"github-issues","value":"$repo=repo:microsoft/vscode-npm-scripts\n$current=milestone:April","outputs":[{"outputKind":3,"data":{"text/html":"
\n
▼ Show More▲ Show Less
0 results, queried {{NOW}}, took 0.0secs
","text/markdown":""}}]},{"kind":1,"language":"github-issues","value":"## Current Issues","outputs":[]},{"kind":2,"language":"github-issues","value":"$repo $current is:issue state:open ","outputs":[{"outputKind":3,"data":{"text/html":"
\n\n
\n\t
\n\t
\n\tBeta versions of a package erroneously show as a downgrade\n\t\n\t\n\t\t\n\t\t • \n\t\tStart Working...\n\t\t\n\t
\n\t\t#83 opened 2/22/2020 by SConaway\n\t
\n\t
\n\t
\n
\n\n
\n\t
\n\t
\n\tUse `yarn` if a `yarn.lock` is detected\n\t\n\t\n\t\t\n\t\t • \n\t\tStart Working...\n\t\t\n\t
\n\t\t#81 opened 8/9/2019 by arcanis\n\t
\n\t
\n\t
\"@egamma\"
\n
\n\n
\n\t
\n\t
\n\tTouchbar commands changes path to home directory when setting `npm.bin`\n\t\n\t\n\t\t\n\t\t • \n\t\tStart Working...\n\t\t\n\t
\n\t\t#78 opened 5/8/2019 by paulbatessonos\n\t
\n\t
\n\t
\"@egamma\"
\n
\n
▼ Show More▲ Show Less
3 results, queried {{NOW}}, took 0.56secs
","text/markdown":"- [#83](https://github.com/microsoft/vscode-npm-scripts/issues/83) Beta versions of a package erroneously show as a downgrade []\n- [#81](https://github.com/microsoft/vscode-npm-scripts/issues/81) Use `yarn` if a `yarn.lock` is detected []- [@egamma](https://github.com/egamma)\n\n- [#78](https://github.com/microsoft/vscode-npm-scripts/issues/78) Touchbar commands changes path to home directory when setting `npm.bin` []- [@egamma](https://github.com/egamma)\n\n"}}]},{"kind":1,"language":"github-issues","value":"## Current Pull Requests","outputs":[]},{"kind":2,"language":"github-issues","value":"$repo $current is:pr state:open ","outputs":[{"outputKind":3,"data":{"text/html":"
\n\n
\n\t
\n\t
\n\t`Module '${moduleName}' the installed version is invalid : `npm install` as a quickfix ?\n\t\n\t\n\t\t\n\t\t • \n\t\tStart Working...\n\t\t\n\t
\n\t\t#109 opened 3/26/2020 by egamma\n\t
\n\t
\n\t
\n
\n\n
\n\t
\n\t
\n\tUse `yarn` if a `yarn.lock` is detected\n\t\n\t\n\t\t\n\t\t • \n\t\tStart Working...\n\t\t\n\t
\n\t\t#108 opened 3/26/2020 by egamma\n\t
\n\t
\n\t
\n
\n
▼ Show More▲ Show Less
2 results, queried {{NOW}}, took 0.35secs
","text/markdown":"- [#109](https://github.com/microsoft/vscode-npm-scripts/pull/109) `Module '${moduleName}' the installed version is invalid : `npm install` as a quickfix ? []\n- [#108](https://github.com/microsoft/vscode-npm-scripts/pull/108) Use `yarn` if a `yarn.lock` is detected []\n"}}]}] -------------------------------------------------------------------------------- /npm_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vscode-npm-scripts/11ad6e3f34dbde4560794c3ceea58f8e3fd795d8/npm_icon.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-npm-script", 3 | "description": "npm support for VS Code", 4 | "displayName": "npm", 5 | "version": "0.3.29", 6 | "publisher": "eg2", 7 | "license": "MIT", 8 | "icon": "npm_icon.png", 9 | "engines": { 10 | "vscode": "^1.66.0" 11 | }, 12 | "capabilities": { 13 | "virtualWorkspaces": { 14 | "supported": false, 15 | "description": "Npm scripts running is not possible in virtual workspaces." 16 | }, 17 | "untrustedWorkspaces": { 18 | "supported": false, 19 | "description": "The extension requires workspace trust because it runs npm scripts from the workspace." 20 | } 21 | }, 22 | "homepage": "https://github.com/Microsoft/vscode-npm-scripts/blob/master/README.md", 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/Microsoft/vscode-npm-scripts.git" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/Microsoft/vscode-npm-scripts/issues" 29 | }, 30 | "categories": [ 31 | "Other" 32 | ], 33 | "keywords": [ 34 | "multi-root ready" 35 | ], 36 | "activationEvents": [ 37 | "onCommand:npm-script.showKeybindingsChangedWarning", 38 | "onCommand:npm-script.install", 39 | "onCommand:npm-script.run", 40 | "onCommand:npm-script.showOutput", 41 | "onCommand:npm-script.rerun-last-script", 42 | "onCommand:npm-script.terminate-script", 43 | "onCommand:npm-script.test", 44 | "onCommand:npm-script.start", 45 | "onCommand:npm-script.audit", 46 | "onCommand:npm-script.outdated", 47 | "onCommand:npm-script.init" 48 | ], 49 | "main": "./dist/extension", 50 | "contributes": { 51 | "commands": [ 52 | { 53 | "command": "npm-script.install", 54 | "title": "Install Dependencies", 55 | "category": "npm", 56 | "icon": "./assets/install.png" 57 | }, 58 | { 59 | "command": "npm-script.terminate-script", 60 | "title": "Terminate Script", 61 | "category": "npm" 62 | }, 63 | { 64 | "command": "npm-script.run", 65 | "title": "Run Script", 66 | "category": "npm" 67 | }, 68 | { 69 | "command": "npm-script.start", 70 | "title": "Start", 71 | "category": "npm", 72 | "icon": "./assets/start.png" 73 | }, 74 | { 75 | "command": "npm-script.showOutput", 76 | "title": "Show Output", 77 | "category": "npm" 78 | }, 79 | { 80 | "command": "npm-script.rerun-last-script", 81 | "title": "Rerun Last Script", 82 | "category": "npm" 83 | }, 84 | { 85 | "command": "npm-script.test", 86 | "title": "Run Test", 87 | "category": "npm", 88 | "icon": "./assets/test.png" 89 | }, 90 | { 91 | "command": "npm-script.build", 92 | "title": "Run Build", 93 | "category": "npm", 94 | "icon": "./assets/build.png" 95 | }, 96 | { 97 | "command": "npm-script.audit", 98 | "title": "Run Audit", 99 | "category": "npm" 100 | }, 101 | { 102 | "command": "npm-script.outdated", 103 | "title": "Run Outdated", 104 | "category": "npm" 105 | }, 106 | { 107 | "command": "npm-script.init", 108 | "title": "Run Init", 109 | "category": "npm" 110 | } 111 | ], 112 | "menus": { 113 | "explorer/context": [ 114 | { 115 | "when": "resourceFilename == 'package.json'", 116 | "command": "npm-script.install", 117 | "group": "navigation@+1" 118 | } 119 | ], 120 | "touchBar": [ 121 | { 122 | "command": "npm-script.install", 123 | "group": "navigation@+1", 124 | "when": "config.npm.enableTouchbar" 125 | }, 126 | { 127 | "command": "npm-script.build", 128 | "group": "navigation@+4", 129 | "when": "config.npm.enableTouchbar" 130 | }, 131 | { 132 | "command": "npm-script.test", 133 | "group": "navigation@+3", 134 | "when": "config.npm.enableTouchbar" 135 | }, 136 | { 137 | "command": "npm-script.start", 138 | "group": "navigation@+2", 139 | "when": "config.npm.enableTouchbar" 140 | } 141 | ] 142 | }, 143 | "keybindings": [ 144 | { 145 | "command": "npm-script.showKeybindingsChangedWarning", 146 | "key": "Ctrl+R", 147 | "mac": "Cmd+R", 148 | "when": "!config.npm.keybindingsChangedWarningShown && !config.npm.oldKeybindings.enable" 149 | }, 150 | { 151 | "command": "npm-script.showOutput", 152 | "key": "Ctrl+R L", 153 | "mac": "Cmd+R L", 154 | "when": "!terminalFocus && config.npm.oldKeybindings.enable" 155 | }, 156 | { 157 | "command": "npm-script.run", 158 | "key": "Ctrl+R Shift+R", 159 | "mac": "Cmd+R Shift+R", 160 | "when": "!terminalFocus && config.npm.oldKeybindings.enable" 161 | }, 162 | { 163 | "command": "npm-script.rerun-last-script", 164 | "key": "Ctrl+R R", 165 | "mac": "Cmd+R R", 166 | "when": "!terminalFocus && config.npm.oldKeybindings.enable" 167 | }, 168 | { 169 | "command": "npm-script.terminate-script", 170 | "key": "Ctrl+R Shift+X", 171 | "mac": "Cmd+R Shift+X", 172 | "when": "!terminalFocus && config.npm.oldKeybindings.enable" 173 | }, 174 | { 175 | "command": "npm-script.test", 176 | "key": "Ctrl+R T", 177 | "mac": "Cmd+R T", 178 | "when": "!terminalFocus && config.npm.oldKeybindings.enable" 179 | }, 180 | { 181 | "command": "npm-script.run", 182 | "key": "Ctrl+K Shift+R", 183 | "mac": "Cmd+K Shift+R", 184 | "when": "!terminalFocus && !config.npm.oldKeybindings.enable" 185 | }, 186 | { 187 | "command": "npm-script.rerun-last-script", 188 | "key": "Ctrl+K L", 189 | "mac": "Cmd+K L", 190 | "when": "!terminalFocus && !config.npm.oldKeybindings.enable" 191 | }, 192 | { 193 | "command": "npm-script.terminate-script", 194 | "key": "Ctrl+K Shift+X", 195 | "mac": "Cmd+K Shift+X", 196 | "when": "!terminalFocus && !config.npm.oldKeybindings.enable" 197 | }, 198 | { 199 | "command": "npm-script.test", 200 | "key": "Ctrl+K T", 201 | "mac": "Cmd+K T", 202 | "when": "!terminalFocus && !config.npm.oldKeybindings.enable" 203 | } 204 | ], 205 | "configuration": { 206 | "type": "object", 207 | "title": "npm Script Runner", 208 | "properties": { 209 | "npm.runInTerminal": { 210 | "type": "boolean", 211 | "default": true, 212 | "description": "Run npm commands in a terminal, otherwise shows the output in the output panel" 213 | }, 214 | "npm.includeDirectories": { 215 | "type": "array", 216 | "scope": "resource", 217 | "default": [], 218 | "description": "Look for 'package.json' files in these directories" 219 | }, 220 | "npm.useRootDirectory": { 221 | "type": "boolean", 222 | "scope": "resource", 223 | "default": true, 224 | "description": "Look for 'package.json' in the root directory of the workspace" 225 | }, 226 | "npm.bin": { 227 | "type": "string", 228 | "default": "npm", 229 | "description": "npm bin name" 230 | }, 231 | "npm.enableTouchbar": { 232 | "type": "boolean", 233 | "scope": "resource", 234 | "default": false, 235 | "description": "Enable npm commands in the macOS touchbar." 236 | }, 237 | "npm.oldKeybindings.enable": { 238 | "type": "boolean", 239 | "scope": "resource", 240 | "default": false, 241 | "description": "Enable the old cmd/ctrl R chording key bindings." 242 | }, 243 | "npm.keybindingsChangedWarningShown": { 244 | "type": "boolean", 245 | "scope": "application", 246 | "default": false, 247 | "description": "Show a warning that the keybindings have changed." 248 | } 249 | } 250 | } 251 | }, 252 | "scripts": { 253 | "vscode:prepublish": "webpack --mode production", 254 | "webpack": "webpack --mode development", 255 | "webpack-dev": "webpack --mode development --watch", 256 | "test": "npm run compile && node ./node_modules/vscode/bin/test", 257 | "lint": "eslint -c .eslintrc.js --ext .ts src/*.ts" 258 | }, 259 | "devDependencies": { 260 | "@types/mocha": "^2.2.32", 261 | "@types/node": "^7.0.43", 262 | "@types/vscode": "^1.66.0", 263 | "@typescript-eslint/eslint-plugin": "^2.18.0", 264 | "@typescript-eslint/parser": "^2.18.0", 265 | "eslint": "^8.0.1", 266 | "ts-loader": "^9.2.6", 267 | "typescript": "^4.4.4", 268 | "webpack": "^5.58.2", 269 | "webpack-cli": "^4.9.1", 270 | "vscode-test": "^1.6.1" 271 | }, 272 | "dependencies": { 273 | "jsonc-parser": "^2.1.0", 274 | "run-in-terminal": "^0.0.2", 275 | "tree-kill": "^1.2.2" 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import * as cp from 'child_process'; 2 | import * as path from 'path'; 3 | import * as fs from 'fs'; 4 | 5 | import { 6 | window, commands, workspace, OutputChannel, ExtensionContext, ViewColumn, 7 | QuickPickItem, Terminal, Uri, ConfigurationTarget, env 8 | } from 'vscode'; 9 | 10 | import { runInTerminal } from 'run-in-terminal'; 11 | import { kill } from 'tree-kill'; 12 | 13 | interface Script extends QuickPickItem { 14 | scriptName: string; 15 | cwd: string | undefined; 16 | execute(): void; 17 | } 18 | 19 | interface Process { 20 | process: cp.ChildProcess; 21 | cmd: string; 22 | } 23 | 24 | class ProcessItem implements QuickPickItem { 25 | constructor(public label: string, public description: string, public pid: number) { } 26 | } 27 | 28 | interface ScriptCommandDescription { 29 | absolutePath: string; 30 | relativePath: string | undefined; // path relative to workspace root, if there is a root 31 | name: string; 32 | cmd: string; 33 | } 34 | 35 | interface CommandArgument { 36 | fsPath: string; 37 | } 38 | 39 | const runningProcesses: Map = new Map(); 40 | 41 | let outputChannel: OutputChannel; 42 | let terminal: Terminal | null = null; 43 | let lastScript: Script | null = null; 44 | 45 | export function activate(context: ExtensionContext) { 46 | registerCommands(context); 47 | 48 | outputChannel = window.createOutputChannel('npm'); 49 | context.subscriptions.push(outputChannel); 50 | 51 | window.onDidCloseTerminal((closedTerminal) => { 52 | if (terminal === closedTerminal) { 53 | terminal = null; 54 | } 55 | }); 56 | 57 | } 58 | 59 | export function deactivate() { 60 | if (terminal) { 61 | terminal.dispose(); 62 | } 63 | } 64 | 65 | function registerCommands(context: ExtensionContext) { 66 | async function showKeybindingsChangedWarning(): Promise { 67 | const configuration = workspace.getConfiguration(); 68 | 69 | // this should not happen since the command should only be available when the setting is false 70 | if (configuration.get("npm.keybindingsChangedWarningShown", false)) { 71 | return; 72 | }; 73 | const learnMore = "Learn More"; 74 | const result = await window.showInformationMessage("The key bindings of the 'npm-scripts' extension have changed!", { 'modal': true}, learnMore); 75 | if (result === learnMore) { 76 | env.openExternal(Uri.parse('https://github.com/microsoft/vscode-npm-scripts#keyboard-shortcuts')); 77 | } 78 | await configuration.update('npm.keybindingsChangedWarningShown', true, ConfigurationTarget.Global); 79 | } 80 | 81 | context.subscriptions.push( 82 | commands.registerCommand('npm-script.install', runNpmInstall), 83 | commands.registerCommand('npm-script.init', runNpmInit), 84 | commands.registerCommand('npm-script.test', runNpmTest), 85 | commands.registerCommand('npm-script.start', runNpmStart), 86 | commands.registerCommand('npm-script.run', runNpmScript), 87 | commands.registerCommand('npm-script.showOutput', showNpmOutput), 88 | commands.registerCommand('npm-script.rerun-last-script', rerunLastScript), 89 | commands.registerCommand('npm-script.build', runNpmBuild), 90 | commands.registerCommand('npm-script.audit', runNpmAudit), 91 | commands.registerCommand('npm-script.outdated', runNpmOutdated), 92 | commands.registerCommand('npm-script.installInOutputWindow', runNpmInstallInOutputWindow), 93 | commands.registerCommand('npm-script.uninstallInOutputWindow', runNpmUninstallInOutputWindow), 94 | commands.registerCommand('npm-script.terminate-script', terminateScript), 95 | commands.registerCommand('npm-script.showKeybindingsChangedWarning', showKeybindingsChangedWarning) 96 | ); 97 | } 98 | 99 | function runNpmCommand(args: string[], cwd: string | undefined, alwaysRunInputWindow = false): void { 100 | if (runSilent()) { 101 | args.push('--silent'); 102 | } 103 | workspace.saveAll().then(() => { 104 | 105 | if (useTerminal() && !alwaysRunInputWindow) { 106 | if (typeof window.createTerminal === 'function') { 107 | runCommandInIntegratedTerminal(args, cwd); 108 | } else { 109 | runCommandInTerminal(args, cwd); 110 | } 111 | } else { 112 | outputChannel.clear(); 113 | runCommandInOutputWindow(args, cwd); 114 | } 115 | }); 116 | } 117 | 118 | function createAllCommand(scriptList: Script[], isScriptCommand: boolean): Script { 119 | return { 120 | label: "All", 121 | description: "Run all " + (isScriptCommand ? "scripts" : "commands") + " listed below", 122 | scriptName: "Dummy", 123 | cwd: undefined, 124 | execute(this: Script) { 125 | for (const s of scriptList) { 126 | // check for null ``cwd to prevent calling the function by itself. 127 | if (s.cwd) { 128 | s.execute(); 129 | } 130 | } 131 | } 132 | }; 133 | } 134 | 135 | function isMultiRoot(): boolean { 136 | if (workspace.workspaceFolders) { 137 | return workspace.workspaceFolders.length > 1; 138 | } 139 | return false; 140 | } 141 | 142 | function pickScriptToExecute(descriptions: ScriptCommandDescription[], command: string[], allowAll = false, alwaysRunInputWindow = false) { 143 | const scriptList: Script[] = []; 144 | const isScriptCommand = command[0] === 'run-script'; 145 | 146 | if (allowAll && descriptions.length > 1) { 147 | scriptList.push(createAllCommand(scriptList, isScriptCommand)); 148 | } 149 | for (const s of descriptions) { 150 | let label = s.name; 151 | if (s.relativePath) { 152 | label = `${s.relativePath} - ${label}`; 153 | } 154 | if (isMultiRoot()) { 155 | const root = workspace.getWorkspaceFolder(Uri.file(s.absolutePath)); 156 | if (root) { 157 | label = `${root.name}: ${label}`; 158 | } 159 | } 160 | scriptList.push({ 161 | label: label, 162 | description: s.cmd, 163 | scriptName: s.name, 164 | cwd: s.absolutePath, 165 | execute(this: Script) { 166 | let script = this.scriptName; 167 | // quote the script name, when it contains white space 168 | if (/\s/g.test(script)) { 169 | script = `"${script}"`; 170 | } 171 | // Create copy of command to ensure that we always get the correct command when the script is rerun. 172 | const cmd = Array.from(command); 173 | if (isScriptCommand) { 174 | lastScript = this; 175 | //Add script name to command array 176 | cmd.push(script); 177 | } 178 | runNpmCommand(cmd, this.cwd, alwaysRunInputWindow); 179 | } 180 | }); 181 | } 182 | 183 | if (scriptList.length === 1) { 184 | scriptList[0].execute(); 185 | return; 186 | } else if (scriptList.length === 0) { 187 | if (isScriptCommand) { 188 | window.showErrorMessage(`Failed to find script with "${command[1]}" command`); 189 | } else { 190 | window.showErrorMessage(`Failed to find handler for "${command[0]}" command`); 191 | } 192 | return; 193 | } 194 | window.showQuickPick(scriptList).then(script => { 195 | if (script) { 196 | script.execute(); 197 | } 198 | }); 199 | } 200 | 201 | /** 202 | * Executes an npm command in a package directory (or in all possible directories). 203 | * @param command Command name. 204 | * @param allowAll Allow to run the command in all possible directory locations, otherwise the user must pick a location. 205 | * @param dirs Array of directories used to determine locations for running the command. 206 | When no argument is passed then `getIncludedDirectories` is used to get list of directories. 207 | */ 208 | function runNpmCommandInPackages(command: string[], allowAll = false, alwaysRunInputWindow = false, dirs?: string[]) { 209 | const descriptions = commandsDescriptions(command, dirs); 210 | if (descriptions.length === 0) { 211 | window.showErrorMessage("No scripts found.", { modal: true }); 212 | return; 213 | } 214 | pickScriptToExecute(descriptions, command, allowAll, alwaysRunInputWindow); 215 | } 216 | 217 | /** 218 | * The first argument in `args` must be the path to the directory where the command will be executed. 219 | */ 220 | function runNpmCommandWithArguments(cmd: string, args: string[]) { 221 | const [dir, ...args1] = args; 222 | runNpmCommand([cmd, ...args1], dir); 223 | } 224 | 225 | function runNpmInstall(arg: CommandArgument) { 226 | let dirs = []; 227 | // Is the command executed from the context menu? 228 | if (arg && arg.fsPath) { 229 | dirs.push(path.dirname(arg.fsPath)); 230 | } else { 231 | dirs = getAllIncludedDirectories(); 232 | } 233 | runNpmCommandInPackages(['install'], true, false, dirs); 234 | } 235 | 236 | function runNpmInstallInOutputWindow(...args: string[]) { 237 | runNpmCommandWithArguments('install', args); 238 | } 239 | 240 | function runNpmUninstallInOutputWindow(...args: string[]) { 241 | runNpmCommandWithArguments('uninstall', args); 242 | } 243 | 244 | function runNpmTest() { 245 | runNpmCommandInPackages(['test'], true); 246 | } 247 | 248 | function runNpmStart() { 249 | runNpmCommandInPackages(['start'], true); 250 | } 251 | 252 | function runNpmBuild() { 253 | runNpmCommandInPackages(['run', 'build'], true); 254 | } 255 | 256 | function runNpmAudit() { 257 | runNpmCommandInPackages(['audit'], true); 258 | } 259 | 260 | function runNpmOutdated() { 261 | runNpmCommandInPackages(['outdated'], true); 262 | } 263 | 264 | function runNpmInit() { 265 | runNpmCommandInPackages(['init'], true); 266 | } 267 | 268 | function runNpmScript(): void { 269 | runNpmCommandInPackages(['run-script'], false); 270 | } 271 | 272 | function rerunLastScript(): void { 273 | if (lastScript) { 274 | lastScript.execute(); 275 | } else { 276 | runNpmScript(); 277 | } 278 | } 279 | 280 | /** 281 | * Adds entries to the `description` argument based on the passed command and the package path. 282 | * The function has two scenarios (based on a given command name): 283 | * - Adds entry with the command, it's name and paths (absolute and relative to workspace). 284 | * - When the command equals to 'run-script' it reads the `package.json` and generates entries: 285 | * - with all script names (when there is no script name defined), 286 | * - with scripts that match the name. 287 | */ 288 | function commandDescriptionsInPackage(param: string[], packagePath: string, descriptions: ScriptCommandDescription[]) { 289 | const absolutePath = packagePath; 290 | const fileUri = Uri.file(absolutePath); 291 | const workspaceFolder = workspace.getWorkspaceFolder(fileUri); 292 | let rootUri: Uri | undefined = undefined; 293 | let relativePath: string | undefined = undefined; 294 | if (workspaceFolder) { 295 | rootUri = workspaceFolder.uri; 296 | relativePath = absolutePath.substring(rootUri.fsPath.length + 1); 297 | } 298 | 299 | const cmd = param[0]; 300 | const name = param[1]; 301 | 302 | if (cmd === 'run-script') { 303 | try { 304 | const fileName = path.join(packagePath, 'package.json'); 305 | const contents = fs.readFileSync(fileName).toString(); 306 | const json = JSON.parse(contents); 307 | if (json.scripts) { 308 | const jsonScripts = json.scripts; 309 | Object.keys(jsonScripts).forEach(key => { 310 | if (!name || key === name) { 311 | descriptions.push({ 312 | absolutePath: absolutePath, 313 | relativePath: relativePath, 314 | name: `${key}`, 315 | cmd: `${cmd} ${jsonScripts[key]}` 316 | }); 317 | } 318 | }); 319 | } 320 | } catch (e) { 321 | } 322 | } else { 323 | descriptions.push({ 324 | absolutePath: absolutePath, 325 | relativePath: relativePath, 326 | name: `${cmd}`, 327 | cmd: `npm ${cmd}` 328 | }); 329 | } 330 | } 331 | 332 | function commandsDescriptions(command: string[], dirs: string[] | undefined): ScriptCommandDescription[] { 333 | if (!dirs) { 334 | dirs = getAllIncludedDirectories(); 335 | } 336 | const descriptions: ScriptCommandDescription[] = []; 337 | dirs.forEach(dir => commandDescriptionsInPackage(command, dir, descriptions)); 338 | return descriptions; 339 | } 340 | 341 | function showNpmOutput(): void { 342 | outputChannel.show(ViewColumn.Three); 343 | } 344 | 345 | function terminateScript(): void { 346 | if (useTerminal()) { 347 | window.showInformationMessage('Killing is only supported when the setting "runInTerminal" is "false"'); 348 | } else { 349 | const items: ProcessItem[] = []; 350 | 351 | runningProcesses.forEach((value) => { 352 | items.push(new ProcessItem(value.cmd, `kill the process ${value.process.pid}`, value.process.pid)); 353 | }); 354 | 355 | window.showQuickPick(items).then((value) => { 356 | if (value) { 357 | outputChannel.appendLine(''); 358 | outputChannel.appendLine(`Killing process ${value.label} (pid: ${value.pid})`); 359 | outputChannel.appendLine(''); 360 | kill(value.pid, 'SIGTERM'); 361 | } 362 | }); 363 | } 364 | } 365 | 366 | function runCommandInOutputWindow(args: string[], cwd: string | undefined) { 367 | const cmd = getNpmBin() + ' ' + args.join(' '); 368 | const p = cp.exec(cmd, { cwd: cwd, env: process.env }); 369 | 370 | runningProcesses.set(p.pid, { process: p, cmd: cmd }); 371 | 372 | p.stderr.on('data', (data: string) => { 373 | outputChannel.append(data); 374 | }); 375 | p.stdout.on('data', (data: string) => { 376 | outputChannel.append(data); 377 | }); 378 | p.on('exit', (_code: number, signal: string) => { 379 | runningProcesses.delete(p.pid); 380 | 381 | if (signal === 'SIGTERM') { 382 | outputChannel.appendLine('Successfully killed process'); 383 | outputChannel.appendLine('-----------------------'); 384 | outputChannel.appendLine(''); 385 | } else { 386 | outputChannel.appendLine('-----------------------'); 387 | outputChannel.appendLine(''); 388 | } 389 | }); 390 | 391 | showNpmOutput(); 392 | } 393 | 394 | function runCommandInTerminal(args: string[], cwd: string | undefined): void { 395 | runInTerminal(getNpmBin(), args, { cwd: cwd, env: process.env }); 396 | } 397 | 398 | function runCommandInIntegratedTerminal(args: string[], cwd: string | undefined): void { 399 | const cmd_args = Array.from(args); 400 | 401 | if (!terminal) { 402 | terminal = window.createTerminal('npm'); 403 | } 404 | terminal.show(); 405 | if (cwd) { 406 | // Replace single backslash with double backslash. 407 | const textCwd = cwd.replace(/\\/g, '\\\\'); 408 | terminal.sendText(['cd', `"${textCwd}"`].join(' ')); 409 | } 410 | cmd_args.splice(0, 0, getNpmBin()); 411 | terminal.sendText(cmd_args.join(' ')); 412 | } 413 | 414 | function useTerminal() { 415 | return workspace.getConfiguration('npm')['runInTerminal']; 416 | } 417 | 418 | function runSilent() { 419 | return workspace.getConfiguration('npm')['runSilent']; 420 | } 421 | 422 | function getAllIncludedDirectories(): string[] { 423 | const allDirs: string[] = []; 424 | 425 | const folders = workspace.workspaceFolders; 426 | 427 | if (!folders) { 428 | return allDirs; 429 | } 430 | 431 | for (let i = 0; i < folders.length; i++) { 432 | if (folders[i].uri.scheme === 'file') { 433 | const dirs = getIncludedDirectories(folders[i].uri); 434 | allDirs.push(...dirs); 435 | } 436 | } 437 | return allDirs; 438 | } 439 | 440 | function getIncludedDirectories(workspaceRoot: Uri): string[] { 441 | const dirs: string[] = []; 442 | 443 | if (workspace.getConfiguration('npm', workspaceRoot)['useRootDirectory'] !== false) { 444 | dirs.push(workspaceRoot.fsPath); 445 | } 446 | 447 | if (workspace.getConfiguration('npm', workspaceRoot)['includeDirectories'].length > 0) { 448 | for (const dir of workspace.getConfiguration('npm', workspaceRoot)['includeDirectories']) { 449 | dirs.push(path.join(workspaceRoot.fsPath, dir)); 450 | } 451 | } 452 | return dirs; 453 | } 454 | 455 | function getNpmBin() { 456 | return workspace.getConfiguration('npm')['bin'] || 'npm'; 457 | } 458 | -------------------------------------------------------------------------------- /test/extension.test.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Note: This example test is leveraging the Mocha test framework. 3 | // Please refer to their documentation on https://mochajs.org/ for help. 4 | // 5 | 6 | // The module 'assert' provides assertion methods from node 7 | import * as assert from 'assert'; 8 | 9 | // You can import and use all API from the 'vscode' module 10 | // as well as import your extension to test it 11 | // import * as vscode from 'vscode'; 12 | // import * as myExtension from '../src/main'; 13 | 14 | // Defines a Mocha test suite to group tests of similar kind together 15 | suite("Extension Tests", () => { 16 | 17 | // Defines a Mocha unit test 18 | test("Something 1", () => { 19 | assert.equal(-1, [1, 2, 3].indexOf(5)); 20 | assert.equal(-1, [1, 2, 3].indexOf(0)); 21 | }); 22 | }); -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 3 | // 4 | // This file is providing the test runner to use when running extension tests. 5 | // By default the test runner in use is Mocha based. 6 | // 7 | // You can provide your own test runner if you want to override it by exporting 8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 9 | // host can call to run the tests. The test runner is expected to use console.log 10 | // to report the results back to the caller. When the tests are finished, return 11 | // a possible error to the callback or null if none. 12 | 13 | var testRunner = require('vscode/lib/testrunner'); 14 | 15 | // You can directly control Mocha options by uncommenting the following lines 16 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info 17 | testRunner.configure({ 18 | ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) 19 | useColors: true // colored output from test results 20 | }); 21 | 22 | module.exports = testRunner; -------------------------------------------------------------------------------- /test/test-includeDirectories/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "npm.includeDirectories": ["folder1", "folder2"] 4 | } -------------------------------------------------------------------------------- /test/test-includeDirectories/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "test", 9 | "path": "folder2/", 10 | "problemMatcher": [] 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /test/test-includeDirectories/folder1/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-npm-script-test-include-directories", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-styles": { 8 | "version": "3.2.1", 9 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 10 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 11 | "requires": { 12 | "color-convert": "1.9.1" 13 | } 14 | }, 15 | "chalk": { 16 | "version": "2.4.1", 17 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 18 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 19 | "requires": { 20 | "ansi-styles": "3.2.1", 21 | "escape-string-regexp": "1.0.5", 22 | "supports-color": "5.4.0" 23 | } 24 | }, 25 | "color-convert": { 26 | "version": "1.9.1", 27 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", 28 | "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", 29 | "requires": { 30 | "color-name": "1.1.3" 31 | } 32 | }, 33 | "color-name": { 34 | "version": "1.1.3", 35 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 36 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 37 | }, 38 | "escape-string-regexp": { 39 | "version": "1.0.5", 40 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 41 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 42 | }, 43 | "has-flag": { 44 | "version": "3.0.0", 45 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 46 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 47 | }, 48 | "supports-color": { 49 | "version": "5.4.0", 50 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 51 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 52 | "requires": { 53 | "has-flag": "3.0.0" 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/test-includeDirectories/folder1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-npm-script-test-include-directories", 3 | "version": "1.0.0", 4 | "description": "Testing npm.includeDirectories part 1", 5 | "scripts": { 6 | "test": "npm config get tag", 7 | "lint": "echo lint test.ts", 8 | "folder1-shell": "npm config get shell", 9 | "postinstall": "echo testincludeDirectories/folder1" 10 | }, 11 | "dependencies": { 12 | "chalk": "^2.4.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/test-includeDirectories/folder1/test.ts: -------------------------------------------------------------------------------- 1 | console.log(); -------------------------------------------------------------------------------- /test/test-includeDirectories/folder2/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-npm-script-test-include-directories", 3 | "lockfileVersion": 1 4 | } 5 | -------------------------------------------------------------------------------- /test/test-includeDirectories/folder2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-npm-script-test-include-directories", 3 | "description": "Testing npm.includeDirectories part 2", 4 | "scripts": { 5 | "test": "npm config get node-version", 6 | "folder1-shell": "npm config get editor", 7 | "postinstall": "echo test-includeDirectories/folder2" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/test-includeDirectories/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-npm-script-test-include-directories", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-styles": { 8 | "version": "3.2.1", 9 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 10 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 11 | "requires": { 12 | "color-convert": "1.9.1" 13 | } 14 | }, 15 | "chalk": { 16 | "version": "2.4.1", 17 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 18 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 19 | "requires": { 20 | "ansi-styles": "3.2.1", 21 | "escape-string-regexp": "1.0.5", 22 | "supports-color": "5.4.0" 23 | } 24 | }, 25 | "color-convert": { 26 | "version": "1.9.1", 27 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", 28 | "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", 29 | "requires": { 30 | "color-name": "1.1.3" 31 | } 32 | }, 33 | "color-name": { 34 | "version": "1.1.3", 35 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 36 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 37 | }, 38 | "escape-string-regexp": { 39 | "version": "1.0.5", 40 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 41 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 42 | }, 43 | "has-flag": { 44 | "version": "3.0.0", 45 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 46 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 47 | }, 48 | "supports-color": { 49 | "version": "5.4.0", 50 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 51 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 52 | "requires": { 53 | "has-flag": "3.0.0" 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/test-includeDirectories/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-npm-script-test-include-directories", 3 | "version": "1.0.0", 4 | "description": "Testing npm.includeDirectories part 1", 5 | "scripts": { 6 | "testA": "npm config get tag", 7 | "lintA": "echo lint", 8 | "folder1-shellA": "npm config get shell", 9 | "postinstall": "echo test-includeDirectories" 10 | }, 11 | "dependencies": { 12 | "chalk": "^2.4.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/test-includeDirectories/test.md: -------------------------------------------------------------------------------- 1 | Each manual Test Case assumes that VS Code is opened on the folder `test/testIncludeDirectories`. 2 | 3 | ## Test Case 1: 4 | 5 | Steps: 6 | 7 | 1. Press F1. 8 | 2. Type: `npm run test`. 9 | 3. Press Enter. 10 | 4. There should be 3 visible entries: 11 | - All 12 | - folder1: test 13 | - folder2: test 14 | 5. Select All. 15 | 16 | Expected: 17 | Based on the npm configuration, the output will be different, but the terminal should return two npm configuration values. 18 | - `npm config get tag` (For example `latest`). 19 | - `npm config get node-version` (For example `6.9.1`); 20 | 21 | ## Test Case 2: 22 | 23 | Steps: 24 | 1. Press F1. 25 | 2. Type: `npm run test`. 26 | 3. Press Enter. 27 | 4. There should be 3 visible entries: 28 | - All 29 | - folder1: test 30 | - folder2: test 31 | 5. Choose "folder1: test". 32 | 33 | Expected: 34 | Based on the npm configuration the output will be different, but the terminal should return value from `npm config get tag`, e.g., `latest`. 35 | 36 | ## Test Case 3: 37 | 38 | Steps: 39 | 1. Press F1. 40 | 2. Type: `npm run test`. 41 | 3. Press Enter. 42 | 4. There should be 3 visible entries: 43 | - All 44 | - folder1: test 45 | - folder2: test 46 | 5. Choose "folder2: test". 47 | 48 | Expected: 49 | Based on the npm configuration the output will be different, but the terminal should return the result from `npm config get node-version`, e.g., `6.9.1`. 50 | 51 | ## Test Case 4: 52 | 53 | 1. Press F1. 54 | 2. Type: `npm run script`. 55 | 3. Press Enter. 56 | 4. There should be 3 visible entries: 57 | - folder1: test 58 | - folder1: folder1-shell 59 | - folder2: test 60 | - folder2: folder2-shell 61 | 5. Choose "folder1: folder1-shell" 62 | 63 | Expected: 64 | Based on the npm configuration the output will be different, but the terminal should return value from `npm config get shell`, e.g. `/bin/bash`. 65 | 66 | ## Test Case 5 67 | 68 | 1. Open the file `folder1/package.json` 69 | 70 | Expected: 71 | 72 | There is a warning that the module `chalk` isn't installed. 73 | 74 | 2. Select the quick fix action shown in light bulb `npm install` 75 | 3. Use the quick fix action `npm validate` 76 | 77 | Expected: 78 | 79 | The warning for the missing module `chalk` no longer shows up. 80 | -------------------------------------------------------------------------------- /test/test-multiroot/folder1/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-npm-script-test-include-directories", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-styles": { 8 | "version": "3.2.1", 9 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 10 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 11 | "requires": { 12 | "color-convert": "1.9.1" 13 | } 14 | }, 15 | "chalk": { 16 | "version": "2.4.1", 17 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 18 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 19 | "requires": { 20 | "ansi-styles": "3.2.1", 21 | "escape-string-regexp": "1.0.5", 22 | "supports-color": "5.4.0" 23 | } 24 | }, 25 | "color-convert": { 26 | "version": "1.9.1", 27 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", 28 | "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", 29 | "requires": { 30 | "color-name": "1.1.3" 31 | } 32 | }, 33 | "color-name": { 34 | "version": "1.1.3", 35 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 36 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 37 | }, 38 | "escape-string-regexp": { 39 | "version": "1.0.5", 40 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 41 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 42 | }, 43 | "has-flag": { 44 | "version": "3.0.0", 45 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 46 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 47 | }, 48 | "supports-color": { 49 | "version": "5.4.0", 50 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 51 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 52 | "requires": { 53 | "has-flag": "3.0.0" 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/test-multiroot/folder1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-npm-script-test-include-directories", 3 | "version": "1.0.0", 4 | "description": "Testing npm.includeDirectories part 1", 5 | "scripts": { 6 | "test": "npm config get tag", 7 | "lint": "echo lint", 8 | "folder1-shell": "npm config get shell", 9 | "postinstall": "echo folder1" 10 | }, 11 | "dependencies": { 12 | "chalk": "^2.4.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/test-multiroot/test-multiroot.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "folder1" 5 | }, 6 | { 7 | "path": "C:\\Users\\egamma\\Projects\\vscode-npm-scripts\\test\\test-includeDirectories" 8 | } 9 | ], 10 | "settings": {} 11 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "noUnusedParameters": true, 11 | "noUnusedLocals": true, 12 | "strictNullChecks": false, 13 | "noFallthroughCasesInSwitch": true, 14 | "allowUnreachableCode": false, 15 | "noImplicitAny": true, 16 | "noImplicitReturns": true, 17 | "alwaysStrict": true, 18 | "noImplicitThis": true 19 | }, 20 | "exclude": [ 21 | "node_modules" 22 | ] 23 | } -------------------------------------------------------------------------------- /typings/tree-kill.d.ts: -------------------------------------------------------------------------------- 1 | declare module "tree-kill" { 2 | export function kill(pid: number, signal?: string, callback?: (err:any)=>any):void; 3 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | //@ts-check 7 | 8 | 'use strict'; 9 | 10 | const path = require('path'); 11 | 12 | /**@type {import('webpack').Configuration}*/ 13 | const config = { 14 | target: 'node', // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ 15 | 16 | entry: './src/main.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ 17 | output: { // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ 18 | path: path.resolve(__dirname, 'dist'), 19 | filename: 'extension.js', 20 | libraryTarget: "commonjs2", 21 | devtoolModuleFilenameTemplate: "../[resource-path]", 22 | }, 23 | devtool: 'source-map', 24 | externals: { 25 | vscode: "commonjs vscode" // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ 26 | }, 27 | resolve: { // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader 28 | extensions: ['.ts', '.js'] 29 | }, 30 | module: { 31 | rules: [{ 32 | test: /\.ts$/, 33 | exclude: /node_modules/, 34 | use: [{ 35 | loader: 'ts-loader', 36 | } 37 | ] 38 | }] 39 | }, 40 | } 41 | 42 | module.exports = config; 43 | --------------------------------------------------------------------------------