UNPKG

webpack-inject-plugin

Version:

A webpack plugin to dynamically inject code into the bundle.

139 lines (111 loc) 3.51 kB
import { randomBytes } from 'crypto'; import path from 'path'; import { Compiler, Entry, EntryFunc } from 'webpack'; type EntryType = string | string[] | Entry | EntryFunc; type EntryFilterFunction = (entryName: string) => boolean; type EntryFilterType = string | EntryFilterFunction; const FAKE_LOADER_NAME = 'webpack-inject-plugin.loader'; export type Loader = () => string; export const registry: { [key: string]: Loader; } = {}; let uniqueIDCounter = 0; function getUniqueID() { const id = (++uniqueIDCounter).toString(16); return `webpack-inject-module-${id}`; } export const enum ENTRY_ORDER { First = 1, Last, NotLast } export interface IInjectOptions { entryName?: EntryFilterType; entryOrder?: ENTRY_ORDER; loaderID?: string; } function injectToArray( originalEntry: string[], newEntry: string, entryOrder = ENTRY_ORDER.NotLast ): string[] { if (entryOrder === ENTRY_ORDER.First) { return [newEntry, ...originalEntry]; } if (entryOrder === ENTRY_ORDER.Last) { return [...originalEntry, newEntry]; } return [ ...originalEntry.splice(0, originalEntry.length - 1), newEntry, ...originalEntry.splice(originalEntry.length - 1) ]; } function createEntryFilter( filterOption?: EntryFilterType ): EntryFilterFunction { if (filterOption === null || filterOption === undefined) { return () => true; } if (typeof filterOption === 'string') { return (entryName: string) => filterOption === entryName; } if (typeof filterOption === 'function') { return filterOption; } throw new Error(`Unknown entry filter: ${typeof filterOption}`); } export function injectEntry( originalEntry: EntryType | undefined, newEntry: string, options: IInjectOptions ): EntryType { if (originalEntry === undefined) { return newEntry; } const filterFunc = createEntryFilter(options.entryName); // Last module in an array gets exported, so the injected one must not be // last. https://webpack.github.io/docs/configuration.html#entry if (typeof originalEntry === 'string') { return injectToArray([originalEntry], newEntry, options.entryOrder); } if (Array.isArray(originalEntry)) { return injectToArray(originalEntry, newEntry, options.entryOrder); } if (originalEntry === Object(originalEntry)) { return Object.entries(originalEntry).reduce( (a: Record<string, EntryType>, [key, entry]) => { if (filterFunc(key)) { a[key] = injectEntry(entry, newEntry, options); } else { a[key] = entry; } return a; }, {} ); } return originalEntry; } export default class WebpackInjectPlugin { private readonly options: IInjectOptions; private readonly loader: Loader; constructor(loader: Loader, options?: IInjectOptions) { this.loader = loader; this.options = { entryName: (options && options.entryName) || undefined, entryOrder: (options && options.entryOrder) || ENTRY_ORDER.NotLast, loaderID: (options && options.loaderID) || getUniqueID() }; } apply(compiler: Compiler) { const id = this.options.loaderID!; const newEntry = path.resolve(__dirname, `${FAKE_LOADER_NAME}?id=${id}!`); registry[id] = this.loader; compiler.options.entry = injectEntry( compiler.options.entry, newEntry, this.options ); } }