Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Code Quality

permissions:
contents: read

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
code-quality:
name: Code Quality
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: latest

- name: Install pnpm
uses: pnpm/action-setup@v4

- name: Setup pnpm config
run: |
pnpm config set store-dir ~/.pnpm-store

- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install

- name: Check Prettier formatting
run: pnpm format --check

- name: Run ESLint and Stylelint
run: pnpm lint
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lint-staged
6 changes: 6 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.md
*.mdx

/pnpm-lock.yaml

/.github/
7 changes: 7 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"recommendations": [
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint"
]
}
141 changes: 141 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/** @author Ka Pui (August) Cheung */

import path from "node:path";
import { fileURLToPath } from "node:url";

import docusaurusPlugin from "@docusaurus/eslint-plugin";
import { includeIgnoreFile } from "@eslint/compat";
import eslintConfigPrettier from "eslint-config-prettier";
import importPlugin from "eslint-plugin-import";
import jsxA11y from "eslint-plugin-jsx-a11y";
import reactPlugin from "eslint-plugin-react";
import simpleImportSort from "eslint-plugin-simple-import-sort";
import eslintPluginUnicorn from "eslint-plugin-unicorn";
import tseslint from "typescript-eslint";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const gitignorePath = path.resolve(__dirname, ".gitignore");

/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/** @see https://eslint.org/docs/latest/use/configure/ */
const eslintConfig = tseslint.config(
includeIgnoreFile(gitignorePath),

// eslint-disable-next-line import/no-named-as-default-member
tseslint.configs.recommendedTypeChecked,
// eslint-disable-next-line import/no-named-as-default-member
tseslint.configs.stylisticTypeChecked,
{
languageOptions: {
parserOptions: {
projectService: {
allowDefaultProject: ["*.config.js"],
},
tsconfigRootDir: import.meta.dirname,
},
},
},

eslintPluginUnicorn.configs.recommended,
importPlugin.flatConfigs.recommended,
importPlugin.flatConfigs.typescript,
{
settings: {
"import/resolver": {
typescript: true,
node: true,
},
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".tsx"],
},
},
},
{
plugins: {
"simple-import-sort": simpleImportSort,
},
rules: {
"simple-import-sort/imports": "warn",
"simple-import-sort/exports": "warn",
},
},

reactPlugin.configs.flat.recommended,
reactPlugin.configs.flat["jsx-runtime"],
jsxA11y.flatConfigs.recommended,

{
rules: {
"@typescript-eslint/consistent-type-imports": [
"warn",
{
fixStyle: "inline-type-imports",
},
],
"@typescript-eslint/no-non-null-assertion": "error",
"import/consistent-type-specifier-style": ["warn", "prefer-inline"],
"import/newline-after-import": "warn",
"import/no-duplicates": [
"error",
{
"prefer-inline": true,
},
],
"react/jsx-curly-brace-presence": ["warn", "never"],
"react/jsx-sort-props": [
"warn",
{
callbacksLast: true,
multiline: "last",
reservedFirst: true,
shorthandFirst: true,
},
],
"unicorn/filename-case": [
"warn",
{
cases: {
kebabCase: true,
camelCase: true,
pascalCase: true,
},
},
],
"unicorn/no-null": "off",
"unicorn/prevent-abbreviations": [
"warn",
{
replacements: {
args: false,
dev: false,
env: false,
params: false,
props: false,
ref: false,
},
},
],
},
},

{
plugins: {
"@docusaurus": docusaurusPlugin,
},
rules: {
"import/no-unresolved": [
"error",
{ ignore: ["^@theme", "^@docusaurus", "^@site"] },
],
"@docusaurus/string-literal-i18n-messages": "error",
"@docusaurus/no-html-links": "warn",
"@docusaurus/prefer-docusaurus-heading": "warn",
},
},

eslintConfigPrettier,
);

export default eslintConfig;
12 changes: 12 additions & 0 deletions lint-staged.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/** @author Ka Pui (August) Cheung */
// @ts-check

const lintStagedConfig = {
"*.{js,jsx,ts,tsx}":
"eslint --fix --cache --cache-location ./node_modules/.cache/eslint",
"*.{css,scss}":
"stylelint --fix --cache --cache-location ./node_modules/.cache/stylelint",
"*": "prettier --write --ignore-unknown --no-error-on-unmatched-pattern --cache",
};

export default lintStagedConfig;
32 changes: 28 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,46 @@
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc"
"typecheck": "tsc",
"format": "prettier --write . --cache",
"lint": "pnpm lint:js && pnpm lint:css",
"lint:js": "eslint . --cache --cache-location ./node_modules/.cache/eslint",
"lint:css": "stylelint \"src/**/*.css\" --cache --cache-location ./node_modules/.cache/stylelint",
"prepare": "husky"
},
"dependencies": {
"@docusaurus/core": "^3.7.0",
"@docusaurus/preset-classic": "^3.7.0",
"@mdx-js/react": "^3.1.0",
"clsx": "^2.1.1",
"prism-react-renderer": "^2.4.1",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@docusaurus/eslint-plugin": "^3.7.0",
"@docusaurus/module-type-aliases": "^3.7.0",
"@docusaurus/tsconfig": "^3.7.0",
"@docusaurus/types": "^3.7.0",
"typescript": "~5.7.3"
"@eslint/compat": "^1.2.7",
"@types/react": "^19.0.10",
"eslint": "^9.21.0",
"eslint-config-prettier": "^10.0.1",
"eslint-import-resolver-typescript": "^3.8.3",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-unicorn": "^57.0.0",
"husky": "^9.1.7",
"lint-staged": "^15.4.3",
"prettier": "^3.5.2",
"prettier-plugin-jsdoc": "^1.3.2",
"stylelint": "^16.14.1",
"stylelint-config-standard": "^37.0.0",
"typescript": "~5.7.3",
"typescript-eslint": "^8.25.0"
},
"browserslist": {
"production": [
Expand Down
Loading
Loading