import path from 'path' ; import fs from 'fs/promises' ; import { compact , difference , uniq } from 'lodash' ; import webpack , { Configuration , Stats } from 'webpack' ; import { logger , pMap , prepareEnvironment , readPackageConfig } from '@reskript/core' ; import { createWebpackConfig , collectEntries , createRuntimeBuildEnv , checkProjectSettings , BuildContext , EntryLocation , } from '@reskript/config-webpack' ; import { readProjectSettings , BuildEnv , ProjectSettings , strictCheckRequiredDependency } from '@reskript/settings' ; import * as partials from './partial' ; import { BuildCommandLineArgs } from './interface' ; import { drawFeatureMatrix , drawBuildReport , printWebpackResult , WebpackResult } from './report' ; import inspect from './inspect' ; export { BuildCommandLineArgs } ; const build = ( configuration ) : Promise < Stats > => { const executor = ( resolve ) => webpack ( configuration as Configuration , ( err ? , stats ? ) => { if ( err ) { logger . error ( err . toString ( ) ) ; process . exit ( 22 ) ; } if ( ! stats ) { logger . error ( 'Unknown error: webpack does not return its build stats' ) ; process . exit ( 22 ) ; } const toJsonOptions = { all : false , errors : true , warnings : true , assets : true } ; const { errors , warnings } = stats . toJson ( toJsonOptions ) ; for ( const error of uniq ( errors ) ) { printWebpackResult ( 'error' , error as unknown as WebpackResult ) ; } for ( const warning of uniq ( warnings ) ) { printWebpackResult ( 'warn' , warning as unknown as WebpackResult ) ; } if ( stats . hasErrors ( ) ) { process . exit ( 22 ) ; } resolve ( stats ) ; } ) ; return new Promise ( executor ) ; } ; const createConfigurations = async ( cmd , projectSettings ) => { const featureNames = difference ( Object . keys ( projectSettings . featureMatrix ) , projectSettings . build . excludeFeatures ) ; if ( cmd . featureOnly && ! featureNames . includes ( cmd . featureOnly ) ) { logger . error ( `Feature ${ cmd . featureOnly } is not configured in reskript.config.js` ) ; process . exit ( 21 ) ; } if ( cmd . analyze && ! cmd . buildTarget ) { logger . error ( '--analyze must be used with --build-target to specify only one target' ) ; process . exit ( 21 ) ; } checkProjectSettings ( projectSettings ) ; drawFeatureMatrix ( projectSettings , cmd . featureOnly ) ; const { name : hostPackageName } = await readPackageConfig ( cmd . cwd ) ; const entryLocation = { cwd : cmd . cwd , srcDirectory : cmd . srcDirectory , entryDirectory : cmd . entriesDirectory , only : cmd . entriesOnly , } ; const entries = await collectEntries ( entryLocation ) ; const featureNamesToUse = cmd . featureOnly ? [ cmd . featureOnly ] : featureNames ; const toConfiguration = async ( featureName ) : Promise < Configuration > => { const buildEnv = { hostPackageName , projectSettings , usage : 'build' , mode : cmd . mode , cwd : cmd . cwd , srcDirectory : cmd . srcDirectory , cacheDirectory : cmd . cacheDirectory , } ; const runtimeBuildEnv = await createRuntimeBuildEnv ( buildEnv ) ; const buildContext = { ... runtimeBuildEnv , entries , features : projectSettings . featureMatrix [ featureName ] , buildTarget : featureName , isDefaultTarget : featureName === cmd . buildTarget || featureName === cmd . featureOnly , } ; const extras = [ cmd . analyze && partials . analyze ( buildContext ) , cmd . profile && partials . profile ( ) , ! cmd . sourceMaps && partials . noSourceMaps ( ) , ] ; return createWebpackConfig ( buildContext , { strict : { disableRequireExtension : cmd . strict , caseSensitiveModuleSource : cmd . strict , typeCheck : cmd . strict , } , extras : compact ( extras ) , } ) ; } ; return pMap ( featureNamesToUse , toConfiguration ) ; } ; export const run = async ( cmd ) : Promise < void > => { process . env . NODE_ENV = cmd . mode ; await prepareEnvironment ( cmd . cwd , cmd . mode ) ; if ( cmd . clean ) { await fs . rm ( path . join ( cmd . cwd , 'dist' ) , { recursive : true , force : true } ) ; } const projectSettings = await readProjectSettings ( cmd , 'build' ) ; await strictCheckRequiredDependency ( projectSettings , cmd . cwd ) ; const [ initial , ... configurations ] = await createConfigurations ( cmd , projectSettings ) ; if ( ! initial ) { const error = 'No build configuration created, you are possibly providing a feature matrix with dev only' ; logger . error ( error ) ; process . exit ( 21 ) ; } const initialStats = await build ( [ initial ] ) ; const stats = ! ! configurations . length && await build ( configurations ) ; drawBuildReport ( stats ? [ initialStats , stats ] : [ initialStats ] ) ; logger . lineBreak ( ) ; await inspect ( initialStats , projectSettings . build . inspect , { cwd : cmd . cwd , exitOnError : ! cmd . analyze } ) ; } ;