├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── images ├── check-jsx-extension.png ├── copy_with_line_number.gif ├── jsonToCode.gif ├── logo.png ├── pluralize.gif ├── remove-typescript-types.gif ├── remove_comments.gif ├── remove_irregular_whitespace.gif ├── space_god.gif ├── transform_color_format.gif └── transform_module_imports.gif ├── package.json ├── pnpm-lock.yaml ├── scripts ├── esbuild.ts └── tsconfig.json ├── src ├── checkJsxFileExtension │ └── index.ts ├── extension.ts ├── gotoDeclaration │ └── index.ts ├── jsUnicodePreview │ ├── index.js │ └── util.js ├── jsonToObject │ └── index.ts ├── pluralize │ └── index.ts ├── removeComments │ ├── index.ts │ └── postcssDiscardComments │ │ ├── commentParser.ts │ │ ├── commentRemover.ts │ │ └── index.ts ├── removeIrregularWhitespace │ └── index.ts ├── removeTsTypes │ └── index.ts ├── shellCommands │ ├── activeFileESLintConfig.ts │ ├── activeFileESLintPerformance.ts │ ├── activeFileStylelintConfig.ts │ ├── forceESLint.ts │ ├── forceMarkdownlint.ts │ ├── forcePrettier.ts │ ├── forceStylelint.ts │ └── runShellCommand.ts ├── spaceGod │ └── index.ts ├── terminalOutputBackup │ ├── clearTerminalWithOutputBackup.ts │ ├── common.ts │ └── openTerminalOutputBackup.ts ├── transformColorFormat │ └── index.ts ├── transformESSyntax │ ├── ES5ToES6.ts │ ├── index.ts │ ├── tscCompile.ts │ ├── type.ts │ └── typescript.ts ├── tsconfig.json ├── typings │ ├── index.d.ts │ └── modules.d.ts └── utils │ ├── ast.ts │ ├── constants.ts │ ├── editor.ts │ ├── fs.ts │ ├── log.ts │ └── store.ts ├── test-workspace ├── Comp.ts ├── colorFormat.txt ├── gotoImport.tsx ├── jsUnicodePreview │ ├── test.js │ └── test.vue ├── jsonToCode │ └── test.js ├── plur.txt ├── removeComments │ ├── test.css │ ├── test.html │ ├── test.js │ ├── test.jsonc │ ├── test.less │ ├── test.scss │ └── test.vue ├── removeTsTypes.ts ├── spaceGod.txt ├── test.ts ├── transformESSyntax │ ├── ES5ToES6.js │ └── tscCompile.js └── transformModule │ ├── cjsToEsm.js │ └── esmToCjs.js ├── test ├── index.ts ├── runTests.ts ├── sample.test.ts └── tsconfig.json └── tsconfig.base.json /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | test: 13 | strategy: 14 | matrix: 15 | os: [macos-latest, ubuntu-latest, windows-latest] 16 | runs-on: ${{ matrix.os }} 17 | outputs: 18 | GIT_TAG: ${{ steps.set-tag.outputs.GIT_TAG }} 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | 23 | - name: Install Node.js 24 | uses: actions/setup-node@v4 25 | with: 26 | node-version: 18.x 27 | 28 | - name: Install pnpm 29 | uses: pnpm/action-setup@v3 30 | id: pnpm-install 31 | with: 32 | version: 9 33 | run_install: false 34 | 35 | - name: Get pnpm store directory 36 | id: pnpm-cache 37 | shell: bash 38 | run: | 39 | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT 40 | 41 | - name: Setup pnpm cache 42 | uses: actions/cache@v4 43 | with: 44 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} 45 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 46 | restore-keys: ${{ runner.os }}-pnpm-store- 47 | 48 | - name: Install dependencies 49 | run: pnpm install --frozen-lockfile --no-optional 50 | 51 | - name: Get the date on Ubuntu/MacOS 52 | id: date_unix 53 | if: runner.os != 'Windows' 54 | run: echo "DATE=$(date +'%Y%m%d')" >> $GITHUB_OUTPUT 55 | 56 | - name: Get the date on Windows 57 | id: date_windows 58 | if: runner.os == 'Windows' 59 | run: echo "DATE=$(Get-Date -Format 'yyyyMMdd')" >> $GITHUB_OUTPUT 60 | 61 | - name: Cache .vscode-test 62 | uses: actions/cache@v4 63 | env: 64 | # we use date as part of key because the vscode insiders updated daily 65 | CACHE_PREFIX: ${{ runner.os }}-vscode-test-${{ steps.date_unix.outputs.DATE || steps.date_windows.outputs.DATE }} 66 | with: 67 | path: .vscode-test 68 | key: ${{ env.CACHE_PREFIX }}-${{ hashFiles('test/runTests.ts') }} 69 | restore-keys: ${{ env.CACHE_PREFIX }} 70 | 71 | - run: xvfb-run -a pnpm test 72 | if: runner.os == 'Linux' 73 | - run: pnpm test 74 | if: runner.os != 'Linux' 75 | 76 | - name: Set GIT_TAG 77 | id: set-tag 78 | if: runner.os == 'Linux' 79 | run: | 80 | git fetch --tags origin 81 | GIT_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") 82 | if [ -n "$GIT_TAG" ] && [ "$(git rev-list -n 1 $GIT_TAG 2>/dev/null || echo "")" = "$(git rev-parse HEAD)" ]; then 83 | echo "GIT_TAG=${GIT_TAG}" >> $GITHUB_OUTPUT 84 | else 85 | echo "GIT_TAG=''" >> $GITHUB_OUTPUT 86 | fi 87 | 88 | publish: 89 | needs: test 90 | if: startsWith(needs.test.outputs.GIT_TAG, 'v') 91 | runs-on: ubuntu-latest 92 | steps: 93 | - name: Checkout 94 | uses: actions/checkout@v4 95 | with: 96 | fetch-depth: 0 97 | 98 | - name: Install Node.js 99 | uses: actions/setup-node@v4 100 | with: 101 | node-version: 20.x 102 | 103 | - name: Install pnpm 104 | uses: pnpm/action-setup@v3 105 | id: pnpm-install 106 | with: 107 | version: 9 108 | run_install: false 109 | 110 | - name: Get pnpm store directory 111 | id: pnpm-cache 112 | shell: bash 113 | run: | 114 | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT 115 | 116 | - name: Setup pnpm cache 117 | uses: actions/cache@v4 118 | with: 119 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} 120 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 121 | restore-keys: | 122 | ${{ runner.os }}-pnpm-store- 123 | 124 | - name: Install dependencies 125 | run: pnpm install --frozen-lockfile --no-optional 126 | 127 | - name: Publish to Visual Studio Marketplace 128 | run: pnpm run publish:vs-marketplace 129 | env: 130 | VSCE_PAT: ${{ secrets.VS_MARKETPLACE_TOKEN }} 131 | 132 | - name: Publish to Open VSX Registry 133 | run: pnpm run publish:open-vsx -p ${{ secrets.OPEN_VSX_TOKEN }} 134 | 135 | - name: Github Release 136 | run: npx changelogithub 137 | env: 138 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 139 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # generated by yo code 2 | out 3 | .vscode-test/ 4 | *.vsix 5 | 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # Diagnostic reports (https://nodejs.org/api/report.html) 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | *.lcov 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 34 | .grunt 35 | 36 | # Bower dependency directory (https://bower.io/) 37 | bower_components 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (https://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directories 46 | node_modules/ 47 | jspm_packages/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Microbundle cache 59 | .rpt2_cache/ 60 | .rts2_cache_cjs/ 61 | .rts2_cache_es/ 62 | .rts2_cache_umd/ 63 | 64 | # Optional REPL history 65 | .node_repl_history 66 | 67 | # Output of 'npm pack' 68 | *.tgz 69 | 70 | # Yarn Integrity file 71 | .yarn-integrity 72 | 73 | # dotenv environment variables file 74 | .env 75 | .env.test 76 | 77 | # parcel-bundler cache (https://parceljs.org/) 78 | .cache 79 | 80 | # Next.js build output 81 | .next 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | .vscode/* 109 | !.vscode/settings.json 110 | !.vscode/tasks.json 111 | !.vscode/launch.json 112 | !.vscode/extensions.json 113 | *.code-workspace 114 | 115 | # Windows thumbnail cache files 116 | Thumbs.db 117 | Thumbs.db:encryptable 118 | ehthumbs.db 119 | ehthumbs_vista.db 120 | 121 | # Dump file 122 | *.stackdump 123 | 124 | # Folder config file 125 | [Dd]esktop.ini 126 | 127 | # Recycle Bin used on file shares 128 | $RECYCLE.BIN/ 129 | 130 | # Windows Installer files 131 | *.cab 132 | *.msi 133 | *.msix 134 | *.msm 135 | *.msp 136 | 137 | # Windows shortcuts 138 | *.lnk 139 | 140 | # custom 141 | *.zip 142 | 143 | # apple 144 | .DS_Store 145 | 146 | # esbuild 147 | stats.html 148 | meta.json 149 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry="https://registry.npmmirror.com/" 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | test-workspace -------------------------------------------------------------------------------- /.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 | "esbenp.prettier-vscode", 7 | "connor4312.esbuild-problem-matchers", 8 | "github.vscode-github-actions" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.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 | "args": [ 13 | // "--disable-extensions", 14 | "--extensionDevelopmentPath=${workspaceFolder}", 15 | "${workspaceFolder}/test-workspace" 16 | ], 17 | "outFiles": ["${workspaceFolder}/out/**/*.js"], 18 | "skipFiles": [ 19 | "/**", 20 | "**/node_modules/**", 21 | "**/resources/app/out/vs/**", 22 | "**/.vscode-insiders/extensions/", 23 | "**/.vscode/extensions/" 24 | ], 25 | "sourceMaps": true, 26 | "env": { 27 | "VSCODE_DEBUG_MODE": "true" 28 | }, 29 | "preLaunchTask": "${defaultBuildTask}" 30 | }, 31 | { 32 | "name": "Extension Tests", 33 | "type": "extensionHost", 34 | "request": "launch", 35 | "args": [ 36 | "--disable-extensions", 37 | "--extensionDevelopmentPath=${workspaceFolder}", 38 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 39 | ], 40 | "outFiles": ["${workspaceFolder}/out/test/**/*.js"], 41 | "preLaunchTask": "${defaultBuildTask}" 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.validate": ["typescript", "json", "jsonc", "markdown"] 3 | } 4 | -------------------------------------------------------------------------------- /.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": "esbuild:watch", 9 | "problemMatcher": "$esbuild-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | # develop 2 | .github 3 | .vscode/ 4 | typings/ 5 | configs/ 6 | scripts/ 7 | src/ 8 | .husky 9 | 10 | # test and output 11 | .vscode-test/ 12 | test/ 13 | test-workspace/ 14 | out/ 15 | !out/extension.js 16 | .nyc_output/ 17 | coverage/ 18 | **/*.js.map 19 | meta.json 20 | stats.html 21 | 22 | # configs 23 | .gitignore 24 | .vscodeignore 25 | tsconfig.json 26 | tsconfig.test.json 27 | tsconfig.base.json 28 | .nvmrc 29 | .editorconfig 30 | .eslintrc.js 31 | .eslintignore 32 | .prettierrc 33 | .prettierignore 34 | yarn.lock 35 | pnpm-lock.yaml 36 | .npmrc 37 | 38 | images 39 | !images/logo.png 40 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v0.13.0 (2024-10-04) 2 | 3 | ### 🚀 Features 4 | 5 | - Use bundled ts-blank-space implement remove ts types ([ace90f4](https://github.com/tjx666/vscode-fe-helper/commit/ace90f4)) 6 | 7 | ### ❤️ Contributors 8 | 9 | - 余腾靖 ([@tjx666](http://github.com/tjx666)) 10 | 11 | [View changes on GitHub](https://github.com/tjx666/vscode-fe-helper/compare/v0.12.1...v0.13.0 '2024-10-04') 12 | 13 | ## v0.12.1 (2024-09-01) 14 | 15 | ### 🚀 Features 16 | 17 | - Goto the first highlight when declaration not found ([85e51fa](https://github.com/tjx666/vscode-fe-helper/commit/85e51fa)) 18 | 19 | ### ❤️ Contributors 20 | 21 | - 余腾靖 ([@tjx666](http://github.com/tjx666)) 22 | 23 | [View changes on GitHub](https://github.com/tjx666/vscode-fe-helper/compare/v0.12.0...v0.12.1 '2024-09-01') 24 | 25 | ## v0.12.0 (2024-09-01) 26 | 27 | ### 🚀 Features 28 | 29 | - New command goto `Goto Declaration` ([46680b0](https://github.com/tjx666/vscode-fe-helper/commit/46680b0)) 30 | 31 | ### ❤️ Contributors 32 | 33 | - 余腾靖 ([@tjx666](http://github.com/tjx666)) 34 | 35 | [View changes on GitHub](https://github.com/tjx666/vscode-fe-helper/compare/v0.11.0...v0.12.0 '2024-09-01') 36 | 37 | ## v0.11.0 (2024-05-19) 38 | 39 | ### 🚀 Features 40 | 41 | - Add setting vscode-fe-helper.check-jsx-extension.fileExtensions ([98490b8](https://github.com/tjx666/vscode-fe-helper/commit/98490b8)) 42 | 43 | ### ❤️ Contributors 44 | 45 | - 余腾靖 ([@tjx666](http://github.com/tjx666)) 46 | 47 | [View changes on GitHub](https://github.com/tjx666/vscode-fe-helper/compare/v0.10.1...v0.11.0 '2024-05-19') 48 | 49 | ## v0.10.1 (2024-05-16) 50 | 51 | No significant changes 52 | 53 | [View changes on GitHub](https://github.com/tjx666/vscode-fe-helper/compare/v0.10.0...v0.10.1 '2024-05-16') 54 | 55 | ## v0.10.0 (2024-05-16) 56 | 57 | ### 🚀 Features 58 | 59 | - Support check jsx extension ([699c3b4](https://github.com/tjx666/vscode-fe-helper/commit/699c3b4)) 60 | - Upgrade deps ([33191d6](https://github.com/tjx666/vscode-fe-helper/commit/33191d6)) 61 | 62 | ### ❤️ Contributors 63 | 64 | - 余腾靖 ([@tjx666](http://github.com/tjx666)) 65 | 66 | [View changes on GitHub](https://github.com/tjx666/vscode-fe-helper/compare/v0.9.1...v0.10.0 '2024-05-16') 67 | 68 | ## v0.9.1 (2024-05-04) 69 | 70 | No significant changes 71 | 72 | [View changes on GitHub](https://github.com/tjx666/vscode-fe-helper/compare/v0.9.0...v0.9.1 '2024-05-04') 73 | 74 | ## v0.9.0 (2024-05-04) 75 | 76 | ### 🚀 Features 77 | 78 | - Support output command env when run shell command ([536414a](https://github.com/tjx666/vscode-fe-helper/commit/536414a)) 79 | 80 | ### ❤️ Contributors 81 | 82 | - YuTengjing 83 | 84 | [View changes on GitHub](https://github.com/tjx666/vscode-fe-helper/compare/v0.8.2...v0.9.0 '2024-05-04') 85 | 86 | ## [0.8.1] - 2024-01-10 87 | 88 | ### Fixed 89 | 90 | - readme `Remove TypeScript Types` gif disappear 91 | 92 | ## [0.8.0] - 2024-01-10 93 | 94 | ### Added 95 | 96 | - support `Remove Typescript Types` 97 | 98 | ## [0.7.1] - 2024-01-04 99 | 100 | ### Added 101 | 102 | - migrate code from 103 | 104 | ## [0.7.0] - 2023-04-28 105 | 106 | ### Braking Change 107 | 108 | - remove transform module feature because typescript has builtin support 109 | 110 | ## [0.6.2] - 2023-04-27 111 | 112 | ### Changed 113 | 114 | - update deps 115 | - adjust README title case 116 | 117 | ## [0.6.1] - 2023-04-19 118 | 119 | ### Fixed 120 | 121 | - doesn't restore origin clipboard content after clear terminal 122 | - doesn't remove the overflow terminal output backups 123 | 124 | ## [0.6.0] - 2023-04-18 125 | 126 | ### Added 127 | 128 | - add new extension icon, thanks 129 | 130 | ## [0.5.0] - 2023-04-18 131 | 132 | ### Added 133 | 134 | some useful frontEnd tools commands: 135 | 136 | - `FE Helper: Force Prettier` 137 | - `FE Helper: Force ESLint` 138 | - `FE Helper: Force Stylelint` 139 | - `FE Helper: Force Markdownlint` 140 | - `FE Helper: Show Active File ESLint Performance` 141 | - `FE Helper: Show Active File ESLint Config` 142 | - `FE Helper: Show Active File Stylelint Config` 143 | 144 | ## [0.4.0] - 2023-04-10 145 | 146 | ### Added 147 | 148 | - new command `Clear Terminal with Output Backup` 149 | - new command `Open Terminal Output Backup` 150 | 151 | ### Fixed 152 | 153 | - remove comments of js file failed 154 | 155 | ## [0.3.3] - 2023-03-20 156 | 157 | ### Changed 158 | 159 | - reduce extension size 160 | 161 | ## [0.3.2] - 2023-03-20 162 | 163 | ### Changed 164 | 165 | - just some code refactor 166 | 167 | ## [0.3.1] - 2023-03-20 168 | 169 | ### Changed 170 | 171 | - log support syntax highlight 172 | - remove bundled typescript 173 | 174 | ## [0.3.0] - 2023-01-29 175 | 176 | ### Changed 177 | 178 | - migrate copy commands to [clipboard master](https://marketplace.visualstudio.com/items?itemName=YuTengjing.clipboard-master) 179 | 180 | ## [0.2.7] - 2023-01-22 181 | 182 | ### Fixed 183 | 184 | - remove comments of vue file 185 | 186 | ### Changed 187 | 188 | - upgrade deps 189 | 190 | ## [0.2.6] - 2022-08-25 191 | 192 | ### Fixed 193 | 194 | - remove comments failed 195 | 196 | ## [0.2.5] - 2022-08-24 197 | 198 | ### Added 199 | 200 | - new command: `FE Helper: Copy as Markdown Code Block` 201 | 202 | ## [0.2.4] - 2022-08-13 203 | 204 | ### Fixed 205 | 206 | - remove xml,jsonc comments 207 | 208 | ### Changed 209 | 210 | - auto format after remove comments 211 | 212 | ## [0.2.3] - 2022-08-13 213 | 214 | ### Fixed 215 | 216 | - cjsToEsm 217 | 218 | ## [0.2.2] - 2022-08-06 219 | 220 | ### Fixed 221 | 222 | - module transform doesn't work 223 | 224 | ### Changed 225 | 226 | - optimize startup speed 227 | - tsCompile will prefer to use local installed typescript 228 | 229 | ## Added 230 | 231 | - smart copy 232 | 233 | ## [0.2.1] - 2022-07-06 234 | 235 | ### Fixed 236 | 237 | - remove comments doesn't work 238 | - upgrade dependencies 239 | 240 | ## [0.2.0] - 2022-02-27 241 | 242 | ### Changed 243 | 244 | - upgrade dependencies 245 | - optimize startup speed 246 | 247 | ## [0.1.5] - 2021-12-4 248 | 249 | ### Changed 250 | 251 | - upgrade dependencies 252 | 253 | ## [0.1.4] - 2021-10-15 254 | 255 | ### Fixed 256 | 257 | - can not require typescript error 258 | 259 | ## [0.1.3] - 2021-10-14 260 | 261 | ### Fixed 262 | 263 | - 264 | 265 | ## [0.1.2] - 2021-10-13 266 | 267 | ### Added 268 | 269 | - transform ECMAScript syntax 270 | 271 | ## [0.1.1] - 2021-10-07 272 | 273 | ### Added 274 | 275 | - copy text without syntax 276 | 277 | ## [0.0.10] - 2021-05-01 278 | 279 | ### Changed 280 | 281 | - upgrade dependencies to latest 282 | 283 | ## [0.0.9] - 2020-12-24 284 | 285 | ### Added 286 | 287 | - `space god` and `copy with line number` usage GIF. 288 | 289 | ## [0.0.8] - 2020-12-22 290 | 291 | ### Added 292 | 293 | - space god 294 | - copy with line number 295 | 296 | ## [0.0.7] - 2020-11-25 297 | 298 | ### Changed 299 | 300 | - update all dependencies to latest 301 | - upgrade webpack5 302 | 303 | ## [0.0.6] - 2020-09-23 304 | 305 | ### Added 306 | 307 | - support paste JSON as code 308 | 309 | #### Changed 310 | 311 | - update all dependencies to latest 312 | 313 | ## [0.0.5] - 2020-08-02 314 | 315 | ### Added 316 | 317 | - add more document 318 | 319 | #### Changed 320 | 321 | - update all dependencies to latest 322 | 323 | ## [0.0.4] - 2020-07-10 324 | 325 | ### Added 326 | 327 | - support transform color format 328 | 329 | ## [0.0.3] - 2020-07-8 330 | 331 | ### Added 332 | 333 | - support remove irregular whitespace 334 | 335 | ## [0.0.2] - 2020-05-29 336 | 337 | ### Fixed 338 | 339 | - remove comments from ignore file doesn't work 340 | 341 | ## [0.0.1] - 2020-05-29 342 | 343 | ### Added 344 | 345 | - remove comments 346 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2021] [YuTengjing] 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 | # VSCode FE Helper 2 | 3 | [![Version](https://img.shields.io/visual-studio-marketplace/v/YuTengjing.vscode-fe-helper)](https://marketplace.visualstudio.com/items/YuTengjing.vscode-fe-helper/changelog) [![Installs](https://img.shields.io/visual-studio-marketplace/i/YuTengjing.vscode-fe-helper)](https://marketplace.visualstudio.com/items?itemName=YuTengjing.vscode-fe-helper) [![Downloads](https://img.shields.io/visual-studio-marketplace/d/YuTengjing.vscode-fe-helper)](https://marketplace.visualstudio.com/items?itemName=YuTengjing.vscode-fe-helper) [![Rating Star](https://img.shields.io/visual-studio-marketplace/stars/YuTengjing.vscode-fe-helper)](https://marketplace.visualstudio.com/items?itemName=YuTengjing.vscode-fe-helper&ssr=false#review-details) [![Last Updated](https://img.shields.io/visual-studio-marketplace/last-updated/YuTengjing.vscode-fe-helper)](https://github.com/tjx666/vscode-fe-helper) 4 | 5 | ![CI](https://github.com/tjx666/vscode-fe-helper/actions/workflows/ci.yml/badge.svg) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com) [![Percentage of issues still open](https://isitmaintained.com/badge/open/tjx666/vscode-fe-helper.svg)](http://isitmaintained.com/project/tjx666/vscode-fe-helper') [![MIT License](https://img.shields.io/github/license/tjx666/vscode-fe-helper)](https://github.com/tjx666/vscode-fe-helper/blob/main/LICENSE) 6 | 7 | ## Usage 8 | 9 | All the features are used by run command. You can show command plate by keyboard shortcut ctrl++P on **Windows** or ++P on **MacOS**. All commands provided by this extension is prefixed with `FE Helper:`. 10 | 11 | ## Features 12 | 13 | ### Remove Comments 14 | 15 | command: `FE Helper: Remove Comments` 16 | 17 | supported languages: 18 | 19 | - html/xml 20 | - css/sass/scss/less 21 | - javascript/javascriptreact/typescript/typescriptreact 22 | - jsonc 23 | - vue 24 | - markdown 25 | - editorconfig 26 | - yaml 27 | - ignore (eg: .gitignore, .eslintignore) 28 | 29 | ![Remove Comments](https://github.com/tjx666/vscode-fe-helper/raw/main/images/remove_comments.gif?raw=true) 30 | 31 | ### Transform ECMAScript Syntax 32 | 33 | Command: `FE Helper: Transform ECMAScript Syntax` 34 | 35 | For now, supports: 36 | 37 | - ES5 to ES6/ES7 38 | - Using tsc compile code to ES5 39 | - Using tsc compile code to ES3 40 | 41 | ### Remove TypeScript Types 42 | 43 | Command `FE Helper: Remove TypeScript Types` 44 | 45 | ![Remove TypeScript Types](https://github.com/tjx666/vscode-fe-helper/blob/main/images/remove-typescript-types.gif?raw=true) 46 | 47 | ### Pluralize 48 | 49 | command: `FE Helper: Pluralize` 50 | 51 | Pluralize all the words selected in current active editor. 52 | 53 | ![Pluralize](https://github.com/tjx666/vscode-fe-helper/raw/main/images/pluralize.gif?raw=true) 54 | 55 | ### Remove Irregular Whitespace 56 | 57 | Command: `FE Helper: Remove Irregular Whitespace` 58 | 59 | Sometime I copy description from LeetCode problem and paste into VSCode, but there are some irregular whitespace in the text. For that time, this feature is very useful and convenient. 60 | 61 | ![Remove Irregular Whitespace](https://github.com/tjx666/vscode-fe-helper/raw/main/images/remove_irregular_whitespace.gif?raw=true) 62 | 63 | ### Transform Color Format 64 | 65 | Command: `FE Helper: Transform Color Format` 66 | 67 | supported formats: 68 | 69 | - hex 70 | - rgb/rgba 71 | - cmyk 72 | - hsv 73 | - hsl 74 | - ansi16 75 | - ansi256 76 | 77 | ![Transform Color Format](https://github.com/tjx666/vscode-fe-helper/raw/main/images/transform_color_format.gif?raw=true) 78 | 79 | ### Paste JSON as Object 80 | 81 | You can copy JSON content, and paste as JavaScript code. The principle behind this functionality is very simple: 82 | 83 | ```javascript 84 | const jsCode = jsonFromClipboard.replaceAll(/"([^"]*)"\s*:/g, '$1:'); 85 | ``` 86 | 87 | ![Paste JSON as Object](https://github.com/tjx666/vscode-fe-helper/raw/main/images/jsonToCode.gif?raw=true) 88 | 89 | ### SpaceGod 90 | 91 | For Chinese users, there should be space between English word, number, and punctuation. It's very convenient to add space between them by command `FE Helper: SpaceGod`。 92 | 93 | ![SpaceGod](https://github.com/tjx666/vscode-fe-helper/raw/main/images/space_god.gif?raw=true) 94 | 95 | ### Other Useful FrontEnd Tools Commands 96 | 97 | - `FE Helper: Force Prettier` 98 | - `FE Helper: Force ESLint` 99 | - `FE Helper: Force Stylelint` 100 | - `FE Helper: Force Markdownlint` 101 | - `FE Helper: Show Active File ESLint Performance` 102 | - `FE Helper: Show Active File ESLint Config` 103 | - `FE Helper: Show Active File Stylelint Config` 104 | 105 | ### JS Unicode Preview 106 | 107 | check [vscode-js-unicode-preview](https://github.com/kufii/vscode-js-unicode-preview) for more details: 108 | 109 | **settings**: 110 | 111 | `vscode-fe-helper.js-unicode-preview.languages`: An array of language ids to add the previews on. Defaults to `["javascript", "javascriptreact", "typescript", "typescriptreact"]` 112 | 113 | `vscode-fe-helper.js-unicode-preview.inline`: Boolean whether or not to show the previews inline. Defaults to `true`. 114 | 115 | `vscode-fe-helper.js-unicode-preview.hover`: Boolean whether or not to show the previews on hover. Defaults to `true`. 116 | 117 | ### Check Jsx Extension 118 | 119 | settings: 120 | 121 | ```json 122 | { 123 | "vscode-fe-helper.check-jsx-extension.fileExtensions": [".jsx", ".tsx"] 124 | } 125 | ``` 126 | 127 | ![check js extension screenshot](https://github.com/tjx666/vscode-fe-helper/blob/main/images/check-jsx-extension.png?raw=true) 128 | 129 | ### Goto Declaration 130 | 131 | command: `FE Helper: Goto Declaration` 132 | 133 | only for javascript/typescript/javascriptreact/typescriptreact file. Unlike built-in `Go to Definition`, this command only work for current file, if the identifier is imported, will jump to it's import statement instead of definition. 134 | 135 | If can't find the declaration, will try to find the first highlight of the identifier. 136 | 137 | ## My extensions 138 | 139 | - [Open in External App](https://github.com/tjx666/open-in-external-app) 140 | - [Package Manager Enhancer](https://github.com/tjx666/package-manager-enhancer) 141 | - [Neo File Utils](https://github.com/tjx666/vscode-neo-file-utils) 142 | - [VSCode FE Helper](https://github.com/tjx666/vscode-fe-helper) 143 | - [VSCode archive](https://github.com/tjx666/vscode-archive) 144 | - [Better Colorizer](https://github.com/tjx666/better-colorizer/tree/main) 145 | - [Modify File Warning](https://github.com/tjx666/modify-file-warning) 146 | - [Power Edit](https://github.com/tjx666/power-edit) 147 | - [Reload Can Solve Any Problems](https://github.com/tjx666/reload-can-solve-any-problems) 148 | 149 | Check all here: [publishers/YuTengjing](https://marketplace.visualstudio.com/publishers/YuTengjing) 150 | -------------------------------------------------------------------------------- /images/check-jsx-extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjx666/vscode-fe-helper/e417e5bf6f2e34890d9d1c7de61df706f876d797/images/check-jsx-extension.png -------------------------------------------------------------------------------- /images/copy_with_line_number.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjx666/vscode-fe-helper/e417e5bf6f2e34890d9d1c7de61df706f876d797/images/copy_with_line_number.gif -------------------------------------------------------------------------------- /images/jsonToCode.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjx666/vscode-fe-helper/e417e5bf6f2e34890d9d1c7de61df706f876d797/images/jsonToCode.gif -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjx666/vscode-fe-helper/e417e5bf6f2e34890d9d1c7de61df706f876d797/images/logo.png -------------------------------------------------------------------------------- /images/pluralize.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjx666/vscode-fe-helper/e417e5bf6f2e34890d9d1c7de61df706f876d797/images/pluralize.gif -------------------------------------------------------------------------------- /images/remove-typescript-types.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjx666/vscode-fe-helper/e417e5bf6f2e34890d9d1c7de61df706f876d797/images/remove-typescript-types.gif -------------------------------------------------------------------------------- /images/remove_comments.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjx666/vscode-fe-helper/e417e5bf6f2e34890d9d1c7de61df706f876d797/images/remove_comments.gif -------------------------------------------------------------------------------- /images/remove_irregular_whitespace.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjx666/vscode-fe-helper/e417e5bf6f2e34890d9d1c7de61df706f876d797/images/remove_irregular_whitespace.gif -------------------------------------------------------------------------------- /images/space_god.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjx666/vscode-fe-helper/e417e5bf6f2e34890d9d1c7de61df706f876d797/images/space_god.gif -------------------------------------------------------------------------------- /images/transform_color_format.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjx666/vscode-fe-helper/e417e5bf6f2e34890d9d1c7de61df706f876d797/images/transform_color_format.gif -------------------------------------------------------------------------------- /images/transform_module_imports.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjx666/vscode-fe-helper/e417e5bf6f2e34890d9d1c7de61df706f876d797/images/transform_module_imports.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-fe-helper", 3 | "displayName": "VSCode FE Helper", 4 | "version": "0.13.0", 5 | "packageManager": "pnpm@9.12.0", 6 | "preview": true, 7 | "description": "Provide some useful tools for front end development", 8 | "publisher": "YuTengjing", 9 | "author": { 10 | "name": "YuTengjing", 11 | "url": "https://github.com/tjx666", 12 | "email": "ytj2713151713@gmail.com" 13 | }, 14 | "license": "SEE LICENSE IN LICENSE", 15 | "badges": [ 16 | { 17 | "url": "https://img.shields.io/badge/License-MIT-brightgreen.svg", 18 | "description": "License: MIT", 19 | "href": "https://github.com/tjx666/vscode-fe-helper/blob/master/LICENSE" 20 | }, 21 | { 22 | "url": "https://img.shields.io/badge/PRs-welcome-brightgreen.svg", 23 | "description": "PRs Welcome", 24 | "href": "https://github.com/tjx666/vscode-fe-helper" 25 | } 26 | ], 27 | "homepage": "https://github.com/tjx666/vscode-fe-helper/blob/master/README.md", 28 | "bugs": { 29 | "url": "https://github.com/tjx666/vscode-fe-helper/issues", 30 | "email": "ytj2713151713@gmail.com" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "https://github.com/tjx666/vscode-fe-helper" 35 | }, 36 | "engines": { 37 | "vscode": "^1.90.0" 38 | }, 39 | "categories": [ 40 | "Other" 41 | ], 42 | "keywords": [ 43 | "fe", 44 | "front end", 45 | "helper", 46 | "comment", 47 | "import", 48 | "pluralize", 49 | "color", 50 | "whitespace", 51 | "copy", 52 | "lebab", 53 | "ES3", 54 | "ES5", 55 | "ES6", 56 | "prettier", 57 | "eslint", 58 | "stylelint", 59 | "markdownlint", 60 | "unicode" 61 | ], 62 | "main": "./out/extension.js", 63 | "icon": "images/logo.png", 64 | "activationEvents": [ 65 | "onStartupFinished" 66 | ], 67 | "contributes": { 68 | "commands": [ 69 | { 70 | "command": "VSCodeFEHelper.removeComments", 71 | "title": "FE Helper: Remove Comments" 72 | }, 73 | { 74 | "command": "VSCodeFEHelper.transformESSyntax", 75 | "title": "FE Helper: Transform ECMAScript Syntax" 76 | }, 77 | { 78 | "command": "VSCodeFEHelper.removeTsTypes", 79 | "title": "FE Helper: Remove TypeScript Types" 80 | }, 81 | { 82 | "command": "VSCodeFEHelper.pluralize", 83 | "title": "FE Helper: Pluralize" 84 | }, 85 | { 86 | "command": "VSCodeFEHelper.removeIrregularWhitespace", 87 | "title": "FE Helper: Remove Irregular Whitespace" 88 | }, 89 | { 90 | "command": "VSCodeFEHelper.transformColorFormat", 91 | "title": "FE Helper: Transform Color Format" 92 | }, 93 | { 94 | "command": "VSCodeFEHelper.jsonToObject", 95 | "title": "FE Helper: Paste JSON as Object" 96 | }, 97 | { 98 | "command": "VSCodeFEHelper.spaceGod", 99 | "title": "FE Helper: SpaceGod" 100 | }, 101 | { 102 | "command": "VSCodeFEHelper.clearTerminalWithOutputBackup", 103 | "title": "FE Helper: Clear Terminal with Output Backup" 104 | }, 105 | { 106 | "command": "VSCodeFEHelper.openTerminalOutputBackup", 107 | "title": "FE Helper: Open Terminal Output Backup" 108 | }, 109 | { 110 | "command": "VSCodeFEHelper.forcePrettier", 111 | "title": "FE Helper: Force Prettier" 112 | }, 113 | { 114 | "command": "VSCodeFEHelper.forceESLint", 115 | "title": "FE Helper: Force ESLint" 116 | }, 117 | { 118 | "command": "VSCodeFEHelper.forceStylelint", 119 | "title": "FE Helper: Force Stylelint" 120 | }, 121 | { 122 | "command": "VSCodeFEHelper.forceMarkdownlint", 123 | "title": "FE Helper: Force Markdownlint" 124 | }, 125 | { 126 | "command": "VSCodeFEHelper.activeFileESLintPerformance", 127 | "title": "FE Helper: Show Active File ESLint Performance" 128 | }, 129 | { 130 | "command": "VSCodeFEHelper.activeFileESLintConfig", 131 | "title": "FE Helper: Show Active File ESLint Config" 132 | }, 133 | { 134 | "command": "VSCodeFEHelper.activeFileStylelintConfig", 135 | "title": "FE Helper: Show Active File Stylelint Config" 136 | }, 137 | { 138 | "command": "VSCodeFEHelper.gotoDeclaration", 139 | "title": "FE Helper: Goto Declaration" 140 | } 141 | ], 142 | "configuration": { 143 | "title": "VSCode FE Helper", 144 | "properties": { 145 | "vscode-fe-helper.js-unicode-preview.languages": { 146 | "type": "array", 147 | "scope": "resource", 148 | "default": [ 149 | "javascript", 150 | "javascriptreact", 151 | "typescript", 152 | "typescriptreact" 153 | ], 154 | "description": "The list of languages to add JS unicode previews to" 155 | }, 156 | "vscode-fe-helper.js-unicode-preview.inline": { 157 | "type": "boolean", 158 | "scope": "resource", 159 | "default": true, 160 | "description": "Show the unicode previews inline" 161 | }, 162 | "vscode-fe-helper.js-unicode-preview.hover": { 163 | "type": "boolean", 164 | "scope": "resource", 165 | "default": true, 166 | "description": "Show the unicode previews as a hover tooltip" 167 | }, 168 | "vscode-fe-helper.check-jsx-extension.fileExtensions": { 169 | "type": "array", 170 | "default": [ 171 | ".js", 172 | ".ts" 173 | ], 174 | "description": "The file extensions to check whether this file should be renamed to jsx" 175 | } 176 | } 177 | } 178 | }, 179 | "eslintConfig": { 180 | "extends": "@yutengjing/eslint-config-typescript", 181 | "ignorePatterns": [ 182 | "test-workspace", 183 | "src/jsUnicodePreview" 184 | ], 185 | "rules": { 186 | "jsdoc/tag-lines": 0 187 | } 188 | }, 189 | "prettier": "@yutengjing/prettier-config", 190 | "lint-staged": { 191 | "*.{js,ts,json,md}": [ 192 | "eslint --fix", 193 | "prettier --write" 194 | ] 195 | }, 196 | "simple-git-hooks": { 197 | "pre-commit": "npx lint-staged" 198 | }, 199 | "scripts": { 200 | "vscode:prepublish": "pnpm esbuild:base --minify", 201 | "preinstall": "npx only-allow pnpm", 202 | "postinstall": "stale-dep -u", 203 | "clean": "rimraf -rf ./out", 204 | "esbuild:base": "stale-dep && tsx scripts/esbuild.ts", 205 | "esbuild:watch": "pnpm esbuild:base --sourcemap --watch", 206 | "esbuild:analyze": "pnpm esbuild:base --minify --metafile --analyze && esbuild-visualizer --metadata ./meta.json --open", 207 | "compile:test": "pnpm clean && tsc -b ./test/tsconfig.json", 208 | "lint": "eslint src --ext ts", 209 | "test": "stale-dep && pnpm compile:test && node ./out/test/runTests.js", 210 | "package": "vsce package --no-dependencies", 211 | "release": "npx @yutengjing/release", 212 | "publish:vs-marketplace": "vsce publish --no-dependencies", 213 | "publish:open-vsx": "ovsx publish --no-dependencies" 214 | }, 215 | "devDependencies": { 216 | "@babel/types": "^7.25.7", 217 | "@types/babel__traverse": "^7.20.6", 218 | "@types/color": "^3.0.6", 219 | "@types/glob": "^8.1.0", 220 | "@types/mocha": "^10.0.8", 221 | "@types/node": "~20.16.10", 222 | "@types/pangu": "^4.0.2", 223 | "@types/pluralize": "^0.0.33", 224 | "@types/vscode": "~1.94.0", 225 | "@vscode/test-electron": "^2.4.1", 226 | "@vscode/vsce": "^3.1.1", 227 | "@yutengjing/eslint-config-typescript": "^1.2.5", 228 | "@yutengjing/prettier-config": "^1.3.0", 229 | "@yutengjing/release": "^0.3.1", 230 | "ast-types": "^0.14.2", 231 | "esbuild": "~0.24.0", 232 | "esbuild-visualizer": "^0.6.0", 233 | "eslint": "^8.57.1", 234 | "glob": "^11.0.0", 235 | "lint-staged": "^15.2.10", 236 | "mocha": "^10.7.3", 237 | "ovsx": "^0.9.5", 238 | "prettier": "^3.3.3", 239 | "rimraf": "^6.0.1", 240 | "simple-git-hooks": "^2.11.1", 241 | "stale-dep": "^0.7.0", 242 | "tsx": "^4.19.1" 243 | }, 244 | "dependencies": { 245 | "@babel/parser": "^7.25.7", 246 | "@babel/traverse": "^7.25.7", 247 | "color": "^4.2.3", 248 | "execa": "^9.4.0", 249 | "jsonc-parser": "^3.3.1", 250 | "lebab": "^3.2.4", 251 | "pangu": "^4.0.7", 252 | "pluralize": "^8.0.0", 253 | "postcss": "^8.4.47", 254 | "postcss-less": "^6.0.0", 255 | "postcss-scss": "^4.0.9", 256 | "recast": "^0.23.9", 257 | "resolve-from": "^5.0.0", 258 | "ts-blank-space": "^0.4.1", 259 | "typescript": "^5.6.2" 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /scripts/esbuild.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tsx 2 | 3 | import fs from 'node:fs/promises'; 4 | import path from 'node:path'; 5 | 6 | import type { BuildContext, BuildOptions } from 'esbuild'; 7 | import esbuild from 'esbuild'; 8 | 9 | const isWatchMode = process.argv.includes('--watch'); 10 | const options: BuildOptions = { 11 | color: true, 12 | logLevel: 'info', 13 | entryPoints: ['src/extension.ts'], 14 | bundle: true, 15 | metafile: process.argv.includes('--metafile'), 16 | outdir: './out', 17 | external: [ 18 | 'vscode', 19 | 'typescript', // vue-component-meta 20 | ], 21 | format: 'cjs', 22 | platform: 'node', 23 | target: 'ESNext', 24 | tsconfig: './src/tsconfig.json', 25 | sourcemap: process.argv.includes('--sourcemap'), 26 | minify: process.argv.includes('--minify'), 27 | plugins: [ 28 | { 29 | name: 'umd2esm', 30 | setup(build) { 31 | build.onResolve({ filter: /^(vscode-.*|estree-walker|jsonc-parser)/ }, (args) => { 32 | const pathUmdMay = require.resolve(args.path, { 33 | paths: [args.resolveDir], 34 | }); 35 | // Call twice the replace is to solve the problem of the path in Windows 36 | const pathEsm = pathUmdMay 37 | .replace('/umd/', '/esm/') 38 | .replace('\\umd\\', '\\esm\\'); 39 | return { path: pathEsm }; 40 | }); 41 | }, 42 | }, 43 | { 44 | name: 'meta', 45 | setup(build) { 46 | build.onEnd(async (result) => { 47 | if (result.metafile && result.errors.length === 0) { 48 | return fs.writeFile( 49 | path.resolve(__dirname, '../meta.json'), 50 | JSON.stringify(result.metafile), 51 | ); 52 | } 53 | }); 54 | }, 55 | }, 56 | ], 57 | }; 58 | 59 | async function main() { 60 | let ctx: BuildContext | undefined; 61 | try { 62 | if (isWatchMode) { 63 | ctx = await esbuild.context(options); 64 | await ctx.watch(); 65 | } else { 66 | const result = await esbuild.build(options); 67 | if (process.argv.includes('--analyze')) { 68 | const chunksTree = await esbuild.analyzeMetafile(result.metafile!, { color: true }); 69 | console.log(chunksTree); 70 | } 71 | } 72 | } catch (error) { 73 | console.error(error); 74 | ctx?.dispose(); 75 | process.exit(1); 76 | } 77 | } 78 | 79 | main(); 80 | -------------------------------------------------------------------------------- /scripts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "include": ["esbuild.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /src/checkJsxFileExtension/index.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises'; 2 | import { extname } from 'node:path'; 3 | 4 | import type { ASTNode } from 'ast-types'; 5 | import * as recast from 'recast'; 6 | import type { ExtensionContext } from 'vscode'; 7 | import vscode from 'vscode'; 8 | 9 | import { getSettings } from '../jsUnicodePreview/util'; 10 | import { parseSourceToAst } from '../utils/ast'; 11 | 12 | /** 13 | * 文件保存的时候,如果文件时 .js 或者 .ts,但是包含 jsx 元素,提示用户修改文件后缀为 .jsx 14 | */ 15 | export function checkJsxFileExtension(context: ExtensionContext) { 16 | let config = { fileExtensions: [] as string[] }; 17 | const settingSection = 'vscode-fe-helper.check-jsx-extension'; 18 | const updateConfig = () => { 19 | config = getSettings(settingSection, ['fileExtensions']); 20 | }; 21 | updateConfig(); 22 | 23 | vscode.workspace.onDidChangeConfiguration( 24 | async (event) => { 25 | if (event.affectsConfiguration(settingSection)) { 26 | await updateConfig(); 27 | } 28 | }, 29 | null, 30 | context.subscriptions, 31 | ); 32 | 33 | vscode.workspace.onDidSaveTextDocument( 34 | async (document) => { 35 | const { fsPath } = document.uri; 36 | const fileExt = extname(fsPath); 37 | const autoSave = vscode.workspace.getConfiguration().get('files.autoSave'); 38 | const autoSaveDelay = vscode.workspace 39 | .getConfiguration() 40 | .get('files.autoSaveDelay'); 41 | if ( 42 | document !== vscode.window.activeTextEditor?.document || 43 | !config.fileExtensions.includes(fileExt) || 44 | (autoSave === 'afterDelay' && autoSaveDelay && autoSaveDelay < 1000) 45 | ) { 46 | return; 47 | } 48 | 49 | // use fs to check file size too large 50 | const stat = await fs.stat(fsPath); 51 | // 50kb 52 | const sizeLimit = 1024 * 1024 * 100; 53 | if (stat.size > sizeLimit) { 54 | return; 55 | } 56 | 57 | const source = document.getText(); 58 | let ast: ASTNode; 59 | try { 60 | ast = parseSourceToAst(source); 61 | } catch { 62 | // ignore syntax error 63 | return; 64 | } 65 | 66 | const notify = async () => { 67 | const correctFileExt = fileExt === '.js' ? '.jsx' : '.tsx'; 68 | const changeAction = `Change to ${correctFileExt}`; 69 | const action = await vscode.window.showWarningMessage( 70 | `Your ${fileExt} code contains JSX elements, consider change the file extension to ${correctFileExt}!`, 71 | changeAction, 72 | ); 73 | if (action === changeAction) { 74 | await vscode.workspace.fs.rename( 75 | document.uri, 76 | document.uri.with({ 77 | path: fsPath.replace(fileExt, correctFileExt), 78 | }), 79 | ); 80 | } 81 | }; 82 | 83 | recast.visit(ast, { 84 | visitJSXElement() { 85 | notify(); 86 | return false; 87 | }, 88 | }); 89 | }, 90 | null, 91 | context.subscriptions, 92 | ); 93 | } 94 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises'; 2 | 3 | import type { TextEditor, TextEditorEdit } from 'vscode'; 4 | import vscode from 'vscode'; 5 | 6 | import { checkJsxFileExtension } from './checkJsxFileExtension'; 7 | import { jsUnicodePreview } from './jsUnicodePreview'; 8 | import { pathExists } from './utils/fs'; 9 | import { logger, shellLogger } from './utils/log'; 10 | import { store } from './utils/store'; 11 | 12 | export async function activate(context: vscode.ExtensionContext) { 13 | const { commands } = vscode; 14 | const extName = 'VSCodeFEHelper'; 15 | 16 | const storageDir = context.storageUri!.fsPath; 17 | store.storageDir = storageDir; 18 | if (!(await pathExists(storageDir))) { 19 | await fs.mkdir(storageDir); 20 | } 21 | 22 | jsUnicodePreview(context); 23 | checkJsxFileExtension(context); 24 | 25 | const registerCommand = ( 26 | commandName: string, 27 | callback: (...args: any[]) => any, 28 | thisArg?: any, 29 | ) => { 30 | const cmd = commands.registerCommand(`${extName}.${commandName}`, callback, thisArg); 31 | context.subscriptions.push(cmd); 32 | return cmd; 33 | }; 34 | 35 | const registerTextEditorCommand = ( 36 | commandName: string, 37 | callback: ( 38 | textEditor: vscode.TextEditor, 39 | edit: vscode.TextEditorEdit, 40 | ...args: any[] 41 | ) => void, 42 | thisArg?: any, 43 | ) => { 44 | const cmd = commands.registerTextEditorCommand( 45 | `${extName}.${commandName}`, 46 | callback, 47 | thisArg, 48 | ); 49 | context.subscriptions.push(cmd); 50 | return cmd; 51 | }; 52 | 53 | // jsUnicodePreview(context); 54 | 55 | registerTextEditorCommand( 56 | 'removeComments', 57 | async (editor: TextEditor, editBuilder: TextEditorEdit) => { 58 | const { RemoveComments } = await import('./removeComments'); 59 | return new RemoveComments(editor, editBuilder).handle(); 60 | }, 61 | ); 62 | 63 | registerTextEditorCommand('transformESSyntax', (editor: TextEditor) => 64 | import('./transformESSyntax').then((mod) => mod.transformESSyntax(editor)), 65 | ); 66 | 67 | registerTextEditorCommand('removeTsTypes', (editor: TextEditor) => 68 | import('./removeTsTypes').then((mod) => mod.removeTsTypes(editor)), 69 | ); 70 | 71 | registerTextEditorCommand('pluralize', (editor: TextEditor) => 72 | import('./pluralize').then((mod) => mod.plur(editor)), 73 | ); 74 | 75 | registerTextEditorCommand( 76 | 'removeIrregularWhitespace', 77 | (editor: TextEditor, editBuilder: TextEditorEdit) => 78 | import('./removeIrregularWhitespace').then((mod) => 79 | mod.removeIrregularWhitespace(editor, editBuilder), 80 | ), 81 | ); 82 | 83 | registerTextEditorCommand('transformColorFormat', (editor: TextEditor) => 84 | import('./transformColorFormat').then((mod) => mod.transformColorFormat(editor)), 85 | ); 86 | 87 | registerTextEditorCommand('jsonToObject', (editor: TextEditor) => 88 | import('./jsonToObject').then((mod) => mod.jsonToObject(editor)), 89 | ); 90 | 91 | registerTextEditorCommand('spaceGod', (editor: TextEditor) => 92 | import('./spaceGod').then((mod) => mod.spaceGod(editor)), 93 | ); 94 | 95 | registerCommand('clearTerminalWithOutputBackup', () => 96 | import('./terminalOutputBackup/clearTerminalWithOutputBackup').then((mod) => 97 | mod.clearTerminalWithOutputBackup(context), 98 | ), 99 | ); 100 | 101 | registerCommand('openTerminalOutputBackup', () => 102 | import('./terminalOutputBackup/openTerminalOutputBackup').then((mod) => 103 | mod.openTerminalOutputBackup(context), 104 | ), 105 | ); 106 | 107 | registerTextEditorCommand('forcePrettier', () => 108 | import('./shellCommands/forcePrettier').then((mod) => mod.forcePrettier()), 109 | ); 110 | 111 | registerTextEditorCommand('forceESLint', () => 112 | import('./shellCommands/forceESLint').then((mod) => mod.forceESLint()), 113 | ); 114 | 115 | registerTextEditorCommand('forceStylelint', () => 116 | import('./shellCommands/forceStylelint').then((mod) => mod.forceStylelint()), 117 | ); 118 | 119 | registerTextEditorCommand('forceMarkdownlint', () => 120 | import('./shellCommands/forceMarkdownlint').then((mod) => mod.forceMarkdownlint()), 121 | ); 122 | 123 | registerTextEditorCommand('activeFileESLintPerformance', () => 124 | import('./shellCommands/activeFileESLintPerformance').then((mod) => 125 | mod.activeFileESLintPerformance(), 126 | ), 127 | ); 128 | 129 | registerTextEditorCommand('activeFileESLintConfig', () => 130 | import('./shellCommands/activeFileESLintConfig').then((mod) => 131 | mod.activeFileESLintConfig(), 132 | ), 133 | ); 134 | 135 | registerTextEditorCommand('activeFileStylelintConfig', () => 136 | import('./shellCommands/activeFileStylelintConfig').then((mod) => 137 | mod.activeFileStylelintConfig(), 138 | ), 139 | ); 140 | 141 | registerTextEditorCommand('gotoDeclaration', (editor: TextEditor) => 142 | import('./gotoDeclaration').then((mod) => mod.gotoDeclaration(editor)), 143 | ); 144 | } 145 | 146 | export function deactivate(): void { 147 | logger.dispose(); 148 | shellLogger.dispose(); 149 | } 150 | -------------------------------------------------------------------------------- /src/gotoDeclaration/index.ts: -------------------------------------------------------------------------------- 1 | import type { Node, NodePath } from '@babel/traverse'; 2 | import traverse from '@babel/traverse'; 3 | import * as t from '@babel/types'; 4 | import type { TextEditor } from 'vscode'; 5 | import vscode, { Position, Range } from 'vscode'; 6 | 7 | import { parseSourceToAst } from '../utils/ast'; 8 | 9 | const NO_DEFINITION_OR_HIGHLIGHT = 'No definition or highlight found for'; 10 | const ERROR_ANALYZING_CODE = 'Error analyzing code'; 11 | const languageUseAst = new Set(['javascript', 'javascriptreact', 'typescript', 'typescriptreact']); 12 | 13 | export async function gotoDeclaration(editor: TextEditor): Promise { 14 | const document = editor.document; 15 | const position = editor.selection.active; 16 | const wordRange = document.getWordRangeAtPosition(position); 17 | 18 | if (!wordRange) return; 19 | 20 | const word = document.getText(wordRange); 21 | const sourceCode = document.getText(); 22 | 23 | try { 24 | if (languageUseAst.has(document.languageId)) { 25 | const ast = parseSourceToAst(sourceCode); 26 | const { definitionNode, definitionPath } = findDefinition(ast, word); 27 | 28 | if (definitionNode && definitionPath) { 29 | const range = getDefinitionRange(definitionNode); 30 | if (range) { 31 | gotoRange(editor, range); 32 | return; 33 | } 34 | } 35 | } 36 | 37 | // If no definition found, try to go to the first highlight 38 | const success = await gotoFirstHighlight(editor, position); 39 | if (!success) { 40 | vscode.window.setStatusBarMessage(`${NO_DEFINITION_OR_HIGHLIGHT} '${word}'`, 3000); 41 | } 42 | } catch { 43 | vscode.window.setStatusBarMessage(ERROR_ANALYZING_CODE, 3000); 44 | } 45 | } 46 | 47 | async function gotoFirstHighlight(editor: TextEditor, position: Position): Promise { 48 | const highlights = 49 | (await vscode.commands.executeCommand( 50 | 'vscode.executeDocumentHighlights', 51 | editor.document.uri, 52 | position, 53 | )) || []; 54 | 55 | if (highlights.length > 0) { 56 | const firstHighlight = highlights[0]; 57 | gotoRange(editor, firstHighlight.range); 58 | return true; 59 | } 60 | 61 | return false; 62 | } 63 | 64 | function gotoRange(editor: TextEditor, range: Range): void { 65 | editor.selection = new vscode.Selection(range.start, range.end); 66 | editor.revealRange(range, vscode.TextEditorRevealType.InCenter); 67 | } 68 | 69 | function findDefinition( 70 | ast: t.File, 71 | word: string, 72 | ): { definitionNode: Node | null; definitionPath: NodePath | null } { 73 | let definitionNode: Node | null = null; 74 | let definitionPath: NodePath | null = null; 75 | 76 | traverse(ast, { 77 | Identifier(path) { 78 | if (path.node.name === word) { 79 | const binding = path.scope.getBinding(word); 80 | if (binding) { 81 | definitionNode = binding.path.node; 82 | definitionPath = binding.path; 83 | path.stop(); 84 | } 85 | } 86 | }, 87 | }); 88 | 89 | return { definitionNode, definitionPath }; 90 | } 91 | 92 | function getDefinitionRange(node: t.Node): Range | null { 93 | let loc: t.SourceLocation | null | undefined; 94 | 95 | if (t.isVariableDeclarator(node) && t.isIdentifier(node.id)) { 96 | loc = node.id.loc; 97 | } else if (t.isFunctionDeclaration(node) || t.isClassDeclaration(node)) { 98 | loc = node.id?.loc ?? node.loc; 99 | } else if ( 100 | t.isImportSpecifier(node) || 101 | t.isImportDefaultSpecifier(node) || 102 | t.isImportNamespaceSpecifier(node) 103 | ) { 104 | loc = node.loc; 105 | } else { 106 | loc = (node as any).id?.loc ?? node.loc; 107 | } 108 | 109 | return loc 110 | ? new Range( 111 | new Position(loc.start.line - 1, loc.start.column), 112 | new Position(loc.end.line - 1, loc.end.column), 113 | ) 114 | : null; 115 | } 116 | -------------------------------------------------------------------------------- /src/jsUnicodePreview/index.js: -------------------------------------------------------------------------------- 1 | // reference: https://github.com/kufii/vscode-js-unicode-preview/blob/master/src/extension.js 2 | 3 | import { Range, Disposable, ThemeColor, window, workspace } from 'vscode'; 4 | import { getMatches, isUnicodePair, isUnicodeModifier, getSettings, curry } from './util'; 5 | 6 | let config; 7 | const settingSection = 'vscode-fe-helper.js-unicode-preview'; 8 | const updateConfig = () => { 9 | config = getSettings(settingSection, ['languages', 'inline', 'hover']); 10 | }; 11 | 12 | const setUnicodeDecorators = (editor, type) => { 13 | if (!editor || !config.languages.includes(editor.document.languageId)) return; 14 | 15 | const ifTrue = (bool, str) => (bool ? str : ''); 16 | const escapeRegex = /(? 18 | `\\\\(${ifTrue(!group, '?:')}[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?)`; 19 | const hexRegex = (group) => `\\\\x${ifTrue(group, '(')}[0-9A-Fa-f]{2}${ifTrue(group, ')')}`; 20 | const unicodeRegex = (group) => `\\\\u${ifTrue(group, '(')}[0-9A-Fa-f]{4}${ifTrue(group, ')')}`; 21 | const codePointRegex = (group) => 22 | `\\\\u\\{${ifTrue(group, '(')}[0-9A-Fa-f]+${ifTrue(group, ')')}\\}`; 23 | 24 | const toDecorator = ({ text, startPos, endPos }) => ({ 25 | range: new Range(editor.document.positionAt(startPos), editor.document.positionAt(endPos)), 26 | ...(config.hover && { hoverMessage: text }), 27 | ...(config.inline && { 28 | renderOptions: { 29 | after: { 30 | contentText: text, 31 | color: new ThemeColor('tab.activeForeground'), 32 | opacity: '0.55', 33 | }, 34 | }, 35 | }), 36 | }); 37 | 38 | const mergeModifiers = (decorators) => { 39 | let current; 40 | const merged = []; 41 | decorators.forEach((d) => { 42 | if (!current) current = d; 43 | else if (isUnicodeModifier(d.text)) { 44 | current.endPos = d.endPos; 45 | current.text += d.text; 46 | } else { 47 | merged.push(current); 48 | current = d; 49 | } 50 | }); 51 | merged.push(current); 52 | return merged; 53 | }; 54 | 55 | const matchToNum = curry((base, [_, group]) => parseInt(group, base)); 56 | 57 | const processNoPairs = curry((base, match, index) => { 58 | const text = String.fromCodePoint(matchToNum(base, match)); 59 | const startPos = index; 60 | const endPos = index + match[0].length; 61 | return { text, startPos, endPos }; 62 | }); 63 | const processHex = processNoPairs(16); 64 | const processOctal = processNoPairs(8); 65 | 66 | const processWithPairs = (str, index) => { 67 | const chars = getMatches(new RegExp(unicodeRegex(true), 'gu'), str).map(matchToNum(16)); 68 | const decorators = []; 69 | 70 | for (let i = 0; i < chars.length; i++) { 71 | const firstPair = chars[i]; 72 | const secondPair = i < chars.length - 1 && chars[i + 1]; 73 | const startPos = index + i * 6; 74 | const endPos = startPos + 6; 75 | if (secondPair && isUnicodePair(firstPair, secondPair)) { 76 | decorators.push({ 77 | text: String.fromCharCode(firstPair, secondPair), 78 | startPos, 79 | endPos: endPos + 6, 80 | }); 81 | i++; 82 | } else { 83 | decorators.push({ text: String.fromCodePoint(firstPair), startPos, endPos }); 84 | } 85 | } 86 | 87 | return decorators; 88 | }; 89 | 90 | const processSet = (match) => { 91 | const parts = match[0] 92 | .split( 93 | new RegExp( 94 | `(${[octalRegex(), hexRegex(), codePointRegex()].join( 95 | '|', 96 | )}|(?:${unicodeRegex()})+)`, 97 | 'u', 98 | ), 99 | ) 100 | .filter(Boolean); 101 | let index = match.index + match[1].length; 102 | const decorators = []; 103 | for (const part of parts) { 104 | let match = part.match(new RegExp(octalRegex(true), 'u')); 105 | if (match) decorators.push(processOctal(match, index)); 106 | match = part.match(new RegExp(hexRegex(true), 'u')); 107 | if (match) decorators.push(processHex(match, index)); 108 | match = part.match(new RegExp(unicodeRegex(), 'u')); 109 | if (match) decorators.push(...processWithPairs(part, index)); 110 | match = part.match(new RegExp(codePointRegex(true), 'u')); 111 | if (match) decorators.push(processHex(match, index)); 112 | index += part.length; 113 | } 114 | return mergeModifiers(decorators); 115 | }; 116 | 117 | const decorators = getMatches( 118 | new RegExp( 119 | `(${escapeRegex})(?:${[octalRegex(), hexRegex(), unicodeRegex(), codePointRegex()].join( 120 | '|', 121 | )})+`, 122 | 'gu', 123 | ), 124 | editor.document.getText(), 125 | ) 126 | .map(processSet) 127 | .flat() 128 | .map(toDecorator); 129 | 130 | editor.setDecorations(type, decorators); 131 | }; 132 | 133 | class DecoratorProvider extends Disposable { 134 | constructor() { 135 | super(() => this.dispose()); 136 | this.disposables = []; 137 | this.timeout = null; 138 | this.activeEditor = window.activeTextEditor; 139 | this.decorationType = window.createTextEditorDecorationType({}); 140 | setUnicodeDecorators(this.activeEditor, this.decorationType); 141 | 142 | window.onDidChangeActiveTextEditor( 143 | (editor) => { 144 | this.activeEditor = editor; 145 | editor && this.triggerUpdateDecorations(); 146 | }, 147 | this, 148 | this.disposables, 149 | ); 150 | 151 | workspace.onDidChangeTextDocument( 152 | (event) => 153 | this.activeEditor && 154 | event.document === this.activeEditor.document && 155 | this.triggerUpdateDecorations(), 156 | this, 157 | this.disposables, 158 | ); 159 | } 160 | 161 | dispose() { 162 | let d; 163 | while ((d = this.disposables.pop())) d.dispose(); 164 | } 165 | 166 | triggerUpdateDecorations() { 167 | if (this.timeout) return; 168 | this.timeout = setTimeout(() => { 169 | try { 170 | setUnicodeDecorators(this.activeEditor, this.decorationType); 171 | } catch (error) { 172 | console.error(error); 173 | } finally { 174 | this.timeout = null; 175 | } 176 | }, 300); 177 | } 178 | } 179 | 180 | export function jsUnicodePreview(context) { 181 | updateConfig(); 182 | workspace.onDidChangeConfiguration( 183 | async (event) => { 184 | if (event.affectsConfiguration(settingSection)) { 185 | await updateConfig(); 186 | } 187 | }, 188 | null, 189 | context.subscriptions, 190 | ); 191 | context.subscriptions.push(new DecoratorProvider()); 192 | } 193 | -------------------------------------------------------------------------------- /src/jsUnicodePreview/util.js: -------------------------------------------------------------------------------- 1 | import { workspace, window } from 'vscode'; 2 | 3 | const regexSurrogatePair = /([\uD800-\uDBFF])([\uDC00-\uDFFF])/; 4 | const regexModifier = /\p{Modifier_Symbol}|\p{Mark}/iu; 5 | 6 | export const isUnicodePair = (hex1, hex2) => 7 | regexSurrogatePair.test(String.fromCharCode(hex1, hex2)); 8 | 9 | export const isUnicodeModifier = (char) => regexModifier.test(char); 10 | 11 | export const getMatches = (regex, str) => { 12 | const matches = []; 13 | let match; 14 | while ((match = regex.exec(str))) { 15 | matches.push(match); 16 | } 17 | return matches; 18 | }; 19 | 20 | export const getSettings = (group, keys) => { 21 | const settings = workspace.getConfiguration(group, null); 22 | const editor = window.activeTextEditor; 23 | const language = editor && editor.document && editor.document.languageId; 24 | const languageSettings = 25 | language && workspace.getConfiguration(null, null).get(`[${language}]`); 26 | return keys.reduce((acc, k) => { 27 | acc[k] = languageSettings && languageSettings[`${group}.${k}`]; 28 | if (acc[k] == null) acc[k] = settings.get(k); 29 | return acc; 30 | }, {}); 31 | }; 32 | 33 | export const curry = 34 | (fn) => 35 | (...args) => 36 | args.length < fn.length ? curry(fn.bind(null, ...args)) : fn(...args); 37 | -------------------------------------------------------------------------------- /src/jsonToObject/index.ts: -------------------------------------------------------------------------------- 1 | import type { TextEditor } from 'vscode'; 2 | import vscode from 'vscode'; 3 | 4 | export async function jsonToObject(editor: TextEditor): Promise { 5 | const json = await vscode.env.clipboard.readText(); 6 | const jsCode = json.replaceAll(/"([^"]*)"\s*:/g, '$1:'); 7 | editor.edit((builder) => { 8 | const selectionText = editor.document.getText(editor.selection); 9 | if (selectionText.length === 0) { 10 | builder.insert(editor.selection.active, jsCode); 11 | } else { 12 | builder.replace(editor.selection, jsCode); 13 | } 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /src/pluralize/index.ts: -------------------------------------------------------------------------------- 1 | import pluralize from 'pluralize'; 2 | import type { TextEditor } from 'vscode'; 3 | 4 | export async function plur(editor: TextEditor): Promise { 5 | editor.edit((editorBuilder) => { 6 | const { document, selections } = editor; 7 | for (const selection of selections) { 8 | const word = document.getText(selection); 9 | const pluralizedWord = pluralize(word); 10 | editorBuilder.replace(selection, pluralizedWord); 11 | } 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /src/removeComments/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable prefer-template */ 2 | import type { ASTNode } from 'ast-types'; 3 | import * as jsonc from 'jsonc-parser'; 4 | import type { Result as PostcssProcessResult } from 'postcss'; 5 | import postcss from 'postcss'; 6 | import lessSyntax from 'postcss-less'; 7 | import scssSyntax from 'postcss-scss'; 8 | // !: 使用默认导入编译不报错,运行时报错 9 | import * as recast from 'recast'; 10 | import type { TextDocument, TextEditor, TextEditorEdit } from 'vscode'; 11 | import vscode, { Range } from 'vscode'; 12 | 13 | import { parseSourceToAst } from '../utils/ast'; 14 | import { ID_LANG_MAPPER } from '../utils/constants'; 15 | import { replaceAllTextOfEditor } from '../utils/editor'; 16 | import postcssDiscardComments from './postcssDiscardComments'; 17 | 18 | export class RemoveComments { 19 | private static readonly supportedMarkLangs = new Set(['html', 'xml', 'markdown']); 20 | private static readonly supportedScriptLangs = new Set([ 21 | 'javascript', 22 | 'typescript', 23 | 'javascriptreact', 24 | 'typescriptreact', 25 | ]); 26 | 27 | private static readonly supportedStyleLangs = new Map([ 28 | ['css', undefined], 29 | ['scss', scssSyntax], 30 | ['less', lessSyntax], 31 | ]); 32 | 33 | private static readonly supportedYamlCommentsLikeLangs = new Set(['yaml', 'editorconfig']); 34 | 35 | private readonly editor: TextEditor; 36 | private readonly editBuilder: TextEditorEdit; 37 | private readonly document: TextDocument; 38 | private readonly languageId: string; 39 | private readonly source: string; 40 | 41 | constructor(editor: TextEditor, editBuilder: TextEditorEdit) { 42 | this.editor = editor; 43 | this.editBuilder = editBuilder; 44 | this.document = editor.document; 45 | this.languageId = editor.document.languageId; 46 | this.source = editor.document.getText(); 47 | } 48 | 49 | public async handle(): Promise { 50 | const { languageId } = this; 51 | if (RemoveComments.supportedMarkLangs.has(languageId)) { 52 | this.removeMarkLanguageComments(); 53 | } else if (RemoveComments.supportedStyleLangs.has(languageId)) { 54 | await this.removeStyleComments(); 55 | } else if (RemoveComments.supportedScriptLangs.has(languageId)) { 56 | await this.removeScriptComments(); 57 | // eslint-disable-next-line unicorn/prefer-switch 58 | } else if (languageId === 'jsonc') { 59 | await this.removeJSONCComments(); 60 | } else if (languageId === 'vue') { 61 | await this.removeVueComments(); 62 | } else if (languageId === 'ignore') { 63 | this.removeIgnoreComments(); 64 | } else if (RemoveComments.supportedYamlCommentsLikeLangs.has(languageId)) { 65 | this.removeYamlComments(); 66 | } else { 67 | return; 68 | } 69 | 70 | return vscode.commands.executeCommand('editor.action.formatDocument'); 71 | } 72 | 73 | private removeCommentsMatchRegexp(commentRegexp: RegExp): void { 74 | const { editor, document, source } = this; 75 | let execResult: RegExpExecArray | null; 76 | editor.edit((editBuilder) => { 77 | // eslint-disable-next-line no-cond-assign 78 | while ((execResult = commentRegexp.exec(source))) { 79 | editBuilder.replace( 80 | new Range( 81 | document.positionAt(execResult.index), 82 | document.positionAt(execResult.index + execResult[0].length), 83 | ), 84 | '', 85 | ); 86 | } 87 | }); 88 | } 89 | 90 | private removeMarkLanguageComments(): void { 91 | const templateCommentRE = //g; 92 | this.removeCommentsMatchRegexp(templateCommentRE); 93 | } 94 | 95 | private static async getCommentsRemovedStyleCode( 96 | source: string, 97 | languageId: string, 98 | ): Promise { 99 | let result: PostcssProcessResult; 100 | try { 101 | result = await postcss([postcssDiscardComments]).process(source, { 102 | syntax: RemoveComments.supportedStyleLangs.get(languageId), 103 | from: undefined, 104 | }); 105 | } catch (error) { 106 | console.error(error); 107 | vscode.window.showErrorMessage( 108 | `Your ${ID_LANG_MAPPER.get(languageId)} code exists syntax error!`, 109 | ); 110 | return source; 111 | } 112 | return result.content; 113 | } 114 | 115 | private static getCommentsRemovedScriptCode(source: string) { 116 | let ast: ASTNode; 117 | try { 118 | ast = parseSourceToAst(source); 119 | } catch (error) { 120 | console.error(error); 121 | vscode.window.showErrorMessage(`Your script code exists syntax error!`); 122 | return source; 123 | } 124 | recast.visit(ast, { 125 | visitComment(path) { 126 | path.prune(); 127 | return false; 128 | }, 129 | }); 130 | 131 | return recast.print(ast).code; 132 | } 133 | 134 | private async removeStyleComments(): Promise { 135 | const { editor, source, languageId } = this; 136 | await replaceAllTextOfEditor( 137 | editor, 138 | await RemoveComments.getCommentsRemovedStyleCode(source, languageId), 139 | ); 140 | } 141 | 142 | private async removeScriptComments(): Promise { 143 | const { editor, document } = this; 144 | await replaceAllTextOfEditor( 145 | editor, 146 | RemoveComments.getCommentsRemovedScriptCode(document.getText()), 147 | ); 148 | } 149 | 150 | private async removeJSONCComments(): Promise { 151 | const { editor, document, source } = this; 152 | 153 | try { 154 | editor.edit((editBuilder) => { 155 | jsonc.visit(source, { 156 | onComment(offset: number, length: number) { 157 | editBuilder.delete( 158 | new Range( 159 | document.positionAt(offset), 160 | document.positionAt(offset + length), 161 | ), 162 | ); 163 | }, 164 | }); 165 | }); 166 | } catch (error) { 167 | console.error(error); 168 | vscode.window.showErrorMessage(`Your jsonc code exists syntax error!`); 169 | } 170 | } 171 | 172 | private async removeVueComments(): Promise { 173 | const { editor, document } = this; 174 | 175 | let source = document.getText(); 176 | const templateRE = /