webpack-license-plugin
Version:
Extracts OSS license information of the npm packages in your webpack output
161 lines (142 loc) • 5.45 kB
text/typescript
import type { Chunk, Compilation, Compiler } from 'webpack'
import type IPluginOptions from './types/IPluginOptions'
import type IWebpackPlugin from './types/IWebpackPlugin'
import webpack from 'webpack'
import LicenseFileWriter from './LicenseFileWriter'
import LicenseMetaAggregator from './LicenseMetaAggregator'
import ModuleDirectoryLocator from './ModuleDirectoryLocator'
import OptionsProvider from './OptionsProvider'
import PackageJsonReader from './PackageJsonReader'
import WebpackAlertAggregator from './WebpackAlertAggregator'
import WebpackAssetManager from './WebpackAssetManager'
import WebpackChunkIterator from './WebpackChunkIterator'
import WebpackFileSystem from './WebpackFileSystem'
const WebpackError = webpack.WebpackError
const pluginName = 'WebpackLicensePlugin'
interface ObservedCompiler {
name: string
isChild: boolean
}
export default class WebpackLicensePlugin implements IWebpackPlugin {
private readonly filenames = new Set<string>()
private createdFiles = false
private observedCompilers: ObservedCompiler[] = []
constructor(private pluginOptions: Partial<IPluginOptions> = {}) {}
public apply(compiler: Compiler) {
if (typeof compiler.hooks !== 'undefined') {
compiler.hooks.compilation.tap(
'webpack-license-plugin',
this.handleCompilation.bind(this, compiler),
)
compiler.hooks.watchRun.tapAsync(
'webpack-license-plugin',
this.handleWatchRun.bind(this),
)
}
// @ts-expect-error plugin doesn't exist on compiler
else if (typeof compiler.plugin !== 'undefined') {
// @ts-expect-error plugin doesn't exist on compiler
compiler.plugin(
'compilation',
this.handleCompilation.bind(this, compiler),
)
// @ts-expect-error plugin doesn't exist on compiler
compiler.plugin('watchRun', this.handleWatchRun.bind(this))
}
}
public async handleWatchRun(_: unknown, callback: () => void) {
this.createdFiles = false
this.observedCompilers = []
callback()
}
public handleCompilation(compiler: Compiler, compilation: Compilation) {
if (typeof compilation.hooks !== 'undefined') {
if (typeof compilation.hooks.processAssets !== 'undefined') {
const boundHandleChunkAssetOptimization
= this.handleChunkAssetOptimization.bind(
this,
compiler,
compilation,
compilation.chunks,
)
compilation.hooks.processAssets.tapAsync(
{
name: 'webpack-license-plugin',
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ANALYSE,
},
(assets, callback) => boundHandleChunkAssetOptimization(callback),
)
}
else {
compilation.hooks.optimizeChunkAssets.tapAsync(
'webpack-license-plugin',
this.handleChunkAssetOptimization.bind(this, compiler, compilation),
)
}
}
// @ts-expect-error plugin doesn't exist on compilation
else if (typeof compilation.plugin !== 'undefined') {
// @ts-expect-error plugin doesn't exist on compilation
compilation.plugin(
'optimize-chunk-assets',
this.handleChunkAssetOptimization.bind(this, compiler, compilation),
)
}
}
public async handleChunkAssetOptimization(
compiler: Compiler,
compilation: Compilation,
chunks: Set<Chunk>,
callback: () => void,
) {
this.observedCompilers.push({
name: compilation.compiler.name,
isChild: compilation.compiler.isChild(),
})
if (this.createdFiles) {
const observedCompilersMessage = this.observedCompilers
.map(({ name, isChild }) => `compiler: ${name}, isChild: ${isChild}`)
.join('\n')
const errorMessage = new WebpackError(
`${pluginName}: Found licenses after license files were already created.\nIf you see this message, you ran into an edge case we thought would not happen. Please open an isssue at https://github.com/codepunkt/webpack-license-plugin/issues with details of your webpack configuration so we can invastigate it further.\n${observedCompilersMessage}`,
)
compilation.errors.push(errorMessage)
callback()
return
}
if (!compilation.compiler.isChild()) {
this.createdFiles = true
}
const alertAggregator = new WebpackAlertAggregator(compilation)
const optionsProvider = new OptionsProvider(alertAggregator)
const options = optionsProvider.getOptions(this.pluginOptions)
alertAggregator.flushAlerts(pluginName)
const chunkIterator = new WebpackChunkIterator()
for (const filename of chunkIterator.iterateChunks(compilation, chunks)) {
this.filenames.add(filename)
}
if (compilation.compiler.isChild()) {
callback()
return
}
const fileSystem = new WebpackFileSystem(compiler.inputFileSystem)
const packageJsonReader = new PackageJsonReader(fileSystem)
const licenseFileWriter = new LicenseFileWriter(
new WebpackAssetManager(compilation),
new ModuleDirectoryLocator(
fileSystem,
compiler.options.context,
packageJsonReader,
),
new LicenseMetaAggregator(
fileSystem,
alertAggregator,
options,
packageJsonReader,
),
)
await licenseFileWriter.writeLicenseFiles([...this.filenames], options)
alertAggregator.flushAlerts(pluginName)
callback()
}
}