mirror of
https://github.com/jlengrand/open-wc.git
synced 2026-03-10 08:31:19 +00:00
feat(rollup-plugin-html): first release
This commit is contained in:
@@ -10,5 +10,7 @@ packages/**/test/**/snapshots
|
||||
/packages/karma-esm/src/esm-debug.html
|
||||
/packages/karma-esm/src/esm-context.html
|
||||
/packages/demoing-storybook/storybook-static/**/*
|
||||
/packages/rollup-plugin-input-html/test/fixtures/**/*
|
||||
/packages/rollup-plugin-html/dist/**/*
|
||||
CHANGELOG.md
|
||||
__snapshots__/
|
||||
|
||||
@@ -10,7 +10,7 @@ module.exports = config => {
|
||||
{
|
||||
pattern: config.grep
|
||||
? config.grep
|
||||
: 'packages/!(webpack-import-meta-loader|create|building-utils|demoing-storybook|webpack-index-html-plugin|rollup-plugin-index-html|import-maps-generate|import-maps-resolve|es-dev-server|karma-esm|building-rollup|building-webpack|polyfills-loader)/test/**/*.test.js',
|
||||
: 'packages/!(webpack-import-meta-loader|create|building-utils|demoing-storybook|webpack-index-html-plugin|rollup-plugin-index-html|import-maps-generate|import-maps-resolve|es-dev-server|karma-esm|building-rollup|building-webpack|polyfills-loader|rollup-plugin-html)/test/**/*.test.js',
|
||||
type: 'module',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"test:bs": "karma start karma.bs.conf.js --coverage",
|
||||
"test:node": "lerna run test:node",
|
||||
"test:update-snapshots": "lerna run test:update-snapshots",
|
||||
"update-dependency": "node scripts/update-dependency.js && yarn format",
|
||||
"update-dependency": "node scripts/update-dependency.js",
|
||||
"vuepress:build": "vuepress build docs",
|
||||
"vuepress:copy-static": "cp -f packages/demoing-storybook/demo/custom-elements.json docs/.vuepress/public/demoing/demo/custom-elements.json",
|
||||
"vuepress:start": "vuepress dev docs"
|
||||
|
||||
@@ -60,11 +60,11 @@
|
||||
"@babel/preset-typescript": "^7.8.3",
|
||||
"chai": "^4.2.0",
|
||||
"es-dev-server": "^1.40.1",
|
||||
"lit-element": "^2.0.1",
|
||||
"lit-element": "^2.2.1",
|
||||
"mocha": "^6.2.2",
|
||||
"puppeteer": "^2.0.0",
|
||||
"rimraf": "^3.0.0",
|
||||
"rollup": "^1.15.6",
|
||||
"rollup": "^1.31.1",
|
||||
"rollup-plugin-typescript2": "^0.19.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ const CleanCSS = require('clean-css');
|
||||
const cleanCSS = new CleanCSS({
|
||||
rebase: false,
|
||||
inline: ['none'],
|
||||
// @ts-ignore
|
||||
level: {
|
||||
1: {
|
||||
all: false,
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
const { constructors, setAttribute, append } = require('./dom5-fork');
|
||||
/** @typedef {import('parse5').Document} Document */
|
||||
/** @typedef {import('parse5').Node} Node */
|
||||
/** @typedef {import('parse5').DefaultTreeElement} DefaultTreeElement */
|
||||
|
||||
const { isUri } = require('valid-url');
|
||||
const {
|
||||
constructors,
|
||||
setAttribute,
|
||||
append,
|
||||
queryAll,
|
||||
predicates,
|
||||
getAttribute,
|
||||
hasAttribute,
|
||||
} = require('./dom5-fork');
|
||||
|
||||
/**
|
||||
* @param {string} tag
|
||||
* @param {Record<string, string>} attributes
|
||||
* @returns {DefaultTreeElement}
|
||||
*/
|
||||
function createElement(tag, attributes) {
|
||||
const element = constructors.element(tag);
|
||||
if (attributes) {
|
||||
@@ -12,6 +30,11 @@ function createElement(tag, attributes) {
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, string | undefined>} attributes
|
||||
* @param {string} [code]
|
||||
* @returns {DefaultTreeElement}
|
||||
*/
|
||||
function createScript(attributes, code) {
|
||||
const script = createElement('script', attributes);
|
||||
if (code) {
|
||||
@@ -21,12 +44,110 @@ function createScript(attributes, code) {
|
||||
return script;
|
||||
}
|
||||
|
||||
function createScriptModule(code) {
|
||||
/**
|
||||
* @param {string} code
|
||||
* @returns {DefaultTreeElement}
|
||||
*/
|
||||
function createModuleScript(code) {
|
||||
return createScript({ type: 'module' }, code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Document} document
|
||||
* @returns {{ inline: Node[], external: Node[]}}
|
||||
*/
|
||||
function findImportMapScripts(document) {
|
||||
/** @type {Node[]} */
|
||||
const allScripts = queryAll(document, predicates.hasTagName('script'));
|
||||
const scripts = allScripts.filter(script => getAttribute(script, 'type') === 'importmap');
|
||||
|
||||
/** @type {Node[]} */
|
||||
const inline = [];
|
||||
/** @type {Node[]} */
|
||||
const external = [];
|
||||
scripts.forEach(script => {
|
||||
if (getAttribute(script, 'src')) {
|
||||
external.push(script);
|
||||
} else {
|
||||
inline.push(script);
|
||||
}
|
||||
});
|
||||
|
||||
return { inline, external };
|
||||
}
|
||||
|
||||
/** @param {Node} script */
|
||||
function isDeferred(script) {
|
||||
return getAttribute(script, 'type') === 'module' || hasAttribute(script, 'defer');
|
||||
}
|
||||
|
||||
/** @param {Node} script */
|
||||
function isAsync(script) {
|
||||
return hasAttribute(script, 'async');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Node} a
|
||||
* @param {Node} b
|
||||
* @returns {number}
|
||||
*/
|
||||
function sortByLoadingPriority(a, b) {
|
||||
if (isAsync(a)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const aDeferred = isDeferred(a);
|
||||
const bDeferred = isDeferred(b);
|
||||
|
||||
if (aDeferred && bDeferred) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (aDeferred) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (bDeferred) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* Finds all js scripts in a document, returns the scripts sorted by loading priority.
|
||||
* @param {Document} document
|
||||
* @param {{ jsScripts?: boolean, jsModules?: boolean, inlineJsScripts?: boolean, inlineJsModules?: boolean }} [exclude]
|
||||
* @returns {Node[]}
|
||||
*/
|
||||
function findJsScripts(document, exclude = {}) {
|
||||
/** @type {Node[]} */
|
||||
const allScripts = queryAll(document, predicates.hasTagName('script'));
|
||||
|
||||
return allScripts
|
||||
.filter(script => {
|
||||
const inline = !hasAttribute(script, 'src');
|
||||
const type = getAttribute(script, 'type');
|
||||
|
||||
// we don't handle scripts which import from a URL (ex. a CDN)
|
||||
if (!inline && isUri(getAttribute(script, 'src'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!type || ['application/javascript', 'text/javascript'].includes(type)) {
|
||||
return inline ? !exclude.inlineJsScripts : !exclude.jsScripts;
|
||||
}
|
||||
if (type === 'module') {
|
||||
return inline ? !exclude.inlineJsModules : !exclude.jsModules;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.sort(sortByLoadingPriority);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createElement,
|
||||
createScript,
|
||||
createScriptModule,
|
||||
createModuleScript,
|
||||
findImportMapScripts,
|
||||
findJsScripts,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
const findSupportedBrowsers = require('./find-supported-browsers');
|
||||
const defaultFileExtensions = require('./default-file-extensions');
|
||||
const { toBrowserPath } = require('./to-browser-path');
|
||||
const dom5Utils = require('./dom5-utils');
|
||||
|
||||
module.exports = {
|
||||
findSupportedBrowsers,
|
||||
defaultFileExtensions,
|
||||
toBrowserPath,
|
||||
...dom5Utils,
|
||||
};
|
||||
|
||||
@@ -44,14 +44,14 @@
|
||||
"html-minifier": "^4.0.0",
|
||||
"lru-cache": "^5.1.1",
|
||||
"minimatch": "^3.0.4",
|
||||
"parse5": "^5.1.0",
|
||||
"parse5": "^5.1.1",
|
||||
"path-is-inside": "^1.0.2",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
"resolve": "^1.11.1",
|
||||
"rimraf": "^3.0.0",
|
||||
"shady-css-scoped-element": "^0.0.1",
|
||||
"systemjs": "^4.0.0",
|
||||
"terser": "^4.0.0",
|
||||
"terser": "^4.6.4",
|
||||
"valid-url": "^1.0.9",
|
||||
"whatwg-fetch": "^3.0.0",
|
||||
"whatwg-url": "^7.0.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { expect } = require('chai');
|
||||
const { parse } = require('parse5');
|
||||
const { getAttribute, getTextContent } = require('@open-wc/building-utils/dom5-fork');
|
||||
const { findJsScripts, findImportMapScripts } = require('../src/utils');
|
||||
const { getAttribute, getTextContent } = require('../dom5-fork');
|
||||
const { findJsScripts, findImportMapScripts } = require('../dom5-utils');
|
||||
|
||||
const htmlString = `
|
||||
<html>
|
||||
@@ -1 +1 @@
|
||||
<html lang="en-GB"><head><title>My app</title><style>my-app{display:block}</style><link rel="preload" href="app.js" as="script" crossorigin="anonymous"></head><body><h1><span>Hello world!</span></h1><my-app></my-app><script>window.importShim=t=>import(t.startsWith(".")?new URL(t,document.baseURI):t);</script><script src="polyfills/custom-b.36a50cce88edee34c249d0276be6531d.js" nomodule=""></script><script src="polyfills/core-js.d58f09bfd9f1c0b1682cf4a93be23230.js" nomodule=""></script><script>!function(){function e(e,o){return new Promise((function(t,n){document.head.appendChild(Object.assign(document.createElement("script"),{src:e,onload:t,onerror:n},o?{type:"module"}:void 0))}))}var o=[];function t(){window.importShim("./app.js")}"foo"in window&&o.push(e("polyfills/custom-a.612310cce7c28a680112cc9eff6ef77c.js",!1)),"fetch"in window||o.push(e("polyfills/fetch.e0fa1d30ce1c9b23c0898a2e34c3fe3b.js",!1)),"noModule"in HTMLScriptElement.prototype&&!("importShim"in window)&&o.push(e("polyfills/dynamic-import.b745cfc9384367cc18b42bbef2bbdcd9.js",!1)),"attachShadow"in Element.prototype&&"getRootNode"in Element.prototype&&(!window.ShadyDOM||!window.ShadyDOM.force)||o.push(e("polyfills/webcomponents.dae9f79d9d6992b6582e204c3dd953d3.js",!1)),!("noModule"in HTMLScriptElement.prototype)&&"getRootNode"in Element.prototype&&o.push(e("polyfills/custom-elements-es5-adapter.84b300ee818dce8b351c7cc7c100bcf7.js",!1)),o.length?Promise.all(o).then(t):t()}();</script></body></html>
|
||||
<html lang="en-GB"><head><title>My app</title><style>my-app{display:block}</style><link rel="preload" href="app.js" as="script" crossorigin="anonymous"></head><body><h1><span>Hello world!</span></h1><my-app></my-app><script>window.importShim=t=>import(t.startsWith(".")?new URL(t,document.baseURI):t);</script><script src="polyfills/custom-b.36a50cce88edee34c249d0276be6531d.js" nomodule=""></script><script src="polyfills/core-js.d58f09bfd9f1c0b1682cf4a93be23230.js" nomodule=""></script><script>!function(){function e(e,o){return new Promise((function(t,n){document.head.appendChild(Object.assign(document.createElement("script"),{src:e,onload:t,onerror:n},o?{type:"module"}:void 0))}))}var o=[];function t(){window.importShim("./app.js")}"foo"in window&&o.push(e("polyfills/custom-a.612310cce7c28a680112cc9eff6ef77c.js",!1)),"fetch"in window||o.push(e("polyfills/fetch.191258a74d74243758f52065f3d0962a.js",!1)),"noModule"in HTMLScriptElement.prototype&&!("importShim"in window)&&o.push(e("polyfills/dynamic-import.b745cfc9384367cc18b42bbef2bbdcd9.js",!1)),"attachShadow"in Element.prototype&&"getRootNode"in Element.prototype&&(!window.ShadyDOM||!window.ShadyDOM.force)||o.push(e("polyfills/webcomponents.dae9f79d9d6992b6582e204c3dd953d3.js",!1)),!("noModule"in HTMLScriptElement.prototype)&&"getRootNode"in Element.prototype&&o.push(e("polyfills/custom-elements-es5-adapter.84b300ee818dce8b351c7cc7c100bcf7.js",!1)),o.length?Promise.all(o).then(t):t()}();</script></body></html>
|
||||
13
packages/building-utils/to-browser-path.js
Normal file
13
packages/building-utils/to-browser-path.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const path = require('path');
|
||||
|
||||
const toBrowserPathRegExp = new RegExp(path.sep === '\\' ? '\\\\' : path.sep, 'g');
|
||||
|
||||
/**
|
||||
* @param {string} filePath
|
||||
* @returns {string}
|
||||
*/
|
||||
function toBrowserPath(filePath) {
|
||||
return filePath.replace(toBrowserPathRegExp, '/');
|
||||
}
|
||||
|
||||
module.exports = { toBrowserPath };
|
||||
@@ -64,7 +64,7 @@
|
||||
"@babel/register": "^7.8.3",
|
||||
"chai": "^4.2.0",
|
||||
"http-server": "^0.11.1",
|
||||
"lit-element": "^2.0.1",
|
||||
"lit-element": "^2.2.1",
|
||||
"mocha": "^6.2.2",
|
||||
"puppeteer": "^2.0.0",
|
||||
"rimraf": "^3.0.0",
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"@open-wc/testing-karma-bs": "^1.0.0",
|
||||
"@open-wc/testing": "^2.0.0",
|
||||
"@open-wc/demoing-storybook": "^1.0.1",
|
||||
"@open-wc/building-rollup": "^0.15.1",
|
||||
"@open-wc/building-rollup": "^0.21.0",
|
||||
"rimraf": "^2.6.3",
|
||||
"rollup": "^1.15.4",
|
||||
"es-dev-server": "^1.5.0"
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"@open-wc/building-rollup": "^0.21.1",
|
||||
"@open-wc/testing": "^2.5.4",
|
||||
"http-server": "^0.11.1",
|
||||
"rollup": "^1.15.6",
|
||||
"rollup": "^1.31.1",
|
||||
"sinon": "^7.4.1"
|
||||
},
|
||||
"module": "index.js",
|
||||
|
||||
@@ -56,13 +56,13 @@
|
||||
"glob": "^7.1.3",
|
||||
"lit-html": "^1.0.0",
|
||||
"magic-string": "^0.25.4",
|
||||
"rollup": "^1.15.6",
|
||||
"rollup": "^1.31.1",
|
||||
"rollup-plugin-index-html": "^1.9.3",
|
||||
"storybook-prebuilt": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"lit-element": "^2.0.1",
|
||||
"lit-element": "^2.2.1",
|
||||
"mocha": "^6.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
"lru-cache": "^5.1.1",
|
||||
"minimatch": "^3.0.4",
|
||||
"opn": "^5.4.0",
|
||||
"parse5": "^5.1.0",
|
||||
"parse5": "^5.1.1",
|
||||
"path-is-inside": "^1.0.2",
|
||||
"polyfills-loader": "^1.2.3",
|
||||
"portfinder": "^1.0.21",
|
||||
@@ -119,7 +119,7 @@
|
||||
"buffer": "^5.4.3",
|
||||
"chai": "^4.2.0",
|
||||
"koa-proxies": "^0.8.1",
|
||||
"lit-element": "^2.0.1",
|
||||
"lit-element": "^2.2.1",
|
||||
"lit-html": "^1.0.0",
|
||||
"lodash-es": "^4.17.15",
|
||||
"mocha": "^6.2.2",
|
||||
|
||||
@@ -11,15 +11,16 @@ import {
|
||||
remove,
|
||||
setTextContent,
|
||||
} from '@open-wc/building-utils/dom5-fork/index.js';
|
||||
import { findJsScripts } from '@open-wc/building-utils';
|
||||
import { parse, serialize } from 'parse5';
|
||||
import path from 'path';
|
||||
import deepmerge from 'deepmerge';
|
||||
import {
|
||||
injectPolyfillsLoader as originalInjectPolyfillsLoader,
|
||||
fileTypes,
|
||||
findJsScripts,
|
||||
getScriptFileType,
|
||||
} from 'polyfills-loader';
|
||||
|
||||
import sytemJsTransformResolver from '../browser-scripts/systemjs-transform-resolver.js';
|
||||
import { compatibilityModes } from '../constants.js';
|
||||
import { logDebug } from './utils.js';
|
||||
|
||||
@@ -16,14 +16,8 @@ const {
|
||||
fileTypes,
|
||||
createContentHash,
|
||||
cleanImportPath,
|
||||
createElement,
|
||||
createScript,
|
||||
createModuleScript,
|
||||
findImportMapScripts,
|
||||
findJsScripts,
|
||||
getScriptFileType,
|
||||
hasFileOfType,
|
||||
toBrowserPath,
|
||||
} = require('./src/utils');
|
||||
|
||||
module.exports = {
|
||||
@@ -34,12 +28,6 @@ module.exports = {
|
||||
fileTypes,
|
||||
createContentHash,
|
||||
cleanImportPath,
|
||||
createElement,
|
||||
createScript,
|
||||
createModuleScript,
|
||||
findImportMapScripts,
|
||||
findJsScripts,
|
||||
getScriptFileType,
|
||||
hasFileOfType,
|
||||
toBrowserPath,
|
||||
};
|
||||
|
||||
@@ -38,11 +38,10 @@
|
||||
"es-module-shims": "^0.4.6",
|
||||
"html-minifier": "^4.0.0",
|
||||
"intersection-observer": "^0.7.0",
|
||||
"parse5": "^5.1.0",
|
||||
"parse5": "^5.1.1",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
"systemjs": "^4.0.0",
|
||||
"terser": "^4.0.0",
|
||||
"valid-url": "^1.0.9",
|
||||
"terser": "^4.6.4",
|
||||
"whatwg-fetch": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -11,8 +11,9 @@ const {
|
||||
append,
|
||||
cloneNode,
|
||||
} = require('@open-wc/building-utils/dom5-fork');
|
||||
const { createScript, findImportMapScripts } = require('@open-wc/building-utils');
|
||||
const { createPolyfillsLoader } = require('./create-polyfills-loader');
|
||||
const { createScript, findImportMapScripts, hasFileOfType, fileTypes } = require('./utils');
|
||||
const { hasFileOfType, fileTypes } = require('./utils');
|
||||
|
||||
/**
|
||||
* @param {DocumentAst} headAst
|
||||
|
||||
@@ -3,21 +3,10 @@
|
||||
/** @typedef {import('./types').FileType} FileType */
|
||||
/** @typedef {import('./types').PolyfillsLoaderConfig} PolyfillsLoaderConfig */
|
||||
|
||||
const {
|
||||
constructors,
|
||||
setAttribute,
|
||||
append,
|
||||
queryAll,
|
||||
predicates,
|
||||
getAttribute,
|
||||
hasAttribute,
|
||||
} = require('@open-wc/building-utils/dom5-fork');
|
||||
const { getAttribute } = require('@open-wc/building-utils/dom5-fork');
|
||||
const crypto = require('crypto');
|
||||
const path = require('path');
|
||||
const { isUri } = require('valid-url');
|
||||
|
||||
const noModuleSupportTest = "!('noModule' in HTMLScriptElement.prototype)";
|
||||
const toBrowserPathRegExp = new RegExp(path.sep === '\\' ? '\\\\' : path.sep, 'g');
|
||||
|
||||
/** @type {Record<'SCRIPT' | 'MODULE' | 'ES_MODULE_SHIMS' | 'SYSTEMJS', FileType>} */
|
||||
const fileTypes = {
|
||||
@@ -54,45 +43,6 @@ function cleanImportPath(importPath) {
|
||||
return `./${importPath}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} tag
|
||||
* @param {Record<string, string>} attributes
|
||||
* @returns {Node}
|
||||
*/
|
||||
function createElement(tag, attributes) {
|
||||
const element = constructors.element(tag);
|
||||
if (attributes) {
|
||||
Object.keys(attributes).forEach(key => {
|
||||
if (attributes[key] != null) {
|
||||
setAttribute(element, key, attributes[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, string>} attributes
|
||||
* @param {string} [code]
|
||||
* @returns {Node}
|
||||
*/
|
||||
function createScript(attributes, code) {
|
||||
const script = createElement('script', attributes);
|
||||
if (code) {
|
||||
const scriptText = constructors.text(code);
|
||||
append(script, scriptText);
|
||||
}
|
||||
return script;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} code
|
||||
* @returns {Node}
|
||||
*/
|
||||
function createModuleScript(code) {
|
||||
return createScript({ type: 'module' }, code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Node} script
|
||||
* @returns {FileType}
|
||||
@@ -112,118 +62,11 @@ function hasFileOfType(cfg, type) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Document} document
|
||||
* @returns {{ inline: Node[], external: Node[]}}
|
||||
*/
|
||||
function findImportMapScripts(document) {
|
||||
/** @type {Node[]} */
|
||||
const allScripts = queryAll(document, predicates.hasTagName('script'));
|
||||
const scripts = allScripts.filter(script => getAttribute(script, 'type') === 'importmap');
|
||||
|
||||
/** @type {Node[]} */
|
||||
const inline = [];
|
||||
/** @type {Node[]} */
|
||||
const external = [];
|
||||
scripts.forEach(script => {
|
||||
if (getAttribute(script, 'src')) {
|
||||
external.push(script);
|
||||
} else {
|
||||
inline.push(script);
|
||||
}
|
||||
});
|
||||
|
||||
return { inline, external };
|
||||
}
|
||||
|
||||
/** @param {Node} script */
|
||||
function isDeferred(script) {
|
||||
return getAttribute(script, 'type') === 'module' || hasAttribute(script, 'defer');
|
||||
}
|
||||
|
||||
/** @param {Node} script */
|
||||
function isAsync(script) {
|
||||
return hasAttribute(script, 'async');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Node} a
|
||||
* @param {Node} b
|
||||
* @returns {number}
|
||||
*/
|
||||
function sortByLoadingPriority(a, b) {
|
||||
if (isAsync(a)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const aDeferred = isDeferred(a);
|
||||
const bDeferred = isDeferred(b);
|
||||
|
||||
if (aDeferred && bDeferred) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (aDeferred) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (bDeferred) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all js scripts in a document, returns the scripts sorted by loading priority.
|
||||
* @param {Document} document
|
||||
* @param {{ jsScripts?: boolean, jsModules?: boolean, inlineJsScripts?: boolean, inlineJsModules?: boolean }} [exclude]
|
||||
* @returns {Node[]}
|
||||
*/
|
||||
function findJsScripts(document, exclude = {}) {
|
||||
/** @type {Node[]} */
|
||||
const allScripts = queryAll(document, predicates.hasTagName('script'));
|
||||
|
||||
return allScripts
|
||||
.filter(script => {
|
||||
const inline = !hasAttribute(script, 'src');
|
||||
const type = getAttribute(script, 'type');
|
||||
|
||||
// we don't handle scripts which import from a URL (ex. a CDN)
|
||||
if (!inline && isUri(getAttribute(script, 'src'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!type || ['application/javascript', 'text/javascript'].includes(type)) {
|
||||
return inline ? !exclude.inlineJsScripts : !exclude.jsScripts;
|
||||
}
|
||||
if (type === 'module') {
|
||||
return inline ? !exclude.inlineJsModules : !exclude.jsModules;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.sort(sortByLoadingPriority);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} filePath
|
||||
* @returns {string}
|
||||
*/
|
||||
function toBrowserPath(filePath) {
|
||||
return filePath.replace(toBrowserPathRegExp, '/');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
noModuleSupportTest,
|
||||
fileTypes,
|
||||
createContentHash,
|
||||
cleanImportPath,
|
||||
createElement,
|
||||
createScript,
|
||||
createModuleScript,
|
||||
findImportMapScripts,
|
||||
findJsScripts,
|
||||
getScriptFileType,
|
||||
hasFileOfType,
|
||||
toBrowserPath,
|
||||
};
|
||||
|
||||
491
packages/rollup-plugin-html/README.md
Normal file
491
packages/rollup-plugin-html/README.md
Normal file
@@ -0,0 +1,491 @@
|
||||
# Rollup Plugin HTML
|
||||
|
||||
Plugin for generating HTML files from rollup.
|
||||
|
||||
- Generate one or more HTML pages from a rollup build
|
||||
- Inject rollup bundle into HTML page
|
||||
- Optionally use HTML as rollup input, bundling any module scripts inside
|
||||
- Minify HTML and inline JS and CSS
|
||||
- Suitable for single page and multi page apps
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple HTML page
|
||||
|
||||
When used without any options, the plugin will inject your rollup bundle into a basic HTML page. Useful for developing a simple application.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Show example</summary>
|
||||
|
||||
```js
|
||||
import html from '@open-wc/rollup-plugin-html';
|
||||
export default {
|
||||
input: './my-app.js',
|
||||
output: { dir: 'dist' },
|
||||
plugins: [html()],
|
||||
};
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Input from file
|
||||
|
||||
During development you will already have a HTML file which imports your application's modules. You can use give this same file to the plugin using the `inputPath` option, which will bundle any modules inside and output the same HTML minified optimized.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Show example</summary>
|
||||
|
||||
```js
|
||||
import html from '@open-wc/rollup-plugin-html';
|
||||
export default {
|
||||
output: { dir: 'dist' },
|
||||
plugins: [
|
||||
html({
|
||||
inputPath: 'index.html',
|
||||
}),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Input from string
|
||||
|
||||
Sometimes the HTML you want to use as input is not available on the file system. With the `inputHtml` option you can provide the HTML as a string directly. This is useful for example when using rollup from javascript directly.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Show example</summary>
|
||||
|
||||
```js
|
||||
import html from '@open-wc/rollup-plugin-html';
|
||||
export default {
|
||||
output: { dir: 'dist' },
|
||||
plugins: [
|
||||
html({
|
||||
inputHtml: '<html><script type="module" src="./app.js></script></html>',
|
||||
}),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Template
|
||||
|
||||
With the `template` option, you can let the plugin know where to inject the rollup build into. This option can be a string, or an (async) function which returns a string.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Show example</summary>
|
||||
|
||||
Template as a string:
|
||||
|
||||
```js
|
||||
import html from '@open-wc/rollup-plugin-html';
|
||||
export default {
|
||||
output: { dir: 'dist' },
|
||||
plugins: [
|
||||
html({
|
||||
template: `
|
||||
<html>
|
||||
<head><title>My app</title></head>
|
||||
<body></body>
|
||||
</html>`,
|
||||
}),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
Template as a function:
|
||||
|
||||
```js
|
||||
import fs from 'fs';
|
||||
import html from '@open-wc/rollup-plugin-html';
|
||||
|
||||
export default {
|
||||
output: { dir: 'dist' },
|
||||
plugins: [
|
||||
html({
|
||||
template() {
|
||||
return new Promise((resolve) => {
|
||||
const indexPath = path.join(__dirname, 'index.html');
|
||||
fs.readFile(indexPath, 'utf-8', (err, data) => {
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Multiple HTML pages
|
||||
|
||||
With this plugin you can generate as many HTML pages as you want. Rollup will efficiently created shared chunks between pages, allowing you to serve from cache between navigations.
|
||||
|
||||
<details>
|
||||
<summary>View example</summary>
|
||||
|
||||
The easiest way is to have the HTML files with module scripts on disk, for each one you can create an instance of the plugin which will bundle the different entrypoints automatically share common code.
|
||||
|
||||
By default the output filename is taken from the input filename. If you want to create a specific directory structure you need to provide an explicit name:
|
||||
|
||||
```js
|
||||
import html from '@open-wc/rollup-plugin-html';
|
||||
|
||||
export default {
|
||||
output: { dir: 'dist' },
|
||||
plugins: [
|
||||
html({
|
||||
inputPath: './home.html',
|
||||
}),
|
||||
html({
|
||||
inputPath: './about.html',
|
||||
}),
|
||||
html({
|
||||
name: 'articles/a.html',
|
||||
inputPath: './articles/a.html',
|
||||
}),
|
||||
html({
|
||||
name: 'articles/b.html',
|
||||
inputPath: './articles/b.html',
|
||||
}),
|
||||
html({
|
||||
name: 'articles/c.html',
|
||||
inputPath: './articles/c.html',
|
||||
}),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Manually inject build output
|
||||
|
||||
If you want to control how the build output is injected on the page, disable the `inject` option and use the arguments provided to the template function.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Show example</summary>
|
||||
|
||||
With a regular template function:
|
||||
|
||||
```js
|
||||
import html from '@open-wc/rollup-plugin-html';
|
||||
export default {
|
||||
input: './app.js',
|
||||
output: { dir: 'dist' },
|
||||
plugins: [
|
||||
html({
|
||||
name: 'index.html',
|
||||
inject: false,
|
||||
template({ bundle }) {
|
||||
return `
|
||||
<html>
|
||||
<head>
|
||||
${bundle.entrypoints.map(bundle => e =>
|
||||
`<script type="module" src="${e.importPath}"></script>`,
|
||||
)}
|
||||
</head>
|
||||
</html>
|
||||
`;
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
When one of the input options are used, the input html is available in the template function. You can use this to inject the bundle into your existing HTML page:
|
||||
|
||||
```js
|
||||
import html from '@open-wc/rollup-plugin-html';
|
||||
export default {
|
||||
input: './app.js',
|
||||
output: { dir: 'dist' },
|
||||
plugins: [
|
||||
html({
|
||||
inputPath: './index.html',
|
||||
inject: false,
|
||||
template({ inputHtml, bundle }) {
|
||||
return inputHtml.replace(
|
||||
'</body>',
|
||||
`<script type="module" src="${bundle[0].entrypoints[0].importPath}"></script></body>`,
|
||||
);
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Transform output HTML
|
||||
|
||||
You can use the `transform` option to manipulate the output HTML before it's written to disk. This is useful for setting meta tags or environment variables based on input from other sources.
|
||||
|
||||
`transform` can be a single function, or an array. This makes it easy to compose transformations.
|
||||
|
||||
<details>
|
||||
<summary>View example</summary>
|
||||
|
||||
Inject language attribute:
|
||||
|
||||
```js
|
||||
import html from '@open-wc/rollup-plugin-html';
|
||||
export default {
|
||||
output: { dir: 'dist' },
|
||||
plugins: [
|
||||
html({
|
||||
inputPath: './index.html',
|
||||
transform: html => html.replace('<html>', '<html lang="en-GB">'),
|
||||
}),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
Inject language attributes and environment variables:
|
||||
|
||||
```js
|
||||
import html from '@open-wc/rollup-plugin-html';
|
||||
import packageJson from './package.json';
|
||||
|
||||
const watchMode = process.env.ROLLUP_WATCH === 'true';
|
||||
|
||||
export default {
|
||||
output: { dir: 'dist' },
|
||||
plugins: [
|
||||
html({
|
||||
inputPath: './index.html',
|
||||
transform: [
|
||||
html => html.replace('<html>', '<html lang="en-GB">'),
|
||||
html =>
|
||||
html.replace(
|
||||
'<head>',
|
||||
`<head>
|
||||
<script>
|
||||
window.ENVIRONMENT = "${watchMode ? 'DEVELOPMENT' : 'PRODUCTION'}";
|
||||
window.APP_VERSION = "${packageJson.version}";
|
||||
</script>`,
|
||||
),
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Public path
|
||||
|
||||
By default all imports are made relative to the HTML file and expect files to be in the rollup output directory. With the `publicPath` option you can modify where files from the HTML file are requested from.
|
||||
|
||||
<details>
|
||||
<summary>View example</summary>
|
||||
|
||||
```js
|
||||
import html from '@open-wc/rollup-plugin-html';
|
||||
|
||||
export default {
|
||||
output: { dir: 'dist' },
|
||||
plugins: [
|
||||
html({
|
||||
inputPath: './index.html',
|
||||
publicPath: '/static/',
|
||||
}),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Multiple build outputs
|
||||
|
||||
It is possible to create multiple rollup build outputs, and inject both bundles into the same HTML file. This way you can ship multiple bundles to your users, and load the most optimal version for the user's browser.
|
||||
|
||||
<details>
|
||||
<summary>View example</summary>
|
||||
|
||||
To create multiple outputs in rollup, you need to set the `output` option as an array. For each output, you need to create a child plugin from a main plugin.
|
||||
|
||||
```js
|
||||
import html from '@open-wc/rollup-plugin-html';
|
||||
|
||||
const htmlPlugin = html({
|
||||
name: 'index.html',
|
||||
inject: false,
|
||||
template({ inputHtml, bundles }) {
|
||||
return `
|
||||
<html>
|
||||
<body>
|
||||
${bundles[0].entrypoints.map(bundle => e =>
|
||||
`<script type="module" src="${e.importPath}"></script>`,
|
||||
)}
|
||||
|
||||
<script src="./systemjs.js"></script>
|
||||
${bundles[1].entrypoints.map(bundle => e =>
|
||||
`<script nomodule>System.import("${e.importPath}</script>`,
|
||||
)}
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
input: './app.js',
|
||||
output: [
|
||||
{
|
||||
format: 'es',
|
||||
dir: 'dist',
|
||||
plugins: [htmlPlugin.addOutput()],
|
||||
},
|
||||
{
|
||||
format: 'system',
|
||||
dir: 'dist/legacy',
|
||||
plugins: [htmlPlugin.addOutput()],
|
||||
},
|
||||
],
|
||||
plugins: [htmlPlugin],
|
||||
};
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Configuration options
|
||||
|
||||
All configuration options are optional, if an option is not set the plugin will fall back to smart defaults. See below example use cases.
|
||||
|
||||
### name
|
||||
|
||||
Type: `string`
|
||||
|
||||
Name of the generated HTML file. If inputPath is set, defaults to the inputPath filename, otherwise defaults to `index.html`.
|
||||
|
||||
### inputPath
|
||||
|
||||
Type: `string`
|
||||
|
||||
Path to the HTML file to use as input. Modules in this file are bundled and the HTML is used as template for the generated HTML file.
|
||||
|
||||
### inputHtml
|
||||
|
||||
Type: `string`
|
||||
|
||||
Same as `inputPath`, but provides the HTML as a string directly.
|
||||
|
||||
### dir
|
||||
|
||||
Type: `string`
|
||||
|
||||
The directory to output the HTML file into. This defaults to main output directory of your rollup build. If your build has multiple outputs in different directories, this defaults to the lowest directory on the file system..
|
||||
|
||||
### publicPath
|
||||
|
||||
Type: `string`
|
||||
|
||||
Path where static resources are hosted. Any file requests (css, js etc.) from the index.html will be prefixed with the public path.
|
||||
|
||||
### inject
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Whether to inject the rollup bundle into the output HTML. If using one of the input options, only the bundled modules in the HTML file are injected. Otherwise all rollup bundles are injected. Default true. Set this to false if you need to apply some custom logic to how the bundle is injected.
|
||||
|
||||
### minify
|
||||
|
||||
Type: `boolean | object | (html: string) => string | Promise<string>`
|
||||
|
||||
When false, does not do any minifcation. When true, does minifcation with default settings. When an object, does minification with a custom config. When a function, the function is called with the html and should return the minified html. Defaults to true.
|
||||
|
||||
Default minification is done using [html-minifier](https://github.com/kangax/html-minifier). When passing an object, the object is given to `html-minifier` directly so you can use any of the regular minify options.
|
||||
|
||||
### template
|
||||
|
||||
Type: `string | (args: TemplateArgs) => string | Promise<string>`
|
||||
|
||||
Template to inject js bundle into. Can be a string or an (async) function. If an input is set, that is used as default output template. Otherwise defaults to a simple html file.
|
||||
|
||||
For more info see the [configuration type definitions](#configuration-types).
|
||||
|
||||
### transform
|
||||
|
||||
Type: `TransformFunction | TransformFunction[]`
|
||||
|
||||
TransformFunction: `(html: string, args: TransformArgs) => string | Promise<string>`
|
||||
|
||||
Function or array of functions which transform the final HTML output.
|
||||
|
||||
For more info see the [configuration type definitions](#configuration-types).
|
||||
|
||||
## Configuration types
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Full typescript definitions of configuration options</summary>
|
||||
|
||||
```ts
|
||||
import { OutputChunk, OutputOptions, OutputBundle } from 'rollup';
|
||||
|
||||
export interface PluginOptions {
|
||||
name?: string;
|
||||
inputPath?: string;
|
||||
inputHtml?: string;
|
||||
dir?: string;
|
||||
publicPath?: string;
|
||||
inject?: boolean;
|
||||
minify?: boolean | object | MinifyFunction;
|
||||
template?: string | TemplateFunction;
|
||||
transform?: TransformFunction | TransformFunction[];
|
||||
}
|
||||
|
||||
export type MinifyFunction = (html: string) => string | Promise<string>;
|
||||
|
||||
export interface GeneratedBundle {
|
||||
options: OutputOptions;
|
||||
bundle: OutputBundle;
|
||||
}
|
||||
|
||||
export interface EntrypointBundle extends GeneratedBundle {
|
||||
entrypoints: {
|
||||
// path to import the entrypoint, can be used in an import statement
|
||||
// or script tag directly
|
||||
importPath: string;
|
||||
// associated rollup chunk, useful if you need to get more information
|
||||
// about the chunk. See the rollup docs for type definitions
|
||||
chunk: OutputChunk;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface TemplateArgs {
|
||||
// if one of the input options was set, this references the HTML set as input
|
||||
inputHtml?: string;
|
||||
// the rollup bundle to be injected on the page. if there are multiple
|
||||
// rollup output options, this will reference the first bundle
|
||||
//
|
||||
// if one of the input options was set, only the bundled module script contained
|
||||
// in the HTML input are available to be injected in both the bundle and bundles
|
||||
// options
|
||||
bundle: EntrypointBundle;
|
||||
// the rollup bundles to be injected on the page. if there is only one
|
||||
// build output options, this will be an array with one option
|
||||
bundles: EntrypointBundle[];
|
||||
}
|
||||
|
||||
export interface TransformArgs {
|
||||
// see TemplateArgs
|
||||
bundle: EntrypointBundle;
|
||||
// see TemplateArgs
|
||||
bundles: EntrypointBundle[];
|
||||
}
|
||||
|
||||
export type TransformFunction = (html: string, args: TransformArgs) => string | Promise<string>;
|
||||
|
||||
export type TemplateFunction = (args: TemplateArgs) => string | Promise<string>;
|
||||
```
|
||||
|
||||
</details>
|
||||
18
packages/rollup-plugin-html/demo/mpa/index.html
Normal file
18
packages/rollup-plugin-html/demo/mpa/index.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<h1>Index</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/">Index</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/pages/page-a.html">A</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/pages/page-B.html">B</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/pages/page-C.html">C</a>
|
||||
</li>
|
||||
</ul>
|
||||
20
packages/rollup-plugin-html/demo/mpa/pages/page-a.html
Normal file
20
packages/rollup-plugin-html/demo/mpa/pages/page-a.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<h1>Page A</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/">Index</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/pages/page-a.html">A</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/pages/page-B.html">B</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/pages/page-C.html">C</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<script type="module" src="./page-a.js"></script>
|
||||
3
packages/rollup-plugin-html/demo/mpa/pages/page-a.js
Normal file
3
packages/rollup-plugin-html/demo/mpa/pages/page-a.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import './shared.js';
|
||||
|
||||
console.log('page-a.js');
|
||||
20
packages/rollup-plugin-html/demo/mpa/pages/page-b.html
Normal file
20
packages/rollup-plugin-html/demo/mpa/pages/page-b.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<h1>Page B</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/">Index</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/pages/page-a.html">A</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/pages/page-B.html">B</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/pages/page-C.html">C</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<script type="module" src="./page-b.js"></script>
|
||||
3
packages/rollup-plugin-html/demo/mpa/pages/page-b.js
Normal file
3
packages/rollup-plugin-html/demo/mpa/pages/page-b.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import './shared.js';
|
||||
|
||||
console.log('page-c.js');
|
||||
20
packages/rollup-plugin-html/demo/mpa/pages/page-c.html
Normal file
20
packages/rollup-plugin-html/demo/mpa/pages/page-c.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<h1>Page B</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/">Index</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/pages/page-a.html">A</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/pages/page-B.html">B</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/pages/page-C.html">C</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<script type="module" src="./page-c.js"></script>
|
||||
3
packages/rollup-plugin-html/demo/mpa/pages/page-c.js
Normal file
3
packages/rollup-plugin-html/demo/mpa/pages/page-c.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import './shared.js';
|
||||
|
||||
console.log('page-c.js');
|
||||
1
packages/rollup-plugin-html/demo/mpa/pages/shared.js
Normal file
1
packages/rollup-plugin-html/demo/mpa/pages/shared.js
Normal file
@@ -0,0 +1 @@
|
||||
console.log('shared.js');
|
||||
24
packages/rollup-plugin-html/demo/mpa/rollup.config.js
Normal file
24
packages/rollup-plugin-html/demo/mpa/rollup.config.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const html = require('../../rollup-plugin-html');
|
||||
|
||||
module.exports = {
|
||||
output: {
|
||||
dir: './demo/dist',
|
||||
},
|
||||
plugins: [
|
||||
html({
|
||||
inputPath: './demo/mpa/index.html',
|
||||
}),
|
||||
html({
|
||||
inputPath: './demo/mpa/pages/page-a.html',
|
||||
name: 'pages/page-a.html',
|
||||
}),
|
||||
html({
|
||||
inputPath: './demo/mpa/pages/page-b.html',
|
||||
name: 'pages/page-b.html',
|
||||
}),
|
||||
html({
|
||||
inputPath: './demo/mpa/pages/page-c.html',
|
||||
name: 'pages/page-c.html',
|
||||
}),
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
const resolve = require('@rollup/plugin-node-resolve');
|
||||
const html = require('../../rollup-plugin-html');
|
||||
|
||||
module.exports = {
|
||||
input: 'demo/spa/src/my-app.js',
|
||||
output: {
|
||||
dir: './demo/dist',
|
||||
},
|
||||
plugins: [html({})],
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
const html = require('../../rollup-plugin-html');
|
||||
|
||||
module.exports = {
|
||||
output: {
|
||||
dir: './demo/dist',
|
||||
},
|
||||
plugins: [
|
||||
html({
|
||||
inputPath: './demo/spa/index.html',
|
||||
}),
|
||||
],
|
||||
};
|
||||
1
packages/rollup-plugin-html/demo/spa/index.html
Normal file
1
packages/rollup-plugin-html/demo/spa/index.html
Normal file
@@ -0,0 +1 @@
|
||||
<script type="module" src="./src/my-app.js"></script>
|
||||
@@ -0,0 +1,17 @@
|
||||
const html = require('../../rollup-plugin-html');
|
||||
|
||||
module.exports = {
|
||||
input: 'demo/spa/src/my-app.js',
|
||||
output: {
|
||||
dir: './demo/dist',
|
||||
},
|
||||
plugins: [
|
||||
html({
|
||||
template({ bundle }) {
|
||||
return `<h1>Hello custom template</h1>
|
||||
${bundle.entrypoints.map(e => `<script type="module" src="${e.importPath}></script>`)}
|
||||
`;
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
1
packages/rollup-plugin-html/demo/spa/src/lazy-1.js
Normal file
1
packages/rollup-plugin-html/demo/spa/src/lazy-1.js
Normal file
@@ -0,0 +1 @@
|
||||
console.log('lazy-1.js');
|
||||
1
packages/rollup-plugin-html/demo/spa/src/lazy-2.js
Normal file
1
packages/rollup-plugin-html/demo/spa/src/lazy-2.js
Normal file
@@ -0,0 +1 @@
|
||||
console.log('lazy-2.js');
|
||||
4
packages/rollup-plugin-html/demo/spa/src/my-app.js
Normal file
4
packages/rollup-plugin-html/demo/spa/src/my-app.js
Normal file
@@ -0,0 +1,4 @@
|
||||
console.log('my-app.js');
|
||||
|
||||
setTimeout(() => import('./lazy-1.js'), 100);
|
||||
setTimeout(() => import('./lazy-2.js'), 1000);
|
||||
@@ -0,0 +1,13 @@
|
||||
const html = require('../../rollup-plugin-html');
|
||||
|
||||
module.exports = {
|
||||
input: 'demo/spa/src/my-app.js',
|
||||
output: {
|
||||
dir: './demo/dist',
|
||||
},
|
||||
plugins: [
|
||||
html({
|
||||
template: '<h1>Hello custom template</h1>',
|
||||
}),
|
||||
],
|
||||
};
|
||||
55
packages/rollup-plugin-html/package.json
Normal file
55
packages/rollup-plugin-html/package.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "@open-wc/rollup-plugin-html",
|
||||
"version": "0.0.0",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"description": "Plugin for generating an html file with rollup",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/open-wc/open-wc.git",
|
||||
"directory": "packages/rollup-plugin-html"
|
||||
},
|
||||
"author": "open-wc",
|
||||
"homepage": "https://github.com/open-wc/open-wc/",
|
||||
"main": "rollup-plugin-html.js",
|
||||
"scripts": {
|
||||
"demo:mpa": "rm -rf demo/dist && rollup -c demo/mpa/rollup.config.js --watch & yarn serve-demo",
|
||||
"demo:spa": "yarn demo:spa:defaults",
|
||||
"demo:spa:defaults": "rm -rf demo/dist && rollup -c demo/spa/defaults.rollup.config.js --watch & yarn serve-demo",
|
||||
"demo:spa:html-input": "rm -rf demo/dist && rollup -c demo/spa/html-input.rollup.config.js --watch & yarn serve-demo",
|
||||
"demo:spa:manual-inject": "rm -rf demo/dist && rollup -c demo/spa/manual-inject.rollup.config.js --watch & yarn serve-demo",
|
||||
"demo:spa:template": "rm -rf demo/dist && rollup -c demo/spa/template.rollup.config.js --watch & yarn serve-demo",
|
||||
"serve-demo": "es-dev-server --watch --root-dir demo/dist --app-index index.html --compatibility none --open",
|
||||
"test": "npm run test:node",
|
||||
"test:node": "mocha test/**/*.test.js test/*.test.js",
|
||||
"test:update-snapshots": "mocha test/**/*.test.js test/*.test.js --update-snapshots",
|
||||
"test:watch": "npm run test:node -- --watch"
|
||||
},
|
||||
"files": [
|
||||
"*.js",
|
||||
"src"
|
||||
],
|
||||
"keywords": [
|
||||
"rollup-plugin",
|
||||
"minify",
|
||||
"html",
|
||||
"polyfill"
|
||||
],
|
||||
"dependencies": {
|
||||
"@open-wc/building-utils": "^2.14.3",
|
||||
"@types/html-minifier": "^3.5.3",
|
||||
"fs-extra": "^8.1.0",
|
||||
"html-minifier": "^4.0.0",
|
||||
"parse5": "^5.1.1",
|
||||
"terser": "^4.6.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"es-dev-server": "^1.40.1",
|
||||
"lit-element": "^2.2.1",
|
||||
"rimraf": "^3.0.0",
|
||||
"rollup": "^1.31.1"
|
||||
}
|
||||
}
|
||||
179
packages/rollup-plugin-html/rollup-plugin-html.js
Normal file
179
packages/rollup-plugin-html/rollup-plugin-html.js
Normal file
@@ -0,0 +1,179 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
/** @typedef {import('parse5').Document} Document */
|
||||
/** @typedef {import('rollup').Plugin} Plugin */
|
||||
/** @typedef {import('rollup').InputOptions} InputOptions */
|
||||
/** @typedef {import('rollup').OutputOptions} OutputOptions */
|
||||
/** @typedef {import('rollup').OutputBundle} OutputBundle */
|
||||
/** @typedef {import('rollup').OutputChunk} OutputChunk */
|
||||
/** @typedef {import('rollup').EmittedFile} EmittedFile */
|
||||
/** @typedef {import('./src/types').PluginOptions} PluginOptions */
|
||||
/** @typedef {import('./src/types').InputHtmlData} InputHtmlData */
|
||||
/** @typedef {import('./src/types').GeneratedBundle} GeneratedBundle */
|
||||
|
||||
const { getInputHtmlData } = require('./src/getInputHtmlData');
|
||||
const { getEntrypointBundles } = require('./src/getEntrypointBundles');
|
||||
const { getOutputHtml } = require('./src/getOutputHtml');
|
||||
const { extractModules } = require('./src/extractModules');
|
||||
const {
|
||||
createError,
|
||||
getMainOutputDir,
|
||||
addRollupInput,
|
||||
getOutputHtmlFileName,
|
||||
} = require('./src/utils');
|
||||
|
||||
const watchMode = process.env.ROLLUP_WATCH === 'true';
|
||||
|
||||
/**
|
||||
* @param {PluginOptions} pluginOptions
|
||||
* @returns {Plugin & { addOutput: () => Plugin }}
|
||||
*/
|
||||
function rollupPluginHtml(pluginOptions) {
|
||||
pluginOptions = {
|
||||
inject: true,
|
||||
minify: !watchMode,
|
||||
...(pluginOptions || {}),
|
||||
};
|
||||
|
||||
let multiOutput = false;
|
||||
let outputCount = 0;
|
||||
/** @type {string} */
|
||||
let inputHtml;
|
||||
/** @type {string | undefined} */
|
||||
let inputHtmlName;
|
||||
/** @type {string[]} */
|
||||
let inputModuleIds;
|
||||
/** @type {Map<string, string>} */
|
||||
let inlineModules;
|
||||
/** @type {GeneratedBundle[]} */
|
||||
let generatedBundles;
|
||||
|
||||
/**
|
||||
* @returns {Promise<EmittedFile>}
|
||||
*/
|
||||
async function createHtmlAsset() {
|
||||
if (generatedBundles.length === 0) {
|
||||
throw createError('Cannot output HTML when no bundles have been generated');
|
||||
}
|
||||
|
||||
const mainOutputDir = getMainOutputDir(pluginOptions, generatedBundles);
|
||||
const fileName = getOutputHtmlFileName(pluginOptions, inputHtmlName);
|
||||
|
||||
const entrypointBundles = getEntrypointBundles({
|
||||
pluginOptions,
|
||||
generatedBundles,
|
||||
inputModuleIds,
|
||||
mainOutputDir,
|
||||
htmlFileName: fileName,
|
||||
});
|
||||
|
||||
const outputHtml = await getOutputHtml({ pluginOptions, entrypointBundles, inputHtml });
|
||||
return {
|
||||
fileName,
|
||||
source: outputHtml,
|
||||
type: 'asset',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'rollup-plugin-html',
|
||||
|
||||
/**
|
||||
* If an input HTML file is given, extracts modules and adds them as rollup
|
||||
* entrypoints.
|
||||
* @param {InputOptions} rollupInputOptions
|
||||
*/
|
||||
options(rollupInputOptions) {
|
||||
if (!pluginOptions.inputHtml && !pluginOptions.inputPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const inputHtmlData = getInputHtmlData(pluginOptions);
|
||||
const inputHtmlResources = extractModules(inputHtmlData);
|
||||
inputHtmlName = inputHtmlData.name;
|
||||
inputHtml = inputHtmlResources.htmlWithoutModules;
|
||||
inlineModules = inputHtmlResources.inlineModules;
|
||||
|
||||
inputModuleIds = [
|
||||
...inputHtmlResources.moduleImports,
|
||||
...inputHtmlResources.inlineModules.keys(),
|
||||
];
|
||||
|
||||
return addRollupInput(rollupInputOptions, inputModuleIds);
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets state whenever a build starts, since builds can restart in watch mode.
|
||||
* Watches input HTML for file reloads.
|
||||
*/
|
||||
buildStart() {
|
||||
generatedBundles = [];
|
||||
|
||||
if (pluginOptions.inputPath) {
|
||||
this.addWatchFile(pluginOptions.inputPath);
|
||||
}
|
||||
},
|
||||
|
||||
resolveId(id) {
|
||||
if (!id.startsWith('inline-module-')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!inlineModules.has(id)) {
|
||||
throw createError(`Could not find inline module: ${id}`);
|
||||
}
|
||||
|
||||
return id;
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads inline modules extracted from HTML page
|
||||
* @param {string} id
|
||||
*/
|
||||
load(id) {
|
||||
if (!id.startsWith('inline-module-')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return inlineModules.get(id) || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Emits output html file if we are doing a single output build.
|
||||
* @param {OutputOptions} options
|
||||
* @param {OutputBundle} bundle
|
||||
*/
|
||||
async generateBundle(options, bundle) {
|
||||
if (multiOutput) return;
|
||||
generatedBundles.push({ options, bundle });
|
||||
this.emitFile(await createHtmlAsset());
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a sub plugin for tracking multiple build outputs, generating a single index.html
|
||||
* file when both build outputs are finished.
|
||||
*/
|
||||
addOutput() {
|
||||
multiOutput = true;
|
||||
outputCount += 1;
|
||||
|
||||
return {
|
||||
name: `rollup-plugin-html-multi-output-${outputCount}`,
|
||||
|
||||
/**
|
||||
* Stores output bundle, and emits output HTML file if all builds
|
||||
* for a multi build are finished.
|
||||
* @param {OutputOptions} options
|
||||
* @param {OutputBundle} bundle
|
||||
*/
|
||||
async generateBundle(options, bundle) {
|
||||
generatedBundles.push({ options, bundle });
|
||||
if (generatedBundles.length === outputCount) {
|
||||
this.emitFile(await createHtmlAsset());
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = rollupPluginHtml;
|
||||
43
packages/rollup-plugin-html/src/extractModules.js
Normal file
43
packages/rollup-plugin-html/src/extractModules.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/** @typedef {import('./types').InputHtmlData} InputHtmlData */
|
||||
|
||||
const { findJsScripts } = require('@open-wc/building-utils');
|
||||
const path = require('path');
|
||||
const { parse, serialize } = require('parse5');
|
||||
const {
|
||||
getAttribute,
|
||||
getTextContent,
|
||||
remove,
|
||||
} = require('@open-wc/building-utils/dom5-fork/index.js');
|
||||
|
||||
/**
|
||||
* @param {InputHtmlData} inputHtmlData
|
||||
* @param {string} [projectRootDir]
|
||||
*/
|
||||
function extractModules(inputHtmlData, projectRootDir = process.cwd()) {
|
||||
const { inputHtml, name, rootDir: htmlRootDir } = inputHtmlData;
|
||||
const documentAst = parse(inputHtml);
|
||||
const scriptNodes = findJsScripts(documentAst, { jsScripts: true, inlineJsScripts: true });
|
||||
|
||||
/** @type {string[]} */
|
||||
const moduleImports = [];
|
||||
/** @type {Map<string, string>} */
|
||||
const inlineModules = new Map();
|
||||
scriptNodes.forEach((scriptNode, i) => {
|
||||
const src = getAttribute(scriptNode, 'src');
|
||||
|
||||
if (!src) {
|
||||
inlineModules.set(`inline-module-${name.split('.')[0]}-${i}`, getTextContent(scriptNode));
|
||||
} else {
|
||||
const importPath = path.join(src.startsWith('/') ? projectRootDir : htmlRootDir, src);
|
||||
moduleImports.push(importPath);
|
||||
}
|
||||
|
||||
remove(scriptNode);
|
||||
});
|
||||
|
||||
const updatedHtmlString = serialize(documentAst);
|
||||
|
||||
return { moduleImports, inlineModules, htmlWithoutModules: updatedHtmlString };
|
||||
}
|
||||
|
||||
module.exports = { extractModules };
|
||||
85
packages/rollup-plugin-html/src/getEntrypointBundles.js
Normal file
85
packages/rollup-plugin-html/src/getEntrypointBundles.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/** @typedef {import('rollup').OutputChunk} OutputChunk */
|
||||
/** @typedef {import('./types').PluginOptions} PluginOptions */
|
||||
/** @typedef {import('./types').EntrypointBundle} EntrypointBundle */
|
||||
/** @typedef {import('./types').GeneratedBundle} GeneratedBundle */
|
||||
|
||||
const path = require('path');
|
||||
const { createError } = require('./utils');
|
||||
|
||||
/**
|
||||
* @param {object} args
|
||||
* @param {string | undefined} [args.publicPath]
|
||||
* @param {string} args.mainOutputDir
|
||||
* @param {string} args.fileOutputDir
|
||||
* @param {string} args.htmlFileName
|
||||
* @param {string} args.fileName
|
||||
*/
|
||||
function createImportPath({ publicPath, mainOutputDir, fileOutputDir, htmlFileName, fileName }) {
|
||||
const pathFromMainToFileDir = path.relative(mainOutputDir, fileOutputDir);
|
||||
let importPath;
|
||||
if (publicPath) {
|
||||
importPath = path.join(publicPath, pathFromMainToFileDir, fileName);
|
||||
} else {
|
||||
const pathFromHtmlToOutputDir = path.relative(
|
||||
path.dirname(htmlFileName),
|
||||
pathFromMainToFileDir,
|
||||
);
|
||||
importPath = path.join(pathFromHtmlToOutputDir, fileName);
|
||||
}
|
||||
|
||||
if (importPath.startsWith('http') || importPath.startsWith('/') || importPath.startsWith('.')) {
|
||||
return importPath;
|
||||
}
|
||||
return `./${importPath}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} args
|
||||
* @param {PluginOptions} args.pluginOptions
|
||||
* @param {GeneratedBundle[]} args.generatedBundles
|
||||
* @param {string} args.mainOutputDir
|
||||
* @param {string} args.htmlFileName
|
||||
* @param {string[] | undefined} [args.inputModuleIds]
|
||||
*/
|
||||
function getEntrypointBundles({
|
||||
pluginOptions,
|
||||
generatedBundles,
|
||||
inputModuleIds,
|
||||
mainOutputDir,
|
||||
htmlFileName,
|
||||
}) {
|
||||
/** @type {EntrypointBundle[]} */
|
||||
const entrypointBundles = [];
|
||||
|
||||
for (const { options, bundle } of generatedBundles) {
|
||||
if (!options.format) throw createError('Missing module format');
|
||||
|
||||
/** @type {{ importPath: string, chunk: OutputChunk }[]} */
|
||||
const entrypoints = [];
|
||||
for (const chunkOrAsset of Object.values(bundle)) {
|
||||
if (chunkOrAsset.type === 'chunk') {
|
||||
const chunk = /** @type {OutputChunk} */ (chunkOrAsset);
|
||||
if (chunk.isEntry) {
|
||||
if (
|
||||
!inputModuleIds ||
|
||||
(chunk.facadeModuleId && inputModuleIds.includes(chunk.facadeModuleId))
|
||||
) {
|
||||
const importPath = createImportPath({
|
||||
publicPath: pluginOptions.publicPath,
|
||||
mainOutputDir,
|
||||
fileOutputDir: options.dir || '',
|
||||
htmlFileName,
|
||||
fileName: chunkOrAsset.fileName,
|
||||
});
|
||||
entrypoints.push({ importPath, chunk: chunkOrAsset });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
entrypointBundles.push({ options, bundle, entrypoints });
|
||||
}
|
||||
|
||||
return entrypointBundles;
|
||||
}
|
||||
|
||||
module.exports = { getEntrypointBundles, createImportPath };
|
||||
40
packages/rollup-plugin-html/src/getInputHtmlData.js
Normal file
40
packages/rollup-plugin-html/src/getInputHtmlData.js
Normal file
@@ -0,0 +1,40 @@
|
||||
/** @typedef {import('./types').PluginOptions} PluginOptions */
|
||||
/** @typedef {import('./types').InputHtmlData} InputHtmlData */
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const { createError } = require('./utils');
|
||||
|
||||
/**
|
||||
* @param {PluginOptions} pluginOptions
|
||||
* @param {string} rootDir
|
||||
* @returns {InputHtmlData}
|
||||
*/
|
||||
function getInputHtmlData(pluginOptions, rootDir = process.cwd()) {
|
||||
if (pluginOptions.inputHtml) {
|
||||
if (!pluginOptions.name) {
|
||||
throw createError('Must set a name option when providing inputHtml directory.');
|
||||
}
|
||||
|
||||
return {
|
||||
name: pluginOptions.name,
|
||||
rootDir,
|
||||
inputHtml: pluginOptions.inputHtml,
|
||||
};
|
||||
}
|
||||
|
||||
if (!pluginOptions.inputPath) {
|
||||
throw createError('Input must have either a path or content.');
|
||||
}
|
||||
|
||||
const htmlPath = path.resolve(rootDir, pluginOptions.inputPath);
|
||||
if (!fs.existsSync) {
|
||||
throw createError(`Could not find HTML input file at: ${htmlPath}`);
|
||||
}
|
||||
const htmlDir = path.dirname(htmlPath);
|
||||
const inputHtml = fs.readFileSync(htmlPath, 'utf-8');
|
||||
const name = pluginOptions.name || path.basename(pluginOptions.inputPath);
|
||||
return { name, rootDir: htmlDir, inputHtml };
|
||||
}
|
||||
|
||||
module.exports = { getInputHtmlData };
|
||||
68
packages/rollup-plugin-html/src/getOutputHtml.js
Normal file
68
packages/rollup-plugin-html/src/getOutputHtml.js
Normal file
@@ -0,0 +1,68 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
|
||||
/** @typedef {import('./types').PluginOptions} PluginOptions */
|
||||
/** @typedef {import('./types').EntrypointBundle} EntrypointBundle */
|
||||
|
||||
const { injectBundles } = require('./injectBundles');
|
||||
const { minifyHtml } = require('./minifyHtml');
|
||||
|
||||
const defaultHtml = '<!DOCTYPE html><html><head><meta charset="utf-8"></head><body></body></html>';
|
||||
|
||||
/**
|
||||
* @param {object} args
|
||||
* @param {PluginOptions} args.pluginOptions
|
||||
* @param {EntrypointBundle[]} args.entrypointBundles
|
||||
* @param {string | undefined} [args.inputHtml]
|
||||
*/
|
||||
async function getOutputHtml({ pluginOptions, entrypointBundles, inputHtml }) {
|
||||
const { template, inject, minify } = pluginOptions;
|
||||
let outputHtml;
|
||||
|
||||
if (typeof template === 'string') {
|
||||
outputHtml = template;
|
||||
} else if (typeof template === 'function') {
|
||||
outputHtml = await template({
|
||||
inputHtml,
|
||||
bundle: entrypointBundles[0],
|
||||
bundles: entrypointBundles,
|
||||
});
|
||||
} else if (inputHtml) {
|
||||
outputHtml = inputHtml;
|
||||
} else {
|
||||
outputHtml = defaultHtml;
|
||||
}
|
||||
|
||||
// inject build output into HTML
|
||||
if (inject) {
|
||||
outputHtml = injectBundles(outputHtml, entrypointBundles);
|
||||
}
|
||||
|
||||
// transform HTML output
|
||||
if (pluginOptions.transform) {
|
||||
const transforms = Array.isArray(pluginOptions.transform)
|
||||
? pluginOptions.transform
|
||||
: [pluginOptions.transform];
|
||||
|
||||
for (const transform of transforms) {
|
||||
outputHtml = await transform(outputHtml, {
|
||||
bundle: entrypointBundles[0],
|
||||
bundles: entrypointBundles,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// minify final HTML output
|
||||
if (minify) {
|
||||
if (typeof minify === 'function') {
|
||||
outputHtml = await minify(outputHtml);
|
||||
} else if (typeof minify === 'object') {
|
||||
outputHtml = minifyHtml(outputHtml, minify);
|
||||
} else {
|
||||
outputHtml = minifyHtml(outputHtml);
|
||||
}
|
||||
}
|
||||
|
||||
return outputHtml;
|
||||
}
|
||||
|
||||
module.exports = { getOutputHtml };
|
||||
49
packages/rollup-plugin-html/src/injectBundles.js
Normal file
49
packages/rollup-plugin-html/src/injectBundles.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/** @typedef {import('./types').EntrypointBundle} EntrypointBundle */
|
||||
/** @typedef {import('rollup').ModuleFormat} ModuleFormat */
|
||||
|
||||
const { parse, serialize } = require('parse5');
|
||||
const { createScript } = require('@open-wc/building-utils');
|
||||
const { query, predicates, append } = require('@open-wc/building-utils/dom5-fork');
|
||||
const { createError } = require('./utils');
|
||||
|
||||
/**
|
||||
* @param {string} src
|
||||
* @param {ModuleFormat} format
|
||||
*/
|
||||
function createLoadScript(src, format) {
|
||||
if (['es', 'esm', 'module'].includes(format)) {
|
||||
return createScript({ type: 'module', src });
|
||||
}
|
||||
|
||||
if (['system', 'systemjs'].includes(format)) {
|
||||
return createScript({}, `System.import(${JSON.stringify(src)});`);
|
||||
}
|
||||
|
||||
return createScript({ src, defer: '' });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} htmlString
|
||||
* @param {EntrypointBundle[]} entrypointBundles
|
||||
* @returns {string}
|
||||
*/
|
||||
function injectBundles(htmlString, entrypointBundles) {
|
||||
const documentAst = parse(htmlString);
|
||||
const body = query(documentAst, predicates.hasTagName('body'));
|
||||
|
||||
for (const { options, entrypoints } of entrypointBundles) {
|
||||
if (!options.format) throw createError('Missing output format.');
|
||||
|
||||
for (const entrypoint of entrypoints) {
|
||||
append(body, createLoadScript(entrypoint.importPath, options.format));
|
||||
}
|
||||
}
|
||||
|
||||
return serialize(documentAst);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
injectBundles,
|
||||
// export for testing
|
||||
createLoadScript,
|
||||
};
|
||||
24
packages/rollup-plugin-html/src/minifyHtml.js
Normal file
24
packages/rollup-plugin-html/src/minifyHtml.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const Terser = require('terser');
|
||||
const htmlMinifier = require('html-minifier');
|
||||
|
||||
const defaultMinifyHTMLConfig = {
|
||||
collapseWhitespace: true,
|
||||
removeComments: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
useShortDoctype: true,
|
||||
minifyCSS: true,
|
||||
/** @param {string} code */
|
||||
minifyJS: code => Terser.minify(code).code,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} htmlString
|
||||
* @param {object} config
|
||||
*/
|
||||
function minifyHtml(htmlString, config = defaultMinifyHTMLConfig) {
|
||||
return htmlMinifier.minify(htmlString, config);
|
||||
}
|
||||
|
||||
module.exports = { minifyHtml };
|
||||
63
packages/rollup-plugin-html/src/types.d.ts
vendored
Normal file
63
packages/rollup-plugin-html/src/types.d.ts
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
import { OutputChunk, OutputOptions, OutputBundle } from 'rollup';
|
||||
|
||||
export interface PluginOptions {
|
||||
name?: string;
|
||||
inputPath?: string;
|
||||
inputHtml?: string;
|
||||
dir?: string;
|
||||
publicPath?: string;
|
||||
inject?: boolean;
|
||||
minify?: boolean | object | MinifyFunction;
|
||||
template?: string | TemplateFunction;
|
||||
transform?: TransformFunction | TransformFunction[];
|
||||
}
|
||||
|
||||
export type MinifyFunction = (html: string) => string | Promise<string>;
|
||||
|
||||
export interface GeneratedBundle {
|
||||
options: OutputOptions;
|
||||
bundle: OutputBundle;
|
||||
}
|
||||
|
||||
export interface EntrypointBundle extends GeneratedBundle {
|
||||
entrypoints: {
|
||||
// path to import the entrypoint, can be used in an import statement
|
||||
// or script tag directly
|
||||
importPath: string;
|
||||
// associated rollup chunk, useful if you need to get more information
|
||||
// about the chunk. See the rollup docs for type definitions
|
||||
chunk: OutputChunk;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface TemplateArgs {
|
||||
// if one of the input options was set, this references the HTML set as input
|
||||
inputHtml?: string;
|
||||
// the rollup bundle to be injected on the page. if there are multiple
|
||||
// rollup output options, this will reference the first bundle
|
||||
//
|
||||
// if one of the input options was set, only the bundled module script contained
|
||||
// in the HTML input are available to be injected in both the bundle and bundles
|
||||
// options
|
||||
bundle: EntrypointBundle;
|
||||
// the rollup bundles to be injected on the page. if there is only one
|
||||
// build output options, this will be an array with one option
|
||||
bundles: EntrypointBundle[];
|
||||
}
|
||||
|
||||
export interface TransformArgs {
|
||||
// see TemplateArgs
|
||||
bundle: EntrypointBundle;
|
||||
// see TemplateArgs
|
||||
bundles: EntrypointBundle[];
|
||||
}
|
||||
|
||||
export type TransformFunction = (html: string, args: TransformArgs) => string | Promise<string>;
|
||||
|
||||
export type TemplateFunction = (args: TemplateArgs) => string | Promise<string>;
|
||||
|
||||
export interface InputHtmlData {
|
||||
name: string;
|
||||
rootDir: string;
|
||||
inputHtml: string;
|
||||
}
|
||||
98
packages/rollup-plugin-html/src/utils.js
Normal file
98
packages/rollup-plugin-html/src/utils.js
Normal file
@@ -0,0 +1,98 @@
|
||||
/** @typedef {import('rollup').InputOptions} InputOptions */
|
||||
/** @typedef {import('./types').PluginOptions} PluginOptions */
|
||||
/** @typedef {import('./types').GeneratedBundle} GeneratedBundle */
|
||||
|
||||
const PLUGIN = '[rollup-plugin-html]';
|
||||
|
||||
/**
|
||||
* @param {string} msg
|
||||
*/
|
||||
function createError(msg) {
|
||||
return new Error(`${PLUGIN} ${msg}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.fromEntries polyfill.
|
||||
* @template V
|
||||
* @param {[string, V][]} entries
|
||||
*/
|
||||
function fromEntries(entries) {
|
||||
/** @type {Record<string, V>} */
|
||||
const obj = {};
|
||||
for (const [k, v] of entries) {
|
||||
obj[k] = v;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PluginOptions} pluginOptions
|
||||
* @param {GeneratedBundle[]} generatedBundles
|
||||
* @returns {string}
|
||||
*/
|
||||
function getMainOutputDir(pluginOptions, generatedBundles) {
|
||||
const mainOutputDir =
|
||||
// user defined output dir
|
||||
pluginOptions.dir ||
|
||||
// if no used defined output dir, we find the "lowest" output dir, ex. if there are
|
||||
// "dist/legacy" and "dist", we take "dist"
|
||||
generatedBundles.map(b => b.options.dir).sort((a, b) => (a && b ? a.length - b.length : 0))[0];
|
||||
|
||||
if (typeof mainOutputDir !== 'string')
|
||||
throw createError(
|
||||
"Rollup must be configured to output in a directory: html({ outputDir: 'dist' })",
|
||||
);
|
||||
return mainOutputDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {InputOptions} inputOptions
|
||||
* @param {string[]} inputModuleIds
|
||||
* @returns {InputOptions}
|
||||
*/
|
||||
function addRollupInput(inputOptions, inputModuleIds) {
|
||||
// Add input module ids to existing input option, whether it's a string, array or object
|
||||
// this way you can use multiple html plugins all adding their own inputs
|
||||
if (!inputOptions.input) {
|
||||
return { ...inputOptions, input: inputModuleIds };
|
||||
}
|
||||
|
||||
if (typeof inputOptions.input === 'string') {
|
||||
return { ...inputOptions, input: [inputOptions.input, ...inputModuleIds] };
|
||||
}
|
||||
|
||||
if (Array.isArray(inputOptions.input)) {
|
||||
return { ...inputOptions, input: [...inputOptions.input, ...inputModuleIds] };
|
||||
}
|
||||
|
||||
if (typeof inputOptions.input === 'object') {
|
||||
return {
|
||||
...inputOptions,
|
||||
input: {
|
||||
...inputOptions.input,
|
||||
...fromEntries(inputModuleIds.map(i => [i, i])),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
throw createError(`Unknown rollup input type. Supported inputs are string, array and object.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PluginOptions} pluginOptions
|
||||
* @param {string} [inputHtmlName]
|
||||
*/
|
||||
function getOutputHtmlFileName(pluginOptions, inputHtmlName) {
|
||||
if (pluginOptions.name) {
|
||||
return pluginOptions.name;
|
||||
}
|
||||
return inputHtmlName || 'index.html';
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createError,
|
||||
getMainOutputDir,
|
||||
fromEntries,
|
||||
addRollupInput,
|
||||
getOutputHtmlFileName,
|
||||
};
|
||||
3
packages/rollup-plugin-html/test/fixtures/getInputHtmlData/index.html
vendored
Normal file
3
packages/rollup-plugin-html/test/fixtures/getInputHtmlData/index.html
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<html>index.html
|
||||
|
||||
</html>
|
||||
3
packages/rollup-plugin-html/test/fixtures/getInputHtmlData/pages/page-a.html
vendored
Normal file
3
packages/rollup-plugin-html/test/fixtures/getInputHtmlData/pages/page-a.html
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<html>page-a.html
|
||||
|
||||
</html>
|
||||
3
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/entrypoint-a.js
vendored
Normal file
3
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/entrypoint-a.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import './modules/module-a.js';
|
||||
|
||||
console.log('entrypoint-a.js');
|
||||
3
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/entrypoint-b.js
vendored
Normal file
3
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/entrypoint-b.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import './modules/module-b.js';
|
||||
|
||||
console.log('entrypoint-b.js');
|
||||
3
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/entrypoint-c.js
vendored
Normal file
3
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/entrypoint-c.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import './modules/module-c.js';
|
||||
|
||||
console.log('entrypoint-c.js');
|
||||
3
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/index.html
vendored
Normal file
3
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/index.html
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<h1>hello world</h1>
|
||||
<script type="module" src="./entrypoint-a.js"></script>
|
||||
<script type="module" src="./entrypoint-b.js"></script>
|
||||
0
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/module.js
vendored
Normal file
0
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/module.js
vendored
Normal file
3
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/modules/module-a.js
vendored
Normal file
3
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/modules/module-a.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import './shared-module.js';
|
||||
|
||||
console.log('module-a.js');
|
||||
3
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/modules/module-b.js
vendored
Normal file
3
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/modules/module-b.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import './shared-module.js';
|
||||
|
||||
console.log('module-b.js');
|
||||
3
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/modules/module-c.js
vendored
Normal file
3
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/modules/module-c.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import './shared-module.js';
|
||||
|
||||
console.log('module-b.js');
|
||||
1
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/modules/shared-module.js
vendored
Normal file
1
packages/rollup-plugin-html/test/fixtures/rollup-plugin-html/modules/shared-module.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
console.log('shared-module.js');
|
||||
470
packages/rollup-plugin-html/test/rollup-plugin-html.test.js
Normal file
470
packages/rollup-plugin-html/test/rollup-plugin-html.test.js
Normal file
@@ -0,0 +1,470 @@
|
||||
/** @typedef {import('rollup').OutputChunk} OutputChunk */
|
||||
/** @typedef {import('rollup').OutputAsset} OutputAsset */
|
||||
/** @typedef {(OutputChunk | OutputAsset)[]} Output */
|
||||
|
||||
const rollup = require('rollup');
|
||||
const { expect } = require('chai');
|
||||
const htmlPlugin = require('../rollup-plugin-html');
|
||||
|
||||
/**
|
||||
* @param {Output} output
|
||||
* @param {string} name
|
||||
* @returns {OutputChunk}
|
||||
*/
|
||||
function getChunk(output, name) {
|
||||
return /** @type {OutputChunk} */ (output.find(o => o.fileName === name && o.type === 'chunk'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Output} output
|
||||
* @param {string} name
|
||||
* @returns {OutputAsset & { source: string }}
|
||||
*/
|
||||
function getAsset(output, name) {
|
||||
return /** @type {OutputAsset & { source: string }} */ (output.find(
|
||||
o => o.fileName === name && o.type === 'asset',
|
||||
));
|
||||
}
|
||||
|
||||
/** @type {any} */
|
||||
const outputConfig = {
|
||||
format: 'es',
|
||||
dir: 'dist',
|
||||
};
|
||||
|
||||
describe('rollup-plugin-html', () => {
|
||||
it('can build an app with rollup bundle injected into a default HTML page and filename', async () => {
|
||||
const config = {
|
||||
input: './test/fixtures/rollup-plugin-html/entrypoint-a.js',
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
minify: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
const bundle = await rollup.rollup(config);
|
||||
const { output } = await bundle.generate(outputConfig);
|
||||
|
||||
expect(output.length).to.equal(2);
|
||||
expect(getChunk(output, 'entrypoint-a.js').code).to.include("console.log('entrypoint-a.js');");
|
||||
expect(getAsset(output, 'index.html').source).to.equal(
|
||||
'<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>' +
|
||||
'<script type="module" src="./entrypoint-a.js"></script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can build with html file as input', async () => {
|
||||
const config = {
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
inputPath: 'test/fixtures/rollup-plugin-html/index.html',
|
||||
minify: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
const bundle = await rollup.rollup(config);
|
||||
const { output } = await bundle.generate(outputConfig);
|
||||
|
||||
expect(output.length).to.equal(4);
|
||||
const { code: entryA } = getChunk(output, 'entrypoint-a.js');
|
||||
const { code: entryB } = getChunk(output, 'entrypoint-b.js');
|
||||
expect(entryA).to.include("console.log('entrypoint-a.js');");
|
||||
expect(entryB).to.include("console.log('entrypoint-b.js');");
|
||||
expect(getAsset(output, 'index.html').source).to.equal(
|
||||
'<html><head></head><body><h1>hello world</h1>\n\n' +
|
||||
'<script type="module" src="./entrypoint-a.js"></script>' +
|
||||
'<script type="module" src="./entrypoint-b.js"></script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can build with a html string as input', async () => {
|
||||
const config = {
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
name: 'index.html',
|
||||
inputHtml:
|
||||
'<h1>Hello world</h1>' +
|
||||
'<script type="module" src="./test/fixtures/rollup-plugin-html/entrypoint-a.js"></script>',
|
||||
minify: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
const bundle = await rollup.rollup(config);
|
||||
const { output } = await bundle.generate(outputConfig);
|
||||
|
||||
expect(output.length).to.equal(2);
|
||||
expect(getAsset(output, 'index.html').source).to.equal(
|
||||
'<html><head></head><body><h1>Hello world</h1>' +
|
||||
'<script type="module" src="./entrypoint-a.js"></script></body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can build with inline modules', async () => {
|
||||
const config = {
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
name: 'index.html',
|
||||
inputHtml:
|
||||
'<h1>Hello world</h1>' +
|
||||
'<script type="module">import "./test/fixtures/rollup-plugin-html/entrypoint-a.js";</script>',
|
||||
minify: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
const bundle = await rollup.rollup(config);
|
||||
const { output } = await bundle.generate(outputConfig);
|
||||
|
||||
expect(output.length).to.equal(2);
|
||||
const { code: appCode } = getChunk(output, 'inline-module-index-0.js');
|
||||
expect(appCode).to.include("console.log('entrypoint-a.js');");
|
||||
expect(getAsset(output, 'index.html').source).to.equal(
|
||||
'<html><head></head><body><h1>Hello world</h1>' +
|
||||
'<script type="module" src="./inline-module-index-0.js"></script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can build with js input and generated html output', async () => {
|
||||
const config = {
|
||||
input: './test/fixtures/rollup-plugin-html/entrypoint-a.js',
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
name: 'index.html',
|
||||
inject: false,
|
||||
template({ bundle }) {
|
||||
return `<h1>Hello world</h1>${bundle.entrypoints.map(
|
||||
e => `<script type="module" src="${e.importPath}"></script>`,
|
||||
)}`;
|
||||
},
|
||||
minify: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
const bundle = await rollup.rollup(config);
|
||||
const { output } = await bundle.generate(outputConfig);
|
||||
|
||||
expect(output.length).to.equal(2);
|
||||
expect(getAsset(output, 'index.html').source).to.equal(
|
||||
'<h1>Hello world</h1><script type="module" src="./entrypoint-a.js"></script>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can build transforming final output', async () => {
|
||||
const config = {
|
||||
input: './test/fixtures/rollup-plugin-html/entrypoint-a.js',
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
name: 'index.html',
|
||||
inputHtml:
|
||||
'<h1>Hello world</h1>' +
|
||||
'<script type="module" src="./test/fixtures/rollup-plugin-html/entrypoint-a.js"></script>',
|
||||
transform(html) {
|
||||
return html.replace('Hello world', 'Goodbye world');
|
||||
},
|
||||
minify: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
const bundle = await rollup.rollup(config);
|
||||
const { output } = await bundle.generate(outputConfig);
|
||||
|
||||
expect(output.length).to.equal(2);
|
||||
expect(getAsset(output, 'index.html').source).to.equal(
|
||||
'<html><head></head><body><h1>Goodbye world</h1>' +
|
||||
'<script type="module" src="./entrypoint-a.js"></script></body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can build with a public path', async () => {
|
||||
const config = {
|
||||
input: './test/fixtures/rollup-plugin-html/entrypoint-a.js',
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
name: 'index.html',
|
||||
inputHtml:
|
||||
'<h1>Hello world</h1>' +
|
||||
'<script type="module" src="./test/fixtures/rollup-plugin-html/entrypoint-a.js"></script>',
|
||||
publicPath: '/static/',
|
||||
minify: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
const bundle = await rollup.rollup(config);
|
||||
const { output } = await bundle.generate(outputConfig);
|
||||
|
||||
expect(output.length).to.equal(2);
|
||||
expect(getAsset(output, 'index.html').source).to.equal(
|
||||
'<html><head></head><body><h1>Hello world</h1>' +
|
||||
'<script type="module" src="/static/entrypoint-a.js"></script></body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can build with a public path with a file in a directory', async () => {
|
||||
const config = {
|
||||
input: './test/fixtures/rollup-plugin-html/entrypoint-a.js',
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
name: 'pages/index.html',
|
||||
inputHtml:
|
||||
'<h1>Hello world</h1>' +
|
||||
'<script type="module" src="./test/fixtures/rollup-plugin-html/entrypoint-a.js"></script>',
|
||||
publicPath: '/static/',
|
||||
minify: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
const bundle = await rollup.rollup(config);
|
||||
const { output } = await bundle.generate(outputConfig);
|
||||
|
||||
expect(output.length).to.equal(2);
|
||||
expect(getAsset(output, 'pages/index.html').source).to.equal(
|
||||
'<html><head></head><body><h1>Hello world</h1>' +
|
||||
'<script type="module" src="/static/entrypoint-a.js"></script></body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can build with multiple build outputs', async () => {
|
||||
const plugin = htmlPlugin({
|
||||
name: 'index.html',
|
||||
inputHtml:
|
||||
'<h1>Hello world</h1>' +
|
||||
'<script type="module" src="./test/fixtures/rollup-plugin-html/entrypoint-a.js"></script>',
|
||||
publicPath: '/static/',
|
||||
minify: false,
|
||||
});
|
||||
|
||||
const config = {
|
||||
input: './test/fixtures/rollup-plugin-html/entrypoint-a.js',
|
||||
plugins: [plugin],
|
||||
};
|
||||
|
||||
const build = await rollup.rollup(config);
|
||||
const bundleA = build.generate({
|
||||
format: 'system',
|
||||
dir: 'dist/legacy',
|
||||
plugins: [plugin.addOutput()],
|
||||
});
|
||||
const bundleB = build.generate({
|
||||
format: 'es',
|
||||
dir: 'dist',
|
||||
plugins: [plugin.addOutput()],
|
||||
});
|
||||
|
||||
const { output: outputA } = await bundleA;
|
||||
const { output: outputB } = await bundleB;
|
||||
|
||||
expect(outputA.length).to.equal(1);
|
||||
expect(outputB.length).to.equal(2);
|
||||
const { code: entrypointA1 } = getChunk(outputA, 'entrypoint-a.js');
|
||||
const { code: entrypointA2 } = getChunk(outputB, 'entrypoint-a.js');
|
||||
expect(entrypointA1).to.include("console.log('entrypoint-a.js');");
|
||||
expect(entrypointA1).to.include("console.log('module-a.js');");
|
||||
expect(entrypointA2).to.include("console.log('entrypoint-a.js');");
|
||||
expect(entrypointA2).to.include("console.log('module-a.js');");
|
||||
expect(getAsset(outputA, 'index.html')).to.not.exist;
|
||||
expect(getAsset(outputB, 'index.html').source).to.equal(
|
||||
'<html><head></head><body><h1>Hello world</h1>' +
|
||||
'<script>System.import("/static/legacy/entrypoint-a.js");</script>' +
|
||||
'<script type="module" src="/static/entrypoint-a.js"></script></body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can build with index.html as input and an extra html file as output', async () => {
|
||||
const config = {
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
name: 'index.html',
|
||||
inputHtml:
|
||||
'<h1>Hello world</h1>' +
|
||||
'<script type="module" src="./test/fixtures/rollup-plugin-html/entrypoint-a.js"></script>',
|
||||
minify: false,
|
||||
}),
|
||||
|
||||
htmlPlugin({
|
||||
name: 'foo.html',
|
||||
template: '<html><body><h1>foo.html</h1></body></html>',
|
||||
minify: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
const bundle = await rollup.rollup(config);
|
||||
const { output } = await bundle.generate(outputConfig);
|
||||
|
||||
expect(output.length).to.equal(3);
|
||||
expect(getChunk(output, 'entrypoint-a.js')).to.exist;
|
||||
expect(getAsset(output, 'index.html').source).to.equal(
|
||||
'<html><head></head><body><h1>Hello world</h1>' +
|
||||
'<script type="module" src="./entrypoint-a.js"></script></body></html>',
|
||||
);
|
||||
expect(getAsset(output, 'foo.html').source).to.equal(
|
||||
'<html><head></head><body><h1>foo.html</h1>' +
|
||||
'<script type="module" src="./entrypoint-a.js"></script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can build with html file as input', async () => {
|
||||
const config = {
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
inputPath: 'test/fixtures/rollup-plugin-html/index.html',
|
||||
minify: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
const bundle = await rollup.rollup(config);
|
||||
const { output } = await bundle.generate(outputConfig);
|
||||
|
||||
expect(output.length).to.equal(4);
|
||||
const { code: entryA } = getChunk(output, 'entrypoint-a.js');
|
||||
const { code: entryB } = getChunk(output, 'entrypoint-b.js');
|
||||
expect(entryA).to.include("console.log('entrypoint-a.js');");
|
||||
expect(entryB).to.include("console.log('entrypoint-b.js');");
|
||||
expect(getAsset(output, 'index.html').source).to.equal(
|
||||
'<html><head></head><body><h1>hello world</h1>\n\n' +
|
||||
'<script type="module" src="./entrypoint-a.js"></script>' +
|
||||
'<script type="module" src="./entrypoint-b.js"></script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can build with js input, injecting the same bundle into multiple html files', async () => {
|
||||
const config = {
|
||||
input: './test/fixtures/rollup-plugin-html/entrypoint-a.js',
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
name: 'page-a.html',
|
||||
template: '<h1>Page A</h1>',
|
||||
minify: false,
|
||||
}),
|
||||
|
||||
htmlPlugin({
|
||||
name: 'page-b.html',
|
||||
template: '<h1>Page B</h1>',
|
||||
minify: false,
|
||||
}),
|
||||
|
||||
htmlPlugin({
|
||||
name: 'page-c.html',
|
||||
template: '<h1>Page C</h1>',
|
||||
minify: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
const bundle = await rollup.rollup(config);
|
||||
const { output } = await bundle.generate(outputConfig);
|
||||
|
||||
expect(output.length).to.equal(4);
|
||||
expect(getChunk(output, 'entrypoint-a.js')).to.exist;
|
||||
expect(getAsset(output, 'page-a.html').source).to.equal(
|
||||
'<html><head></head><body><h1>Page A</h1>' +
|
||||
'<script type="module" src="./entrypoint-a.js"></script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
expect(getAsset(output, 'page-b.html').source).to.equal(
|
||||
'<html><head></head><body><h1>Page B</h1>' +
|
||||
'<script type="module" src="./entrypoint-a.js"></script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
expect(getAsset(output, 'page-c.html').source).to.equal(
|
||||
'<html><head></head><body><h1>Page C</h1>' +
|
||||
'<script type="module" src="./entrypoint-a.js"></script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can build with multiple html inputs', async () => {
|
||||
const config = {
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
name: 'page-a.html',
|
||||
inputHtml:
|
||||
'<h1>Page A</h1><script type="module" src="./test/fixtures/rollup-plugin-html/entrypoint-a.js"></script>',
|
||||
minify: false,
|
||||
}),
|
||||
htmlPlugin({
|
||||
name: 'page-b.html',
|
||||
inputHtml:
|
||||
'<h1>Page B</h1><script type="module" src="./test/fixtures/rollup-plugin-html/entrypoint-b.js"></script>',
|
||||
minify: false,
|
||||
}),
|
||||
htmlPlugin({
|
||||
name: 'page-c.html',
|
||||
inputHtml:
|
||||
'<h1>Page C</h1><script type="module" src="./test/fixtures/rollup-plugin-html/entrypoint-c.js"></script>',
|
||||
minify: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
const bundle = await rollup.rollup(config);
|
||||
const { output } = await bundle.generate(outputConfig);
|
||||
|
||||
expect(output.length).to.equal(7);
|
||||
expect(getChunk(output, 'entrypoint-a.js')).to.exist;
|
||||
expect(getChunk(output, 'entrypoint-b.js')).to.exist;
|
||||
expect(getChunk(output, 'entrypoint-c.js')).to.exist;
|
||||
expect(getAsset(output, 'page-a.html').source).to.equal(
|
||||
'<html><head></head><body><h1>Page A</h1><script type="module" src="./entrypoint-a.js"></script></body></html>',
|
||||
);
|
||||
expect(getAsset(output, 'page-b.html').source).to.equal(
|
||||
'<html><head></head><body><h1>Page B</h1><script type="module" src="./entrypoint-b.js"></script></body></html>',
|
||||
);
|
||||
expect(getAsset(output, 'page-c.html').source).to.equal(
|
||||
'<html><head></head><body><h1>Page C</h1><script type="module" src="./entrypoint-c.js"></script></body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('outputs the hashed entrypoint name', async () => {
|
||||
const config = {
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
name: 'index.html',
|
||||
inputHtml:
|
||||
'<h1>Hello world</h1>' +
|
||||
'<script type="module" src="./test/fixtures/rollup-plugin-html/entrypoint-a.js"></script>',
|
||||
minify: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
const bundle = await rollup.rollup(config);
|
||||
const { output } = await bundle.generate({
|
||||
...outputConfig,
|
||||
entryFileNames: '[name]-[hash].js',
|
||||
});
|
||||
|
||||
expect(output.length).to.equal(2);
|
||||
const entrypoint = /** @type {OutputChunk} */ (output.find(f =>
|
||||
// @ts-ignore
|
||||
f.facadeModuleId.endsWith('entrypoint-a.js'),
|
||||
));
|
||||
// ensure it's actually hashed
|
||||
expect(entrypoint.fileName).to.not.equal('entrypoint-a.js');
|
||||
// get hashed name dynamically
|
||||
expect(getAsset(output, 'index.html').source).to.equal(
|
||||
`<html><head></head><body><h1>Hello world</h1><script type="module" src="./${entrypoint.fileName}"></script></body></html>`,
|
||||
);
|
||||
});
|
||||
|
||||
it('outputs import path relative to the final output html', async () => {
|
||||
const config = {
|
||||
plugins: [
|
||||
htmlPlugin({
|
||||
name: 'pages/index.html',
|
||||
inputHtml:
|
||||
'<h1>Hello world</h1>' +
|
||||
'<script type="module" src="./test/fixtures/rollup-plugin-html/entrypoint-a.js"></script>',
|
||||
minify: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
const bundle = await rollup.rollup(config);
|
||||
const { output } = await bundle.generate(outputConfig);
|
||||
|
||||
expect(output.length).to.equal(2);
|
||||
expect(getAsset(output, 'pages/index.html').source).to.equal(
|
||||
'<html><head></head><body><h1>Hello world</h1><script type="module" src="../entrypoint-a.js"></script></body></html>',
|
||||
);
|
||||
});
|
||||
});
|
||||
92
packages/rollup-plugin-html/test/src/extractModules.test.js
Normal file
92
packages/rollup-plugin-html/test/src/extractModules.test.js
Normal file
@@ -0,0 +1,92 @@
|
||||
const { expect } = require('chai');
|
||||
const { extractModules } = require('../../src/extractModules');
|
||||
|
||||
describe('extractModules()', () => {
|
||||
it('extracts all modules from a html document', () => {
|
||||
const { moduleImports, inlineModules, htmlWithoutModules } = extractModules(
|
||||
{
|
||||
name: 'index.html',
|
||||
inputHtml:
|
||||
'<div>before</div>' +
|
||||
'<script type="module" src="./foo.js"></script>' +
|
||||
'<script type="module" src="/bar.js"></script>' +
|
||||
'<div>after</div>',
|
||||
rootDir: '/',
|
||||
},
|
||||
'/',
|
||||
);
|
||||
|
||||
expect(inlineModules.size).to.equal(0);
|
||||
expect(moduleImports).to.eql(['/foo.js', '/bar.js']);
|
||||
expect(htmlWithoutModules).to.eql(
|
||||
'<html><head></head><body><div>before</div><div>after</div></body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('resolves imports relative to the root dir', () => {
|
||||
const { moduleImports, inlineModules, htmlWithoutModules } = extractModules(
|
||||
{
|
||||
name: 'index.html',
|
||||
inputHtml:
|
||||
'<div>before</div>' +
|
||||
'<script type="module" src="./foo.js"></script>' +
|
||||
'<script type="module" src="/bar.js"></script>' +
|
||||
'<div>after</div>',
|
||||
rootDir: '/base/',
|
||||
},
|
||||
'/base/',
|
||||
);
|
||||
|
||||
expect(inlineModules.size).to.equal(0);
|
||||
expect(moduleImports).to.eql(['/base/foo.js', '/base/bar.js']);
|
||||
expect(htmlWithoutModules).to.eql(
|
||||
'<html><head></head><body><div>before</div><div>after</div></body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('resolves relative imports relative to the relative import base', () => {
|
||||
const { moduleImports, inlineModules, htmlWithoutModules } = extractModules(
|
||||
{
|
||||
name: 'index.html',
|
||||
inputHtml:
|
||||
'<div>before</div>' +
|
||||
'<script type="module" src="./foo.js"></script>' +
|
||||
'<script type="module" src="/bar.js"></script>' +
|
||||
'<div>after</div>',
|
||||
rootDir: '/base-1/base-2/',
|
||||
},
|
||||
'/base-1/',
|
||||
);
|
||||
|
||||
expect(inlineModules.size).to.equal(0);
|
||||
expect(moduleImports).to.eql(['/base-1/base-2/foo.js', '/base-1/bar.js']);
|
||||
expect(htmlWithoutModules).to.eql(
|
||||
'<html><head></head><body><div>before</div><div>after</div></body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('extracts all inline modules from a html document', () => {
|
||||
const { moduleImports, inlineModules, htmlWithoutModules } = extractModules(
|
||||
{
|
||||
name: 'index.html',
|
||||
inputHtml:
|
||||
'<div>before</div>' +
|
||||
'<script type="module">/* my module 1 */</script>' +
|
||||
'<script type="module">/* my module 2 */</script>' +
|
||||
'<div>after</div>',
|
||||
rootDir: '/base-1/base-2/',
|
||||
},
|
||||
|
||||
'/',
|
||||
);
|
||||
|
||||
expect([...inlineModules.entries()]).to.eql([
|
||||
['inline-module-index-0', '/* my module 1 */'],
|
||||
['inline-module-index-1', '/* my module 2 */'],
|
||||
]);
|
||||
expect(moduleImports).to.eql([]);
|
||||
expect(htmlWithoutModules).to.eql(
|
||||
'<html><head></head><body><div>before</div><div>after</div></body></html>',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,346 @@
|
||||
/** @typedef {import('../../src/types').GeneratedBundle} GeneratedBundle */
|
||||
|
||||
const { expect } = require('chai');
|
||||
const { getEntrypointBundles, createImportPath } = require('../../src/getEntrypointBundles');
|
||||
|
||||
describe('createImportPath()', () => {
|
||||
it('creates a relative import path', () => {
|
||||
expect(
|
||||
createImportPath({
|
||||
mainOutputDir: 'dist',
|
||||
fileOutputDir: 'dist',
|
||||
htmlFileName: 'index.html',
|
||||
fileName: 'foo.js',
|
||||
}),
|
||||
).to.equal('./foo.js');
|
||||
});
|
||||
|
||||
it('handles files output in a different directory', () => {
|
||||
expect(
|
||||
createImportPath({
|
||||
mainOutputDir: 'dist',
|
||||
fileOutputDir: 'dist/legacy',
|
||||
htmlFileName: 'index.html',
|
||||
fileName: 'foo.js',
|
||||
}),
|
||||
).to.equal('./legacy/foo.js');
|
||||
});
|
||||
|
||||
it('handles directory in filename', () => {
|
||||
expect(
|
||||
createImportPath({
|
||||
mainOutputDir: 'dist',
|
||||
fileOutputDir: 'dist',
|
||||
htmlFileName: 'index.html',
|
||||
fileName: 'legacy/foo.js',
|
||||
}),
|
||||
).to.equal('./legacy/foo.js');
|
||||
});
|
||||
|
||||
it('allows configuring a public path', () => {
|
||||
expect(
|
||||
createImportPath({
|
||||
publicPath: 'static',
|
||||
mainOutputDir: 'dist',
|
||||
fileOutputDir: 'dist',
|
||||
htmlFileName: 'index.html',
|
||||
fileName: 'foo.js',
|
||||
}),
|
||||
).to.equal('./static/foo.js');
|
||||
});
|
||||
|
||||
it('allows configuring an absolute public path', () => {
|
||||
expect(
|
||||
createImportPath({
|
||||
publicPath: '/static',
|
||||
mainOutputDir: 'dist',
|
||||
fileOutputDir: 'dist',
|
||||
htmlFileName: 'index.html',
|
||||
fileName: 'foo.js',
|
||||
}),
|
||||
).to.equal('/static/foo.js');
|
||||
});
|
||||
|
||||
it('allows configuring an absolute public path with just a /', () => {
|
||||
expect(
|
||||
createImportPath({
|
||||
publicPath: '/',
|
||||
mainOutputDir: 'dist',
|
||||
fileOutputDir: 'dist',
|
||||
htmlFileName: 'index.html',
|
||||
fileName: 'foo.js',
|
||||
}),
|
||||
).to.equal('/foo.js');
|
||||
});
|
||||
|
||||
it('allows configuring an absolute public path with a trailing /', () => {
|
||||
expect(
|
||||
createImportPath({
|
||||
publicPath: '/static/public/',
|
||||
mainOutputDir: 'dist',
|
||||
fileOutputDir: 'dist',
|
||||
htmlFileName: 'index.html',
|
||||
fileName: 'foo.js',
|
||||
}),
|
||||
).to.equal('/static/public/foo.js');
|
||||
});
|
||||
|
||||
it('respects a different output dir when configuring a public path', () => {
|
||||
expect(
|
||||
createImportPath({
|
||||
publicPath: '/static',
|
||||
mainOutputDir: 'dist',
|
||||
fileOutputDir: 'dist/legacy',
|
||||
htmlFileName: 'index.html',
|
||||
fileName: 'foo.js',
|
||||
}),
|
||||
).to.equal('/static/legacy/foo.js');
|
||||
});
|
||||
|
||||
it('when html is output in a directory, creates a relative path from the html file to the js file', () => {
|
||||
expect(
|
||||
createImportPath({
|
||||
mainOutputDir: 'dist',
|
||||
fileOutputDir: 'dist',
|
||||
htmlFileName: 'pages/index.html',
|
||||
fileName: 'foo.js',
|
||||
}),
|
||||
).to.equal('../foo.js');
|
||||
});
|
||||
|
||||
it('when html is output in a directory and absolute path is set, creates a direct path from the root to the js file', () => {
|
||||
expect(
|
||||
createImportPath({
|
||||
publicPath: '/static/',
|
||||
mainOutputDir: 'dist',
|
||||
fileOutputDir: 'dist',
|
||||
htmlFileName: 'pages/index.html',
|
||||
fileName: 'foo.js',
|
||||
}),
|
||||
).to.equal('/static/foo.js');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getEntrypointBundles()', () => {
|
||||
/** @type {GeneratedBundle[]} */
|
||||
const defaultBundles = [
|
||||
{
|
||||
options: { format: 'es', dir: 'dist' },
|
||||
bundle: {
|
||||
// @ts-ignore
|
||||
'app.js': {
|
||||
isEntry: true,
|
||||
fileName: 'app.js',
|
||||
facadeModuleId: '/root/app.js',
|
||||
type: 'chunk',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const defaultOptions = {
|
||||
pluginOptions: {},
|
||||
inputModuleIds: ['/root/app.js', '/root/foo.js'],
|
||||
mainOutputDir: 'dist',
|
||||
htmlFileName: 'index.html',
|
||||
generatedBundles: defaultBundles,
|
||||
};
|
||||
|
||||
it('generates entrypoints for a simple project', async () => {
|
||||
const output = await getEntrypointBundles(defaultOptions);
|
||||
expect(output.length).to.equal(1);
|
||||
expect(output[0].options).to.equal(defaultBundles[0].options);
|
||||
expect(output[0].bundle).to.equal(defaultBundles[0].bundle);
|
||||
expect(output[0].entrypoints.length).to.equal(1);
|
||||
expect(output[0].entrypoints[0].chunk).to.equal(defaultBundles[0].bundle['app.js']);
|
||||
expect(output[0].entrypoints.map(e => e.importPath)).to.eql(['./app.js']);
|
||||
});
|
||||
|
||||
it('does not output non-entrypoints', async () => {
|
||||
/** @type {GeneratedBundle[]} */
|
||||
const generatedBundles = [
|
||||
{
|
||||
options: { format: 'es', dir: 'dist' },
|
||||
bundle: {
|
||||
// @ts-ignore
|
||||
'app.js': {
|
||||
isEntry: true,
|
||||
fileName: 'app.js',
|
||||
facadeModuleId: '/root/app.js',
|
||||
type: 'chunk',
|
||||
},
|
||||
// @ts-ignore
|
||||
'not-app.js': {
|
||||
isEntry: false,
|
||||
fileName: 'not-app.js',
|
||||
facadeModuleId: '/root/app.js',
|
||||
type: 'chunk',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
const output = await getEntrypointBundles({
|
||||
...defaultOptions,
|
||||
generatedBundles,
|
||||
});
|
||||
expect(output.length).to.equal(1);
|
||||
expect(output[0].entrypoints.length).to.equal(1);
|
||||
expect(output[0].entrypoints.map(e => e.importPath)).to.eql(['./app.js']);
|
||||
});
|
||||
|
||||
it('does not output non-chunks', async () => {
|
||||
/** @type {GeneratedBundle[]} */
|
||||
const generatedBundles = [
|
||||
{
|
||||
options: { format: 'es', dir: 'dist' },
|
||||
bundle: {
|
||||
// @ts-ignore
|
||||
'app.js': {
|
||||
isEntry: true,
|
||||
fileName: 'app.js',
|
||||
facadeModuleId: '/root/app.js',
|
||||
type: 'chunk',
|
||||
},
|
||||
// @ts-ignore
|
||||
'not-app.js': {
|
||||
// @ts-ignore
|
||||
isEntry: true,
|
||||
fileName: 'not-app.js',
|
||||
facadeModuleId: '/root/app.js',
|
||||
type: 'asset',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
const output = await getEntrypointBundles({
|
||||
...defaultOptions,
|
||||
generatedBundles,
|
||||
});
|
||||
expect(output.length).to.equal(1);
|
||||
expect(output[0].entrypoints.length).to.equal(1);
|
||||
expect(output[0].entrypoints.map(e => e.importPath)).to.eql(['./app.js']);
|
||||
});
|
||||
|
||||
it('matches on facadeModuleId', async () => {
|
||||
/** @type {GeneratedBundle[]} */
|
||||
const generatedBundles = [
|
||||
{
|
||||
options: { format: 'es', dir: 'dist' },
|
||||
bundle: {
|
||||
// @ts-ignore
|
||||
'app.js': {
|
||||
isEntry: true,
|
||||
fileName: 'app.js',
|
||||
facadeModuleId: '/root/app.js',
|
||||
type: 'chunk',
|
||||
},
|
||||
// @ts-ignore
|
||||
'not-app.js': {
|
||||
isEntry: true,
|
||||
fileName: 'not-app.js',
|
||||
facadeModuleId: '/root/not-app.js',
|
||||
type: 'chunk',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
const output = await getEntrypointBundles({
|
||||
...defaultOptions,
|
||||
generatedBundles,
|
||||
});
|
||||
expect(output.length).to.equal(1);
|
||||
expect(output[0].entrypoints.length).to.equal(1);
|
||||
expect(output[0].entrypoints.map(e => e.importPath)).to.eql(['./app.js']);
|
||||
});
|
||||
|
||||
it('returns all entrypoints when no input module ids are given', async () => {
|
||||
/** @type {GeneratedBundle[]} */
|
||||
const generatedBundles = [
|
||||
{
|
||||
options: { format: 'es', dir: 'dist' },
|
||||
bundle: {
|
||||
// @ts-ignore
|
||||
'app.js': {
|
||||
isEntry: true,
|
||||
fileName: 'app.js',
|
||||
facadeModuleId: '/root/app.js',
|
||||
type: 'chunk',
|
||||
},
|
||||
// @ts-ignore
|
||||
'not-app.js': {
|
||||
isEntry: true,
|
||||
fileName: 'not-app.js',
|
||||
facadeModuleId: '/root/not-app.js',
|
||||
type: 'chunk',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
const output = await getEntrypointBundles({
|
||||
...defaultOptions,
|
||||
inputModuleIds: undefined,
|
||||
generatedBundles,
|
||||
});
|
||||
expect(output.length).to.equal(1);
|
||||
expect(output[0].entrypoints.length).to.equal(2);
|
||||
expect(output[0].entrypoints.map(e => e.importPath)).to.eql(['./app.js', './not-app.js']);
|
||||
});
|
||||
|
||||
it('generates entrypoint for multiple bundles', async () => {
|
||||
/** @type {GeneratedBundle[]} */
|
||||
const generatedBundles = [
|
||||
{
|
||||
options: { format: 'es', dir: 'dist' },
|
||||
bundle: {
|
||||
// @ts-ignore
|
||||
'app.js': {
|
||||
isEntry: true,
|
||||
fileName: 'app.js',
|
||||
facadeModuleId: '/root/app.js',
|
||||
type: 'chunk',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
options: { format: 'es', dir: 'dist/legacy' },
|
||||
bundle: {
|
||||
// @ts-ignore
|
||||
'app.js': {
|
||||
isEntry: true,
|
||||
fileName: 'app.js',
|
||||
facadeModuleId: '/root/app.js',
|
||||
type: 'chunk',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const output = await getEntrypointBundles({
|
||||
...defaultOptions,
|
||||
generatedBundles,
|
||||
});
|
||||
|
||||
expect(output.length).to.equal(2);
|
||||
expect(output[0].options).to.equal(generatedBundles[0].options);
|
||||
expect(output[1].options).to.equal(generatedBundles[1].options);
|
||||
expect(output[0].bundle).to.equal(generatedBundles[0].bundle);
|
||||
expect(output[1].bundle).to.equal(generatedBundles[1].bundle);
|
||||
expect(output[0].entrypoints.length).to.equal(1);
|
||||
expect(output[0].entrypoints[0].chunk).to.equal(generatedBundles[0].bundle['app.js']);
|
||||
expect(output[0].entrypoints.map(e => e.importPath)).to.eql(['./app.js']);
|
||||
expect(output[1].entrypoints.length).to.equal(1);
|
||||
expect(output[1].entrypoints[0].chunk).to.equal(generatedBundles[1].bundle['app.js']);
|
||||
expect(output[1].entrypoints.map(e => e.importPath)).to.eql(['./legacy/app.js']);
|
||||
});
|
||||
|
||||
it('allows configuring a public path', async () => {
|
||||
const output = await getEntrypointBundles({
|
||||
...defaultOptions,
|
||||
pluginOptions: { publicPath: '/static' },
|
||||
});
|
||||
|
||||
expect(output.length).to.equal(1);
|
||||
expect(output[0].entrypoints.length).to.equal(1);
|
||||
expect(output[0].entrypoints.map(e => e.importPath)).to.eql(['/static/app.js']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
const { expect } = require('chai');
|
||||
const path = require('path');
|
||||
const { getInputHtmlData } = require('../../src/getInputHtmlData');
|
||||
|
||||
const rootDir = path.join(__dirname, '..', 'fixtures', 'getInputHtmlData');
|
||||
|
||||
describe('getInputHtmlData()', () => {
|
||||
it('supports setting path as input', () => {
|
||||
const options = {
|
||||
inputPath: 'index.html',
|
||||
};
|
||||
const result = getInputHtmlData(options, rootDir);
|
||||
|
||||
expect(result).to.eql({
|
||||
rootDir,
|
||||
name: 'index.html',
|
||||
inputHtml: '<html>index.html\n\n</html>',
|
||||
});
|
||||
});
|
||||
|
||||
it('supports setting html string as input', () => {
|
||||
const options = {
|
||||
name: 'foo.html',
|
||||
inputHtml: '<html>My HTML</html>',
|
||||
};
|
||||
const result = getInputHtmlData(options, rootDir);
|
||||
|
||||
expect(result).to.eql({
|
||||
name: 'foo.html',
|
||||
rootDir,
|
||||
inputHtml: options.inputHtml,
|
||||
});
|
||||
});
|
||||
|
||||
it('supports setting path with segments as input', () => {
|
||||
const options = {
|
||||
inputPath: 'pages/page-a.html',
|
||||
};
|
||||
const result = getInputHtmlData(options, rootDir);
|
||||
|
||||
expect(result).to.eql({
|
||||
rootDir: path.join(rootDir, 'pages'),
|
||||
name: 'page-a.html',
|
||||
inputHtml: '<html>page-a.html\n\n</html>',
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when no inputPath or inputHtml is given', () => {
|
||||
const options = {
|
||||
name: 'index.html',
|
||||
};
|
||||
expect(() => getInputHtmlData(options, rootDir)).to.throw();
|
||||
});
|
||||
});
|
||||
298
packages/rollup-plugin-html/test/src/getOutputHtml.test.js
Normal file
298
packages/rollup-plugin-html/test/src/getOutputHtml.test.js
Normal file
@@ -0,0 +1,298 @@
|
||||
/* eslint-disable prefer-template */
|
||||
/** @typedef {import('../../src/types').EntrypointBundle} EntrypointBundle */
|
||||
|
||||
const { expect } = require('chai');
|
||||
const { getOutputHtml } = require('../../src/getOutputHtml');
|
||||
|
||||
describe('getOutputHtml()', () => {
|
||||
/** @type {EntrypointBundle[]} */
|
||||
const defaultEntrypointBundles = [
|
||||
{
|
||||
options: { format: 'es' },
|
||||
// @ts-ignore
|
||||
entrypoints: [{ importPath: '/app.js' }, { importPath: '/module.js' }],
|
||||
},
|
||||
];
|
||||
|
||||
const defaultOptions = {
|
||||
pluginOptions: { inject: true },
|
||||
entrypointBundles: defaultEntrypointBundles,
|
||||
};
|
||||
|
||||
it('injects bundle into a default generated HTML file', async () => {
|
||||
const output = await getOutputHtml(defaultOptions);
|
||||
|
||||
expect(output).to.equal(
|
||||
'<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>' +
|
||||
'<script type="module" src="/app.js"></script>' +
|
||||
'<script type="module" src="/module.js"></script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('allows setting an output html template', async () => {
|
||||
const output = await getOutputHtml({
|
||||
...defaultOptions,
|
||||
pluginOptions: {
|
||||
...defaultOptions.pluginOptions,
|
||||
template: '<h1>Output template</h1>',
|
||||
},
|
||||
});
|
||||
|
||||
expect(output).to.equal(
|
||||
'<html><head></head><body><h1>Output template</h1>' +
|
||||
'<script type="module" src="/app.js"></script>' +
|
||||
'<script type="module" src="/module.js"></script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('template can be a function', async () => {
|
||||
const output = await getOutputHtml({
|
||||
...defaultOptions,
|
||||
pluginOptions: {
|
||||
...defaultOptions.pluginOptions,
|
||||
template: () => '<h1>Output template</h1>',
|
||||
},
|
||||
});
|
||||
|
||||
expect(output).to.equal(
|
||||
'<html><head></head><body><h1>Output template</h1>' +
|
||||
'<script type="module" src="/app.js"></script>' +
|
||||
'<script type="module" src="/module.js"></script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('uses the input HTML as output template', async () => {
|
||||
const output = await getOutputHtml({
|
||||
...defaultOptions,
|
||||
inputHtml: '<h1>Input HTML</h1>',
|
||||
});
|
||||
expect(output).to.equal(
|
||||
'<html><head></head><body><h1>Input HTML</h1>' +
|
||||
'<script type="module" src="/app.js"></script>' +
|
||||
'<script type="module" src="/module.js"></script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('generates a HTML file for multiple rollup bundles', async () => {
|
||||
/** @type {EntrypointBundle[]} */
|
||||
const entrypointBundles = [
|
||||
{
|
||||
options: { format: 'es' },
|
||||
// @ts-ignore
|
||||
entrypoints: [{ importPath: '/app.js' }, { importPath: '/module.js' }],
|
||||
},
|
||||
{
|
||||
options: { format: 'system' },
|
||||
// @ts-ignore
|
||||
entrypoints: [{ importPath: '/legacy/app.js' }, { importPath: '/legacy/module.js' }],
|
||||
},
|
||||
];
|
||||
|
||||
const output = await getOutputHtml({ ...defaultOptions, entrypointBundles });
|
||||
expect(output).to.equal(
|
||||
'<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>' +
|
||||
'<script type="module" src="/app.js"></script>' +
|
||||
'<script type="module" src="/module.js"></script>' +
|
||||
'<script>System.import("/legacy/app.js");</script>' +
|
||||
'<script>System.import("/legacy/module.js");</script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can prevent injecting output', async () => {
|
||||
const output = await getOutputHtml({
|
||||
...defaultOptions,
|
||||
pluginOptions: {
|
||||
...defaultOptions.pluginOptions,
|
||||
inject: false,
|
||||
template: '<h1>Just h1</h1>',
|
||||
},
|
||||
});
|
||||
|
||||
expect(output).to.equal('<h1>Just h1</h1>');
|
||||
});
|
||||
|
||||
it('can inject build output in template function', async () => {
|
||||
const output = await getOutputHtml({
|
||||
...defaultOptions,
|
||||
pluginOptions: {
|
||||
...defaultOptions.pluginOptions,
|
||||
inject: false,
|
||||
template: ({ bundle }) =>
|
||||
`<h1>Hello world</h1>` +
|
||||
bundle.entrypoints
|
||||
.map(e => `<script type="module" src="${e.importPath}"></script>`)
|
||||
.join(''),
|
||||
},
|
||||
});
|
||||
|
||||
expect(output).to.equal(
|
||||
'<h1>Hello world</h1>' +
|
||||
'<script type="module" src="/app.js"></script>' +
|
||||
'<script type="module" src="/module.js"></script>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can inject multi build output in template function', async () => {
|
||||
/** @type {EntrypointBundle[]} */
|
||||
const entrypointBundles = [
|
||||
{
|
||||
options: { format: 'es' },
|
||||
// @ts-ignore
|
||||
entrypoints: [{ importPath: '/app.js' }, { importPath: '/module.js' }],
|
||||
},
|
||||
{
|
||||
options: { format: 'system' },
|
||||
// @ts-ignore
|
||||
entrypoints: [{ importPath: '/legacy/app.js' }, { importPath: '/legacy/module.js' }],
|
||||
},
|
||||
];
|
||||
|
||||
const output = await getOutputHtml({
|
||||
...defaultOptions,
|
||||
entrypointBundles,
|
||||
pluginOptions: {
|
||||
...defaultOptions.pluginOptions,
|
||||
inject: false,
|
||||
template: ({ bundles }) =>
|
||||
`<h1>Hello world</h1>` +
|
||||
bundles[0].entrypoints
|
||||
.map(e => `<script type="module" src="${e.importPath}"></script>`)
|
||||
.join('') +
|
||||
bundles[1].entrypoints
|
||||
.map(e => `<script nomodule src="${e.importPath}"></script>`)
|
||||
.join(''),
|
||||
},
|
||||
});
|
||||
|
||||
expect(output).to.equal(
|
||||
'<h1>Hello world</h1>' +
|
||||
'<script type="module" src="/app.js"></script>' +
|
||||
'<script type="module" src="/module.js"></script>' +
|
||||
'<script nomodule src="/legacy/app.js"></script>' +
|
||||
'<script nomodule src="/legacy/module.js"></script>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can transform html output', async () => {
|
||||
const output = await getOutputHtml({
|
||||
...defaultOptions,
|
||||
pluginOptions: {
|
||||
...defaultOptions.pluginOptions,
|
||||
template: '<h1>Hello world</h1>',
|
||||
transform: html => html.replace('Hello world', 'Goodbye world'),
|
||||
},
|
||||
});
|
||||
|
||||
expect(output).to.equal(
|
||||
'<html><head></head><body><h1>Goodbye world</h1>' +
|
||||
'<script type="module" src="/app.js"></script>' +
|
||||
'<script type="module" src="/module.js"></script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('allows setting multiple html transform functions', async () => {
|
||||
const output = await getOutputHtml({
|
||||
...defaultOptions,
|
||||
pluginOptions: {
|
||||
...defaultOptions.pluginOptions,
|
||||
template: '<h1>Hello world</h1>',
|
||||
transform: [
|
||||
html => html.replace('Hello world', 'Goodbye world'),
|
||||
html => html.replace(/h1/g, 'h2'),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(output).to.equal(
|
||||
'<html><head></head><body><h2>Goodbye world</h2>' +
|
||||
'<script type="module" src="/app.js"></script>' +
|
||||
'<script type="module" src="/module.js"></script>' +
|
||||
'</body></html>',
|
||||
);
|
||||
});
|
||||
|
||||
it('default minify minifies html', async () => {
|
||||
const output = await getOutputHtml({
|
||||
...defaultOptions,
|
||||
pluginOptions: {
|
||||
...defaultOptions.pluginOptions,
|
||||
template:
|
||||
'<h1> Foo </h1> \n\n <!-- my comment --> <script type="text/javascript" src="foo.js"></script>',
|
||||
inject: false,
|
||||
minify: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(output).to.equal('<h1>Foo</h1><script src="foo.js"></script>');
|
||||
});
|
||||
|
||||
it('default minify minifies inline js', async () => {
|
||||
const output = await getOutputHtml({
|
||||
...defaultOptions,
|
||||
pluginOptions: {
|
||||
...defaultOptions.pluginOptions,
|
||||
template: '<script> (() => { \nconst foo = "bar"; \n console.log(foo); })() </script>',
|
||||
inject: false,
|
||||
minify: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(output).to.equal('<script>console.log("bar");</script>');
|
||||
});
|
||||
|
||||
it('default minify minifies inline css', async () => {
|
||||
const output = await getOutputHtml({
|
||||
...defaultOptions,
|
||||
pluginOptions: {
|
||||
...defaultOptions.pluginOptions,
|
||||
template: '<style> * { color: blue; } </style>',
|
||||
inject: false,
|
||||
minify: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(output).to.equal('<style>*{color:#00f}</style>');
|
||||
});
|
||||
|
||||
it('can set custom minify options', async () => {
|
||||
const output = await getOutputHtml({
|
||||
...defaultOptions,
|
||||
pluginOptions: {
|
||||
...defaultOptions.pluginOptions,
|
||||
template:
|
||||
'<!-- my comment --> <style> * { color: blue; } </style> \n\n <script> (() => { \nconst foo = "bar"; \n console.log(foo); })() </script>',
|
||||
inject: false,
|
||||
minify: {
|
||||
collapseWhitespace: true,
|
||||
removeComments: true,
|
||||
minifyCSS: false,
|
||||
minifyJS: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(output).to.equal(
|
||||
'<style>* { color: blue; }</style><script>(() => { \nconst foo = "bar"; \n console.log(foo); })()</script>',
|
||||
);
|
||||
});
|
||||
|
||||
it('can set a custom minify function', async () => {
|
||||
const output = await getOutputHtml({
|
||||
...defaultOptions,
|
||||
pluginOptions: {
|
||||
...defaultOptions.pluginOptions,
|
||||
template: '<div>Foo</div>',
|
||||
inject: false,
|
||||
minify: html => `${html} <!-- custom minified -->`,
|
||||
},
|
||||
});
|
||||
|
||||
expect(output).to.equal('<div>Foo</div> <!-- custom minified -->');
|
||||
});
|
||||
});
|
||||
126
packages/rollup-plugin-html/test/src/injectBundles.test.js
Normal file
126
packages/rollup-plugin-html/test/src/injectBundles.test.js
Normal file
@@ -0,0 +1,126 @@
|
||||
/** @typedef {import('rollup').OutputBundle} OutputBundle */
|
||||
/** @typedef {import('rollup').ModuleFormat} ModuleFormat */
|
||||
|
||||
const { expect } = require('chai');
|
||||
const { getTextContent } = require('@open-wc/building-utils/dom5-fork');
|
||||
|
||||
const { injectBundles, createLoadScript } = require('../../src/injectBundles');
|
||||
|
||||
describe('createLoadScript()', () => {
|
||||
it('creates a script for es modules', () => {
|
||||
const scriptAst = createLoadScript('./app.js', 'es');
|
||||
|
||||
expect(scriptAst.tagName).to.equal('script');
|
||||
expect(scriptAst.attrs).to.eql([
|
||||
{ name: 'type', value: 'module' },
|
||||
{ name: 'src', value: './app.js' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('creates a script for systemjs', () => {
|
||||
const scriptAst = createLoadScript('./app.js', 'system');
|
||||
|
||||
expect(scriptAst.tagName).to.equal('script');
|
||||
expect(getTextContent(scriptAst)).to.equal('System.import("./app.js");');
|
||||
});
|
||||
|
||||
it('creates a script for other modules types', () => {
|
||||
const scriptAst = createLoadScript('./app.js', 'iife');
|
||||
|
||||
expect(scriptAst.tagName).to.equal('script');
|
||||
expect(scriptAst.attrs).to.eql([
|
||||
{ name: 'src', value: './app.js' },
|
||||
{ name: 'defer', value: '' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('injectBundles()', () => {
|
||||
it('can inject a single bundle', () => {
|
||||
const html = [
|
||||
//
|
||||
'<html>',
|
||||
'<head></head>',
|
||||
'<body>',
|
||||
'<h1>Hello world</h1>',
|
||||
'</body>',
|
||||
'</html>',
|
||||
].join('');
|
||||
|
||||
const output = injectBundles(html, [
|
||||
{
|
||||
// @ts-ignore
|
||||
options: { format: 'es' },
|
||||
entrypoints: [
|
||||
{
|
||||
importPath: 'app.js',
|
||||
// @ts-ignore
|
||||
chunk: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
const expected = [
|
||||
//
|
||||
'<html>',
|
||||
'<head></head>',
|
||||
'<body>',
|
||||
'<h1>Hello world</h1>',
|
||||
'<script type="module" src="app.js"></script>',
|
||||
'</body>',
|
||||
'</html>',
|
||||
].join('');
|
||||
|
||||
expect(output).to.eql(expected);
|
||||
});
|
||||
|
||||
it('can inject multiple bundles', () => {
|
||||
const html = [
|
||||
//
|
||||
'<html>',
|
||||
'<head></head>',
|
||||
'<body>',
|
||||
'<h1>Hello world</h1>',
|
||||
'</body>',
|
||||
'</html>',
|
||||
].join('');
|
||||
|
||||
const output = injectBundles(html, [
|
||||
{
|
||||
// @ts-ignore
|
||||
options: { format: 'es' },
|
||||
entrypoints: [
|
||||
{
|
||||
importPath: './app.js',
|
||||
// @ts-ignore
|
||||
chunk: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
// @ts-ignore
|
||||
options: { format: 'iife' },
|
||||
entrypoints: [
|
||||
{
|
||||
importPath: '/scripts/script.js',
|
||||
// @ts-ignore
|
||||
chunk: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
const expected = [
|
||||
//
|
||||
'<html>',
|
||||
'<head></head>',
|
||||
'<body>',
|
||||
'<h1>Hello world</h1>',
|
||||
'<script type="module" src="./app.js"></script>',
|
||||
'<script src="/scripts/script.js" defer=""></script>',
|
||||
'</body>',
|
||||
'</html>',
|
||||
].join('');
|
||||
|
||||
expect(output).to.eql(expected);
|
||||
});
|
||||
});
|
||||
6
packages/rollup-plugin-html/tsconfig.json
Normal file
6
packages/rollup-plugin-html/tsconfig.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
@@ -32,11 +32,11 @@
|
||||
"@import-maps/resolve": "^0.2.4",
|
||||
"@open-wc/building-utils": "^2.14.3",
|
||||
"deepmerge": "^3.2.0",
|
||||
"lit-element": "^2.0.1",
|
||||
"lit-element": "^2.2.1",
|
||||
"lit-html": "^1.0.0",
|
||||
"md5": "^2.2.1",
|
||||
"mkdirp": "^0.5.1",
|
||||
"parse5": "^5.1.0"
|
||||
"parse5": "^5.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@webcomponents/shadycss": "^1.9.4",
|
||||
@@ -45,7 +45,7 @@
|
||||
"intersection-observer": "^0.7.0",
|
||||
"mocha": "^6.2.2",
|
||||
"rimraf": "^3.0.0",
|
||||
"rollup": "^1.15.6",
|
||||
"rollup": "^1.31.1",
|
||||
"whatwg-fetch": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
<html lang="en-GB"><head><title>My app</title><style>my-app{display:block}</style><link rel="preload" href="app.js" as="script" crossorigin="anonymous"></head><body><h1><span>Hello world!</span></h1><my-app></my-app><script>window.importShim=t=>import(t.startsWith(".")?new URL(t,document.baseURI):t);</script><script src="polyfills/core-js.d58f09bfd9f1c0b1682cf4a93be23230.js" nomodule=""></script><script src="polyfills/regenerator-runtime.323cb013cc2a9c88ff67ee256cbf5942.js" nomodule=""></script><script>console.log("hello inline script");</script><script src="loader.823a756d08b723f1ff77a3211ce84548.js"></script></body></html>
|
||||
<html lang="en-GB"><head><title>My app</title><style>my-app{display:block}</style><link rel="preload" href="app.js" as="script" crossorigin="anonymous"></head><body><h1><span>Hello world!</span></h1><my-app></my-app><script>window.importShim=t=>import(t.startsWith(".")?new URL(t,document.baseURI):t);</script><script src="polyfills/core-js.d58f09bfd9f1c0b1682cf4a93be23230.js" nomodule=""></script><script src="polyfills/regenerator-runtime.92d44da139046113cb3739b173605787.js" nomodule=""></script><script>console.log("hello inline script");</script><script src="loader.823a756d08b723f1ff77a3211ce84548.js"></script></body></html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
<html lang="en-GB"><head><title>My app</title><style>my-app{display:block}</style><link rel="preload" href="app.js" as="script" crossorigin="anonymous"></head><body><h1><span>Hello world!</span></h1><my-app></my-app><script>window.importShim=t=>import(t.startsWith(".")?new URL(t,document.baseURI):t);</script><script src="polyfills/core-js.d58f09bfd9f1c0b1682cf4a93be23230.js" nomodule=""></script><script src="polyfills/regenerator-runtime.323cb013cc2a9c88ff67ee256cbf5942.js" nomodule=""></script><script>console.log("hello inline script");</script><script>!function(){function e(e,o){return new Promise((function(t,n){document.head.appendChild(Object.assign(document.createElement("script"),{src:e,onload:t,onerror:n},o?{type:"module"}:void 0))}))}var o=[];function t(){"noModule"in HTMLScriptElement.prototype?window.importShim("./app.js"):System.import("./legacy/app.js")}"noModule"in HTMLScriptElement.prototype&&!("importShim"in window)&&o.push(e("polyfills/dynamic-import.b745cfc9384367cc18b42bbef2bbdcd9.js",!1)),"attachShadow"in Element.prototype&&"getRootNode"in Element.prototype&&(!window.ShadyDOM||!window.ShadyDOM.force)||o.push(e("polyfills/webcomponents.dae9f79d9d6992b6582e204c3dd953d3.js",!1)),!("noModule"in HTMLScriptElement.prototype)&&"getRootNode"in Element.prototype&&o.push(e("polyfills/custom-elements-es5-adapter.84b300ee818dce8b351c7cc7c100bcf7.js",!1)),o.length?Promise.all(o).then(t):t()}();</script></body></html>
|
||||
<html lang="en-GB"><head><title>My app</title><style>my-app{display:block}</style><link rel="preload" href="app.js" as="script" crossorigin="anonymous"></head><body><h1><span>Hello world!</span></h1><my-app></my-app><script>window.importShim=t=>import(t.startsWith(".")?new URL(t,document.baseURI):t);</script><script src="polyfills/core-js.d58f09bfd9f1c0b1682cf4a93be23230.js" nomodule=""></script><script src="polyfills/regenerator-runtime.92d44da139046113cb3739b173605787.js" nomodule=""></script><script>console.log("hello inline script");</script><script>!function(){function e(e,o){return new Promise((function(t,n){document.head.appendChild(Object.assign(document.createElement("script"),{src:e,onload:t,onerror:n},o?{type:"module"}:void 0))}))}var o=[];function t(){"noModule"in HTMLScriptElement.prototype?window.importShim("./app.js"):System.import("./legacy/app.js")}"noModule"in HTMLScriptElement.prototype&&!("importShim"in window)&&o.push(e("polyfills/dynamic-import.b745cfc9384367cc18b42bbef2bbdcd9.js",!1)),"attachShadow"in Element.prototype&&"getRootNode"in Element.prototype&&(!window.ShadyDOM||!window.ShadyDOM.force)||o.push(e("polyfills/webcomponents.dae9f79d9d6992b6582e204c3dd953d3.js",!1)),!("noModule"in HTMLScriptElement.prototype)&&"getRootNode"in Element.prototype&&o.push(e("polyfills/custom-elements-es5-adapter.84b300ee818dce8b351c7cc7c100bcf7.js",!1)),o.length?Promise.all(o).then(t):t()}();</script></body></html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
||||
import './shared-ed942ddb.js';
|
||||
console.log('shared');
|
||||
|
||||
console.log('my app');
|
||||
|
||||
import('./lazy-1d008aa1.js');
|
||||
import('./lazy-6dc2cd83.js');
|
||||
|
||||
@@ -1 +1 @@
|
||||
<html lang="en-GB"><head><title>My app</title><style>my-app{display:block}</style><link rel="preload" href="app.js" as="script" crossorigin="anonymous"><link rel="preload" href="shared-ed942ddb.js" as="script" crossorigin="anonymous"></head><body><h1><span>Hello world!</span></h1><my-app></my-app><script>window.importShim=t=>import(t.startsWith(".")?new URL(t,document.baseURI):t);</script><script src="polyfills/core-js.d58f09bfd9f1c0b1682cf4a93be23230.js" nomodule=""></script><script src="polyfills/regenerator-runtime.323cb013cc2a9c88ff67ee256cbf5942.js" nomodule=""></script><script>console.log("hello inline script");</script><script>!function(){var e,o,n=[];function t(){"noModule"in HTMLScriptElement.prototype?window.importShim("./app.js"):System.import("./legacy/app.js")}"noModule"in HTMLScriptElement.prototype&&!("importShim"in window)&&n.push((e="polyfills/dynamic-import.b745cfc9384367cc18b42bbef2bbdcd9.js",o=!1,new Promise((function(n,t){document.head.appendChild(Object.assign(document.createElement("script"),{src:e,onload:n,onerror:t},o?{type:"module"}:void 0))})))),n.length?Promise.all(n).then(t):t()}();</script></body></html>
|
||||
<html lang="en-GB"><head><title>My app</title><style>my-app{display:block}</style><link rel="preload" href="app.js" as="script" crossorigin="anonymous"></head><body><h1><span>Hello world!</span></h1><my-app></my-app><script>window.importShim=t=>import(t.startsWith(".")?new URL(t,document.baseURI):t);</script><script src="polyfills/core-js.d58f09bfd9f1c0b1682cf4a93be23230.js" nomodule=""></script><script src="polyfills/regenerator-runtime.92d44da139046113cb3739b173605787.js" nomodule=""></script><script>console.log("hello inline script");</script><script>!function(){var e,o,n=[];function t(){"noModule"in HTMLScriptElement.prototype?window.importShim("./app.js"):System.import("./legacy/app.js")}"noModule"in HTMLScriptElement.prototype&&!("importShim"in window)&&n.push((e="polyfills/dynamic-import.b745cfc9384367cc18b42bbef2bbdcd9.js",o=!1,new Promise((function(n,t){document.head.appendChild(Object.assign(document.createElement("script"),{src:e,onload:n,onerror:t},o?{type:"module"}:void 0))})))),n.length?Promise.all(n).then(t):t()}();</script></body></html>
|
||||
@@ -1,3 +0,0 @@
|
||||
import './shared-ed942ddb.js';
|
||||
|
||||
console.log('my lazy');
|
||||
@@ -0,0 +1,3 @@
|
||||
import './app.js';
|
||||
|
||||
console.log('my lazy');
|
||||
@@ -1,12 +1,13 @@
|
||||
System.register(['./shared-37ad104a.js'], function (exports, module) {
|
||||
System.register([], function (exports, module) {
|
||||
'use strict';
|
||||
return {
|
||||
setters: [function () {}],
|
||||
execute: function () {
|
||||
|
||||
console.log('shared');
|
||||
|
||||
console.log('my app');
|
||||
|
||||
module.import('./lazy-80ba0959.js');
|
||||
module.import('./lazy-c54dffe7.js');
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
System.register(['./shared-37ad104a.js'], function () {
|
||||
System.register(['./app.js'], function () {
|
||||
'use strict';
|
||||
return {
|
||||
setters: [function () {}],
|
||||
@@ -1,10 +0,0 @@
|
||||
System.register([], function () {
|
||||
'use strict';
|
||||
return {
|
||||
execute: function () {
|
||||
|
||||
console.log('shared');
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
console.log('shared');
|
||||
@@ -1 +1 @@
|
||||
<html lang="en-GB"><head><title>My app</title><style>my-app{display:block}</style><link rel="preload" href="app.js" as="script" crossorigin="anonymous"></head><body><h1><span>Hello world!</span></h1><my-app></my-app><script>window.importShim=t=>import(t.startsWith(".")?new URL(t,document.baseURI):t);</script><script src="polyfills/core-js.d58f09bfd9f1c0b1682cf4a93be23230.js" nomodule=""></script><script src="polyfills/regenerator-runtime.323cb013cc2a9c88ff67ee256cbf5942.js" nomodule=""></script><script>console.log("hello inline script");</script><script>!function(){function e(e,o){return new Promise((function(t,n){document.head.appendChild(Object.assign(document.createElement("script"),{src:e,onload:t,onerror:n},o?{type:"module"}:void 0))}))}var o=[];function t(){window.importShim("./app.js")}"noModule"in HTMLScriptElement.prototype&&!("importShim"in window)&&o.push(e("polyfills/dynamic-import.b745cfc9384367cc18b42bbef2bbdcd9.js",!1)),"attachShadow"in Element.prototype&&"getRootNode"in Element.prototype&&(!window.ShadyDOM||!window.ShadyDOM.force)||o.push(e("polyfills/webcomponents.dae9f79d9d6992b6582e204c3dd953d3.js",!1)),!("noModule"in HTMLScriptElement.prototype)&&"getRootNode"in Element.prototype&&o.push(e("polyfills/custom-elements-es5-adapter.84b300ee818dce8b351c7cc7c100bcf7.js",!1)),o.length?Promise.all(o).then(t):t()}();</script></body></html>
|
||||
<html lang="en-GB"><head><title>My app</title><style>my-app{display:block}</style><link rel="preload" href="app.js" as="script" crossorigin="anonymous"></head><body><h1><span>Hello world!</span></h1><my-app></my-app><script>window.importShim=t=>import(t.startsWith(".")?new URL(t,document.baseURI):t);</script><script src="polyfills/core-js.d58f09bfd9f1c0b1682cf4a93be23230.js" nomodule=""></script><script src="polyfills/regenerator-runtime.92d44da139046113cb3739b173605787.js" nomodule=""></script><script>console.log("hello inline script");</script><script>!function(){function e(e,o){return new Promise((function(t,n){document.head.appendChild(Object.assign(document.createElement("script"),{src:e,onload:t,onerror:n},o?{type:"module"}:void 0))}))}var o=[];function t(){window.importShim("./app.js")}"noModule"in HTMLScriptElement.prototype&&!("importShim"in window)&&o.push(e("polyfills/dynamic-import.b745cfc9384367cc18b42bbef2bbdcd9.js",!1)),"attachShadow"in Element.prototype&&"getRootNode"in Element.prototype&&(!window.ShadyDOM||!window.ShadyDOM.force)||o.push(e("polyfills/webcomponents.dae9f79d9d6992b6582e204c3dd953d3.js",!1)),!("noModule"in HTMLScriptElement.prototype)&&"getRootNode"in Element.prototype&&o.push(e("polyfills/custom-elements-es5-adapter.84b300ee818dce8b351c7cc7c100bcf7.js",!1)),o.length?Promise.all(o).then(t):t()}();</script></body></html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
||||
import './shared-ed942ddb.js';
|
||||
console.log('shared');
|
||||
|
||||
console.log('my app');
|
||||
|
||||
import('./lazy-1d008aa1.js');
|
||||
import('./lazy-6dc2cd83.js');
|
||||
|
||||
@@ -1 +1 @@
|
||||
<html lang="en-GB"><head><title>My app</title><style>my-app{display:block}</style><link rel="preload" href="shared-ed942ddb.js" as="script" crossorigin="anonymous"></head><body><h1><span>Hello world!</span></h1><my-app></my-app><script src="./app.js" type="module"></script><script>console.log("hello inline script");</script></body></html>
|
||||
<html lang="en-GB"><head><title>My app</title><style>my-app{display:block}</style></head><body><h1><span>Hello world!</span></h1><my-app></my-app><script src="./app.js" type="module"></script><script>console.log("hello inline script");</script></body></html>
|
||||
@@ -1,3 +0,0 @@
|
||||
import './shared-ed942ddb.js';
|
||||
|
||||
console.log('my lazy');
|
||||
@@ -0,0 +1,3 @@
|
||||
import './app.js';
|
||||
|
||||
console.log('my lazy');
|
||||
@@ -1 +0,0 @@
|
||||
console.log('shared');
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user