lintcn 0.3.0 → 0.4.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 +12 -0
- package/dist/cache.d.ts +1 -1
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +15 -17
- package/dist/codegen.d.ts +4 -15
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +39 -84
- package/package.json +1 -1
- package/src/cache.ts +15 -18
- package/src/codegen.ts +36 -97
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 0.4.0
|
|
2
|
+
|
|
3
|
+
1. **Simpler rule imports** — rules now import from `pkg/rule`, `pkg/utils`, etc. instead of `internal/rule`. The `internal/` child-module-path hack is gone. Your `.go` files use clean import paths:
|
|
4
|
+
```go
|
|
5
|
+
import (
|
|
6
|
+
"github.com/typescript-eslint/tsgolint/pkg/rule"
|
|
7
|
+
"github.com/typescript-eslint/tsgolint/pkg/utils"
|
|
8
|
+
)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
2. **Simpler codegen** — uses a tsgolint fork with `pkg/runner.Run()`, eliminating all regex surgery on main.go. The generated binary entry point is a 15-line template instead of a patched copy of tsgolint's main.go.
|
|
12
|
+
|
|
1
13
|
## 0.3.0
|
|
2
14
|
|
|
3
15
|
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.
|
package/dist/cache.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const DEFAULT_TSGOLINT_VERSION = "
|
|
1
|
+
export declare const DEFAULT_TSGOLINT_VERSION = "a93604379da2631b70332a65bc47eb5ced689a3b";
|
|
2
2
|
export declare function getCacheDir(): string;
|
|
3
3
|
export declare function getTsgolintSourceDir(version: string): string;
|
|
4
4
|
export declare function getBinDir(): string;
|
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":"AAiBA,eAAO,MAAM,wBAAwB,6CAA6C,CAAA;AAQlF,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;AAqCD,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,6 +1,6 @@
|
|
|
1
1
|
// Manage cached tsgolint source and compiled binaries.
|
|
2
|
-
// Downloads tsgolint + typescript-go as tarballs from GitHub
|
|
3
|
-
// applies patches
|
|
2
|
+
// Downloads tsgolint fork + typescript-go as tarballs from GitHub,
|
|
3
|
+
// applies tsgolint's patches to typescript-go, and copies collections.
|
|
4
4
|
//
|
|
5
5
|
// Cache layout:
|
|
6
6
|
// ~/.cache/lintcn/tsgolint/<version>/ — extracted source (read-only)
|
|
@@ -10,15 +10,15 @@ import os from 'node:os';
|
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { pipeline } from 'node:stream/promises';
|
|
12
12
|
import { execAsync } from "./exec.js";
|
|
13
|
-
// Pinned tsgolint
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
//
|
|
13
|
+
// Pinned tsgolint fork commit — updated with each lintcn release.
|
|
14
|
+
// Uses remorses/tsgolint fork which exposes pkg/runner.Run() and
|
|
15
|
+
// moves internal/ packages to pkg/ for clean external imports.
|
|
16
|
+
export const DEFAULT_TSGOLINT_VERSION = 'a93604379da2631b70332a65bc47eb5ced689a3b';
|
|
17
|
+
// Pinned typescript-go base commit from microsoft/typescript-go (before patches).
|
|
18
|
+
// Patches from tsgolint/patches/ are applied on top during setup.
|
|
19
|
+
// This is the upstream commit the tsgolint submodule was forked from.
|
|
20
20
|
// Must be updated when DEFAULT_TSGOLINT_VERSION changes.
|
|
21
|
-
const TYPESCRIPT_GO_COMMIT = '
|
|
21
|
+
const TYPESCRIPT_GO_COMMIT = '1b7eabe122e1575a0df9c77eccdf4e063c623224';
|
|
22
22
|
export function getCacheDir() {
|
|
23
23
|
return path.join(os.homedir(), '.cache', 'lintcn');
|
|
24
24
|
}
|
|
@@ -43,7 +43,6 @@ async function downloadAndExtract(url, targetDir) {
|
|
|
43
43
|
throw new Error(`Failed to download ${url}: ${response.status} ${response.statusText}`);
|
|
44
44
|
}
|
|
45
45
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
46
|
-
// pipe through gunzip, then extract with tar (strip top-level directory)
|
|
47
46
|
const tmpTarGz = path.join(os.tmpdir(), `lintcn-${Date.now()}.tar.gz`);
|
|
48
47
|
const fileStream = fs.createWriteStream(tmpTarGz);
|
|
49
48
|
// @ts-ignore ReadableStream vs NodeJS.ReadableStream mismatch
|
|
@@ -59,7 +58,6 @@ async function applyPatches(patchesDir, targetDir) {
|
|
|
59
58
|
.sort();
|
|
60
59
|
for (const patchFile of patches) {
|
|
61
60
|
const patchPath = path.join(patchesDir, patchFile);
|
|
62
|
-
// --batch silences interactive prompts, -f forces application
|
|
63
61
|
await execAsync('patch', ['-p1', '--batch', '-i', patchPath], { cwd: targetDir });
|
|
64
62
|
}
|
|
65
63
|
return patches.length;
|
|
@@ -75,16 +73,16 @@ export async function ensureTsgolintSource(version) {
|
|
|
75
73
|
fs.rmSync(sourceDir, { recursive: true });
|
|
76
74
|
}
|
|
77
75
|
try {
|
|
78
|
-
// download tsgolint
|
|
79
|
-
console.log(`Downloading tsgolint@${version}...`);
|
|
80
|
-
const tsgolintUrl = `https://github.com/
|
|
76
|
+
// download tsgolint fork tarball
|
|
77
|
+
console.log(`Downloading tsgolint@${version.slice(0, 8)}...`);
|
|
78
|
+
const tsgolintUrl = `https://github.com/remorses/tsgolint/archive/${version}.tar.gz`;
|
|
81
79
|
await downloadAndExtract(tsgolintUrl, sourceDir);
|
|
82
|
-
// download typescript-go
|
|
80
|
+
// download typescript-go from microsoft (base commit before patches)
|
|
83
81
|
const tsGoDir = path.join(sourceDir, 'typescript-go');
|
|
84
82
|
console.log('Downloading typescript-go...');
|
|
85
83
|
const tsGoUrl = `https://github.com/microsoft/typescript-go/archive/${TYPESCRIPT_GO_COMMIT}.tar.gz`;
|
|
86
84
|
await downloadAndExtract(tsGoUrl, tsGoDir);
|
|
87
|
-
// apply patches to typescript-go
|
|
85
|
+
// apply tsgolint's patches to typescript-go
|
|
88
86
|
const patchesDir = path.join(sourceDir, 'patches');
|
|
89
87
|
if (fs.existsSync(patchesDir)) {
|
|
90
88
|
const count = await applyPatches(patchesDir, tsGoDir);
|
package/dist/codegen.d.ts
CHANGED
|
@@ -1,24 +1,13 @@
|
|
|
1
1
|
import type { RuleMetadata } from './discover.ts';
|
|
2
|
-
/** Generate .lintcn/go.work and .lintcn/go.mod for editor/gopls support.
|
|
3
|
-
*
|
|
4
|
-
* Key learnings from testing:
|
|
5
|
-
* - Module name MUST be a child path of github.com/typescript-eslint/tsgolint
|
|
6
|
-
* so Go allows importing internal/ packages across the module boundary.
|
|
7
|
-
* - go.work must `use` both .tsgolint AND .tsgolint/typescript-go since
|
|
8
|
-
* tsgolint's own go.work (which does this) is ignored by the outer workspace.
|
|
9
|
-
* - go.mod should be minimal (no requires) — the workspace resolves everything. */
|
|
2
|
+
/** Generate .lintcn/go.work and .lintcn/go.mod for editor/gopls support. */
|
|
10
3
|
export declare function generateEditorGoFiles(lintcnDir: string): void;
|
|
11
|
-
/** Generate build workspace
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* code always matches the pinned tsgolint version. */
|
|
4
|
+
/** Generate build workspace for compiling the custom binary.
|
|
5
|
+
* With pkg/runner.Run(), the generated main.go is a static template —
|
|
6
|
+
* no regex surgery or file copying needed. */
|
|
15
7
|
export declare function generateBuildWorkspace({ buildDir, tsgolintDir, lintcnDir, rules, }: {
|
|
16
8
|
buildDir: string;
|
|
17
9
|
tsgolintDir: string;
|
|
18
10
|
lintcnDir: string;
|
|
19
11
|
rules: RuleMetadata[];
|
|
20
12
|
}): 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;
|
|
24
13
|
//# 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":"
|
|
1
|
+
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AA2BjD,4EAA4E;AAC5E,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAkC7D;AAED;;+CAE+C;AAC/C,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,CA2CP"}
|
package/dist/codegen.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
// Generate Go workspace files for building a custom tsgolint binary.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// build/go.work — build workspace in cache dir
|
|
6
|
-
// build/wrapper/go.mod — wrapper module
|
|
7
|
-
// build/wrapper/main.go — tsgolint main.go with custom rules appended
|
|
2
|
+
// With the fork (remorses/tsgolint) exposing pkg/runner.Run(), codegen is
|
|
3
|
+
// minimal: a 10-line main.go template + go.work with shim replaces.
|
|
4
|
+
// No regex surgery, no file copying, no fragile string manipulation.
|
|
8
5
|
import fs from 'node:fs';
|
|
9
6
|
import path from 'node:path';
|
|
10
|
-
//
|
|
11
|
-
// These redirect
|
|
7
|
+
// Shim modules that need replace directives in go.work.
|
|
8
|
+
// These redirect module paths to local directories inside the tsgolint source.
|
|
12
9
|
const SHIM_MODULES = [
|
|
13
10
|
'ast',
|
|
14
11
|
'bundled',
|
|
@@ -30,14 +27,7 @@ function generateReplaceDirectives(tsgolintRelPath) {
|
|
|
30
27
|
return `\tgithub.com/microsoft/typescript-go/shim/${mod} => ${tsgolintRelPath}/shim/${mod}`;
|
|
31
28
|
}).join('\n');
|
|
32
29
|
}
|
|
33
|
-
/** Generate .lintcn/go.work and .lintcn/go.mod for editor/gopls support.
|
|
34
|
-
*
|
|
35
|
-
* Key learnings from testing:
|
|
36
|
-
* - Module name MUST be a child path of github.com/typescript-eslint/tsgolint
|
|
37
|
-
* so Go allows importing internal/ packages across the module boundary.
|
|
38
|
-
* - go.work must `use` both .tsgolint AND .tsgolint/typescript-go since
|
|
39
|
-
* tsgolint's own go.work (which does this) is ignored by the outer workspace.
|
|
40
|
-
* - go.mod should be minimal (no requires) — the workspace resolves everything. */
|
|
30
|
+
/** Generate .lintcn/go.work and .lintcn/go.mod for editor/gopls support. */
|
|
41
31
|
export function generateEditorGoFiles(lintcnDir) {
|
|
42
32
|
const goWork = `go 1.26
|
|
43
33
|
|
|
@@ -51,7 +41,8 @@ replace (
|
|
|
51
41
|
${generateReplaceDirectives('./.tsgolint')}
|
|
52
42
|
)
|
|
53
43
|
`;
|
|
54
|
-
|
|
44
|
+
// No child-path hack needed — pkg/ is public, any module name works
|
|
45
|
+
const goMod = `module lintcn-rules
|
|
55
46
|
|
|
56
47
|
go 1.26
|
|
57
48
|
`;
|
|
@@ -68,10 +59,9 @@ go.sum
|
|
|
68
59
|
fs.writeFileSync(gitignorePath, gitignore);
|
|
69
60
|
}
|
|
70
61
|
}
|
|
71
|
-
/** Generate build workspace
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* code always matches the pinned tsgolint version. */
|
|
62
|
+
/** Generate build workspace for compiling the custom binary.
|
|
63
|
+
* With pkg/runner.Run(), the generated main.go is a static template —
|
|
64
|
+
* no regex surgery or file copying needed. */
|
|
75
65
|
export function generateBuildWorkspace({ buildDir, tsgolintDir, lintcnDir, rules, }) {
|
|
76
66
|
fs.mkdirSync(path.join(buildDir, 'wrapper'), { recursive: true });
|
|
77
67
|
// symlink tsgolint source
|
|
@@ -86,7 +76,7 @@ export function generateBuildWorkspace({ buildDir, tsgolintDir, lintcnDir, rules
|
|
|
86
76
|
fs.rmSync(rulesLink, { recursive: true });
|
|
87
77
|
}
|
|
88
78
|
fs.symlinkSync(path.resolve(lintcnDir), rulesLink);
|
|
89
|
-
// go.work
|
|
79
|
+
// go.work
|
|
90
80
|
const goWork = `go 1.26
|
|
91
81
|
|
|
92
82
|
use (
|
|
@@ -101,73 +91,38 @@ ${generateReplaceDirectives('./tsgolint')}
|
|
|
101
91
|
)
|
|
102
92
|
`;
|
|
103
93
|
fs.writeFileSync(path.join(buildDir, 'go.work'), goWork);
|
|
104
|
-
// wrapper/go.mod —
|
|
105
|
-
|
|
106
|
-
// Adding explicit requires with v0.0.0 triggers Go proxy lookups that fail.
|
|
107
|
-
const wrapperGoMod = `module github.com/typescript-eslint/tsgolint/lintcn-wrapper
|
|
94
|
+
// wrapper/go.mod — simple module name, no child-path hack needed
|
|
95
|
+
const wrapperGoMod = `module lintcn-wrapper
|
|
108
96
|
|
|
109
97
|
go 1.26
|
|
110
98
|
`;
|
|
111
99
|
fs.writeFileSync(path.join(buildDir, 'wrapper', 'go.mod'), wrapperGoMod);
|
|
112
|
-
//
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
// wrapper/main.go — copy from tsgolint and inject custom rules
|
|
116
|
-
const mainGo = generateMainGoFromSource(tsgolintDir, rules);
|
|
117
|
-
fs.writeFileSync(path.join(wrapperDir, 'main.go'), mainGo);
|
|
100
|
+
// wrapper/main.go — simple template, no regex or string surgery
|
|
101
|
+
const mainGo = generateMainGo(rules);
|
|
102
|
+
fs.writeFileSync(path.join(buildDir, 'wrapper', 'main.go'), mainGo);
|
|
118
103
|
}
|
|
119
|
-
/**
|
|
120
|
-
*
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
function generateMainGoFromSource(tsgolintDir, customRules) {
|
|
125
|
-
const mainGoPath = path.join(tsgolintDir, 'cmd', 'tsgolint', 'main.go');
|
|
126
|
-
const original = fs.readFileSync(mainGoPath, 'utf-8');
|
|
127
|
-
// 1. Remove built-in rule import lines, add lintcn import
|
|
128
|
-
const lines = original.split('\n');
|
|
129
|
-
const filtered = lines.filter((line) => {
|
|
130
|
-
return !line.includes('/internal/rules/');
|
|
131
|
-
});
|
|
132
|
-
// Insert lintcn import before the first shim import (microsoft/typescript-go)
|
|
133
|
-
const lintcnImport = `\tlintcn "github.com/typescript-eslint/tsgolint/lintcn-rules"`;
|
|
134
|
-
let shimImportIndex = -1;
|
|
135
|
-
for (let i = 0; i < filtered.length; i++) {
|
|
136
|
-
if (filtered[i].includes('microsoft/typescript-go/shim')) {
|
|
137
|
-
shimImportIndex = i;
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
if (shimImportIndex === -1) {
|
|
142
|
-
throw new Error('Failed to find shim import in tsgolint main.go. The source layout may have changed.');
|
|
143
|
-
}
|
|
144
|
-
if (customRules.length > 0) {
|
|
145
|
-
filtered.splice(shimImportIndex, 0, lintcnImport, '');
|
|
146
|
-
}
|
|
147
|
-
let mainGo = filtered.join('\n');
|
|
148
|
-
// 2. Replace allRules body with only custom entries
|
|
149
|
-
const customEntries = customRules.map((r) => {
|
|
150
|
-
return `\tlintcn.${r.varName},`;
|
|
104
|
+
/** Generate a minimal main.go that imports user rules and calls runner.Run().
|
|
105
|
+
* This is a static template — no copying or patching of tsgolint source. */
|
|
106
|
+
function generateMainGo(rules) {
|
|
107
|
+
const ruleEntries = rules.map((r) => {
|
|
108
|
+
return `\t\tlintcn.${r.varName},`;
|
|
151
109
|
}).join('\n');
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
110
|
+
return `// Code generated by lintcn. DO NOT EDIT.
|
|
111
|
+
package main
|
|
112
|
+
|
|
113
|
+
import (
|
|
114
|
+
\t"os"
|
|
115
|
+
|
|
116
|
+
\t"github.com/typescript-eslint/tsgolint/pkg/rule"
|
|
117
|
+
\t"github.com/typescript-eslint/tsgolint/pkg/runner"
|
|
118
|
+
\tlintcn "lintcn-rules"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
func main() {
|
|
122
|
+
\trules := []rule.Rule{
|
|
123
|
+
${ruleEntries}
|
|
124
|
+
\t}
|
|
125
|
+
\tos.Exit(runner.Run(rules, os.Args[1:]))
|
|
162
126
|
}
|
|
163
|
-
|
|
164
|
-
* main.go is generated separately with custom rules injected. */
|
|
165
|
-
export function copyTsgolintCmdFiles(tsgolintDir, wrapperDir) {
|
|
166
|
-
const cmdDir = path.join(tsgolintDir, 'cmd', 'tsgolint');
|
|
167
|
-
const files = fs.readdirSync(cmdDir).filter((f) => {
|
|
168
|
-
return f.endsWith('.go') && f !== 'main.go' && !f.endsWith('_test.go');
|
|
169
|
-
});
|
|
170
|
-
for (const file of files) {
|
|
171
|
-
fs.copyFileSync(path.join(cmdDir, file), path.join(wrapperDir, file));
|
|
172
|
-
}
|
|
127
|
+
`;
|
|
173
128
|
}
|
package/package.json
CHANGED
package/src/cache.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Manage cached tsgolint source and compiled binaries.
|
|
2
|
-
// Downloads tsgolint + typescript-go as tarballs from GitHub
|
|
3
|
-
// applies patches
|
|
2
|
+
// Downloads tsgolint fork + typescript-go as tarballs from GitHub,
|
|
3
|
+
// applies tsgolint's patches to typescript-go, and copies collections.
|
|
4
4
|
//
|
|
5
5
|
// Cache layout:
|
|
6
6
|
// ~/.cache/lintcn/tsgolint/<version>/ — extracted source (read-only)
|
|
@@ -10,19 +10,18 @@ import fs from 'node:fs'
|
|
|
10
10
|
import os from 'node:os'
|
|
11
11
|
import path from 'node:path'
|
|
12
12
|
import { pipeline } from 'node:stream/promises'
|
|
13
|
-
import { createGunzip } from 'node:zlib'
|
|
14
13
|
import { execAsync } from './exec.ts'
|
|
15
14
|
|
|
16
|
-
// Pinned tsgolint
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
export const DEFAULT_TSGOLINT_VERSION = 'v0.9.2'
|
|
15
|
+
// Pinned tsgolint fork commit — updated with each lintcn release.
|
|
16
|
+
// Uses remorses/tsgolint fork which exposes pkg/runner.Run() and
|
|
17
|
+
// moves internal/ packages to pkg/ for clean external imports.
|
|
18
|
+
export const DEFAULT_TSGOLINT_VERSION = 'a93604379da2631b70332a65bc47eb5ced689a3b'
|
|
21
19
|
|
|
22
|
-
// Pinned typescript-go commit
|
|
23
|
-
//
|
|
20
|
+
// Pinned typescript-go base commit from microsoft/typescript-go (before patches).
|
|
21
|
+
// Patches from tsgolint/patches/ are applied on top during setup.
|
|
22
|
+
// This is the upstream commit the tsgolint submodule was forked from.
|
|
24
23
|
// Must be updated when DEFAULT_TSGOLINT_VERSION changes.
|
|
25
|
-
const TYPESCRIPT_GO_COMMIT = '
|
|
24
|
+
const TYPESCRIPT_GO_COMMIT = '1b7eabe122e1575a0df9c77eccdf4e063c623224'
|
|
26
25
|
|
|
27
26
|
export function getCacheDir(): string {
|
|
28
27
|
return path.join(os.homedir(), '.cache', 'lintcn')
|
|
@@ -55,7 +54,6 @@ async function downloadAndExtract(url: string, targetDir: string): Promise<void>
|
|
|
55
54
|
|
|
56
55
|
fs.mkdirSync(targetDir, { recursive: true })
|
|
57
56
|
|
|
58
|
-
// pipe through gunzip, then extract with tar (strip top-level directory)
|
|
59
57
|
const tmpTarGz = path.join(os.tmpdir(), `lintcn-${Date.now()}.tar.gz`)
|
|
60
58
|
const fileStream = fs.createWriteStream(tmpTarGz)
|
|
61
59
|
// @ts-ignore ReadableStream vs NodeJS.ReadableStream mismatch
|
|
@@ -74,7 +72,6 @@ async function applyPatches(patchesDir: string, targetDir: string): Promise<numb
|
|
|
74
72
|
|
|
75
73
|
for (const patchFile of patches) {
|
|
76
74
|
const patchPath = path.join(patchesDir, patchFile)
|
|
77
|
-
// --batch silences interactive prompts, -f forces application
|
|
78
75
|
await execAsync('patch', ['-p1', '--batch', '-i', patchPath], { cwd: targetDir })
|
|
79
76
|
}
|
|
80
77
|
|
|
@@ -95,18 +92,18 @@ export async function ensureTsgolintSource(version: string): Promise<string> {
|
|
|
95
92
|
}
|
|
96
93
|
|
|
97
94
|
try {
|
|
98
|
-
// download tsgolint
|
|
99
|
-
console.log(`Downloading tsgolint@${version}...`)
|
|
100
|
-
const tsgolintUrl = `https://github.com/
|
|
95
|
+
// download tsgolint fork tarball
|
|
96
|
+
console.log(`Downloading tsgolint@${version.slice(0, 8)}...`)
|
|
97
|
+
const tsgolintUrl = `https://github.com/remorses/tsgolint/archive/${version}.tar.gz`
|
|
101
98
|
await downloadAndExtract(tsgolintUrl, sourceDir)
|
|
102
99
|
|
|
103
|
-
// download typescript-go
|
|
100
|
+
// download typescript-go from microsoft (base commit before patches)
|
|
104
101
|
const tsGoDir = path.join(sourceDir, 'typescript-go')
|
|
105
102
|
console.log('Downloading typescript-go...')
|
|
106
103
|
const tsGoUrl = `https://github.com/microsoft/typescript-go/archive/${TYPESCRIPT_GO_COMMIT}.tar.gz`
|
|
107
104
|
await downloadAndExtract(tsGoUrl, tsGoDir)
|
|
108
105
|
|
|
109
|
-
// apply patches to typescript-go
|
|
106
|
+
// apply tsgolint's patches to typescript-go
|
|
110
107
|
const patchesDir = path.join(sourceDir, 'patches')
|
|
111
108
|
if (fs.existsSync(patchesDir)) {
|
|
112
109
|
const count = await applyPatches(patchesDir, tsGoDir)
|
package/src/codegen.ts
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
// Generate Go workspace files for building a custom tsgolint binary.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// build/go.work — build workspace in cache dir
|
|
6
|
-
// build/wrapper/go.mod — wrapper module
|
|
7
|
-
// build/wrapper/main.go — tsgolint main.go with custom rules appended
|
|
2
|
+
// With the fork (remorses/tsgolint) exposing pkg/runner.Run(), codegen is
|
|
3
|
+
// minimal: a 10-line main.go template + go.work with shim replaces.
|
|
4
|
+
// No regex surgery, no file copying, no fragile string manipulation.
|
|
8
5
|
|
|
9
6
|
import fs from 'node:fs'
|
|
10
7
|
import path from 'node:path'
|
|
11
8
|
import type { RuleMetadata } from './discover.ts'
|
|
12
9
|
|
|
13
|
-
//
|
|
14
|
-
// These redirect
|
|
10
|
+
// Shim modules that need replace directives in go.work.
|
|
11
|
+
// These redirect module paths to local directories inside the tsgolint source.
|
|
15
12
|
const SHIM_MODULES = [
|
|
16
13
|
'ast',
|
|
17
14
|
'bundled',
|
|
@@ -35,14 +32,7 @@ function generateReplaceDirectives(tsgolintRelPath: string): string {
|
|
|
35
32
|
}).join('\n')
|
|
36
33
|
}
|
|
37
34
|
|
|
38
|
-
/** Generate .lintcn/go.work and .lintcn/go.mod for editor/gopls support.
|
|
39
|
-
*
|
|
40
|
-
* Key learnings from testing:
|
|
41
|
-
* - Module name MUST be a child path of github.com/typescript-eslint/tsgolint
|
|
42
|
-
* so Go allows importing internal/ packages across the module boundary.
|
|
43
|
-
* - go.work must `use` both .tsgolint AND .tsgolint/typescript-go since
|
|
44
|
-
* tsgolint's own go.work (which does this) is ignored by the outer workspace.
|
|
45
|
-
* - go.mod should be minimal (no requires) — the workspace resolves everything. */
|
|
35
|
+
/** Generate .lintcn/go.work and .lintcn/go.mod for editor/gopls support. */
|
|
46
36
|
export function generateEditorGoFiles(lintcnDir: string): void {
|
|
47
37
|
const goWork = `go 1.26
|
|
48
38
|
|
|
@@ -57,7 +47,8 @@ ${generateReplaceDirectives('./.tsgolint')}
|
|
|
57
47
|
)
|
|
58
48
|
`
|
|
59
49
|
|
|
60
|
-
|
|
50
|
+
// No child-path hack needed — pkg/ is public, any module name works
|
|
51
|
+
const goMod = `module lintcn-rules
|
|
61
52
|
|
|
62
53
|
go 1.26
|
|
63
54
|
`
|
|
@@ -78,10 +69,9 @@ go.sum
|
|
|
78
69
|
}
|
|
79
70
|
}
|
|
80
71
|
|
|
81
|
-
/** Generate build workspace
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* code always matches the pinned tsgolint version. */
|
|
72
|
+
/** Generate build workspace for compiling the custom binary.
|
|
73
|
+
* With pkg/runner.Run(), the generated main.go is a static template —
|
|
74
|
+
* no regex surgery or file copying needed. */
|
|
85
75
|
export function generateBuildWorkspace({
|
|
86
76
|
buildDir,
|
|
87
77
|
tsgolintDir,
|
|
@@ -109,7 +99,7 @@ export function generateBuildWorkspace({
|
|
|
109
99
|
}
|
|
110
100
|
fs.symlinkSync(path.resolve(lintcnDir), rulesLink)
|
|
111
101
|
|
|
112
|
-
// go.work
|
|
102
|
+
// go.work
|
|
113
103
|
const goWork = `go 1.26
|
|
114
104
|
|
|
115
105
|
use (
|
|
@@ -125,92 +115,41 @@ ${generateReplaceDirectives('./tsgolint')}
|
|
|
125
115
|
`
|
|
126
116
|
fs.writeFileSync(path.join(buildDir, 'go.work'), goWork)
|
|
127
117
|
|
|
128
|
-
// wrapper/go.mod —
|
|
129
|
-
|
|
130
|
-
// Adding explicit requires with v0.0.0 triggers Go proxy lookups that fail.
|
|
131
|
-
const wrapperGoMod = `module github.com/typescript-eslint/tsgolint/lintcn-wrapper
|
|
118
|
+
// wrapper/go.mod — simple module name, no child-path hack needed
|
|
119
|
+
const wrapperGoMod = `module lintcn-wrapper
|
|
132
120
|
|
|
133
121
|
go 1.26
|
|
134
122
|
`
|
|
135
123
|
fs.writeFileSync(path.join(buildDir, 'wrapper', 'go.mod'), wrapperGoMod)
|
|
136
124
|
|
|
137
|
-
//
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// wrapper/main.go — copy from tsgolint and inject custom rules
|
|
142
|
-
const mainGo = generateMainGoFromSource(tsgolintDir, rules)
|
|
143
|
-
fs.writeFileSync(path.join(wrapperDir, 'main.go'), mainGo)
|
|
125
|
+
// wrapper/main.go — simple template, no regex or string surgery
|
|
126
|
+
const mainGo = generateMainGo(rules)
|
|
127
|
+
fs.writeFileSync(path.join(buildDir, 'wrapper', 'main.go'), mainGo)
|
|
144
128
|
}
|
|
145
129
|
|
|
146
|
-
/**
|
|
147
|
-
*
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
function generateMainGoFromSource(tsgolintDir: string, customRules: RuleMetadata[]): string {
|
|
152
|
-
const mainGoPath = path.join(tsgolintDir, 'cmd', 'tsgolint', 'main.go')
|
|
153
|
-
const original = fs.readFileSync(mainGoPath, 'utf-8')
|
|
154
|
-
|
|
155
|
-
// 1. Remove built-in rule import lines, add lintcn import
|
|
156
|
-
const lines = original.split('\n')
|
|
157
|
-
const filtered = lines.filter((line) => {
|
|
158
|
-
return !line.includes('/internal/rules/')
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
// Insert lintcn import before the first shim import (microsoft/typescript-go)
|
|
162
|
-
const lintcnImport = `\tlintcn "github.com/typescript-eslint/tsgolint/lintcn-rules"`
|
|
163
|
-
let shimImportIndex = -1
|
|
164
|
-
for (let i = 0; i < filtered.length; i++) {
|
|
165
|
-
if (filtered[i].includes('microsoft/typescript-go/shim')) {
|
|
166
|
-
shimImportIndex = i
|
|
167
|
-
break
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
if (shimImportIndex === -1) {
|
|
171
|
-
throw new Error(
|
|
172
|
-
'Failed to find shim import in tsgolint main.go. The source layout may have changed.',
|
|
173
|
-
)
|
|
174
|
-
}
|
|
175
|
-
if (customRules.length > 0) {
|
|
176
|
-
filtered.splice(shimImportIndex, 0, lintcnImport, '')
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
let mainGo = filtered.join('\n')
|
|
180
|
-
|
|
181
|
-
// 2. Replace allRules body with only custom entries
|
|
182
|
-
const customEntries = customRules.map((r) => {
|
|
183
|
-
return `\tlintcn.${r.varName},`
|
|
130
|
+
/** Generate a minimal main.go that imports user rules and calls runner.Run().
|
|
131
|
+
* This is a static template — no copying or patching of tsgolint source. */
|
|
132
|
+
function generateMainGo(rules: RuleMetadata[]): string {
|
|
133
|
+
const ruleEntries = rules.map((r) => {
|
|
134
|
+
return `\t\tlintcn.${r.varName},`
|
|
184
135
|
}).join('\n')
|
|
185
136
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
throw new Error(
|
|
189
|
-
'Failed to find allRules slice in tsgolint main.go. The source layout may have changed.',
|
|
190
|
-
)
|
|
191
|
-
}
|
|
137
|
+
return `// Code generated by lintcn. DO NOT EDIT.
|
|
138
|
+
package main
|
|
192
139
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
`var allRules = []rule.Rule{\n${customEntries}\n}`,
|
|
196
|
-
)
|
|
140
|
+
import (
|
|
141
|
+
\t"os"
|
|
197
142
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
143
|
+
\t"github.com/typescript-eslint/tsgolint/pkg/rule"
|
|
144
|
+
\t"github.com/typescript-eslint/tsgolint/pkg/runner"
|
|
145
|
+
\tlintcn "lintcn-rules"
|
|
146
|
+
)
|
|
202
147
|
|
|
203
|
-
|
|
148
|
+
func main() {
|
|
149
|
+
\trules := []rule.Rule{
|
|
150
|
+
${ruleEntries}
|
|
151
|
+
\t}
|
|
152
|
+
\tos.Exit(runner.Run(rules, os.Args[1:]))
|
|
204
153
|
}
|
|
205
|
-
|
|
206
|
-
/** Copy all supporting .go files from cmd/tsgolint/ into the wrapper dir.
|
|
207
|
-
* main.go is generated separately with custom rules injected. */
|
|
208
|
-
export function copyTsgolintCmdFiles(tsgolintDir: string, wrapperDir: string): void {
|
|
209
|
-
const cmdDir = path.join(tsgolintDir, 'cmd', 'tsgolint')
|
|
210
|
-
const files = fs.readdirSync(cmdDir).filter((f) => {
|
|
211
|
-
return f.endsWith('.go') && f !== 'main.go' && !f.endsWith('_test.go')
|
|
212
|
-
})
|
|
213
|
-
for (const file of files) {
|
|
214
|
-
fs.copyFileSync(path.join(cmdDir, file), path.join(wrapperDir, file))
|
|
215
|
-
}
|
|
154
|
+
`
|
|
216
155
|
}
|