├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .github └── workflows │ └── codeql-analysis.yml ├── .gitignore ├── .prettierrc.json ├── .vscode ├── launch.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── icon.png ├── media ├── Example.gif ├── QR-Code.png ├── excludePaths1.png ├── excludePaths2.png └── excludePattern.png ├── package.json ├── src ├── extension.ts └── utils │ ├── fileUtils.ts │ └── vscodeUtils.ts ├── test.tsconfig.json ├── test ├── Makefile ├── testTemplates │ ├── inner │ │ ├── inner_inner │ │ │ └── main.c │ │ └── main.c │ └── main.c └── testTemplatesmain.pdb ├── tsconfig.json └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | end_of_line = lf 11 | charset = utf-8 12 | 13 | [*.{js,ts,json}] 14 | indent_size = 2 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Ignore artifacts: 2 | dist 3 | out 4 | node_modules 5 | test 6 | 7 | # Ignore files: 8 | *.js 9 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "parser": "@typescript-eslint/parser", 8 | "parserOptions": { 9 | "project": "tsconfig.json", 10 | "sourceType": "module" 11 | }, 12 | "plugins": [ 13 | "@typescript-eslint", 14 | "@typescript-eslint/tslint", 15 | "eslint-plugin-jsdoc", 16 | "@typescript-eslint/eslint-plugin-tslint", 17 | "eslint-plugin-import" 18 | ], 19 | "rules": { 20 | "@typescript-eslint/adjacent-overload-signatures": "error", 21 | "@typescript-eslint/array-type": "error", 22 | "@typescript-eslint/member-delimiter-style": [ 23 | "error", 24 | { 25 | "multiline": { 26 | "delimiter": "semi", 27 | "requireLast": true 28 | }, 29 | "singleline": { 30 | "delimiter": "semi", 31 | "requireLast": false 32 | } 33 | } 34 | ], 35 | "@typescript-eslint/no-misused-new": "error", 36 | "@typescript-eslint/no-namespace": "error", 37 | "@typescript-eslint/no-non-null-assertion": "error", 38 | "@typescript-eslint/no-extra-non-null-assertion": "error", 39 | "@typescript-eslint/no-this-alias": "error", 40 | "@typescript-eslint/no-unnecessary-qualifier": "error", 41 | "@typescript-eslint/no-unnecessary-type-arguments": "error", 42 | "@typescript-eslint/no-var-requires": "error", 43 | "@typescript-eslint/prefer-function-type": "error", 44 | "@typescript-eslint/prefer-namespace-keyword": "error", 45 | "@typescript-eslint/semi": "error", 46 | "@typescript-eslint/triple-slash-reference": "error", 47 | "@typescript-eslint/type-annotation-spacing": "error", 48 | "@typescript-eslint/unified-signatures": "error", 49 | "arrow-body-style": "error", 50 | "constructor-super": "error", 51 | "eol-last": "error", 52 | "eqeqeq": ["error", "always"], 53 | "import/no-default-export": "error", 54 | "import/no-unassigned-import": "error", 55 | "jsdoc/no-types": "error", 56 | "new-parens": "error", 57 | "no-bitwise": "error", 58 | "no-caller": "error", 59 | "no-cond-assign": "error", 60 | "no-debugger": "error", 61 | "no-duplicate-case": "error", 62 | "no-duplicate-imports": "error", 63 | "no-eval": "error", 64 | "no-fallthrough": "error", 65 | "no-invalid-this": "error", 66 | "no-irregular-whitespace": "error", 67 | "no-multiple-empty-lines": [ 68 | "error", 69 | { "max": 1, "maxEOF": 1, "maxBOF": 0 } 70 | ], 71 | "no-new-wrappers": "error", 72 | "no-redeclare": "error", 73 | "no-return-await": "error", 74 | "no-sequences": "error", 75 | "no-sparse-arrays": "error", 76 | "no-trailing-spaces": "error", 77 | "no-undef-init": "error", 78 | "no-unsafe-finally": "error", 79 | "no-unused-expressions": "error", 80 | "no-unused-labels": "error", 81 | "no-var": "error", 82 | "one-var": ["error", "never"], 83 | "prefer-const": "error", 84 | "prefer-object-spread": "error", 85 | "space-in-parens": ["error", "never"], 86 | "use-isnan": "error", 87 | "valid-typeof": "error", 88 | "yoda": "error", 89 | "@typescript-eslint/tslint/config": [ 90 | "error", 91 | { 92 | "rules": { 93 | "encoding": true, 94 | "import-spacing": true, 95 | "match-default-export-name": true, 96 | "no-boolean-literal-compare": true, 97 | "no-mergeable-namespace": true, 98 | "no-reference-import": true, 99 | "number-literal-format": true, 100 | "one-line": [ 101 | true, 102 | "check-catch", 103 | "check-finally", 104 | "check-else", 105 | "check-open-brace", 106 | "check-whitespace" 107 | ], 108 | "prefer-method-signature": true, 109 | "prefer-while": true, 110 | "typedef": ["variable-declaration"], 111 | "whitespace": [ 112 | true, 113 | "check-branch", 114 | "check-operator", 115 | "check-separator", 116 | "check-preblock", 117 | "check-type" 118 | ], 119 | "max-len": ["error", { "code": 80 }] 120 | } 121 | } 122 | ] 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.png binary 2 | *.exe binary 3 | *.gif binary 4 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | analyze: 11 | name: Analyze 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | language: [ 'javascript' ] 18 | 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v2 22 | 23 | - name: Initialize CodeQL 24 | uses: github/codeql-action/init@v1 25 | with: 26 | languages: ${{ matrix.language }} 27 | 28 | - name: Autobuild 29 | uses: github/codeql-action/autobuild@v1 30 | 31 | - name: Perform CodeQL Analysis 32 | uses: github/codeql-action/analyze@v1 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/ 3 | out/ 4 | build/ 5 | .mypy_cache 6 | .vscode-test 7 | 8 | test/testAssets/ 9 | 10 | package-lock.json 11 | 12 | **/.vscode/tasks.json 13 | **/.vscode/launch.json 14 | **/.vscode/c_cpp_properties.json 15 | **/.vscode/settings.json 16 | 17 | !.vscode/tasks.json 18 | !.vscode/launch.json 19 | 20 | *.vsix 21 | *.cpuprofile 22 | *.log 23 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Run Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "runtimeExecutable": "${execPath}", 9 | "args": ["--extensionDevelopmentPath=${workspaceFolder}"], 10 | "outFiles": ["${workspaceFolder}/dist/**/*.js"], 11 | "preLaunchTask": "npm: compile" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.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 | "type": "npm", 21 | "script": "compile", 22 | "group": "build", 23 | "problemMatcher": [], 24 | "label": "npm: compile", 25 | "detail": "npm run webpack && tsc -p ./" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | node_modules/ 3 | test/ 4 | src/ 5 | 6 | .editorconfig 7 | .eslintignore 8 | .eslintrc.js 9 | .gitattributes 10 | .gitignore 11 | .prettierrc.json 12 | tsconfig.json 13 | webpack.config.js 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Pro Formatter Change Log 2 | 3 | ## Version 1.2.0: May 25, 2025 4 | 5 | - **Bugfix**: Dont close pinned tabs 6 | - **Bugfix**: Ignore gitingore files 7 | 8 | ## Version 1.1.0: Feb 14, 2024 9 | 10 | - **Info**: Added node_modules to default exclude 11 | 12 | ## Version 1.0.0: Jul 11, 2023 13 | 14 | - **Info**: Fixed bug where already opened files were closed 15 | 16 | ## Version 0.1.7: Nov 1, 2022 17 | 18 | - **Info**: Added PayPal donation link 19 | 20 | ## Version 0.1.6: March 27, 2022 21 | 22 | - **Bugfix**: Fixed bug for formatting a directory which had no sub directories 23 | 24 | ## Version 0.1.5: December 15, 2021 25 | 26 | - **Bugfix**: Fixed bug for includePattern 27 | 28 | ## Version 0.1.4: December 14, 2021 29 | 30 | - **Info**: Updated icon 31 | 32 | ## Version 0.1.3: December 14, 2021 33 | 34 | - **Info**: Updated icon 35 | 36 | ## Version 0.1.2: December 14, 2021 37 | 38 | - **Bugfix**: Fixed file path bug 39 | - **Bugfix**: Fixed formatting bug 40 | - **Info**: Removed *showFormatting* due to vscode API issues 41 | 42 | ## Version 0.1.1: December 14, 2021 43 | 44 | - **Bugfix**: Fixed extension settings bug 45 | 46 | ## Version 0.1.0: December 14, 2021 47 | 48 | - **Info**: First release, for more information see [README.md](./README.md) 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Jan Schaffranek. All rights reserved. 2 | 3 | MIT License 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VSCode Workspace Formatter 2 | 3 | 🔧 Format all your files in a workspace with one click. 🔧 4 | 5 | [![Visual Studio Marketplace](https://img.shields.io/visual-studio-marketplace/v/franneck94.workspace-formatter?label=VS%20Marketplace&logo=visual-studio-code)](https://marketplace.visualstudio.com/items?itemName=franneck94.workspace-formatter) 6 | [![Installs](https://img.shields.io/visual-studio-marketplace/i/franneck94.workspace-formatter?label=Installs)](https://marketplace.visualstudio.com/items?itemName=franneck94.workspace-formatter) 7 | [![License](https://img.shields.io/github/license/franneck94/Vscode-Workspace-Formatter?label=License)](https://github.com/franneck94/Vscode-Workspace-Formatter/blob/master/LICENSE) 8 | 9 | You can define include and exclude patterns to manage which files and directories should be formatted. 10 | Besides that, you can also select only certain directories to format with the context menu. 11 | 12 | ## Example 13 | 14 | ![ExampleGif](./media/Example.gif?raw=true) 15 | 16 | ## How to use: Format the whole workspace 17 | 18 | 1. Open the command palette (F1) 19 | 2. Run the command: *Workspace Formatter: Run* 20 | 3. All files will be formatted w.r.t the include/exclude patterns 21 | 22 | ## How to use: Only format selected directories 23 | 24 | 1. Select any folder in the context menu, by right-clicking 25 | 2. Run the context-menu command: *Format Directory* 26 | 3. All files will be formatted w.r.t the include/exclude patterns 27 | 28 | ## Extension Features 29 | 30 | For including/excluding glob patterns are used. 31 | For more information about glob patterns see [here](https://en.wikipedia.org/wiki/Glob_(Workspacegramming)#Syntax). 32 | 33 | ### Include Folders for Selection 34 | 35 | You can add glob patterns to include folder and file names. 36 | Per default, there is no include filter. 37 | 38 | For example, if you only want to format typescript files in your workspace, you could add the following glob pattern: 39 | 40 | ```json 41 | "Workspace_Formatter.includePattern": ["*.ts"] 42 | ``` 43 | 44 | ### Exclude Folders for Selection 45 | 46 | You can add glob patterns to exclude folder and file names. 47 | Per default, all folders and files starting with a *"."* are excluded, also all directories that are called *build*. 48 | 49 | For example, if you want to skip formatting for javascript files in your workspace, you could add the following glob pattern: 50 | 51 | ```json 52 | "Workspace_Formatter.excludePattern": ["*.js"] 53 | ``` 54 | 55 | ### Extension Settings 56 | 57 | - ⚙️ Glob pattern to include from the folder selection (defaults to ["\*"]) 58 | - ⚙️ Glob pattern to exclude from the folder selection (defaults to ["\*\*\/build", "\*\*/.\*", "\*\*/.vscode"]) 59 | - ⚙️ Whether to save the currently formatted file (defaults to true) 60 | - ⚙️ Whether to close the currently formatted file. Only used (defaults to true) 61 | 62 | ## Release Notes 63 | 64 | Refer to the [CHANGELOG](CHANGELOG.md). 65 | 66 | ## License 67 | 68 | Copyright (C) 2021 Jan Schaffranek. 69 | Licensed under the [MIT License](LICENSE). 70 | 71 | ## Supporting the Work 72 | 73 | Feel free to make a donation, such that i have more time to work on my VSCode extension*s*. 74 | 75 | ![PayPal QR Code](./media/QR-Code.png) 76 | 77 | Or use the Link: 78 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franneck94/Vscode-Workspace-Formatter/68d4e5a7bc603eaa0de8fafb990c142399dc0b3e/icon.png -------------------------------------------------------------------------------- /media/Example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franneck94/Vscode-Workspace-Formatter/68d4e5a7bc603eaa0de8fafb990c142399dc0b3e/media/Example.gif -------------------------------------------------------------------------------- /media/QR-Code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franneck94/Vscode-Workspace-Formatter/68d4e5a7bc603eaa0de8fafb990c142399dc0b3e/media/QR-Code.png -------------------------------------------------------------------------------- /media/excludePaths1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franneck94/Vscode-Workspace-Formatter/68d4e5a7bc603eaa0de8fafb990c142399dc0b3e/media/excludePaths1.png -------------------------------------------------------------------------------- /media/excludePaths2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franneck94/Vscode-Workspace-Formatter/68d4e5a7bc603eaa0de8fafb990c142399dc0b3e/media/excludePaths2.png -------------------------------------------------------------------------------- /media/excludePattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franneck94/Vscode-Workspace-Formatter/68d4e5a7bc603eaa0de8fafb990c142399dc0b3e/media/excludePattern.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workspace-formatter", 3 | "displayName": "Workspace Formatter", 4 | "description": "🔧 Format all your files in a workspace with one click. 🔧", 5 | "version": "1.2.0", 6 | "publisher": "franneck94", 7 | "license": "MIT", 8 | "icon": "icon.png", 9 | "galleryBanner": { 10 | "color": "#9c9c9c", 11 | "theme": "dark" 12 | }, 13 | "engines": { 14 | "vscode": "^1.100.0" 15 | }, 16 | "categories": [ 17 | "Formatters" 18 | ], 19 | "tags": [ 20 | "format", 21 | "workspace" 22 | ], 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/franneck94/vscode-Workspace-formatter.git" 26 | }, 27 | "homepage": "https://github.com/franneck94/vscode-Workspace-formatter/README.md", 28 | "bugs": { 29 | "url": "https://github.com/franneck94/vscode-Workspace-formatter/issues" 30 | }, 31 | "activationEvents": [ 32 | "onStartupFinished" 33 | ], 34 | "main": "./dist/extension", 35 | "contributes": { 36 | "taskDefinitions": [ 37 | { 38 | "type": "Workspace_Formatter" 39 | } 40 | ], 41 | "commands": [ 42 | { 43 | "command": "Workspace_Formatter.runOnWorkspace", 44 | "title": "Run", 45 | "category": "Workspace Formatter" 46 | }, 47 | { 48 | "command": "Workspace_Formatter.runOnContextMenu", 49 | "title": "Format Directory", 50 | "category": "Workspace Formatter" 51 | } 52 | ], 53 | "menus": { 54 | "explorer/context": [ 55 | { 56 | "command": "Workspace_Formatter.runOnContextMenu", 57 | "when": "explorerResourceIsFolder", 58 | "group": "WorkspaceFormatter@1" 59 | } 60 | ] 61 | }, 62 | "configuration": { 63 | "type": "object", 64 | "title": "Workspace Formatter", 65 | "properties": { 66 | "Workspace_Formatter.includePattern": { 67 | "type": "array", 68 | "default": [ 69 | "*" 70 | ], 71 | "items": { 72 | "type": "string" 73 | }, 74 | "description": "List glob patterns to include for formatting. Note: Include pattern is used before the exclude pattern is applied.", 75 | "scope": "resource" 76 | }, 77 | "Workspace_Formatter.excludePattern": { 78 | "type": "array", 79 | "default": [ 80 | "**/build", 81 | "**/.*", 82 | "**/.vscode" 83 | ], 84 | "items": { 85 | "type": "string" 86 | }, 87 | "description": "List glob patterns to exclude for formatting.", 88 | "scope": "resource" 89 | }, 90 | "Workspace_Formatter.saveAfterFormat": { 91 | "type": "boolean", 92 | "default": true, 93 | "description": "Whether to save the currently formatted file.", 94 | "scope": "resource" 95 | }, 96 | "Workspace_Formatter.closeAfterSave": { 97 | "type": "boolean", 98 | "default": true, 99 | "description": "Whether to close the currently formatted file.", 100 | "scope": "resource" 101 | } 102 | } 103 | } 104 | }, 105 | "scripts": { 106 | "webpack": "webpack --mode development", 107 | "webpack-watch": "webpack --mode development --watch", 108 | "vscode:prepublish": "npm run webpack", 109 | "compile": "set NODE_OPTIONS=--openssl-legacy-provider && npm run webpack", 110 | "compile-watch": "npm run webpack-watch", 111 | "watch": "tsc -watch -p ./" 112 | }, 113 | "devDependencies": { 114 | "@types/glob": "^7.2.0", 115 | "@types/mocha": "^8.2.3", 116 | "@types/node": "^14.18.63", 117 | "@types/vscode": "^1.100.0", 118 | "@typescript-eslint/eslint-plugin": "^4.33.0", 119 | "@typescript-eslint/eslint-plugin-tslint": "^4.33.0", 120 | "@typescript-eslint/parser": "^4.33.0", 121 | "eslint": "^7.32.0", 122 | "eslint-plugin-import": "^2.31.0", 123 | "eslint-plugin-jsdoc": "^32.3.4", 124 | "glob": "^7.2.3", 125 | "mocha": "^11.5.0", 126 | "prettier": "^2.8.8", 127 | "ts-loader": "^9.5.2", 128 | "typescript": "^4.9.5", 129 | "webpack": "^5.99.9", 130 | "webpack-cli": "^4.10.0" 131 | }, 132 | "dependencies": { 133 | "fs": "^0.0.1-security", 134 | "minimatch": "^3.1.2", 135 | "tslint": "^6.1.3", 136 | "vsce": "^1.97.0" 137 | }, 138 | "extensionDependencies": [] 139 | } 140 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as vscode from 'vscode'; 3 | 4 | import { 5 | filesInDir, 6 | getDirectoriesRecursive, 7 | isIgnoredByGitignore, 8 | replaceBackslashes, 9 | } from './utils/fileUtils'; 10 | import { 11 | disposeItem, 12 | getExtensionSetting, 13 | getGlobalSetting, 14 | } from './utils/vscodeUtils'; 15 | 16 | let runOnContextMenuDisposable: vscode.Disposable | undefined; 17 | let commandRunDisposable: vscode.Disposable | undefined; 18 | let eventConfigurationDisposable: vscode.Disposable | undefined; 19 | 20 | const DEFAULT_GLOBAL_EXCLUDE: string[] = []; 21 | const DEFAULT_EXCLUDE_PATTERN: string[] = [ 22 | '**/build', 23 | '**/node_modules', 24 | '**/.*', 25 | '**/.vscode', 26 | ]; 27 | const DEFAULT_INCLUDE_PATTERN: string[] = ['*']; 28 | const DEFAULT_SAVE_FORMAT: boolean = true; 29 | const DEFAULT_CLOSE_FORMAT: boolean = false; 30 | 31 | const globalExclude: string[] = DEFAULT_GLOBAL_EXCLUDE; 32 | let excludePattern: string[] = DEFAULT_EXCLUDE_PATTERN; 33 | let includePattern: string[] = DEFAULT_INCLUDE_PATTERN; 34 | let saveAfterFormat: boolean = DEFAULT_SAVE_FORMAT; 35 | let closeAfterSave: boolean = DEFAULT_CLOSE_FORMAT; 36 | 37 | let workspaceFolder: string | undefined; 38 | export const EXTENSION_NAME = 'Workspace_Formatter'; 39 | 40 | export let extensionContext: vscode.ExtensionContext | undefined; 41 | export let extensionState: vscode.Memento | undefined; 42 | export let extensionPath: string | undefined; 43 | 44 | export function activate(context: vscode.ExtensionContext) { 45 | if ( 46 | !vscode.workspace.workspaceFolders || 47 | vscode.workspace.workspaceFolders.length === 0 48 | ) { 49 | return; 50 | } 51 | 52 | if ( 53 | !vscode.workspace.workspaceFolders[0] || 54 | !vscode.workspace.workspaceFolders[0].uri 55 | ) { 56 | return; 57 | } 58 | 59 | if (vscode.workspace.workspaceFolders.length === 1) { 60 | workspaceFolder = vscode.workspace.workspaceFolders[0].uri.fsPath; 61 | } 62 | 63 | extensionContext = context; 64 | extensionPath = context.extensionPath; 65 | extensionState = context.workspaceState; 66 | 67 | loadGlobalExcludeSettings(); 68 | loadSettings(); 69 | 70 | initRunStatusBar(); 71 | initContextMenuDisposable(); 72 | initConfigurationChangeDisposable(); 73 | } 74 | 75 | export function deactivate() { 76 | disposeItem(runOnContextMenuDisposable); 77 | disposeItem(commandRunDisposable); 78 | disposeItem(eventConfigurationDisposable); 79 | } 80 | 81 | function loadGlobalExcludeSettings() { 82 | const globalExcludeObj = getGlobalSetting( 83 | 'files.exclude', 84 | DEFAULT_GLOBAL_EXCLUDE, 85 | ); 86 | 87 | const globalExcludeKeys = Object.keys(globalExcludeObj); 88 | 89 | for (const key of globalExcludeKeys) { 90 | if (globalExcludeObj[key] === true) { 91 | globalExclude.push(key); 92 | } 93 | } 94 | 95 | excludePattern.push(...globalExclude); 96 | } 97 | 98 | function loadSettings() { 99 | saveAfterFormat = getExtensionSetting('saveAfterFormat', DEFAULT_SAVE_FORMAT); 100 | closeAfterSave = getExtensionSetting('closeAfterSave', DEFAULT_CLOSE_FORMAT); 101 | includePattern = getExtensionSetting( 102 | 'includePattern', 103 | DEFAULT_INCLUDE_PATTERN, 104 | ); 105 | excludePattern = getExtensionSetting( 106 | 'excludePattern', 107 | DEFAULT_EXCLUDE_PATTERN, 108 | ); 109 | } 110 | 111 | function initConfigurationChangeDisposable() { 112 | if (eventConfigurationDisposable) return; 113 | 114 | eventConfigurationDisposable = vscode.workspace.onDidChangeConfiguration( 115 | (e: vscode.ConfigurationChangeEvent) => { 116 | const isChanged = e.affectsConfiguration(EXTENSION_NAME); 117 | 118 | if (isChanged) loadSettings(); 119 | }, 120 | ); 121 | 122 | extensionContext?.subscriptions.push(eventConfigurationDisposable); 123 | } 124 | 125 | function initContextMenuDisposable() { 126 | if (runOnContextMenuDisposable) return; 127 | 128 | runOnContextMenuDisposable = vscode.commands.registerCommand( 129 | `${EXTENSION_NAME}.runOnContextMenu`, 130 | async (clickedUriItem: vscode.Uri, selectedUriItems: vscode.Uri[]) => { 131 | const files: string[] = []; 132 | 133 | if (selectedUriItems.length > 0) { 134 | for (const selectedUriItem of selectedUriItems) { 135 | if (!selectedUriItem) continue; 136 | 137 | const selectedItem = replaceBackslashes( 138 | selectedUriItem.fsPath.toString(), 139 | ); 140 | files.push(...(getAllFiles(selectedItem) ?? [])); 141 | } 142 | } else { 143 | const clickedItem = replaceBackslashes( 144 | clickedUriItem.fsPath.toString(), 145 | ); 146 | files.push(...(getAllFiles(clickedItem) ?? [])); 147 | } 148 | 149 | if (!files) return; 150 | 151 | formatAllFiles(files); 152 | }, 153 | ); 154 | 155 | extensionContext?.subscriptions.push(runOnContextMenuDisposable); 156 | } 157 | 158 | function initRunStatusBar() { 159 | if (commandRunDisposable) return; 160 | 161 | const commandName = `${EXTENSION_NAME}.runOnWorkspace`; 162 | commandRunDisposable = vscode.commands.registerCommand( 163 | commandName, 164 | async () => { 165 | if (!workspaceFolder) return; 166 | 167 | const files = getAllFiles(workspaceFolder); 168 | 169 | if (!files) return; 170 | 171 | formatAllFiles(files); 172 | }, 173 | ); 174 | 175 | extensionContext?.subscriptions.push(commandRunDisposable); 176 | } 177 | 178 | function getOpenedFiles(): Set { 179 | const openedFiles = new Set(); 180 | vscode.workspace.textDocuments.forEach((document) => { 181 | if (!document.isClosed) { 182 | const filename = document.fileName.toString(); 183 | if (fs.existsSync(filename)) { 184 | openedFiles.add(replaceBackslashes(filename)); 185 | } 186 | } 187 | }); 188 | return openedFiles; 189 | } 190 | 191 | function getAllFiles(startingDirectory: string) { 192 | console.log(`Getting all files from: ${startingDirectory}`); 193 | 194 | // Get workspace root for .gitignore filtering 195 | const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || ''; 196 | 197 | // Get all directories recursively 198 | let allDirectories = getDirectoriesRecursive( 199 | startingDirectory, 200 | includePattern, 201 | excludePattern, 202 | ); 203 | 204 | // Add the starting directory itself 205 | const normalizedStartingDir = replaceBackslashes(startingDirectory); 206 | if (allDirectories) { 207 | if (!allDirectories.includes(normalizedStartingDir)) { 208 | allDirectories.push(normalizedStartingDir); 209 | } 210 | } else { 211 | allDirectories = [normalizedStartingDir]; 212 | } 213 | 214 | // Filter out directories that are ignored by .gitignore 215 | if (workspaceRoot) { 216 | allDirectories = allDirectories.filter(dir => !isIgnoredByGitignore(dir, workspaceRoot)); 217 | } 218 | 219 | console.log(`Found ${allDirectories.length} directories to process`); 220 | 221 | // Get all files from all directories 222 | const allFiles: string[] = []; 223 | allDirectories.forEach((dir) => { 224 | const filesInDirectory = filesInDir(dir, includePattern, excludePattern); 225 | allFiles.push(...filesInDirectory); 226 | }); 227 | 228 | console.log(`Found ${allFiles.length} files to format`); 229 | return allFiles; 230 | } 231 | 232 | function formatAllFiles(files: string[]) { 233 | const increment = (1 / files.length) * 100; 234 | 235 | const progressOptions: vscode.ProgressOptions = { 236 | location: vscode.ProgressLocation.Notification, 237 | title: 'Formatting files', 238 | cancellable: true, 239 | }; 240 | 241 | const openedFiles = getOpenedFiles(); 242 | 243 | vscode.window.withProgress( 244 | progressOptions, 245 | async ( 246 | progress: vscode.Progress<{ 247 | message?: string; 248 | increment?: number; 249 | }>, 250 | cancellationToken: vscode.CancellationToken, 251 | ) => { 252 | for (const [i, file] of files.entries()) { 253 | if (file === undefined) break; 254 | if (cancellationToken.isCancellationRequested) break; 255 | 256 | try { 257 | progress.report({ 258 | message: `${i + 1}/${files.length}`, 259 | }); 260 | const document: vscode.TextDocument = await vscode.workspace.openTextDocument( 261 | file, 262 | ); 263 | await vscode.window.showTextDocument(document, { 264 | preserveFocus: false, 265 | preview: true, 266 | }); 267 | await vscode.commands.executeCommand( 268 | 'editor.action.formatDocument', 269 | document.uri, 270 | ); 271 | if (saveAfterFormat) { 272 | await vscode.commands.executeCommand( 273 | 'workbench.action.files.save', 274 | document.uri, 275 | ); 276 | } 277 | if (closeAfterSave) { 278 | const filename = replaceBackslashes(document.fileName.toString()); 279 | 280 | // Don't close files that were already open before formatting 281 | if (openedFiles.has(filename)) { 282 | console.log(`Skipping close for already open file: ${filename}`); 283 | continue; 284 | } 285 | 286 | // Check if the current tab is pinned before closing it 287 | const isPinned = await isTabPinned(document.uri); 288 | if (isPinned) { 289 | console.log(`Skipping close for pinned file: ${filename}`); 290 | continue; 291 | } 292 | 293 | // Close the editor 294 | try { 295 | await vscode.commands.executeCommand( 296 | 'workbench.action.closeActiveEditor', 297 | document.uri, 298 | ); 299 | console.log(`Closed editor for: ${filename}`); 300 | } catch (closeError) { 301 | console.error(`Error closing editor for ${filename}:`, closeError); 302 | } 303 | } 304 | } catch (exception) { 305 | console.log(`Could not format file ${file}`); 306 | } 307 | progress.report({ 308 | increment: increment, 309 | }); 310 | } 311 | }, 312 | ); 313 | } 314 | 315 | // Function to check if a tab is pinned 316 | async function isTabPinned(uri: vscode.Uri): Promise { 317 | try { 318 | // Get all tab groups 319 | const tabGroups = vscode.window.tabGroups; 320 | 321 | // Check all groups for the tab with matching URI 322 | for (const group of tabGroups.all) { 323 | for (const tab of group.tabs) { 324 | // Type guard for tab input with URI 325 | const hasUri = (input: unknown): input is { uri: vscode.Uri } => 326 | typeof input === 'object' && input !== null && 'uri' in input; 327 | 328 | // Check if this tab represents our document 329 | if (tab.input && hasUri(tab.input)) { 330 | // Compare URIs using fsPath for more reliable comparison 331 | const tabPath = tab.input.uri.fsPath; 332 | const docPath = uri.fsPath; 333 | 334 | if (tabPath === docPath) { 335 | console.log(`Tab for ${docPath} is ${tab.isPinned ? 'pinned' : 'not pinned'}`); 336 | return tab.isPinned || false; 337 | } 338 | } 339 | } 340 | } 341 | } catch (error) { 342 | console.error('Error checking if tab is pinned:', error); 343 | } 344 | 345 | return false; 346 | } 347 | -------------------------------------------------------------------------------- /src/utils/fileUtils.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as minimatch from 'minimatch'; 3 | import * as path from 'path'; 4 | import * as vscode from 'vscode'; 5 | 6 | export function replaceBackslashes(text: string) { 7 | return text.replace(/\\/g, '/'); 8 | } 9 | 10 | // Function to check if a file is ignored by .gitignore 11 | export function isIgnoredByGitignore(filePath: string, rootDir: string): boolean { 12 | try { 13 | // Normalize paths for consistent comparison 14 | filePath = replaceBackslashes(filePath); 15 | rootDir = replaceBackslashes(rootDir); 16 | 17 | // Find all .gitignore files from the file path up to the root directory 18 | const gitignoreFiles: string[] = []; 19 | let currentDir = path.dirname(filePath); 20 | 21 | // Add root .gitignore first (if it exists) 22 | const rootGitignore = path.join(rootDir, '.gitignore'); 23 | if (fs.existsSync(rootGitignore)) { 24 | gitignoreFiles.push(rootGitignore); 25 | } 26 | 27 | // Then add all parent directory .gitignore files 28 | while (currentDir.startsWith(rootDir)) { 29 | const gitignorePath = path.join(currentDir, '.gitignore'); 30 | if (fs.existsSync(gitignorePath) && gitignorePath !== rootGitignore) { 31 | gitignoreFiles.push(gitignorePath); 32 | } 33 | // Move up one directory 34 | const parentDir = path.dirname(currentDir); 35 | if (parentDir === currentDir) { 36 | break; // Prevent infinite loop at root 37 | } 38 | currentDir = parentDir; 39 | } 40 | 41 | // Check each .gitignore file, starting from the closest one (reverse order) 42 | for (const gitignorePath of gitignoreFiles.reverse()) { 43 | try { 44 | const content = fs.readFileSync(gitignorePath, 'utf8'); 45 | const patterns = parseGitignoreContent(content); 46 | const gitignoreDir = path.dirname(gitignorePath); 47 | const relativePath = path.relative(gitignoreDir, filePath); 48 | const normalizedPath = replaceBackslashes(relativePath); 49 | 50 | console.log(`Checking if ${normalizedPath} matches patterns in ${gitignorePath}`); 51 | 52 | for (const pattern of patterns) { 53 | if (minimatch(normalizedPath, pattern, { dot: true })) { 54 | console.log(`File ${filePath} is ignored by pattern ${pattern} in ${gitignorePath}`); 55 | return true; 56 | } 57 | } 58 | } catch (error) { 59 | console.error(`Error reading .gitignore file: ${gitignorePath}`, error); 60 | } 61 | } 62 | } catch (error) { 63 | console.error(`Error checking .gitignore for ${filePath}:`, error); 64 | } 65 | 66 | return false; 67 | } 68 | 69 | // Parse .gitignore content into an array of patterns 70 | function parseGitignoreContent(content: string): string[] { 71 | return content 72 | .split('\n') 73 | .map(line => line.trim()) 74 | .filter(line => line && !line.startsWith('#')) // Remove empty lines and comments 75 | .map(pattern => { 76 | // Handle negated patterns (those starting with !) 77 | if (pattern.startsWith('!')) { 78 | // For now, we'll just skip negated patterns as they're more complex to handle 79 | // In a full implementation, we would need to track both included and excluded files 80 | return ''; // Skip this pattern 81 | } 82 | 83 | // Handle directory patterns (ending with /) 84 | if (pattern.endsWith('/')) { 85 | return pattern + '**'; // Match all files under this directory 86 | } 87 | 88 | // Handle patterns with wildcards 89 | if (pattern.includes('*')) { 90 | return pattern; // Already a glob pattern 91 | } 92 | 93 | // For simple patterns without wildcards, match both the file and any directories with that name 94 | if (!pattern.includes('/')) { 95 | return `{${pattern},${pattern}/**}`; // Match file or directory 96 | } 97 | 98 | return pattern; 99 | }) 100 | .filter(pattern => pattern !== ''); // Remove any skipped patterns 101 | } 102 | 103 | export function getDirectoriesRecursive( 104 | dir: fs.PathLike, 105 | includePattern: string[], 106 | excludePattern: string[], 107 | ) { 108 | const directories = foldersInDir(dir, includePattern, excludePattern); 109 | 110 | if (directories.length === 0) return; 111 | 112 | directories.forEach((dir) => 113 | getDirectoriesRecursive( 114 | dir, 115 | includePattern, 116 | excludePattern, 117 | )?.forEach((newDir) => directories.push(newDir)), 118 | ); 119 | 120 | return directories; 121 | } 122 | 123 | export function readDir(dir: string | fs.PathLike) { 124 | try { 125 | return fs.readdirSync(dir, { withFileTypes: true }); 126 | } catch (err) { 127 | return undefined; 128 | } 129 | } 130 | 131 | export function filesInDir( 132 | dir: string, 133 | includePattern: string[], 134 | excludePattern: string[], 135 | ) { 136 | const fileDirents = readDir(dir); 137 | 138 | if (!fileDirents) return []; 139 | 140 | let files = fileDirents 141 | .filter((file) => file.isFile()) 142 | .map((file) => replaceBackslashes(file.name)); 143 | 144 | if (files.length === 0) return []; 145 | 146 | if (includePattern.length > 0) { 147 | files = includePatternFromList(includePattern, files, false); 148 | } 149 | 150 | if (excludePattern.length > 0) { 151 | files = excludePatternFromList(excludePattern, files, false); 152 | } 153 | 154 | if (files.length > 0) { 155 | files = files.map((file: string) => 156 | replaceBackslashes(path.join(dir, file)), 157 | ); 158 | } 159 | 160 | // Filter out files that are ignored by .gitignore 161 | const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || ''; 162 | if (workspaceRoot) { 163 | files = files.filter(file => !isIgnoredByGitignore(file, workspaceRoot)); 164 | } 165 | 166 | return files; 167 | } 168 | 169 | export function includePatternFromList( 170 | excludeSearch: string[], 171 | stringList: string[], 172 | isFolders: boolean = true, 173 | ) { 174 | let result: string[] = []; 175 | 176 | for (const pattern of excludeSearch) { 177 | if (isFolders && pattern.includes('/')) { 178 | result.push(...stringList.filter((str) => minimatch(str, pattern))); 179 | } else if (!isFolders) { 180 | result.push(...stringList.filter((str) => minimatch(str, pattern))); 181 | } 182 | } 183 | 184 | if (isFolders && result.length === 0) { 185 | result = stringList; 186 | } 187 | 188 | return result; 189 | } 190 | 191 | export function excludePatternFromList( 192 | excludeSearch: string[], 193 | stringList: string[], 194 | isFolders: boolean = true, 195 | ) { 196 | for (const pattern of excludeSearch) { 197 | if (isFolders && pattern.includes('/')) { 198 | stringList = stringList.filter((str) => !minimatch(str, pattern)); 199 | } else if (!isFolders && !pattern.includes('/')) { 200 | stringList = stringList.filter((str) => !minimatch(str, pattern)); 201 | } 202 | } 203 | 204 | return stringList; 205 | } 206 | 207 | export function foldersInDir( 208 | dir: fs.PathLike, 209 | includePattern: string[], 210 | excludePattern: string[], 211 | ) { 212 | const fileDirents = readDir(dir); 213 | 214 | if (!fileDirents) return []; 215 | 216 | const folders = fileDirents.filter((folder) => folder.isDirectory()); 217 | let folderNames = folders.map((folder) => 218 | replaceBackslashes(path.join(dir.toString(), folder.name)), 219 | ); 220 | 221 | if (folderNames.length === 0) return []; 222 | 223 | if (includePattern.length > 0) { 224 | folderNames = includePatternFromList(includePattern, folderNames, true); 225 | } 226 | 227 | if (excludePattern.length > 0) { 228 | folderNames = excludePatternFromList(excludePattern, folderNames, true); 229 | } 230 | 231 | // Filter out folders that are ignored by .gitignore 232 | const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || ''; 233 | if (workspaceRoot) { 234 | folderNames = folderNames.filter(folder => !isIgnoredByGitignore(folder, workspaceRoot)); 235 | } 236 | 237 | return folderNames; 238 | } 239 | -------------------------------------------------------------------------------- /src/utils/vscodeUtils.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | import { EXTENSION_NAME } from '../extension'; 4 | 5 | export function disposeItem(disposableItem: vscode.Disposable | undefined) { 6 | disposableItem?.dispose(); 7 | } 8 | 9 | export function getExtensionSetting(name_: string, defaultValue: any) { 10 | const name: string = `${EXTENSION_NAME}.${name_}`; 11 | 12 | const settingsValue = vscode.workspace.getConfiguration().get(name); 13 | 14 | if (settingsValue === undefined) { 15 | return defaultValue; 16 | } 17 | 18 | return settingsValue; 19 | } 20 | 21 | export function getGlobalSetting(name: string, defaultValue: any) { 22 | const settingsValue = vscode.workspace.getConfiguration().get(name); 23 | 24 | if (settingsValue === undefined) { 25 | return defaultValue; 26 | } 27 | 28 | return settingsValue; 29 | } 30 | -------------------------------------------------------------------------------- /test.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "dist", 6 | "lib": ["es6", "dom"], 7 | "sourceMap": true, 8 | "rootDir": ".", 9 | "removeComments": true, 10 | "noImplicitUseStrict": true 11 | }, 12 | "include": ["./test"] 13 | } 14 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | reset: 2 | rm -rf ./testAssets/**/* 3 | rm -rf ./testAssets/* 4 | cp -r ./testTemplates/**/{.,}* ./testAssets/ 2>&1 5 | -------------------------------------------------------------------------------- /test/testTemplates/inner/inner_inner/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char **argv) 5 | { 6 | printf("argc: %d\n", argc); 7 | for (int i = 0; i < argc; i++) 8 | { 9 | printf("argv[%d]: %s\n", i, argv[i]); 10 | } 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /test/testTemplates/inner/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char **argv) 5 | { 6 | printf("argc: %d\n", argc); 7 | for (int i = 0; i < argc; i++) 8 | { 9 | printf("argv[%d]: %s\n", i, argv[i]); 10 | } 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /test/testTemplates/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char **argv) 5 | { 6 | printf("argc: %d\n", argc); 7 | for (int i = 0; i < argc; i++) 8 | { 9 | printf("argv[%d]: %s\n", i, argv[i]); 10 | } 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /test/testTemplatesmain.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/franneck94/Vscode-Workspace-Formatter/68d4e5a7bc603eaa0de8fafb990c142399dc0b3e/test/testTemplatesmain.pdb -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "ES2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2020', or 'ESNEXT'. */, 5 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 6 | "lib": [ 7 | "es6", 8 | "ES2020" 9 | ] /* Specify library files to be included in the compilation. */, 10 | "sourceMap": true /* Generates corresponding '.map' file. */, 11 | "outDir": "dist" /* Redirect output structure to the directory. */, 12 | "rootDir": "./" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, 13 | "removeComments": true /* Do not emit comments to output. */, 14 | 15 | /* Strict Type-Checking Options */ 16 | "strict": true /* Enable all strict type-checking options. */, 17 | "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, 18 | "strictNullChecks": true /* Enable strict null checks. */, 19 | "strictFunctionTypes": true /* Enable strict checking of function types. */, 20 | "strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */, 21 | "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, 22 | "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, 23 | "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, 24 | 25 | /* Additional Checks */ 26 | "noUnusedLocals": true /* Report errors on unused locals. */, 27 | "noUnusedParameters": true /* Report errors on unused parameters. */, 28 | "noImplicitReturns": false /* Report error when not all code paths in function return a value. */, 29 | "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, 30 | "noUncheckedIndexedAccess": true /* Include 'undefined' in index signature results */, 31 | "noImplicitOverride": false /* Ensure overriding members in derived classes are marked with an 'override' modifier. */, 32 | "noPropertyAccessFromIndexSignature": true /* Require undeclared properties from index signatures to use element accesses. */, 33 | 34 | /* Module Resolution Options */ 35 | "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, 36 | "esModuleInterop": false /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 37 | 38 | /* Advanced Options */ 39 | "skipLibCheck": true /* Skip type checking of declaration files. */, 40 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, 41 | "experimentalDecorators": true, 42 | "resolveJsonModule": true 43 | }, 44 | "include": ["src/**/*.ts"], 45 | "exclude": ["node_modules", ".vscode", "out", "dist"] 46 | } 47 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 3 | 'use strict'; 4 | 5 | const path = require('path'); 6 | const webpack = require('webpack'); 7 | 8 | /**@type {import('webpack').Configuration}*/ 9 | const config = { 10 | target: 'webworker', // vscode extensions run in webworker context for VS Code web 📖 -> https://webpack.js.org/configuration/target/#target 11 | 12 | entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ 13 | output: { 14 | // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ 15 | path: path.resolve(__dirname, 'dist'), 16 | filename: 'extension.js', 17 | libraryTarget: 'commonjs2', 18 | devtoolModuleFilenameTemplate: '../[resource-path]', 19 | }, 20 | devtool: 'source-map', 21 | externals: { 22 | 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/ 23 | }, 24 | resolve: { 25 | // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader 26 | mainFields: ['browser', 'module', 'main'], // look for `browser` entry point in imported node modules 27 | extensions: ['.ts', '.js'], 28 | alias: { 29 | // provides alternate implementation for node module and source files 30 | }, 31 | fallback: { 32 | // Webpack 5 no longer polyfills Node.js core modules automatically. 33 | // see https://webpack.js.org/configuration/resolve/#resolvefallback 34 | // for the list of Node.js core module polyfills. 35 | path: false, 36 | fs: false, 37 | }, 38 | }, 39 | module: { 40 | rules: [ 41 | { 42 | test: /\.ts$/, 43 | exclude: /node_modules/, 44 | use: [ 45 | { 46 | loader: 'ts-loader', 47 | }, 48 | ], 49 | }, 50 | ], 51 | }, 52 | }; 53 | module.exports = config; 54 | --------------------------------------------------------------------------------