From 1afed84abe179d2ffac70d53524b48b21a40f916 Mon Sep 17 00:00:00 2001
From: Mikhail Bashkirov page-a.html page-b.html page-c.html page-a.html page-b.html page-c.html
+
+
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: './index.html',
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { output, assets } = await generateTestBundle(build, {
+ ...outputConfig,
+ assetFileNames: 'static/[name].immutable.[hash][extname]',
+ });
+
+ expect(assets).to.have.keys([
+ 'static/font.immutable.C5MNjX-h.woff2',
+ 'static/global.immutable.DB0fKkjs.css',
+ 'static/image.immutable.7xJLr_7N.png',
+ 'static/styles.immutable.D4tZXVv0.css',
+ 'index.html',
+ ]);
+
+ expect(assets['index.html']).to.equal(html`
+
+
+
+
+
+
+
+
+
+
+ `);
+
+ expect(assets['static/global.immutable.DB0fKkjs.css']).to.equal(css`
+ @font-face {
+ font-family: Font;
+ src: url('font.immutable.C5MNjX-h.woff2') format('woff2');
+ font-weight: normal;
+ font-style: normal;
+ font-display: swap;
+ }
+ `);
+
+ expect(assets['static/styles.immutable.D4tZXVv0.css']).to.equal(css`
+ #a {
+ background-image: url('image.immutable.7xJLr_7N.png');
+ }
+ `);
+ });
+
+ it('correctly resolves paths by using publicPath when assetFileNames puts assets in different dirs', async () => {
+ const rootDir = createApp({
+ 'node_modules/ing-web/fonts/font.woff2': 'font.woff',
+ 'node_modules/ing-web/global.css': css`
+ @font-face {
+ font-family: Font;
+ src: url('fonts/font.woff2') format('woff2');
+ font-weight: normal;
+ font-style: normal;
+ font-display: swap;
+ }
+ `,
+ 'assets/images/image.png': 'image.png',
+ 'assets/styles.css': css`
+ #a {
+ background-image: url('images/image.png');
+ }
+ `,
+ 'src/main.js': js`
+ const imageUrl = new URL('../assets/images/image.png', import.meta.url).href;
+ `,
+ 'index.html': html`
+
+
+
+
+
+
+
+
+
+
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: './index.html',
+ publicPath: '/static/',
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { output, assets } = await generateTestBundle(build, {
+ ...outputConfig,
+ assetFileNames: assetInfo => {
+ const name = assetInfo.names[0] || '';
+ if (name.endsWith('.woff2')) {
+ return 'fonts/[name].immutable.[hash][extname]';
+ } else if (name.endsWith('.css')) {
+ return 'styles/[name].immutable.[hash][extname]';
+ } else if (name.endsWith('.png')) {
+ return 'images/[name].immutable.[hash][extname]';
+ }
+ return '[name].immutable.[hash][extname]';
+ },
+ });
+
+ expect(assets).to.have.keys([
+ 'fonts/font.immutable.C5MNjX-h.woff2',
+ 'styles/global.immutable.B3Q0ucg4.css',
+ 'images/image.immutable.7xJLr_7N.png',
+ 'styles/styles.immutable.C3Z0Fs2-.css',
+ 'index.html',
+ ]);
+
+ expect(assets['index.html']).to.equal(html`
+
+
+
+
+
+
+
+
+
+
+ `);
+
+ expect(assets['styles/global.immutable.B3Q0ucg4.css']).to.equal(css`
+ @font-face {
+ font-family: Font;
+ src: url('/static/fonts/font.immutable.C5MNjX-h.woff2') format('woff2');
+ font-weight: normal;
+ font-style: normal;
+ font-display: swap;
+ }
+ `);
+
+ expect(assets['styles/styles.immutable.C3Z0Fs2-.css']).to.equal(css`
+ #a {
+ background-image: url('/static/images/image.immutable.7xJLr_7N.png');
+ }
+ `);
+ });
+});
diff --git a/packages/rollup-plugin-html/test-new/new.test.ts b/packages/rollup-plugin-html/test-new/new.test.ts
new file mode 100644
index 000000000..e98a8438f
--- /dev/null
+++ b/packages/rollup-plugin-html/test-new/new.test.ts
@@ -0,0 +1,2402 @@
+import synchronizedPrettier from '@prettier/sync';
+import * as prettier from 'prettier';
+import { rollup, OutputChunk, OutputOptions, Plugin, RollupBuild } from 'rollup';
+import { expect } from 'chai';
+import path from 'path';
+import fs from 'fs';
+import { rollupPluginHTML } from '../src/index.js';
+
+// TODO: test output "fileName" too, like the real output name, not always it's properly checked besides checking the index.html source
+
+function collapseWhitespaceAll(str: string) {
+ return (
+ str &&
+ str.replace(/[ \n\r\t\f\xA0]+/g, spaces => {
+ return spaces === '\t' ? '\t' : spaces.replace(/(^|\xA0+)[^\xA0]+/g, '$1 ');
+ })
+ );
+}
+
+function format(str: string, parser: prettier.BuiltInParserName) {
+ return synchronizedPrettier.format(str, { parser, semi: true, singleQuote: true });
+}
+
+function merge(strings: TemplateStringsArray, ...values: string[]): string {
+ return strings.reduce((acc, str, i) => acc + str + (values[i] || ''), '');
+}
+
+const extnameToFormatter: Recordhello world
+
+
+ `,
+ });
+
+ const config = {
+ input: './index.html',
+ plugins: [rollupPluginHTML({ rootDir })],
+ };
+
+ const build = await rollup(config);
+ const { assets } = await generateTestBundle(build, outputConfig);
+
+ expect(assets['index.html']).to.equal(html`
+
+
+
+ hello world
+
+
+ `);
+ });
+
+ it('can build with multiple pure html inputs', async () => {
+ const rootDir = createApp({
+ 'index1.html': html`
+
+
+
+ hello world
+
+
+ `,
+ 'index2.html': html`
+
+
+
+ hey there
+
+
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: ['./index1.html', './index2.html'],
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { assets } = await generateTestBundle(build, outputConfig);
+
+ expect(assets['index1.html']).to.equal(html`
+
+
+
+ hello world
+
+
+ `);
+ expect(assets['index2.html']).to.equal(html`
+
+
+
+ hey there
+
+
+ `);
+ });
+
+ it('can build with html string as input', async () => {
+ const rootDir = createApp({
+ 'app.js': js`
+ console.log('app.js');
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: {
+ name: 'index.html',
+ html: ``,
+ },
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { chunks, assets } = await generateTestBundle(build, outputConfig);
+
+ expect(Object.keys(chunks)).to.have.lengthOf(1);
+ expect(Object.keys(assets)).to.have.lengthOf(1);
+
+ expect(assets['index.html']).to.equal(html`
+
+
+
+
+
+
+ `);
+ });
+
+ it('resolves paths relative to virtual html filename', async () => {
+ const rootDir = createApp({
+ 'app.js': js`
+ console.log('app.js');
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: {
+ name: 'nested/index.html',
+ html: ``,
+ },
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { chunks, assets } = await generateTestBundle(build, outputConfig);
+
+ expect(Object.keys(chunks)).to.have.lengthOf(1);
+ expect(Object.keys(assets)).to.have.lengthOf(1);
+
+ expect(assets['nested/index.html']).to.equal(html`
+
+
+
+
+
+
+ `);
+ });
+
+ it('can build with inline modules', async () => {
+ const rootDir = createApp({
+ 'app.js': js`
+ console.log('app.js');
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: {
+ name: 'index.html',
+ html: ``,
+ },
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { chunks, assets } = await generateTestBundle(build, outputConfig);
+
+ expect(Object.keys(chunks)).to.have.lengthOf(1);
+ expect(Object.keys(assets)).to.have.lengthOf(1);
+
+ const hash = '16165cb387fc14ed1fe1749d05f19f7b';
+
+ expect(assets['index.html']).to.equal(html`
+
+
+
+
+
+
+ `);
+
+ expect(chunks[`inline-module-${hash}.js`]).to.include(js`console.log('app.js');`);
+ });
+
+ it('resolves inline module imports relative to the HTML file', async () => {
+ const rootDir = createApp({
+ 'nested/index.html': html`
+
+
+
+
+
+
+ `,
+ 'nested/app.js': js`
+ console.log('app.js');
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: './nested/index.html',
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { chunks, assets } = await generateTestBundle(build, outputConfig);
+
+ expect(Object.keys(chunks)).to.have.lengthOf(1);
+ expect(Object.keys(assets)).to.have.lengthOf(1);
+
+ const hash = 'b774aefb8bf002b291fd54d27694a34d';
+ expect(chunks[`inline-module-${hash}.js`]).to.include(js`console.log('app.js');`);
+ });
+
+ it('can build transforming final output', async () => {
+ const rootDir = createApp({
+ 'app.js': js`
+ console.log('app.js');
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: {
+ html: `Hello world
`,
+ },
+ transformHtml(html) {
+ return html.replace('Hello world', 'Goodbye world');
+ },
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { chunks, assets } = await generateTestBundle(build, outputConfig);
+
+ expect(Object.keys(chunks)).to.have.lengthOf(1);
+ expect(Object.keys(assets)).to.have.lengthOf(1);
+
+ expect(assets['index.html']).to.equal(html`
+
+
+
+ Goodbye world
+
+
+
+ `);
+ });
+
+ it('can build with a public path', async () => {
+ const rootDir = createApp({
+ 'app.js': js`
+ console.log('app.js');
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: {
+ html: ``,
+ },
+ publicPath: '/static/',
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { chunks, assets } = await generateTestBundle(build, outputConfig);
+
+ expect(Object.keys(chunks)).to.have.lengthOf(1);
+ expect(Object.keys(assets)).to.have.lengthOf(1);
+
+ expect(assets['index.html']).to.equal(html`
+
+
+
+
+
+
+ `);
+ });
+
+ it('can build with a public path with a file in a directory', async () => {
+ const rootDir = createApp({
+ 'app.js': js`
+ console.log('app.js');
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: {
+ name: 'nested/index.html',
+ html: ``,
+ },
+ publicPath: '/static/',
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { chunks, assets } = await generateTestBundle(build, outputConfig);
+
+ expect(Object.keys(chunks)).to.have.lengthOf(1);
+ expect(Object.keys(assets)).to.have.lengthOf(1);
+
+ expect(assets['nested/index.html']).to.equal(html`
+
+
+
+
+
+
+ `);
+ });
+
+ it('can build with multiple build outputs', async () => {
+ const rootDir = createApp({
+ 'app.js': js`
+ import './modules/module.js';
+ console.log('app.js');
+ `,
+ 'modules/module.js': js`
+ console.log('module.js');
+ `,
+ });
+
+ const plugin = rollupPluginHTML({
+ rootDir,
+ input: {
+ html: ``,
+ },
+ publicPath: '/static/',
+ });
+
+ const config = {
+ input: path.join(rootDir, 'app.js'),
+ plugins: [plugin],
+ };
+
+ const build = await rollup(config);
+
+ const bundleA = generateTestBundle(build, {
+ format: 'system',
+ dir: 'dist',
+ plugins: [plugin.api.addOutput('legacy')],
+ });
+
+ const bundleB = generateTestBundle(build, {
+ format: 'es',
+ dir: 'dist',
+ plugins: [plugin.api.addOutput('modern')],
+ });
+
+ const { chunks: chunksA, assets: assetsA } = await bundleA;
+ const { chunks: chunksB, assets: assetsB } = await bundleB;
+
+ expect(Object.keys(chunksA)).to.have.lengthOf(1);
+ expect(Object.keys(assetsA)).to.have.lengthOf(0);
+ expect(Object.keys(chunksB)).to.have.lengthOf(1);
+ expect(Object.keys(assetsB)).to.have.lengthOf(1);
+
+ expect(chunksA['app.js']).to.include(js`console.log('app.js');`);
+ expect(chunksA['app.js']).to.include(js`console.log('module.js');`);
+ expect(chunksB['app.js']).to.include(js`console.log('app.js');`);
+ expect(chunksB['app.js']).to.include(js`console.log('module.js');`);
+
+ expect(assetsA['index.html']).to.not.exist;
+ expect(assetsB['index.html']).to.equal(html`
+
+
+
+
+
+
+
+ `);
+ });
+
+ it('can build with index.html as input and an extra html file as output', async () => {
+ const rootDir = createApp({
+ 'app.js': js`
+ console.log('app.js');
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: {
+ html: ``,
+ },
+ }),
+ rollupPluginHTML({
+ rootDir,
+ input: {
+ name: 'foo.html',
+ html: `foo.html
`,
+ },
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { chunks, assets } = await generateTestBundle(build, outputConfig);
+
+ expect(Object.keys(chunks)).to.have.lengthOf(2);
+ expect(Object.keys(assets)).to.have.lengthOf(2);
+
+ expect(chunks['app.js']).to.exist;
+ expect(assets['index.html']).to.equal(html`
+
+
+
+
+
+
+ `);
+ expect(assets['foo.html']).to.equal(html`
+
+
+
+ foo.html
+
+
+ `);
+ });
+
+ it('can build with multiple html inputs', async () => {
+ const rootDir = createApp({
+ 'entrypoint-a.js': js`
+ import './modules/module-a.js';
+ console.log('entrypoint-a.js');
+ `,
+ 'entrypoint-b.js': js`
+ import './modules/module-b.js';
+ console.log('entrypoint-b.js');
+ `,
+ 'entrypoint-c.js': js`
+ import './modules/module-c.js';
+ console.log('entrypoint-c.js');
+ `,
+ 'modules/module-a.js': js`
+ import './shared-module.js';
+ console.log('module-a.js');
+ `,
+ 'modules/module-b.js': js`
+ import './shared-module.js';
+ console.log('module-b.js');
+ `,
+ 'modules/module-c.js': js`
+ import './shared-module.js';
+ console.log('module-c.js');
+ `,
+ 'modules/shared-module.js': js`
+ console.log('shared-module.js');
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: [
+ {
+ name: 'page-a.html',
+ html: `Page A
`,
+ },
+ {
+ name: 'page-b.html',
+ html: `Page B
`,
+ },
+ {
+ name: 'page-c.html',
+ html: `Page C
`,
+ },
+ ],
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { chunks, assets } = await generateTestBundle(build, outputConfig);
+
+ expect(Object.keys(chunks)).to.have.lengthOf(4);
+ expect(Object.keys(assets)).to.have.lengthOf(3);
+
+ expect(chunks['entrypoint-a.js']).to.exist;
+ expect(chunks['entrypoint-b.js']).to.exist;
+ expect(chunks['entrypoint-c.js']).to.exist;
+
+ expect(assets['page-a.html']).to.equal(html`
+
+
+
+ Page A
+
+
+
+ `);
+ expect(assets['page-b.html']).to.equal(html`
+
+
+
+ Page B
+
+
+
+ `);
+ expect(assets['page-c.html']).to.equal(html`
+
+
+
+ Page C
+
+
+
+ `);
+ });
+
+ it('can use a glob to build multiple pages', async () => {
+ const rootDir = createApp({
+ 'pages/page-a.html': html`
+
+
+ Page A
`,
+ },
+ {
+ name: 'nestedB/indexB.html',
+ html: `Page B
`,
+ },
+ {
+ name: 'indexC.html',
+ html: `Page C
`,
+ },
+ ],
+ }),
+ ],
+ };
+ const build = await rollup(config);
+ const { chunks, assets } = await generateTestBundle(build, outputConfig);
+
+ expect(Object.keys(chunks)).to.have.lengthOf(3);
+ expect(Object.keys(assets)).to.have.lengthOf(3);
+
+ expect(chunks['inline-module-d463148d1d5869e52917a3b270db9e72.js']).to.exist;
+ expect(chunks['inline-module-b81da853430abdf130bcc7c4d0ade6d9.js']).to.exist;
+ expect(chunks['inline-module-170bb2146da66c440259138c7e0fea7e.js']).to.exist;
+
+ expect(assets['nestedA/indexA.html']).to.equal(html`
+
+
+
+ Page A
+
+
+
+ `);
+ expect(assets['nestedB/indexB.html']).to.equal(html`
+
+
+
+ Page B
+
+
+
+ `);
+ expect(assets['indexC.html']).to.equal(html`
+
+
+
+ Page C
+
+
+
+ `);
+ });
+
+ it('deduplicates common modules', async () => {
+ const rootDir = createApp({});
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: [
+ {
+ name: 'a.html',
+ html: `Page A
`,
+ },
+ {
+ name: 'b.html',
+ html: `Page B
`,
+ },
+ {
+ name: 'c.html',
+ html: `Page C
`,
+ },
+ ],
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { chunks, assets } = await generateTestBundle(build, outputConfig);
+
+ expect(Object.keys(chunks)).to.have.lengthOf(1);
+ expect(Object.keys(assets)).to.have.lengthOf(3);
+
+ expect(chunks['inline-module-44281cf3dede62434e0dd368df08902f.js']).to.exist;
+
+ expect(assets['a.html']).to.equal(html`
+
+
+
+ Page A
+
+
+
+ `);
+ expect(assets['b.html']).to.equal(html`
+
+
+
+ Page B
+
+
+
+ `);
+ expect(assets['c.html']).to.equal(html`
+
+
+
+ Page C
+
+
+
+ `);
+ });
+
+ it('outputs the hashed entrypoint name', async () => {
+ const rootDir = createApp({
+ 'app.js': js`
+ console.log('app.js');
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: {
+ html: ``,
+ },
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { output, chunks, assets } = await generateTestBundle(build, {
+ ...outputConfig,
+ entryFileNames: '[name]-[hash].js',
+ });
+
+ expect(Object.keys(chunks)).to.have.lengthOf(1);
+ expect(Object.keys(assets)).to.have.lengthOf(1);
+
+ const appChunk = output.find(f =>
+ // @ts-ignore
+ f.facadeModuleId.endsWith('app.js'),
+ ) as OutputChunk;
+
+ // ensure it's actually hashed
+ expect(appChunk.fileName).to.not.equal('app.js');
+
+ // get hashed name dynamically
+ expect(assets['index.html']).to.equal(html`
+
+
+
+
+
+
+ `);
+ });
+
+ it('outputs import path relative to the final output html', async () => {
+ const rootDir = createApp({
+ 'app.js': js`
+ console.log('app.js');
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: {
+ name: 'nested/index.html',
+ html: '',
+ },
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { chunks, assets } = await generateTestBundle(build, outputConfig);
+
+ expect(Object.keys(chunks)).to.have.lengthOf(1);
+ expect(Object.keys(assets)).to.have.lengthOf(1);
+
+ expect(assets['nested/index.html']).to.equal(html`
+
+
+
+
+
+
+ `);
+ });
+
+ it('can change HTML root directory', async () => {
+ const rootDir = createApp({
+ 'different-root/src/app.js': js`
+ console.log('app.js');
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir: path.join(rootDir, 'different-root'),
+ input: {
+ name: 'src/nested/index.html',
+ html: '',
+ },
+ }),
+ ],
+ };
+
+ const build = await rollup(config);
+ const { chunks, assets } = await generateTestBundle(build, outputConfig);
+
+ expect(Object.keys(chunks)).to.have.lengthOf(1);
+ expect(Object.keys(assets)).to.have.lengthOf(1);
+
+ expect(assets['src/nested/index.html']).to.equal(html`
+
+
+
+
+
+
+ `);
+ });
+
+ it('can get the input with getInputs()', async () => {
+ // default filename
+ const pluginA = rollupPluginHTML({ input: { html: 'Hello world' } });
+
+ // filename inferred from input filename
+ const rootDirB = createApp({
+ 'my-page.html': html``,
+ 'app.js': js`console.log('app.js');`,
+ });
+ const pluginB = rollupPluginHTML({
+ input: path.join(rootDirB, 'my-page.html'),
+ });
+
+ // filename explicitly set
+ const rootDirC = createApp({
+ 'index.html': html``,
+ 'app.js': js`console.log('app.js');`,
+ });
+ const pluginC = rollupPluginHTML({
+ input: {
+ name: 'nested/my-other-page.html',
+ path: path.join(rootDirC, 'index.html'),
+ },
+ });
+
+ await rollup({ plugins: [pluginA] });
+ await rollup({ plugins: [pluginB] });
+ await rollup({ plugins: [pluginC] });
+
+ expect(pluginA.api.getInputs()[0].name).to.equal('index.html');
+ expect(pluginB.api.getInputs()[0].name).to.equal('my-page.html');
+ expect(pluginC.api.getInputs()[0].name).to.equal('nested/my-other-page.html');
+ });
+
+ it('supports other plugins injecting a transform function', async () => {
+ const rootDir = createApp({
+ 'index.html': html`
+
+
+
+
+
+
+
+ `,
+ 'entrypoint-a.js': js`
+ import './modules/module-a.js';
+ console.log('entrypoint-a.js');
+ `,
+ 'entrypoint-b.js': js`
+ import './modules/module-b.js';
+ console.log('entrypoint-b.js');
+ `,
+ 'modules/module-a.js': js`
+ import './shared-module.js';
+ console.log('module-a.js');
+ `,
+ 'modules/module-b.js': js`
+ import './shared-module.js';
+ console.log('module-b.js');
+ `,
+ 'modules/shared-module.js': js`
+ console.log('shared-module.js');
+ `,
+ });
+
+ const config = {
+ plugins: [
+ rollupPluginHTML({
+ rootDir,
+ input: './index.html',
+ }),
+ {
+ name: 'other-plugin',
+ buildStart(options) {
+ if (!options.plugins) throw new Error('no plugins');
+ const plugin = options.plugins.find(pl => {
+ if (pl.name === '@web/rollup-plugin-html') {
+ return pl!.api.getInputs()[0].name === 'index.html';
+ }
+ return false;
+ });
+ plugin!.api.addHtmlTransformer((html: string) =>
+ html.replace('
', ''), + ); + }, + } as Plugin, + ], + }; + + const build = await rollup(config); + const { chunks, assets } = await generateTestBundle(build, outputConfig); + + expect(Object.keys(chunks)).to.have.lengthOf(3); + expect(Object.keys(assets)).to.have.lengthOf(1); + + expect(chunks['entrypoint-a.js']).to.include(js`console.log('entrypoint-a.js');`); + expect(chunks['entrypoint-b.js']).to.include(js`console.log('entrypoint-b.js');`); + expect(assets['index.html']).to.equal(html` + +
+
+