@kubb/fabric-core 0.2.18 → 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.
Files changed (51) hide show
  1. package/dist/{Fabric-CVe8cc8b.d.ts → Fabric-RmoYWGrr.d.cts} +4 -4
  2. package/dist/{Fabric-BezqNTQ9.d.cts → Fabric-cIhiQpgN.d.ts} +4 -4
  3. package/dist/defineProperty-DwFON4j7.cjs +367 -0
  4. package/dist/defineProperty-DwFON4j7.cjs.map +1 -0
  5. package/dist/defineProperty-fiNt9UhD.js +325 -0
  6. package/dist/defineProperty-fiNt9UhD.js.map +1 -0
  7. package/dist/{getRelativePath-C6lvNCs7.cjs → getRelativePath-eCdp2Z8M.cjs} +1 -2
  8. package/dist/{getRelativePath-C6lvNCs7.cjs.map → getRelativePath-eCdp2Z8M.cjs.map} +1 -1
  9. package/dist/index.cjs +26 -27
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +26 -25
  14. package/dist/index.js.map +1 -1
  15. package/dist/parsers/typescript.cjs +2 -2
  16. package/dist/parsers/typescript.d.cts +2 -2
  17. package/dist/parsers/typescript.d.ts +2 -2
  18. package/dist/parsers/typescript.js +1 -1
  19. package/dist/parsers.cjs +2 -2
  20. package/dist/parsers.d.cts +2 -2
  21. package/dist/parsers.d.ts +2 -2
  22. package/dist/parsers.js +1 -1
  23. package/dist/plugins.cjs +79 -46
  24. package/dist/plugins.cjs.map +1 -1
  25. package/dist/plugins.d.cts +1 -1
  26. package/dist/plugins.d.ts +1 -1
  27. package/dist/plugins.js +78 -43
  28. package/dist/plugins.js.map +1 -1
  29. package/dist/types.d.cts +1 -1
  30. package/dist/types.d.ts +1 -1
  31. package/dist/{typescriptParser-CWT7zCJy.js → typescriptParser-BFhqWjdo.js} +20 -35
  32. package/dist/typescriptParser-BFhqWjdo.js.map +1 -0
  33. package/dist/{typescriptParser-B5SxjtvV.d.ts → typescriptParser-BjqVuRHF.d.cts} +3 -14
  34. package/dist/{typescriptParser-PfAO0SSm.d.cts → typescriptParser-Cy9_9o6I.d.ts} +3 -14
  35. package/dist/{typescriptParser-CNHO6H2_.cjs → typescriptParser-DJxEGCz3.cjs} +21 -36
  36. package/dist/typescriptParser-DJxEGCz3.cjs.map +1 -0
  37. package/package.json +1 -1
  38. package/src/Fabric.ts +1 -1
  39. package/src/FileManager.ts +8 -8
  40. package/src/FileProcessor.ts +8 -15
  41. package/src/createFile.ts +110 -57
  42. package/src/defineFabric.ts +15 -3
  43. package/src/parsers/typescriptParser.ts +33 -50
  44. package/src/plugins/barrelPlugin.ts +63 -36
  45. package/src/utils/TreeNode.ts +54 -27
  46. package/dist/defineProperty-DZi5DvrW.cjs +0 -390
  47. package/dist/defineProperty-DZi5DvrW.cjs.map +0 -1
  48. package/dist/defineProperty-DcP1vZ2K.js +0 -346
  49. package/dist/defineProperty-DcP1vZ2K.js.map +0 -1
  50. package/dist/typescriptParser-CNHO6H2_.cjs.map +0 -1
  51. package/dist/typescriptParser-CWT7zCJy.js.map +0 -1
@@ -6,29 +6,11 @@ import { createParser } from './createParser.ts'
6
6
 
7
7
  const { factory } = ts
8
8
 
9
- type PrintOptions = {
10
- source?: string
11
- baseName?: string
12
- scriptKind?: ts.ScriptKind
13
- }
14
-
15
- /**
16
- * Escaped new lines in code with block comments so they can be restored by {@link restoreNewLines}
17
- */
18
- const escapeNewLines = (code: string) => code.replace(/\n\n/g, '\n/* :newline: */')
19
-
20
- /**
21
- * Reverses {@link escapeNewLines} and restores new lines
22
- */
23
- const restoreNewLines = (code: string) => code.replace(/\/\* :newline: \*\//g, '\n')
24
-
25
9
  /**
26
10
  * Convert AST TypeScript/TSX nodes to a string based on the TypeScript printer.
27
- * Ensures consistent output across environments.
28
- * Also works as a formatter when `source` is provided without `elements`.
29
11
  */
30
- export function print(elements: Array<ts.Node> = [], { source = '', baseName = 'print.tsx', scriptKind = ts.ScriptKind.TSX }: PrintOptions = {}): string {
31
- const sourceFile = ts.createSourceFile(baseName, escapeNewLines(source), ts.ScriptTarget.ES2022, true, scriptKind)
12
+ export function print(...elements: Array<ts.Node>): string {
13
+ const sourceFile = ts.createSourceFile('print.tsx', '', ts.ScriptTarget.ES2022, true, ts.ScriptKind.TSX)
32
14
 
33
15
  const printer = ts.createPrinter({
34
16
  omitTrailingSemicolon: true,
@@ -37,18 +19,9 @@ export function print(elements: Array<ts.Node> = [], { source = '', baseName = '
37
19
  noEmitHelpers: true,
38
20
  })
39
21
 
40
- let output: string
22
+ const output = printer.printList(ts.ListFormat.MultiLine, factory.createNodeArray(elements), sourceFile)
41
23
 
42
- if (elements.length > 0) {
43
- // Print only provided nodes
44
- const nodes = elements.filter(Boolean).sort((a, b) => (a.pos ?? 0) - (b.pos ?? 0))
45
- output = printer.printList(ts.ListFormat.MultiLine, factory.createNodeArray(nodes), sourceFile)
46
- } else {
47
- // Format the whole file
48
- output = printer.printFile(sourceFile)
49
- }
50
-
51
- return restoreNewLines(output).replace(/\r\n/g, '\n')
24
+ return output.replace(/\r\n/g, '\n')
52
25
  }
53
26
 
54
27
  export function createImport({
@@ -153,36 +126,46 @@ export const typescriptParser = createParser({
153
126
  extNames: ['.ts', '.js'],
154
127
  install() {},
155
128
  async parse(file, options = { extname: '.ts' }) {
156
- const source = file.sources.map((item) => item.value).join('\n\n')
129
+ const sourceParts: Array<string> = []
130
+ for (const item of file.sources) {
131
+ if (item.value) {
132
+ sourceParts.push(item.value)
133
+ }
134
+ }
135
+ const source = sourceParts.join('\n\n')
157
136
 
158
- const importNodes = file.imports
159
- .map((item) => {
160
- const importPath = item.root ? getRelativePath(item.root, item.path) : item.path
161
- const hasExtname = !!path.extname(importPath)
137
+ const importNodes: Array<ts.ImportDeclaration> = []
138
+ for (const item of file.imports) {
139
+ const importPath = item.root ? getRelativePath(item.root, item.path) : item.path
140
+ const hasExtname = !!path.extname(importPath)
162
141
 
163
- return createImport({
142
+ importNodes.push(
143
+ createImport({
164
144
  name: item.name,
165
145
  path: options.extname && hasExtname ? `${trimExtName(importPath)}${options.extname}` : item.root ? trimExtName(importPath) : importPath,
166
146
  isTypeOnly: item.isTypeOnly,
167
- })
168
- })
169
- .filter(Boolean)
170
-
171
- const exportNodes = file.exports
172
- .map((item) => {
173
- const exportPath = item.path
147
+ }),
148
+ )
149
+ }
174
150
 
175
- const hasExtname = !!path.extname(exportPath)
151
+ const exportNodes: Array<ts.ExportDeclaration> = []
152
+ for (const item of file.exports) {
153
+ const exportPath = item.path
154
+ const hasExtname = !!path.extname(exportPath)
176
155
 
177
- return createExport({
156
+ exportNodes.push(
157
+ createExport({
178
158
  name: item.name,
179
159
  path: options.extname && hasExtname ? `${trimExtName(item.path)}${options.extname}` : trimExtName(item.path),
180
160
  isTypeOnly: item.isTypeOnly,
181
161
  asAlias: item.asAlias,
182
- })
183
- })
184
- .filter(Boolean)
162
+ }),
163
+ )
164
+ }
185
165
 
186
- return [file.banner, print([...importNodes, ...exportNodes]), source, file.footer].join('\n')
166
+ const parts = [file.banner, print(...importNodes, ...exportNodes), source, file.footer].filter(
167
+ (segment): segment is string => segment != null,
168
+ )
169
+ return parts.join('\n')
187
170
  },
188
171
  })
@@ -50,6 +50,20 @@ export function getBarrelFiles({ files, root, mode }: GetBarrelFilesOptions): Ar
50
50
  return []
51
51
  }
52
52
 
53
+ const indexableSourcesMap = new Map<KubbFile.File, Array<KubbFile.Source>>()
54
+
55
+ for (const file of files) {
56
+ const indexableSources: Array<KubbFile.Source> = []
57
+ for (const source of file.sources || []) {
58
+ if (source.isIndexable && source.name) {
59
+ indexableSources.push(source)
60
+ }
61
+ }
62
+ if (indexableSources.length > 0) {
63
+ indexableSourcesMap.set(file, indexableSources)
64
+ }
65
+ }
66
+
53
67
  const cachedFiles = new Map<KubbFile.Path, KubbFile.File>()
54
68
  const dedupe = new Map<KubbFile.Path, Set<string>>()
55
69
 
@@ -82,41 +96,40 @@ export function getBarrelFiles({ files, root, mode }: GetBarrelFilesOptions): Ar
82
96
 
83
97
  const seen = dedupe.get(barrelPath)!
84
98
 
85
- // Collect all leaves under the current directory node
86
- node.leaves.forEach((leaf) => {
99
+ for (const leaf of node.leaves) {
87
100
  const file = leaf.data.file
88
- if (!file) {
89
- return
101
+ if (!file || !file.path) {
102
+ continue
90
103
  }
91
104
 
92
- const sources = file.sources || []
93
- sources.forEach((source) => {
94
- if (!file.path || !source.isIndexable || !source.name) {
95
- return
96
- }
105
+ const indexableSources = indexableSourcesMap.get(file)
106
+ if (!indexableSources) {
107
+ continue
108
+ }
97
109
 
110
+ for (const source of indexableSources) {
98
111
  const key = `${source.name}|${source.isTypeOnly ? '1' : '0'}`
99
112
  if (seen.has(key)) {
100
- return
113
+ continue
101
114
  }
102
115
  seen.add(key)
103
116
 
104
117
  // Always compute relative path from the parent directory to the file path
105
118
  barrelFile!.exports!.push({
106
- name: [source.name],
119
+ name: [source.name!],
107
120
  path: getRelativePath(parentPath, file.path),
108
121
  isTypeOnly: source.isTypeOnly,
109
122
  })
110
123
 
111
124
  barrelFile!.sources.push({
112
- name: source.name,
125
+ name: source.name!,
113
126
  isTypeOnly: source.isTypeOnly,
114
127
  value: '', // TODO use parser to generate import
115
128
  isExportable: mode === 'all' || mode === 'named',
116
129
  isIndexable: mode === 'all' || mode === 'named',
117
130
  })
118
- })
119
- })
131
+ }
132
+ }
120
133
  })
121
134
 
122
135
  const result = [...cachedFiles.values()]
@@ -162,32 +175,46 @@ export const barrelPlugin = createPlugin<Options, ExtendOptions>({
162
175
 
163
176
  const rootPath = path.resolve(root, 'index.ts')
164
177
 
165
- const barrelFiles = ctx.files.filter((file) => {
166
- return file.sources.some((source) => source.isIndexable)
167
- })
178
+ const barrelFiles: Array<KubbFile.ResolvedFile> = []
179
+ for (const file of ctx.files) {
180
+ for (const source of file.sources) {
181
+ if (source.isIndexable) {
182
+ barrelFiles.push(file)
183
+
184
+ break
185
+ }
186
+ }
187
+ }
188
+
189
+ const fileTypeCache = new Map<KubbFile.ResolvedFile, boolean>()
190
+ for (const file of barrelFiles) {
191
+ fileTypeCache.set(
192
+ file,
193
+ file.sources.every((source) => source.isTypeOnly),
194
+ )
195
+ }
196
+
197
+ const exports: Array<KubbFile.Export> = []
198
+ for (const file of barrelFiles) {
199
+ const containsOnlyTypes = fileTypeCache.get(file) ?? false
200
+
201
+ for (const source of file.sources) {
202
+ if (!file.path || !source.isIndexable) {
203
+ continue
204
+ }
205
+
206
+ exports.push({
207
+ name: mode === 'all' ? undefined : [source.name],
208
+ path: getRelativePath(rootPath, file.path),
209
+ isTypeOnly: mode === 'all' ? containsOnlyTypes : source.isTypeOnly,
210
+ } as KubbFile.Export)
211
+ }
212
+ }
168
213
 
169
214
  const entryFile = createFile({
170
215
  path: rootPath,
171
216
  baseName: 'index.ts',
172
- exports: barrelFiles
173
- .flatMap((file) => {
174
- const containsOnlyTypes = file.sources.every((source) => source.isTypeOnly)
175
-
176
- return file.sources
177
- ?.map((source) => {
178
- if (!file.path || !source.isIndexable) {
179
- return undefined
180
- }
181
-
182
- return {
183
- name: mode === 'all' ? undefined : [source.name],
184
- path: getRelativePath(rootPath, file.path),
185
- isTypeOnly: mode === 'all' ? containsOnlyTypes : source.isTypeOnly,
186
- } as KubbFile.Export
187
- })
188
- .filter(Boolean)
189
- })
190
- .filter(Boolean),
217
+ exports,
191
218
  sources: [],
192
219
  })
193
220
 
@@ -15,6 +15,7 @@ export class TreeNode<TData = unknown> {
15
15
  data: TData
16
16
  parent?: TreeNode<TData>
17
17
  children: Array<TreeNode<TData>> = []
18
+ #childrenMap = new Map<string, TreeNode<TData>>()
18
19
  #cachedLeaves?: Array<TreeNode<TData>>
19
20
 
20
21
  constructor(data: TData, parent?: TreeNode<TData>) {
@@ -25,20 +26,35 @@ export class TreeNode<TData = unknown> {
25
26
  addChild(data: TData): TreeNode<TData> {
26
27
  const child = new TreeNode(data, this)
27
28
  this.children.push(child)
29
+ // Update Map if data has a name property (for BarrelData)
30
+ if (typeof data === 'object' && data !== null && 'name' in data) {
31
+ this.#childrenMap.set((data as { name: string }).name, child)
32
+ }
28
33
  this.#cachedLeaves = undefined // invalidate cached leaves
29
34
  return child
30
35
  }
31
36
 
37
+ getChildByName(name: string): TreeNode<TData> | undefined {
38
+ return this.#childrenMap.get(name)
39
+ }
40
+
32
41
  get leaves(): Array<TreeNode<TData>> {
33
42
  if (this.#cachedLeaves) return this.#cachedLeaves
34
43
  if (this.children.length === 0) return [this]
35
44
 
36
- const stack: Array<TreeNode<TData>> = [...this.children]
37
45
  const result: Array<TreeNode<TData>> = []
46
+ const stack: Array<TreeNode<TData>> = [...this.children]
47
+ const visited = new Set<TreeNode<TData>>()
38
48
 
39
- for (const node of stack) {
40
- if (node.children.length) {
41
- for (const child of node.children) stack.push(child)
49
+ while (stack.length > 0) {
50
+ const node = stack.pop()!
51
+ if (visited.has(node)) {
52
+ continue
53
+ }
54
+ visited.add(node)
55
+
56
+ if (node.children.length > 0) {
57
+ stack.push(...node.children)
42
58
  } else {
43
59
  result.push(node)
44
60
  }
@@ -51,12 +67,15 @@ export class TreeNode<TData = unknown> {
51
67
  forEach(callback: (node: TreeNode<TData>) => void): this {
52
68
  const stack: Array<TreeNode<TData>> = [this]
53
69
 
54
- for (const node of stack) {
70
+ for (let i = 0; i < stack.length; i++) {
71
+ const node = stack[i]!
55
72
  callback(node)
56
- if (node.children.length) {
57
- for (const child of node.children) stack.push(child)
73
+
74
+ if (node.children.length > 0) {
75
+ stack.push(...node.children)
58
76
  }
59
77
  }
78
+
60
79
  return this
61
80
  }
62
81
 
@@ -71,19 +90,28 @@ export class TreeNode<TData = unknown> {
71
90
  const nodes: Array<{ id: string; label: string }> = []
72
91
  const edges: Array<{ from: string; to: string }> = []
73
92
 
74
- root.forEach((node) => {
93
+ const stack: Array<TreeNode<BarrelData>> = [root]
94
+
95
+ for (let i = 0; i < stack.length; i++) {
96
+ const node = stack[i]!
97
+
75
98
  nodes.push({
76
99
  id: node.data.path,
77
100
  label: node.data.name,
78
101
  })
79
102
 
80
- for (const child of node.children) {
81
- edges.push({
82
- from: node.data.path,
83
- to: child.data.path,
84
- })
103
+ const children = node.children
104
+ if (children.length > 0) {
105
+ for (let j = 0, len = children.length; j < len; j++) {
106
+ const child = children[j]!
107
+ edges.push({
108
+ from: node.data.path,
109
+ to: child.data.path,
110
+ })
111
+ stack.push(child)
112
+ }
85
113
  }
86
- })
114
+ }
87
115
 
88
116
  return { nodes, edges }
89
117
  }
@@ -93,11 +121,15 @@ export class TreeNode<TData = unknown> {
93
121
  const normalizedRoot = normalizePath(rootFolder)
94
122
  const rootPrefix = normalizedRoot.endsWith('/') ? normalizedRoot : `${normalizedRoot}/`
95
123
 
96
- const filteredFiles = files.filter((file) => {
97
- const filePath = normalizePath(file.path)
98
-
99
- return !filePath.endsWith('.json') && (!rootFolder || filePath.startsWith(rootPrefix))
100
- })
124
+ const normalizedPaths = new Map<KubbFile.File, string>()
125
+ const filteredFiles: Array<KubbFile.File> = []
126
+ for (const file of files) {
127
+ const filePath = normalizedPaths.get(file) ?? normalizePath(file.path)
128
+ normalizedPaths.set(file, filePath)
129
+ if (!filePath.endsWith('.json') && (!rootFolder || filePath.startsWith(rootPrefix))) {
130
+ filteredFiles.push(file)
131
+ }
132
+ }
101
133
 
102
134
  if (filteredFiles.length === 0) {
103
135
  return null
@@ -110,7 +142,8 @@ export class TreeNode<TData = unknown> {
110
142
  })
111
143
 
112
144
  for (const file of filteredFiles) {
113
- const relPath = normalizePath(file.path).slice(rootPrefix.length)
145
+ const filePath = normalizedPaths.get(file)!
146
+ const relPath = filePath.slice(rootPrefix.length)
114
147
  const parts = relPath.split('/')
115
148
 
116
149
  let current = treeNode
@@ -120,13 +153,7 @@ export class TreeNode<TData = unknown> {
120
153
  const isLast = index === parts.length - 1
121
154
  currentPath += (currentPath.endsWith('/') ? '' : '/') + part
122
155
 
123
- let next: TreeNode<BarrelData> | undefined
124
- for (const child of current.children) {
125
- if ((child.data as BarrelData).name === part) {
126
- next = child
127
- break
128
- }
129
- }
156
+ let next = current.getChildByName(part)
130
157
 
131
158
  if (!next) {
132
159
  next = current.addChild({