kea-typegen 3.7.0 → 3.8.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 +9 -0
- package/dist/package.json +3 -1
- package/dist/src/__tests__/inline.d.ts +1 -0
- package/dist/src/__tests__/inline.js +224 -0
- package/dist/src/__tests__/inline.js.map +1 -0
- package/dist/src/__tests__/watch.js +178 -0
- package/dist/src/__tests__/watch.js.map +1 -1
- package/dist/src/cli/typegen.js +4 -0
- package/dist/src/cli/typegen.js.map +1 -1
- package/dist/src/print/print.d.ts +6 -0
- package/dist/src/print/print.js +59 -42
- package/dist/src/print/print.js.map +1 -1
- package/dist/src/typegen.js +38 -2
- package/dist/src/typegen.js.map +1 -1
- package/dist/src/types.d.ts +1 -0
- package/dist/src/utils.d.ts +4 -0
- package/dist/src/utils.js +55 -0
- package/dist/src/utils.js.map +1 -1
- package/dist/src/visit/visit.js +6 -0
- package/dist/src/visit/visit.js.map +1 -1
- package/dist/src/watch.d.ts +22 -0
- package/dist/src/watch.js +183 -0
- package/dist/src/watch.js.map +1 -0
- package/dist/src/write/writeInlineLogicTypes.d.ts +8 -0
- package/dist/src/write/writeInlineLogicTypes.js +236 -0
- package/dist/src/write/writeInlineLogicTypes.js.map +1 -0
- package/dist/src/write/writeTypeImports.d.ts +8 -0
- package/dist/src/write/writeTypeImports.js +3 -0
- package/dist/src/write/writeTypeImports.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -1
- package/samples/manualTypedLogic.ts +20 -0
- package/samples-inline/counterLogic.ts +36 -0
- package/samples-inline/manualLogic.ts +20 -0
- package/samples-inline/profileLogic.ts +30 -0
- package/samples-inline/searchLogic.ts +30 -0
- package/samples-inline/tsconfig.json +17 -0
- package/samples-inline/types.ts +5 -0
- package/src/__tests__/inline.ts +266 -0
- package/src/__tests__/watch.ts +259 -32
- package/src/cli/typegen.ts +4 -0
- package/src/print/print.ts +102 -57
- package/src/test-support/watch-mode-smoke.js +39 -4
- package/src/test-support/write-mode-smoke.js +6 -1
- package/src/typegen.ts +53 -2
- package/src/types.ts +2 -0
- package/src/utils.ts +81 -0
- package/src/visit/visit.ts +14 -0
- package/src/watch.ts +261 -0
- package/src/write/writeInlineLogicTypes.ts +305 -0
- package/src/write/writeTypeImports.ts +4 -4
- package/tsconfig.json +1 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import * as fs from 'fs'
|
|
2
|
+
import * as os from 'os'
|
|
3
|
+
import * as path from 'path'
|
|
4
|
+
import * as ts from 'typescript'
|
|
5
|
+
import { AppOptions } from '../types'
|
|
6
|
+
import { programFromSource } from '../utils'
|
|
7
|
+
import { visitProgram } from '../visit/visit'
|
|
8
|
+
import { printToFiles } from '../print/print'
|
|
9
|
+
|
|
10
|
+
describe('manually typed logics', () => {
|
|
11
|
+
test('skips logics typed with MakeLogicType', () => {
|
|
12
|
+
const logicSource = `
|
|
13
|
+
import { kea, MakeLogicType } from 'kea'
|
|
14
|
+
|
|
15
|
+
interface Values { name: string }
|
|
16
|
+
interface Actions { updateName: (name: string) => { name: string } }
|
|
17
|
+
|
|
18
|
+
const logic = kea<MakeLogicType<Values, Actions>>({
|
|
19
|
+
actions: () => ({
|
|
20
|
+
updateName: (name: string) => ({ name }),
|
|
21
|
+
}),
|
|
22
|
+
})
|
|
23
|
+
`
|
|
24
|
+
expect(visitProgram(programFromSource(logicSource)).length).toBe(0)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('skips logics typed with a custom local type, even when named like a generated one', () => {
|
|
28
|
+
const logicSource = `
|
|
29
|
+
import { kea, MakeLogicType } from 'kea'
|
|
30
|
+
|
|
31
|
+
export type logicType = MakeLogicType<{ name: string }, { updateName: (name: string) => { name: string } }>
|
|
32
|
+
|
|
33
|
+
const logic = kea<logicType>({
|
|
34
|
+
actions: () => ({
|
|
35
|
+
updateName: (name: string) => ({ name }),
|
|
36
|
+
}),
|
|
37
|
+
})
|
|
38
|
+
`
|
|
39
|
+
expect(visitProgram(programFromSource(logicSource)).length).toBe(0)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('does not skip local logic types carrying the kea-typegen marker', () => {
|
|
43
|
+
const logicSource = `
|
|
44
|
+
import { kea, MakeLogicType } from 'kea'
|
|
45
|
+
|
|
46
|
+
// Generated by kea-typegen. DO NOT EDIT THIS BLOCK MANUALLY.
|
|
47
|
+
export type logicType = MakeLogicType<{ name: string }, { updateName: (name: string) => { name: string } }>
|
|
48
|
+
|
|
49
|
+
const logic = kea<logicType>({
|
|
50
|
+
actions: () => ({
|
|
51
|
+
updateName: (name: string) => ({ name }),
|
|
52
|
+
}),
|
|
53
|
+
})
|
|
54
|
+
`
|
|
55
|
+
const parsedLogics = visitProgram(programFromSource(logicSource))
|
|
56
|
+
expect(parsedLogics.length).toBe(1)
|
|
57
|
+
expect(parsedLogics[0].actions.map((a) => a.name)).toEqual(['updateName'])
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('does not skip generated blocks where the marker sits on the values interface', () => {
|
|
61
|
+
const logicSource = `
|
|
62
|
+
import { kea, MakeLogicType } from 'kea'
|
|
63
|
+
|
|
64
|
+
// Generated by kea-typegen. DO NOT EDIT THIS BLOCK MANUALLY.
|
|
65
|
+
export interface logicValues { name: string }
|
|
66
|
+
|
|
67
|
+
export interface logicActions { updateName: (name: string) => { name: string } }
|
|
68
|
+
|
|
69
|
+
export type logicType = MakeLogicType<logicValues, logicActions>
|
|
70
|
+
|
|
71
|
+
const logic = kea<logicType>({
|
|
72
|
+
actions: () => ({
|
|
73
|
+
updateName: (name: string) => ({ name }),
|
|
74
|
+
}),
|
|
75
|
+
})
|
|
76
|
+
`
|
|
77
|
+
const parsedLogics = visitProgram(programFromSource(logicSource))
|
|
78
|
+
expect(parsedLogics.length).toBe(1)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('does not skip logics with a generated type that does not resolve yet', () => {
|
|
82
|
+
const logicSource = `
|
|
83
|
+
import { kea } from 'kea'
|
|
84
|
+
|
|
85
|
+
const logic = kea<logicType>({
|
|
86
|
+
actions: () => ({
|
|
87
|
+
updateName: (name: string) => ({ name }),
|
|
88
|
+
}),
|
|
89
|
+
})
|
|
90
|
+
`
|
|
91
|
+
expect(visitProgram(programFromSource(logicSource)).length).toBe(1)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
describe('inline mode', () => {
|
|
96
|
+
const createProgram = (fileNames: string[]) =>
|
|
97
|
+
ts.createProgram(fileNames, {
|
|
98
|
+
module: ts.ModuleKind.CommonJS,
|
|
99
|
+
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
|
100
|
+
target: ts.ScriptTarget.ES2020,
|
|
101
|
+
skipLibCheck: true,
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('writes and updates MakeLogicType blocks above the logic', async () => {
|
|
105
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kea-typegen-inline-'))
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const logicDir = path.join(tempDir, 'src')
|
|
109
|
+
const logicPath = path.join(logicDir, 'logic.ts')
|
|
110
|
+
const logicTypePath = path.join(logicDir, 'logicType.ts')
|
|
111
|
+
const keaDtsPath = path.join(tempDir, 'node_modules', 'kea', 'index.d.ts')
|
|
112
|
+
|
|
113
|
+
fs.mkdirSync(path.dirname(keaDtsPath), { recursive: true })
|
|
114
|
+
fs.mkdirSync(logicDir, { recursive: true })
|
|
115
|
+
|
|
116
|
+
fs.writeFileSync(
|
|
117
|
+
keaDtsPath,
|
|
118
|
+
[
|
|
119
|
+
'export function kea<T = any>(input: any): T',
|
|
120
|
+
'export interface MakeLogicType<V = any, A = any, P = any> {}',
|
|
121
|
+
'',
|
|
122
|
+
].join('\n'),
|
|
123
|
+
)
|
|
124
|
+
fs.writeFileSync(
|
|
125
|
+
logicPath,
|
|
126
|
+
[
|
|
127
|
+
"import { kea } from 'kea'",
|
|
128
|
+
'',
|
|
129
|
+
'export const logic = kea({',
|
|
130
|
+
' props: {} as { id: number },',
|
|
131
|
+
' actions: () => ({',
|
|
132
|
+
' setValue: (value: string) => ({ value }),',
|
|
133
|
+
' }),',
|
|
134
|
+
' reducers: () => ({',
|
|
135
|
+
" value: ['' as string, { setValue: (_, { value }) => value }],",
|
|
136
|
+
' }),',
|
|
137
|
+
'})',
|
|
138
|
+
'',
|
|
139
|
+
].join('\n'),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
const appOptions: AppOptions = {
|
|
143
|
+
rootPath: logicDir,
|
|
144
|
+
typesPath: logicDir,
|
|
145
|
+
packageJsonPath: path.join(tempDir, 'package.json'),
|
|
146
|
+
inline: true,
|
|
147
|
+
write: true,
|
|
148
|
+
log: () => {},
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const program = createProgram([logicPath])
|
|
152
|
+
const parsedLogics = visitProgram(program, appOptions)
|
|
153
|
+
expect(parsedLogics.length).toBe(1)
|
|
154
|
+
|
|
155
|
+
const response = await printToFiles(program, appOptions, parsedLogics)
|
|
156
|
+
expect(response.writtenFiles).toBe(1)
|
|
157
|
+
|
|
158
|
+
const writtenLogic = fs.readFileSync(logicPath, 'utf8')
|
|
159
|
+
|
|
160
|
+
expect(writtenLogic).toContain("import { MakeLogicType, kea } from 'kea'")
|
|
161
|
+
expect(writtenLogic).toContain('// Generated by kea-typegen. DO NOT EDIT THIS BLOCK MANUALLY.')
|
|
162
|
+
expect(writtenLogic).toContain('export interface logicValues {')
|
|
163
|
+
expect(writtenLogic).toContain('export interface logicActions {')
|
|
164
|
+
expect(writtenLogic).toContain('export interface logicProps {')
|
|
165
|
+
expect(writtenLogic).toContain('export type logicType = MakeLogicType<logicValues, logicActions, logicProps>')
|
|
166
|
+
expect(writtenLogic).toContain('export const logic = kea<logicType>({')
|
|
167
|
+
expect(writtenLogic).toContain('value: string')
|
|
168
|
+
expect(writtenLogic).toContain('setValue: (value: string) =>')
|
|
169
|
+
expect(fs.existsSync(logicTypePath)).toBe(false)
|
|
170
|
+
|
|
171
|
+
// a second pass over the written file changes nothing
|
|
172
|
+
const secondProgram = createProgram([logicPath])
|
|
173
|
+
const secondParsedLogics = visitProgram(secondProgram, appOptions)
|
|
174
|
+
expect(secondParsedLogics.length).toBe(1)
|
|
175
|
+
|
|
176
|
+
const secondResponse = await printToFiles(secondProgram, appOptions, secondParsedLogics)
|
|
177
|
+
expect(secondResponse).toEqual({ filesToWrite: 0, writtenFiles: 0, filesToModify: 0 })
|
|
178
|
+
expect(fs.readFileSync(logicPath, 'utf8')).toBe(writtenLogic)
|
|
179
|
+
|
|
180
|
+
// adding an action updates the existing block in place
|
|
181
|
+
fs.writeFileSync(
|
|
182
|
+
logicPath,
|
|
183
|
+
fs
|
|
184
|
+
.readFileSync(logicPath, 'utf8')
|
|
185
|
+
.replace('setValue: (value: string) => ({ value }),', 'setValue: (value: string) => ({ value }),\n reset: true,'),
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
const thirdProgram = createProgram([logicPath])
|
|
189
|
+
const thirdResponse = await printToFiles(thirdProgram, appOptions, visitProgram(thirdProgram, appOptions))
|
|
190
|
+
expect(thirdResponse.writtenFiles).toBe(1)
|
|
191
|
+
|
|
192
|
+
const updatedLogic = fs.readFileSync(logicPath, 'utf8')
|
|
193
|
+
expect(updatedLogic).toContain('reset: ')
|
|
194
|
+
expect(updatedLogic.match(/export type logicType = MakeLogicType</g)?.length).toBe(1)
|
|
195
|
+
expect(updatedLogic.match(/export interface logicValues \{/g)?.length).toBe(1)
|
|
196
|
+
expect(updatedLogic.match(/export interface logicActions \{/g)?.length).toBe(1)
|
|
197
|
+
expect(updatedLogic.match(/DO NOT EDIT THIS BLOCK MANUALLY/g)?.length).toBe(1)
|
|
198
|
+
} finally {
|
|
199
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
test('replaces the old logicType.ts import when switching to inline mode', async () => {
|
|
204
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kea-typegen-inline-migrate-'))
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const logicDir = path.join(tempDir, 'src')
|
|
208
|
+
const logicPath = path.join(logicDir, 'logic.ts')
|
|
209
|
+
const logicTypePath = path.join(logicDir, 'logicType.ts')
|
|
210
|
+
const keaDtsPath = path.join(tempDir, 'node_modules', 'kea', 'index.d.ts')
|
|
211
|
+
|
|
212
|
+
fs.mkdirSync(path.dirname(keaDtsPath), { recursive: true })
|
|
213
|
+
fs.mkdirSync(logicDir, { recursive: true })
|
|
214
|
+
|
|
215
|
+
fs.writeFileSync(
|
|
216
|
+
keaDtsPath,
|
|
217
|
+
[
|
|
218
|
+
'export function kea<T = any>(input: any): T',
|
|
219
|
+
'export interface MakeLogicType<V = any, A = any, P = any> {}',
|
|
220
|
+
'',
|
|
221
|
+
].join('\n'),
|
|
222
|
+
)
|
|
223
|
+
fs.writeFileSync(logicTypePath, 'export interface logicType {}\n')
|
|
224
|
+
fs.writeFileSync(
|
|
225
|
+
logicPath,
|
|
226
|
+
[
|
|
227
|
+
"import { kea } from 'kea'",
|
|
228
|
+
"import type { logicType } from './logicType'",
|
|
229
|
+
'',
|
|
230
|
+
'export const logic = kea<logicType>({',
|
|
231
|
+
' actions: () => ({',
|
|
232
|
+
' setValue: (value: string) => ({ value }),',
|
|
233
|
+
' }),',
|
|
234
|
+
'})',
|
|
235
|
+
'',
|
|
236
|
+
].join('\n'),
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
const appOptions: AppOptions = {
|
|
240
|
+
rootPath: logicDir,
|
|
241
|
+
typesPath: logicDir,
|
|
242
|
+
packageJsonPath: path.join(tempDir, 'package.json'),
|
|
243
|
+
inline: true,
|
|
244
|
+
write: true,
|
|
245
|
+
delete: true,
|
|
246
|
+
log: () => {},
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const program = createProgram([logicPath])
|
|
250
|
+
const response = await printToFiles(program, appOptions, visitProgram(program, appOptions))
|
|
251
|
+
expect(response.writtenFiles).toBe(1)
|
|
252
|
+
|
|
253
|
+
const writtenLogic = fs.readFileSync(logicPath, 'utf8')
|
|
254
|
+
|
|
255
|
+
expect(writtenLogic).not.toContain("from './logicType'")
|
|
256
|
+
expect(writtenLogic).toContain("import { MakeLogicType, kea } from 'kea'")
|
|
257
|
+
expect(writtenLogic).toContain('export interface logicActions {')
|
|
258
|
+
// no values or props on this logic, so no interfaces are generated for them
|
|
259
|
+
expect(writtenLogic).toContain('export type logicType = MakeLogicType<{}, logicActions>')
|
|
260
|
+
expect(writtenLogic).toContain('export const logic = kea<logicType>({')
|
|
261
|
+
expect(fs.existsSync(logicTypePath)).toBe(false)
|
|
262
|
+
} finally {
|
|
263
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
})
|
package/src/__tests__/watch.ts
CHANGED
|
@@ -1,44 +1,271 @@
|
|
|
1
1
|
import { spawn } from 'child_process'
|
|
2
|
+
import * as fs from 'fs'
|
|
3
|
+
import * as os from 'os'
|
|
2
4
|
import * as path from 'path'
|
|
5
|
+
import * as ts from 'typescript'
|
|
6
|
+
import { planWatchTypegenPass } from '../watch'
|
|
3
7
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const scriptPath = path.join(repoRoot, 'src/test-support/watch-mode-smoke.js')
|
|
8
|
+
function writeFile(filePath: string, source: string): void {
|
|
9
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
10
|
+
fs.writeFileSync(filePath, source)
|
|
11
|
+
}
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
function createProgram(fileNames: string[]): ts.Program {
|
|
14
|
+
return ts.createProgram(fileNames, {
|
|
15
|
+
module: ts.ModuleKind.CommonJS,
|
|
16
|
+
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
|
17
|
+
target: ts.ScriptTarget.ES2020,
|
|
18
|
+
skipLibCheck: true,
|
|
19
|
+
})
|
|
20
|
+
}
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
test('watch mode does not overlap successive typegen passes', async () => {
|
|
23
|
+
const repoRoot = path.resolve(__dirname, '..', '..')
|
|
24
|
+
const scriptPath = path.join(repoRoot, 'src/test-support/watch-mode-smoke.js')
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
const result = await new Promise<{
|
|
27
|
+
code: number | null
|
|
28
|
+
signal: NodeJS.Signals | null
|
|
29
|
+
stdout: string
|
|
30
|
+
stderr: string
|
|
31
|
+
}>((resolve, reject) => {
|
|
32
|
+
const child = spawn(process.execPath, [scriptPath], {
|
|
33
|
+
cwd: repoRoot,
|
|
34
|
+
env: { ...process.env, FORCE_COLOR: '0' },
|
|
35
|
+
})
|
|
23
36
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
})
|
|
37
|
+
let stdout = ''
|
|
38
|
+
let stderr = ''
|
|
27
39
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
40
|
+
child.stdout.on('data', (chunk) => {
|
|
41
|
+
stdout += chunk.toString()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
child.stderr.on('data', (chunk) => {
|
|
45
|
+
stderr += chunk.toString()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
child.on('error', reject)
|
|
49
|
+
child.on('close', (code, signal) => resolve({ code, signal, stdout, stderr }))
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
expect(result.signal).toBeNull()
|
|
53
|
+
expect(result.code).toBe(0)
|
|
54
|
+
|
|
55
|
+
const payload = JSON.parse(result.stdout.trim())
|
|
56
|
+
|
|
57
|
+
expect(payload.started).toBeGreaterThan(1)
|
|
58
|
+
expect(payload.completed).toBeGreaterThan(0)
|
|
59
|
+
expect(payload.maxActive).toBe(1)
|
|
60
|
+
expect(payload.incrementalAfterEdit).toBe(true)
|
|
61
|
+
expect(payload.parsedLogicCountsAfterEdit).toContain(1)
|
|
62
|
+
expect(payload.sourceFilePathsAfterEdit.some(Boolean)).toBe(true)
|
|
63
|
+
expect(result.stderr).toBe('')
|
|
64
|
+
}, 30000)
|
|
65
|
+
|
|
66
|
+
test('watch planner limits a direct logic edit to the changed logic and dependent logics', () => {
|
|
67
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kea-typegen-watch-direct-logic-'))
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const logicDir = path.join(tempDir, 'src')
|
|
71
|
+
const firstLogicPath = path.join(logicDir, 'firstLogic.ts')
|
|
72
|
+
const secondLogicPath = path.join(logicDir, 'secondLogic.ts')
|
|
73
|
+
const unrelatedLogicPath = path.join(logicDir, 'unrelatedLogic.ts')
|
|
74
|
+
const keaDtsPath = path.join(tempDir, 'node_modules', 'kea', 'index.d.ts')
|
|
75
|
+
|
|
76
|
+
writeFile(keaDtsPath, 'export function kea<T = any>(input: any): T\n')
|
|
77
|
+
writeFile(
|
|
78
|
+
firstLogicPath,
|
|
79
|
+
[
|
|
80
|
+
"import { kea } from 'kea'",
|
|
81
|
+
'',
|
|
82
|
+
'export const firstLogic = kea({',
|
|
83
|
+
' actions: () => ({ first: true }),',
|
|
84
|
+
'})',
|
|
85
|
+
'',
|
|
86
|
+
].join('\n'),
|
|
87
|
+
)
|
|
88
|
+
writeFile(
|
|
89
|
+
secondLogicPath,
|
|
90
|
+
[
|
|
91
|
+
"import { kea } from 'kea'",
|
|
92
|
+
"import { firstLogic } from './firstLogic'",
|
|
93
|
+
'',
|
|
94
|
+
'export const secondLogic = kea({',
|
|
95
|
+
' connect: { values: [firstLogic, ["first"]] },',
|
|
96
|
+
'})',
|
|
97
|
+
'',
|
|
98
|
+
].join('\n'),
|
|
99
|
+
)
|
|
100
|
+
writeFile(
|
|
101
|
+
unrelatedLogicPath,
|
|
102
|
+
[
|
|
103
|
+
"import { kea } from 'kea'",
|
|
104
|
+
'',
|
|
105
|
+
'export const unrelatedLogic = kea({',
|
|
106
|
+
' actions: () => ({ unrelated: true }),',
|
|
107
|
+
'})',
|
|
108
|
+
'',
|
|
109
|
+
].join('\n'),
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
const program = createProgram([firstLogicPath, secondLogicPath, unrelatedLogicPath])
|
|
113
|
+
const plan = planWatchTypegenPass(program, [
|
|
114
|
+
{ fileName: firstLogicPath, eventKind: ts.FileWatcherEventKind.Changed },
|
|
115
|
+
])
|
|
116
|
+
|
|
117
|
+
expect(plan).toEqual({
|
|
118
|
+
kind: 'files',
|
|
119
|
+
sourceFilePaths: [firstLogicPath, secondLogicPath].sort(),
|
|
120
|
+
reason: '2 affected Kea logic files',
|
|
121
|
+
})
|
|
122
|
+
} finally {
|
|
123
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
test('watch planner regenerates logics affected by a helper type edit', () => {
|
|
128
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kea-typegen-watch-helper-'))
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const logicDir = path.join(tempDir, 'src')
|
|
132
|
+
const helperPath = path.join(logicDir, 'helper.ts')
|
|
133
|
+
const logicPath = path.join(logicDir, 'logic.ts')
|
|
134
|
+
const unrelatedLogicPath = path.join(logicDir, 'unrelatedLogic.ts')
|
|
135
|
+
const keaDtsPath = path.join(tempDir, 'node_modules', 'kea', 'index.d.ts')
|
|
136
|
+
|
|
137
|
+
writeFile(keaDtsPath, 'export function kea<T = any>(input: any): T\n')
|
|
138
|
+
writeFile(helperPath, 'export interface HelperType { value: string }\n')
|
|
139
|
+
writeFile(
|
|
140
|
+
logicPath,
|
|
141
|
+
[
|
|
142
|
+
"import { kea } from 'kea'",
|
|
143
|
+
"import type { HelperType } from './helper'",
|
|
144
|
+
'',
|
|
145
|
+
'export const logic = kea({',
|
|
146
|
+
' actions: () => ({ setValue: (value: HelperType) => ({ value }) }),',
|
|
147
|
+
'})',
|
|
148
|
+
'',
|
|
149
|
+
].join('\n'),
|
|
150
|
+
)
|
|
151
|
+
writeFile(
|
|
152
|
+
unrelatedLogicPath,
|
|
153
|
+
[
|
|
154
|
+
"import { kea } from 'kea'",
|
|
155
|
+
'',
|
|
156
|
+
'export const unrelatedLogic = kea({',
|
|
157
|
+
' actions: () => ({ unrelated: true }),',
|
|
158
|
+
'})',
|
|
159
|
+
'',
|
|
160
|
+
].join('\n'),
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
const program = createProgram([helperPath, logicPath, unrelatedLogicPath])
|
|
164
|
+
const plan = planWatchTypegenPass(program, [
|
|
165
|
+
{ fileName: helperPath, eventKind: ts.FileWatcherEventKind.Changed },
|
|
166
|
+
])
|
|
167
|
+
|
|
168
|
+
expect(plan).toEqual({
|
|
169
|
+
kind: 'files',
|
|
170
|
+
sourceFilePaths: [logicPath],
|
|
171
|
+
reason: '1 affected Kea logic file',
|
|
172
|
+
})
|
|
173
|
+
} finally {
|
|
174
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
test('watch planner maps generated type changes back to source logics and dependents', () => {
|
|
179
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kea-typegen-watch-type-file-'))
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const logicDir = path.join(tempDir, 'src')
|
|
183
|
+
const firstLogicPath = path.join(logicDir, 'firstLogic.ts')
|
|
184
|
+
const firstLogicTypePath = path.join(logicDir, 'firstLogicType.ts')
|
|
185
|
+
const secondLogicPath = path.join(logicDir, 'secondLogic.ts')
|
|
186
|
+
const keaDtsPath = path.join(tempDir, 'node_modules', 'kea', 'index.d.ts')
|
|
187
|
+
|
|
188
|
+
writeFile(keaDtsPath, 'export function kea<T = any>(input: any): T\n')
|
|
189
|
+
writeFile(firstLogicTypePath, 'export interface firstLogicType {}\n')
|
|
190
|
+
writeFile(
|
|
191
|
+
firstLogicPath,
|
|
192
|
+
[
|
|
193
|
+
"import { kea } from 'kea'",
|
|
194
|
+
"import type { firstLogicType } from './firstLogicType'",
|
|
195
|
+
'',
|
|
196
|
+
'export const firstLogic = kea<firstLogicType>({',
|
|
197
|
+
' actions: () => ({ first: true }),',
|
|
198
|
+
'})',
|
|
199
|
+
'',
|
|
200
|
+
].join('\n'),
|
|
201
|
+
)
|
|
202
|
+
writeFile(
|
|
203
|
+
secondLogicPath,
|
|
204
|
+
[
|
|
205
|
+
"import { kea } from 'kea'",
|
|
206
|
+
"import { firstLogic } from './firstLogic'",
|
|
207
|
+
'',
|
|
208
|
+
'export const secondLogic = kea({',
|
|
209
|
+
' connect: { actions: [firstLogic, ["first"]] },',
|
|
210
|
+
'})',
|
|
211
|
+
'',
|
|
212
|
+
].join('\n'),
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
const program = createProgram([firstLogicPath, firstLogicTypePath, secondLogicPath])
|
|
216
|
+
const plan = planWatchTypegenPass(program, [
|
|
217
|
+
{ fileName: firstLogicTypePath, eventKind: ts.FileWatcherEventKind.Changed },
|
|
218
|
+
])
|
|
219
|
+
|
|
220
|
+
expect(plan).toEqual({
|
|
221
|
+
kind: 'files',
|
|
222
|
+
sourceFilePaths: [firstLogicPath, secondLogicPath].sort(),
|
|
223
|
+
reason: '2 affected Kea logic files',
|
|
224
|
+
})
|
|
225
|
+
} finally {
|
|
226
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
test('watch planner falls back to a full pass for deleted files and resetContext changes', () => {
|
|
231
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kea-typegen-watch-full-'))
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
const logicDir = path.join(tempDir, 'src')
|
|
235
|
+
const logicPath = path.join(logicDir, 'logic.ts')
|
|
236
|
+
const contextPath = path.join(logicDir, 'context.ts')
|
|
237
|
+
const keaDtsPath = path.join(tempDir, 'node_modules', 'kea', 'index.d.ts')
|
|
238
|
+
|
|
239
|
+
writeFile(keaDtsPath, 'export function kea<T = any>(input: any): T\n')
|
|
240
|
+
writeFile(
|
|
241
|
+
logicPath,
|
|
242
|
+
[
|
|
243
|
+
"import { kea } from 'kea'",
|
|
244
|
+
'',
|
|
245
|
+
'export const logic = kea({',
|
|
246
|
+
' actions: () => ({ value: true }),',
|
|
247
|
+
'})',
|
|
248
|
+
'',
|
|
249
|
+
].join('\n'),
|
|
250
|
+
)
|
|
251
|
+
writeFile(
|
|
252
|
+
contextPath,
|
|
253
|
+
["import { resetContext } from 'kea'", '', 'resetContext({ plugins: [] })', ''].join('\n'),
|
|
31
254
|
)
|
|
32
255
|
|
|
33
|
-
|
|
34
|
-
expect(result.code).toBe(0)
|
|
256
|
+
const program = createProgram([logicPath, contextPath])
|
|
35
257
|
|
|
36
|
-
|
|
258
|
+
expect(
|
|
259
|
+
planWatchTypegenPass(program, [{ fileName: logicPath, eventKind: ts.FileWatcherEventKind.Deleted }]),
|
|
260
|
+
).toEqual({ kind: 'full', reason: 'deleted file detected' })
|
|
37
261
|
|
|
38
|
-
expect(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
262
|
+
expect(
|
|
263
|
+
planWatchTypegenPass(program, [{ fileName: contextPath, eventKind: ts.FileWatcherEventKind.Changed }]),
|
|
264
|
+
).toEqual({
|
|
265
|
+
kind: 'full',
|
|
266
|
+
reason: `resetContext changed: ${path.relative(process.cwd(), contextPath)}`,
|
|
267
|
+
})
|
|
268
|
+
} finally {
|
|
269
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
270
|
+
}
|
|
271
|
+
})
|
package/src/cli/typegen.ts
CHANGED
|
@@ -39,6 +39,10 @@ yargs
|
|
|
39
39
|
.option('delete', { describe: 'Delete logicType.ts files without a corresponding logic.ts', type: 'boolean' })
|
|
40
40
|
.option('add-ts-nocheck', { describe: 'Add @ts-nocheck to top of logicType.ts files', type: 'boolean' })
|
|
41
41
|
.option('convert-to-builders', { describe: 'Convert Kea 2.0 inputs to Kea 3.0 logic builders', type: 'boolean' })
|
|
42
|
+
.option('inline', {
|
|
43
|
+
describe: 'Write logic types as MakeLogicType blocks above each kea() call, instead of logicType.ts files',
|
|
44
|
+
type: 'boolean',
|
|
45
|
+
})
|
|
42
46
|
.option('import-global-types', {
|
|
43
47
|
describe: 'Add import statements in logicType.ts files for global types (e.g. @types/node)',
|
|
44
48
|
type: 'boolean',
|