From f1619ae847445e4aeefbb51643d5e7d72febc4da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?KARASZI=20Istv=C3=A1n?= Date: Sun, 24 Aug 2025 17:26:43 +0200 Subject: [PATCH 1/2] Add format SQL option --- package-lock.json | 90 +++++++++++++++++++++++++++++++++++++++- package.json | 42 +++++++++++++++++-- src/format-angular.ts | 4 +- src/format-css.ts | 4 +- src/format-glimmer.ts | 4 +- src/format-graphql.ts | 4 +- src/format-html.ts | 4 +- src/format-javascript.ts | 4 +- src/format-json.ts | 4 +- src/format-json5.ts | 4 +- src/format-less.ts | 4 +- src/format-lwc.ts | 4 +- src/format-markdown.ts | 4 +- src/format-mdx.ts | 4 +- src/format-scss.ts | 4 +- src/format-sql.tsx | 18 ++++++++ src/format-typescript.ts | 4 +- src/format-vue.ts | 4 +- src/format-yaml.ts | 4 +- src/format.tsx | 37 +++-------------- src/pipeline.ts | 16 ++----- src/preferences.ts | 25 +++++++++++ src/prettier.tsx | 43 +++++++++++++++++++ src/types.ts | 9 +--- 24 files changed, 256 insertions(+), 88 deletions(-) create mode 100644 src/format-sql.tsx create mode 100644 src/preferences.ts create mode 100644 src/prettier.tsx diff --git a/package-lock.json b/package-lock.json index 1a38341..bf8744f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "dependencies": { "@raycast/api": "^1.55.2", "@raycast/utils": "^1.8.0", - "prettier": "^2.8.8" + "prettier": "^2.8.8", + "sql-formatter": "^15.6.2" }, "devDependencies": { "@raycast/eslint-config": "1.0.5", @@ -719,6 +720,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -803,6 +810,12 @@ "node": ">=8" } }, + "node_modules/discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", + "license": "MIT" + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1472,6 +1485,12 @@ "node": "*" } }, + "node_modules/moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==", + "license": "BSD-3-Clause" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1490,6 +1509,28 @@ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, + "node_modules/nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "license": "MIT", + "dependencies": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + }, + "bin": { + "nearley-railroad": "bin/nearley-railroad.js", + "nearley-test": "bin/nearley-test.js", + "nearley-unparse": "bin/nearley-unparse.js", + "nearleyc": "bin/nearleyc.js" + }, + "funding": { + "type": "individual", + "url": "https://nearley.js.org/#give-to-nearley" + } + }, "node_modules/node-fetch": { "version": "2.6.12", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", @@ -1655,6 +1696,25 @@ } ] }, + "node_modules/railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==", + "license": "CC0-1.0" + }, + "node_modules/randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "license": "MIT", + "dependencies": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/react": { "version": "18.1.0", "resolved": "https://registry.npmjs.org/react/-/react-18.1.0.tgz", @@ -1711,6 +1771,15 @@ "node": ">=4" } }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "license": "MIT", + "engines": { + "node": ">=0.12" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -1846,6 +1915,25 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/sql-formatter": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.6.2.tgz", + "integrity": "sha512-ZjqOfJGuB97UeHzTJoTbadlM0h9ynehtSTHNUbGfXR4HZ4rCIoD2oIW91W+A5oE76k8hl0Uz5GD8Sx3Pt9Xa3w==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "nearley": "^2.20.1" + }, + "bin": { + "sql-formatter": "bin/sql-formatter-cli.cjs" + } + }, + "node_modules/sql-formatter/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", diff --git a/package.json b/package.json index 6c21c17..1662cc9 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,7 @@ "description": "Format code on the clipboard", "icon": "icon.png", "author": "ikaraszi", - "categories": [ - "Developer Tools" - ], + "categories": ["Developer Tools"], "license": "MIT", "commands": [ { @@ -105,6 +103,12 @@ "title": "Format YAML", "description": "Formats YAML on the clipboard", "mode": "view" + }, + { + "name": "format-sql", + "title": "Format SQL", + "description": "Formats SQL on the clipboard", + "mode": "view" } ], "preferences": [ @@ -115,12 +119,41 @@ "description": "Specify the line length that the printer will wrap on.", "placeholder": "100", "required": false + }, + { + "name": "sqlDialect", + "title": "SQL dialect", + "type": "dropdown", + "description": "Set the SQL dialect", + "data": [ + { "value": "sql", "title": "Standard SQL" }, + { "value": "bigquery", "title": "GCP BigQuery" }, + { "value": "db2", "title": "IBM DB2" }, + { "value": "db2i", "title": "IBM DB2i (experimental)" }, + { "value": "duckdb", "title": "DuckDB" }, + { "value": "hive", "title": "Apache Hive" }, + { "value": "mariadb", "title": "MariaDB" }, + { "value": "mysql", "title": "MySQL" }, + { "value": "tidb", "title": "TiDB" }, + { "value": "n1ql", "title": "Couchbase N1QL" }, + { "value": "plsql", "title": "Oracle PL/SQL" }, + { "value": "postgresql", "title": "PostgreSQL" }, + { "value": "redshift", "title": "Amazon Redshift" }, + { "value": "singlestoredb", "title": "SingleStoreDB" }, + { "value": "snowflake", "title": "Snowflake" }, + { "value": "spark", "title": "Spark" }, + { "value": "sqlite", "title": "SQLite" }, + { "value": "transactsql", "title": "SQL Server Transact-SQL" }, + { "value": "trino", "title": "Trino / Presto" } + ], + "required": false } ], "dependencies": { "@raycast/api": "^1.55.2", "@raycast/utils": "^1.8.0", - "prettier": "^2.8.8" + "prettier": "^2.8.8", + "sql-formatter": "^15.6.2" }, "devDependencies": { "@raycast/eslint-config": "1.0.5", @@ -134,6 +167,7 @@ "build": "ray build -e dist", "dev": "ray develop", "fix-lint": "ray lint --fix", + "eslint": "eslint src", "lint": "ray lint", "publish": "npx @raycast/api@latest publish" } diff --git a/src/format-angular.ts b/src/format-angular.ts index d36bd45..a73a13a 100644 --- a/src/format-angular.ts +++ b/src/format-angular.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.angular; +export default prettier.angular; diff --git a/src/format-css.ts b/src/format-css.ts index 0f52733..a7cb2dc 100644 --- a/src/format-css.ts +++ b/src/format-css.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.css; +export default prettier.css; diff --git a/src/format-glimmer.ts b/src/format-glimmer.ts index b35330a..90138ad 100644 --- a/src/format-glimmer.ts +++ b/src/format-glimmer.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.glimmer; +export default prettier.glimmer; diff --git a/src/format-graphql.ts b/src/format-graphql.ts index 0cdebe9..cc59ffe 100644 --- a/src/format-graphql.ts +++ b/src/format-graphql.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.graphql; +export default prettier.graphql; diff --git a/src/format-html.ts b/src/format-html.ts index 813b5cf..1d0bcdd 100644 --- a/src/format-html.ts +++ b/src/format-html.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.html; +export default prettier.html; diff --git a/src/format-javascript.ts b/src/format-javascript.ts index 952e9ec..9335b49 100644 --- a/src/format-javascript.ts +++ b/src/format-javascript.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.javascript; +export default prettier.javascript; diff --git a/src/format-json.ts b/src/format-json.ts index 1082e4e..4618d97 100644 --- a/src/format-json.ts +++ b/src/format-json.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.json; +export default prettier.json; diff --git a/src/format-json5.ts b/src/format-json5.ts index 8b4ebb9..a656b29 100644 --- a/src/format-json5.ts +++ b/src/format-json5.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.json5; +export default prettier.json5; diff --git a/src/format-less.ts b/src/format-less.ts index 6ac9aa3..b9a3c29 100644 --- a/src/format-less.ts +++ b/src/format-less.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.less; +export default prettier.less; diff --git a/src/format-lwc.ts b/src/format-lwc.ts index 0d66544..f29c453 100644 --- a/src/format-lwc.ts +++ b/src/format-lwc.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.lwc; +export default prettier.lwc; diff --git a/src/format-markdown.ts b/src/format-markdown.ts index 980da5d..5ca4ab4 100644 --- a/src/format-markdown.ts +++ b/src/format-markdown.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.markdown; +export default prettier.markdown; diff --git a/src/format-mdx.ts b/src/format-mdx.ts index 6d8f0fe..7f56987 100644 --- a/src/format-mdx.ts +++ b/src/format-mdx.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.mdx; +export default prettier.mdx; diff --git a/src/format-scss.ts b/src/format-scss.ts index ebf97df..df3ec8f 100644 --- a/src/format-scss.ts +++ b/src/format-scss.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.scss; +export default prettier.scss; diff --git a/src/format-sql.tsx b/src/format-sql.tsx new file mode 100644 index 0000000..9003e8c --- /dev/null +++ b/src/format-sql.tsx @@ -0,0 +1,18 @@ +import { format } from "sql-formatter"; + +import { Format } from "./format"; +import { getPreferences } from "./preferences"; +import { Formatter } from "./types"; + +const { sqlDialect } = getPreferences(); + +const formatter: Formatter = async (input) => { + return format(input, { + language: sqlDialect, + functionCase: "upper", + dataTypeCase: "lower", + keywordCase: "upper", + }); +}; + +export default () => ; diff --git a/src/format-typescript.ts b/src/format-typescript.ts index cc58383..6c8657c 100644 --- a/src/format-typescript.ts +++ b/src/format-typescript.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.typescript; +export default prettier.typescript; diff --git a/src/format-vue.ts b/src/format-vue.ts index 9a08979..6fa906e 100644 --- a/src/format-vue.ts +++ b/src/format-vue.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.vue; +export default prettier.vue; diff --git a/src/format-yaml.ts b/src/format-yaml.ts index 8a24a95..95109d2 100644 --- a/src/format-yaml.ts +++ b/src/format-yaml.ts @@ -1,3 +1,3 @@ -import format from "./format"; +import prettier from "./prettier"; -export default format.yaml; +export default prettier.yaml; diff --git a/src/format.tsx b/src/format.tsx index 4819f1c..2e437c0 100644 --- a/src/format.tsx +++ b/src/format.tsx @@ -3,44 +3,17 @@ import { usePromise } from "@raycast/utils"; import { useRef } from "react"; import { pipeline } from "./pipeline"; -import { Language, Parser } from "./types"; +import { Formatter, Language } from "./types"; -const supportedLanguages: Record = { - angular: "angular", - css: "css", - glimmer: "glimmer", - graphql: "graphql", - html: "html", - javascript: "babel", - json: "json", - json5: "json5", - less: "less", - lwc: "lwc", - markdown: "markdown", - mdx: "mdx", - scss: "scss", - typescript: "typescript", - vue: "vue", - yaml: "yaml", -}; - -type CommandProps = { +type FormatProps = { language: Language; - parser: Parser; + formatter: Formatter; }; -function Format({ language, parser }: CommandProps) { +export function Format({ language, formatter }: FormatProps) { const abortable = useRef(); - const { isLoading, data: markdown } = usePromise(pipeline, [language, parser], { abortable }); + const { isLoading, data: markdown } = usePromise(pipeline, [language, formatter], { abortable }); return ; } - -const formatterEntries = Object.entries(supportedLanguages).map(([language, parser]) => { - return [language, () => ]; -}); - -const formatters = Object.fromEntries(formatterEntries) as Record; - -export default formatters; diff --git a/src/pipeline.ts b/src/pipeline.ts index 8984c34..02a3f6c 100644 --- a/src/pipeline.ts +++ b/src/pipeline.ts @@ -1,17 +1,9 @@ -import { Clipboard, getPreferenceValues } from "@raycast/api"; -import { format } from "prettier"; +import { Clipboard } from "@raycast/api"; -import { Language, Parser, Preferences } from "./types"; +import { Formatter, Language } from "./types"; const readFromClipboard = async (): Promise => (await Clipboard.readText()) || ""; -const formatCode = (parser: Parser) => { - const preferences = getPreferenceValues(); - const printWidth = parseInt(preferences.printWidth || "100", 10); - - return async (query: string): Promise => format(query, { parser, printWidth }); -}; - const copyToClipboard = async (code: string): Promise => { await Clipboard.copy(code); return code; @@ -21,6 +13,6 @@ const wrapWithCodeblock = (language: Language) => { return (code: string) => ["```", " ", language, "\n", code, "\n", "``` "].join(""); }; -export const pipeline = async (language: Language, parser: Parser): Promise => { - return readFromClipboard().then(formatCode(parser)).then(copyToClipboard).then(wrapWithCodeblock(language)); +export const pipeline = async (language: Language, formatter: Formatter): Promise => { + return readFromClipboard().then(formatter).then(copyToClipboard).then(wrapWithCodeblock(language)); }; diff --git a/src/preferences.ts b/src/preferences.ts new file mode 100644 index 0000000..5e83a55 --- /dev/null +++ b/src/preferences.ts @@ -0,0 +1,25 @@ +import { getPreferenceValues } from "@raycast/api"; +import { SqlLanguage } from "sql-formatter"; + +type RawPreferences = { + printWidth?: string; + sqlDialect?: string; +}; + +export type Preferences = { + printWidth: number; + sqlDialect: SqlLanguage; +}; + +export const getPreferences = (): Preferences => { + const rawPreferences = getPreferenceValues(); + + const printWidth = parseInt(rawPreferences.printWidth || "100", 10); + const rawDialect = rawPreferences.sqlDialect || "sql"; + const sqlDialect = (rawDialect || "sql") as SqlLanguage; + + return { + printWidth, + sqlDialect, + }; +}; diff --git a/src/prettier.tsx b/src/prettier.tsx new file mode 100644 index 0000000..88ebe44 --- /dev/null +++ b/src/prettier.tsx @@ -0,0 +1,43 @@ +import { format, BuiltInParserName } from "prettier"; + +import { Format } from "./format"; +import { getPreferences } from "./preferences"; +import { Formatter, Language } from "./types"; + +type Parser = BuiltInParserName; +type PrettierLanguage = Exclude; + +const prettierParsers: Record = { + angular: "angular", + css: "css", + glimmer: "glimmer", + graphql: "graphql", + html: "html", + javascript: "babel", + json5: "json5", + json: "json", + less: "less", + lwc: "lwc", + markdown: "markdown", + mdx: "mdx", + scss: "scss", + typescript: "typescript", + vue: "vue", + yaml: "yaml", +}; + +const { printWidth } = getPreferences(); + +const buildFormatter = (parser: Parser): Formatter => { + return async (query) => format(query, { parser, printWidth }); +}; + +const formatterEntries = Object.entries(prettierParsers).map(([language, parser]) => { + const formatter = buildFormatter(parser); + + return [language, () => ]; +}); + +const formatters = Object.fromEntries(formatterEntries) as Record; + +export default formatters; diff --git a/src/types.ts b/src/types.ts index 2a1dc6b..437a7b3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,4 @@ -import { BuiltInParserName } from "prettier"; - -export type Parser = BuiltInParserName; +export type Formatter = (query: string) => Promise; export type Language = | "angular" @@ -16,10 +14,7 @@ export type Language = | "markdown" | "mdx" | "scss" + | "sql" | "typescript" | "vue" | "yaml"; - -export type Preferences = { - printWidth: string; -}; From 0c4521298a6afc95c242fcfd197399ec85600853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?KARASZI=20Istv=C3=A1n?= Date: Sun, 24 Aug 2025 17:48:27 +0200 Subject: [PATCH 2/2] Add GitHub actions --- .github/workflows/ci.yml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4fd3f42 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,40 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: "npm" + + - name: Cache node modules + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install dependencies + run: npm ci + + - name: Run ESLint + run: npm run eslint + + - name: Run lint + run: npm run lint + + - name: Run build + run: npm run build