Skip to content

Commit bc4a8ab

Browse files
ryanbas21claude
andcommitted
feat: add treeshake-check package, syncpack, and lefthook
Add @wolfcola/treeshake-check — a tree-shakeability analyzer built on Effect and Rollup. Bundles a package as a side-effect-only import and reports what Rollup couldn't eliminate, with AST-based cause detection. Tooling: - syncpack for dependency version consistency and catalog enforcement - lefthook pre-commit hook: syncpack lint, eslint --fix, prettier --write - .prettierignore and eslint ignores for fixtures and build output - Add @effect/cli, @effect/platform, @effect/platform-node, @effect/vitest to workspace catalog; bump effect to ^3.21.1 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 668e660 commit bc4a8ab

31 files changed

Lines changed: 5952 additions & 1226 deletions

.changeset/add-treeshake-check.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@wolfcola/treeshake-check': minor
3+
---
4+
5+
Add treeshake-check package — a tree-shakeability analyzer for npm packages built on Effect and Rollup

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ elm-stuff/
99
*.crx.zip
1010
*.zip
1111
packged/
12+
out-tsc/

.prettierignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
dist
2+
node_modules
3+
coverage
4+
elm-stuff
5+
out-tsc
6+
pnpm-lock.yaml
7+
**/__fixtures__/bad-syntax/**

.syncpackrc.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// @ts-check
2+
3+
/** @type {import("syncpack").RcFile} */
4+
const config = {
5+
sortFirst: [
6+
'name',
7+
'version',
8+
'private',
9+
'description',
10+
'license',
11+
'type',
12+
'exports',
13+
'main',
14+
'types',
15+
'bin',
16+
'files',
17+
'scripts',
18+
'dependencies',
19+
'devDependencies',
20+
'peerDependencies',
21+
],
22+
versionGroups: [
23+
// ─── Ignore catalog source definitions ─────────────────────────────
24+
// pnpm-workspace.yaml defines the actual versions — these are the
25+
// source of truth for catalogs and should not be pinned.
26+
{
27+
label: 'Catalog source definitions are ignored',
28+
packages: ['pnpm-workspace.yaml'],
29+
isIgnored: true,
30+
},
31+
// ─── Ignore peer dependencies ──────────────────────────────────────
32+
// Peer deps use semver ranges set by the consumer, not catalogs.
33+
{
34+
label: 'Peer dependencies are ignored',
35+
dependencyTypes: ['peer'],
36+
isIgnored: true,
37+
},
38+
// ─── Catalog enforcement: effect ecosystem ─────────────────────────
39+
{
40+
label: 'Effect packages must use catalog:effect',
41+
dependencies: [
42+
'effect',
43+
'@effect/cli',
44+
'@effect/platform',
45+
'@effect/platform-node',
46+
'@effect/vitest',
47+
],
48+
pinVersion: 'catalog:effect',
49+
},
50+
// ─── Catalog enforcement: vitest ───────────────────────────────────
51+
{
52+
label: 'Vitest must use catalog:vitest',
53+
dependencies: ['vitest'],
54+
pinVersion: 'catalog:vitest',
55+
},
56+
// ─── Catalog enforcement: vite ─────────────────────────────────────
57+
{
58+
label: 'Vite must use catalog:vite',
59+
dependencies: ['vite'],
60+
pinVersion: 'catalog:vite',
61+
},
62+
// ─── Internal workspace deps ───────────────────────────────────────
63+
{
64+
label: 'Internal @wolfcola/* packages must use workspace:*',
65+
dependencies: ['@wolfcola/*'],
66+
pinVersion: 'workspace:*',
67+
},
68+
// ─── Banned packages ───────────────────────────────────────────────
69+
{
70+
label: '@effect/schema is deprecated — use effect/Schema',
71+
dependencies: ['@effect/schema'],
72+
isBanned: true,
73+
},
74+
],
75+
};
76+
77+
export default config;

eslint.config.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export default [
1919
'**/coverage',
2020
'**/elm-stuff',
2121
'**/vite.config.*.timestamp*',
22+
'**/__fixtures__/**',
23+
'**/out-tsc/**',
2224
],
2325
},
2426
...compat.extends('plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'),
@@ -44,7 +46,6 @@ export default [
4446
'@typescript-eslint/no-use-before-define': 'warn',
4547
'@typescript-eslint/no-unused-vars': ['error', { ignoreRestSiblings: true }],
4648
'max-len': 'off',
47-
quotes: ['error', 'single', { allowTemplateLiterals: true }],
4849
},
4950
},
5051
{

lefthook.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
pre-commit:
2+
commands:
3+
syncpack-lint:
4+
run: pnpm syncpack lint
5+
stage_fixed: true
6+
eslint:
7+
glob: '*.{js,mjs,ts,mts,tsx}'
8+
exclude: '(out-tsc|__fixtures__|dist|node_modules)/'
9+
run: pnpm eslint --no-warn-ignored --fix {staged_files}
10+
stage_fixed: true
11+
prettier:
12+
glob: '*.{js,mjs,ts,mts,tsx,json,yaml,yml,md,css,html}'
13+
exclude: '(out-tsc|__fixtures__/bad-syntax|dist|node_modules)/'
14+
run: pnpm prettier --write {staged_files}
15+
stage_fixed: true

package.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,28 @@
2020
"test": "vitest run",
2121
"typecheck": "tsc --build",
2222
"changeset": "changeset",
23-
"version": "changeset version",
24-
"release": "pnpm build && changeset publish"
23+
"version": "changeset version && prettier --write '**/package.json' pnpm-workspace.yaml",
24+
"release": "pnpm build && changeset publish",
25+
"syncpack:lint": "syncpack lint",
26+
"syncpack:fix": "syncpack fix-mismatches",
27+
"prepare": "test \"$CI\" = 'true' || lefthook install"
2528
},
2629
"devDependencies": {
2730
"@changesets/changelog-github": "^0.6.0",
2831
"@changesets/cli": "^2.27.9",
2932
"@eslint/eslintrc": "^3.0.0",
3033
"@eslint/js": "~9.39.0",
34+
"@types/node": "^22.0.0",
3135
"@typescript-eslint/eslint-plugin": "^8.45.0",
3236
"@typescript-eslint/parser": "^8.45.0",
3337
"eslint": "^9.8.0",
3438
"eslint-config-prettier": "10.1.8",
3539
"eslint-plugin-import": "2.31.0",
3640
"eslint-plugin-prettier": "^5.2.3",
37-
"@types/node": "^22.0.0",
3841
"jsdom": "^26.1.0",
42+
"lefthook": "^2.1.6",
3943
"prettier": "^3.2.5",
44+
"syncpack": "^15.1.2",
4045
"typescript": "5.8.3",
4146
"vite": "catalog:vite",
4247
"vitest": "catalog:vitest"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "@wolfcola/treeshake-check",
3+
"version": "0.0.0",
4+
"description": "Check whether a package can be fully tree-shaken by Rollup",
5+
"license": "MIT",
6+
"type": "module",
7+
"exports": {
8+
".": {
9+
"types": "./dist/src/index.d.ts",
10+
"import": "./dist/src/index.js",
11+
"default": "./dist/src/index.js"
12+
},
13+
"./package.json": "./package.json"
14+
},
15+
"main": "./dist/src/index.js",
16+
"types": "./dist/src/index.d.ts",
17+
"bin": {
18+
"treeshake-check": "./dist/src/index.js"
19+
},
20+
"files": [
21+
"dist"
22+
],
23+
"scripts": {
24+
"build": "tsc -p tsconfig.lib.json",
25+
"lint": "eslint .",
26+
"test": "vitest run"
27+
},
28+
"dependencies": {
29+
"@effect/cli": "catalog:effect",
30+
"@effect/platform": "catalog:effect",
31+
"@effect/platform-node": "catalog:effect",
32+
"@rollup/plugin-virtual": "^3.0.2",
33+
"acorn": "^8.16.0",
34+
"effect": "catalog:effect",
35+
"rollup": "^4.59.0"
36+
},
37+
"devDependencies": {
38+
"@effect/vitest": "catalog:effect",
39+
"vitest": "catalog:vitest"
40+
}
41+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
this is not valid javascript !!!###
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Pure function exports — no side effects, fully tree-shakeable
2+
export function add(a, b) {
3+
return a + b;
4+
}
5+
6+
export function multiply(a, b) {
7+
return a * b;
8+
}
9+
10+
export const PI = 3.14159;

0 commit comments

Comments
 (0)