feat: initial support for importing html from js
This commit is contained in:
parent
afd4a3c9ae
commit
71a377417d
71
src/index.ts
71
src/index.ts
@ -77,7 +77,7 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
||||
let filter = createFilter(include, exclude, {});
|
||||
|
||||
// TODO, we need to clear all these properly at sme point to avoid odd bugs in watch mode
|
||||
let htmlModules = new Map<string, HtmlModule>();// todo clean this per new build?
|
||||
// let htmlModules = new Map<string, HtmlModule>();// todo clean this per new build?
|
||||
let virtualSources = new Map<string, string>();
|
||||
let addedEntries = new Map<string, string>();
|
||||
let entryNames = new Map<string,string>();
|
||||
@ -129,22 +129,29 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
||||
const moduleId = resolved.id;
|
||||
const moduleExt = extname(resolved.id);
|
||||
const moduleName = specifier.replace(new RegExp(`${moduleExt}\$`),''); // strip extension of the name if any
|
||||
const htmlModule : HtmlModule = htmlModules.get(moduleId) ?? {
|
||||
id: resolved.id,
|
||||
name: moduleName,
|
||||
imports: [],
|
||||
assetId: null,
|
||||
importers: new Set(),
|
||||
};
|
||||
htmlModule.importers.add(importer);
|
||||
// const htmlModule : HtmlModule = htmlModules.get(moduleId) ?? {
|
||||
// id: resolved.id,
|
||||
// name: moduleName,
|
||||
// imports: [],
|
||||
// assetId: null,
|
||||
// importers: new Set(),
|
||||
// };
|
||||
// htmlModule.importers.add(importer);
|
||||
// htmlModules.set(htmlModule.id, htmlModule);
|
||||
|
||||
htmlModules.set(htmlModule.id, htmlModule);
|
||||
// TODO: trigger special handling when imported from a JS file (in which case we want might want to export a module returning the HTML, instead of HTML directly)
|
||||
return {
|
||||
...resolved,
|
||||
meta: {
|
||||
...resolved.meta,
|
||||
[pluginName]: {name: specifier}
|
||||
[pluginName]: {
|
||||
specifier: specifier,
|
||||
id: resolved.id,
|
||||
name: moduleName,
|
||||
imports: [],
|
||||
assetId: null,
|
||||
importers: new Set(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -158,14 +165,30 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
||||
}
|
||||
},
|
||||
transform: {
|
||||
order: 'pre',
|
||||
async handler(...args){
|
||||
const [code, id] = args;
|
||||
if (!filter(id)) return;
|
||||
|
||||
// parse
|
||||
const htmlModule = htmlModules.get(id);
|
||||
if(htmlModule) {
|
||||
const contents = await readFile(id, {encoding: "utf-8"});
|
||||
const moduleInfo = this.getModuleInfo(id);
|
||||
const moduleMeta = moduleInfo!.meta ?? {};
|
||||
let htmlModule = moduleMeta[pluginName];
|
||||
if(!htmlModule){
|
||||
const moduleExt = extname(id);
|
||||
const moduleName = id.replace(new RegExp(`${moduleExt}\$`),''); // strip extension of the name if any
|
||||
htmlModule = moduleMeta[pluginName] = {
|
||||
id: id,
|
||||
name: moduleName,
|
||||
imports: [],
|
||||
assetId: null,
|
||||
importers: new Set(),
|
||||
}
|
||||
}
|
||||
// const htmlModule = htmlModules.get(id);
|
||||
|
||||
// const contents = await readFile(id, {encoding: "utf-8"});
|
||||
const contents = code;
|
||||
|
||||
const htmlSrc = transform ? await transform(contents, {
|
||||
id,
|
||||
@ -288,15 +311,17 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
||||
return {
|
||||
code: htmlJS.toString(),
|
||||
map: map.toString(),
|
||||
meta: moduleMeta,
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
outputOptions(options){
|
||||
return {
|
||||
...options,
|
||||
entryFileNames: (chunkInfo)=>{
|
||||
const htmlModule = chunkInfo.facadeModuleId ? htmlModules.get(chunkInfo.facadeModuleId!) : null;
|
||||
const moduleInfo = chunkInfo.facadeModuleId? this.getModuleInfo(chunkInfo.facadeModuleId) : null;
|
||||
const htmlModule = moduleInfo?.meta?.[pluginName];
|
||||
// const htmlModule = chunkInfo.facadeModuleId ? htmlModules.get(chunkInfo.facadeModuleId!) : null;
|
||||
const addedEntry = chunkInfo.facadeModuleId ? addedEntries.get(chunkInfo.facadeModuleId!) : null;
|
||||
const defaultOption = options.entryFileNames ?? "[name]-[hash].js";// This default is copied from the docs. TODO: don't like overwrite it this way, can we remove the need for this or fetch the true default?
|
||||
if(htmlModule){
|
||||
@ -313,7 +338,9 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
||||
}
|
||||
},
|
||||
resolveFileUrl(options){
|
||||
const htmlModule = htmlModules.get(options.moduleId);
|
||||
// const htmlModule = htmlModules.get(options.moduleId);
|
||||
const moduleInfo = this.getModuleInfo(options.moduleId);
|
||||
const htmlModule = moduleInfo?.meta?.[pluginName];
|
||||
if(htmlModule){
|
||||
// Simply use the relative path in our HTML-fileURLs instead of the default `new URL('${fileName}', document.baseURI).href`)
|
||||
return `"${options.relativePath}"`;
|
||||
@ -324,7 +351,9 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
||||
order:'post',
|
||||
handler(chunk: RenderedChunk){
|
||||
if(chunk.facadeModuleId) {
|
||||
const htmlModule = htmlModules.get(chunk.facadeModuleId);
|
||||
const moduleInfo = chunk.facadeModuleId? this.getModuleInfo(chunk.facadeModuleId) : null;
|
||||
const htmlModule = moduleInfo?.meta?.[pluginName];
|
||||
// const htmlModule = htmlModules.get(chunk.facadeModuleId);
|
||||
if (htmlModule) {
|
||||
return modulePrefix; // Overwrite any added banner with our own
|
||||
}
|
||||
@ -342,7 +371,11 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
||||
const chunk = (<OutputChunk>bundle);
|
||||
if(chunk.facadeModuleId) {
|
||||
facadeToChunk.set(chunk.facadeModuleId, chunk);
|
||||
const htmlModule = htmlModules.get(chunk.facadeModuleId);
|
||||
|
||||
const moduleInfo = this.getModuleInfo(chunk.facadeModuleId);
|
||||
const htmlModule = moduleInfo?.meta?.[pluginName];
|
||||
// const htmlModule = htmlModules.get(chunk.facadeModuleId);
|
||||
|
||||
if(htmlModule){ htmlResults.set(bundleName, {chunk, htmlModule})}
|
||||
else if(virtualSources.has(chunk.facadeModuleId)){
|
||||
virtualBundles.add(bundleName);
|
||||
|
||||
@ -58,6 +58,12 @@ Generated by [AVA](https://avajs.dev).
|
||||
> Snapshot 1
|
||||
|
||||
[
|
||||
{
|
||||
code: undefined,
|
||||
fileName: 'script.body.script.js-e3b82208.js.map',
|
||||
map: undefined,
|
||||
source: '{"version":3,"file":"script.body.script.js-e3b82208.js","sources":["../batman.js","../script.html.body.script.js"],"sourcesContent":["export const b = ()=>\'batman\';\\nconsole.log(b());\\n","\\n import {b} from \\"./batman.js\\";\\n document.body.appendChild(\\n document.createTextNode(`Inline script including ${b()}`)\\n );\\n "],"names":[],"mappings":"AAAO,MAAM,CAAC,GAAG,IAAI,QAAQ,CAAC;AAC9B,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;;ACCJ,QAAQ,CAAC,IAAI,CAAC,WAAW;AACrC,gBAAgB,QAAQ,CAAC,cAAc,CAAC,CAAC,wBAAwB,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AACzE,aAAa"}',
|
||||
},
|
||||
{
|
||||
code: undefined,
|
||||
fileName: 'script.html',
|
||||
@ -71,16 +77,10 @@ Generated by [AVA](https://avajs.dev).
|
||||
document.body.appendChild(␊
|
||||
document.createTextNode(\`Inline script including ${b()}\`)␊
|
||||
);␊
|
||||
//# sourceMappingURL=script.html.body.script.js-e3b82208.js.map␊
|
||||
//# sourceMappingURL=script.body.script.js-e3b82208.js.map␊
|
||||
</script>␊
|
||||
␊
|
||||
␊
|
||||
</body></html>`,
|
||||
},
|
||||
{
|
||||
code: undefined,
|
||||
fileName: 'script.html.body.script.js-e3b82208.js.map',
|
||||
map: undefined,
|
||||
source: '{"version":3,"file":"script.html.body.script.js-e3b82208.js","sources":["../batman.js","../script.html.body.script.js"],"sourcesContent":["export const b = ()=>\'batman\';\\nconsole.log(b());\\n","\\n import {b} from \\"./batman.js\\";\\n document.body.appendChild(\\n document.createTextNode(`Inline script including ${b()}`)\\n );\\n "],"names":[],"mappings":"AAAO,MAAM,CAAC,GAAG,IAAI,QAAQ,CAAC;AAC9B,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;;ACCJ,QAAQ,CAAC,IAAI,CAAC,WAAW;AACrC,gBAAgB,QAAQ,CAAC,cAAc,CAAC,CAAC,wBAAwB,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AACzE,aAAa"}',
|
||||
},
|
||||
]
|
||||
|
||||
Binary file not shown.
2
test/js-import/fixtures/batman.js
Normal file
2
test/js-import/fixtures/batman.js
Normal file
@ -0,0 +1,2 @@
|
||||
export const b = ()=>'batman';
|
||||
console.log(b());
|
||||
3
test/js-import/fixtures/icon.svg
Normal file
3
test/js-import/fixtures/icon.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<path style="fill:none;stroke:#00ff0d;stroke-width:5;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" d="M4.1 14.72 16 26.31 28.38 5.09"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 244 B |
9
test/js-import/fixtures/index.html
Normal file
9
test/js-import/fixtures/index.html
Normal file
@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="icon" href="./icon.svg">
|
||||
<!-- <link rel="stylesheet" href="./joker.css">-->
|
||||
</head>
|
||||
<body>
|
||||
<!--<script src="./batman.js" type="module"></script>-->
|
||||
</body>
|
||||
</html>
|
||||
5
test/js-import/fixtures/index.js
Normal file
5
test/js-import/fixtures/index.js
Normal file
@ -0,0 +1,5 @@
|
||||
import html from "./index.html"
|
||||
|
||||
export function render(){
|
||||
return html;
|
||||
}
|
||||
1
test/js-import/fixtures/joker.css
Normal file
1
test/js-import/fixtures/joker.css
Normal file
@ -0,0 +1 @@
|
||||
* { width: 100%; }
|
||||
71
test/js-import/snapshots/test.js.md
Normal file
71
test/js-import/snapshots/test.js.md
Normal file
@ -0,0 +1,71 @@
|
||||
# Snapshot report for `test/js-import/test.js`
|
||||
|
||||
The actual snapshot is saved in `test.js.snap`.
|
||||
|
||||
Generated by [AVA](https://avajs.dev).
|
||||
|
||||
## js-import
|
||||
|
||||
> Snapshot 1
|
||||
|
||||
[
|
||||
{
|
||||
code: `var asset0 = "data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2032%2032%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%20%20%3Cpath%20style%3D%22fill%3Anone%3Bstroke%3A%2300ff0d%3Bstroke-width%3A5%3Bstroke-linecap%3Asquare%3Bstroke-linejoin%3Amiter%3Bstroke-dasharray%3Anone%3Bstroke-opacity%3A1%22%20d%3D%22M4.1%2014.72%2016%2026.31%2028.38%205.09%22%2F%3E%3C%2Fsvg%3E";␊
|
||||
␊
|
||||
const html = \`<html><head>␊
|
||||
<link rel="icon" href="${asset0}">␊
|
||||
<!-- <link rel="stylesheet" href="./joker.css">-->␊
|
||||
</head>␊
|
||||
<body>␊
|
||||
<!--<script src="./batman.js" type="module"></script>-->␊
|
||||
␊
|
||||
␊
|
||||
</body></html>\`;␊
|
||||
␊
|
||||
function render(){␊
|
||||
return html;␊
|
||||
}␊
|
||||
␊
|
||||
export { render };␊
|
||||
//# sourceMappingURL=index-f75fa1e5.js.map␊
|
||||
`,
|
||||
fileName: 'index-f75fa1e5.js',
|
||||
map: SourceMap {
|
||||
file: 'index-f75fa1e5.js',
|
||||
mappings: 'AAAA,aAAe;;ACAf,MAAA,IAAA,GAAA,CAAA;AACA,+BAA+B,EAAwD,MAAA,CAAA;AACvF;AACA;AACA;AACA;AACA;AACA;AACA,cAAa,CAAA;;ACNN,SAAS,MAAM,EAAE;AACxB,IAAI,OAAO,IAAI,CAAC;AAChB;;;;',
|
||||
names: [],
|
||||
sources: [
|
||||
'../icon.svg',
|
||||
'../index.html',
|
||||
'../index.js',
|
||||
],
|
||||
sourcesContent: [
|
||||
'export default "data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2032%2032%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%20%20%3Cpath%20style%3D%22fill%3Anone%3Bstroke%3A%2300ff0d%3Bstroke-width%3A5%3Bstroke-linecap%3Asquare%3Bstroke-linejoin%3Amiter%3Bstroke-dasharray%3Anone%3Bstroke-opacity%3A1%22%20d%3D%22M4.1%2014.72%2016%2026.31%2028.38%205.09%22%2F%3E%3C%2Fsvg%3E"',
|
||||
`<html>␊
|
||||
<head>␊
|
||||
<link rel="icon" href="./icon.svg">␊
|
||||
<!-- <link rel="stylesheet" href="./joker.css">-->␊
|
||||
</head>␊
|
||||
<body>␊
|
||||
<!--<script src="./batman.js" type="module"></script>-->␊
|
||||
</body>␊
|
||||
</html>␊
|
||||
`,
|
||||
`import html from "./index.html"␊
|
||||
␊
|
||||
export function render(){␊
|
||||
return html;␊
|
||||
}␊
|
||||
`,
|
||||
],
|
||||
version: 3,
|
||||
},
|
||||
source: undefined,
|
||||
},
|
||||
{
|
||||
code: undefined,
|
||||
fileName: 'index-f75fa1e5.js.map',
|
||||
map: undefined,
|
||||
source: '{"version":3,"file":"index-f75fa1e5.js","sources":["../icon.svg","../index.html","../index.js"],"sourcesContent":["export default \\"data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2032%2032%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%20%20%3Cpath%20style%3D%22fill%3Anone%3Bstroke%3A%2300ff0d%3Bstroke-width%3A5%3Bstroke-linecap%3Asquare%3Bstroke-linejoin%3Amiter%3Bstroke-dasharray%3Anone%3Bstroke-opacity%3A1%22%20d%3D%22M4.1%2014.72%2016%2026.31%2028.38%205.09%22%2F%3E%3C%2Fsvg%3E\\"","<html>\\n <head>\\n <link rel=\\"icon\\" href=\\"./icon.svg\\">\\n<!-- <link rel=\\"stylesheet\\" href=\\"./joker.css\\">-->\\n </head>\\n <body>\\n <!--<script src=\\"./batman.js\\" type=\\"module\\"></script>-->\\n </body>\\n</html>\\n","import html from \\"./index.html\\"\\n\\nexport function render(){\\n return html;\\n}\\n"],"names":[],"mappings":"AAAA,aAAe;;ACAf,MAAA,IAAA,GAAA,CAAA;AACA,+BAA+B,EAAwD,MAAA,CAAA;AACvF;AACA;AACA;AACA;AACA;AACA;AACA,cAAa,CAAA;;ACNN,SAAS,MAAM,EAAE;AACxB,IAAI,OAAO,IAAI,CAAC;AAChB;;;;"}',
|
||||
},
|
||||
]
|
||||
BIN
test/js-import/snapshots/test.js.snap
Normal file
BIN
test/js-import/snapshots/test.js.snap
Normal file
Binary file not shown.
51
test/js-import/test.js
Normal file
51
test/js-import/test.js
Normal file
@ -0,0 +1,51 @@
|
||||
import {join, dirname} from "node:path";
|
||||
|
||||
import test from "ava";
|
||||
import { rollup } from "rollup";
|
||||
|
||||
import {debugPrintOutput, getCode} from "../util/test.js";
|
||||
|
||||
import html from "../../src/index.ts";
|
||||
import handlebars from "handlebars";
|
||||
|
||||
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 {fileURLToPath} from "node:url";
|
||||
import urlPlugin from "@rollup/plugin-url";
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
process.chdir(join(__dirname, 'fixtures'));
|
||||
|
||||
const defaultAssetInclude = [
|
||||
'**/*.(png|jpg|jpeg|gif|ico|svg)',// images, svg
|
||||
'**/*.(woff|woff2|eot|ttf|otf)',// fonts
|
||||
'**/*.(webm|mp4)',// video
|
||||
];
|
||||
|
||||
test.serial('js-import', async (t) => {
|
||||
const bundle = await rollup({
|
||||
input: 'index.js',
|
||||
plugins: [
|
||||
html({
|
||||
}),
|
||||
// Test with assets
|
||||
urlPlugin({
|
||||
include: defaultAssetInclude,
|
||||
limit: Number.MAX_SAFE_INTEGER,// Always inline things
|
||||
}),
|
||||
]
|
||||
});
|
||||
const code = await getCode(bundle, output, true);
|
||||
debugPrintOutput('js-import',code);
|
||||
t.snapshot(code);
|
||||
});
|
||||
|
||||
|
||||
// TODO various parameters
|
||||
// - format: cjs, iifi, ...
|
||||
// - sourcemap: inline, false, (and the various exotic sourcemap options)
|
||||
// Watch mode tests would be its own dir
|
||||
// ...
|
||||
Binary file not shown.
@ -105,6 +105,12 @@ Generated by [AVA](https://avajs.dev).
|
||||
map: undefined,
|
||||
source: '{"version":3,"file":"app-01141b67.js","sources":["../app/app.js"],"sourcesContent":["export const bootstrap = (el,deps = [])=>{\\n el.innerHtml = `\\n <div>I\'m \\"annoying\\" ${\\"in case we need to test \\\\`string\\\\` escaping.\\"}. Hence this file \\\\\'tries\\\\\' to include all allowed forms of \'it\'</div>\\n <div>Deps: ${deps}</div>\\n `;\\n}\\n"],"names":[],"mappings":"AAAY,MAAC,SAAS,GAAG,CAAC,EAAE,CAAC,IAAI,GAAG,EAAE,GAAG;AACzC,IAAI,EAAE,CAAC,SAAS,GAAG,CAAC;AACpB,4BAA4B,EAAE,8CAA8C,CAAC;AAC7E,mBAAmB,EAAE,IAAI,CAAC;AAC1B,IAAI,CAAC,CAAC;AACN;;;;"}',
|
||||
},
|
||||
{
|
||||
code: undefined,
|
||||
fileName: 'index.body.script.js-45303f0f.js.map',
|
||||
map: undefined,
|
||||
source: '{"version":3,"file":"index.body.script.js-45303f0f.js","sources":["../index.html.body.script.js"],"sourcesContent":["\\n import {bootstrap} from \\"./app/app.js\\"\\n bootstrap(document.getElementById(\'root\'), \\"<none>\\");\\n "],"names":[],"mappings":";;AAEY,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC"}',
|
||||
},
|
||||
{
|
||||
code: undefined,
|
||||
fileName: 'index.html',
|
||||
@ -116,16 +122,10 @@ Generated by [AVA](https://avajs.dev).
|
||||
<script type="module">import { b as bootstrap } from './app-01141b67.js';␊
|
||||
␊
|
||||
bootstrap(document.getElementById('root'), "<none>");␊
|
||||
//# sourceMappingURL=index.html.body.script.js-45303f0f.js.map␊
|
||||
//# sourceMappingURL=index.body.script.js-45303f0f.js.map␊
|
||||
</script>␊
|
||||
␊
|
||||
␊
|
||||
</body></html>`,
|
||||
},
|
||||
{
|
||||
code: undefined,
|
||||
fileName: 'index.html.body.script.js-45303f0f.js.map',
|
||||
map: undefined,
|
||||
source: '{"version":3,"file":"index.html.body.script.js-45303f0f.js","sources":["../index.html.body.script.js"],"sourcesContent":["\\n import {bootstrap} from \\"./app/app.js\\"\\n bootstrap(document.getElementById(\'root\'), \\"<none>\\");\\n "],"names":[],"mappings":";;AAEY,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC"}',
|
||||
},
|
||||
]
|
||||
|
||||
Binary file not shown.
Binary file not shown.
3
test/url-plugin/fixtures/output/fb585fdb6db313c9.svg
Normal file
3
test/url-plugin/fixtures/output/fb585fdb6db313c9.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<path style="fill:none;stroke:#00ff0d;stroke-width:5;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" d="M4.1 14.72 16 26.31 28.38 5.09"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 244 B |
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user