kea-typegen 3.0.0 → 3.1.3

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/package.json +4 -4
  3. package/dist/src/print/print.d.ts +1 -2
  4. package/dist/src/print/print.js +46 -46
  5. package/dist/src/print/print.js.map +1 -1
  6. package/dist/src/print/printInternalExtraInput.js +1 -4
  7. package/dist/src/print/printInternalExtraInput.js.map +1 -1
  8. package/dist/src/types.d.ts +2 -2
  9. package/dist/src/utils.d.ts +1 -2
  10. package/dist/src/utils.js +29 -41
  11. package/dist/src/utils.js.map +1 -1
  12. package/dist/src/visit/visit.js +7 -4
  13. package/dist/src/visit/visit.js.map +1 -1
  14. package/dist/src/write/writeTypeImports.d.ts +2 -2
  15. package/dist/src/write/writeTypeImports.js +1 -4
  16. package/dist/src/write/writeTypeImports.js.map +1 -1
  17. package/dist/tsconfig.tsbuildinfo +1 -1
  18. package/form-plugin/package.json +1 -1
  19. package/package.json +4 -4
  20. package/samples/autoImportLogic.ts +4 -4
  21. package/samples/autoImportLogicType.ts +7 -7
  22. package/samples/builderLogicType.ts +3 -3
  23. package/samples/complexLogic.ts +7 -9
  24. package/samples/complexLogicType.ts +5 -6
  25. package/samples/dist/tsconfig.tsbuildinfo +1 -0
  26. package/samples/githubConnectLogicType.ts +3 -3
  27. package/samples/githubImportLogicType.ts +3 -3
  28. package/samples/githubLogicType.ts +3 -3
  29. package/samples/githubType.ts +2 -2
  30. package/samples/loadersLogicType.ts +3 -3
  31. package/samples/logic.ts +3 -3
  32. package/samples/logicType.ts +5 -3
  33. package/samples/pluginLogicType.ts +2 -2
  34. package/samples/propsLogic.ts +1 -1
  35. package/samples/propsLogicType.ts +2 -2
  36. package/samples/routerConnectLogicType.ts +3 -3
  37. package/samples/typed-builder/typedForm.typegen.ts +10 -0
  38. package/samples/typed-builder/typedFormDemoLogicType.ts +5 -2
  39. package/samples/windowValuesLogicType.ts +2 -2
  40. package/src/__tests__/e2e/__snapshots__/loaders.ts.snap +2 -2
  41. package/src/print/print.ts +54 -54
  42. package/src/print/printInternalExtraInput.ts +1 -5
  43. package/src/types.ts +2 -2
  44. package/src/utils.ts +31 -43
  45. package/src/visit/visit.ts +8 -5
  46. package/src/write/writeTypeImports.ts +9 -18
@@ -9,7 +9,7 @@ import {
9
9
  ScriptKind,
10
10
  ScriptTarget,
11
11
  SyntaxKind,
12
- TypeParameterDeclaration,
12
+ TypeNode,
13
13
  } from 'typescript'
14
14
  import * as fs from 'fs'
15
15
  import * as path from 'path'
@@ -132,7 +132,7 @@ export function printToFiles(
132
132
  }
133
133
  })
134
134
  .filter(({ fullPath }) => !shouldIgnore(fullPath))
135
- .map(({ list, relativePath }) => `import { ${list.join(', ')} } from '${relativePath}'`)
135
+ .map(({ list, relativePath }) => `import type { ${list.join(', ')} } from '${relativePath}'`)
136
136
  .join('\n')
137
137
 
138
138
  const finalOutput = [
@@ -142,7 +142,7 @@ export function printToFiles(
142
142
  ]
143
143
  .filter((a) => !!a)
144
144
  .join('\n'),
145
- `import { ${[...requiredKeys.values()].join(', ')} } from 'kea'`,
145
+ `import type { ${[...requiredKeys.values()].join(', ')} } from 'kea'`,
146
146
  otherimports,
147
147
  output,
148
148
  ]
@@ -181,7 +181,7 @@ export function printToFiles(
181
181
  // reload if logic type not imported
182
182
  (pl.logicTypeImported === false ||
183
183
  // reload if don't have the right types in arguments
184
- pl.logicTypeArguments.join(', ') !== [...pl.typeReferencesInLogicInput].sort().join(', ')) &&
184
+ pl.logicTypeArguments.length > 0) &&
185
185
  pl.fileName.match(/\.tsx?$/)
186
186
 
187
187
  // write the type into the logic itself
@@ -261,70 +261,70 @@ export function parsedLogicToTypeString(parsedLogic: ParsedLogic, appOptions?: A
261
261
  return nodeToString(parsedLogic.interfaceDeclaration)
262
262
  }
263
263
 
264
- export function getLogicTypeArguments(parsedLogic: ParsedLogic): TypeParameterDeclaration[] {
265
- return [...parsedLogic.typeReferencesInLogicInput]
266
- .sort()
267
- .map((text) => factory.createTypeParameterDeclaration(factory.createIdentifier(text), undefined))
268
- }
269
-
270
264
  export function printLogicType(parsedLogic: ParsedLogic, appOptions?: AppOptions): void {
271
- const printProperty = (name, typeNode) =>
272
- factory.createPropertySignature(undefined, factory.createIdentifier(name), undefined, typeNode)
273
-
274
265
  const addSelectorTypeHelp = parsedLogic.selectors.filter((s) => s.functionTypes.length > 0).length > 0
275
266
 
276
- const logicProperties = [
277
- printProperty('actionCreators', printActionCreators(parsedLogic, appOptions)),
278
- printProperty('actionKeys', printActionKeys(parsedLogic, appOptions)),
279
- printProperty('actionTypes', printActionTypes(parsedLogic, appOptions)),
280
- printProperty('actions', printActions(parsedLogic, appOptions)),
281
- printProperty('defaults', printDefaults(parsedLogic)),
282
- printProperty('events', printEvents(parsedLogic)),
283
- printProperty('key', printKey(parsedLogic)),
284
- printProperty('listeners', printListeners(parsedLogic)),
285
- printProperty(
286
- 'path',
287
- factory.createTupleTypeNode(
288
- parsedLogic.path.map((p) => factory.createLiteralTypeNode(factory.createStringLiteral(p))),
289
- ),
267
+ const logicProperties: Record<string, TypeNode | null> = {
268
+ actionCreators: printActionCreators(parsedLogic, appOptions),
269
+ actionKeys: printActionKeys(parsedLogic, appOptions),
270
+ actionTypes: printActionTypes(parsedLogic, appOptions),
271
+ actions: printActions(parsedLogic, appOptions),
272
+ defaults: printDefaults(parsedLogic),
273
+ events: printEvents(parsedLogic),
274
+ key: printKey(parsedLogic),
275
+ listeners: printListeners(parsedLogic),
276
+ path: factory.createTupleTypeNode(
277
+ parsedLogic.path.map((p) => factory.createLiteralTypeNode(factory.createStringLiteral(p))),
290
278
  ),
291
- printProperty('pathString', factory.createStringLiteral(parsedLogic.pathString)),
292
- printProperty('props', printProps(parsedLogic)),
293
- printProperty('reducer', printReducer(parsedLogic)),
294
- printProperty('reducers', printReducers(parsedLogic)),
295
- printProperty('selector', printSelector(parsedLogic)),
296
- printProperty('selectors', printSelectors(parsedLogic)),
297
- printProperty('sharedListeners', printSharedListeners(parsedLogic)),
298
- printProperty('values', printValues(parsedLogic)),
299
- printProperty('_isKea', factory.createTrue()),
300
- printProperty('_isKeaWithKey', parsedLogic.keyType ? factory.createTrue() : factory.createFalse()),
301
- addSelectorTypeHelp
302
- ? printProperty('__keaTypeGenInternalSelectorTypes', printInternalSelectorTypes(parsedLogic))
303
- : null,
304
- Object.keys(parsedLogic.extraActions).length > 0
305
- ? printProperty('__keaTypeGenInternalReducerActions', printInternalReducerActions(parsedLogic))
306
- : null,
307
- Object.keys(parsedLogic.extraInput).length > 0
308
- ? printProperty('__keaTypeGenInternalExtraInput', printInternalExtraInput(parsedLogic))
309
- : null,
310
- ].filter((a) => !!a)
279
+ pathString: factory.createLiteralTypeNode(factory.createStringLiteral(parsedLogic.pathString)),
280
+ props: printProps(parsedLogic),
281
+ reducer: printReducer(parsedLogic),
282
+ reducers: printReducers(parsedLogic),
283
+ selector: printSelector(parsedLogic),
284
+ selectors: printSelectors(parsedLogic),
285
+ sharedListeners: printSharedListeners(parsedLogic),
286
+ values: printValues(parsedLogic),
287
+ }
288
+ for (const [name, typeNode] of Object.entries(parsedLogic.extraLogicFields)) {
289
+ if (name in logicProperties) {
290
+ console.error(`❗ Can not add extra logic field ${name} because this field is already in the logic.`)
291
+ } else {
292
+ logicProperties[name] = typeNode
293
+ }
294
+ }
295
+ const logicMetaProperties: Record<string, TypeNode | null> = {
296
+ _isKea: factory.createLiteralTypeNode(factory.createTrue()),
297
+ _isKeaWithKey: factory.createLiteralTypeNode(
298
+ parsedLogic.keyType ? factory.createTrue() : factory.createFalse(),
299
+ ),
300
+ __keaTypeGenInternalSelectorTypes: addSelectorTypeHelp ? printInternalSelectorTypes(parsedLogic) : null,
301
+ __keaTypeGenInternalReducerActions:
302
+ Object.keys(parsedLogic.extraActions).length > 0 ? printInternalReducerActions(parsedLogic) : null,
303
+ __keaTypeGenInternalExtraInput:
304
+ Object.keys(parsedLogic.extraInput).length > 0 ? printInternalExtraInput(parsedLogic) : null,
305
+ }
311
306
 
312
- const logicTypeArguments = getLogicTypeArguments(parsedLogic)
307
+ const sortedLogicProperties = {
308
+ ...Object.fromEntries(
309
+ Object.entries(logicProperties).sort((a, b) => (a[0] === b[0] ? 0 : a[0] < b[0] ? -1 : 1)),
310
+ ),
311
+ ...logicMetaProperties,
312
+ }
313
313
 
314
314
  parsedLogic.interfaceDeclaration = factory.createInterfaceDeclaration(
315
315
  undefined,
316
316
  [factory.createModifier(SyntaxKind.ExportKeyword)],
317
317
  factory.createIdentifier(`${parsedLogic.logicName}Type`),
318
- logicTypeArguments,
318
+ undefined,
319
319
  [
320
320
  factory.createHeritageClause(SyntaxKind.ExtendsKeyword, [
321
321
  factory.createExpressionWithTypeArguments(factory.createIdentifier('Logic'), undefined),
322
322
  ]),
323
323
  ],
324
- logicProperties,
324
+ Object.entries(sortedLogicProperties)
325
+ .filter(([_, value]) => !!value)
326
+ .map(([name, typeNode]) =>
327
+ factory.createPropertySignature(undefined, factory.createIdentifier(name), undefined, typeNode),
328
+ ),
325
329
  )
326
330
  }
327
-
328
- // haha
329
- let i = 0
330
- const smiles = ['/', ']', '[', ')', '(', '\\', 'D', '|', 'O']
@@ -5,10 +5,6 @@ export function printInternalExtraInput(parsedLogic: ParsedLogic) {
5
5
  return factory.createTypeLiteralNode(
6
6
  Object.entries(parsedLogic.extraInput).map(([type, { typeNode, withLogicFunction }]) => {
7
7
  if (withLogicFunction) {
8
- const logicTypeArguments = [...parsedLogic.typeReferencesInLogicInput]
9
- .sort()
10
- .map((text) => factory.createTypeReferenceNode(factory.createIdentifier(text), undefined))
11
-
12
8
  return factory.createPropertySignature(
13
9
  undefined,
14
10
  factory.createStringLiteral(type),
@@ -27,7 +23,7 @@ export function printInternalExtraInput(parsedLogic: ParsedLogic) {
27
23
  undefined,
28
24
  factory.createTypeReferenceNode(
29
25
  factory.createIdentifier(parsedLogic.logicTypeName),
30
- logicTypeArguments.length > 0 ? logicTypeArguments : undefined,
26
+ undefined,
31
27
  ),
32
28
  undefined,
33
29
  ),
package/src/types.ts CHANGED
@@ -43,13 +43,13 @@ export interface ParsedLogic {
43
43
  selectors: SelectorTransform[]
44
44
  listeners: ListenerTransform[]
45
45
  sharedListeners: ListenerTransform[]
46
- extraActions: Record<string, ts.TypeNode>
47
46
  propsType?: ts.TypeNode
48
47
  keyType?: ts.TypeNode
49
48
  typeReferencesToImportFromFiles: Record<string, Set<string>>
50
- typeReferencesInLogicInput: Set<string>
51
49
  interfaceDeclaration?: ts.InterfaceDeclaration
50
+ extraActions: Record<string, ts.TypeNode>
52
51
  extraInput: Record<string, { typeNode: ts.TypeNode; withLogicFunction: boolean }>
52
+ extraLogicFields: Record<string, ts.TypeNode>
53
53
  importFromKeaInLogicType: Set<string>
54
54
  inputBuilderArray: boolean
55
55
  }
package/src/utils.ts CHANGED
@@ -4,7 +4,7 @@ import { cloneNode } from '@wessberg/ts-clone-node'
4
4
  import { visitProgram } from './visit/visit'
5
5
  import { parsedLogicToTypeString } from './print/print'
6
6
  import { AppOptions, NameType, ParsedLogic } from './types'
7
- import { factory, NodeBuilderFlags, SyntaxKind } from 'typescript'
7
+ import { factory, isSourceFile, NodeBuilderFlags, SyntaxKind } from 'typescript'
8
8
 
9
9
  export function logicSourceToLogicType(logicSource: string, appOptions?: AppOptions) {
10
10
  const program = programFromSource(logicSource)
@@ -23,6 +23,13 @@ export function programFromSource(sourceCode: string) {
23
23
  return ts.createProgram(['logic.ts'], options, compilerHost)
24
24
  }
25
25
 
26
+ function rejectImportPath(path: string): boolean {
27
+ if (path.includes('/node_modules/typescript/')) {
28
+ return true
29
+ }
30
+ return false
31
+ }
32
+
26
33
  export function isKeaCall(node: ts.Node, checker: ts.TypeChecker) {
27
34
  if (!ts.isIdentifier(node)) {
28
35
  return false
@@ -216,9 +223,7 @@ export function getLogicPathString(appOptions: AppOptions, fileName: string) {
216
223
  }
217
224
 
218
225
  export function getFilenamesForSymbol(symbol: ts.Symbol): string[] | undefined {
219
- return (symbol?.declarations || [])
220
- .map((d) => d.getSourceFile().fileName)
221
- .filter((str) => !str.includes('/node_modules/typescript/lib/lib'))
226
+ return (symbol?.declarations || []).map((d) => d.getSourceFile().fileName).filter((f) => !rejectImportPath(f))
222
227
  }
223
228
 
224
229
  /** gathers onto parsedLogic the TypeReference nodes that are declared in a different sourceFile */
@@ -237,7 +242,6 @@ export function gatherImports(input: ts.Node, checker: ts.TypeChecker, parsedLog
237
242
  typeRootName = (node.typeName.left as any)?.escapedText
238
243
  }
239
244
  }
240
-
241
245
  const symbol = checker.getSymbolAtLocation(node.typeName) || (node.typeName as any).symbol
242
246
  if (symbol) {
243
247
  storeExtractedSymbol(symbol, checker, parsedLogic, typeRootName)
@@ -255,32 +259,23 @@ export function storeExtractedSymbol(
255
259
  typeRootName?: string,
256
260
  ) {
257
261
  const declaration = symbol.getDeclarations()?.[0]
262
+ let typeName = typeRootName
258
263
 
259
- if (declaration && ts.isImportSpecifier(declaration)) {
260
- const importFilename = getFilenameForImportSpecifier(declaration, checker)
261
- if (importFilename) {
262
- addTypeImport(parsedLogic, importFilename, typeRootName || declaration.getText())
263
- } else {
264
- parsedLogic.typeReferencesInLogicInput.add(typeRootName || declaration.getText())
265
- }
266
- return
267
- }
268
-
269
- const files = getFilenamesForSymbol(symbol)
270
- if (files[0]) {
271
- // same file, add to logicType<...>
272
- if (
264
+ if (
265
+ declaration &&
266
+ (ts.isImportSpecifier(declaration) ||
273
267
  ts.isTypeAliasDeclaration(declaration) ||
274
268
  ts.isInterfaceDeclaration(declaration) ||
275
269
  ts.isEnumDeclaration(declaration) ||
276
- ts.isClassDeclaration(declaration)
277
- ) {
278
- if (files[0] === parsedLogic.fileName) {
279
- parsedLogic.typeReferencesInLogicInput.add(typeRootName || declaration.name.getText())
280
- } else {
281
- // but is it exported?
282
- addTypeImport(parsedLogic, files[0], typeRootName || declaration.name.getText())
283
- }
270
+ ts.isClassDeclaration(declaration))
271
+ ) {
272
+ typeName = typeName || declaration.name.getText()
273
+ }
274
+
275
+ if (typeName) {
276
+ const importFilename = getFilenameForNode(declaration, checker)
277
+ if (importFilename && !rejectImportPath(importFilename)) {
278
+ addTypeImport(parsedLogic, importFilename, typeName)
284
279
  }
285
280
  }
286
281
  }
@@ -293,14 +288,17 @@ export function getFilenameForImportDeclaration(checker: ts.TypeChecker, importN
293
288
  }
294
289
  }
295
290
 
296
- export function getFilenameForImportSpecifier(declaration: ts.ImportSpecifier, checker: ts.TypeChecker): string | void {
297
- let importNode: ts.Node = declaration
298
- while (importNode && !ts.isImportDeclaration(importNode)) {
291
+ export function getFilenameForNode(node: ts.Node, checker: ts.TypeChecker): string | void {
292
+ let importNode: ts.Node = node
293
+ while (importNode) {
294
+ if (ts.isImportDeclaration(importNode)) {
295
+ return getFilenameForImportDeclaration(checker, importNode)
296
+ }
297
+ if (isSourceFile(importNode)) {
298
+ return importNode.fileName
299
+ }
299
300
  importNode = importNode.parent
300
301
  }
301
- if (ts.isImportDeclaration(importNode)) {
302
- return getFilenameForImportDeclaration(checker, importNode)
303
- }
304
302
  }
305
303
 
306
304
  function addTypeImport(parsedLogic: ParsedLogic, file: string, typeName: string) {
@@ -310,16 +308,6 @@ function addTypeImport(parsedLogic: ParsedLogic, file: string, typeName: string)
310
308
  parsedLogic.typeReferencesToImportFromFiles[file].add(typeName.split('.')[0])
311
309
  }
312
310
 
313
- export function arrayContainsSet(array: string[], setToContain: Set<string>): boolean {
314
- const arraySet = new Set(array)
315
- for (const str of setToContain) {
316
- if (!arraySet.has(str)) {
317
- return false
318
- }
319
- }
320
- return true
321
- }
322
-
323
311
  export function unPromisify(node: ts.Node): ts.Node {
324
312
  if (ts.isTypeReferenceNode(node) && (node.typeName as any)?.escapedText === 'Promise') {
325
313
  return node.typeArguments?.[0]
@@ -4,7 +4,7 @@ import { AppOptions, ParsedLogic, PluginModule, TypeBuilderModule, VisitKeaPrope
4
4
  import {
5
5
  gatherImports,
6
6
  getFilenameForImportDeclaration,
7
- getFilenameForImportSpecifier,
7
+ getFilenameForNode,
8
8
  getLogicPathString,
9
9
  getTypeNodeForNode,
10
10
  isKeaCall,
@@ -185,7 +185,7 @@ export function visitKeaCalls(
185
185
  const declaration = symbol.getDeclarations()?.[0]
186
186
 
187
187
  if (declaration && ts.isImportSpecifier(declaration)) {
188
- const filename = getFilenameForImportSpecifier(declaration, checker)
188
+ const filename = getFilenameForNode(declaration, checker)
189
189
  logicTypeImported = filename === typeFileName
190
190
  }
191
191
  }
@@ -213,7 +213,6 @@ export function visitKeaCalls(
213
213
  listeners: [],
214
214
  sharedListeners: [],
215
215
  events: {},
216
- extraActions: {},
217
216
  keyType: undefined,
218
217
  propsType: undefined,
219
218
  path: pathString.split('.'),
@@ -221,8 +220,9 @@ export function visitKeaCalls(
221
220
  hasKeyInLogic: false,
222
221
  hasPathInLogic: false,
223
222
  typeReferencesToImportFromFiles: {},
224
- typeReferencesInLogicInput: new Set(),
223
+ extraActions: {},
225
224
  extraInput: {},
225
+ extraLogicFields: {},
226
226
  importFromKeaInLogicType: new Set([]),
227
227
  inputBuilderArray: ts.isArrayLiteralExpression(input),
228
228
  }
@@ -286,7 +286,10 @@ export function visitKeaCalls(
286
286
  }
287
287
 
288
288
  const folder = path.dirname(filename)
289
- const fileNoExt = path.basename(filename, path.extname(filename))
289
+ let fileNoExt = path.basename(filename, path.extname(filename))
290
+ if (fileNoExt.endsWith('.d')) {
291
+ fileNoExt = fileNoExt.slice(0, -2)
292
+ }
290
293
  const pathsToTry = [
291
294
  'typegen.js',
292
295
  'typegen.ts',
@@ -1,10 +1,10 @@
1
- import { AppOptions, ParsedLogic } from "../types";
2
- import * as ts from "typescript";
3
- import { print, visit } from "recast";
4
- import * as osPath from "path";
5
- import { runThroughPrettier } from "../print/print";
6
- import * as fs from "fs";
7
- import { t, b, visitAllKeaCalls, getAst } from "./utils";
1
+ import { AppOptions, ParsedLogic } from '../types'
2
+ import * as ts from 'typescript'
3
+ import { print, visit } from 'recast'
4
+ import * as osPath from 'path'
5
+ import { runThroughPrettier } from '../print/print'
6
+ import * as fs from 'fs'
7
+ import { t, b, visitAllKeaCalls, getAst } from './utils'
8
8
 
9
9
  export function writeTypeImports(
10
10
  appOptions: AppOptions,
@@ -68,19 +68,10 @@ export function writeTypeImports(
68
68
  })
69
69
  }
70
70
 
71
- // find all kea calls, add `<logicType<a,b>>` type parameters if needed
71
+ // find all kea calls, add `<logicType>` type parameters if needed
72
72
  visitAllKeaCalls(ast, logicsNeedingImports, filename, ({ path, parsedLogic }) => {
73
- const { logicTypeName, typeReferencesInLogicInput } = parsedLogic
74
-
75
73
  path.node.typeParameters = b.tsTypeParameterInstantiation([
76
- b.tsTypeReference(
77
- b.identifier(logicTypeName),
78
- typeReferencesInLogicInput.size > 0 ? b.tsTypeParameterInstantiation(
79
- [...typeReferencesInLogicInput.values()]
80
- .sort()
81
- .map((type) => b.tsTypeReference(b.identifier(type))),
82
- ) : null,
83
- ),
74
+ b.tsTypeReference(b.identifier(parsedLogic.logicTypeName)),
84
75
  ])
85
76
  })
86
77