import { extname } from "node:path"; import type { Plugin, NormalizedOutputOptions, OutputBundle, EmittedAsset } from 'rollup'; import type { RollupHtmlOptions, RollupHtmlTemplateOptions } from '../types/index.d.ts'; const getFiles = (bundle: OutputBundle): RollupHtmlTemplateOptions['files'] => { const result = {} as ReturnType; for (const file of Object.values(bundle)) { const { fileName } = file; const extension = extname(fileName).substring(1); result[extension] = (result[extension] || []).concat(file); } return result; }; export const makeHtmlAttributes = (attributes: Record): string => { if (!attributes) { return ''; } const keys = Object.keys(attributes); // eslint-disable-next-line no-param-reassign return keys.reduce((result, key) => (result += ` ${key}="${attributes[key]}"`), ''); }; const defaultTemplate = async ({ attributes, files, meta, publicPath, title }: RollupHtmlTemplateOptions) => { const scripts = (files.js || []) .map(({ fileName }) => { const attrs = makeHtmlAttributes(attributes.script); return ``; }) .join('\n'); const links = (files.css || []) .map(({ fileName }) => { const attrs = makeHtmlAttributes(attributes.link); return ``; }) .join('\n'); const metas = meta .map((input) => { const attrs = makeHtmlAttributes(input); return ``; }) .join('\n'); return ` ${metas} ${title} ${links} ${scripts} `; }; const supportedFormats = ['es', 'esm', 'iife', 'umd']; const defaults = { attributes: { link: null, html: { lang: 'en' }, script: null }, fileName: 'index.html', meta: [{ charset: 'utf-8' }], publicPath: '', template: defaultTemplate, title: 'Rollup Bundle' }; export default function html(opts: RollupHtmlOptions = {}): Plugin { const { attributes, fileName, meta, publicPath, template, title } = Object.assign( {}, defaults, opts ); return { name: 'html', async generateBundle(output: NormalizedOutputOptions, bundle: OutputBundle) { if (!supportedFormats.includes(output.format) && !opts.template) { this.warn( `plugin-html: The output format '${ output.format }' is not directly supported. A custom \`template\` is probably required. Supported formats include: ${supportedFormats.join( ', ' )}` ); } if (output.format === 'es') { attributes.script = Object.assign({}, attributes.script, { type: 'module' }); } const files = getFiles(bundle); const source = await template({ attributes, bundle, files, meta, publicPath, title }); const htmlFile: EmittedAsset = { type: 'asset', source, name: 'Rollup HTML Asset', fileName }; this.emitFile(htmlFile); } }; }