lintcn 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +82 -41
- package/dist/codegen.d.ts +7 -1
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +64 -498
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +29 -16
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +2 -5
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +3 -4
- package/dist/commands/remove.d.ts.map +1 -1
- package/dist/commands/remove.js +2 -5
- package/dist/discover.d.ts.map +1 -1
- package/dist/discover.js +8 -1
- package/dist/hash.d.ts.map +1 -1
- package/dist/hash.js +6 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/paths.d.ts +6 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +23 -1
- package/package.json +2 -1
- package/src/cache.ts +93 -46
- package/src/codegen.ts +74 -498
- package/src/commands/add.ts +34 -19
- package/src/commands/lint.ts +2 -5
- package/src/commands/list.ts +3 -4
- package/src/commands/remove.ts +2 -6
- package/src/discover.ts +10 -1
- package/src/hash.ts +7 -2
- package/src/index.ts +1 -0
- package/src/paths.ts +25 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
## 0.3.0
|
|
2
|
+
|
|
3
|
+
1. **Only custom rules run by default** — previously the binary included all 44 built-in tsgolint rules, producing thousands of noisy errors. Now only your `.lintcn/` rules run. True shadcn model: explicitly add each rule you want.
|
|
4
|
+
|
|
5
|
+
Before (0.2.0): `Found 2315 errors (linted 193 files with 45 rules)`
|
|
6
|
+
After (0.3.0): `Found 8 errors (linted 193 files with 1 rule)`
|
|
7
|
+
|
|
8
|
+
2. **Run `lintcn lint` from any subdirectory** — uses `find-up` to walk parent directories for `.lintcn/`. You no longer need to be at the project root:
|
|
9
|
+
```bash
|
|
10
|
+
cd packages/my-app
|
|
11
|
+
lintcn lint # finds .lintcn/ in parent
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
3. **No git required** — tsgolint source is now downloaded as a tarball from GitHub instead of cloned. Patches applied with `patch -p1`. Faster first setup, works without git installed.
|
|
15
|
+
|
|
16
|
+
4. **Fixed stale binary cache** — added `CACHE_SCHEMA_VERSION` to the content hash. Upgrading lintcn now correctly invalidates cached binaries built by older versions.
|
|
17
|
+
|
|
18
|
+
5. **Fixed partial download corruption** — if the tsgolint download fails midway, the partial directory is cleaned up so the next run starts fresh instead of failing repeatedly.
|
|
19
|
+
|
|
20
|
+
6. **Fixed GitHub URLs with `/` in branch names** — `lintcn add` now correctly handles branch names like `feature/my-branch` in GitHub blob URLs.
|
|
21
|
+
|
|
1
22
|
## 0.2.0
|
|
2
23
|
|
|
3
24
|
1. **Pinned tsgolint version** — each lintcn release bundles a specific tsgolint version (`v0.9.2`). Builds are now reproducible: everyone on the same lintcn version compiles against the same tsgolint API. Previously used `main` branch which was non-deterministic.
|
package/dist/cache.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,wBAAwB,WAAW,CAAA;AAOhD,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAuCD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2D3E;AAED,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAQ/D"}
|
package/dist/cache.js
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
|
-
// Manage cached tsgolint source
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// Manage cached tsgolint source and compiled binaries.
|
|
2
|
+
// Downloads tsgolint + typescript-go as tarballs from GitHub (no git required),
|
|
3
|
+
// applies patches with `patch -p1`, and copies internal/collections.
|
|
4
|
+
//
|
|
5
|
+
// Cache layout:
|
|
6
|
+
// ~/.cache/lintcn/tsgolint/<version>/ — extracted source (read-only)
|
|
7
|
+
// ~/.cache/lintcn/bin/<content-hash> — compiled binaries
|
|
5
8
|
import fs from 'node:fs';
|
|
6
9
|
import os from 'node:os';
|
|
7
10
|
import path from 'node:path';
|
|
11
|
+
import { pipeline } from 'node:stream/promises';
|
|
8
12
|
import { execAsync } from "./exec.js";
|
|
9
13
|
// Pinned tsgolint version — updated with each lintcn release.
|
|
10
14
|
// This ensures reproducible builds: every user on the same lintcn version
|
|
11
15
|
// compiles rules against the same tsgolint API. Changing this is a conscious
|
|
12
16
|
// decision — tsgolint API changes can break user rules.
|
|
13
17
|
export const DEFAULT_TSGOLINT_VERSION = 'v0.9.2';
|
|
18
|
+
// Pinned typescript-go commit that tsgolint v0.9.2 depends on.
|
|
19
|
+
// Found via `git ls-tree HEAD typescript-go` in the tsgolint repo.
|
|
20
|
+
// Must be updated when DEFAULT_TSGOLINT_VERSION changes.
|
|
21
|
+
const TYPESCRIPT_GO_COMMIT = '2437fa43e85103d2a18e8e41e1a2a994d0708ccf';
|
|
14
22
|
export function getCacheDir() {
|
|
15
23
|
return path.join(os.homedir(), '.cache', 'lintcn');
|
|
16
24
|
}
|
|
@@ -26,54 +34,87 @@ export function getBinaryPath(contentHash) {
|
|
|
26
34
|
export function getBuildDir() {
|
|
27
35
|
return path.join(getCacheDir(), 'build');
|
|
28
36
|
}
|
|
37
|
+
/** Download a tarball from URL and extract it to targetDir.
|
|
38
|
+
* GitHub tarballs have a top-level directory like `repo-ref/`,
|
|
39
|
+
* so we strip the first path component during extraction. */
|
|
40
|
+
async function downloadAndExtract(url, targetDir) {
|
|
41
|
+
const response = await fetch(url);
|
|
42
|
+
if (!response.ok || !response.body) {
|
|
43
|
+
throw new Error(`Failed to download ${url}: ${response.status} ${response.statusText}`);
|
|
44
|
+
}
|
|
45
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
46
|
+
// pipe through gunzip, then extract with tar (strip top-level directory)
|
|
47
|
+
const tmpTarGz = path.join(os.tmpdir(), `lintcn-${Date.now()}.tar.gz`);
|
|
48
|
+
const fileStream = fs.createWriteStream(tmpTarGz);
|
|
49
|
+
// @ts-ignore ReadableStream vs NodeJS.ReadableStream mismatch
|
|
50
|
+
await pipeline(response.body, fileStream);
|
|
51
|
+
await execAsync('tar', ['xzf', tmpTarGz, '--strip-components=1', '-C', targetDir]);
|
|
52
|
+
fs.rmSync(tmpTarGz, { force: true });
|
|
53
|
+
}
|
|
54
|
+
/** Apply git-format patches using `patch -p1` (no git required).
|
|
55
|
+
* Patches are standard unified diff format, `patch` ignores the git metadata. */
|
|
56
|
+
async function applyPatches(patchesDir, targetDir) {
|
|
57
|
+
const patches = fs.readdirSync(patchesDir)
|
|
58
|
+
.filter((f) => { return f.endsWith('.patch'); })
|
|
59
|
+
.sort();
|
|
60
|
+
for (const patchFile of patches) {
|
|
61
|
+
const patchPath = path.join(patchesDir, patchFile);
|
|
62
|
+
// --batch silences interactive prompts, -f forces application
|
|
63
|
+
await execAsync('patch', ['-p1', '--batch', '-i', patchPath], { cwd: targetDir });
|
|
64
|
+
}
|
|
65
|
+
return patches.length;
|
|
66
|
+
}
|
|
29
67
|
export async function ensureTsgolintSource(version) {
|
|
30
68
|
const sourceDir = getTsgolintSourceDir(version);
|
|
31
69
|
const readyMarker = path.join(sourceDir, '.lintcn-ready');
|
|
32
70
|
if (fs.existsSync(readyMarker)) {
|
|
33
71
|
return sourceDir;
|
|
34
72
|
}
|
|
35
|
-
|
|
36
|
-
fs.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
73
|
+
// clean up any partial previous attempt so we start fresh
|
|
74
|
+
if (fs.existsSync(sourceDir)) {
|
|
75
|
+
fs.rmSync(sourceDir, { recursive: true });
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
// download tsgolint source tarball
|
|
79
|
+
console.log(`Downloading tsgolint@${version}...`);
|
|
80
|
+
const tsgolintUrl = `https://github.com/oxc-project/tsgolint/archive/refs/tags/${version}.tar.gz`;
|
|
81
|
+
await downloadAndExtract(tsgolintUrl, sourceDir);
|
|
82
|
+
// download typescript-go source tarball into tsgolint/typescript-go/
|
|
83
|
+
const tsGoDir = path.join(sourceDir, 'typescript-go');
|
|
84
|
+
console.log('Downloading typescript-go...');
|
|
85
|
+
const tsGoUrl = `https://github.com/microsoft/typescript-go/archive/${TYPESCRIPT_GO_COMMIT}.tar.gz`;
|
|
86
|
+
await downloadAndExtract(tsGoUrl, tsGoDir);
|
|
87
|
+
// apply patches to typescript-go
|
|
88
|
+
const patchesDir = path.join(sourceDir, 'patches');
|
|
89
|
+
if (fs.existsSync(patchesDir)) {
|
|
90
|
+
const count = await applyPatches(patchesDir, tsGoDir);
|
|
91
|
+
if (count > 0) {
|
|
92
|
+
console.log(`Applied ${count} patches`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// copy internal/collections from typescript-go (required by tsgolint, done by `just init`)
|
|
96
|
+
const collectionsDir = path.join(sourceDir, 'internal', 'collections');
|
|
97
|
+
const tsGoCollections = path.join(tsGoDir, 'internal', 'collections');
|
|
98
|
+
if (fs.existsSync(tsGoCollections)) {
|
|
99
|
+
fs.mkdirSync(collectionsDir, { recursive: true });
|
|
100
|
+
const files = fs.readdirSync(tsGoCollections).filter((f) => {
|
|
101
|
+
return f.endsWith('.go') && !f.endsWith('_test.go');
|
|
58
102
|
});
|
|
103
|
+
for (const file of files) {
|
|
104
|
+
fs.copyFileSync(path.join(tsGoCollections, file), path.join(collectionsDir, file));
|
|
105
|
+
}
|
|
59
106
|
}
|
|
107
|
+
// write ready marker
|
|
108
|
+
fs.writeFileSync(readyMarker, new Date().toISOString());
|
|
109
|
+
console.log('tsgolint source ready');
|
|
60
110
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
fs.mkdirSync(collectionsDir, { recursive: true });
|
|
66
|
-
const files = fs.readdirSync(tsGoCollections).filter((f) => {
|
|
67
|
-
return f.endsWith('.go') && !f.endsWith('_test.go');
|
|
68
|
-
});
|
|
69
|
-
for (const file of files) {
|
|
70
|
-
fs.copyFileSync(path.join(tsGoCollections, file), path.join(collectionsDir, file));
|
|
111
|
+
catch (err) {
|
|
112
|
+
// clean up partial download so next run starts fresh
|
|
113
|
+
if (fs.existsSync(sourceDir)) {
|
|
114
|
+
fs.rmSync(sourceDir, { recursive: true });
|
|
71
115
|
}
|
|
72
|
-
|
|
116
|
+
throw err;
|
|
73
117
|
}
|
|
74
|
-
// write ready marker
|
|
75
|
-
fs.writeFileSync(readyMarker, new Date().toISOString());
|
|
76
|
-
console.log('tsgolint source ready');
|
|
77
118
|
return sourceDir;
|
|
78
119
|
}
|
|
79
120
|
export function cachedBinaryExists(contentHash) {
|
package/dist/codegen.d.ts
CHANGED
|
@@ -8,11 +8,17 @@ import type { RuleMetadata } from './discover.ts';
|
|
|
8
8
|
* tsgolint's own go.work (which does this) is ignored by the outer workspace.
|
|
9
9
|
* - go.mod should be minimal (no requires) — the workspace resolves everything. */
|
|
10
10
|
export declare function generateEditorGoFiles(lintcnDir: string): void;
|
|
11
|
-
/** Generate build workspace in cache dir for compiling the custom binary
|
|
11
|
+
/** Generate build workspace in cache dir for compiling the custom binary.
|
|
12
|
+
* Instead of hardcoding the built-in rule list, we copy tsgolint's actual
|
|
13
|
+
* main.go and inject custom rule imports + entries. This way the generated
|
|
14
|
+
* code always matches the pinned tsgolint version. */
|
|
12
15
|
export declare function generateBuildWorkspace({ buildDir, tsgolintDir, lintcnDir, rules, }: {
|
|
13
16
|
buildDir: string;
|
|
14
17
|
tsgolintDir: string;
|
|
15
18
|
lintcnDir: string;
|
|
16
19
|
rules: RuleMetadata[];
|
|
17
20
|
}): void;
|
|
21
|
+
/** Copy all supporting .go files from cmd/tsgolint/ into the wrapper dir.
|
|
22
|
+
* main.go is generated separately with custom rules injected. */
|
|
23
|
+
export declare function copyTsgolintCmdFiles(tsgolintDir: string, wrapperDir: string): void;
|
|
18
24
|
//# sourceMappingURL=codegen.d.ts.map
|
package/dist/codegen.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AA2BjD;;;;;;;oFAOoF;AACpF,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAiC7D;AAED;;;uDAGuD;AACvD,wBAAgB,sBAAsB,CAAC,EACrC,QAAQ,EACR,WAAW,EACX,SAAS,EACT,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,YAAY,EAAE,CAAA;CACtB,GAAG,IAAI,CAiDP;AA8DD;kEACkE;AAClE,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAQlF"}
|