11import { bold } from 'ansis' ;
2- import {
3- type Audit ,
4- type AuditOutput ,
5- type AuditOutputs ,
6- type AuditReport ,
7- type PluginConfig ,
8- type PluginReport ,
9- auditOutputsSchema ,
2+ import type {
3+ Audit ,
4+ AuditOutput ,
5+ AuditReport ,
6+ CacheConfig ,
7+ PersistConfig ,
8+ PluginConfig ,
9+ PluginReport ,
1010} from '@code-pushup/models' ;
1111import {
1212 type ProgressBar ,
@@ -15,29 +15,19 @@ import {
1515 logMultipleResults ,
1616 pluralizeToken ,
1717} from '@code-pushup/utils' ;
18- import { normalizeAuditOutputs } from '../normalize.js' ;
19- import { executeRunnerConfig , executeRunnerFunction } from './runner.js' ;
20-
21- /**
22- * Error thrown when plugin output is invalid.
23- */
24- export class PluginOutputMissingAuditError extends Error {
25- constructor ( auditSlug : string ) {
26- super (
27- `Audit metadata not present in plugin config. Missing slug: ${ bold (
28- auditSlug ,
29- ) } `,
30- ) ;
31- }
32- }
18+ import {
19+ executePluginRunner ,
20+ readRunnerResults ,
21+ writeRunnerResults ,
22+ } from './runner.js' ;
3323
3424/**
3525 * Execute a plugin.
3626 *
3727 * @public
3828 * @param pluginConfig - {@link ProcessConfig} object with runner and meta
3929 * @returns {Promise<AuditOutput[]> } - audit outputs from plugin runner
40- * @throws {PluginOutputMissingAuditError } - if plugin runner output is invalid
30+ * @throws {AuditOutputsMissingAuditError } - if plugin runner output is invalid
4131 *
4232 * @example
4333 * // plugin execution
@@ -54,7 +44,12 @@ export class PluginOutputMissingAuditError extends Error {
5444 */
5545export async function executePlugin (
5646 pluginConfig : PluginConfig ,
47+ opt : {
48+ cache : CacheConfig ;
49+ persist : Required < Pick < PersistConfig , 'outputDir' > > ;
50+ } ,
5751) : Promise < PluginReport > {
52+ const { cache, persist } = opt ;
5853 const {
5954 runner,
6055 audits : pluginConfigAudits ,
@@ -63,26 +58,25 @@ export async function executePlugin(
6358 groups,
6459 ...pluginMeta
6560 } = pluginConfig ;
66-
67- // execute plugin runner
68- const runnerResult =
69- typeof runner === 'object'
70- ? await executeRunnerConfig ( runner )
71- : await executeRunnerFunction ( runner ) ;
72- const { audits : unvalidatedAuditOutputs , ...executionMeta } = runnerResult ;
73-
74- // validate auditOutputs
75- const result = auditOutputsSchema . safeParse ( unvalidatedAuditOutputs ) ;
76- if ( ! result . success ) {
77- throw new Error ( `Audit output is invalid: ${ result . error . message } ` ) ;
61+ const { write : cacheWrite = false , read : cacheRead = false } = cache ;
62+ const { outputDir } = persist ;
63+
64+ const { audits, ...executionMeta } = cacheRead
65+ ? // IF not null, take the result from cache
66+ ( ( await readRunnerResults ( pluginMeta . slug , outputDir ) ) ??
67+ // ELSE execute the plugin runner
68+ ( await executePluginRunner ( pluginConfig ) ) )
69+ : await executePluginRunner ( pluginConfig ) ;
70+
71+ if ( cacheWrite ) {
72+ await writeRunnerResults ( pluginMeta . slug , outputDir , {
73+ ...executionMeta ,
74+ audits,
75+ } ) ;
7876 }
79- const auditOutputs = result . data ;
80- auditOutputsCorrelateWithPluginOutput ( auditOutputs , pluginConfigAudits ) ;
81-
82- const normalizedAuditOutputs = await normalizeAuditOutputs ( auditOutputs ) ;
8377
8478 // enrich `AuditOutputs` to `AuditReport`
85- const auditReports : AuditReport [ ] = normalizedAuditOutputs . map (
79+ const auditReports : AuditReport [ ] = audits . map (
8680 ( auditOutput : AuditOutput ) => ( {
8781 ...auditOutput ,
8882 ...( pluginConfigAudits . find (
@@ -103,13 +97,18 @@ export async function executePlugin(
10397}
10498
10599const wrapProgress = async (
106- pluginCfg : PluginConfig ,
100+ cfg : {
101+ plugin : PluginConfig ;
102+ persist : Required < Pick < PersistConfig , 'outputDir' > > ;
103+ cache : CacheConfig ;
104+ } ,
107105 steps : number ,
108106 progressBar : ProgressBar | null ,
109107) => {
108+ const { plugin : pluginCfg , ...rest } = cfg ;
110109 progressBar ?. updateTitle ( `Executing ${ bold ( pluginCfg . title ) } ` ) ;
111110 try {
112- const pluginReport = await executePlugin ( pluginCfg ) ;
111+ const pluginReport = await executePlugin ( pluginCfg , rest ) ;
113112 progressBar ?. incrementInSteps ( steps ) ;
114113 return pluginReport ;
115114 } catch ( error ) {
@@ -146,15 +145,24 @@ const wrapProgress = async (
146145 *
147146 */
148147export async function executePlugins (
149- plugins : PluginConfig [ ] ,
148+ cfg : {
149+ plugins : PluginConfig [ ] ;
150+ persist : Required < Pick < PersistConfig , 'outputDir' > > ;
151+ cache : CacheConfig ;
152+ } ,
150153 options ?: { progress ?: boolean } ,
151154) : Promise < PluginReport [ ] > {
155+ const { plugins, ...cacheCfg } = cfg ;
152156 const { progress = false } = options ?? { } ;
153157
154158 const progressBar = progress ? getProgressBar ( 'Run plugins' ) : null ;
155159
156160 const pluginsResult = plugins . map ( pluginCfg =>
157- wrapProgress ( pluginCfg , plugins . length , progressBar ) ,
161+ wrapProgress (
162+ { plugin : pluginCfg , ...cacheCfg } ,
163+ plugins . length ,
164+ progressBar ,
165+ ) ,
158166 ) ;
159167
160168 const errorsTransform = ( { reason } : PromiseRejectedResult ) => String ( reason ) ;
@@ -179,17 +187,3 @@ export async function executePlugins(
179187
180188 return fulfilled . map ( result => result . value ) ;
181189}
182-
183- function auditOutputsCorrelateWithPluginOutput (
184- auditOutputs : AuditOutputs ,
185- pluginConfigAudits : PluginConfig [ 'audits' ] ,
186- ) {
187- auditOutputs . forEach ( auditOutput => {
188- const auditMetadata = pluginConfigAudits . find (
189- audit => audit . slug === auditOutput . slug ,
190- ) ;
191- if ( ! auditMetadata ) {
192- throw new PluginOutputMissingAuditError ( auditOutput . slug ) ;
193- }
194- } ) ;
195- }
0 commit comments