@kubb/middleware-barrel 5.0.0-beta.3 → 5.0.0-beta.30
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/README.md +98 -0
- package/dist/index.cjs +107 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +14 -7
- package/dist/index.js +107 -68
- package/dist/index.js.map +1 -1
- package/extension.yaml +198 -0
- package/package.json +7 -5
- package/src/middleware.ts +52 -39
- package/src/{utils/getBarrelFiles.ts → utils.ts} +54 -27
- package/src/constants.ts +0 -4
- package/src/utils/excludedPaths.ts +0 -22
package/src/middleware.ts
CHANGED
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
import { resolve } from 'node:path'
|
|
3
|
+
import type { FileNode } from '@kubb/ast'
|
|
3
4
|
import { defineMiddleware } from '@kubb/core'
|
|
4
|
-
import type { Middleware } from '@kubb/core'
|
|
5
|
+
import type { Config, Middleware, NormalizedPlugin } from '@kubb/core'
|
|
5
6
|
import type { BarrelConfig, PluginBarrelConfig } from './types.ts'
|
|
6
|
-
import { getPluginOutputPrefix, isExcludedPath } from './utils
|
|
7
|
-
|
|
7
|
+
import { getBarrelFiles, getPluginOutputPrefix, isExcludedPath } from './utils.ts'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Applies a plugin's configured `output.banner`/`footer` to a barrel file, flagged as `isBarrel`.
|
|
11
|
+
*
|
|
12
|
+
* Resolves through the plugin's own resolver, and only when the plugin explicitly sets a
|
|
13
|
+
* banner/footer — so barrels stay banner-free by default and never inherit the implicit
|
|
14
|
+
* "Generated by Kubb" notice.
|
|
15
|
+
*/
|
|
16
|
+
function withBarrelBannerFooter({ file, plugin, config }: { file: FileNode; plugin: NormalizedPlugin; config: Config }): FileNode {
|
|
17
|
+
const output = plugin.options?.output
|
|
18
|
+
const resolver = plugin.resolver
|
|
19
|
+
if (!resolver) return file
|
|
20
|
+
|
|
21
|
+
const hasBanner = output?.banner !== undefined
|
|
22
|
+
const hasFooter = output?.footer !== undefined
|
|
23
|
+
if (!hasBanner && !hasFooter) return file
|
|
24
|
+
|
|
25
|
+
const context = { output, config, file: { path: file.path, baseName: file.baseName, isBarrel: true } }
|
|
26
|
+
return {
|
|
27
|
+
...file,
|
|
28
|
+
banner: hasBanner ? resolver.resolveBanner(undefined, context) : file.banner,
|
|
29
|
+
footer: hasFooter ? resolver.resolveFooter(undefined, context) : file.footer,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
8
32
|
|
|
9
33
|
declare global {
|
|
10
34
|
namespace Kubb {
|
|
@@ -38,17 +62,29 @@ declare global {
|
|
|
38
62
|
}
|
|
39
63
|
|
|
40
64
|
/**
|
|
41
|
-
*
|
|
65
|
+
* Canonical middleware name for `@kubb/middleware-barrel`. Used for driver
|
|
66
|
+
* lookups.
|
|
67
|
+
*/
|
|
68
|
+
export const middlewareBarrelName = 'middleware-barrel' satisfies Middleware['name']
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Generates an `index.ts` for every plugin output directory and one root
|
|
72
|
+
* barrel at `config.output.path/index.ts` after the build completes. Ships
|
|
73
|
+
* with Kubb and is registered by default in `defineConfig`.
|
|
42
74
|
*
|
|
43
|
-
* Each plugin inherits `output.barrel` from `config.output.barrel` (
|
|
44
|
-
* Set `barrel: false` on a plugin to
|
|
75
|
+
* Each plugin inherits `output.barrel` from `config.output.barrel` (which
|
|
76
|
+
* defaults to `{ type: 'named' }`). Set `barrel: false` on a plugin to skip
|
|
77
|
+
* its barrel and also exclude its files from the root barrel.
|
|
45
78
|
*
|
|
46
79
|
* @example
|
|
47
80
|
* ```ts
|
|
48
81
|
* import { defineConfig } from '@kubb/core'
|
|
49
82
|
* import { middlewareBarrel } from '@kubb/middleware-barrel'
|
|
83
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
84
|
+
* import { pluginZod } from '@kubb/plugin-zod'
|
|
50
85
|
*
|
|
51
86
|
* export default defineConfig({
|
|
87
|
+
* input: { path: './petStore.yaml' },
|
|
52
88
|
* output: { path: 'src/gen', barrel: { type: 'named' } },
|
|
53
89
|
* plugins: [
|
|
54
90
|
* pluginTs({ output: { path: 'types', barrel: { type: 'all' } } }),
|
|
@@ -58,12 +94,6 @@ declare global {
|
|
|
58
94
|
* })
|
|
59
95
|
* ```
|
|
60
96
|
*/
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Stable string identifier for the barrel middleware.
|
|
64
|
-
*/
|
|
65
|
-
export const middlewareBarrelName = 'middleware-barrel' satisfies Middleware['name']
|
|
66
|
-
|
|
67
97
|
export const middlewareBarrel = defineMiddleware(() => {
|
|
68
98
|
const excludedPrefixes = new Set<string>()
|
|
69
99
|
|
|
@@ -75,15 +105,12 @@ export const middlewareBarrel = defineMiddleware(() => {
|
|
|
75
105
|
const configBarrel = config.output.barrel
|
|
76
106
|
const defaultBarrel = { type: 'named' } as const
|
|
77
107
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
} else {
|
|
85
|
-
barrelConfig = defaultBarrel
|
|
86
|
-
}
|
|
108
|
+
// Root config barrel doesn't have nested, so we add it
|
|
109
|
+
const barrelConfig: PluginBarrelConfig | false = (() => {
|
|
110
|
+
if (pluginBarrel !== undefined) return pluginBarrel
|
|
111
|
+
if (configBarrel !== undefined) return configBarrel === false ? false : { ...configBarrel, nested: false }
|
|
112
|
+
return defaultBarrel
|
|
113
|
+
})()
|
|
87
114
|
|
|
88
115
|
if (barrelConfig === false) {
|
|
89
116
|
excludedPrefixes.add(getPluginOutputPrefix(plugin, config))
|
|
@@ -99,16 +126,8 @@ export const middlewareBarrel = defineMiddleware(() => {
|
|
|
99
126
|
if (relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
100
127
|
throw new Error('Invalid output path')
|
|
101
128
|
}
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
files,
|
|
105
|
-
barrelType,
|
|
106
|
-
nested,
|
|
107
|
-
recursive: true,
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
if (barrelFiles.length > 0) {
|
|
111
|
-
upsertFile(...barrelFiles)
|
|
129
|
+
for (const file of getBarrelFiles({ outputPath: target, files, barrelType, nested, recursive: true })) {
|
|
130
|
+
upsertFile(withBarrelBannerFooter({ file, plugin, config }))
|
|
112
131
|
}
|
|
113
132
|
},
|
|
114
133
|
'kubb:plugins:end'({ files, config, upsertFile }) {
|
|
@@ -121,14 +140,8 @@ export const middlewareBarrel = defineMiddleware(() => {
|
|
|
121
140
|
|
|
122
141
|
const barrelType = barrelConfig.type
|
|
123
142
|
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
files: filteredFiles,
|
|
127
|
-
barrelType,
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
if (rootBarrelFiles.length > 0) {
|
|
131
|
-
upsertFile(...rootBarrelFiles)
|
|
143
|
+
for (const file of getBarrelFiles({ outputPath: resolve(config.root, config.output.path), files: filteredFiles, barrelType })) {
|
|
144
|
+
upsertFile(file)
|
|
132
145
|
}
|
|
133
146
|
},
|
|
134
147
|
},
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { extname } from 'node:path'
|
|
1
|
+
import { extname, resolve } from 'node:path'
|
|
2
2
|
import { createExport, createFile } from '@kubb/ast'
|
|
3
3
|
import type { ExportNode, FileNode, SourceNode } from '@kubb/ast'
|
|
4
|
-
import {
|
|
5
|
-
import type { BarrelType } from '../types.ts'
|
|
4
|
+
import type { Config, NormalizedPlugin } from '@kubb/core'
|
|
6
5
|
import { type BuildTree, buildTree, toPosixPath } from '@internals/utils'
|
|
6
|
+
import type { BarrelType } from './types.ts'
|
|
7
7
|
|
|
8
8
|
const SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx'])
|
|
9
|
-
const BARREL_SUFFIX =
|
|
9
|
+
const BARREL_SUFFIX = `/index.ts`
|
|
10
10
|
|
|
11
11
|
function toRelativeModulePath(fromDir: string, filePath: string): string {
|
|
12
12
|
return `./${filePath.slice(fromDir.length + 1)}`
|
|
@@ -18,13 +18,14 @@ function isBarrelPath(path: string): boolean {
|
|
|
18
18
|
|
|
19
19
|
function makeBarrel(dirPath: string, exports: Array<ExportNode>): FileNode {
|
|
20
20
|
return createFile({
|
|
21
|
-
baseName:
|
|
21
|
+
baseName: 'index.ts',
|
|
22
22
|
path: `${dirPath}${BARREL_SUFFIX}`,
|
|
23
23
|
exports,
|
|
24
24
|
sources: [],
|
|
25
25
|
imports: [],
|
|
26
|
-
//
|
|
27
|
-
//
|
|
26
|
+
// Default to no banner/footer. The middleware resolves a configured plugin
|
|
27
|
+
// banner/footer (with isBarrel: true) afterwards, so a `banner` function can
|
|
28
|
+
// decide per file whether a barrel should carry a directive like "use server".
|
|
28
29
|
banner: undefined,
|
|
29
30
|
footer: undefined,
|
|
30
31
|
})
|
|
@@ -33,7 +34,7 @@ function makeBarrel(dirPath: string, exports: Array<ExportNode>): FileNode {
|
|
|
33
34
|
type LeafContext = {
|
|
34
35
|
dirPath: string
|
|
35
36
|
leafPath: string
|
|
36
|
-
sourceFile: FileNode |
|
|
37
|
+
sourceFile: FileNode | null
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
type LeafStrategy = (ctx: LeafContext) => Array<ExportNode>
|
|
@@ -99,9 +100,10 @@ type LeafWalkParams = {
|
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
/**
|
|
102
|
-
* Post-order walk that
|
|
103
|
+
* Post-order walk that yields a barrel per visited directory.
|
|
104
|
+
* Returns the list of leaf file paths collected in this subtree (used by the parent call).
|
|
103
105
|
*/
|
|
104
|
-
function walkAllOrNamed(node: BuildTree, params: LeafWalkParams, isRoot: boolean
|
|
106
|
+
function* walkAllOrNamed(node: BuildTree, params: LeafWalkParams, isRoot: boolean): Generator<FileNode, Array<string>> {
|
|
105
107
|
const subtreeLeaves: Array<string> = []
|
|
106
108
|
|
|
107
109
|
for (const child of node.children) {
|
|
@@ -110,26 +112,26 @@ function walkAllOrNamed(node: BuildTree, params: LeafWalkParams, isRoot: boolean
|
|
|
110
112
|
continue
|
|
111
113
|
}
|
|
112
114
|
|
|
113
|
-
const childLeaves = walkAllOrNamed(child, params, false
|
|
115
|
+
const childLeaves = yield* walkAllOrNamed(child, params, false)
|
|
114
116
|
for (const leaf of childLeaves) subtreeLeaves.push(leaf)
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
if (!isRoot && !params.recursive) return subtreeLeaves
|
|
118
120
|
|
|
119
|
-
const exports = subtreeLeaves.flatMap((leafPath) => params.strategy({ dirPath: node.path, leafPath, sourceFile: params.sourceFiles.get(leafPath) }))
|
|
121
|
+
const exports = subtreeLeaves.flatMap((leafPath) => params.strategy({ dirPath: node.path, leafPath, sourceFile: params.sourceFiles.get(leafPath) ?? null }))
|
|
120
122
|
|
|
121
123
|
if (exports.length > 0) {
|
|
122
|
-
|
|
124
|
+
yield makeBarrel(node.path, exports)
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
return subtreeLeaves
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
/**
|
|
129
|
-
* Recursive walk that
|
|
131
|
+
* Recursive walk that yields one barrel per directory, re-exporting files and sub-barrels.
|
|
130
132
|
* Used when nested: true.
|
|
131
133
|
*/
|
|
132
|
-
function walkNested(node: BuildTree
|
|
134
|
+
function* walkNested(node: BuildTree): Generator<FileNode> {
|
|
133
135
|
const exports: Array<ExportNode> = []
|
|
134
136
|
|
|
135
137
|
for (const child of node.children) {
|
|
@@ -139,12 +141,12 @@ function walkNested(node: BuildTree, out: Array<FileNode>): void {
|
|
|
139
141
|
continue
|
|
140
142
|
}
|
|
141
143
|
|
|
142
|
-
walkNested(child
|
|
144
|
+
yield* walkNested(child)
|
|
143
145
|
exports.push(createExport({ path: toRelativeModulePath(node.path, `${child.path}${BARREL_SUFFIX}`) }))
|
|
144
146
|
}
|
|
145
147
|
|
|
146
148
|
if (exports.length > 0) {
|
|
147
|
-
|
|
149
|
+
yield makeBarrel(node.path, exports)
|
|
148
150
|
}
|
|
149
151
|
}
|
|
150
152
|
|
|
@@ -204,24 +206,49 @@ type GetBarrelFilesParams = {
|
|
|
204
206
|
}
|
|
205
207
|
|
|
206
208
|
/**
|
|
207
|
-
*
|
|
209
|
+
* Yields barrel `FileNode`s for the directory rooted at `outputPath`.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```ts
|
|
213
|
+
* for (const file of getBarrelFiles({ outputPath, files, barrelType })) {
|
|
214
|
+
* upsertFile(file)
|
|
215
|
+
* }
|
|
216
|
+
* // or collect into an array
|
|
217
|
+
* const barrels = [...getBarrelFiles({ outputPath, files, barrelType })]
|
|
218
|
+
* ```
|
|
208
219
|
*/
|
|
209
|
-
export function getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }: GetBarrelFilesParams):
|
|
220
|
+
export function* getBarrelFiles({ outputPath, files, barrelType, nested = false, recursive = false }: GetBarrelFilesParams): Generator<FileNode> {
|
|
210
221
|
const { sourceFiles, paths } = indexRelevantFiles(files, outputPath)
|
|
211
|
-
if (paths.length === 0) return
|
|
222
|
+
if (paths.length === 0) return
|
|
212
223
|
|
|
213
224
|
const tree = buildTree(outputPath, paths)
|
|
214
|
-
const result: Array<FileNode> = []
|
|
215
225
|
|
|
216
|
-
// Use nested walk for hierarchical barrel structure
|
|
217
226
|
if (nested) {
|
|
218
|
-
walkNested(tree
|
|
219
|
-
return
|
|
227
|
+
yield* walkNested(tree)
|
|
228
|
+
return
|
|
220
229
|
}
|
|
221
230
|
|
|
222
231
|
const strategy = LEAF_STRATEGIES.get(barrelType)
|
|
223
|
-
if (!strategy) return
|
|
232
|
+
if (!strategy) return
|
|
224
233
|
|
|
225
|
-
walkAllOrNamed(tree, { sourceFiles, strategy, recursive }, true
|
|
226
|
-
|
|
234
|
+
yield* walkAllOrNamed(tree, { sourceFiles, strategy, recursive }, true)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Builds a POSIX-normalized prefix for a plugin's output directory, with a trailing `/`.
|
|
239
|
+
*
|
|
240
|
+
* Used to detect (and later exclude) files generated by plugins that opted out of the root barrel.
|
|
241
|
+
*/
|
|
242
|
+
export function getPluginOutputPrefix(plugin: NormalizedPlugin, config: Config): string {
|
|
243
|
+
return `${toPosixPath(resolve(config.root, config.output.path, plugin.options.output.path))}/`
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Returns `true` when `filePath` lives under any of the given excluded directory prefixes.
|
|
248
|
+
*
|
|
249
|
+
* Both sides are POSIX-normalized so Windows backslash paths match correctly.
|
|
250
|
+
*/
|
|
251
|
+
export function isExcludedPath(filePath: string, prefixes: ReadonlySet<string>): boolean {
|
|
252
|
+
const normalized = toPosixPath(filePath)
|
|
253
|
+
return prefixes.values().some((prefix) => normalized.startsWith(prefix))
|
|
227
254
|
}
|
package/src/constants.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { resolve } from 'node:path'
|
|
2
|
-
import type { Config, NormalizedPlugin } from '@kubb/core'
|
|
3
|
-
import { toPosixPath } from '@internals/utils'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Builds a POSIX-normalized prefix for a plugin's output directory, with a trailing `/`.
|
|
7
|
-
*
|
|
8
|
-
* Used to detect (and later exclude) files generated by plugins that opted out of the root barrel.
|
|
9
|
-
*/
|
|
10
|
-
export function getPluginOutputPrefix(plugin: NormalizedPlugin, config: Config): string {
|
|
11
|
-
return `${toPosixPath(resolve(config.root, config.output.path, plugin.options.output.path))}/`
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Returns `true` when `filePath` lives under any of the given excluded directory prefixes.
|
|
16
|
-
*
|
|
17
|
-
* Both sides are POSIX-normalized so Windows backslash paths match correctly.
|
|
18
|
-
*/
|
|
19
|
-
export function isExcludedPath(filePath: string, prefixes: ReadonlySet<string>): boolean {
|
|
20
|
-
const normalized = toPosixPath(filePath)
|
|
21
|
-
return prefixes.values().some((prefix) => normalized.startsWith(prefix))
|
|
22
|
-
}
|