import path from 'path' ; import fs from 'fs/promises' ; import { uniq , compact , flatMap , last } from 'lodash' ; import { StatsCompilation } from 'webpack' ; import { BuildInspectSettings , SourceFilter } from '@reskript/settings' ; import { RuleProcessor , isIncluded } from './utils' ; const extractScriptEntries = ( compilations ) : string [ ] => { const entries = flatMap ( compilations , c => Object . values ( c . entrypoints ?? { } ) ) ; return uniq ( compact ( entries . map ( v => last ( v . assets ) ?. name ) ) ) ; } ; const extractHTMLEntries = ( compilations ) : string [ ] => { const assets = flatMap ( compilations , c => c . assets ?? [ ] ) ; const files = assets . filter ( v => /^\.\.\/.+\.html$/ . test ( v . name ) ) . map ( v => v . name ) ; return files ; } ; const checkHtml = async ( file , allowedScripts , report , cwd ) => { const content = await fs . readFile ( path . join ( cwd , 'dist' , 'assets' , file ) , 'utf-8' ) ; const lastScript = [ ... content . matchAll ( /<script.+src="(.+)">/g ) ] . pop ( ) ; if ( ! lastScript ) { return true ; } const src = lastScript [ 1 ] ; if ( allowedScripts . some ( v => src . includes ( v ) ) ) { return true ; } const message = `The last script in ${ file . replace ( /^\.\.\// , '' ) } doesn't reference to an entry script, ` + 'this can break micro-frontend frameworks like qiankun' ; report ( message ) ; } ; export default ( compilations , settings ) => { const processor = { config : settings , defaultConfigValue : { } , check : async ( { includes , excludes } , { report } , { cwd } ) => { const entryScripts = extractScriptEntries ( compilations ) ; const htmlFiles = extractHTMLEntries ( compilations ) . filter ( v => isIncluded ( v , includes , excludes ) ) ; const results = await Promise . all ( htmlFiles . map ( v => checkHtml ( v , entryScripts , report , cwd ) ) ) ; return results . every ( v => v ) ; } , } ; return processor ; } ;