WIP: Basic working version. Will import .html files and resolve its dependencies. But it will generate invalid html when sourcemaps are enabled, assets like <link..> do not yet work...
This commit is contained in:
parent
240d5cfe9a
commit
831e607591
@ -1,5 +1,6 @@
|
|||||||
# Remove me:
|
# Remove me:
|
||||||
TODO: This started as a fork of, but is now something different entirely. Changelog is no longer relevant (neither is the [README.md](README.md))
|
TODO: This started as a fork of, but is now something different entirely. Changelog is no longer relevant (neither is the [README.md](README.md))
|
||||||
|
rollup-plugin-css was used in the initial tests, but it hasnt been update in **7** years. Remove this
|
||||||
|
|
||||||
# @rollup/plugin-html ChangeLog
|
# @rollup/plugin-html ChangeLog
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
"ci:coverage": "nyc pnpm test && nyc report --reporter=text-lcov > coverage.lcov",
|
"ci:coverage": "nyc pnpm test && nyc report --reporter=text-lcov > coverage.lcov",
|
||||||
"ci:lint": "pnpm build && pnpm lint-staged",
|
"ci:lint": "pnpm build && pnpm lint-staged",
|
||||||
"ci:test": "pnpm test -- --verbose",
|
"ci:test": "pnpm test -- --verbose",
|
||||||
"dev-test": "ava --match='handlebars*'"
|
"dev-test": "ava --match='handlebars*' --update-snapshots"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
@ -94,7 +94,8 @@
|
|||||||
"js": true
|
"js": true
|
||||||
},
|
},
|
||||||
"nodeArguments": [
|
"nodeArguments": [
|
||||||
"--loader=ts-node/esm"
|
"--loader=ts-node/esm",
|
||||||
|
"--experimental-vm-modules"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
76
src/index.ts
76
src/index.ts
@ -7,7 +7,9 @@ import type {
|
|||||||
OutputAsset,
|
OutputAsset,
|
||||||
NormalizedOutputOptions,
|
NormalizedOutputOptions,
|
||||||
// ModuleInfo,
|
// ModuleInfo,
|
||||||
ResolvedId, PreRenderedChunk
|
ResolvedId,
|
||||||
|
PreRenderedChunk,
|
||||||
|
RenderedChunk,
|
||||||
} from 'rollup';
|
} from 'rollup';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
@ -18,6 +20,7 @@ import type {
|
|||||||
} from '../types/index.d.ts';
|
} from '../types/index.d.ts';
|
||||||
import {createFilter} from '@rollup/pluginutils';
|
import {createFilter} from '@rollup/pluginutils';
|
||||||
import {parse as parseHtml, serialize as serializeHtml, DefaultTreeAdapterMap} from "parse5";
|
import {parse as parseHtml, serialize as serializeHtml, DefaultTreeAdapterMap} from "parse5";
|
||||||
|
// import {Script, SourceTextModule, createContext} from "node:vm";
|
||||||
|
|
||||||
const getFiles = (bundle: OutputBundle): Record<string, (OutputChunk | OutputAsset)[]> => {
|
const getFiles = (bundle: OutputBundle): Record<string, (OutputChunk | OutputAsset)[]> => {
|
||||||
const result = {} as ReturnType<typeof getFiles>;
|
const result = {} as ReturnType<typeof getFiles>;
|
||||||
@ -88,6 +91,7 @@ type HtmlModule = {
|
|||||||
// TODO might want to impose an own unique id, in case this changes after multiple builds
|
// TODO might want to impose an own unique id, in case this changes after multiple builds
|
||||||
id: string,
|
id: string,
|
||||||
resolved: HtmlImport[];
|
resolved: HtmlImport[];
|
||||||
|
document: DefaultTreeAdapterMap['document'],
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
||||||
@ -139,18 +143,19 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
|||||||
async handler(code: string, id: string){
|
async handler(code: string, id: string){
|
||||||
if(!filter(id)) return;
|
if(!filter(id)) return;
|
||||||
|
|
||||||
const handled : HtmlModule = {
|
|
||||||
id,
|
|
||||||
resolved: [],
|
|
||||||
};
|
|
||||||
handledHtmls.set(id, handled);
|
|
||||||
|
|
||||||
const htmlSrc = transform? await transform(code, {
|
const htmlSrc = transform? await transform(code, {
|
||||||
id,
|
id,
|
||||||
}) : code;
|
}) : code;
|
||||||
|
|
||||||
const document = parseHtml(htmlSrc);
|
const document = parseHtml(htmlSrc);
|
||||||
|
|
||||||
|
const handled : HtmlModule = {
|
||||||
|
id,
|
||||||
|
resolved: [],
|
||||||
|
document,
|
||||||
|
};
|
||||||
|
handledHtmls.set(id, handled);
|
||||||
|
|
||||||
// Figure out which references to load from this HTML by iterating all nodes (looking for src or href attributes)
|
// Figure out which references to load from this HTML by iterating all nodes (looking for src or href attributes)
|
||||||
let loadResults : { reference: LoadReference, node: DefaultTreeAdapterMap['element'] }[] = [];
|
let loadResults : { reference: LoadReference, node: DefaultTreeAdapterMap['element'] }[] = [];
|
||||||
if(document.childNodes){
|
if(document.childNodes){
|
||||||
@ -199,7 +204,7 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
|||||||
if(resolvedId){
|
if(resolvedId){
|
||||||
const rollupResolved = await this.resolve(resolvedId, id, {
|
const rollupResolved = await this.resolve(resolvedId, id, {
|
||||||
skipSelf: true,
|
skipSelf: true,
|
||||||
isEntry: true, // TODO: for href/src tags, this is probably the right option. For anything that is to be inlined into the HTML... probably no
|
isEntry: true, // TODO: for href/src tags, this is probably the right option. For anything that is to be inlined into the HTML... probably not
|
||||||
});
|
});
|
||||||
// TODO: should we check if this is refused for resolving here. i.e. external?
|
// TODO: should we check if this is refused for resolving here. i.e. external?
|
||||||
const htmlImport: HtmlImport = {
|
const htmlImport: HtmlImport = {
|
||||||
@ -216,6 +221,7 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
|||||||
index,
|
index,
|
||||||
};
|
};
|
||||||
if(htmlImport.referenceId) {
|
if(htmlImport.referenceId) {
|
||||||
|
// only used when importing from javascript
|
||||||
reference.set(`\${import.meta.ROLLUP_FILE_URL_${htmlImport.referenceId}\}`);
|
reference.set(`\${import.meta.ROLLUP_FILE_URL_${htmlImport.referenceId}\}`);
|
||||||
}
|
}
|
||||||
handled.resolved.push(htmlImport);
|
handled.resolved.push(htmlImport);
|
||||||
@ -227,8 +233,7 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
|||||||
// Transform to JS
|
// Transform to JS
|
||||||
const serialized = serializeHtml(document);
|
const serialized = serializeHtml(document);
|
||||||
const jsModule = [
|
const jsModule = [
|
||||||
//...resolveResults.map(x=>`import * as dep${x.index} from "${x.id}";`),
|
// This will only make sense when importing from javascript
|
||||||
// ...handled.resolved.map(x=>`import("${x.id}");`),// Inject as a dynamic import. We need to remove these before outputting // todo better solution to mark the ids as dependencies of this bundle...
|
|
||||||
`export const html = \`${serialized.replaceAll(/`/g,'\\\`')}\`;`,
|
`export const html = \`${serialized.replaceAll(/`/g,'\\\`')}\`;`,
|
||||||
`export default html;`
|
`export default html;`
|
||||||
].join('\n');
|
].join('\n');
|
||||||
@ -236,39 +241,24 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
|||||||
return {code: jsModule};
|
return {code: jsModule};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async generateBundle(output: NormalizedOutputOptions, bundle: OutputBundle) {
|
async renderChunk(
|
||||||
const files = getFiles(bundle);
|
code: string,
|
||||||
console.log("must output?!", output, bundle, files);
|
chunk: RenderedChunk,
|
||||||
|
options: NormalizedOutputOptions,
|
||||||
|
meta: { chunks: Record<string, RenderedChunk> }
|
||||||
|
){
|
||||||
|
const htmlModule = chunk.facadeModuleId ? handledHtmls.get(chunk.facadeModuleId!) : null;
|
||||||
|
if(htmlModule){
|
||||||
|
let html = '';
|
||||||
|
for(const htmlImport of htmlModule.resolved){
|
||||||
|
if(htmlImport.referenceId) {
|
||||||
|
const fileName = this.getFileName(htmlImport.referenceId);
|
||||||
|
htmlImport.reference.set(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
html = serializeHtml(htmlModule.document);// This might contain temporary hashes, but it should be alright
|
||||||
|
return {code: html};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// async generateBundle(output: NormalizedOutputOptions, bundle: OutputBundle) {
|
|
||||||
// if (!supportedFormats.includes(output.format) && !opts.transform) {
|
|
||||||
// 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 htmlFile: EmittedAsset = {
|
|
||||||
// type: 'asset',
|
|
||||||
// source,
|
|
||||||
// name: 'Rollup HTML Asset',
|
|
||||||
// fileName
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// this.emitFile(htmlFile);
|
|
||||||
// }
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
<html>
|
<html>
|
||||||
|
<head>
|
||||||
<meta data-test="{{a}}"/>
|
<meta data-test="{{a}}"/>
|
||||||
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script src="./batman.js" type="module"></script>
|
<script src="./batman.js" type="module"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -10,29 +10,65 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
code: `(function (factory) {␊
|
code: `<html><head>␊
|
||||||
typeof define === 'function' && define.amd ? define(factory) :␊
|
<meta data-test="a">␊
|
||||||
factory();␊
|
</head>␊
|
||||||
})((function () { 'use strict';␊
|
<body>␊
|
||||||
␊=-u
|
<script src="batman-f8ac73ff.js" type="module"></script>␊
|
||||||
␊
|
␊
|
||||||
␊
|
␊
|
||||||
}));␊
|
</body></html>␊
|
||||||
|
//# sourceMappingURL=index.html.map␊
|
||||||
`,
|
`,
|
||||||
fileName: 'batman.js',
|
fileName: 'index.html',
|
||||||
map: null,
|
map: SourceMap {
|
||||||
|
file: 'index.html',
|
||||||
|
mappings: '',
|
||||||
|
names: [],
|
||||||
|
sources: [],
|
||||||
|
sourcesContent: [],
|
||||||
|
version: 3,
|
||||||
|
},
|
||||||
|
source: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: `const notSoIifi = ()=>{␊
|
||||||
|
return \`I'm "annoying" ${"in case we need to test \\\`string\\\` escaping.''"}\`;␊
|
||||||
|
};␊
|
||||||
|
console.log(notSoIifi());␊
|
||||||
|
␊
|
||||||
|
export { notSoIifi };␊
|
||||||
|
//# sourceMappingURL=batman-f8ac73ff.js.map␊
|
||||||
|
`,
|
||||||
|
fileName: 'batman-f8ac73ff.js',
|
||||||
|
map: SourceMap {
|
||||||
|
file: 'batman-f8ac73ff.js',
|
||||||
|
mappings: 'AAAY,MAAC,SAAS,GAAG,IAAI;AAC7B,IAAI,OAAO,CAAC,eAAe,EAAE,gDAAgD,CAAC,CAAC,CAAC;AAChF,EAAC;AACD,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;;;;',
|
||||||
|
names: [],
|
||||||
|
sources: [
|
||||||
|
'../batman.js',
|
||||||
|
],
|
||||||
|
sourcesContent: [
|
||||||
|
`export const notSoIifi = ()=>{␊
|
||||||
|
return \`I'm "annoying" ${"in case we need to test \\\`string\\\` escaping.''"}\`;␊
|
||||||
|
}␊
|
||||||
|
console.log(notSoIifi());␊
|
||||||
|
`,
|
||||||
|
],
|
||||||
|
version: 3,
|
||||||
|
},
|
||||||
source: undefined,
|
source: undefined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: undefined,
|
code: undefined,
|
||||||
fileName: 'index.html',
|
fileName: 'batman-f8ac73ff.js.map',
|
||||||
map: undefined,
|
map: undefined,
|
||||||
source: `<html>␊
|
source: '{"version":3,"file":"batman-f8ac73ff.js","sources":["../batman.js"],"sourcesContent":["export const notSoIifi = ()=>{\\n return `I\'m \\"annoying\\" ${\\"in case we need to test \\\\`string\\\\` escaping.\'\'\\"}`;\\n}\\nconsole.log(notSoIifi());\\n"],"names":[],"mappings":"AAAY,MAAC,SAAS,GAAG,IAAI;AAC7B,IAAI,OAAO,CAAC,eAAe,EAAE,gDAAgD,CAAC,CAAC,CAAC;AAChF,EAAC;AACD,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;;;;"}',
|
||||||
<meta data-test="a"/>␊
|
},
|
||||||
<body>␊
|
{
|
||||||
<script src="./batman.js" type="module"></script>␊
|
code: undefined,
|
||||||
</body>␊
|
fileName: 'index.html.map',
|
||||||
</html>␊
|
map: undefined,
|
||||||
`,
|
source: '{"version":3,"file":"index.html","sources":[],"sourcesContent":[],"names":[],"mappings":""}',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
Binary file not shown.
@ -8,7 +8,11 @@ import { getCode } from "../util/test.js";
|
|||||||
import html from "../../src/index.ts";
|
import html from "../../src/index.ts";
|
||||||
import handlebars from "handlebars";
|
import handlebars from "handlebars";
|
||||||
|
|
||||||
const output = { dir: 'output', format: 'es' };
|
const output = {
|
||||||
|
dir: 'output', // Output all files
|
||||||
|
format: 'es', // iifi and cjs should be added to tests
|
||||||
|
sourcemap: true,// Test if #sourcemapUrl is not accidentally included in the html-output
|
||||||
|
};
|
||||||
|
|
||||||
import {readFile} from "node:fs/promises";
|
import {readFile} from "node:fs/promises";
|
||||||
import {fileURLToPath} from "node:url";
|
import {fileURLToPath} from "node:url";
|
||||||
|
|||||||
@ -4,8 +4,9 @@
|
|||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"noEmitOnError": false,
|
"noEmitOnError": false,
|
||||||
"noUnusedLocals": true,
|
// Surpress errors about unused stuff: doesn't solve any bugs and is just annyoing during development, leave warning about this to the IDE or linters
|
||||||
"noUnusedParameters": true,
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
"pretty": true,
|
"pretty": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user