Converted @rollup/html to a standalone package (out of the original monorepo) and made it ESM-native

This commit is contained in:
Miel Truyen 2023-04-17 21:04:05 +02:00
commit 2dc701c5b7
21 changed files with 4923 additions and 0 deletions

96
.gitignore vendored Normal file
View File

@ -0,0 +1,96 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
**/node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
#**/.env
**/.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
#Ignore vscode
.vscode/
.DS_Store
.git
node_modules
npm-debug.log
npm-error.log
yarn-debug.log
yarn-error.log
.dockerignore
.idea
dist

86
CHANGELOG.md Normal file
View File

@ -0,0 +1,86 @@
# @rollup/plugin-html ChangeLog
## v1.0.2
_2023-01-20_
### Bugfixes
- fix: types should come first in exports [#1403](https://github.com/rollup/plugins/pull/1403)
## v1.0.1
_2022-10-21_
### Updates
- chore: update rollup dependencies ([3038271](https://github.com/rollup/plugins/commit/303827191ede6b2e4eade96c6968ed16a587683f))
## v1.0.0
_2022-10-09_
### Breaking Changes
- fix: prepare for Rollup 3 [#1294](https://github.com/rollup/plugins/pull/1294)
### Bugfixes
- fix: function signature of html updated [#975](https://github.com/rollup/plugins/pull/975)
## v0.2.3
_2021-02-14_
### Bugfixes
- fix: package.json files (#802)
## v0.2.2
_2021-01-29_
### Updates
- chore: run build before publish (ce65c12)
- chore: upgrade TypeScript (#713)
- refactor: refactor to typescript (#634)
- chore: update dependencies (6c8614c)
## v0.2.1
_2021-01-29_
### Updates
- chore: run build before publish (ce65c12)
- chore: upgrade TypeScript (#713)
- refactor: refactor to typescript (#634)
- chore: update dependencies (6c8614c)
## v0.2.0
_2020-05-02_
### Features
- feat: support custom meta element creation (#308)
### Updates
- test: update snapshots for rollup v2 (c09509f)
## v0.1.1
_2020-01-04_
### Updates
- docs: fix <br> tag (#89)
- test: change tests to serial to avoid weird snapshot conflicts (a492ce7)
## 0.1.0
_2019-11-29_
- First Release

165
README.md Normal file
View File

@ -0,0 +1,165 @@
# Work-in-progress changelog
[npm]: https://img.shields.io/npm/v/@rollup/plugin-html
[npm-url]: https://www.npmjs.com/package/@rollup/plugin-html
[size]: https://packagephobia.now.sh/badge?p=@rollup/plugin-html
[size-url]: https://packagephobia.now.sh/result?p=@rollup/plugin-html
[![npm][npm]][npm-url]
[![size][size]][size-url]
[![libera manifesto](https://img.shields.io/badge/libera-manifesto-lightgrey.svg)](https://liberamanifesto.com)
# @rollup/plugin-html
🍣 A Rollup plugin which creates HTML files to serve Rollup bundles.
Please see [Supported Output Formats](#supported-output-formats) for information about using this plugin with output formats other than `esm` (`es`), `iife`, and `umd`.
## Requirements
This plugin requires an [LTS](https://github.com/nodejs/Release) Node version (v14.0.0+) and Rollup v1.20.0+.
## Install
Using npm:
```console
npm install @rollup/plugin-html --save-dev
```
## Usage
Create a `rollup.config.js` [configuration file](https://www.rollupjs.org/guide/en/#configuration-files) and import the plugin:
```js
const html = require('@rollup/plugin-html');
module.exports = {
input: 'src/index.js',
output: {
dir: 'output',
format: 'cjs'
},
plugins: [html()]
};
```
Then call `rollup` either via the [CLI](https://www.rollupjs.org/guide/en/#command-line-reference) or the [API](https://www.rollupjs.org/guide/en/#javascript-api).
Once run successfully, an HTML file should be written to the bundle output destination.
## Options
### `attributes`
Type: `Object`<br>
Default: `{ html: { lang: 'en' }, link: null, script: null }`
Specifies additional attributes for `html`, `link`, and `script` elements. For each property, provide an object with key-value pairs that represent an HTML element attribute name and value. By default, the `html` element is rendered with an attribute of `lang="en"`.
_Note: If using the `es` / `esm` output format, `{ type: 'module'}` is automatically added to `attributes.script`._
### `fileName`
Type: `String`<br>
Default: `'index.html'`
### `meta`
Type: `Array[...object]`<br>
Default: `[{ charset: 'utf-8' }]`
Specifies attributes used to create `<meta>` elements. For each array item, provide an object with key-value pairs that represent `<meta>` element attribute names and values.
Specifies the name of the HTML to emit.
### `publicPath`
Type: `String`<br>
Default: `''`
Specifies a path to prepend to all bundle assets (files) in the HTML output.
### `template`
Type: `Function`<br>
Default: `internal function`
Returns: `String`
Specifies a function that provides the rendered source for the HTML output. The function should be in the form of:
```js
const template = ({ attributes, bundle, files, publicPath, title }) => { ... }
```
- `attributes`: Corresponds to the `attributes` option passed to the plugin
- `bundle`: An `Object` containing key-value pairs of [`AssetInfo` or `ChunkInfo`](https://rollupjs.org/guide/en/#generatebundle)
- `files`: An `Array` of `AssetInfo` or `ChunkInfo` containing any entry (`isEntry: true`) files, and any asset (`isAsset: true`) files in the bundle that will be emitted
- `publicPath`: Corresponds to the `publicPath` option passed to the plugin
- `title`: Corresponds to the `title` option passed to the plugin
By default this is handled internally and produces HTML in the following format:
```html
<!DOCTYPE html>
<html ${attributes}>
<head>
${metas}
<title>${title}</title>
${links}
</head>
<body>
${scripts}
</body>
</html>
```
Where `${links}` represents all `<link ..` tags for CSS and `${scripts}` represents all `<script...` tags for JavaScript files.
### `title`
Type: `String`<br>
Default: `'Rollup Bundle'`
Specifies the HTML document title.
## Exports
### `makeHtmlAttributes(attributes)`
Parameters: `attributes`, Type: `Object`<br>
Returns: `String`
Consumes an object with key-value pairs that represent an HTML element attribute name and value. The function returns all pairs as a space-separated string of valid HTML element attributes. e.g.
```js
const { makeHtmlAttributes } = require('@rollup/plugin-html');
makeHtmlAttributes({ lang: 'en', 'data-batcave': 'secret' });
// -> 'lang="en" data-batcave="secret"'
```
## Supported Output Formats
By default, this plugin supports the `esm` (`es`), `iife`, and `umd` [output formats](https://rollupjs.org/guide/en/#outputformat), as those are most commonly used as browser bundles. Other formats can be used, but will require using the [`template`](#template) option to specify a custom template function which renders the unique requirements of other formats.
### `amd`
Will likely require use of RequireJS semantics, which allows only for a single entry `<script>` tag. If more entry chunks are emitted, these need to be loaded via a proxy file. RequireJS would also need to be a dependency and added to the build: https://requirejs.org/docs/start.html.
### `system`
Would require a separate `<script>` tag first that adds the `s.js` minimal loader. Loading modules might then resemble: `<script>System.import('./batman.js')</script>`.
## Attribution
This plugin was inspired by and is based upon [mini-html-webpack-plugin](https://github.com/styleguidist/mini-html-webpack-plugin) by Juho Vepsäläinen and Artem Sapegin, with permission.
## Meta
[CONTRIBUTING](/.github/CONTRIBUTING.md)
[LICENSE (MIT)](/LICENSE)

12
babel.config.js Normal file
View File

@ -0,0 +1,12 @@
console.log("Do i get called");
/** @type {import('@types/babel-core').TransformOptions} */
const babelConfig ={
presets: [
["@babel/preset-typescript", {
}],
],
plugins: [
],
};
export default babelConfig;

100
package.json Normal file
View File

@ -0,0 +1,100 @@
{
"name": "@rollup-apps/plugin-html",
"version": "0.0.1",
"publishConfig": {
"access": "public"
},
"description": "Creates HTML files to serve Rollup bundles",
"license": "MIT",
"repository": {
"url": "rollup/plugins",
"directory": "packages/html"
},
"author": "Andrew Powell <andrew@shellscape.org>",
"homepage": "https://github.com/rollup/plugins/tree/master/packages/html#readme",
"bugs": "https://github.com/rollup/plugins/issues",
"type": "module",
"engines": {
"node": ">=18"
},
"main": "dist/es/index.js",
"module": "./dist/es/index.js",
"exports": {
"types": "./types/index.d.ts",
"import": "./dist/es/index.js",
"default": "./dist/cjs/index.js"
},
"scripts": {
"build": "rollup -c",
"ci:coverage": "nyc pnpm test && nyc report --reporter=text-lcov > coverage.lcov",
"ci:lint": "pnpm build && pnpm lint",
"ci:lint:commits": "commitlint --from=${CIRCLE_BRANCH} --to=${CIRCLE_SHA1}",
"ci:test": "pnpm test -- --verbose",
"prebuild": "del-cli dist",
"prerelease": "pnpm build",
"pretest": "pnpm build",
"release": "pnpm --workspace-root plugin:release --pkg $npm_package_name",
"test": "ava",
"test:ts": "tsc --noEmit"
},
"files": [
"dist",
"!dist/**/*.map",
"types",
"README.md",
"LICENSE"
],
"keywords": [
"rollup",
"plugin",
"html",
"template"
],
"peerDependencies": {
"rollup": "^3.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
},
"devDependencies": {
"@types/node": "^18.15.11",
"@rollup/plugin-typescript": "^11.1.0",
"postcss": "^8.4.22",
"rollup": "^3.20.3",
"rollup-plugin-postcss": "^4.0.2",
"typescript": "^5.0.4",
"del-cli": "^5.0.0",
"tslib": "^2.5.0",
"ava": "^5.2.0",
"@ava/babel": "^2.0.0",
"ts-node": "^10.9.1",
"@babel/core": "^7.21.4",
"@babel/plugin-syntax-import-assertions": "^7.20.0",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-node-resolve": "^15.0.2",
"rollup-plugin-delete": "^2.0.0",
"esm": "^3.2.25",
"@node-loader/babel": "^2.0.1",
"babel-register-esm": "^1.2.4"
},
"types": "./types/index.d.ts",
"ava": {
"workerThreads": false,
"files": [
"!**/fixtures/**",
"!**/util/**",
"!**/helpers/**",
"!**/recipes/**",
"!**/types.ts"
],
"extensions": {
"ts": "module",
"js": true
},
"nodeArguments": [
"--loader=ts-node/esm"
]
}
}

3435
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,64 @@
This method provides the ability to reference external css/js files for the generated html, and supports adjusting the file loading sequence.
when using it:
```js
import html from '@rollup/plugin-html';
import templateExternalFiles from '@rollup/plugin-html/recipes/external-files';
import postcss from 'rollup-plugin-postcss';
export default [
{
input: ['demo/demo.ts'],
output: [{ file: 'dist/demo.js' }],
plugins: [
postcss({
extract: 'demo.css',
minimize: false,
use: ['sass'],
extensions: ['.scss', '.css']
}),
html({
title: 'sdk demo page',
publicPath: './',
fileName: 'demo.html',
attributes: { html: { lang: 'zh-cn' } },
template: templateExternalFiles([
{ type: 'js', file: 'example1.js', pos: 'before' },
{ type: 'js', file: 'example2.js', pos: 'before' },
{ type: 'js', file: 'example3.js' },
{ type: 'js', file: 'example4.js', pos: 'before' },
{ type: 'css', file: 'example1.css', pos: 'before' },
{ type: 'css', file: 'example2.css', pos: 'before' },
{ type: 'css', file: 'example3.css' },
{ type: 'css', file: 'example4.css', pos: 'before' }
])
})
]
}
];
```
The content of the generated html file:
```html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<title>sdk demo page</title>
<link href="./example1.css" rel="stylesheet" />
<link href="./example2.css" rel="stylesheet" />
<link href="./example4.css" rel="stylesheet" />
<link href="./demo.css" rel="stylesheet" />
<link href="./example3.css" rel="stylesheet" />
</head>
<body>
<script src="./example1.js"></script>
<script src="./example2.js"></script>
<script src="./example4.js"></script>
<script src="./demo.js"></script>
<script src="./example3.js"></script>
</body>
</html>
```

View File

@ -0,0 +1,76 @@
/**
* Provides the ability to reference external css/js files for the generated html
* Method source once issues: https://github.com/rollup/plugins/issues/755
* @param {Array} externals List of external files.
* The format is: [{ type: 'js', file: '//xxxx1.js', pos: 'before' }, { type: 'css', file: '//xxxx1.css' }]
*
* @return {Function} The templae method required by plugin-html
*/
export default function htmlTemplate(externals) {
return ({ attributes, files, meta, publicPath, title }) => {
let scripts = [...(files.js || [])];
let links = [...(files.css || [])];
// externals = [{ type: 'js', file: '//xxxx1.js', pos: 'before' }, { type: 'css', file: '//xxxx1.css' }]
if (Array.isArray(externals)) {
const beforeLinks = [];
const beforeScripts = [];
externals.forEach((node) => {
let fileList;
const isCssFile = node.type === 'css';
if (node.pos === 'before') {
fileList = isCssFile ? beforeLinks : beforeScripts;
} else {
fileList = isCssFile ? links : scripts;
}
fileList.push({ fileName: node.file });
});
scripts = beforeScripts.concat(scripts);
links = beforeLinks.concat(links);
}
scripts = scripts
.map(({ fileName }) => {
const attrs = makeHtmlAttributes(attributes.script);
return `<script src="${publicPath}${fileName}"${attrs}></script>`;
})
.join('\n');
links = links
.map(({ fileName }) => {
const attrs = makeHtmlAttributes(attributes.link);
return `<link href="${publicPath}${fileName}" rel="stylesheet"${attrs}>`;
})
.join('\n');
const metas = meta
.map((input) => {
const attrs = makeHtmlAttributes(input);
return `<meta${attrs}>`;
})
.join('\n');
return `
<!doctype html>
<html${makeHtmlAttributes(attributes.html)}>
<head>
${metas}
<title>${title}</title>
${links}
</head>
<body>
${scripts}
</body>
</html>`;
};
}
function makeHtmlAttributes(attributes) {
if (!attributes) {
return '';
}
const keys = Object.keys(attributes);
// eslint-disable-next-line no-param-reassign
return keys.reduce((result, key) => (result += ` ${key}="${attributes[key]}"`), '');
}

80
rollup.config.mjs Normal file
View File

@ -0,0 +1,80 @@
import {readFileSync} from "node:fs";
import {builtinModules} from "node:module";
import {resolve, join} from "node:path";
import nodeResolve from "@rollup/plugin-node-resolve";
import babelPlugin from "@rollup/plugin-babel";
import deletePlugin from "rollup-plugin-delete";
/**
* Create a base rollup config
* @param {Record<string,any>} pkg Imported package.json
* @param {string[]} external Imported package.json
* @returns {import('rollup').RollupOptions}
*/
export function createConfig({ pkg, external = [] }) {
const commonOpts = {
extensions: ['.js', '.mjs', '.jsx', '.ts', '.tsx'],
exclude: 'node_modules/**',
};
return {
input: 'src/index.ts',
external: Object.keys(pkg.dependencies || {})
.concat(Object.keys(pkg.peerDependencies || {}))
.concat(builtinModules)
.concat(external),
onwarn: (warning) => {
throw Object.assign(new Error(), warning);
},
strictDeprecations: true,
output: [
{
format: 'cjs',
file: pkg.main,
exports: 'named',
footer: 'module.exports = Object.assign(exports.default, exports);',
sourcemap: true
},
{
format: 'es',
file: pkg.module,
plugins: [emitModulePackageFile()],
sourcemap: true
}
],
plugins: [
deletePlugin({
targets: join('./dist','*'),
runOnce: true,
}),
nodeResolve({
...commonOpts,
browser: true,
}),
babelPlugin({
...commonOpts,
babelHelpers: "bundled",
}),
// typescript({ sourceMap: true })]
]
};
}
export function emitModulePackageFile() {
return {
name: 'emit-module-package-file',
generateBundle() {
this.emitFile({
type: 'asset',
fileName: 'package.json',
source: `{"type":"module"}`
});
}
};
}
export default createConfig({
pkg: JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'))
});

133
src/index.ts Normal file
View File

@ -0,0 +1,133 @@
import { extname } from "node:path";
import type { Plugin, NormalizedOutputOptions, OutputBundle, EmittedAsset } from 'rollup';
import type { RollupHtmlOptions, RollupHtmlTemplateOptions } from '../types/index.d.ts';
const getFiles = (bundle: OutputBundle): RollupHtmlTemplateOptions['files'] => {
const result = {} as ReturnType<typeof getFiles>;
for (const file of Object.values(bundle)) {
const { fileName } = file;
const extension = extname(fileName).substring(1);
result[extension] = (result[extension] || []).concat(file);
}
return result;
};
export const makeHtmlAttributes = (attributes: Record<string, any>): string => {
if (!attributes) {
return '';
}
const keys = Object.keys(attributes);
// eslint-disable-next-line no-param-reassign
return keys.reduce((result, key) => (result += ` ${key}="${attributes[key]}"`), '');
};
const defaultTemplate = async ({
attributes,
files,
meta,
publicPath,
title
}: RollupHtmlTemplateOptions) => {
const scripts = (files.js || [])
.map(({ fileName }) => {
const attrs = makeHtmlAttributes(attributes.script);
return `<script src="${publicPath}${fileName}"${attrs}></script>`;
})
.join('\n');
const links = (files.css || [])
.map(({ fileName }) => {
const attrs = makeHtmlAttributes(attributes.link);
return `<link href="${publicPath}${fileName}" rel="stylesheet"${attrs}>`;
})
.join('\n');
const metas = meta
.map((input) => {
const attrs = makeHtmlAttributes(input);
return `<meta${attrs}>`;
})
.join('\n');
return `
<!doctype html>
<html${makeHtmlAttributes(attributes.html)}>
<head>
${metas}
<title>${title}</title>
${links}
</head>
<body>
${scripts}
</body>
</html>`;
};
const supportedFormats = ['es', 'esm', 'iife', 'umd'];
const defaults = {
attributes: {
link: null,
html: { lang: 'en' },
script: null
},
fileName: 'index.html',
meta: [{ charset: 'utf-8' }],
publicPath: '',
template: defaultTemplate,
title: 'Rollup Bundle'
};
export default function html(opts: RollupHtmlOptions = {}): Plugin {
const { attributes, fileName, meta, publicPath, template, title } = Object.assign(
{},
defaults,
opts
);
return {
name: 'html',
async generateBundle(output: NormalizedOutputOptions, bundle: OutputBundle) {
if (!supportedFormats.includes(output.format) && !opts.template) {
this.warn(
`plugin-html: The output format '${
output.format
}' is not directly supported. A custom \`template\` is probably required. Supported formats include: ${supportedFormats.join(
', '
)}`
);
}
if (output.format === 'es') {
attributes.script = Object.assign({}, attributes.script, {
type: 'module'
});
}
const files = getFiles(bundle);
const source = await template({
attributes,
bundle,
files,
meta,
publicPath,
title
});
const htmlFile: EmittedAsset = {
type: 'asset',
source,
name: 'Rollup HTML Asset',
fileName
};
this.emitFile(htmlFile);
}
};
}

0
test/fixtures/batman.js vendored Normal file
View File

1
test/fixtures/joker.css vendored Normal file
View File

@ -0,0 +1 @@
* { width: 100%; }

2
test/fixtures/joker.js vendored Normal file
View File

@ -0,0 +1,2 @@
// eslint-disable-next-line
import style from './joker.css';

2
test/fixtures/robin.js vendored Normal file
View File

@ -0,0 +1,2 @@
// eslint-disable-next-line
import * as batman from './batman.js';

357
test/snapshots/test.js.md Normal file
View File

@ -0,0 +1,357 @@
# Snapshot report for `test/test.js`
The actual snapshot is saved in `test.js.snap`.
Generated by [AVA](https://avajs.dev).
## default options
> Snapshot 1
[
{
code: `(function (factory) {␊
typeof define === 'function' && define.amd ? define(factory) :␊
factory();␊
})((function () { 'use strict';␊
}));␊
`,
fileName: 'batman.js',
map: null,
source: undefined,
},
{
code: undefined,
fileName: 'index.html',
map: undefined,
source: `␊
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Rollup Bundle</title>
</head>
<body>
<script src="batman.js"></script>
</body>
</html>`,
},
]
## options
> Snapshot 1
[
{
code: `(function (factory) {␊
typeof define === 'function' && define.amd ? define(factory) :␊
factory();␊
})((function () { 'use strict';␊
}));␊
`,
fileName: 'batman.js',
map: null,
source: undefined,
},
{
code: undefined,
fileName: 'batman.html',
map: undefined,
source: `␊
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width">
<title>Batcave</title>
</head>
<body>
<script src="batcave/batman.js"></script>
</body>
</html>`,
},
]
## iife
> Snapshot 1
[
{
code: `(function () {␊
'use strict';␊
})();␊
`,
fileName: 'batman.js',
map: null,
source: undefined,
},
{
code: undefined,
fileName: 'index.html',
map: undefined,
source: `␊
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Rollup Bundle</title>
</head>
<body>
<script src="batman.js"></script>
</body>
</html>`,
},
]
## esm
> Snapshot 1
[
{
code: `␊
`,
fileName: 'batman.js',
map: null,
source: undefined,
},
{
code: undefined,
fileName: 'index.html',
map: undefined,
source: `␊
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Rollup Bundle</title>
</head>
<body>
<script src="batman.js" type="module"></script>
</body>
</html>`,
},
]
## unsupported output format
> Snapshot 1
[
{
code: `'use strict';␊
`,
fileName: 'batman.js',
map: null,
source: undefined,
},
{
code: undefined,
fileName: 'index.html',
map: undefined,
source: `␊
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Rollup Bundle</title>
</head>
<body>
<script src="batman.js" type="module"></script>
</body>
</html>`,
},
]
> Snapshot 2
[
{
code: 'EMPTY_BUNDLE',
message: 'Generated an empty chunk: "batman".',
names: [
'batman',
],
toString: Function {},
},
{
code: 'PLUGIN_WARNING',
message: 'plugin-html: The output format \'cjs\' is not directly supported. A custom `template` is probably required. Supported formats include: es, esm, iife, umd',
plugin: 'html',
toString: Function {},
},
]
## css
> Snapshot 1
[
{
code: `(function (factory) {␊
typeof define === 'function' && define.amd ? define(factory) :␊
factory();␊
})((function () { 'use strict';␊
}));␊
`,
fileName: 'joker.js',
map: null,
source: undefined,
},
{
code: undefined,
fileName: 'joker.css',
map: undefined,
source: Buffer @Uint8Array [
2a207b20 77696474 683a2031 3030253b 207d0a
],
},
{
code: undefined,
fileName: 'index.html',
map: undefined,
source: `␊
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Rollup Bundle</title>
<link href="joker.css" rel="stylesheet">
</head>
<body>
<script src="joker.js" type="module"></script>
</body>
</html>`,
},
]
## attributes
> Snapshot 1
[
{
code: `(function (factory) {␊
typeof define === 'function' && define.amd ? define(factory) :␊
factory();␊
})((function () { 'use strict';␊
}));␊
`,
fileName: 'joker.js',
map: null,
source: undefined,
},
{
code: undefined,
fileName: 'joker.css',
map: undefined,
source: Buffer @Uint8Array [
2a207b20 77696474 683a2031 3030253b 207d0a
],
},
{
code: undefined,
fileName: 'index.html',
map: undefined,
source: `␊
<!doctype html>
<html batsignal="on" lang="bat">
<head>
<meta charset="utf-8">
<title>Rollup Bundle</title>
<link href="joker.css" rel="stylesheet" data-vilian="joker">
</head>
<body>
<script src="joker.js" defer="true"></script>
</body>
</html>`,
},
]
## imports
> Snapshot 1
[
{
code: `(function (factory) {␊
typeof define === 'function' && define.amd ? define(factory) :␊
factory();␊
})((function () { 'use strict';␊
}));␊
`,
fileName: 'robin.js',
map: null,
source: undefined,
},
{
code: undefined,
fileName: 'index.html',
map: undefined,
source: `␊
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Rollup Bundle</title>
</head>
<body>
<script src="robin.js" type="module"></script>
</body>
</html>`,
},
]
## template
> Snapshot 1
[
{
code: `(function (factory) {␊
typeof define === 'function' && define.amd ? define(factory) :␊
factory();␊
})((function () { 'use strict';␊
}));␊
`,
fileName: 'batman.js',
map: null,
source: undefined,
},
{
code: undefined,
fileName: 'index.html',
map: undefined,
source: '<html><body><main></main></body></html>',
},
]

BIN
test/snapshots/test.js.snap Normal file

Binary file not shown.

124
test/test.js Normal file
View File

@ -0,0 +1,124 @@
import {join, dirname} from "node:path";
import test from "ava";
import { rollup } from "rollup";
import css from "rollup-plugin-postcss";
import { getCode } from "./util/test.js";
import html from "../src/index.ts";
// const read = (file = 'index.html') => readFileSync(join('output/', file), 'utf-8');
const output = { dir: 'output', format: 'umd' };
import {fileURLToPath} from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
process.chdir(join(__dirname, 'fixtures'));
test.serial('default options', async (t) => {
const bundle = await rollup({
input: 'batman.js',
plugins: [html()]
});
const code = await getCode(bundle, output, true);
t.snapshot(code);
});
test.serial('options', async (t) => {
const bundle = await rollup({
input: 'batman.js',
plugins: [
html({
fileName: 'batman.html',
publicPath: 'batcave/',
title: 'Batcave',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'minimum-scale=1, initial-scale=1, width=device-width' }
]
})
]
});
const code = await getCode(bundle, output, true);
t.snapshot(code);
});
test.serial('iife', async (t) => {
const bundle = await rollup({
input: 'batman.js',
plugins: [html()]
});
const code = await getCode(bundle, { dir: 'output', format: 'iife' }, true);
t.snapshot(code);
});
test.serial('esm', async (t) => {
const bundle = await rollup({
input: 'batman.js',
plugins: [html()]
});
const code = await getCode(bundle, { dir: 'output', format: 'es' }, true);
t.snapshot(code);
});
test.serial('unsupported output format', async (t) => {
const warnings = [];
const bundle = await rollup({
input: 'batman.js',
onwarn: (warning) => warnings.push(warning),
plugins: [html()]
});
const code = await getCode(bundle, { dir: 'output', format: 'cjs' }, true);
t.snapshot(code);
t.snapshot(warnings);
});
test.serial('css', async (t) => {
const bundle = await rollup({
input: 'joker.js',
plugins: [css({ extract: true }), html()]
});
const code = await getCode(bundle, output, true);
t.snapshot(code);
});
test.serial('attributes', async (t) => {
const bundle = await rollup({
input: 'joker.js',
plugins: [
css({ extract: true }),
html({
attributes: {
html: { batsignal: 'on', lang: 'bat' },
link: { 'data-vilian': 'joker' },
script: { defer: true }
}
})
]
});
const code = await getCode(bundle, output, true);
t.snapshot(code);
});
test.serial('imports', async (t) => {
const bundle = await rollup({
input: 'robin.js',
plugins: [html()]
});
const code = await getCode(bundle, output, true);
t.snapshot(code);
});
test.serial('template', async (t) => {
const bundle = await rollup({
input: 'batman.js',
plugins: [
html({
template: () => '<html><body><main></main></body></html>'
})
]
});
const code = await getCode(bundle, output, true);
t.snapshot(code);
});

45
test/util/test.d.ts vendored Normal file
View File

@ -0,0 +1,45 @@
/* eslint-disable import/no-extraneous-dependencies */
import type { RollupBuild, OutputOptions, OutputChunk, OutputAsset } from 'rollup';
import type { Assertions } from 'ava';
interface GetCode {
(bundle: RollupBuild, outputOptions?: OutputOptions | null, allFiles?: false): Promise<string>;
(bundle: RollupBuild, outputOptions: OutputOptions | null | undefined, allFiles: true): Promise<
Array<{
code: OutputChunk['code'] | undefined;
fileName: OutputChunk['fileName'] | OutputAsset['fileName'];
source: OutputAsset['source'] | undefined;
}>
>;
}
export const getCode: GetCode;
export function getFiles(
bundle: RollupBuild,
outputOptions?: OutputOptions
): Promise<
{
fileName: string;
content: any;
}[]
>;
export function evaluateBundle(bundle: RollupBuild): Promise<Pick<NodeModule, 'exports'>>;
export function getImports(bundle: RollupBuild): Promise<string[]>;
export function getResolvedModules(bundle: RollupBuild): Promise<Record<string, string>>;
export function onwarn(warning: string | any): void;
export function testBundle(
t: Assertions,
bundle: RollupBuild,
options: { inject: Record<string, any>; options: Record<string, any> }
): Promise<{
code: string;
error?: any;
result?: any;
module: Pick<NodeModule, 'exports'>;
}>;

95
test/util/test.js Normal file
View File

@ -0,0 +1,95 @@
import path from "node:path";
import process from "node:process";
/**
* @param {import('rollup').RollupBuild} bundle
* @param {import('rollup').OutputOptions} [outputOptions]
*/
export const getCode = async (bundle, outputOptions, allFiles = false) => {
const { output } = await bundle.generate(outputOptions || { format: 'cjs', exports: 'auto' });
if (allFiles) {
return output.map(({ code, fileName, source, map }) => {
return {
code,
fileName,
source,
map
};
});
}
const [{ code }] = output;
return code;
};
/**
* @param {import('rollup').RollupBuild} bundle
* @param {import('rollup').OutputOptions} [outputOptions]
*/
export const getFiles = async (bundle, outputOptions) => {
if (!outputOptions.dir && !outputOptions.file)
throw new Error('You must specify "output.file" or "output.dir" for the build.');
const { output } = await bundle.generate(outputOptions || { format: 'cjs', exports: 'auto' });
return output.map(({ code, fileName, source }) => {
const absPath = path.resolve(outputOptions.dir || path.dirname(outputOptions.file), fileName);
return {
fileName: path.relative(process.cwd(), absPath).split(path.sep).join('/'),
content: code || source
};
});
};
export const getImports = async (bundle) => {
if (bundle.imports) {
return bundle.imports;
}
const { output } = await bundle.generate({ format: 'es' });
const [{ imports }] = output;
return imports;
};
export const getResolvedModules = async (bundle) => {
const {
output: [{ modules }]
} = await bundle.generate({ format: 'es' });
return modules;
};
// eslint-disable-next-line no-console
export const onwarn = (warning) => console.warn(warning.toString());
/**
* @param {import('ava').Assertions} t
* @param {import('rollup').RollupBuild} bundle
* @param {object} args
*/
export const testBundle = async (t, bundle, { inject = {}, options = {} } = {}) => {
const { output } = await bundle.generate({ format: 'cjs', exports: 'auto', ...options });
const [{ code }] = output;
const module = { exports: {} };
// as of 1/2/2020 Github Actions + Windows has changed in a way that we must now escape backslashes
const cwd = process.cwd().replace(/\\/g, '\\\\');
const params = ['module', 'exports', 'require', 't', ...Object.keys(inject)].concat(
`process.chdir('${cwd}'); let result;\n\n${code}\n\nreturn result;`
);
// eslint-disable-next-line no-new-func
const func = new Function(...params);
let error;
let result;
try {
result = func(...[module, module.exports, require, t, ...Object.values(inject)]);
} catch (e) {
error = e;
}
return { code, error, module, result };
};
export const evaluateBundle = async (bundle) => {
const { module } = await testBundle(null, bundle);
return module.exports;
};

22
tsconfig.json Normal file
View File

@ -0,0 +1,22 @@
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"noEmit": true,
"noEmitOnError": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"pretty": true,
"sourceMap": true,
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "NodeNext",
"allowJs": true
},
"exclude": ["dist", "node_modules", "test/types"],
"include": ["src/**/*", "types/**/*"],
"ts-node": {
"esm": true // from the top of https://typestrong.org/ts-node/docs/imports/
}
}

28
types/index.d.ts vendored Normal file
View File

@ -0,0 +1,28 @@
import type { Plugin, OutputChunk, OutputAsset, OutputBundle } from 'rollup';
export interface RollupHtmlTemplateOptions {
title: string;
attributes: Record<string, any>;
publicPath: string;
meta: Record<string, any>[];
bundle: OutputBundle;
files: Record<string, (OutputChunk | OutputAsset)[]>;
}
export interface RollupHtmlOptions {
title?: string;
attributes?: Record<string, any>;
fileName?: string;
meta?: Record<string, any>[];
publicPath?: string;
template?: (templateoptions?: RollupHtmlTemplateOptions) => string;
}
export function makeHtmlAttributes(attributes: Record<string, string>): string;
/**
* A Rollup plugin which creates HTML files to serve Rollup bundles.
* @param options - Plugin options.
* @returns Plugin instance.
*/
export default function html(options?: RollupHtmlOptions): Plugin;