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+ CacheConfigObject ,
7+ PersistConfig ,
8+ PluginConfig ,
9+ PluginReport ,
1010} from '@code-pushup/models' ;
1111import {
1212 type ProgressBar ,
@@ -15,29 +15,20 @@ 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
29+ * @param opt
3930 * @returns {Promise<AuditOutput[]> } - audit outputs from plugin runner
40- * @throws {PluginOutputMissingAuditError } - if plugin runner output is invalid
31+ * @throws {AuditOutputsMissingAuditError } - if plugin runner output is invalid
4132 *
4233 * @example
4334 * // plugin execution
@@ -54,7 +45,12 @@ export class PluginOutputMissingAuditError extends Error {
5445 */
5546export async function executePlugin (
5647 pluginConfig : PluginConfig ,
48+ opt : {
49+ cache : CacheConfigObject ;
50+ persist : Required < Pick < PersistConfig , 'outputDir' > > ;
51+ } ,
5752) : Promise < PluginReport > {
53+ const { cache, persist } = opt ;
5854 const {
5955 runner,
6056 audits : pluginConfigAudits ,
@@ -63,26 +59,25 @@ export async function executePlugin(
6359 groups,
6460 ...pluginMeta
6561 } = 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 } ` ) ;
62+ const { write : cacheWrite = false , read : cacheRead = false } = cache ;
63+ const { outputDir } = persist ;
64+
65+ const { audits, ...executionMeta } = cacheRead
66+ ? // IF not null, take the result from cache
67+ ( ( await readRunnerResults ( pluginMeta . slug , outputDir ) ) ??
68+ // ELSE execute the plugin runner
69+ ( await executePluginRunner ( pluginConfig ) ) )
70+ : await executePluginRunner ( pluginConfig ) ;
71+
72+ if ( cacheWrite ) {
73+ await writeRunnerResults ( pluginMeta . slug , outputDir , {
74+ ...executionMeta ,
75+ audits,
76+ } ) ;
7877 }
79- const auditOutputs = result . data ;
80- auditOutputsCorrelateWithPluginOutput ( auditOutputs , pluginConfigAudits ) ;
81-
82- const normalizedAuditOutputs = await normalizeAuditOutputs ( auditOutputs ) ;
8378
8479 // enrich `AuditOutputs` to `AuditReport`
85- const auditReports : AuditReport [ ] = normalizedAuditOutputs . map (
80+ const auditReports : AuditReport [ ] = audits . map (
8681 ( auditOutput : AuditOutput ) => ( {
8782 ...auditOutput ,
8883 ...( pluginConfigAudits . find (
@@ -103,13 +98,18 @@ export async function executePlugin(
10398}
10499
105100const wrapProgress = async (
106- pluginCfg : PluginConfig ,
101+ cfg : {
102+ plugin : PluginConfig ;
103+ persist : Required < Pick < PersistConfig , 'outputDir' > > ;
104+ cache : CacheConfigObject ;
105+ } ,
107106 steps : number ,
108107 progressBar : ProgressBar | null ,
109108) => {
109+ const { plugin : pluginCfg , ...rest } = cfg ;
110110 progressBar ?. updateTitle ( `Executing ${ bold ( pluginCfg . title ) } ` ) ;
111111 try {
112- const pluginReport = await executePlugin ( pluginCfg ) ;
112+ const pluginReport = await executePlugin ( pluginCfg , rest ) ;
113113 progressBar ?. incrementInSteps ( steps ) ;
114114 return pluginReport ;
115115 } catch ( error ) {
@@ -127,7 +127,7 @@ const wrapProgress = async (
127127/**
128128 * Execute multiple plugins and aggregates their output.
129129 * @public
130- * @param plugins array of { @link PluginConfig} objects
130+ * @param cfg
131131 * @param {Object } [options] execution options
132132 * @param {boolean } options.progress show progress bar
133133 * @returns {Promise<PluginReport[]> } plugin report
@@ -146,15 +146,24 @@ const wrapProgress = async (
146146 *
147147 */
148148export async function executePlugins (
149- plugins : PluginConfig [ ] ,
149+ cfg : {
150+ plugins : PluginConfig [ ] ;
151+ persist : Required < Pick < PersistConfig , 'outputDir' > > ;
152+ cache : CacheConfigObject ;
153+ } ,
150154 options ?: { progress ?: boolean } ,
151155) : Promise < PluginReport [ ] > {
156+ const { plugins, ...cacheCfg } = cfg ;
152157 const { progress = false } = options ?? { } ;
153158
154159 const progressBar = progress ? getProgressBar ( 'Run plugins' ) : null ;
155160
156161 const pluginsResult = plugins . map ( pluginCfg =>
157- wrapProgress ( pluginCfg , plugins . length , progressBar ) ,
162+ wrapProgress (
163+ { plugin : pluginCfg , ...cacheCfg } ,
164+ plugins . length ,
165+ progressBar ,
166+ ) ,
158167 ) ;
159168
160169 const errorsTransform = ( { reason } : PromiseRejectedResult ) => String ( reason ) ;
@@ -179,17 +188,3 @@ export async function executePlugins(
179188
180189 return fulfilled . map ( result => result . value ) ;
181190}
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