├── .editorconfig ├── .github └── workflows │ ├── build.yaml │ └── release.yaml ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets └── icon.png ├── package.json ├── src ├── AbsolutePath.ts ├── Current.ts ├── SwiftFormatEditProvider.ts ├── UrlLiteral.ts ├── UserInteraction.ts ├── execShell.ts └── extension.ts ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = false -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: [main] 5 | pull_request: 6 | branches: [main] 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-node@v4 15 | with: 16 | node-version: 18 17 | 18 | - name: Install dependencies 19 | run: npm i 20 | 21 | - name: Build 22 | run: npm run compile 23 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-node@v4 15 | with: 16 | node-version: 18 17 | 18 | - name: Install dependencies 19 | run: npm i 20 | 21 | - name: Install vsce and ovsx 22 | run: npm i -g vsce ovsx 23 | 24 | - name: Publish for VS Code 25 | run: vsce publish -p ${{ secrets.VSCE_TOKEN }} 26 | 27 | - name: Publish for VS Codium 28 | run: ovsx publish -p ${{ secrets.OVSX_TOKEN }} 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | yarn-error.log 4 | npm-debug.log* 5 | *.vsix* -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.1.0", 4 | "configurations": [ 5 | { 6 | "name": "Launch Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}"], 11 | "stopOnEntry": false, 12 | "sourceMaps": true, 13 | "outFiles": ["${workspaceRoot}/out/src"], 14 | "preLaunchTask": "npm" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${fileBasename}: the current opened file's basename 5 | // ${fileDirname}: the current opened file's dirname 6 | // ${fileExtname}: the current opened file's extension 7 | // ${cwd}: the current working directory of the spawned process 8 | 9 | // A task runner that calls a custom npm script that compiles the extension. 10 | { 11 | "version": "2.0.0", 12 | 13 | // we want to run npm 14 | "command": "npm", 15 | 16 | "type": "shell", 17 | 18 | // show the output window only if unrecognized errors occur. 19 | "presentation": { "clear": true, "focus": true }, 20 | 21 | // we run the custom script "compile" as defined in package.json 22 | "args": ["run", "compile", "--loglevel", "silent"], 23 | 24 | // The tsc compiler is started in watching mode 25 | "isBackground": true, 26 | 27 | // use the standard tsc in watch mode problem matcher to find compile problems in the output. 28 | "problemMatcher": "$tsc-watch" 29 | } 30 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | typings/** 3 | out/test/** 4 | test/** 5 | src/** 6 | **/*.map 7 | .gitignore 8 | tsconfig.json 9 | vsc-extension-quickstart.md 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First of all, thanks for considering to improve Swift support for Visual Studio Code! 4 | In general most of these extensions are created similarly and share the general architecture. 5 | 6 | - [vscode-swiftlint](https://github.com/vknabel/vscode-swiftlint) 7 | - [vscode-swiftformat](https://github.com/vknabel/vscode-swiftformat) 8 | - [vscode-apple-swift-format](https://github.com/vknabel/vscode-apple-swift-format) 9 | 10 | ## Architecture 11 | 12 | Each of these project above have the following, central files. Many of them are shared, but might differ between extensions. When fixing bugs, it might make sense to replicate them to different extensions. 13 | 14 | - extension.ts acts as the entrypoint for the extension. 15 | - Current.ts mostly encapsulating external dependencies for system and vscode access. 16 | - UserInteraction.ts which handles alerts and displaying error messages. 17 | - some *Provider.ts representing the core of the extension. 18 | - execShell.ts provides a platform independent way for shell access. 19 | 20 | ## Releasing 21 | 22 | If you want to release an update to this extension, follow these steps: 23 | 24 | 1. Commit the new version number to the `package.json`. 25 | 2. Create an according release tag in the form of vX.X.X. 26 | 3. Create a release on GitHub for the tag with autogenerated release notes. 27 | 4. A new release build should automatically start off. 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Valentin Knabel 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 | # apple/swift-format for VS Code 2 | 3 | Non-official VS Code extension to prettify your Swift code automatically via [apple/swift-format](https://github.com/apple/swift-format). You can 4 | use apple/swift-format installed globally or via the Swift Package Manager. 5 | 6 | > There are two formatters for Swift code. Use this extension if you wish to use [apple/swift-format](https://github.com/apple/swift-format). 7 | > Use [SwiftFormat](https://github.com/vknabel/vscode-swiftformat) if you want to use [nicklockwood/SwiftFormat](https://github.com/nicklockwood/SwiftFormat). 8 | 9 | ### Global Installation 10 | 11 | You can [install](https://github.com/apple/swift-format#matching-swift-format-to-your-swift-version) apple/swift-format globally using [Homebrew](https://brew.sh), [Mint](https://github.com/yonaskolb/Mint) or manually. 12 | 13 | ```bash 14 | # Using Mint 15 | $ mint install apple/swift-format@release/5.8 16 | # Using Homebrew 17 | $ brew install swift-format 18 | # Manually 19 | $ git clone -b release/5.8 https://github.com/apple/swift-format.git 20 | $ swift build -c release 21 | ``` 22 | 23 | > **Attention:** Pick the same branch name to install `apple/swift-format` as your Swift version! E.g. `swift-5.5-branch` for Swift `5.5` and `release/5.6` for `5.6`. For a complete and up-to-date mapping, see [apple/swift-format#Matching Swift Format to your Swift version](https://github.com/apple/swift-format#matching-swift-format-to-your-swift-version). 24 | 25 | ### Local Installation 26 | 27 | Add the package to your dependencies in `Package.swift`: 28 | 29 | ```diff 30 | // swift-tools-version:5.8 31 | 32 | import PackageDescription 33 | 34 | let package = Package( 35 | name: "Komondor", 36 | products: [ ... ], 37 | dependencies: [ 38 | // My dependencies 39 | .package(url: "https://github.com/orta/PackageConfig.git", from: "0.0.1"), 40 | // Dev deps 41 | .package(url: "https://github.com/orta/Komondor.git", from: "0.0.1"), 42 | + .package(url: "https://github.com/apple/swift-format.git", from: "601.0.0"), 43 | ], 44 | targets: [...] 45 | ) 46 | ``` 47 | 48 | > **Attention:** Pick the same branch name to install `apple/swift-format` as your Swift version! E.g. `swift-5.5-branch` for Swift `5.5` and `release/5.6` for `5.6`. For a complete and up-to-date mapping, see [apple/swift-format#Matching Swift Format to your Swift version](https://github.com/apple/swift-format#matching-swift-format-to-your-swift-version). 49 | 50 | ## Configuration 51 | 52 | | Config | Type | Default | Description | | 53 | | ------------------------------------------------ | ---------- | ------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | 54 | | `apple-swift-format.enable` | `Bool` | `true` | Whether apple/swift-format should actually do something. | | 55 | | `apple-swift-format.onlyEnableOnSwiftPMProjects` | `Bool` | `false` | Requires and uses an apple/swift-format as SwiftPM dependency. This will cause the extension to build the Swift package upon first launch. | | 56 | | `apple-swift-format.onlyEnableWithConfig` | `Bool` | `false` | Only format if config present. | | 57 | | `apple-swift-format.path` | `[String] \| String` | platform dependent | `swift-format` | The location of the globally installed SwiftFormat (resolved with the current path if only a filename). | 58 | | `apple-swift-format.configSearchPaths` | `[String]` | `[".swift-format"]` | Possible paths for apple/swift-format config. | | 59 | 60 | Note that when `apple-swift-format.onlyEnableOnSwiftPMProjects` is enabled, the extension will only run `swift-format` 61 | executables built as part of the Swift package open in the workspace. It will try to build the binary once on first 62 | launch. If the build fails, the extension will not fall back to a globally installed `swift-format`. If you prefer a 63 | locally built `swift-format`, but want to skip the automatic initial build, let `apple-swift-format.path` point to the 64 | local executable you have built manually or by other means independent of the extension. 65 | 66 | ## FAQs 67 | 68 | ### How do I enable formatting on type? 69 | 70 | To enable formatting on while typing code without saving, simply enable the setting `"editor.formatOnType": true`. 71 | In case you only want to enable it when editing Swift files, you can override as [`[swift]` language specific setting](https://code.visualstudio.com/docs/getstarted/settings#_language-specific-editor-settings). 72 | 73 | ## License 74 | 75 | vscode-apple-swift-format is available under the [MIT](./LICENSE) license. 76 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vknabel/vscode-apple-swift-format/00ef55166e0b82edddce00fd6c3f3dc3f8dc1cc0/assets/icon.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-apple-swift-format", 3 | "displayName": "apple-swift-format", 4 | "description": "Formatting Swift code with apple/swift-format.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/vknabel/vscode-apple-swift-format" 8 | }, 9 | "version": "1.5.0", 10 | "license": "MIT", 11 | "author": { 12 | "name": "Valentin Knabel", 13 | "email": "dev@vknabel.com", 14 | "url": "https://github.com/vknabel" 15 | }, 16 | "publisher": "vknabel", 17 | "icon": "assets/icon.png", 18 | "galleryBanner": { 19 | "color": "#FC823F", 20 | "theme": "light" 21 | }, 22 | "engines": { 23 | "vscode": "^1.26.0" 24 | }, 25 | "categories": [ 26 | "Formatters", 27 | "Programming Languages", 28 | "Other" 29 | ], 30 | "activationEvents": [ 31 | "onLanguage:swift" 32 | ], 33 | "keywords": [ 34 | "swift", 35 | "swiftformat", 36 | "prettifier", 37 | "formatter" 38 | ], 39 | "main": "./out/src/extension", 40 | "contributes": { 41 | "languages": [ 42 | { 43 | "id": "swift", 44 | "aliases": [ 45 | "Swift" 46 | ], 47 | "extensions": [ 48 | "swift" 49 | ] 50 | } 51 | ], 52 | "configuration": { 53 | "type": "object", 54 | "title": "apple/swift-format Configuration", 55 | "properties": { 56 | "apple-swift-format.enable": { 57 | "type": "boolean", 58 | "default": true, 59 | "description": "Whether apple/swift-format should actually start up on this project." 60 | }, 61 | "apple-swift-format.onlyEnableOnSwiftPMProjects": { 62 | "type": "boolean", 63 | "default": false, 64 | "description": "Only allows the extension to load up when apple/swift-format is available via Swift PM." 65 | }, 66 | "apple-swift-format.onlyEnableWithConfig": { 67 | "type": "boolean", 68 | "default": false, 69 | "description": "Only use apple/swift-format when a config exists." 70 | }, 71 | "apple-swift-format.path": { 72 | "description": "The location of your globally installed apple/swift-format.", 73 | "scope": "machine", 74 | "default": [ 75 | "/usr/bin/env", 76 | "swift-format" 77 | ], 78 | "oneOf": [ 79 | { 80 | "type": "string", 81 | "default": "/usr/local/bin/swift-format" 82 | }, 83 | { 84 | "type": "array", 85 | "minItems": 1, 86 | "default": [ 87 | "/usr/bin/env", 88 | "swift-format" 89 | ], 90 | "items": { 91 | "type": "string" 92 | } 93 | } 94 | ] 95 | }, 96 | "apple-swift-format.configSearchPaths": { 97 | "type": "array", 98 | "default": [ 99 | ".swift-format" 100 | ], 101 | "description": "Possible paths for a apple/swift-format config. See https://github.com/apple/swift-format#configuration", 102 | "items": { 103 | "type": "string" 104 | } 105 | } 106 | } 107 | } 108 | }, 109 | "scripts": { 110 | "vscode:prepublish": "yarn run compile", 111 | "compile": "tsc -p ./", 112 | "format": "prettier --write ./*.json ./**/*.ts" 113 | }, 114 | "devDependencies": { 115 | "@types/node": "^18.0.0", 116 | "@types/vscode": "^1.80.0", 117 | "prettier": "^3.0.3", 118 | "typescript": "^5.2.2" 119 | }, 120 | "dependencies": { 121 | "glob": "^10.3.4" 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/AbsolutePath.ts: -------------------------------------------------------------------------------- 1 | import * as os from "os"; 2 | import * as path from "path"; 3 | 4 | export function absolutePath(userDefinedPath: string) { 5 | return userDefinedPath.includes("~") 6 | ? path.normalize(userDefinedPath.replace(/^~/, os.homedir() + "/")) 7 | : userDefinedPath; 8 | } 9 | -------------------------------------------------------------------------------- /src/Current.ts: -------------------------------------------------------------------------------- 1 | export interface Current { 2 | editor: { 3 | openURL(url: string): Thenable; 4 | reportIssueForError( 5 | error: Partial 6 | ): Thenable; 7 | showErrorMessage( 8 | message: string, 9 | ...actions: T[] 10 | ): Thenable; 11 | showWarningMessage( 12 | message: string, 13 | ...actions: T[] 14 | ): Thenable; 15 | }; 16 | config: { 17 | isEnabled(): boolean; 18 | onlyEnableOnSwiftPMProjects(): boolean; 19 | onlyEnableWithConfig(): boolean; 20 | swiftFormatPath(document: vscode.TextDocument): string[] | null; 21 | resetSwiftFormatPath(): void; 22 | configureSwiftFormatPath(): void; 23 | formatConfigSearchPaths(): string[]; 24 | }; 25 | } 26 | 27 | import * as vscode from "vscode"; 28 | import { url } from "./UrlLiteral"; 29 | import { absolutePath } from "./AbsolutePath"; 30 | import { existsSync } from "fs"; 31 | import * as paths from "path"; 32 | import * as glob from "glob"; 33 | import * as os from "os"; 34 | 35 | export function prodEnvironment(): Current { 36 | return { 37 | editor: { 38 | async openURL(url: string) { 39 | await vscode.commands.executeCommand( 40 | "vscode.open", 41 | vscode.Uri.parse(url) 42 | ); 43 | }, 44 | async reportIssueForError(error) { 45 | const title = `Report ${error.code || ""} ${ 46 | error.message || "" 47 | }`.replace(/\\n/, " "); 48 | const body = "`" + (error.stack || JSON.stringify(error)) + "`"; 49 | await Current.editor.openURL( 50 | url`https://github.com/vknabel/vscode-apple-swift-format/issues/new?title=${title}&body=${body}` 51 | ); 52 | }, 53 | showErrorMessage: (message: string, ...actions: T[]) => 54 | vscode.window.showErrorMessage(message, ...actions) as Thenable< 55 | T | undefined 56 | >, 57 | showWarningMessage: ( 58 | message: string, 59 | ...actions: T[] 60 | ) => 61 | vscode.window.showWarningMessage(message, ...actions) as Thenable< 62 | T | undefined 63 | >, 64 | }, 65 | config: { 66 | isEnabled: () => 67 | vscode.workspace 68 | .getConfiguration() 69 | .get("apple-swift-format.enable", true), 70 | onlyEnableOnSwiftPMProjects: () => 71 | vscode.workspace 72 | .getConfiguration() 73 | .get("apple-swift-format.onlyEnableOnSwiftPMProjects", false), 74 | onlyEnableWithConfig: () => 75 | vscode.workspace 76 | .getConfiguration() 77 | .get("apple-swift-format.onlyEnableWithConfig", false), 78 | 79 | swiftFormatPath: (document: vscode.TextDocument) => { 80 | // Grab the project root from the local workspace 81 | const workspace = vscode.workspace.getWorkspaceFolder(document.uri); 82 | if (workspace == null) { 83 | return fallbackGlobalSwiftFormatPath(); 84 | } 85 | 86 | // Support running from Swift PM projects 87 | if (Current.config.onlyEnableOnSwiftPMProjects()) { 88 | const possibleLocalPaths = glob.sync( 89 | "**/.build/{release,debug}/swift-format", 90 | { maxDepth: 5 }, 91 | ); 92 | for (const path of possibleLocalPaths) { 93 | const fullPath = paths.resolve(workspace.uri.fsPath, path); 94 | if (existsSync(fullPath)) { 95 | return [absolutePath(fullPath)]; 96 | } 97 | } 98 | } 99 | 100 | return fallbackGlobalSwiftFormatPath(); 101 | }, 102 | resetSwiftFormatPath: () => 103 | vscode.workspace 104 | .getConfiguration() 105 | .update("apple-swift-format.path", undefined), 106 | configureSwiftFormatPath: () => 107 | vscode.commands.executeCommand("workbench.action.openSettings"), 108 | formatConfigSearchPaths: () => 109 | vscode.workspace 110 | .getConfiguration() 111 | .get("apple-swift-format.configSearchPaths", [".swift-format"]) 112 | .map(absolutePath), 113 | }, 114 | }; 115 | } 116 | 117 | const fallbackGlobalSwiftFormatPath = (): string[] | null => { 118 | if (Current.config.onlyEnableOnSwiftPMProjects()) { 119 | return null; 120 | } 121 | var path = vscode.workspace 122 | .getConfiguration() 123 | .get("apple-swift-format.path", null); 124 | 125 | if (typeof path === "string") { 126 | return [path]; 127 | } 128 | if (!Array.isArray(path) || path.length === 0) { 129 | path = [os.platform() === "win32" ? "swift-format.exe" : "swift-format"]; 130 | } 131 | 132 | if (os.platform() !== "win32" && !path[0].includes("/")) { 133 | // Only a binary name, not a path. Search for it in the path (on Windows this is implicit). 134 | path = ["/usr/bin/env", ...path]; 135 | } 136 | 137 | return path; 138 | }; 139 | 140 | const Current = prodEnvironment(); 141 | export default Current as Current; 142 | -------------------------------------------------------------------------------- /src/SwiftFormatEditProvider.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import Current from "./Current"; 3 | import { handleFormatError } from "./UserInteraction"; 4 | import { existsSync } from "fs"; 5 | import { resolve } from "path"; 6 | import { execShellSync } from "./execShell"; 7 | 8 | const wholeDocumentRange = new vscode.Range( 9 | 0, 10 | 0, 11 | Number.MAX_SAFE_INTEGER, 12 | Number.MAX_SAFE_INTEGER 13 | ); 14 | 15 | function userDefinedFormatOptionsForDocument(document: vscode.TextDocument): { 16 | options: string[]; 17 | hasConfig: boolean; 18 | } { 19 | const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri); 20 | const rootPath = 21 | (workspaceFolder && workspaceFolder.uri.fsPath) || 22 | vscode.workspace.rootPath || 23 | "./"; 24 | const searchPaths = Current.config 25 | .formatConfigSearchPaths() 26 | .map((current) => resolve(rootPath, current)); 27 | const existingConfig = searchPaths.find(existsSync); 28 | const options = 29 | existingConfig != null ? ["--configuration", existingConfig] : []; 30 | return { 31 | options, 32 | hasConfig: existingConfig != null, 33 | }; 34 | } 35 | 36 | function format(request: { 37 | document: vscode.TextDocument; 38 | parameters?: string[]; 39 | range?: vscode.Range; 40 | formatting: vscode.FormattingOptions; 41 | }): vscode.TextEdit[] { 42 | try { 43 | const swiftFormatPath = Current.config.swiftFormatPath(request.document); 44 | if (swiftFormatPath == null) { 45 | return []; 46 | } 47 | const input = request.document.getText(request.range); 48 | if (input.trim() === "") return []; 49 | const userDefinedParams = userDefinedFormatOptionsForDocument( 50 | request.document 51 | ); 52 | if (!userDefinedParams.hasConfig && Current.config.onlyEnableWithConfig()) { 53 | return []; 54 | } 55 | const newContents = execShellSync( 56 | swiftFormatPath[0], 57 | [ 58 | ...swiftFormatPath.slice(1), 59 | ...userDefinedParams.options, 60 | ...(request.parameters || []), 61 | ], 62 | { 63 | encoding: "utf8", 64 | input, 65 | } 66 | ); 67 | return newContents !== request.document.getText(request.range) 68 | ? [ 69 | vscode.TextEdit.replace( 70 | request.document.validateRange(request.range || wholeDocumentRange), 71 | newContents 72 | ), 73 | ] 74 | : []; 75 | } catch (error) { 76 | handleFormatError(error, request.document); 77 | return []; 78 | } 79 | } 80 | 81 | export class SwiftFormatEditProvider 82 | implements 83 | vscode.DocumentRangeFormattingEditProvider, 84 | vscode.DocumentFormattingEditProvider, 85 | vscode.OnTypeFormattingEditProvider 86 | { 87 | provideDocumentRangeFormattingEdits( 88 | document: vscode.TextDocument, 89 | range: vscode.Range, 90 | formatting: vscode.FormattingOptions 91 | ) { 92 | return format({ 93 | document, 94 | parameters: [], 95 | range, 96 | formatting, 97 | }); 98 | } 99 | provideDocumentFormattingEdits( 100 | document: vscode.TextDocument, 101 | formatting: vscode.FormattingOptions 102 | ) { 103 | return format({ document, formatting }); 104 | } 105 | provideOnTypeFormattingEdits( 106 | document: vscode.TextDocument, 107 | position: vscode.Position, 108 | ch: string, 109 | formatting: vscode.FormattingOptions, 110 | ) { 111 | // Don't format if user has inserted an empty line 112 | if (document.lineAt(position.line).text.trim() === "") { 113 | return []; 114 | } 115 | return format({ document, formatting }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/UrlLiteral.ts: -------------------------------------------------------------------------------- 1 | export function url(literals: TemplateStringsArray, ...placeholders: string[]) { 2 | return placeholders 3 | .map(encodeURIComponent) 4 | .reduce( 5 | (url, placeholder, index) => url + placeholder + literals[index + 1], 6 | literals[0] 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /src/UserInteraction.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as path from "path"; 3 | import Current from "./Current"; 4 | 5 | enum FormatErrorInteraction { 6 | configure = "Configure", 7 | reset = "Reset", 8 | howTo = "How?", 9 | } 10 | 11 | enum UnknownErrorInteraction { 12 | reportIssue = "Report issue", 13 | } 14 | 15 | const stdinIncompatibleSwiftSyntaxErrorRegex = 16 | /((:\d+:\d+: error)?: SwiftSyntax parser library isn't compatible)/; 17 | const stdinErrorRegex = /((:\d+:\d+: error)?: [^.]+.)/; 18 | 19 | export async function handleFormatError( 20 | error: any, 21 | document: vscode.TextDocument 22 | ) { 23 | function matches(...codeOrStatus: Array) { 24 | return codeOrStatus.some((c) => { 25 | if (typeof error.stderr === "string" && error.stderr.includes(c)) { 26 | return true; 27 | } 28 | return c === error.code || c === error.status; 29 | }); 30 | } 31 | 32 | if (matches("Domain=NSCocoaErrorDomain Code=260")) { 33 | return; 34 | } 35 | if (matches("Found a configuration for")) { 36 | await Current.editor.showWarningMessage(error.stderr); 37 | return; 38 | } 39 | if (matches("ENOENT", "EACCES", 127)) { 40 | const selection = await Current.editor.showErrorMessage( 41 | `Could not find apple/swift-format: ${Current.config 42 | .swiftFormatPath(document) 43 | ?.join(" ")}`, 44 | FormatErrorInteraction.reset, 45 | FormatErrorInteraction.configure, 46 | FormatErrorInteraction.howTo 47 | ); 48 | switch (selection) { 49 | case FormatErrorInteraction.reset: 50 | Current.config.resetSwiftFormatPath(); 51 | break; 52 | case FormatErrorInteraction.configure: 53 | Current.config.configureSwiftFormatPath(); 54 | break; 55 | case FormatErrorInteraction.howTo: 56 | await Current.editor.openURL( 57 | "https://github.com/vknabel/vscode-apple-swift-format#global-installation" 58 | ); 59 | break; 60 | } 61 | } else if (error.code === "EPIPE") { 62 | await Current.editor.showErrorMessage( 63 | `apple/swift-format was closed. ${error.stderr || ""}` 64 | ); 65 | } else if (error.status === 70) { 66 | await Current.editor.showErrorMessage( 67 | `apple/swift-format failed. ${error.stderr || ""}` 68 | ); 69 | } else if ( 70 | error.status === 1 && 71 | (stdinIncompatibleSwiftSyntaxErrorRegex.test(error.message) || 72 | ("stderr" in error && 73 | typeof error.stderr === "string" && 74 | error.stderr.includes("_InternalSwiftSyntaxParser"))) 75 | ) { 76 | const selection = await Current.editor.showWarningMessage( 77 | `apple/swift-format does not fit your Swift version. Do you need to update and recompile it?`, 78 | "How?" 79 | ); 80 | if (selection == "How?") { 81 | await Current.editor.openURL( 82 | "https://github.com/vknabel/vscode-apple-swift-format#appleswift-format-for-vs-code" 83 | ); 84 | } 85 | } else if (error.status === 1 && stdinErrorRegex.test(error.stderr)) { 86 | const execArray = stdinErrorRegex.exec(error.stderr)!; 87 | Current.editor.showWarningMessage( 88 | `${path.basename(document.fileName)}${execArray[1]}` 89 | ); 90 | } else { 91 | const unknownErrorSelection = await Current.editor.showErrorMessage( 92 | `An unknown error occurred. ${error.message || ""}`, 93 | UnknownErrorInteraction.reportIssue 94 | ); 95 | if (unknownErrorSelection === UnknownErrorInteraction.reportIssue) { 96 | await Current.editor.reportIssueForError(error); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/execShell.ts: -------------------------------------------------------------------------------- 1 | import { 2 | execFileSync, 3 | ExecFileSyncOptionsWithStringEncoding, 4 | spawnSync, 5 | } from "child_process"; 6 | import * as os from "os"; 7 | 8 | export function execShellSync( 9 | file: string, 10 | args: ReadonlyArray, 11 | options: ExecFileSyncOptionsWithStringEncoding, 12 | ): string { 13 | if (os.platform() === "win32") { 14 | const result = spawnSync(file, args ?? [], { 15 | ...options, 16 | encoding: "utf8", 17 | shell: true, 18 | }); 19 | if (result.error) { 20 | throw result.error; 21 | } 22 | return result.stdout; 23 | } else { 24 | return execFileSync(file, args, options); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import * as vscode from "vscode"; 4 | import { SwiftFormatEditProvider } from "./SwiftFormatEditProvider"; 5 | import Current from "./Current"; 6 | import { promisify } from "util"; 7 | import * as path from "path"; 8 | import { exec } from "child_process"; 9 | 10 | export function activate(context: vscode.ExtensionContext) { 11 | if (Current.config.isEnabled() === false) { 12 | return; 13 | } 14 | 15 | buildSwiftFormatIfNeeded().then(() => { 16 | const swiftSelector: vscode.DocumentSelector = { 17 | scheme: "file", 18 | language: "swift", 19 | }; 20 | const editProvider = new SwiftFormatEditProvider(); 21 | vscode.languages.registerDocumentRangeFormattingEditProvider( 22 | swiftSelector, 23 | editProvider 24 | ); 25 | vscode.languages.registerDocumentFormattingEditProvider( 26 | swiftSelector, 27 | editProvider 28 | ); 29 | vscode.languages.registerOnTypeFormattingEditProvider( 30 | swiftSelector, 31 | editProvider, 32 | "\n", 33 | ); 34 | }); 35 | } 36 | 37 | async function buildSwiftFormatIfNeeded() { 38 | if (!Current.config.onlyEnableOnSwiftPMProjects()) { 39 | return; 40 | } 41 | const manifests = await vscode.workspace.findFiles( 42 | "**/Package.swift", 43 | "**/.build/**", 44 | 2 45 | ); 46 | if (manifests.length == 0) { 47 | return; 48 | } 49 | const buildOperations = manifests.map((manifest) => { 50 | const manifestPath = manifest.fsPath; 51 | const manifestDir = path.dirname(manifestPath); 52 | return promisify(exec)("swift run -c release swift-format --version", { 53 | cwd: manifestDir, 54 | }); 55 | }); 56 | try { 57 | await vscode.window.withProgress( 58 | { 59 | location: vscode.ProgressLocation.Window, 60 | title: "Prepare apple/swift-format", 61 | }, 62 | async () => { 63 | await Promise.all(buildOperations); 64 | } 65 | ); 66 | } catch (error) { 67 | console.log(error); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES2015", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": ".", 11 | "strictNullChecks": true 12 | }, 13 | "exclude": [ 14 | "node_modules", 15 | ".vscode-test" 16 | ] 17 | } -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@isaacs/cliui@^8.0.2": 6 | version "8.0.2" 7 | resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" 8 | integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== 9 | dependencies: 10 | string-width "^5.1.2" 11 | string-width-cjs "npm:string-width@^4.2.0" 12 | strip-ansi "^7.0.1" 13 | strip-ansi-cjs "npm:strip-ansi@^6.0.1" 14 | wrap-ansi "^8.1.0" 15 | wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" 16 | 17 | "@pkgjs/parseargs@^0.11.0": 18 | version "0.11.0" 19 | resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" 20 | integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== 21 | 22 | "@types/node@^18.0.0": 23 | version "18.19.39" 24 | resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.39.tgz#c316340a5b4adca3aee9dcbf05de385978590593" 25 | integrity sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ== 26 | dependencies: 27 | undici-types "~5.26.4" 28 | 29 | "@types/vscode@^1.80.0": 30 | version "1.90.0" 31 | resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.90.0.tgz#c122384d51bd774cec4aa86ca443858adc9edef2" 32 | integrity sha512-oT+ZJL7qHS9Z8bs0+WKf/kQ27qWYR3trsXpq46YDjFqBsMLG4ygGGjPaJ2tyrH0wJzjOEmDyg9PDJBBhWg9pkQ== 33 | 34 | ansi-regex@^5.0.1: 35 | version "5.0.1" 36 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 37 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 38 | 39 | ansi-regex@^6.0.1: 40 | version "6.0.1" 41 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" 42 | integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== 43 | 44 | ansi-styles@^4.0.0: 45 | version "4.3.0" 46 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 47 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 48 | dependencies: 49 | color-convert "^2.0.1" 50 | 51 | ansi-styles@^6.1.0: 52 | version "6.2.1" 53 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" 54 | integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== 55 | 56 | balanced-match@^1.0.0: 57 | version "1.0.2" 58 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 59 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 60 | 61 | brace-expansion@^2.0.1: 62 | version "2.0.1" 63 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" 64 | integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== 65 | dependencies: 66 | balanced-match "^1.0.0" 67 | 68 | color-convert@^2.0.1: 69 | version "2.0.1" 70 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 71 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 72 | dependencies: 73 | color-name "~1.1.4" 74 | 75 | color-name@~1.1.4: 76 | version "1.1.4" 77 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 78 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 79 | 80 | cross-spawn@^7.0.0: 81 | version "7.0.6" 82 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" 83 | integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== 84 | dependencies: 85 | path-key "^3.1.0" 86 | shebang-command "^2.0.0" 87 | which "^2.0.1" 88 | 89 | eastasianwidth@^0.2.0: 90 | version "0.2.0" 91 | resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" 92 | integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== 93 | 94 | emoji-regex@^8.0.0: 95 | version "8.0.0" 96 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 97 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 98 | 99 | emoji-regex@^9.2.2: 100 | version "9.2.2" 101 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" 102 | integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== 103 | 104 | foreground-child@^3.1.0: 105 | version "3.2.1" 106 | resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.2.1.tgz#767004ccf3a5b30df39bed90718bab43fe0a59f7" 107 | integrity sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA== 108 | dependencies: 109 | cross-spawn "^7.0.0" 110 | signal-exit "^4.0.1" 111 | 112 | glob@^10.3.4: 113 | version "10.4.2" 114 | resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.2.tgz#bed6b95dade5c1f80b4434daced233aee76160e5" 115 | integrity sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w== 116 | dependencies: 117 | foreground-child "^3.1.0" 118 | jackspeak "^3.1.2" 119 | minimatch "^9.0.4" 120 | minipass "^7.1.2" 121 | package-json-from-dist "^1.0.0" 122 | path-scurry "^1.11.1" 123 | 124 | is-fullwidth-code-point@^3.0.0: 125 | version "3.0.0" 126 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 127 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 128 | 129 | isexe@^2.0.0: 130 | version "2.0.0" 131 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 132 | integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== 133 | 134 | jackspeak@^3.1.2: 135 | version "3.4.0" 136 | resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.0.tgz#a75763ff36ad778ede6a156d8ee8b124de445b4a" 137 | integrity sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw== 138 | dependencies: 139 | "@isaacs/cliui" "^8.0.2" 140 | optionalDependencies: 141 | "@pkgjs/parseargs" "^0.11.0" 142 | 143 | lru-cache@^10.2.0: 144 | version "10.2.2" 145 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878" 146 | integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ== 147 | 148 | minimatch@^9.0.4: 149 | version "9.0.4" 150 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" 151 | integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== 152 | dependencies: 153 | brace-expansion "^2.0.1" 154 | 155 | "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: 156 | version "7.1.2" 157 | resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" 158 | integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== 159 | 160 | package-json-from-dist@^1.0.0: 161 | version "1.0.0" 162 | resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00" 163 | integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw== 164 | 165 | path-key@^3.1.0: 166 | version "3.1.1" 167 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" 168 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 169 | 170 | path-scurry@^1.11.1: 171 | version "1.11.1" 172 | resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" 173 | integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== 174 | dependencies: 175 | lru-cache "^10.2.0" 176 | minipass "^5.0.0 || ^6.0.2 || ^7.0.0" 177 | 178 | prettier@^3.0.3: 179 | version "3.3.2" 180 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.2.tgz#03ff86dc7c835f2d2559ee76876a3914cec4a90a" 181 | integrity sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA== 182 | 183 | shebang-command@^2.0.0: 184 | version "2.0.0" 185 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" 186 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 187 | dependencies: 188 | shebang-regex "^3.0.0" 189 | 190 | shebang-regex@^3.0.0: 191 | version "3.0.0" 192 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" 193 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 194 | 195 | signal-exit@^4.0.1: 196 | version "4.1.0" 197 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" 198 | integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== 199 | 200 | "string-width-cjs@npm:string-width@^4.2.0": 201 | version "4.2.3" 202 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 203 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 204 | dependencies: 205 | emoji-regex "^8.0.0" 206 | is-fullwidth-code-point "^3.0.0" 207 | strip-ansi "^6.0.1" 208 | 209 | string-width@^4.1.0: 210 | version "4.2.3" 211 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 212 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 213 | dependencies: 214 | emoji-regex "^8.0.0" 215 | is-fullwidth-code-point "^3.0.0" 216 | strip-ansi "^6.0.1" 217 | 218 | string-width@^5.0.1, string-width@^5.1.2: 219 | version "5.1.2" 220 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" 221 | integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== 222 | dependencies: 223 | eastasianwidth "^0.2.0" 224 | emoji-regex "^9.2.2" 225 | strip-ansi "^7.0.1" 226 | 227 | "strip-ansi-cjs@npm:strip-ansi@^6.0.1": 228 | version "6.0.1" 229 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 230 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 231 | dependencies: 232 | ansi-regex "^5.0.1" 233 | 234 | strip-ansi@^6.0.0, strip-ansi@^6.0.1: 235 | version "6.0.1" 236 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 237 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 238 | dependencies: 239 | ansi-regex "^5.0.1" 240 | 241 | strip-ansi@^7.0.1: 242 | version "7.1.0" 243 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" 244 | integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== 245 | dependencies: 246 | ansi-regex "^6.0.1" 247 | 248 | typescript@^5.2.2: 249 | version "5.5.2" 250 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.2.tgz#c26f023cb0054e657ce04f72583ea2d85f8d0507" 251 | integrity sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew== 252 | 253 | undici-types@~5.26.4: 254 | version "5.26.5" 255 | resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" 256 | integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== 257 | 258 | which@^2.0.1: 259 | version "2.0.2" 260 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 261 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 262 | dependencies: 263 | isexe "^2.0.0" 264 | 265 | "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": 266 | version "7.0.0" 267 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" 268 | integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== 269 | dependencies: 270 | ansi-styles "^4.0.0" 271 | string-width "^4.1.0" 272 | strip-ansi "^6.0.0" 273 | 274 | wrap-ansi@^8.1.0: 275 | version "8.1.0" 276 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" 277 | integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== 278 | dependencies: 279 | ansi-styles "^6.1.0" 280 | string-width "^5.0.1" 281 | strip-ansi "^7.0.1" 282 | --------------------------------------------------------------------------------