1- import { isFile } from '@metamask/snaps-utils/node' ;
1+ import { handlerEndowments } from '@metamask/snaps-rpc-methods' ;
2+ import { checkManifest , isFile } from '@metamask/snaps-utils/node' ;
3+ import { writeManifest } from '@metamask/snaps-webpack-plugin' ;
24import { assert } from '@metamask/utils' ;
3- import { resolve as pathResolve } from 'path' ;
5+ import { red , reset , yellow } from 'chalk' ;
6+ import { readFile } from 'fs/promises' ;
7+ import { dirname , resolve as pathResolve } from 'path' ;
48
59import { build } from './implementation' ;
610import { getBundleAnalyzerPort } from './utils' ;
711import type { ProcessedConfig } from '../../config' ;
812import { CommandError } from '../../errors' ;
913import type { Steps } from '../../utils' ;
10- import { success , executeSteps , info } from '../../utils' ;
14+ import { error , success , executeSteps , info , warn } from '../../utils' ;
15+ import { formatError } from '../../webpack/utils' ;
1116import { evaluate } from '../eval' ;
1217
1318export type BuildContext = {
1419 analyze : boolean ;
1520 build : boolean ;
1621 config : ProcessedConfig ;
1722 port ?: number ;
23+ exports ?: string [ ] ;
1824} ;
1925
2026export const steps : Steps < BuildContext > = [
2127 {
2228 name : 'Checking the input file.' ,
23- condition : ( { build } ) => build ,
29+ condition : ( { build : enableBuild } ) => enableBuild ,
2430 task : async ( { config } ) => {
2531 const { input } = config ;
2632
2733 if ( ! ( await isFile ( input ) ) ) {
2834 throw new CommandError (
29- `Input file not found: "${ input } ". Make sure that the "input" field in your snap config is correct.` ,
35+ `Input file not found: "${ input } ". Make sure that the "input" field in your Snap config is correct.` ,
3036 ) ;
3137 }
3238 } ,
3339 } ,
3440 {
35- name : 'Building the snap bundle.' ,
36- condition : ( { build } ) => build ,
41+ name : 'Building the Snap bundle.' ,
42+ condition : ( { build : enableBuild } ) => enableBuild ,
3743 task : async ( { analyze, build : enableBuild , config, spinner } ) => {
3844 // We don't evaluate the bundle here, because it's done in a separate
3945 // step.
@@ -57,18 +63,86 @@ export const steps: Steps<BuildContext> = [
5763 } ,
5864 } ,
5965 {
60- name : 'Evaluating the snap bundle.' ,
61- condition : ( { build, config } ) => build && config . evaluate ,
62- task : async ( { config, spinner } ) => {
66+ name : 'Evaluating the Snap bundle.' ,
67+ condition : ( { build : enableBuild , config } ) =>
68+ enableBuild && config . evaluate ,
69+ task : async ( context ) => {
70+ const { config, spinner } = context ;
6371 const path = pathResolve (
6472 process . cwd ( ) ,
6573 config . output . path ,
6674 config . output . filename ,
6775 ) ;
6876
69- await evaluate ( path ) ;
77+ const { exports } = await evaluate ( path ) ;
7078
7179 info ( `Snap bundle evaluated successfully.` , spinner ) ;
80+
81+ return {
82+ ...context ,
83+ exports,
84+ } ;
85+ } ,
86+ } ,
87+
88+ // TODO: Share this between the `build` and `manifest` commands.
89+ {
90+ name : 'Validating the Snap manifest.' ,
91+ condition : ( { config } ) => config . evaluate ,
92+ task : async ( { config, exports, spinner } ) => {
93+ const bundlePath = pathResolve (
94+ process . cwd ( ) ,
95+ config . output . path ,
96+ config . output . filename ,
97+ ) ;
98+
99+ const { reports } = await checkManifest ( dirname ( config . manifest . path ) , {
100+ updateAndWriteManifest : config . manifest . update ,
101+ sourceCode : await readFile ( bundlePath , 'utf-8' ) ,
102+ exports,
103+ handlerEndowments,
104+ writeFileFn : async ( path , data ) => {
105+ return writeManifest ( path , data ) ;
106+ } ,
107+ } ) ;
108+
109+ // TODO: Use `Object.groupBy` when available.
110+ const errors = reports
111+ . filter ( ( report ) => report . severity === 'error' && ! report . wasFixed )
112+ . map ( ( report ) => report . message ) ;
113+ const warnings = reports
114+ . filter ( ( report ) => report . severity === 'warning' && ! report . wasFixed )
115+ . map ( ( report ) => report . message ) ;
116+ const fixed = reports
117+ . filter ( ( report ) => report . wasFixed )
118+ . map ( ( report ) => report . message ) ;
119+
120+ if ( errors . length > 0 ) {
121+ error (
122+ `The following errors were found in the manifest:\n\n${ errors
123+ . map ( ( value ) => formatError ( value , '' , red ) )
124+ . join ( '\n\n' ) } \n`,
125+ spinner ,
126+ ) ;
127+ }
128+
129+ if ( warnings . length > 0 ) {
130+ warn (
131+ `The following warnings were found in the manifest:\n\n${ warnings
132+ . map ( ( value ) => formatError ( value , '' , yellow ) )
133+ . join ( '\n\n' ) } \n`,
134+ spinner ,
135+ ) ;
136+ }
137+
138+ if ( fixed . length > 0 ) {
139+ info (
140+ `The following issues were fixed in the manifest:\n\n${ reset (
141+ fixed . map ( ( value ) => formatError ( value , '' , reset ) ) . join ( '\n\n' ) ,
142+ ) } \n`,
143+ spinner ,
144+ ) ;
145+ }
72146 } ,
73147 } ,
74148 {
0 commit comments