import * as path from 'path' ; import * as fs from 'fs' ; import { findUp } from '@angular/cli/utilities/find-up' ; import { removeMiniCssExtractRules } from './webpack-5/remove-mini-css-extract' ; export interface DefaultExtraOptions { removeMiniCssExtract : boolean ; } const defaultExtraOptions = { removeMiniCssExtract : true , } ; interface Options { customWebpackConfig ? : { libraryName ? : string ; libraryTarget ? : string ; } ; main ? : string ; sourceMap ? : boolean | { scripts ? : boolean ; vendor ? : boolean ; } ; } export default ( config , options ? , extraOptions ? ) => { const libraryName = getLibraryName ( options ) ; extraOptions = { ... defaultExtraOptions , ... extraOptions } ; const singleSpaConfig = { output : { library : libraryName , libraryTarget : options ?. customWebpackConfig ?. libraryTarget ?? 'umd' , chunkLoadingGlobal : 'webpackJsonp' + libraryName , devtoolNamespace : libraryName , } , externals : [ 'zone.js' ] , devServer : { historyApiFallback : false , contentBase : path . resolve ( process . cwd ( ) , 'src' ) , headers : { 'Access-Control-Allow-Headers' : '*' , } , } , module : { rules : [ { } , ] , } , devtool : resolveDevtool ( options ) , } ; const mergedConfig = mergeConfigs ( config , singleSpaConfig ) ; if ( mergedConfig . output . libraryTarget === 'system' ) { delete mergedConfig . output . library ; } removePluginByName ( mergedConfig . plugins , 'IndexHtmlWebpackPlugin' ) ; if ( extraOptions . removeMiniCssExtract ) { removeMiniCssExtract ( mergedConfig ) ; } if ( Array . isArray ( mergedConfig . entry . styles ) ) { mergedConfig . entry . main = [ ... mergedConfig . entry . styles , ... mergedConfig . entry . main ] ; } if ( mergedConfig . entry [ 'polyfills-es5' ] ) { delete mergedConfig . entry [ 'polyfills-es5' ] ; } delete mergedConfig . entry . polyfills ; delete mergedConfig . entry . styles ; delete mergedConfig . optimization . runtimeChunk ; delete mergedConfig . optimization . splitChunks ; return mergedConfig ; } ; function removePluginByName ( plugins , name ) { const pluginIndex = plugins . findIndex ( plugin => plugin . constructor . name === name ) ; if ( pluginIndex > - 1 ) { plugins . splice ( pluginIndex , 1 ) ; } } function removeMiniCssExtract ( config ) { removeMiniCssExtractRules ( config ) ; removePluginByName ( config . plugins , 'MiniCssExtractPlugin' ) ; } function getLibraryName ( options ) { if ( options ?. customWebpackConfig ?. libraryName ) { return options . customWebpackConfig . libraryName ; } const projectName = getProjectNameFromAngularJson ( options ) ; if ( projectName ) return projectName ; console . warn ( 'Warning: single-spa-angular could not determine a library name to use and has used a default value.' ) ; console . info ( 'This may cause issues if this app uses code-splitting or lazy loading.' ) ; if ( ! options ) { console . info ( 'You may also need to update extra-webpack.config.json.' ) ; } console . info ( 'See <https://single-spa.js.org/docs/ecosystem-angular/#use-custom-webpack> for information on how to resolve this.' ) ; return 'angular_single_spa_project' ; } function getProjectNameFromAngularJson ( options ) { const angularJsonPath = findUp ( [ 'angular.json' , '.angular.json' , 'workspace.json' ] , process . cwd ( ) ) ; if ( ! angularJsonPath ) return null ; const angularJson = JSON . parse ( fs . readFileSync ( angularJsonPath , 'utf8' ) ) ; if ( ! angularJson . projects ) return null ; const projects = Object . keys ( angularJson . projects ) ; if ( projects . length === 1 ) return projects [ 0 ] ; try { return projects . find ( project => angularJson . projects [ project ] . architect . build . options . main === options ! . main ) ; } catch { return null ; } } function resolveDevtool ( options ) { const allSourceMapsEnabled = options ?. sourceMap === true ; const scriptsSourceMapsEnabled = typeof options ?. sourceMap === 'object' && options . sourceMap . scripts === true ; if ( allSourceMapsEnabled || scriptsSourceMapsEnabled ) { return 'source-map' ; } else { return false ; } } function mergeConfigs ( config , singleSpaConfig ) { const webpackMerge = require ( 'webpack-merge' ) ; try { return webpackMerge . smart ( config , singleSpaConfig ) ; } catch { return webpackMerge . default ( config , singleSpaConfig ) ; } }