@web/rollup-plugin-html
Version:
Rollup plugin for bundling HTML files
79 lines (69 loc) • 2.53 kB
text/typescript
import { findElements, getAttribute, getTagName, getTextContent, remove } from '@web/parse5-utils';
import { Document, Attribute } from 'parse5';
import path from 'path';
import crypto from 'crypto';
import { resolveAssetFilePath } from '../../assets/utils.js';
import { getAttributes } from '@web/parse5-utils';
import { ScriptModuleTag } from '../../RollupPluginHTMLOptions.js';
export interface ExtractModulesParams {
document: Document;
htmlDir: string;
rootDir: string;
absolutePathPrefix?: string;
}
function createContentHash(content: string) {
return crypto.createHash('md5').update(content).digest('hex');
}
function isAbsolute(src: string) {
try {
new URL(src);
return true;
} catch {
return false;
}
}
export function extractModules(params: ExtractModulesParams) {
const { document, htmlDir, rootDir, absolutePathPrefix } = params;
const scriptNodes = findElements(
document,
e => getTagName(e) === 'script' && getAttribute(e, 'type') === 'module',
);
const moduleImports: ScriptModuleTag[] = [];
const inlineModules: ScriptModuleTag[] = [];
for (const scriptNode of scriptNodes) {
const src = getAttribute(scriptNode, 'src');
const allAttributes = getAttributes(scriptNode);
const attributes: Attribute[] = [];
for (const attributeName of Object.keys(allAttributes)) {
if (attributeName !== 'src' && attributeName !== 'type') {
attributes.push({ name: attributeName, value: allAttributes[attributeName] });
}
}
if (!src) {
// turn inline module (<script type="module"> ...code ... </script>)
const code = getTextContent(scriptNode);
// inline modules should be relative to the HTML file to resolve relative imports
// we make it unique with a content hash, so that duplicate modules are deduplicated
const importPath = path.posix.join(htmlDir, `/inline-module-${createContentHash(code)}.js`);
if (!inlineModules.find(tag => tag.importPath === importPath)) {
inlineModules.push({
importPath,
attributes,
code,
});
}
remove(scriptNode);
} else {
if (!isAbsolute(src)) {
// external script <script type="module" src="./foo.js"></script>
const importPath = resolveAssetFilePath(src, htmlDir, rootDir, absolutePathPrefix);
moduleImports.push({
importPath,
attributes,
});
remove(scriptNode);
}
}
}
return { moduleImports, inlineModules };
}