@getmikk/core 1.3.2 → 1.5.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/package.json +1 -1
- package/src/contract/contract-generator.ts +87 -8
- package/src/contract/lock-compiler.ts +174 -8
- package/src/contract/lock-reader.ts +269 -3
- package/src/contract/schema.ts +31 -0
- package/src/graph/cluster-detector.ts +286 -18
- package/src/graph/graph-builder.ts +2 -0
- package/src/graph/types.ts +2 -0
- package/src/index.ts +2 -1
- package/src/parser/boundary-checker.ts +74 -2
- package/src/parser/types.ts +11 -0
- package/src/parser/typescript/ts-extractor.ts +146 -8
- package/src/parser/typescript/ts-parser.ts +32 -5
- package/src/utils/fs.ts +586 -4
- package/tests/fs.test.ts +186 -0
- package/tests/helpers.ts +6 -0
package/tests/fs.test.ts
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'bun:test'
|
|
2
|
+
import * as fs from 'node:fs/promises'
|
|
3
|
+
import * as path from 'node:path'
|
|
4
|
+
import * as os from 'node:os'
|
|
5
|
+
import { detectProjectLanguage, getDiscoveryPatterns, parseMikkIgnore, type ProjectLanguage } from '../src/utils/fs'
|
|
6
|
+
|
|
7
|
+
// ── detectProjectLanguage ───────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
describe('detectProjectLanguage', () => {
|
|
10
|
+
let tmpDir: string
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mikk-fs-test-'))
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
afterAll(async () => {
|
|
17
|
+
await fs.rm(tmpDir, { recursive: true, force: true })
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
async function withFile(name: string, fn: () => Promise<void>) {
|
|
21
|
+
const filePath = path.join(tmpDir, name)
|
|
22
|
+
await fs.writeFile(filePath, '', 'utf-8')
|
|
23
|
+
try {
|
|
24
|
+
await fn()
|
|
25
|
+
} finally {
|
|
26
|
+
await fs.unlink(filePath).catch(() => {})
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
it('detects TypeScript from tsconfig.json', async () => {
|
|
31
|
+
await withFile('tsconfig.json', async () => {
|
|
32
|
+
expect(await detectProjectLanguage(tmpDir)).toBe('typescript')
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('detects Rust from Cargo.toml', async () => {
|
|
37
|
+
await withFile('Cargo.toml', async () => {
|
|
38
|
+
expect(await detectProjectLanguage(tmpDir)).toBe('rust')
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('detects Go from go.mod', async () => {
|
|
43
|
+
await withFile('go.mod', async () => {
|
|
44
|
+
expect(await detectProjectLanguage(tmpDir)).toBe('go')
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('detects Python from pyproject.toml', async () => {
|
|
49
|
+
await withFile('pyproject.toml', async () => {
|
|
50
|
+
expect(await detectProjectLanguage(tmpDir)).toBe('python')
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('detects Python from requirements.txt', async () => {
|
|
55
|
+
await withFile('requirements.txt', async () => {
|
|
56
|
+
expect(await detectProjectLanguage(tmpDir)).toBe('python')
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('detects Ruby from Gemfile', async () => {
|
|
61
|
+
await withFile('Gemfile', async () => {
|
|
62
|
+
expect(await detectProjectLanguage(tmpDir)).toBe('ruby')
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('detects Java from pom.xml', async () => {
|
|
67
|
+
await withFile('pom.xml', async () => {
|
|
68
|
+
expect(await detectProjectLanguage(tmpDir)).toBe('java')
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('detects PHP from composer.json', async () => {
|
|
73
|
+
await withFile('composer.json', async () => {
|
|
74
|
+
expect(await detectProjectLanguage(tmpDir)).toBe('php')
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('detects C# from .csproj file', async () => {
|
|
79
|
+
await withFile('MyApp.csproj', async () => {
|
|
80
|
+
expect(await detectProjectLanguage(tmpDir)).toBe('csharp')
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('detects C# from .sln file', async () => {
|
|
85
|
+
await withFile('MyApp.sln', async () => {
|
|
86
|
+
expect(await detectProjectLanguage(tmpDir)).toBe('csharp')
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('detects JavaScript from package.json (no tsconfig)', async () => {
|
|
91
|
+
await withFile('package.json', async () => {
|
|
92
|
+
expect(await detectProjectLanguage(tmpDir)).toBe('javascript')
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('returns unknown for empty directory', async () => {
|
|
97
|
+
expect(await detectProjectLanguage(tmpDir)).toBe('unknown')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('prioritises TypeScript over JavaScript', async () => {
|
|
101
|
+
const tsconfig = path.join(tmpDir, 'tsconfig.json')
|
|
102
|
+
const pkg = path.join(tmpDir, 'package.json')
|
|
103
|
+
await fs.writeFile(tsconfig, '', 'utf-8')
|
|
104
|
+
await fs.writeFile(pkg, '', 'utf-8')
|
|
105
|
+
try {
|
|
106
|
+
expect(await detectProjectLanguage(tmpDir)).toBe('typescript')
|
|
107
|
+
} finally {
|
|
108
|
+
await fs.unlink(tsconfig).catch(() => {})
|
|
109
|
+
await fs.unlink(pkg).catch(() => {})
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// ── getDiscoveryPatterns ────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
describe('getDiscoveryPatterns', () => {
|
|
117
|
+
const languages: ProjectLanguage[] = [
|
|
118
|
+
'typescript', 'javascript', 'python', 'go', 'rust',
|
|
119
|
+
'java', 'ruby', 'php', 'csharp', 'unknown',
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
for (const lang of languages) {
|
|
123
|
+
it(`returns patterns and ignore for ${lang}`, () => {
|
|
124
|
+
const result = getDiscoveryPatterns(lang)
|
|
125
|
+
expect(result.patterns.length).toBeGreaterThan(0)
|
|
126
|
+
expect(result.ignore.length).toBeGreaterThan(0)
|
|
127
|
+
// All patterns should be glob strings
|
|
128
|
+
for (const p of result.patterns) {
|
|
129
|
+
expect(typeof p).toBe('string')
|
|
130
|
+
expect(p).toContain('*')
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
it('typescript includes JS files too', () => {
|
|
136
|
+
const { patterns } = getDiscoveryPatterns('typescript')
|
|
137
|
+
expect(patterns).toContain('**/*.ts')
|
|
138
|
+
expect(patterns).toContain('**/*.js')
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('python patterns include .py', () => {
|
|
142
|
+
const { patterns } = getDiscoveryPatterns('python')
|
|
143
|
+
expect(patterns).toContain('**/*.py')
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('all languages ignore .mikk and .git', () => {
|
|
147
|
+
for (const lang of languages) {
|
|
148
|
+
const { ignore } = getDiscoveryPatterns(lang)
|
|
149
|
+
expect(ignore).toContain('**/.mikk/**')
|
|
150
|
+
expect(ignore).toContain('**/.git/**')
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// ── parseMikkIgnore ─────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
describe('parseMikkIgnore', () => {
|
|
158
|
+
it('parses non-empty lines', () => {
|
|
159
|
+
const result = parseMikkIgnore('src/test/**\n\n# comment\nsrc/generated/**')
|
|
160
|
+
expect(result).toEqual(['src/test/**', 'src/generated/**'])
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('returns empty array for empty content', () => {
|
|
164
|
+
expect(parseMikkIgnore('')).toEqual([])
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('strips comments and blank lines', () => {
|
|
168
|
+
const result = parseMikkIgnore('# ignore test files\n\n \nfoo/**')
|
|
169
|
+
expect(result).toEqual(['foo/**'])
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('converts trailing-slash directories without path to **/ glob', () => {
|
|
173
|
+
const result = parseMikkIgnore('dist/\nnode_modules/')
|
|
174
|
+
expect(result).toEqual(['**/dist/**', '**/node_modules/**'])
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('converts trailing-slash directories with path to ** glob', () => {
|
|
178
|
+
const result = parseMikkIgnore('packages/*/tests/\napps/*/test/')
|
|
179
|
+
expect(result).toEqual(['packages/*/tests/**', 'apps/*/test/**'])
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('handles bare file patterns without slash', () => {
|
|
183
|
+
const result = parseMikkIgnore('*.d.ts\n*.log')
|
|
184
|
+
expect(result).toEqual(['**/*.d.ts', '**/*.log'])
|
|
185
|
+
})
|
|
186
|
+
})
|
package/tests/helpers.ts
CHANGED
|
@@ -14,8 +14,10 @@ export function mockParsedFile(
|
|
|
14
14
|
language: 'typescript',
|
|
15
15
|
functions,
|
|
16
16
|
classes: [],
|
|
17
|
+
generics: [],
|
|
17
18
|
imports,
|
|
18
19
|
exports: [],
|
|
20
|
+
routes: [],
|
|
19
21
|
hash: hashContent(filePath),
|
|
20
22
|
parsedAt: Date.now(),
|
|
21
23
|
}
|
|
@@ -40,6 +42,10 @@ export function mockFunction(
|
|
|
40
42
|
isAsync: false,
|
|
41
43
|
calls,
|
|
42
44
|
hash: hashContent(name),
|
|
45
|
+
purpose: '',
|
|
46
|
+
edgeCasesHandled: [],
|
|
47
|
+
errorHandling: [],
|
|
48
|
+
detailedLines: [],
|
|
43
49
|
}
|
|
44
50
|
}
|
|
45
51
|
|