feat: initial support for importing html from js
This commit is contained in:
parent
afd4a3c9ae
commit
71a377417d
313
src/index.ts
313
src/index.ts
@ -77,7 +77,7 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
|||||||
let filter = createFilter(include, exclude, {});
|
let filter = createFilter(include, exclude, {});
|
||||||
|
|
||||||
// TODO, we need to clear all these properly at sme point to avoid odd bugs in watch mode
|
// 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 virtualSources = new Map<string, string>();
|
||||||
let addedEntries = new Map<string, string>();
|
let addedEntries = new Map<string, string>();
|
||||||
let entryNames = 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 moduleId = resolved.id;
|
||||||
const moduleExt = extname(resolved.id);
|
const moduleExt = extname(resolved.id);
|
||||||
const moduleName = specifier.replace(new RegExp(`${moduleExt}\$`),''); // strip extension of the name if any
|
const moduleName = specifier.replace(new RegExp(`${moduleExt}\$`),''); // strip extension of the name if any
|
||||||
const htmlModule : HtmlModule = htmlModules.get(moduleId) ?? {
|
// const htmlModule : HtmlModule = htmlModules.get(moduleId) ?? {
|
||||||
id: resolved.id,
|
// id: resolved.id,
|
||||||
name: moduleName,
|
// name: moduleName,
|
||||||
imports: [],
|
// imports: [],
|
||||||
assetId: null,
|
// assetId: null,
|
||||||
importers: new Set(),
|
// importers: new Set(),
|
||||||
};
|
// };
|
||||||
htmlModule.importers.add(importer);
|
// 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)
|
// 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 {
|
return {
|
||||||
...resolved,
|
...resolved,
|
||||||
meta: {
|
meta: {
|
||||||
...resolved.meta,
|
...resolved.meta,
|
||||||
[pluginName]: {name: specifier}
|
[pluginName]: {
|
||||||
|
specifier: specifier,
|
||||||
|
id: resolved.id,
|
||||||
|
name: moduleName,
|
||||||
|
imports: [],
|
||||||
|
assetId: null,
|
||||||
|
importers: new Set(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,145 +165,163 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
transform: {
|
transform: {
|
||||||
|
order: 'pre',
|
||||||
async handler(...args){
|
async handler(...args){
|
||||||
const [code, id] = args;
|
const [code, id] = args;
|
||||||
if (!filter(id)) return;
|
if (!filter(id)) return;
|
||||||
|
|
||||||
// parse
|
// parse
|
||||||
const htmlModule = htmlModules.get(id);
|
const moduleInfo = this.getModuleInfo(id);
|
||||||
if(htmlModule) {
|
const moduleMeta = moduleInfo!.meta ?? {};
|
||||||
const contents = await readFile(id, {encoding: "utf-8"});
|
let htmlModule = moduleMeta[pluginName];
|
||||||
|
if(!htmlModule){
|
||||||
const htmlSrc = transform ? await transform(contents, {
|
const moduleExt = extname(id);
|
||||||
id,
|
const moduleName = id.replace(new RegExp(`${moduleExt}\$`),''); // strip extension of the name if any
|
||||||
}) : contents;
|
htmlModule = moduleMeta[pluginName] = {
|
||||||
|
id: id,
|
||||||
// Parse document and store it
|
name: moduleName,
|
||||||
const document = htmlModule.document = parseHtml(htmlSrc);
|
imports: [],
|
||||||
|
assetId: null,
|
||||||
// Figure out which references to load from this HTML by iterating all nodes (looking for src or href attributes)
|
importers: new Set(),
|
||||||
let htmlImports: HtmlImport[] = htmlModule.imports = [];
|
|
||||||
if (document.childNodes) {
|
|
||||||
let nodeQueue = document.childNodes;
|
|
||||||
do {
|
|
||||||
const nextQueue: DefaultTreeAdapterMap['childNode'][][] = [];
|
|
||||||
await Promise.all(nodeQueue.map(async (node) => {
|
|
||||||
const el = (<DefaultTreeAdapterMap['element']>node);
|
|
||||||
const loadFunction: LoadFunction = async ({
|
|
||||||
id: sourceId,
|
|
||||||
source,
|
|
||||||
type
|
|
||||||
})=>{
|
|
||||||
if(!sourceId){
|
|
||||||
sourceId = makeInlineId(id, node, 'js');
|
|
||||||
}
|
|
||||||
if(source){
|
|
||||||
virtualSources.set(sourceId, source);
|
|
||||||
}
|
|
||||||
const resolved = await this.resolve(sourceId, id, {
|
|
||||||
isEntry: type==='entryChunk',
|
|
||||||
});
|
|
||||||
if(!resolved){
|
|
||||||
throw new Error(`Could not resolve ${sourceId} from ${id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const selfInfo = this.getModuleInfo(id);
|
|
||||||
|
|
||||||
let entryName: string|undefined = undefined;
|
|
||||||
const parentName = entryNames.get(id)??selfInfo?.meta[pluginName].name;
|
|
||||||
if(type==='entryChunk'){
|
|
||||||
entryName= posix.join(posix.dirname(parentName),sourceId);
|
|
||||||
entryName = entryName.slice(0,-(posix.extname(entryName).length)); // Cut off the extension (TODO, is this wise?)
|
|
||||||
}
|
|
||||||
|
|
||||||
const importName = (source && selfInfo?.meta[pluginName].name)
|
|
||||||
? makeInlineId(parentName, node, extname(sourceId))
|
|
||||||
: entryName;
|
|
||||||
|
|
||||||
const htmlImport: HtmlImport = {
|
|
||||||
id: <string>sourceId,
|
|
||||||
resolved: resolved,
|
|
||||||
// loaded: loaded,
|
|
||||||
node: el,
|
|
||||||
type,
|
|
||||||
source,
|
|
||||||
referenceId:
|
|
||||||
(resolved && (['chunk','entryChunk'].includes(type!))) ? this.emitFile({
|
|
||||||
type: 'chunk', // Might want to adapt, or make configurable (see LoadType)
|
|
||||||
id: resolved.id,
|
|
||||||
name: importName,
|
|
||||||
importer: id,
|
|
||||||
}) : null,
|
|
||||||
placeholder: `html-import-${crypto.randomBytes(32).toString('base64')}`,
|
|
||||||
index: htmlImports.length,
|
|
||||||
}
|
|
||||||
// if(entryName){
|
|
||||||
// addedEntries.set(resolved.id, entryName);// (we could do this using meta?)
|
|
||||||
// }
|
|
||||||
htmlImports.push(htmlImport);
|
|
||||||
return htmlImport.placeholder;
|
|
||||||
}
|
|
||||||
|
|
||||||
let toLoad: LoadResult | undefined = load? await Promise.resolve(load({
|
|
||||||
node: el,
|
|
||||||
sourceId: id
|
|
||||||
}, loadFunction)) : undefined;
|
|
||||||
|
|
||||||
if (toLoad !== false) {
|
|
||||||
let asParent = (<DefaultTreeAdapterMap['parentNode']>node);
|
|
||||||
if (asParent.childNodes) {
|
|
||||||
nextQueue.push(asParent.childNodes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
nodeQueue = nextQueue.flat();
|
|
||||||
} while (nodeQueue.length > 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let htmlJS = new MagicString(serializeHtml(htmlModule.document));// TODO this is still a leak of AST, we're taking parse5 edited result and then transforming sourcemaps, this will only work when no edits were made
|
|
||||||
htmlJS.replaceAll(/`/g,'\\\`').replaceAll(/\$\{/g,'\\${');
|
|
||||||
const moduleImports = [];
|
|
||||||
for(const htmlImport of htmlImports){
|
|
||||||
if(htmlImport.type === 'default') {
|
|
||||||
const assetId: string = `asset${moduleImports.length}`;
|
|
||||||
moduleImports.push(`import ${assetId} from "${htmlImport.id}";`);// TODO: This is just the easy & safe solution. Would prefer to have recognizable names, and reeuse when something is the exact same resource..
|
|
||||||
htmlJS = htmlJS.replace(htmlImport.placeholder, `\${${assetId}}`);// TODO: Should we be worried about windows absolute URLs here?
|
|
||||||
// }else if(htmlImport.type === 'entryChunk' && htmlImport.referenceId){
|
|
||||||
// html = html.replace(htmlImport.placeholder, `\${import.meta.ROLLUP_FILE_URL_${htmlImport.referenceId}\}`);
|
|
||||||
}else{
|
|
||||||
// TODO: this will probably not do for complicated cases ( presumably no other method then emitting the chunk as file, loading its result but excluding it from the output bundle)
|
|
||||||
// html = html.replace(htmlImport.placeholder, htmlImport.loaded?.code||htmlImport.source||'');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO when importing html from .js this will not do. (
|
|
||||||
htmlJS.prepend([
|
|
||||||
...moduleImports,
|
|
||||||
`export const html = \``
|
|
||||||
].join('\n')).append([
|
|
||||||
`\`;`,
|
|
||||||
`export default html;`,
|
|
||||||
].join('\n'));
|
|
||||||
|
|
||||||
const map = htmlJS.generateMap({
|
|
||||||
source: id,
|
|
||||||
file: `${id}.map`,
|
|
||||||
includeContent: true,
|
|
||||||
hires: 'boundary'
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
code: htmlJS.toString(),
|
|
||||||
map: map.toString(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
// const htmlModule = htmlModules.get(id);
|
||||||
|
|
||||||
|
// const contents = await readFile(id, {encoding: "utf-8"});
|
||||||
|
const contents = code;
|
||||||
|
|
||||||
|
const htmlSrc = transform ? await transform(contents, {
|
||||||
|
id,
|
||||||
|
}) : contents;
|
||||||
|
|
||||||
|
// Parse document and store it
|
||||||
|
const document = htmlModule.document = parseHtml(htmlSrc);
|
||||||
|
|
||||||
|
// Figure out which references to load from this HTML by iterating all nodes (looking for src or href attributes)
|
||||||
|
let htmlImports: HtmlImport[] = htmlModule.imports = [];
|
||||||
|
if (document.childNodes) {
|
||||||
|
let nodeQueue = document.childNodes;
|
||||||
|
do {
|
||||||
|
const nextQueue: DefaultTreeAdapterMap['childNode'][][] = [];
|
||||||
|
await Promise.all(nodeQueue.map(async (node) => {
|
||||||
|
const el = (<DefaultTreeAdapterMap['element']>node);
|
||||||
|
const loadFunction: LoadFunction = async ({
|
||||||
|
id: sourceId,
|
||||||
|
source,
|
||||||
|
type
|
||||||
|
})=>{
|
||||||
|
if(!sourceId){
|
||||||
|
sourceId = makeInlineId(id, node, 'js');
|
||||||
|
}
|
||||||
|
if(source){
|
||||||
|
virtualSources.set(sourceId, source);
|
||||||
|
}
|
||||||
|
const resolved = await this.resolve(sourceId, id, {
|
||||||
|
isEntry: type==='entryChunk',
|
||||||
|
});
|
||||||
|
if(!resolved){
|
||||||
|
throw new Error(`Could not resolve ${sourceId} from ${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const selfInfo = this.getModuleInfo(id);
|
||||||
|
|
||||||
|
let entryName: string|undefined = undefined;
|
||||||
|
const parentName = entryNames.get(id)??selfInfo?.meta[pluginName].name;
|
||||||
|
if(type==='entryChunk'){
|
||||||
|
entryName= posix.join(posix.dirname(parentName),sourceId);
|
||||||
|
entryName = entryName.slice(0,-(posix.extname(entryName).length)); // Cut off the extension (TODO, is this wise?)
|
||||||
|
}
|
||||||
|
|
||||||
|
const importName = (source && selfInfo?.meta[pluginName].name)
|
||||||
|
? makeInlineId(parentName, node, extname(sourceId))
|
||||||
|
: entryName;
|
||||||
|
|
||||||
|
const htmlImport: HtmlImport = {
|
||||||
|
id: <string>sourceId,
|
||||||
|
resolved: resolved,
|
||||||
|
// loaded: loaded,
|
||||||
|
node: el,
|
||||||
|
type,
|
||||||
|
source,
|
||||||
|
referenceId:
|
||||||
|
(resolved && (['chunk','entryChunk'].includes(type!))) ? this.emitFile({
|
||||||
|
type: 'chunk', // Might want to adapt, or make configurable (see LoadType)
|
||||||
|
id: resolved.id,
|
||||||
|
name: importName,
|
||||||
|
importer: id,
|
||||||
|
}) : null,
|
||||||
|
placeholder: `html-import-${crypto.randomBytes(32).toString('base64')}`,
|
||||||
|
index: htmlImports.length,
|
||||||
|
}
|
||||||
|
// if(entryName){
|
||||||
|
// addedEntries.set(resolved.id, entryName);// (we could do this using meta?)
|
||||||
|
// }
|
||||||
|
htmlImports.push(htmlImport);
|
||||||
|
return htmlImport.placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
let toLoad: LoadResult | undefined = load? await Promise.resolve(load({
|
||||||
|
node: el,
|
||||||
|
sourceId: id
|
||||||
|
}, loadFunction)) : undefined;
|
||||||
|
|
||||||
|
if (toLoad !== false) {
|
||||||
|
let asParent = (<DefaultTreeAdapterMap['parentNode']>node);
|
||||||
|
if (asParent.childNodes) {
|
||||||
|
nextQueue.push(asParent.childNodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
nodeQueue = nextQueue.flat();
|
||||||
|
} while (nodeQueue.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let htmlJS = new MagicString(serializeHtml(htmlModule.document));// TODO this is still a leak of AST, we're taking parse5 edited result and then transforming sourcemaps, this will only work when no edits were made
|
||||||
|
htmlJS.replaceAll(/`/g,'\\\`').replaceAll(/\$\{/g,'\\${');
|
||||||
|
const moduleImports = [];
|
||||||
|
for(const htmlImport of htmlImports){
|
||||||
|
if(htmlImport.type === 'default') {
|
||||||
|
const assetId: string = `asset${moduleImports.length}`;
|
||||||
|
moduleImports.push(`import ${assetId} from "${htmlImport.id}";`);// TODO: This is just the easy & safe solution. Would prefer to have recognizable names, and reeuse when something is the exact same resource..
|
||||||
|
htmlJS = htmlJS.replace(htmlImport.placeholder, `\${${assetId}}`);// TODO: Should we be worried about windows absolute URLs here?
|
||||||
|
// }else if(htmlImport.type === 'entryChunk' && htmlImport.referenceId){
|
||||||
|
// html = html.replace(htmlImport.placeholder, `\${import.meta.ROLLUP_FILE_URL_${htmlImport.referenceId}\}`);
|
||||||
|
}else{
|
||||||
|
// TODO: this will probably not do for complicated cases ( presumably no other method then emitting the chunk as file, loading its result but excluding it from the output bundle)
|
||||||
|
// html = html.replace(htmlImport.placeholder, htmlImport.loaded?.code||htmlImport.source||'');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO when importing html from .js this will not do. (
|
||||||
|
htmlJS.prepend([
|
||||||
|
...moduleImports,
|
||||||
|
`export const html = \``
|
||||||
|
].join('\n')).append([
|
||||||
|
`\`;`,
|
||||||
|
`export default html;`,
|
||||||
|
].join('\n'));
|
||||||
|
|
||||||
|
const map = htmlJS.generateMap({
|
||||||
|
source: id,
|
||||||
|
file: `${id}.map`,
|
||||||
|
includeContent: true,
|
||||||
|
hires: 'boundary'
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: htmlJS.toString(),
|
||||||
|
map: map.toString(),
|
||||||
|
meta: moduleMeta,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
outputOptions(options){
|
outputOptions(options){
|
||||||
return {
|
return {
|
||||||
...options,
|
...options,
|
||||||
entryFileNames: (chunkInfo)=>{
|
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 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?
|
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){
|
if(htmlModule){
|
||||||
@ -313,7 +338,9 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
resolveFileUrl(options){
|
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){
|
if(htmlModule){
|
||||||
// Simply use the relative path in our HTML-fileURLs instead of the default `new URL('${fileName}', document.baseURI).href`)
|
// Simply use the relative path in our HTML-fileURLs instead of the default `new URL('${fileName}', document.baseURI).href`)
|
||||||
return `"${options.relativePath}"`;
|
return `"${options.relativePath}"`;
|
||||||
@ -324,7 +351,9 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
|||||||
order:'post',
|
order:'post',
|
||||||
handler(chunk: RenderedChunk){
|
handler(chunk: RenderedChunk){
|
||||||
if(chunk.facadeModuleId) {
|
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) {
|
if (htmlModule) {
|
||||||
return modulePrefix; // Overwrite any added banner with our own
|
return modulePrefix; // Overwrite any added banner with our own
|
||||||
}
|
}
|
||||||
@ -342,7 +371,11 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin {
|
|||||||
const chunk = (<OutputChunk>bundle);
|
const chunk = (<OutputChunk>bundle);
|
||||||
if(chunk.facadeModuleId) {
|
if(chunk.facadeModuleId) {
|
||||||
facadeToChunk.set(chunk.facadeModuleId, chunk);
|
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})}
|
if(htmlModule){ htmlResults.set(bundleName, {chunk, htmlModule})}
|
||||||
else if(virtualSources.has(chunk.facadeModuleId)){
|
else if(virtualSources.has(chunk.facadeModuleId)){
|
||||||
virtualBundles.add(bundleName);
|
virtualBundles.add(bundleName);
|
||||||
|
|||||||
@ -58,6 +58,12 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
> Snapshot 1
|
> 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,
|
code: undefined,
|
||||||
fileName: 'script.html',
|
fileName: 'script.html',
|
||||||
@ -71,16 +77,10 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
document.body.appendChild(␊
|
document.body.appendChild(␊
|
||||||
document.createTextNode(\`Inline script including ${b()}\`)␊
|
document.createTextNode(\`Inline script including ${b()}\`)␊
|
||||||
);␊
|
);␊
|
||||||
//# sourceMappingURL=script.html.body.script.js-e3b82208.js.map␊
|
//# sourceMappingURL=script.body.script.js-e3b82208.js.map␊
|
||||||
</script>␊
|
</script>␊
|
||||||
␊
|
␊
|
||||||
␊
|
␊
|
||||||
</body></html>`,
|
</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,
|
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;;;;"}',
|
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,
|
code: undefined,
|
||||||
fileName: 'index.html',
|
fileName: 'index.html',
|
||||||
@ -116,16 +122,10 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
<script type="module">import { b as bootstrap } from './app-01141b67.js';␊
|
<script type="module">import { b as bootstrap } from './app-01141b67.js';␊
|
||||||
␊
|
␊
|
||||||
bootstrap(document.getElementById('root'), "<none>");␊
|
bootstrap(document.getElementById('root'), "<none>");␊
|
||||||
//# sourceMappingURL=index.html.body.script.js-45303f0f.js.map␊
|
//# sourceMappingURL=index.body.script.js-45303f0f.js.map␊
|
||||||
</script>␊
|
</script>␊
|
||||||
␊
|
␊
|
||||||
␊
|
␊
|
||||||
</body></html>`,
|
</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