Skip to content

Commit 5ff09f9

Browse files
authored
Feat: build optimization, add tests, set up knip config, remove dead code (#708)
1 parent d573566 commit 5ff09f9

42 files changed

Lines changed: 3843 additions & 3764 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cursor/rules/posthog-integration.mdc

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,60 @@
44

55
PostHog is already integrated into this Astro project. The configuration includes:
66

7-
- PostHog initialization in `src/components/posthog.astro`
7+
- PostHog initialization in `src/components/PostHog.astro`
88
- Layout setup in `src/layouts/PostHogLayout.astro`
9+
- Typed analytics helpers in `src/utils/analytics.ts`
910
- Environment variables for API key and host
1011

1112
## Key Guidelines
1213

14+
### Analytics Helper (`src/utils/analytics.ts`)
15+
All PostHog event tracking MUST go through the typed helper functions in `src/utils/analytics.ts`. Do NOT call `window.posthog.capture()` or `posthog.capture()` directly.
16+
17+
Available functions:
18+
- `trackToolPageView()` - Tool detail page loads
19+
- `trackOutboundClick()` - External link clicks
20+
- `trackSponsorshipCTA()` - Sponsor-related interactions
21+
- `trackFilterApplied()` - Language/platform filter changes
22+
- `trackFeaturedArticleClick()` - Featured article clicks
23+
- `trackScrollDepth()` - Scroll depth thresholds
24+
25+
Each function has typed props and handles the `window.posthog` safety check internally.
26+
27+
### Using in React/TSX Components
28+
Import and call the tracking functions directly:
29+
```tsx
30+
import { trackOutboundClick } from '@/utils/analytics';
31+
32+
trackOutboundClick({ url, tool_slug, link_type: 'website', is_sponsored: false, placement: 'header' });
33+
```
34+
35+
### Using in Astro Components
36+
Use bundled `<script>` tags (NOT `is:inline`) so imports work:
37+
```astro
38+
<script>
39+
import { trackSponsorshipCTA } from '@/utils/analytics';
40+
41+
trackSponsorshipCTA({ cta_type: 'banner_click', source_page: window.location.pathname });
42+
</script>
43+
```
44+
45+
If you need server-side data in the script, pass it via `data-*` attributes on an HTML element:
46+
```astro
47+
<div id="tracking-data" data-tool-slug={slug} hidden></div>
48+
49+
<script>
50+
import { trackToolPageView } from '@/utils/analytics';
51+
52+
const el = document.getElementById('tracking-data');
53+
if (el) {
54+
trackToolPageView({ tool_slug: el.dataset.toolSlug! });
55+
}
56+
</script>
57+
```
58+
59+
Do NOT use `<script is:inline>` with `define:vars` for analytics — this prevents bundling and bypasses the typed helpers.
60+
1361
### Component Structure
1462
- PostHog component uses `is:inline` directive to prevent Astro from processing the script
1563
- Layout wraps PostHog component in the `<head>` section
@@ -20,25 +68,21 @@ PostHog is already integrated into this Astro project. The configuration include
2068
- `PUBLIC_POSTHOG_KEY` - Your PostHog project API key
2169
- `PUBLIC_POSTHOG_HOST` - Your PostHog instance URL
2270

23-
### Best Practices
24-
- Always use `posthog.identify()` when users sign in
25-
- Use `posthog.capture()` for custom events
26-
- Feature flags can be accessed with `posthog.isFeatureEnabled()`
27-
- Keep the PostHog script in the head section for accurate tracking
28-
2971
### File Structure
3072
```
3173
src/
3274
├── components/
33-
│ └── posthog.astro # PostHog initialization
75+
│ └── PostHog.astro # PostHog initialization
3476
├── layouts/
35-
│ └── PostHogLayout.astro # Layout with PostHog
77+
│ └── PostHogLayout.astro # Layout with PostHog + scroll tracking
78+
├── utils/
79+
│ └── analytics.ts # Typed tracking helpers (use these!)
3680
└── pages/
37-
└── *.astro # Your pages using PostHogLayout
81+
└── *.astro # Your pages using PostHogLayout
3882
```
3983

4084
### Common Patterns
4185
- Wrap pages with PostHogLayout for analytics
4286
- Use PostHog's autocapture for basic interaction tracking
43-
- Implement custom events for business-specific actions
87+
- Use the typed helpers in `analytics.ts` for all custom events
4488
- Use feature flags for A/B testing and gradual rollouts

.github/workflows/build-and-test.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ jobs:
3939
- name: Install dependencies
4040
run: pnpm install
4141

42+
# Run tests
43+
- name: Test
44+
run: pnpm test
45+
46+
# Check for unused dependencies and exports
47+
- name: Dependency check
48+
run: pnpm depcheck
49+
4250
# Run linting
4351
- name: Lint
4452
run: pnpm lint

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,8 @@ coverage/
2828

2929
.turbo/
3030

31+
# build cache
32+
.cache/
33+
3134
# check errors
3235
.lint-check-errors.md

eslint.config.mjs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import reactHooksPlugin from 'eslint-plugin-react-hooks';
77
import globals from 'globals';
88
import tseslint from 'typescript-eslint';
99

10-
export default tseslint.config(
10+
/** @type {import('eslint').Linter.Config[]} */
11+
export default [
1112
// Global ignores
1213
{
1314
ignores: [
@@ -93,5 +94,5 @@ export default tseslint.config(
9394
},
9495

9596
// Prettier config (must be last to override other formatting rules)
96-
prettierConfig
97-
);
97+
prettierConfig,
98+
];

knip.config.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { KnipConfig } from 'knip';
2+
3+
const config: KnipConfig = {
4+
// UI primitives and icons are a shared library — exports are used dynamically
5+
// StaticPageLayout is referenced via markdown frontmatter `layout:` which knip can't detect
6+
ignore: [
7+
'src/components/ui/**',
8+
'src/components/icons/**',
9+
'src/layouts/StaticPageLayout.astro',
10+
],
11+
// Radix deps are used in ignored ui/ components; posthog-js is loaded via
12+
// script tag in PostHog.astro; framer-motion is a peer dep of ShadCN UI
13+
ignoreDependencies: [
14+
'@radix-ui/react-checkbox',
15+
'@radix-ui/react-scroll-area',
16+
'framer-motion',
17+
'posthog-js',
18+
],
19+
20+
// Icon and UI components re-export named + default — this is intentional
21+
exclude: ['duplicates'],
22+
};
23+
24+
export default config;

package.json

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,67 +18,65 @@
1818
"lint-fix": "eslint --fix .",
1919
"prettier": "prettier --check .",
2020
"prettier-fix": "prettier --write .",
21-
"check-links": "node scripts/check-tool-links.mjs"
21+
"check-links": "node scripts/check-tool-links.mjs",
22+
"depcheck": "knip",
23+
"test": "vitest run"
2224
},
2325
"dependencies": {
24-
"@algolia/autocomplete-core": "^1.19.4",
25-
"@astrojs/check": "0.9.6-beta.1",
26-
"@astrojs/netlify": "7.0.0-beta.5",
27-
"@astrojs/react": "5.0.0-beta.1",
28-
"@astrojs/rss": "4.0.15-beta.1",
29-
"@astrojs/sitemap": "3.6.1-beta.2",
30-
"@astrojs/ts-plugin": "^1.10.6",
26+
"@astrojs/check": "0.9.7",
27+
"@astrojs/netlify": "7.0.2",
28+
"@astrojs/react": "5.0.0",
29+
"@astrojs/rss": "4.0.17",
30+
"@astrojs/sitemap": "3.7.1",
31+
"@astrojs/ts-plugin": "^1.10.7",
3132
"@headlessui/react": "^2.2.9",
32-
"@markdoc/markdoc": "^0.5.4",
3333
"@radix-ui/react-checkbox": "^1.3.3",
3434
"@radix-ui/react-dialog": "^1.1.15",
3535
"@radix-ui/react-popover": "^1.1.15",
3636
"@radix-ui/react-scroll-area": "^1.2.10",
3737
"@radix-ui/react-slot": "^1.2.4",
38-
"@sindresorhus/slugify": "^3.0.0",
3938
"@tanstack/react-table": "^8.21.3",
40-
"astro": "6.0.0-beta.1",
39+
"astro": "6.0.4",
4140
"astro-seo": "^1.1.0",
4241
"class-variance-authority": "^0.7.1",
4342
"clsx": "^2.1.1",
4443
"cmdk": "^1.1.1",
4544
"date-fns": "^4.1.0",
46-
"framer-motion": "^12.24.7",
47-
"js-yaml": "^4.1.1",
48-
"lucide-react": "^0.562.0",
49-
"posthog-js": "^1.315.0",
45+
"framer-motion": "^12.36.0",
46+
"lucide-react": "^0.577.0",
47+
"posthog-js": "^1.360.1",
5048
"prism-react-renderer": "^2.4.1",
51-
"react": "^19.2.0",
52-
"react-dom": "^19.2.0",
53-
"react-highlight-words": "^0.21.0",
54-
"satori": "^0.18.3",
49+
"react": "^19.2.4",
50+
"react-dom": "^19.2.4",
51+
"satori": "^0.25.0",
5552
"satori-html": "^0.3.2",
5653
"schema-dts": "^1.1.5",
5754
"sharp": "^0.34.0",
58-
"tailwind-merge": "^3.4.0",
59-
"tw-animate-css": "^1.3.0",
60-
"zod": "^4.3.5"
55+
"tailwind-merge": "^3.5.0",
56+
"tw-animate-css": "^1.3.0"
6157
},
6258
"devDependencies": {
63-
"@eslint/js": "^9.39.0",
64-
"@ianvs/prettier-plugin-sort-imports": "^4.7.0",
59+
"@eslint/js": "^10.0.1",
60+
"@ianvs/prettier-plugin-sort-imports": "^4.7.1",
6561
"@tailwindcss/typography": "^0.5.19",
66-
"@tailwindcss/vite": "^4.1.0",
67-
"@types/react": "^19.2.0",
62+
"@tailwindcss/vite": "^4.2.1",
63+
"@types/react": "^19.2.14",
6864
"@types/react-dom": "^19.2.0",
69-
"eslint": "^9.39.0",
65+
"eslint": "^10.0.3",
7066
"eslint-config-prettier": "^10.1.0",
71-
"eslint-plugin-astro": "^1.5.0",
67+
"eslint-plugin-astro": "^1.6.0",
7268
"eslint-plugin-jsx-a11y": "^6.10.2",
7369
"eslint-plugin-react": "^7.37.5",
7470
"eslint-plugin-react-hooks": "^7.0.0",
75-
"globals": "^16.5.0",
71+
"globals": "^17.4.0",
72+
"knip": "^5.86.0",
7673
"open-graph-scraper": "^6.11.0",
77-
"prettier": "^3.7.4",
74+
"prettier": "^3.8.1",
7875
"prettier-plugin-astro": "^0.14.1",
7976
"prettier-plugin-tailwindcss": "^0.7.0",
80-
"tailwindcss": "^4.1.0",
77+
"tailwindcss": "^4.2.1",
8178
"typescript": "5.9.3",
82-
"typescript-eslint": "^8.52.0"
79+
"typescript-eslint": "^8.57.0",
80+
"vitest": "^4.1.0"
8381
}
8482
}

0 commit comments

Comments
 (0)