@kubb/core 5.0.0-beta.17 → 5.0.0-beta.19
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/dist/{PluginDriver-C5hyNJfM.cjs → PluginDriver-DXp767s2.cjs} +77 -27
- package/dist/PluginDriver-DXp767s2.cjs.map +1 -0
- package/dist/{PluginDriver-CT33kVoQ.js → PluginDriver-uNex0SAr.js} +77 -27
- package/dist/PluginDriver-uNex0SAr.js.map +1 -0
- package/dist/{createKubb-ZgT1MTxG.d.ts → createKubb-BJGymYhe.d.ts} +224 -92
- package/dist/index.cjs +409 -291
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +409 -291
- package/dist/index.js.map +1 -1
- package/dist/mocks.cjs +25 -13
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.ts +1 -1
- package/dist/mocks.js +25 -13
- package/dist/mocks.js.map +1 -1
- package/package.json +5 -5
- package/src/FileManager.ts +4 -0
- package/src/FileProcessor.ts +15 -16
- package/src/PluginDriver.ts +37 -10
- package/src/createKubb.ts +371 -179
- package/src/createRenderer.ts +23 -22
- package/src/defineParser.ts +1 -1
- package/src/defineResolver.ts +62 -60
- package/src/mocks.ts +3 -3
- package/dist/PluginDriver-C5hyNJfM.cjs.map +0 -1
- package/dist/PluginDriver-CT33kVoQ.js.map +0 -1
package/src/createKubb.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { resolve } from 'node:path'
|
|
2
2
|
import { version as nodeVersion } from 'node:process'
|
|
3
3
|
import type { PossiblePromise } from '@internals/utils'
|
|
4
|
-
import { AsyncEventEmitter, BuildError, exists, formatMs, getElapsedMs, URLPath } from '@internals/utils'
|
|
5
|
-
import type { FileNode, InputNode,
|
|
4
|
+
import { AsyncEventEmitter, BuildError, exists, formatMs, getElapsedMs, URLPath, isPromise } from '@internals/utils'
|
|
5
|
+
import type { FileNode, InputNode, OperationNode, SchemaNode } from '@kubb/ast'
|
|
6
6
|
import { collectUsedSchemaNames, transform, walk } from '@kubb/ast'
|
|
7
7
|
import { version as KubbVersion } from '../package.json'
|
|
8
|
-
import { DEFAULT_BANNER, DEFAULT_EXTENSION, DEFAULT_STUDIO_URL,
|
|
8
|
+
import { DEFAULT_BANNER, DEFAULT_EXTENSION, DEFAULT_STUDIO_URL, STREAM_SCHEMA_THRESHOLD } from './constants.ts'
|
|
9
9
|
import type { Adapter, AdapterSource } from './createAdapter.ts'
|
|
10
10
|
import type { RendererFactory } from './createRenderer.ts'
|
|
11
11
|
import { createStorage, type Storage } from './createStorage.ts'
|
|
@@ -14,6 +14,7 @@ import type { Middleware } from './defineMiddleware.ts'
|
|
|
14
14
|
import type { Parser } from './defineParser.ts'
|
|
15
15
|
import type { KubbPluginEndContext, KubbPluginSetupContext, KubbPluginStartContext, NormalizedPlugin, Plugin } from './definePlugin.ts'
|
|
16
16
|
import { FileProcessor } from './FileProcessor.ts'
|
|
17
|
+
|
|
17
18
|
import { applyHookResult, PluginDriver } from './PluginDriver.ts'
|
|
18
19
|
import { fsStorage } from './storages/fsStorage.ts'
|
|
19
20
|
|
|
@@ -480,21 +481,16 @@ declare global {
|
|
|
480
481
|
|
|
481
482
|
/**
|
|
482
483
|
* Lifecycle events emitted during Kubb code generation.
|
|
483
|
-
*
|
|
484
|
+
* Attach listeners before calling `setup()` or `build()` to observe and react to build progress.
|
|
484
485
|
*
|
|
485
486
|
* @example
|
|
486
|
-
* ```
|
|
487
|
-
*
|
|
488
|
-
* import type { KubbHooks } from '@kubb/core'
|
|
489
|
-
*
|
|
490
|
-
* const hooks: AsyncEventEmitter<KubbHooks> = new AsyncEventEmitter()
|
|
491
|
-
*
|
|
492
|
-
* hooks.on('kubb:lifecycle:start', () => {
|
|
487
|
+
* ```ts
|
|
488
|
+
* kubb.hooks.on('kubb:lifecycle:start', () => {
|
|
493
489
|
* console.log('Starting Kubb generation')
|
|
494
490
|
* })
|
|
495
491
|
*
|
|
496
|
-
* hooks.on('kubb:plugin:end', ({ plugin, duration }) => {
|
|
497
|
-
* console.log(
|
|
492
|
+
* kubb.hooks.on('kubb:plugin:end', ({ plugin, duration }) => {
|
|
493
|
+
* console.log(`${plugin.name} completed in ${duration}ms`)
|
|
498
494
|
* })
|
|
499
495
|
* ```
|
|
500
496
|
*/
|
|
@@ -535,51 +531,96 @@ export interface KubbHooks {
|
|
|
535
531
|
}
|
|
536
532
|
|
|
537
533
|
export type KubbBuildStartContext = {
|
|
534
|
+
/**
|
|
535
|
+
* Resolved configuration for this build.
|
|
536
|
+
*/
|
|
538
537
|
config: Config
|
|
538
|
+
/**
|
|
539
|
+
* Adapter that parsed the input into the universal AST.
|
|
540
|
+
*/
|
|
539
541
|
adapter: Adapter
|
|
542
|
+
/**
|
|
543
|
+
* Parsed input node. For streaming builds the node is a synthetic empty shell
|
|
544
|
+
* with only `meta` populated — use `kubb:generate:schema` / `kubb:generate:operation` to observe individual nodes.
|
|
545
|
+
*/
|
|
540
546
|
inputNode: InputNode
|
|
547
|
+
/**
|
|
548
|
+
* Looks up a registered plugin by name, typed by the plugin registry.
|
|
549
|
+
*/
|
|
541
550
|
getPlugin<TName extends keyof Kubb.PluginRegistry>(name: TName): Plugin<Kubb.PluginRegistry[TName]> | undefined
|
|
542
551
|
getPlugin(name: string): Plugin | undefined
|
|
552
|
+
/**
|
|
553
|
+
* Snapshot of all files accumulated so far.
|
|
554
|
+
*/
|
|
543
555
|
readonly files: ReadonlyArray<FileNode>
|
|
556
|
+
/**
|
|
557
|
+
* Adds or merges one or more files into the file manager.
|
|
558
|
+
*/
|
|
544
559
|
upsertFile: (...files: Array<FileNode>) => void
|
|
545
560
|
}
|
|
546
561
|
|
|
547
562
|
export type KubbPluginsEndContext = {
|
|
563
|
+
/**
|
|
564
|
+
* Resolved configuration for this build.
|
|
565
|
+
*/
|
|
548
566
|
config: Config
|
|
567
|
+
/**
|
|
568
|
+
* Snapshot of all files accumulated across all plugins.
|
|
569
|
+
*/
|
|
549
570
|
readonly files: ReadonlyArray<FileNode>
|
|
571
|
+
/**
|
|
572
|
+
* Adds or merges one or more files into the file manager.
|
|
573
|
+
*/
|
|
550
574
|
upsertFile: (...files: Array<FileNode>) => void
|
|
551
575
|
}
|
|
552
576
|
|
|
553
577
|
export type KubbBuildEndContext = {
|
|
578
|
+
/**
|
|
579
|
+
* All files generated during this build.
|
|
580
|
+
*/
|
|
554
581
|
files: Array<FileNode>
|
|
582
|
+
/**
|
|
583
|
+
* Resolved configuration for this build.
|
|
584
|
+
*/
|
|
555
585
|
config: Config
|
|
586
|
+
/**
|
|
587
|
+
* Absolute path to the output directory.
|
|
588
|
+
*/
|
|
556
589
|
outputDir: string
|
|
557
590
|
}
|
|
558
591
|
|
|
559
592
|
export type KubbLifecycleStartContext = {
|
|
593
|
+
/**
|
|
594
|
+
* Current Kubb version string.
|
|
595
|
+
*/
|
|
560
596
|
version: string
|
|
561
597
|
}
|
|
562
598
|
|
|
563
599
|
export type KubbConfigEndContext = {
|
|
600
|
+
/**
|
|
601
|
+
* All resolved configs after defaults are applied.
|
|
602
|
+
*/
|
|
564
603
|
configs: Array<Config>
|
|
565
604
|
}
|
|
566
605
|
|
|
567
606
|
export type KubbGenerationStartContext = {
|
|
607
|
+
/**
|
|
608
|
+
* Resolved configuration for this generation run.
|
|
609
|
+
*/
|
|
568
610
|
config: Config
|
|
569
611
|
}
|
|
570
612
|
|
|
571
613
|
export type KubbGenerationEndContext = {
|
|
614
|
+
/**
|
|
615
|
+
* Resolved configuration for this generation run.
|
|
616
|
+
*/
|
|
572
617
|
config: Config
|
|
573
618
|
/**
|
|
574
|
-
* Read-only view of the files
|
|
575
|
-
*
|
|
576
|
-
* Keys are scoped to this run; files from earlier builds are not included.
|
|
577
|
-
* Reads go directly to `config.storage`, so nothing is buffered in memory.
|
|
619
|
+
* Read-only view of the files written during this build.
|
|
620
|
+
* Reads go directly to `config.storage` — nothing extra is held in memory.
|
|
578
621
|
*
|
|
579
622
|
* @example Read a generated file
|
|
580
|
-
*
|
|
581
|
-
* const code = await storage.getItem('/src/gen/pet.ts')
|
|
582
|
-
* ```
|
|
623
|
+
* `const code = await storage.getItem('/src/gen/pet.ts')`
|
|
583
624
|
*
|
|
584
625
|
* @example Walk every generated file
|
|
585
626
|
* ```ts
|
|
@@ -592,73 +633,178 @@ export type KubbGenerationEndContext = {
|
|
|
592
633
|
}
|
|
593
634
|
|
|
594
635
|
export type KubbGenerationSummaryContext = {
|
|
636
|
+
/**
|
|
637
|
+
* Resolved configuration for this generation run.
|
|
638
|
+
*/
|
|
595
639
|
config: Config
|
|
640
|
+
/**
|
|
641
|
+
* Plugins that threw during generation, paired with their errors.
|
|
642
|
+
*/
|
|
596
643
|
failedPlugins: Set<{ plugin: Plugin; error: Error }>
|
|
644
|
+
/**
|
|
645
|
+
* `'success'` when all plugins completed without errors, `'failed'` otherwise.
|
|
646
|
+
*/
|
|
597
647
|
status: 'success' | 'failed'
|
|
648
|
+
/**
|
|
649
|
+
* High-resolution start time from `process.hrtime()`.
|
|
650
|
+
*/
|
|
598
651
|
hrStart: [number, number]
|
|
652
|
+
/**
|
|
653
|
+
* Total number of files created during this run.
|
|
654
|
+
*/
|
|
599
655
|
filesCreated: number
|
|
656
|
+
/**
|
|
657
|
+
* Elapsed milliseconds per plugin, keyed by plugin name.
|
|
658
|
+
*/
|
|
600
659
|
pluginTimings?: Map<Plugin['name'], number>
|
|
601
660
|
}
|
|
602
661
|
|
|
603
662
|
export type KubbVersionNewContext = {
|
|
663
|
+
/**
|
|
664
|
+
* The installed Kubb version.
|
|
665
|
+
*/
|
|
604
666
|
currentVersion: string
|
|
667
|
+
/**
|
|
668
|
+
* The newest available version on npm.
|
|
669
|
+
*/
|
|
605
670
|
latestVersion: string
|
|
606
671
|
}
|
|
607
672
|
|
|
608
673
|
export type KubbInfoContext = {
|
|
674
|
+
/**
|
|
675
|
+
* Human-readable info message.
|
|
676
|
+
*/
|
|
609
677
|
message: string
|
|
678
|
+
/**
|
|
679
|
+
* Optional supplementary detail.
|
|
680
|
+
*/
|
|
610
681
|
info?: string
|
|
611
682
|
}
|
|
612
683
|
|
|
613
684
|
export type KubbErrorContext = {
|
|
685
|
+
/**
|
|
686
|
+
* The caught error.
|
|
687
|
+
*/
|
|
614
688
|
error: Error
|
|
689
|
+
/**
|
|
690
|
+
* Optional structured metadata for additional context.
|
|
691
|
+
*/
|
|
615
692
|
meta?: Record<string, unknown>
|
|
616
693
|
}
|
|
617
694
|
|
|
618
695
|
export type KubbSuccessContext = {
|
|
696
|
+
/**
|
|
697
|
+
* Human-readable success message.
|
|
698
|
+
*/
|
|
619
699
|
message: string
|
|
700
|
+
/**
|
|
701
|
+
* Optional supplementary detail.
|
|
702
|
+
*/
|
|
620
703
|
info?: string
|
|
621
704
|
}
|
|
622
705
|
|
|
623
706
|
export type KubbWarnContext = {
|
|
707
|
+
/**
|
|
708
|
+
* Human-readable warning message.
|
|
709
|
+
*/
|
|
624
710
|
message: string
|
|
711
|
+
/**
|
|
712
|
+
* Optional supplementary detail.
|
|
713
|
+
*/
|
|
625
714
|
info?: string
|
|
626
715
|
}
|
|
627
716
|
|
|
628
717
|
export type KubbDebugContext = {
|
|
718
|
+
/**
|
|
719
|
+
* Timestamp when the debug entry was created.
|
|
720
|
+
*/
|
|
629
721
|
date: Date
|
|
722
|
+
/**
|
|
723
|
+
* One or more log lines to emit.
|
|
724
|
+
*/
|
|
630
725
|
logs: Array<string>
|
|
726
|
+
/**
|
|
727
|
+
* Optional source file name associated with this entry.
|
|
728
|
+
*/
|
|
631
729
|
fileName?: string
|
|
632
730
|
}
|
|
633
731
|
|
|
634
732
|
export type KubbFilesProcessingStartContext = {
|
|
733
|
+
/**
|
|
734
|
+
* Files about to be serialised and written.
|
|
735
|
+
*/
|
|
635
736
|
files: Array<FileNode>
|
|
636
737
|
}
|
|
637
738
|
|
|
638
739
|
export type KubbFileProcessingUpdateContext = {
|
|
740
|
+
/**
|
|
741
|
+
* Number of files processed so far in this batch.
|
|
742
|
+
*/
|
|
639
743
|
processed: number
|
|
744
|
+
/**
|
|
745
|
+
* Total number of files in this batch.
|
|
746
|
+
*/
|
|
640
747
|
total: number
|
|
748
|
+
/**
|
|
749
|
+
* Completion percentage (`0`–`100`).
|
|
750
|
+
*/
|
|
641
751
|
percentage: number
|
|
752
|
+
/**
|
|
753
|
+
* Serialised file content, or `undefined` when the file produced no output.
|
|
754
|
+
*/
|
|
642
755
|
source?: string
|
|
756
|
+
/**
|
|
757
|
+
* The file that was just processed.
|
|
758
|
+
*/
|
|
643
759
|
file: FileNode
|
|
760
|
+
/**
|
|
761
|
+
* Resolved configuration for this build.
|
|
762
|
+
*/
|
|
644
763
|
config: Config
|
|
645
764
|
}
|
|
646
765
|
|
|
647
766
|
export type KubbFilesProcessingEndContext = {
|
|
767
|
+
/**
|
|
768
|
+
* All files that were serialised in this batch.
|
|
769
|
+
*/
|
|
648
770
|
files: Array<FileNode>
|
|
649
771
|
}
|
|
650
772
|
|
|
651
773
|
export type KubbHookStartContext = {
|
|
774
|
+
/**
|
|
775
|
+
* Optional identifier for correlating start/end events.
|
|
776
|
+
*/
|
|
652
777
|
id?: string
|
|
778
|
+
/**
|
|
779
|
+
* The shell command that is about to run.
|
|
780
|
+
*/
|
|
653
781
|
command: string
|
|
782
|
+
/**
|
|
783
|
+
* Parsed argument list, when available.
|
|
784
|
+
*/
|
|
654
785
|
args?: readonly string[]
|
|
655
786
|
}
|
|
656
787
|
|
|
657
788
|
export type KubbHookEndContext = {
|
|
789
|
+
/**
|
|
790
|
+
* Optional identifier matching the corresponding `kubb:hook:start` event.
|
|
791
|
+
*/
|
|
658
792
|
id?: string
|
|
793
|
+
/**
|
|
794
|
+
* The shell command that ran.
|
|
795
|
+
*/
|
|
659
796
|
command: string
|
|
797
|
+
/**
|
|
798
|
+
* Parsed argument list, when available.
|
|
799
|
+
*/
|
|
660
800
|
args?: readonly string[]
|
|
801
|
+
/**
|
|
802
|
+
* `true` when the command exited with code `0`.
|
|
803
|
+
*/
|
|
661
804
|
success: boolean
|
|
805
|
+
/**
|
|
806
|
+
* Error thrown by the command, or `null` on success.
|
|
807
|
+
*/
|
|
662
808
|
error: Error | null
|
|
663
809
|
}
|
|
664
810
|
|
|
@@ -666,9 +812,19 @@ export type KubbHookEndContext = {
|
|
|
666
812
|
* CLI options derived from command-line flags.
|
|
667
813
|
*/
|
|
668
814
|
export type CLIOptions = {
|
|
815
|
+
/**
|
|
816
|
+
* Path to the Kubb config file.
|
|
817
|
+
*/
|
|
669
818
|
config?: string
|
|
819
|
+
/**
|
|
820
|
+
* Re-run generation whenever input files change.
|
|
821
|
+
*/
|
|
670
822
|
watch?: boolean
|
|
671
|
-
/**
|
|
823
|
+
/**
|
|
824
|
+
* Controls how much output the CLI prints.
|
|
825
|
+
*
|
|
826
|
+
* @default 'silent'
|
|
827
|
+
*/
|
|
672
828
|
logLevel?: 'silent' | 'info' | 'debug'
|
|
673
829
|
}
|
|
674
830
|
|
|
@@ -689,31 +845,34 @@ type SetupOptions = {
|
|
|
689
845
|
*/
|
|
690
846
|
export type BuildOutput = {
|
|
691
847
|
/**
|
|
692
|
-
* Plugins that threw during
|
|
848
|
+
* Plugins that threw during generation, paired with their errors.
|
|
693
849
|
*/
|
|
694
850
|
failedPlugins: Set<{ plugin: Plugin; error: Error }>
|
|
851
|
+
/**
|
|
852
|
+
* All files generated during this build.
|
|
853
|
+
*/
|
|
695
854
|
files: Array<FileNode>
|
|
855
|
+
/**
|
|
856
|
+
* The plugin driver that orchestrated this build.
|
|
857
|
+
*/
|
|
696
858
|
driver: PluginDriver
|
|
697
859
|
/**
|
|
698
|
-
* Elapsed
|
|
860
|
+
* Elapsed milliseconds per plugin, keyed by plugin name.
|
|
699
861
|
*/
|
|
700
862
|
pluginTimings: Map<string, number>
|
|
863
|
+
/**
|
|
864
|
+
* Top-level error when the build threw before completing, otherwise `undefined`.
|
|
865
|
+
*/
|
|
701
866
|
error?: Error
|
|
702
867
|
/**
|
|
703
868
|
* Read-only view of every file written during this build.
|
|
704
|
-
*
|
|
705
|
-
* Keys are limited to this run. Reads go straight to `config.storage`,
|
|
706
|
-
* so nothing extra is held in memory.
|
|
869
|
+
* Reads go straight to `config.storage` — nothing extra is held in memory.
|
|
707
870
|
*
|
|
708
871
|
* @example Read a generated file
|
|
709
|
-
*
|
|
710
|
-
* const code = await buildOutput.storage.getItem('/src/gen/pet.ts')
|
|
711
|
-
* ```
|
|
872
|
+
* `const code = await buildOutput.storage.getItem('/src/gen/pet.ts')`
|
|
712
873
|
*
|
|
713
874
|
* @example List all generated file paths
|
|
714
|
-
*
|
|
715
|
-
* const paths = await buildOutput.storage.getKeys()
|
|
716
|
-
* ```
|
|
875
|
+
* `const paths = await buildOutput.storage.getKeys()`
|
|
717
876
|
*/
|
|
718
877
|
storage: Storage
|
|
719
878
|
}
|
|
@@ -778,6 +937,7 @@ type SetupResult = {
|
|
|
778
937
|
storage: Storage
|
|
779
938
|
config: Config
|
|
780
939
|
dispose: () => void
|
|
940
|
+
[Symbol.dispose](): void
|
|
781
941
|
}
|
|
782
942
|
|
|
783
943
|
/**
|
|
@@ -944,19 +1104,10 @@ async function setup(userConfig: UserConfig, options: SetupOptions = {}): Promis
|
|
|
944
1104
|
` • Operations: ${operationCount}`,
|
|
945
1105
|
],
|
|
946
1106
|
})
|
|
947
|
-
} else {
|
|
948
|
-
driver.inputNode = await config.adapter.parse(source)
|
|
949
|
-
|
|
950
|
-
await hooks.emit('kubb:debug', {
|
|
951
|
-
date: new Date(),
|
|
952
|
-
logs: [
|
|
953
|
-
`✓ Adapter '${config.adapter.name}' resolved InputNode`,
|
|
954
|
-
` • Schemas: ${driver.inputNode.schemas.length}`,
|
|
955
|
-
` • Operations: ${driver.inputNode.operations.length}`,
|
|
956
|
-
],
|
|
957
|
-
})
|
|
958
1107
|
}
|
|
959
|
-
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
if (!driver.inputStreamNode) {
|
|
960
1111
|
driver.inputNode = await config.adapter.parse(source)
|
|
961
1112
|
|
|
962
1113
|
await hooks.emit('kubb:debug', {
|
|
@@ -975,31 +1126,39 @@ async function setup(userConfig: UserConfig, options: SetupOptions = {}): Promis
|
|
|
975
1126
|
hooks,
|
|
976
1127
|
driver,
|
|
977
1128
|
storage,
|
|
978
|
-
dispose
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1129
|
+
dispose,
|
|
1130
|
+
[Symbol.dispose]: dispose,
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
function dispose() {
|
|
1134
|
+
driver.dispose()
|
|
1135
|
+
for (const [event, handler] of middlewareListeners) {
|
|
1136
|
+
hooks.off(event, handler as never)
|
|
1137
|
+
}
|
|
984
1138
|
}
|
|
985
1139
|
}
|
|
986
1140
|
|
|
987
|
-
/**
|
|
988
|
-
* Walks the AST and dispatches nodes to a plugin's direct AST hooks
|
|
989
|
-
* (`schema`, `operation`, `operations`).
|
|
990
|
-
*
|
|
991
|
-
* When `include` contains only operation-scoped filters (`tag`, `operationId`, `path`,
|
|
992
|
-
* `method`, `contentType`) and no `schemaName` filter, the function pre-computes the set
|
|
993
|
-
* of top-level schema names transitively reachable from the included operations and skips
|
|
994
|
-
* schemas that fall outside that set. This ensures that component schemas referenced
|
|
995
|
-
* exclusively by excluded operations are not generated.
|
|
996
|
-
*/
|
|
997
1141
|
type PluginStreamEntry = {
|
|
998
1142
|
plugin: NormalizedPlugin
|
|
999
1143
|
context: GeneratorContext
|
|
1000
1144
|
hrStart: ReturnType<typeof process.hrtime>
|
|
1001
1145
|
}
|
|
1002
1146
|
|
|
1147
|
+
type PluginState = {
|
|
1148
|
+
plugin: NormalizedPlugin
|
|
1149
|
+
generatorContext: GeneratorContext
|
|
1150
|
+
generators: Generator[]
|
|
1151
|
+
hrStart: ReturnType<typeof process.hrtime>
|
|
1152
|
+
failed: boolean
|
|
1153
|
+
error: Error | undefined
|
|
1154
|
+
/**
|
|
1155
|
+
* `true` when the plugin's options have no `include`, `exclude`, or `override`
|
|
1156
|
+
* filters. The per-node `resolveOptions` call always returns the same `options`
|
|
1157
|
+
* reference in that case, so the inner loop can skip it entirely.
|
|
1158
|
+
*/
|
|
1159
|
+
optionsAreStatic: boolean
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1003
1162
|
/**
|
|
1004
1163
|
* Single-pass fan-out for streaming mode.
|
|
1005
1164
|
*
|
|
@@ -1011,91 +1170,111 @@ type PluginStreamEntry = {
|
|
|
1011
1170
|
* Each plugin still gets independent `plugin:start` / `plugin:end` events and its own
|
|
1012
1171
|
* timing, but the schema and operation nodes are parsed only once total.
|
|
1013
1172
|
*/
|
|
1014
|
-
async function runPluginStreamHooks(
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
generatorContext: GeneratorContext
|
|
1027
|
-
generators: Generator[]
|
|
1028
|
-
hrStart: ReturnType<typeof process.hrtime>
|
|
1029
|
-
failed: boolean
|
|
1030
|
-
error: Error | undefined
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1173
|
+
async function runPluginStreamHooks({
|
|
1174
|
+
entries,
|
|
1175
|
+
driver,
|
|
1176
|
+
pluginTimings,
|
|
1177
|
+
failedPlugins,
|
|
1178
|
+
}: {
|
|
1179
|
+
entries: PluginStreamEntry[]
|
|
1180
|
+
driver: PluginDriver
|
|
1181
|
+
pluginTimings: Map<string, number>
|
|
1182
|
+
failedPlugins: Set<{ plugin: Plugin; error: Error }>
|
|
1183
|
+
}): Promise<void> {
|
|
1184
|
+
const inputStreamNode = driver.inputStreamNode!
|
|
1033
1185
|
function resolveRendererFor(gen: Generator, state: PluginState): RendererFactory | undefined {
|
|
1034
1186
|
return gen.renderer === null ? undefined : (gen.renderer ?? state.plugin.renderer ?? state.generatorContext.config.renderer)
|
|
1035
1187
|
}
|
|
1036
1188
|
|
|
1037
|
-
const states: PluginState[] = entries.map(({ plugin, context, hrStart }) =>
|
|
1038
|
-
plugin
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1189
|
+
const states: PluginState[] = entries.map(({ plugin, context, hrStart }) => {
|
|
1190
|
+
const { exclude, include, override } = plugin.options
|
|
1191
|
+
const hasExclude = Array.isArray(exclude) && exclude.length > 0
|
|
1192
|
+
const hasInclude = Array.isArray(include) && include.length > 0
|
|
1193
|
+
const hasOverride = Array.isArray(override) && override.length > 0
|
|
1194
|
+
return {
|
|
1195
|
+
plugin,
|
|
1196
|
+
generatorContext: { ...context, resolver: driver.getResolver(plugin.name) },
|
|
1197
|
+
generators: plugin.generators ?? [],
|
|
1198
|
+
hrStart,
|
|
1199
|
+
failed: false,
|
|
1200
|
+
error: undefined,
|
|
1201
|
+
optionsAreStatic: !hasExclude && !hasInclude && !hasOverride,
|
|
1202
|
+
}
|
|
1203
|
+
})
|
|
1045
1204
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
for (const state of states) {
|
|
1049
|
-
if (state.failed) continue
|
|
1050
|
-
try {
|
|
1051
|
-
const { plugin, generatorContext, generators } = state
|
|
1052
|
-
const { exclude, include, override } = plugin.options
|
|
1053
|
-
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
|
|
1054
|
-
const options = generatorContext.resolver.resolveOptions(transformedNode, { options: plugin.options, exclude, include, override })
|
|
1055
|
-
if (options === null) continue
|
|
1205
|
+
async function dispatchSchema(state: PluginState, node: SchemaNode): Promise<void> {
|
|
1206
|
+
if (state.failed) return
|
|
1056
1207
|
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1208
|
+
try {
|
|
1209
|
+
const { plugin, generatorContext, generators } = state
|
|
1210
|
+
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
|
|
1211
|
+
const { exclude, include, override } = plugin.options
|
|
1212
|
+
const options: typeof plugin.options | null = state.optionsAreStatic
|
|
1213
|
+
? plugin.options
|
|
1214
|
+
: generatorContext.resolver.resolveOptions(transformedNode, { options: plugin.options, exclude, include, override })
|
|
1215
|
+
|
|
1216
|
+
if (options === null) return
|
|
1217
|
+
|
|
1218
|
+
const ctx = { ...generatorContext, options }
|
|
1219
|
+
for (const gen of generators) {
|
|
1220
|
+
if (!gen.schema) continue
|
|
1221
|
+
|
|
1222
|
+
const raw = gen.schema(transformedNode, ctx)
|
|
1223
|
+
const result = isPromise(raw) ? await raw : raw
|
|
1224
|
+
const applied = applyHookResult({ result, driver, rendererFactory: resolveRendererFor(gen, state) })
|
|
1225
|
+
|
|
1226
|
+
if (isPromise(applied)) await applied
|
|
1067
1227
|
}
|
|
1228
|
+
await driver.hooks.emit('kubb:generate:schema', transformedNode, ctx)
|
|
1229
|
+
} catch (caughtError) {
|
|
1230
|
+
state.failed = true
|
|
1231
|
+
state.error = caughtError as Error
|
|
1068
1232
|
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
async function dispatchOperation(state: PluginState, node: OperationNode): Promise<void> {
|
|
1236
|
+
if (state.failed) return
|
|
1237
|
+
try {
|
|
1238
|
+
const { plugin, generatorContext, generators } = state
|
|
1239
|
+
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
|
|
1240
|
+
const { exclude, include, override } = plugin.options
|
|
1241
|
+
const options: typeof plugin.options | null = state.optionsAreStatic
|
|
1242
|
+
? plugin.options
|
|
1243
|
+
: generatorContext.resolver.resolveOptions(transformedNode, { options: plugin.options, exclude, include, override })
|
|
1244
|
+
|
|
1245
|
+
if (options === null) return
|
|
1246
|
+
|
|
1247
|
+
const ctx = { ...generatorContext, options }
|
|
1248
|
+
|
|
1249
|
+
for (const gen of generators) {
|
|
1250
|
+
if (!gen.operation) continue
|
|
1251
|
+
|
|
1252
|
+
const raw = gen.operation(transformedNode, ctx)
|
|
1253
|
+
const result = isPromise(raw) ? await raw : raw
|
|
1254
|
+
const applied = applyHookResult({ result, driver, rendererFactory: resolveRendererFor(gen, state) })
|
|
1255
|
+
|
|
1256
|
+
if (isPromise(applied)) await applied
|
|
1257
|
+
}
|
|
1258
|
+
await driver.hooks.emit('kubb:generate:operation', transformedNode, ctx)
|
|
1259
|
+
} catch (caughtError) {
|
|
1260
|
+
state.failed = true
|
|
1261
|
+
state.error = caughtError as Error
|
|
1072
1262
|
}
|
|
1073
1263
|
}
|
|
1074
1264
|
|
|
1265
|
+
for await (const node of inputStreamNode.schemas) {
|
|
1266
|
+
// Plugins are dispatched concurrently; per-plugin work (the inner generator
|
|
1267
|
+
// loop) stays sequential so `FileManager.upsert` ordering for any single
|
|
1268
|
+
// plugin chain remains deterministic.
|
|
1269
|
+
await Promise.all(states.map((state) => dispatchSchema(state, node)))
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1075
1272
|
const collectedOperations: OperationNode[] = []
|
|
1273
|
+
|
|
1076
1274
|
for await (const node of inputStreamNode.operations) {
|
|
1077
1275
|
collectedOperations.push(node)
|
|
1078
|
-
for (const state of states) {
|
|
1079
|
-
if (state.failed) continue
|
|
1080
|
-
try {
|
|
1081
|
-
const { plugin, generatorContext, generators } = state
|
|
1082
|
-
const { exclude, include, override } = plugin.options
|
|
1083
|
-
const transformedNode = plugin.transformer ? transform(node, plugin.transformer) : node
|
|
1084
|
-
const options = generatorContext.resolver.resolveOptions(transformedNode, { options: plugin.options, exclude, include, override })
|
|
1085
|
-
if (options === null) continue
|
|
1086
1276
|
|
|
1087
|
-
|
|
1088
|
-
for (const gen of generators) {
|
|
1089
|
-
if (!gen.operation) continue
|
|
1090
|
-
const result = await gen.operation(transformedNode, ctx)
|
|
1091
|
-
await applyHookResult(result, driver, resolveRendererFor(gen, state))
|
|
1092
|
-
}
|
|
1093
|
-
await driver.hooks.emit('kubb:generate:operation', transformedNode, ctx)
|
|
1094
|
-
} catch (caughtError) {
|
|
1095
|
-
state.failed = true
|
|
1096
|
-
state.error = caughtError as Error
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1277
|
+
await Promise.all(states.map((state) => dispatchOperation(state, node)))
|
|
1099
1278
|
}
|
|
1100
1279
|
|
|
1101
1280
|
// After stream: gen.operations for each plugin, then emit plugin:end
|
|
@@ -1104,11 +1283,13 @@ async function runPluginStreamHooks(
|
|
|
1104
1283
|
try {
|
|
1105
1284
|
const { plugin, generatorContext, generators } = state
|
|
1106
1285
|
const ctx = { ...generatorContext, options: plugin.options }
|
|
1286
|
+
|
|
1107
1287
|
for (const gen of generators) {
|
|
1108
1288
|
if (!gen.operations) continue
|
|
1109
1289
|
const result = await gen.operations(collectedOperations, ctx)
|
|
1110
|
-
await applyHookResult(result, driver, resolveRendererFor(gen, state))
|
|
1290
|
+
await applyHookResult({ result, driver, rendererFactory: resolveRendererFor(gen, state) })
|
|
1111
1291
|
}
|
|
1292
|
+
|
|
1112
1293
|
await driver.hooks.emit('kubb:generate:operations', collectedOperations, ctx)
|
|
1113
1294
|
} catch (caughtError) {
|
|
1114
1295
|
state.failed = true
|
|
@@ -1119,12 +1300,12 @@ async function runPluginStreamHooks(
|
|
|
1119
1300
|
const duration = getElapsedMs(state.hrStart)
|
|
1120
1301
|
pluginTimings.set(state.plugin.name, duration)
|
|
1121
1302
|
|
|
1122
|
-
await hooks.emit('kubb:plugin:end', {
|
|
1303
|
+
await driver.hooks.emit('kubb:plugin:end', {
|
|
1123
1304
|
plugin: state.plugin,
|
|
1124
1305
|
duration,
|
|
1125
1306
|
success: !state.failed,
|
|
1126
1307
|
...(state.failed && state.error ? { error: state.error } : {}),
|
|
1127
|
-
config,
|
|
1308
|
+
config: driver.config,
|
|
1128
1309
|
get files() {
|
|
1129
1310
|
return driver.fileManager.files
|
|
1130
1311
|
},
|
|
@@ -1135,15 +1316,23 @@ async function runPluginStreamHooks(
|
|
|
1135
1316
|
failedPlugins.add({ plugin: state.plugin, error: state.error })
|
|
1136
1317
|
}
|
|
1137
1318
|
|
|
1138
|
-
await hooks.emit('kubb:debug', {
|
|
1319
|
+
await driver.hooks.emit('kubb:debug', {
|
|
1139
1320
|
date: new Date(),
|
|
1140
1321
|
logs: [state.failed ? '✗ Plugin start failed' : `✓ Plugin started successfully (${formatMs(duration)})`],
|
|
1141
1322
|
})
|
|
1142
1323
|
}
|
|
1143
|
-
|
|
1144
|
-
await flushPendingFiles()
|
|
1145
1324
|
}
|
|
1146
1325
|
|
|
1326
|
+
/**
|
|
1327
|
+
* Walks the AST and dispatches nodes to a plugin's direct AST hooks
|
|
1328
|
+
* (`schema`, `operation`, `operations`).
|
|
1329
|
+
*
|
|
1330
|
+
* When `include` contains only operation-scoped filters (`tag`, `operationId`, `path`,
|
|
1331
|
+
* `method`, `contentType`) and no `schemaName` filter, the function pre-computes the set
|
|
1332
|
+
* of top-level schema names transitively reachable from the included operations and skips
|
|
1333
|
+
* schemas that fall outside that set. This ensures that component schemas referenced
|
|
1334
|
+
* exclusively by excluded operations are not generated.
|
|
1335
|
+
*/
|
|
1147
1336
|
async function runPluginAstHooks(plugin: NormalizedPlugin, context: GeneratorContext): Promise<void> {
|
|
1148
1337
|
const { adapter, inputNode, resolver, driver } = context
|
|
1149
1338
|
const { exclude, include, override } = plugin.options
|
|
@@ -1164,7 +1353,6 @@ async function runPluginAstHooks(plugin: NormalizedPlugin, context: GeneratorCon
|
|
|
1164
1353
|
resolver: driver.getResolver(plugin.name),
|
|
1165
1354
|
}
|
|
1166
1355
|
|
|
1167
|
-
// ── BATCH PATH ────────────────────────────────────────────────────────────
|
|
1168
1356
|
// When `include` has operation-based filters (tag, operationId, path, method, contentType)
|
|
1169
1357
|
// but no schema-level filters (schemaName), pre-compute the set of top-level schema names
|
|
1170
1358
|
// that are transitively referenced by the included operations. Schemas outside that set are
|
|
@@ -1173,11 +1361,11 @@ async function runPluginAstHooks(plugin: NormalizedPlugin, context: GeneratorCon
|
|
|
1173
1361
|
const hasOperationBasedIncludes = include?.some(({ type }) => operationFilterTypes.has(type)) ?? false
|
|
1174
1362
|
const hasSchemaNameIncludes = include?.some(({ type }) => type === 'schemaName') ?? false
|
|
1175
1363
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1364
|
+
const allowedSchemaNames: Set<string> | undefined = (() => {
|
|
1365
|
+
if (!hasOperationBasedIncludes || hasSchemaNameIncludes) return undefined
|
|
1178
1366
|
const includedOps = inputNode!.operations.filter((op) => resolver.resolveOptions(op, { options: plugin.options, exclude, include, override }) !== null)
|
|
1179
|
-
|
|
1180
|
-
}
|
|
1367
|
+
return collectUsedSchemaNames(includedOps, inputNode!.schemas)
|
|
1368
|
+
})()
|
|
1181
1369
|
|
|
1182
1370
|
await walk(inputNode!, {
|
|
1183
1371
|
depth: 'shallow',
|
|
@@ -1202,7 +1390,10 @@ async function runPluginAstHooks(plugin: NormalizedPlugin, context: GeneratorCon
|
|
|
1202
1390
|
await Promise.all(
|
|
1203
1391
|
generators
|
|
1204
1392
|
.filter((gen) => gen.schema)
|
|
1205
|
-
.map(
|
|
1393
|
+
.map(async (gen) => {
|
|
1394
|
+
const result = await gen.schema!(transformedNode, ctx)
|
|
1395
|
+
return applyHookResult({ result, driver, rendererFactory: resolveRenderer(gen) })
|
|
1396
|
+
}),
|
|
1206
1397
|
)
|
|
1207
1398
|
|
|
1208
1399
|
await driver.hooks.emit('kubb:generate:schema', transformedNode, ctx)
|
|
@@ -1215,19 +1406,22 @@ async function runPluginAstHooks(plugin: NormalizedPlugin, context: GeneratorCon
|
|
|
1215
1406
|
include,
|
|
1216
1407
|
override,
|
|
1217
1408
|
})
|
|
1218
|
-
if (options
|
|
1219
|
-
collectedOperations.push(transformedNode)
|
|
1409
|
+
if (options === null) return
|
|
1220
1410
|
|
|
1221
|
-
|
|
1411
|
+
collectedOperations.push(transformedNode)
|
|
1222
1412
|
|
|
1223
|
-
|
|
1224
|
-
generators
|
|
1225
|
-
.filter((gen) => gen.operation)
|
|
1226
|
-
.map((gen) => Promise.resolve(gen.operation!(transformedNode, ctx)).then((result) => applyHookResult(result, driver, resolveRenderer(gen)))),
|
|
1227
|
-
)
|
|
1413
|
+
const ctx = { ...generatorContext, options }
|
|
1228
1414
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1415
|
+
await Promise.all(
|
|
1416
|
+
generators
|
|
1417
|
+
.filter((gen) => gen.operation)
|
|
1418
|
+
.map(async (gen) => {
|
|
1419
|
+
const result = await gen.operation!(transformedNode, ctx)
|
|
1420
|
+
return applyHookResult({ result, driver, rendererFactory: resolveRenderer(gen) })
|
|
1421
|
+
}),
|
|
1422
|
+
)
|
|
1423
|
+
|
|
1424
|
+
await driver.hooks.emit('kubb:generate:operation', transformedNode, ctx)
|
|
1231
1425
|
},
|
|
1232
1426
|
})
|
|
1233
1427
|
|
|
@@ -1237,7 +1431,7 @@ async function runPluginAstHooks(plugin: NormalizedPlugin, context: GeneratorCon
|
|
|
1237
1431
|
for (const gen of generators) {
|
|
1238
1432
|
if (!gen.operations) continue
|
|
1239
1433
|
const result = await gen.operations(collectedOperations, ctx)
|
|
1240
|
-
await applyHookResult(result, driver, resolveRenderer(gen))
|
|
1434
|
+
await applyHookResult({ result, driver, rendererFactory: resolveRenderer(gen) })
|
|
1241
1435
|
}
|
|
1242
1436
|
|
|
1243
1437
|
await driver.hooks.emit('kubb:generate:operations', collectedOperations, ctx)
|
|
@@ -1245,6 +1439,7 @@ async function runPluginAstHooks(plugin: NormalizedPlugin, context: GeneratorCon
|
|
|
1245
1439
|
}
|
|
1246
1440
|
|
|
1247
1441
|
async function safeBuild(setupResult: SetupResult): Promise<BuildOutput> {
|
|
1442
|
+
using _cleanup = setupResult
|
|
1248
1443
|
const { driver, hooks, storage } = setupResult
|
|
1249
1444
|
|
|
1250
1445
|
const failedPlugins = new Set<{ plugin: Plugin; error: Error }>()
|
|
@@ -1261,8 +1456,8 @@ async function safeBuild(setupResult: SetupResult): Promise<BuildOutput> {
|
|
|
1261
1456
|
}
|
|
1262
1457
|
const fileProcessor = new FileProcessor()
|
|
1263
1458
|
|
|
1264
|
-
async function flushPendingFiles(
|
|
1265
|
-
const files = driver.fileManager.files.filter((f) => !writtenPaths.has(f.path)
|
|
1459
|
+
async function flushPendingFiles(): Promise<void> {
|
|
1460
|
+
const files = driver.fileManager.files.filter((f) => !writtenPaths.has(f.path))
|
|
1266
1461
|
if (files.length === 0) {
|
|
1267
1462
|
return
|
|
1268
1463
|
}
|
|
@@ -1276,7 +1471,7 @@ async function safeBuild(setupResult: SetupResult): Promise<BuildOutput> {
|
|
|
1276
1471
|
|
|
1277
1472
|
const stream = fileProcessor.stream(files, { parsers: parsersMap, extension: config.output.extension })
|
|
1278
1473
|
|
|
1279
|
-
for
|
|
1474
|
+
for (const { file, source, processed, total, percentage } of stream) {
|
|
1280
1475
|
await hooks.emit('kubb:file:processing:update', { file, source, processed, total, percentage, config })
|
|
1281
1476
|
if (source) {
|
|
1282
1477
|
await storage.setItem(file.path, source)
|
|
@@ -1309,7 +1504,6 @@ async function safeBuild(setupResult: SetupResult): Promise<BuildOutput> {
|
|
|
1309
1504
|
|
|
1310
1505
|
const inputStreamNode = driver.inputStreamNode
|
|
1311
1506
|
if (inputStreamNode) {
|
|
1312
|
-
// ── STREAMING: fan-out single-pass ────────────────────────────────────
|
|
1313
1507
|
// Emit plugin:start for all plugins up front, collect generator-plugins
|
|
1314
1508
|
// for the fan-out pass, then handle non-generator plugins immediately.
|
|
1315
1509
|
const streamPluginEntries: PluginStreamEntry[] = []
|
|
@@ -1326,32 +1520,32 @@ async function safeBuild(setupResult: SetupResult): Promise<BuildOutput> {
|
|
|
1326
1520
|
|
|
1327
1521
|
if (plugin.generators?.length || driver.hasRegisteredGenerators(plugin.name)) {
|
|
1328
1522
|
streamPluginEntries.push({ plugin, context, hrStart })
|
|
1329
|
-
|
|
1330
|
-
// No generators: plugin ran via setup hooks; finish it now.
|
|
1331
|
-
const duration = getElapsedMs(hrStart)
|
|
1332
|
-
pluginTimings.set(plugin.name, duration)
|
|
1333
|
-
await hooks.emit('kubb:plugin:end', {
|
|
1334
|
-
plugin,
|
|
1335
|
-
duration,
|
|
1336
|
-
success: true,
|
|
1337
|
-
config,
|
|
1338
|
-
get files() {
|
|
1339
|
-
return driver.fileManager.files
|
|
1340
|
-
},
|
|
1341
|
-
upsertFile: (...files) => driver.fileManager.upsert(...files),
|
|
1342
|
-
})
|
|
1343
|
-
await hooks.emit('kubb:debug', {
|
|
1344
|
-
date: new Date(),
|
|
1345
|
-
logs: [`✓ Plugin started successfully (${formatMs(duration)})`],
|
|
1346
|
-
})
|
|
1523
|
+
continue
|
|
1347
1524
|
}
|
|
1525
|
+
// No generators: plugin ran via setup hooks; finish it now.
|
|
1526
|
+
const duration = getElapsedMs(hrStart)
|
|
1527
|
+
pluginTimings.set(plugin.name, duration)
|
|
1528
|
+
await hooks.emit('kubb:plugin:end', {
|
|
1529
|
+
plugin,
|
|
1530
|
+
duration,
|
|
1531
|
+
success: true,
|
|
1532
|
+
config,
|
|
1533
|
+
get files() {
|
|
1534
|
+
return driver.fileManager.files
|
|
1535
|
+
},
|
|
1536
|
+
upsertFile: (...files) => driver.fileManager.upsert(...files),
|
|
1537
|
+
})
|
|
1538
|
+
await hooks.emit('kubb:debug', {
|
|
1539
|
+
date: new Date(),
|
|
1540
|
+
logs: [`✓ Plugin started successfully (${formatMs(duration)})`],
|
|
1541
|
+
})
|
|
1348
1542
|
}
|
|
1349
1543
|
|
|
1350
1544
|
if (streamPluginEntries.length > 0) {
|
|
1351
|
-
await runPluginStreamHooks(
|
|
1545
|
+
await runPluginStreamHooks({ entries: streamPluginEntries, driver, pluginTimings, failedPlugins })
|
|
1546
|
+
await flushPendingFiles()
|
|
1352
1547
|
}
|
|
1353
1548
|
} else {
|
|
1354
|
-
// ── BATCH: existing per-plugin sequential loop ────────────────────────
|
|
1355
1549
|
for (const plugin of driver.plugins.values()) {
|
|
1356
1550
|
const context = driver.getContext(plugin)
|
|
1357
1551
|
const hrStart = process.hrtime()
|
|
@@ -1455,8 +1649,6 @@ async function safeBuild(setupResult: SetupResult): Promise<BuildOutput> {
|
|
|
1455
1649
|
error: error as Error,
|
|
1456
1650
|
storage,
|
|
1457
1651
|
}
|
|
1458
|
-
} finally {
|
|
1459
|
-
setupResult.dispose()
|
|
1460
1652
|
}
|
|
1461
1653
|
}
|
|
1462
1654
|
|