add .sql file formatter

Uses the logic from the sqltools VSCode add-on, with a workaround
for the use of 'type' in some table columns.

By detecting the presence of 'BUILD_WORKSPACE_DIRECTORY' we can tell
if the rule is running in test mode or was run directly, avoiding the
need for separate check and fix rules. It might be nice to extend this
to other formatting rules in the future as well.
This commit is contained in:
Damien Elmes 2021-01-09 14:16:26 +10:00
parent 56e4e2d518
commit 3dad3c90d0
8 changed files with 115 additions and 2 deletions

View file

@ -115,6 +115,7 @@ in the relevant package:
```
bazel run //rslib:format
bazel run //rslib:sql_format
bazel run //pylib:format
bazel run //qt:format
bazel run //ts:format

View file

@ -2,6 +2,7 @@ load("@rules_proto//proto:defs.bzl", "proto_library")
load("@io_bazel_rules_rust//rust:rust.bzl", "rust_binary", "rust_library", "rust_test")
load("@io_bazel_rules_rust//cargo:cargo_build_script.bzl", "cargo_build_script")
load(":rustfmt.bzl", "rustfmt_fix", "rustfmt_test")
load("//ts:sql_format.bzl", "sql_format")
# Build script
#######################
@ -143,6 +144,11 @@ rustfmt_fix(
]),
)
sql_format(
name = "sql_format",
srcs = glob(["**/*.sql"]),
)
# fluent.proto generation
###########################
# This separate step is required to make the file available to downstream consumers.

View file

@ -1,7 +1,18 @@
load("//ts:prettier.bzl", "prettier")
load("//ts:prettier.bzl", "prettier", "prettier_test")
load("//ts:sql_format.bzl", "sql_format_setup")
prettier()
prettier_test(
name = "format_check",
srcs = glob([
"*.ts",
"*.js",
]),
)
sql_format_setup()
# Exported files
#################

View file

@ -12,6 +12,7 @@
"@pyoner/svelte-types": "^3.4.4-2",
"@rollup/plugin-commonjs": "^15.1.0",
"@rollup/plugin-node-resolve": "^9.0.0",
"@sqltools/formatter": "^1.2.2",
"@tsconfig/svelte": "^1.0.10",
"@types/d3-array": "^2.0.0",
"@types/d3-axis": "^1.0.12",
@ -22,15 +23,18 @@
"@types/d3-shape": "^1.3.2",
"@types/d3-time": "^2.0.0",
"@types/d3-transition": "^1.1.6",
"@types/diff": "^5.0.0",
"@types/jquery": "^3.5.0",
"@types/jqueryui": "^1.12.13",
"@types/lodash": "^4.14.162",
"@types/long": "^4.0.1",
"@types/node": "^14.14.20",
"@types/react": "^16.9.53",
"@types/react-dom": "^16.9.8",
"@typescript-eslint/eslint-plugin": "^2.11.0",
"@typescript-eslint/parser": "^2.11.0",
"cross-env": "^7.0.2",
"diff": "^5.0.0",
"eslint": "^6.7.2",
"license-checker-rseidelsohn": "=1.1.2",
"patch-package": "^6.2.2",

25
ts/sql_format.bzl Normal file
View file

@ -0,0 +1,25 @@
load("@npm//@bazel/typescript:index.bzl", "ts_library")
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_test")
def sql_format_setup():
ts_library(
name = "sql_format_lib",
srcs = ["//ts:sql_format.ts"],
deps = [
"@npm//@sqltools/formatter",
"@npm//@types/node",
"@npm//@types/diff",
"@npm//diff",
],
visibility = ["//visibility:public"],
)
native.exports_files(["sql_format.ts"])
def sql_format(name = "sql_format", srcs = [], **kwargs):
nodejs_test(
name = name,
entry_point = "//ts:sql_format.ts",
args = [native.package_name() + "/" + f for f in srcs],
data = ["//ts:sql_format_lib", "@npm//tslib", "@npm//diff"] + srcs,
**kwargs
)

46
ts/sql_format.ts Normal file
View file

@ -0,0 +1,46 @@
import sqlFormatter from "@sqltools/formatter";
import * as Diff from "diff";
import process from "process";
import path from "path";
import fs from "fs";
const workspace = process.env.BUILD_WORKSPACE_DIRECTORY;
const wantFix = workspace !== undefined;
function fixFile(relpath: string, newText: string): void {
const workspacePath = path.join(workspace!, relpath);
fs.writeFileSync(workspacePath, newText);
}
function formatText(text: string): string {
const newText: string = sqlFormatter.format(text, {
indent: " ",
reservedWordCase: "upper",
});
// 'type' is treated as a reserved word, but Anki uses it in various sql
// tables, so we don't want it uppercased
return newText.replace(/\bTYPE\b/g, "type");
}
let errorFound = false;
for (const path of process.argv.slice(2)) {
const orig = fs.readFileSync(path).toString();
const formatted = formatText(orig);
if (orig !== formatted) {
if (wantFix) {
fixFile(path, formatted);
console.log(`Fixed ${path}`);
} else {
if (!errorFound) {
errorFound = true;
console.log("SQL formatting issues found:");
}
console.log(Diff.createPatch(path, orig, formatted));
}
}
}
if (errorFound) {
console.log("Use 'bazel run //rslib:sql_format' to fix.");
console.log(process.env.BUILD_WORKSPACE_DIRECTORY);
process.exit(1);
}

View file

@ -24,7 +24,7 @@
"allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
"jsx": "react",
"types": ["svelte", "long"],
"types": ["svelte", "long", "node"],
"noEmitHelpers": true,
"importHelpers": true
}

View file

@ -145,6 +145,11 @@
estree-walker "^1.0.1"
picomatch "^2.2.2"
"@sqltools/formatter@^1.2.2":
version "1.2.2"
resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.2.tgz#9390a8127c0dcba61ebd7fdcc748655e191bdd68"
integrity sha512-/5O7Fq6Vnv8L6ucmPjaWbVG1XkP4FO+w5glqfkIsq3Xw4oyNAdJddbnYodNDAfjVUvo/rrSCTom4kAND7T1o5Q==
"@tsconfig/svelte@^1.0.10":
version "1.0.10"
resolved "https://registry.yarnpkg.com/@tsconfig/svelte/-/svelte-1.0.10.tgz#30ec7feeee0bdf38b12a50f0686f8a2e7b6b9dc0"
@ -220,6 +225,11 @@
dependencies:
"@types/d3-selection" "^1"
"@types/diff@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@types/diff/-/diff-5.0.0.tgz#eb71e94feae62548282c4889308a3dfb57e36020"
integrity sha512-jrm2K65CokCCX4NmowtA+MfXyuprZC13jbRuwprs6/04z/EcFg/MCwYdsHn+zgV4CQBiATiI7AEq7y1sZCtWKA==
"@types/eslint-visitor-keys@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
@ -279,6 +289,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.38.tgz#66a7c068305dbd64cf167d0f6b6b6be71dd453e1"
integrity sha512-oxo8j9doh7ab9NwDA9bCeFfjHRF/uzk+fTljCy8lMjZ3YzZGAXNDKhTE3Byso/oy32UTUQIXB3HCVHu3d2T3xg==
"@types/node@^14.14.20":
version "14.14.20"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.20.tgz#f7974863edd21d1f8a494a73e8e2b3658615c340"
integrity sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==
"@types/prop-types@*":
version "15.7.3"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
@ -959,6 +974,11 @@ dezalgo@^1.0.0:
asap "^2.0.0"
wrappy "1"
diff@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
doctrine@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"