├── .gitignore ├── .DS_Store ├── logo ├── index.jpg └── index.png ├── .gitattributes ├── screenshot ├── 1.gif ├── 2.png └── 3.png ├── delete-node-modules-1.0.4.vsix ├── .vscodeignore ├── .vscode ├── extensions.json ├── tasks.json ├── settings.json └── launch.json ├── out ├── utils.js ├── sort-files.js ├── test │ ├── runTest.js.map │ ├── suite │ │ ├── extension.test.js.map │ │ ├── extension.test.js │ │ ├── index.js.map │ │ └── index.js │ └── runTest.js ├── find-parent-modules.js ├── extension.js.map ├── find-child-packages.js ├── extension.js └── search-node-modules.js ├── .eslintrc.json ├── src ├── test │ ├── suite │ │ ├── extension.test.ts │ │ └── index.ts │ └── runTest.ts └── extension.ts ├── tsconfig.json ├── License ├── README.md └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wscats/delete-node-modules/HEAD/.DS_Store -------------------------------------------------------------------------------- /logo/index.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wscats/delete-node-modules/HEAD/logo/index.jpg -------------------------------------------------------------------------------- /logo/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wscats/delete-node-modules/HEAD/logo/index.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | **.vsix -------------------------------------------------------------------------------- /screenshot/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wscats/delete-node-modules/HEAD/screenshot/1.gif -------------------------------------------------------------------------------- /screenshot/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wscats/delete-node-modules/HEAD/screenshot/2.png -------------------------------------------------------------------------------- /screenshot/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wscats/delete-node-modules/HEAD/screenshot/3.png -------------------------------------------------------------------------------- /delete-node-modules-1.0.4.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wscats/delete-node-modules/HEAD/delete-node-modules-1.0.4.vsix -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/.eslintrc.json 9 | **/*.map 10 | **/*.ts 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /out/utils.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode'); 2 | 3 | const formatMsg = message => `Search node_modules: ${message}`; 4 | const showError = message => vscode.window.showErrorMessage(formatMsg(message)); 5 | const showWarning = message => vscode.window.showWarningMessage(formatMsg(message)); 6 | 7 | module.exports = { showError, showWarning }; 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /out/sort-files.js: -------------------------------------------------------------------------------- 1 | const sortFiles = (origFiles, origPriorities) => { 2 | const priorities = [ ...origPriorities ].reverse().map(p => p.toLowerCase()); 3 | const files = origFiles.map(file => ({ original: file, lower: file.toLowerCase() })); 4 | const rank = file => priorities.indexOf(file) + 1; 5 | 6 | return files 7 | .sort((a, b) => rank(b.lower) - rank(a.lower)) 8 | .map(file => file.original); 9 | }; 10 | 11 | module.exports = { sortFiles }; 12 | -------------------------------------------------------------------------------- /.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 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": [ 9 | "@typescript-eslint" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/class-name-casing": "warn", 13 | "@typescript-eslint/semi": "warn", 14 | "curly": "warn", 15 | "eqeqeq": "warn", 16 | "no-throw-literal": "warn", 17 | "semi": "off" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from 'vscode'; 6 | // import * as myExtension from '../../extension'; 7 | 8 | suite('Extension Test Suite', () => { 9 | vscode.window.showInformationMessage('Start all tests.'); 10 | 11 | test('Sample test', () => { 12 | assert.equal(-1, [1, 2, 3].indexOf(5)); 13 | assert.equal(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /out/test/runTest.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"runTest.js","sourceRoot":"","sources":["../../src/test/runTest.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,6BAA6B;AAE7B,6CAAuC;AAEvC,SAAe,IAAI;;QAClB,IAAI;YACH,4DAA4D;YAC5D,yCAAyC;YACzC,MAAM,wBAAwB,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAEnE,0BAA0B;YAC1B,iCAAiC;YACjC,MAAM,kBAAkB,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;YAEpE,0DAA0D;YAC1D,MAAM,sBAAQ,CAAC,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,CAAC,CAAC;SACjE;QAAC,OAAO,GAAG,EAAE;YACb,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SAChB;IACF,CAAC;CAAA;AAED,IAAI,EAAE,CAAC"} -------------------------------------------------------------------------------- /out/test/suite/extension.test.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"extension.test.js","sourceRoot":"","sources":["../../../src/test/suite/extension.test.ts"],"names":[],"mappings":";;AAAA,iCAAiC;AAEjC,0DAA0D;AAC1D,8CAA8C;AAC9C,iCAAiC;AACjC,kDAAkD;AAElD,KAAK,CAAC,sBAAsB,EAAE,GAAG,EAAE;IAClC,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,kBAAkB,CAAC,CAAC;IAEzD,IAAI,CAAC,aAAa,EAAE,GAAG,EAAE;QACxB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"} -------------------------------------------------------------------------------- /out/test/suite/extension.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const assert = require("assert"); 4 | // You can import and use all API from the 'vscode' module 5 | // as well as import your extension to test it 6 | const vscode = require("vscode"); 7 | // import * as myExtension from '../../extension'; 8 | suite('Extension Test Suite', () => { 9 | vscode.window.showInformationMessage('Start all tests.'); 10 | test('Sample test', () => { 11 | assert.equal(-1, [1, 2, 3].indexOf(5)); 12 | assert.equal(-1, [1, 2, 3].indexOf(0)); 13 | }); 14 | }); 15 | //# sourceMappingURL=extension.test.js.map -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | "strict": true /* enable all strict type-checking options */ 12 | /* Additional Checks */ 13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | ".vscode-test" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import { runTests } from 'vscode-test'; 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 17 | } catch (err) { 18 | console.error('Failed to run tests'); 19 | process.exit(1); 20 | } 21 | } 22 | 23 | main(); 24 | -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as Mocha from 'mocha'; 3 | import * as glob from 'glob'; 4 | 5 | export function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: 'tdd', 9 | color: true 10 | }); 11 | 12 | const testsRoot = path.resolve(__dirname, '..'); 13 | 14 | return new Promise((c, e) => { 15 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 16 | if (err) { 17 | return e(err); 18 | } 19 | 20 | // Add files to the test suite 21 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 22 | 23 | try { 24 | // Run the mocha test 25 | mocha.run(failures => { 26 | if (failures > 0) { 27 | e(new Error(`${failures} tests failed.`)); 28 | } else { 29 | c(); 30 | } 31 | }); 32 | } catch (err) { 33 | console.error(err); 34 | e(err); 35 | } 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /out/test/suite/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/test/suite/index.ts"],"names":[],"mappings":";;;AAAA,6BAA6B;AAC7B,+BAA+B;AAC/B,6BAA6B;AAE7B,SAAgB,GAAG;IAClB,wBAAwB;IACxB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;QACvB,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,IAAI;KACX,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAEhD,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC3B,IAAI,CAAC,eAAe,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;YACxD,IAAI,GAAG,EAAE;gBACR,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC;aACd;YAED,8BAA8B;YAC9B,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAE9D,IAAI;gBACH,qBAAqB;gBACrB,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;oBACpB,IAAI,QAAQ,GAAG,CAAC,EAAE;wBACjB,CAAC,CAAC,IAAI,KAAK,CAAC,GAAG,QAAQ,gBAAgB,CAAC,CAAC,CAAC;qBAC1C;yBAAM;wBACN,CAAC,EAAE,CAAC;qBACJ;gBACF,CAAC,CAAC,CAAC;aACH;YAAC,OAAO,GAAG,EAAE;gBACb,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBACnB,CAAC,CAAC,GAAG,CAAC,CAAC;aACP;QACF,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC;AAjCD,kBAiCC"} -------------------------------------------------------------------------------- /.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": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/out/**/*.js" 18 | ], 19 | "preLaunchTask": "${defaultBuildTask}" 20 | }, 21 | { 22 | "name": "Extension Tests", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "runtimeExecutable": "${execPath}", 26 | "args": [ 27 | "--extensionDevelopmentPath=${workspaceFolder}", 28 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 29 | ], 30 | "outFiles": [ 31 | "${workspaceFolder}/out/test/**/*.js" 32 | ], 33 | "preLaunchTask": "${defaultBuildTask}" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2019-2020 Eno Yao. https://github.com/wscats 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /out/test/suite/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.run = void 0; 4 | const path = require("path"); 5 | const Mocha = require("mocha"); 6 | const glob = require("glob"); 7 | function run() { 8 | // Create the mocha test 9 | const mocha = new Mocha({ 10 | ui: 'tdd', 11 | color: true 12 | }); 13 | const testsRoot = path.resolve(__dirname, '..'); 14 | return new Promise((c, e) => { 15 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 16 | if (err) { 17 | return e(err); 18 | } 19 | // Add files to the test suite 20 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 21 | try { 22 | // Run the mocha test 23 | mocha.run(failures => { 24 | if (failures > 0) { 25 | e(new Error(`${failures} tests failed.`)); 26 | } 27 | else { 28 | c(); 29 | } 30 | }); 31 | } 32 | catch (err) { 33 | console.error(err); 34 | e(err); 35 | } 36 | }); 37 | }); 38 | } 39 | exports.run = run; 40 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /out/find-parent-modules.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const util = require('util'); 4 | 5 | const fsExists = util.promisify(fs.exists); 6 | const fsReaddir = util.promisify(fs.readdir); 7 | 8 | // Looks for node_modules in parent folders of the workspace recursively. 9 | // Returns a list of paths relative to workspaceRoot/nodeModulesPath 10 | const findParentModules = async (workspaceRoot, nodeModulesPath) => { 11 | const rootDirectoryPath = path.parse(process.cwd()).root.toLowerCase(); 12 | const absoluteRootNodeModules = path.join(rootDirectoryPath, nodeModulesPath); 13 | 14 | const find = async dir => { 15 | const ret = []; 16 | if (await fsExists(dir)) { 17 | const getFilePath = file => 18 | path.relative(path.join(workspaceRoot, nodeModulesPath), path.join(dir, file)); 19 | 20 | const dirFiles = await fsReaddir(dir); 21 | ret.push(...dirFiles.map(getFilePath)); 22 | } 23 | 24 | if (dir !== absoluteRootNodeModules) { 25 | const parent = path.join(dir, '..', '..', nodeModulesPath); 26 | ret.push(...(await find(parent))); 27 | } 28 | 29 | return ret; 30 | }; 31 | 32 | return find(path.join(workspaceRoot, '..', nodeModulesPath)); 33 | }; 34 | 35 | module.exports = { 36 | findParentModules 37 | }; 38 | -------------------------------------------------------------------------------- /out/test/runTest.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const path = require("path"); 13 | const vscode_test_1 = require("vscode-test"); 14 | function main() { 15 | return __awaiter(this, void 0, void 0, function* () { 16 | try { 17 | // The folder containing the Extension Manifest package.json 18 | // Passed to `--extensionDevelopmentPath` 19 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 20 | // The path to test runner 21 | // Passed to --extensionTestsPath 22 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 23 | // Download VS Code, unzip it and run the integration test 24 | yield vscode_test_1.runTests({ extensionDevelopmentPath, extensionTestsPath }); 25 | } 26 | catch (err) { 27 | console.error('Failed to run tests'); 28 | process.exit(1); 29 | } 30 | }); 31 | } 32 | main(); 33 | //# sourceMappingURL=runTest.js.map -------------------------------------------------------------------------------- /out/extension.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":";;;AAAA,iCAAiC;AACjC,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AACjC,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;AAC1C,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AACzB,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAC7B,MAAM,iBAAiB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;AAE3D,SAAgB,4BAA4B,CAAC,GAAW;IACtD,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;QACtB,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACzC,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAClC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YACpC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE;gBACtB,IAAI,IAAI,KAAK,cAAc,EAAE;oBAC3B,0BAA0B,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;iBACjD;gBACD,4BAA4B,CAAC,OAAO,CAAC,CAAC;aACvC;SACF;KACF;AACH,CAAC;AAfD,oEAeC;AAED,SAAS,0BAA0B,CAAC,IAAS;IAC3C,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAS,EAAE,EAAE;QAChC,IAAI,CAAC,IAAI,EAAE;YACT,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAClC,gCAAgC,CACjC,CAAC;YACF,IAAI,CAAC,iBAAiB,EAAE,CAAC,GAAQ,EAAE,MAAW,EAAE,MAAW,EAAE,EAAE;gBAC7D,IAAI,GAAG,EAAE;oBACP,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;oBACpC,OAAO;iBACR;gBACD,OAAO,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;YACzC,CAAC,CAAC,CAAC;SACJ;aAAM;YACL,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,6BAA6B,CAAC,CAAC;SACrE;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAgB,QAAQ,CAAC,OAAgC;IACvD,OAAO,CAAC,GAAG,CACT,sEAAsE,CACvE,CAAC;IAEF,IAAI,iCAAiC,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,CACrE,uDAAuD,EACvD,CAAC,IAAI,EAAE,EAAE;QACP,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,cAAc,EAAE;YAC7C,0BAA0B,CAAC,IAAI,CAAC,CAAC;SAClC;aAAM;YACL,4BAA4B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;SAC3C;IACH,CAAC,CACF,CAAC;IAEF,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;IAC9D,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;AAChD,CAAC;AAlBD,4BAkBC;AAED,SAAgB,UAAU,KAAK,CAAC;AAAhC,gCAAgC"} -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | const rimraf = require("rimraf"); 3 | const { exec } = require("child_process"); 4 | const fs = require("fs"); 5 | const path = require("path"); 6 | const searchNodeModules = require("./search-node-modules"); 7 | 8 | export function deleteAllNodeModulesInFolder(dir: string) { 9 | if (fs.existsSync(dir)) { 10 | const files = fs.readdirSync(dir); 11 | for (let i = 0; i < files.length; i++) { 12 | const newPath = path.join(dir, files[i]); 13 | const stat = fs.statSync(newPath); 14 | const name = path.basename(newPath); 15 | if (stat.isDirectory()) { 16 | if (name === 'node_modules') { 17 | usePathToDeleteNodeModules({ fsPath: newPath }); 18 | } 19 | deleteAllNodeModulesInFolder(newPath); 20 | } 21 | } 22 | } 23 | } 24 | 25 | function usePathToDeleteNodeModules(path: any) { 26 | rimraf(path.fsPath, (data: any) => { 27 | if (!data) { 28 | vscode.window.showInformationMessage( 29 | "Delete Node Modules Succeeded!" 30 | ); 31 | exec("npm cache clean", (err: any, stdout: any, stderr: any) => { 32 | if (err) { 33 | console.error("clean cache failed"); 34 | return; 35 | } 36 | console.error("clean cache succeeded"); 37 | }); 38 | } else { 39 | vscode.window.showInformationMessage("Delete Node Modules Failed!"); 40 | } 41 | }); 42 | } 43 | 44 | export function activate(context: vscode.ExtensionContext) { 45 | console.log( 46 | 'Congratulations, your extension "delete-node-modules" is now active!' 47 | ); 48 | 49 | let usePathToDeleteNodeModulesCommand = vscode.commands.registerCommand( 50 | "delete-node-modules.usePathToDeleteNodeModulesCommand", 51 | (path) => { 52 | if (path.fsPath.slice(-12) === 'node_modules') { 53 | usePathToDeleteNodeModules(path); 54 | } else { 55 | deleteAllNodeModulesInFolder(path.fsPath); 56 | } 57 | } 58 | ); 59 | 60 | context.subscriptions.push(usePathToDeleteNodeModulesCommand); 61 | context.subscriptions.push(searchNodeModules); 62 | } 63 | 64 | export function deactivate() { } 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Delete Node Modules 2 | 3 | Download 4 | Macketplace 5 | Github Page 6 | Eno Yao 7 | Status 8 | 9 | ✂️ Simple extension for Visual Studio Code that allows you to quickly delete your project's node_modules directory. 10 | 11 | # Search Node Modules 12 | 13 | Use `searchNodeModules` command to search node_modules folder. 14 | 15 | 16 | 3 17 | 18 | # Features 19 | 20 | - Select the node_modules folder 21 | - Right click 22 | - Select `Delete Node Modules` item 23 | 24 | Then, all contents in the node_modules folder will be cleared. 25 | 26 | 27 | ![1](https://user-images.githubusercontent.com/17243165/100516912-23dc7900-31c2-11eb-936b-aeaa3cd23121.gif) 28 | 29 | Or click delete in the parent folder menu to delete all `node_modules` folders in the subfolder. 30 | 31 | 32 | 2 33 | 34 | # Thanks 35 | 36 | If you think it's useful, you can leave us a [message and like](https://marketplace.visualstudio.com/items?itemName=Wscats.delete-node-modules&ssr=false#review-details) it, Your support is our driving force😀 37 | 38 | # License 39 | 40 | Compile Hero is released under the MIT. 41 | -------------------------------------------------------------------------------- /out/find-child-packages.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const util = require('util'); 3 | const path = require('path'); 4 | const loadJsonFile = require('load-json-file'); 5 | const glob = util.promisify(require('glob')); 6 | const { showWarning } = require('./utils'); 7 | 8 | const exists = util.promisify(fs.exists); 9 | 10 | const PACKAGE_JSON_FILE = 'package.json'; 11 | const LERNA_CONFIG_FILE = 'lerna.json'; 12 | const DOUBLE_STAR = '**'; // globstar 13 | 14 | const flat = arrays => [].concat.apply([], arrays); 15 | 16 | const distinct = array => [ ...new Set(array) ]; 17 | 18 | const findPatternMatches = async (root, pattern) => { 19 | // patterns with double star e.g. '/src/**/' are not supported at the moment, because they are too general and may match nested node_modules 20 | if (pattern.includes(DOUBLE_STAR)) return []; 21 | 22 | const matches = await glob(path.join(pattern, PACKAGE_JSON_FILE), { 23 | cwd: root 24 | }); 25 | 26 | return matches.map(match => path.join(match, '..')); 27 | }; 28 | 29 | const getLernaPackagesConfig = async root => { 30 | const lernaConfigFile = path.join(root, LERNA_CONFIG_FILE); 31 | if (!(await exists(lernaConfigFile))) { 32 | return []; 33 | } 34 | 35 | const config = await loadJsonFile(lernaConfigFile).catch(() => 36 | showWarning(`Ignoring invalid ${LERNA_CONFIG_FILE} file at: ${lernaConfigFile}`) 37 | ); 38 | return config && Array.isArray(config.packages) ? config.packages : []; 39 | }; 40 | 41 | const getYarnWorkspacesConfig = async root => { 42 | const packageJsonFile = path.join(root, PACKAGE_JSON_FILE); 43 | if (!(await exists(packageJsonFile))) { 44 | return []; 45 | } 46 | 47 | const config = await loadJsonFile(packageJsonFile).catch(() => 48 | showWarning(`Ignoring invalid ${PACKAGE_JSON_FILE} file at: ${packageJsonFile}`) 49 | ); 50 | return config && Array.isArray(config.workspaces) ? config.workspaces : []; 51 | }; 52 | 53 | const findChildPackages = async root => { 54 | const patterns = distinct([ 55 | ...(await getLernaPackagesConfig(root)), 56 | ...(await getYarnWorkspacesConfig(root)) 57 | ]); 58 | 59 | const matchesArr = await Promise.all( 60 | patterns.map(pattern => findPatternMatches(root, pattern)) 61 | ); 62 | 63 | return flat(matchesArr); 64 | }; 65 | 66 | module.exports = { findChildPackages }; 67 | -------------------------------------------------------------------------------- /out/extension.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.deactivate = exports.activate = exports.deleteAllNodeModulesInFolder = void 0; 4 | const vscode = require("vscode"); 5 | const rimraf = require("rimraf"); 6 | const { exec } = require("child_process"); 7 | const fs = require("fs"); 8 | const path = require("path"); 9 | const searchNodeModules = require("./search-node-modules"); 10 | function deleteAllNodeModulesInFolder(dir) { 11 | if (fs.existsSync(dir)) { 12 | const files = fs.readdirSync(dir); 13 | for (let i = 0; i < files.length; i++) { 14 | const newPath = path.join(dir, files[i]); 15 | const stat = fs.statSync(newPath); 16 | const name = path.basename(newPath); 17 | if (stat.isDirectory()) { 18 | if (name === 'node_modules') { 19 | usePathToDeleteNodeModules({ fsPath: newPath }); 20 | } 21 | deleteAllNodeModulesInFolder(newPath); 22 | } 23 | } 24 | } 25 | } 26 | exports.deleteAllNodeModulesInFolder = deleteAllNodeModulesInFolder; 27 | function usePathToDeleteNodeModules(path) { 28 | rimraf(path.fsPath, (data) => { 29 | if (!data) { 30 | vscode.window.showInformationMessage("Delete Node Modules Succeeded!"); 31 | exec("npm cache clean", (err, stdout, stderr) => { 32 | if (err) { 33 | console.error("clean cache failed"); 34 | return; 35 | } 36 | console.error("clean cache succeeded"); 37 | }); 38 | } 39 | else { 40 | vscode.window.showInformationMessage("Delete Node Modules Failed!"); 41 | } 42 | }); 43 | } 44 | function activate(context) { 45 | console.log('Congratulations, your extension "delete-node-modules" is now active!'); 46 | let usePathToDeleteNodeModulesCommand = vscode.commands.registerCommand("delete-node-modules.usePathToDeleteNodeModulesCommand", (path) => { 47 | if (path.fsPath.slice(-12) === 'node_modules') { 48 | usePathToDeleteNodeModules(path); 49 | } 50 | else { 51 | deleteAllNodeModulesInFolder(path.fsPath); 52 | } 53 | }); 54 | context.subscriptions.push(usePathToDeleteNodeModulesCommand); 55 | context.subscriptions.push(searchNodeModules); 56 | } 57 | exports.activate = activate; 58 | function deactivate() { } 59 | exports.deactivate = deactivate; 60 | //# sourceMappingURL=extension.js.map -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "delete-node-modules", 3 | "displayName": "Node Modules", 4 | "description": "✂️ Simple extension for Visual Studio Code that allows you to quickly delete or search your project's node_modules directory.", 5 | "author": { 6 | "name": "Eno Yao", 7 | "email": "kalone.cool@gmail.com", 8 | "url": "https://github.com/Wscats" 9 | }, 10 | "version": "1.0.6", 11 | "publisher": "Wscats", 12 | "icon": "logo/index.png", 13 | "homepage": "https://github.com/Wscats/delete-node-modules", 14 | "preview": true, 15 | "engines": { 16 | "vscode": "^1.45.0" 17 | }, 18 | "categories": [ 19 | "Extension Packs", 20 | "Debuggers", 21 | "Other" 22 | ], 23 | "keywords": [ 24 | "node_modules", 25 | "package.json", 26 | "delete", 27 | "search", 28 | "clean", 29 | "node" 30 | ], 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/Wscats/delete-node-modules/issues/new" 34 | }, 35 | "activationEvents": [ 36 | "onCommand:delete-node-modules.usePathToDeleteNodeModulesCommand", 37 | "onCommand:delete-node-modules.deleteAllNodeModulesInFolderCommand", 38 | "onCommand:delete-node-modules.searchNodeModules" 39 | ], 40 | "repository": { 41 | "type": "git", 42 | "url": "https://github.com/Wscats/delete-node-modules" 43 | }, 44 | "main": "./out/extension.js", 45 | "contributes": { 46 | "commands": [ 47 | { 48 | "command": "delete-node-modules.usePathToDeleteNodeModulesCommand", 49 | "title": "Delete Node Modules" 50 | }, 51 | { 52 | "command": "delete-node-modules.searchNodeModules", 53 | "title": "Search Node Modules" 54 | } 55 | ], 56 | "menus": { 57 | "explorer/context": [ 58 | { 59 | "command": "delete-node-modules.usePathToDeleteNodeModulesCommand", 60 | "group": "delete-node-modules", 61 | "when": "explorerResourceIsFolder == true" 62 | }, 63 | { 64 | "command": "delete-node-modules.searchNodeModules", 65 | "group": "search-node-modules", 66 | "when": "explorerResourceIsFolder == true" 67 | } 68 | ], 69 | "editor/context": [ 70 | { 71 | "command": "delete-node-modules.usePathToDeleteNodeModulesCommand", 72 | "group": "delete-node-modules", 73 | "when": "explorerResourceIsFolder == true" 74 | }, 75 | { 76 | "command": "delete-node-modules.searchNodeModules", 77 | "group": "search-node-modules", 78 | "when": "explorerResourceIsFolder == true" 79 | } 80 | ], 81 | "editor/title/context": [ 82 | { 83 | "command": "delete-node-modules.usePathToDeleteNodeModulesCommand", 84 | "group": "delete-node-modules", 85 | "when": "explorerResourceIsFolder == true" 86 | }, 87 | { 88 | "command": "delete-node-modules.searchNodeModules", 89 | "group": "search-node-modules", 90 | "when": "explorerResourceIsFolder == true" 91 | } 92 | ] 93 | }, 94 | "keybindings": [ 95 | { 96 | "command": "delete-node-modules.searchNodeModules", 97 | "key": "ctrl+k ctrl+n", 98 | "mac": "cmd+k cmd+n", 99 | "when": "!terminalFocus" 100 | } 101 | ], 102 | "configuration": { 103 | "title": "Search Node Modules", 104 | "properties": { 105 | "search-node-modules.useLastFolder": { 106 | "type": "boolean", 107 | "default": false, 108 | "description": "Default to folder of last opened file when searching." 109 | }, 110 | "search-node-modules.path": { 111 | "type": "string", 112 | "default": "node_modules", 113 | "description": "Relative path to node_modules folder." 114 | }, 115 | "search-node-modules.searchParentModules": { 116 | "type": "boolean", 117 | "default": true, 118 | "description": "Include modules from parent folders in search results." 119 | }, 120 | "search-node-modules.orderPriority": { 121 | "type": "array", 122 | "default": [ 123 | "index.js", 124 | "README.md", 125 | "package.json" 126 | ], 127 | "description": "List of preferred names that should be shown at the top of the result list." 128 | } 129 | } 130 | } 131 | }, 132 | "scripts": { 133 | "build": "vsce package", 134 | "vscode:prepublish": "npm run compile", 135 | "compile": "tsc -p ./", 136 | "lint": "eslint src --ext ts", 137 | "watch": "tsc -watch -p ./", 138 | "pretest": "npm run compile && npm run lint", 139 | "test": "node ./out/test/runTest.js" 140 | }, 141 | "devDependencies": { 142 | "@types/vscode": "^1.45.0", 143 | "@types/glob": "^7.1.1", 144 | "@types/mocha": "^7.0.2", 145 | "@types/node": "^13.11.0", 146 | "eslint": "^6.8.0", 147 | "@typescript-eslint/parser": "^2.30.0", 148 | "@typescript-eslint/eslint-plugin": "^2.30.0", 149 | "glob": "^7.1.6", 150 | "mocha": "^7.1.2", 151 | "typescript": "^3.8.3", 152 | "vscode-test": "^1.3.0" 153 | }, 154 | "dependencies": { 155 | "rimraf": "^3.0.2", 156 | "glob": "^7.1.3", 157 | "load-json-file": "^6.0.0" 158 | } 159 | } -------------------------------------------------------------------------------- /out/search-node-modules.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const { findParentModules } = require('./find-parent-modules'); 5 | const { findChildPackages } = require('./find-child-packages'); 6 | const { showError } = require('./utils'); 7 | const { sortFiles } = require('./sort-files'); 8 | 9 | let lastFolder = ''; 10 | let lastWorkspaceName = ''; 11 | let lastWorkspaceRoot = ''; 12 | 13 | const nodeModules = 'node_modules'; 14 | 15 | const searchNodeModules = vscode.commands.registerCommand('delete-node-modules.searchNodeModules', () => { 16 | const preferences = vscode.workspace.getConfiguration('search-node-modules'); 17 | 18 | const useLastFolder = preferences.get('useLastFolder', false); 19 | const nodeModulesPath = preferences.get('path', nodeModules); 20 | const searchParentModules = preferences.get('searchParentModules', true); 21 | const orderPriority = preferences.get('orderPriority', []); 22 | 23 | const searchPath = (workspaceName, workspaceRoot, folderPath) => { 24 | // Path to node_modules in this workspace folder 25 | const workspaceNodeModules = path.join(workspaceName, nodeModulesPath); 26 | 27 | // Reset last folder 28 | lastFolder = ''; 29 | lastWorkspaceName = ''; 30 | lastWorkspaceRoot = ''; 31 | 32 | // Path to current folder 33 | const folderFullPath = path.join(workspaceRoot, folderPath); 34 | 35 | // Read folder, built quick pick with files/folder (and shortcuts) 36 | fs.readdir(folderFullPath, async (readErr, files) => { 37 | if (readErr) { 38 | if (folderPath === nodeModulesPath) { 39 | return showError('No node_modules folder in this workspace.'); 40 | } 41 | 42 | return showError(`Unable to open folder ${folderPath}`); 43 | } 44 | 45 | const isParentFolder = folderPath.includes('..'); 46 | const options = sortFiles(files, orderPriority); 47 | 48 | // If searching in root node_modules, also include modules from parent folders, that are outside of the workspace 49 | if (folderPath === nodeModulesPath) { 50 | if (searchParentModules) { 51 | const parentModules = await findParentModules(workspaceRoot, nodeModulesPath); 52 | options.push(...parentModules); 53 | } 54 | } else { 55 | // Otherwise, show option to move back to root 56 | options.push(''); 57 | options.push(workspaceNodeModules); 58 | 59 | // If current folder is not outside of the workspace, also add option to move a step back 60 | if (!isParentFolder) { 61 | options.push('..'); 62 | } 63 | } 64 | 65 | 66 | vscode.window.showQuickPick(options, { 67 | placeHolder: path.format({ dir: workspaceName, base: folderPath }) 68 | 69 | }) 70 | .then(selected => { 71 | // node_modules shortcut selected 72 | if (selected === workspaceNodeModules) { 73 | searchPath(workspaceName, workspaceRoot, nodeModulesPath); 74 | } else { 75 | const selectedPath = path.join(folderPath, selected); 76 | const selectedFullPath = path.join(workspaceRoot, selectedPath); 77 | 78 | // If selected is a folder, traverse it, 79 | // otherwise open file. 80 | fs.stat(selectedFullPath, (statErr, stats) => { 81 | if (stats.isDirectory()) { 82 | searchPath(workspaceName, workspaceRoot, selectedPath); 83 | } else { 84 | lastWorkspaceName = workspaceName; 85 | lastWorkspaceRoot = workspaceRoot; 86 | lastFolder = folderPath; 87 | 88 | vscode.workspace.openTextDocument(selectedFullPath, selectedPath) 89 | .then(vscode.window.showTextDocument); 90 | } 91 | }); 92 | } 93 | }); 94 | }); 95 | }; 96 | 97 | const getProjectFolder = async (workspaceFolder) => { 98 | const packages = await findChildPackages(workspaceFolder.uri.fsPath); 99 | // If in a lerna/yarn monorepo, prompt user to select which project to traverse 100 | if (packages.length > 0) { 101 | const selected = await vscode.window.showQuickPick( 102 | [ 103 | { label: workspaceFolder.name, packageDir: '' }, // First option is the root dir 104 | ...packages.map(packageDir => ({ label: path.join(workspaceFolder.name, packageDir), packageDir })) 105 | ] 106 | , { placeHolder: 'Select Project' } 107 | ); 108 | if (!selected) { 109 | return; 110 | } 111 | 112 | return { 113 | name: selected.label, 114 | path: path.join(workspaceFolder.uri.fsPath, selected.packageDir) 115 | }; 116 | } 117 | 118 | // Otherwise, use the root folder 119 | return { 120 | name: workspaceFolder.name, 121 | path: workspaceFolder.uri.fsPath 122 | }; 123 | }; 124 | 125 | const getWorkspaceFolder = async () => { 126 | // If in a multifolder workspace, prompt user to select which one to traverse. 127 | if (vscode.workspace.workspaceFolders.length > 1) { 128 | const selected = await vscode.window.showQuickPick(vscode.workspace.workspaceFolders.map(folder => ({ 129 | label: folder.name, 130 | folder 131 | })), { 132 | placeHolder: 'Select workspace folder' 133 | }); 134 | 135 | if (!selected) { 136 | return; 137 | } 138 | 139 | return selected.folder; 140 | } 141 | 142 | // Otherwise, use the first one 143 | const folder = vscode.workspace.workspaceFolders[0]; 144 | return folder; 145 | }; 146 | 147 | // Open last folder if there is one 148 | if (useLastFolder && lastFolder) { 149 | return searchPath(lastWorkspaceName, lastWorkspaceRoot, lastFolder); 150 | } 151 | 152 | // Must have at least one workspace folder 153 | if (!vscode.workspace.workspaceFolders.length) { 154 | return showError('You must have a workspace opened.'); 155 | } 156 | 157 | getWorkspaceFolder().then(folder => folder && getProjectFolder(folder)).then(folder => { 158 | if (folder) { 159 | searchPath(folder.name, folder.path, nodeModulesPath); 160 | } 161 | }); 162 | }); 163 | 164 | module.exports = searchNodeModules; --------------------------------------------------------------------------------