-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add proper linting and pre-commit hooks
Signed-off-by: Rūdolfs Ošiņš <[email protected]>
- Loading branch information
Showing
8 changed files
with
1,356 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// Copyright © 2022 The Radicle Design System Contributors | ||
// | ||
// This file is part of radicle-design-system, distributed under the GPLv3 | ||
// with Radicle Linking Exception. For full terms see the included | ||
// LICENSE file. | ||
|
||
module.exports = { | ||
env: { | ||
node: true, | ||
browser: true, | ||
es6: true, | ||
}, | ||
parser: "@typescript-eslint/parser", | ||
parserOptions: { | ||
createDefaultProgram: true, | ||
ecmaVersion: 2019, | ||
sourceType: "module", | ||
}, | ||
ignorePatterns: ["!.license-compliancerc.js"], | ||
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], | ||
plugins: ["svelte3", "@typescript-eslint"], | ||
overrides: [ | ||
{ | ||
files: ["*.svelte"], | ||
processor: "svelte3/svelte3", | ||
}, | ||
{ | ||
files: ["scripts/*.ts"], | ||
rules: { | ||
// Script files are not bundled so we can’t use module imports. | ||
"@typescript-eslint/no-var-requires": "off", | ||
}, | ||
}, | ||
{ | ||
files: ["*.js", "*.mjs"], | ||
rules: { | ||
"@typescript-eslint/explicit-module-boundary-types": "off", | ||
}, | ||
}, | ||
], | ||
rules: { | ||
// Disallow Unused Variables | ||
// https://eslint.org/docs/rules/no-unused-vars | ||
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], | ||
// require using arrow functions as callbacks | ||
// https://eslint.org/docs/rules/prefer-arrow-callback | ||
"prefer-arrow-callback": "error", | ||
// require using template literals instead of string concatenation | ||
// http://eslint.org/docs/rules/prefer-template | ||
"prefer-template": "error", | ||
// require using of const declaration for variables that are never modified after declared | ||
// https://eslint.org/docs/rules/prefer-const | ||
"prefer-const": "error", | ||
// disallow modifying variables that are declared using const | ||
// https://eslint.org/docs/rules/no-const-assign | ||
"no-const-assign": "error", | ||
// require let or const instead of var | ||
// https://eslint.org/docs/rules/no-var | ||
"no-var": "error", | ||
// require at least one whitespace after comments( // and /*) | ||
// https://eslint.org/docs/rules/spaced-comment | ||
"spaced-comment": ["warn", "always"], | ||
// Require `===` and `!==` comparisons | ||
eqeqeq: "error", | ||
// Enforce curly braces for if/else statements for better clarity. | ||
curly: "error", | ||
|
||
// We are ok with providing explict type annotations for additional | ||
// clarity. | ||
"@typescript-eslint/no-inferrable-types": "off", | ||
// We are ok with empty functions. Often we need a no-op function | ||
// as an argument. | ||
"@typescript-eslint/no-empty-function": "off", | ||
"@typescript-eslint/no-implicit-any-catch": "error", | ||
"@typescript-eslint/explicit-member-accessibility": "error", | ||
"@typescript-eslint/explicit-module-boundary-types": "error", | ||
}, | ||
settings: { | ||
"svelte3/typescript": true, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#!/bin/sh | ||
. "$(dirname "$0")/_/husky.sh" | ||
|
||
yarn run lint-staged |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
"*.{ts,js,css,json,html,svelte}": | ||
"*.{ts,css,json,html,svelte}": | ||
- prettier --write | ||
"*.{js,ts,svelte}": | ||
"*.{ts,svelte}": | ||
- eslint --fix --max-warnings=0 | ||
"*": | ||
- ./scripts/license-header.ts check |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
#!/usr/bin/env -S node -r ts-node/register/transpile-only | ||
|
||
// Copyright © 2022 The Radicle Design System Contributors | ||
// | ||
// This file is part of radicle-design-system, distributed under the GPLv3 | ||
// with Radicle Linking Exception. For full terms see the included | ||
// LICENSE file. | ||
|
||
import yargs from "yargs"; | ||
import * as fs from "fs/promises"; | ||
import * as Path from "path"; | ||
import execa from "execa"; | ||
|
||
// Error that is shown without a stacktrace to the user | ||
class UserError extends Error { | ||
public constructor(public message: string) { | ||
super(message); | ||
} | ||
} | ||
|
||
async function main() { | ||
yargs | ||
.command<{ files: string[] | undefined }>({ | ||
command: "check [files...]", | ||
describe: "Check presence of license headers in files", | ||
builder: yargs => { | ||
return yargs.positional("files", { | ||
describe: | ||
"Files to check. If not provided, all files are checked for a license header", | ||
array: true, | ||
type: "string", | ||
}); | ||
}, | ||
handler: async ({ files }) => { | ||
if (!files || files.length === 0) { | ||
files = await getPaths(); | ||
} | ||
|
||
let failure = false; | ||
for (const file of files) { | ||
if (!requireLicenseHeader(file)) { | ||
continue; | ||
} | ||
|
||
const content = await fs.readFile(file, "utf8"); | ||
if (!hasLicenseHeader(content)) { | ||
failure = true; | ||
console.error(`License missing from ${file}`); | ||
} | ||
} | ||
|
||
if (failure) { | ||
throw new UserError( | ||
"License headers missing. Run `./scripts/license-header.ts add` to fix this." | ||
); | ||
} | ||
}, | ||
}) | ||
.command({ | ||
command: "add", | ||
describe: "Add missing license headers to files", | ||
handler: async () => { | ||
for (const path of await getPaths()) { | ||
const content = await fs.readFile(path, "utf8"); | ||
if (!hasLicenseHeader(content)) { | ||
console.log(`Writing license to ${path}`); | ||
const licenseComment = makeLicenseComment(Path.extname(path)); | ||
const fixedContent = `${licenseComment}${content}`; | ||
await fs.writeFile(path, fixedContent, "utf8"); | ||
} | ||
} | ||
}, | ||
}) | ||
.version(false) | ||
.strict() | ||
.wrap(Math.min(100, yargs.terminalWidth())) | ||
// For `UserError` we don’t show the stack trace. We also don’t show the help | ||
// when an error is thrown. | ||
.fail((msg, err, yargs) => { | ||
if (err === undefined) { | ||
yargs.showHelp("error"); | ||
console.error(""); | ||
console.error(msg); | ||
} else if (err instanceof UserError) { | ||
console.error(err.message); | ||
} else { | ||
console.error(err); | ||
} | ||
process.exit(1); | ||
}) | ||
.demandCommand().argv; | ||
} | ||
|
||
const licenseHeaderContent = [ | ||
` Copyright © ${new Date().getFullYear()} The Radicle Design System Contributors`, | ||
"", | ||
" This file is part of radicle-design-system, distributed under the GPLv3", | ||
" with Radicle Linking Exception. For full terms see the included", | ||
" LICENSE file.", | ||
]; | ||
|
||
function makeLicenseComment(extName: string): string { | ||
if (extName === ".js" || extName === ".ts" || extName === ".rs") { | ||
const commentLines = licenseHeaderContent.map(x => `//${x}`); | ||
return `${commentLines.join("\n")}\n\n`; | ||
} else if (extName === ".sh") { | ||
const commentLines = licenseHeaderContent.map(x => `#${x}`); | ||
return `${commentLines.join("\n")}\n\n`; | ||
} else if (extName === ".svelte") { | ||
return `<!--\n${licenseHeaderContent.join("\n")}\n-->\n`; | ||
} else if (extName === ".css") { | ||
const commentLines = licenseHeaderContent.map(x => ` *${x}`); | ||
return `/**\n${commentLines.join("\n")}\n */\n`; | ||
} else { | ||
throw new Error(`Unknown file extension ${extName}`); | ||
} | ||
} | ||
|
||
const EXTENSIONS = [".js", ".rs", ".sh", ".ts", ".svelte"]; | ||
|
||
// Returns true if the file at path requires a license header. This is | ||
// `true` if the path has one of `EXTENSIONS`. | ||
function requireLicenseHeader(path: string): boolean { | ||
if (path.endsWith("typings/node-fetch.d.ts")) { | ||
return false; | ||
} else { | ||
return EXTENSIONS.includes(Path.extname(path)); | ||
} | ||
} | ||
|
||
// Returns the list of file paths that should include license headers. | ||
// | ||
// The list consists of all files checked into version control that | ||
// have one of `EXTENSIONS`. | ||
async function getPaths(): Promise<string[]> { | ||
const result = await execa("git", ["ls-files"]); | ||
const gitPaths = result.stdout.split("\n"); | ||
return gitPaths.filter(path => { | ||
return EXTENSIONS.includes(Path.extname(path)); | ||
}); | ||
} | ||
|
||
// Pattern we use to check for the presence for the license headers in | ||
// a given line. | ||
const licenseHeaderPattern = | ||
/Copyright © \d{4} The Radicle Design System Contributors/; | ||
|
||
function hasLicenseHeader(fileContent: string): boolean { | ||
// We check the first three lines to account for shebangs and comment | ||
// starts. | ||
const head = fileContent.split("\n").slice(0, 3); | ||
return head.some(line => licenseHeaderPattern.test(line)); | ||
} | ||
|
||
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.