diff --git a/README.md b/README.md index 06ca4203..e89428ca 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,41 @@ There are two ways to get help or provide feedback (and we try to always respond ## Development Tools -For information about the development tools used to maintain derived projects (like opensaas.sh), see [tools/README.md](./tools/README.md). +### Code Quality Tools + +This repository includes comprehensive code quality tooling to help maintain code standards: + +#### Prettier (Code Formatting) +Prettier is configured for automatic code formatting across all JavaScript, TypeScript, and other supported files. + +```bash +# Check if files are formatted correctly +npm run prettier:check + +# Automatically format all files +npm run prettier:format +``` + +#### ESLint (Code Linting) +ESLint is configured with TypeScript and React support to catch potential bugs and enforce code quality standards. + +```bash +# Run ESLint to check for issues +npm run lint + +# Automatically fix fixable issues +npm run lint:fix +``` + +The ESLint configuration includes: +- TypeScript support with `@typescript-eslint` +- React and React Hooks linting +- Sensible defaults tuned for a SaaS application +- Automatic support for CommonJS (.cjs), ES Modules (.mjs), and TypeScript files + +Both Prettier and ESLint checks are automatically run in CI/CD pipelines to ensure code quality. + +For information about other development tools used to maintain derived projects (like opensaas.sh), see [tools/README.md](./tools/README.md). ## Contributing diff --git a/eslint.config.js b/eslint.config.js index b52cdbf9..6fab8a66 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,7 +1,7 @@ import js from "@eslint/js"; -import tseslint from "typescript-eslint"; import reactPlugin from "eslint-plugin-react"; import reactHooksPlugin from "eslint-plugin-react-hooks"; +import tseslint from "typescript-eslint"; export default tseslint.config( // Ignore patterns @@ -42,6 +42,9 @@ export default tseslint.config( "react/prop-types": "off", // Using TypeScript for type checking "react/no-unescaped-entities": "off", // Allow apostrophes in JSX "react/no-unknown-property": "off", // Allow custom properties (e.g., Alpine.js x-data) + "react-hooks/exhaustive-deps": "warn", // React hooks dependencies - warning instead of error + "react-hooks/set-state-in-effect": "warn", // Allow setState in effects (common pattern in this codebase) + "react/jsx-key": "error", // JSX key prop - keep as error since it's important }, }, @@ -55,6 +58,21 @@ export default tseslint.config( require: "readonly", __dirname: "readonly", __filename: "readonly", + process: "readonly", + console: "readonly", + }, + }, + }, + + // Node.js scripts and MJS files + { + files: ["**/*.mjs", "**/scripts/**/*.js"], + languageOptions: { + globals: { + process: "readonly", + console: "readonly", + __dirname: "readonly", + __filename: "readonly", }, }, }, @@ -79,6 +97,18 @@ export default tseslint.config( "@typescript-eslint/ban-ts-comment": "warn", // Don't enforce preference for const "prefer-const": "warn", + // Allow extra non-null assertions - warn instead of error + "@typescript-eslint/no-extra-non-null-assertion": "warn", + // Allow unused expressions (common in ternaries) + "@typescript-eslint/no-unused-expressions": "warn", + // Case declarations - warn instead of error + "no-case-declarations": "warn", + // Sparse arrays - warn instead of error + "no-sparse-arrays": "warn", + // Extra boolean cast - warn + "no-extra-boolean-cast": "warn", + // Prefer rest params - warn + "prefer-rest-params": "warn", }, }, ); diff --git a/package.json b/package.json index 16ae0238..384d4cc9 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,10 @@ "name": "wasp-repo", "type": "module", "scripts": { - "prettier:check": "prettier --ignore-unknown --check --config .prettierrc .", - "prettier:format": "prettier --ignore-unknown --write --config .prettierrc .", "lint": "eslint .", - "lint:fix": "eslint . --fix" + "lint:fix": "eslint . --fix", + "prettier:check": "prettier --ignore-unknown --check --config .prettierrc .", + "prettier:format": "prettier --ignore-unknown --write --config .prettierrc ." }, "devDependencies": { "@eslint/js": "^9.39.1", diff --git a/template/app/src/admin/dashboards/analytics/SourcesTable.tsx b/template/app/src/admin/dashboards/analytics/SourcesTable.tsx index 16f0084e..8d5c48b5 100644 --- a/template/app/src/admin/dashboards/analytics/SourcesTable.tsx +++ b/template/app/src/admin/dashboards/analytics/SourcesTable.tsx @@ -32,7 +32,10 @@ const SourcesTable = ({ {sources && sources.length > 0 ? ( sources.map((source) => ( -
+

{source.name}

diff --git a/template/app/src/landing-page/components/Clients.tsx b/template/app/src/landing-page/components/Clients.tsx index 09f41540..05c73978 100644 --- a/template/app/src/landing-page/components/Clients.tsx +++ b/template/app/src/landing-page/components/Clients.tsx @@ -4,6 +4,13 @@ import PrismaLogo from "../logos/PrismaLogo"; import SalesforceLogo from "../logos/SalesforceLogo"; export default function Clients() { + const logos = [ + { id: "salesforce", component: }, + { id: "prisma", component: }, + { id: "astro", component: }, + { id: "openai", component: }, + ]; + return (

@@ -11,17 +18,12 @@ export default function Clients() {

- {[ - , - , - , - , - ].map((logo, index) => ( + {logos.map((logo) => (
- {logo} + {logo.component}
))}
diff --git a/template/app/src/payment/stripe/webhook.ts b/template/app/src/payment/stripe/webhook.ts index 44d1924d..d0a7cd40 100644 --- a/template/app/src/payment/stripe/webhook.ts +++ b/template/app/src/payment/stripe/webhook.ts @@ -141,7 +141,7 @@ async function handleInvoicePaid( function getInvoicePriceId(invoice: Stripe.Invoice): Stripe.Price["id"] { const invoiceLineItems = invoice.lines.data; - // We only expect one line item. + // We only expect one line item. // If your workflow expects more, you should change this function to handle them. if (invoiceLineItems.length !== 1) { throw new Error("There should be exactly one line item in Stripe invoice"); @@ -204,7 +204,7 @@ function getSubscriptionPriceId( subscription: Stripe.Subscription, ): Stripe.Price["id"] { const subscriptionItems = subscription.items.data; - // We only expect one subscription item. + // We only expect one subscription item. // If your workflow expects more, you should change this function to handle them. if (subscriptionItems.length !== 1) { throw new Error(