rollup#rollup TypeScript Examples

The following examples show how to use rollup#rollup. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.ts    From rollup-plugin-swc with MIT License 6 votes vote down vote up
runMinify = async (
  options: JsMinifyOptions,
  {
    input = './fixture/index.js',
    otherRollupPlugins = [],
    sourcemap = false,
    dir = '.'
  }
) => {
  const build = await rollup({
    input: [...(Array.isArray(input) ? input : [input])].map((v) => path.resolve(dir, v)),
    plugins: [...otherRollupPlugins, minify(options)]
  });
  const { output } = await build.generate({ format: 'esm', sourcemap });
  return output;
}
Example #2
Source File: gulpfile.ts    From gdir with Do What The F*ck You Want To Public License 6 votes vote down vote up
gulp.task('worker.rollup', async () => {
    const output: OutputOptions = {
        file: './dist/worker.js',
        format: 'iife',
        sourcemap: true,
    };
    const input: RollupOptions = {
        input: './worker/index.ts',
        plugins: [
            resolve({
                extensions: ['.js', '.ts'],
            }),
            commonjs({
                include: 'node_modules/**',
                extensions: ['.js', '.ts'],
            }),
            typescriptPlugin({
                tsconfig: './worker/tsconfig.json',
            }),
        ],
    };
    const bundle = await rollup(input);
    await bundle.write(output);
});
Example #3
Source File: gulpfile.ts    From gdir with Do What The F*ck You Want To Public License 6 votes vote down vote up
gulp.task('app.rollup', async () => {
    const input: RollupOptions = {
        input: './app/index.tsx',
        external: ['react', 'react-dom', 'react-router-dom', 'rxjs', 'rxjs/ajax', 'rxjs/operators'],
        plugins: [
            resolve({
                extensions: ['.js', '.ts', '.tsx'],
            }),
            commonjs({
                include: 'node_modules/**',
                extensions: ['.js', '.ts'],
            }),
            typescriptPlugin({
                tsconfig: './app/tsconfig.json',
            }),
        ],
    };
    const output: RollupOptions = {
        output: {
            file: './dist/static/app.js',
            format: 'iife',
            sourcemap: 'inline',
            globals: {
                react: 'React',
                'react-dom': 'ReactDOM',
                'react-router-dom': 'ReactRouterDOM',
                rxjs: 'rxjs',
                'rxjs/ajax': 'rxjs.ajax',
                'rxjs/operators': 'rxjs.operators',
            },
        },
    };

    const bundle = await rollup(input);
    await bundle.write(output);
});
Example #4
Source File: rollup-plugin.test.ts    From vanilla-extract with MIT License 6 votes vote down vote up
async function build(outputOptions: OutputOptions) {
  const bundle = await rollup({
    input: require.resolve('@fixtures/themed'),
    plugins: [
      vanillaExtractPlugin({
        cwd: path.dirname(require.resolve('@fixtures/themed/package.json')),
      }),
      esbuild(),
      json(),
    ],
    external: ['@vanilla-extract/dynamic'],
  });
  const { output } = await bundle.generate(outputOptions);
  output.sort((a, b) => a.fileName.localeCompare(b.fileName));
  return output;
}
Example #5
Source File: rollupPluginPolyfillsLoader.test.ts    From web with MIT License 6 votes vote down vote up
async function testSnapshot({ name, fileName, inputOptions, outputOptions }: SnapshotArgs) {
  const snapshotPath = path.join(__dirname, '..', 'snapshots', `${name}.html`);
  const bundle = await rollup(inputOptions);
  let output;
  for (const outputConfig of outputOptions) {
    ({ output } = await bundle.generate(outputConfig));
  }

  if (!output) throw new Error('');

  const file = getAsset(output, fileName);
  if (!file) throw new Error(`Build did not output ${fileName}`);

  if (updateSnapshots) {
    fs.writeFileSync(snapshotPath, file.source, 'utf-8');
  } else {
    const snapshot = fs.readFileSync(snapshotPath, 'utf-8');
    expect(file.source.trim()).to.equal(snapshot.trim());
    // expect(file.source.replace(/\s/g, '')).to.equal(snapshot.replace(/\s/g, ''));
  }
  return output;
}
Example #6
Source File: buildAndWrite.ts    From web with MIT License 6 votes vote down vote up
export async function buildAndWrite(options: RollupOptions) {
  const bundle = await rollup(options);

  if (Array.isArray(options.output)) {
    await bundle.write(options.output[0]);
    await bundle.write(options.output[1]);
  } else if (options.output) {
    await bundle.write(options.output);
  }
}
Example #7
Source File: rollupBundlePlugin.ts    From web with MIT License 6 votes vote down vote up
async function bundleEntrypoints(rollupConfig: RollupOptions) {
  const bundle = await rollup(rollupConfig);
  if (Array.isArray(rollupConfig.output)) {
    throw new Error('Multiple outputs not supported.');
  }

  return bundle.generate({
    ...rollupConfig.output,
    chunkFileNames: '__rollup-generated__[name].js',
    assetFileNames: '__rollup-generated__[name][extname]',
  });
}
Example #8
Source File: build-rollup.ts    From r6operators with MIT License 6 votes vote down vote up
// build ts
export async function buildBundle(): Promise<void> {
  // Track iterations over output files
  let analyzePluginIterations = 0

  const bundle = await rollup({
    input: ENTRY_FILE,
    plugins: [
      nodeResolve(),
      ts({ transpiler: "babel" }),
      analyze({
        summaryOnly: true,
        onAnalysis: () => {
          if (analyzePluginIterations > 0) {
            throw "" // We only want reports on the first output
          }
          analyzePluginIterations++
        },
      }),
    ],
  })

  await bundle.write({
    file: pkg.main,
    format: "cjs",
    sourcemap: true,
    exports: "named",
  })
  await bundle.write({
    file: pkg.module,
    format: "esm",
    sourcemap: true,
    exports: "named",
  })
  console.log(`\nSuccessfully bundled library!\n`)
}
Example #9
Source File: test.ts    From stencil-tailwind with MIT License 6 votes vote down vote up
test('sass: @apply (css)', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/components/styles/apply.input.css',
    plugins: [ tailwind() ]
  })
  const actual = await getCodeFromBundle(bundle)
  const expected = readFileSync('test/fixtures/components/styles/apply.output.js', 'utf-8');
  t.is(actual, expected)
})
Example #10
Source File: packager.ts    From backstage with Apache License 2.0 6 votes vote down vote up
async function rollupBuild(config: RollupOptions) {
  try {
    const bundle = await rollup(config);
    if (config.output) {
      for (const output of [config.output].flat()) {
        await bundle.generate(output);
        await bundle.write(output);
      }
    }
  } catch (error) {
    throw new Error(formatErrorMessage(error));
  }
}
Example #11
Source File: index.ts    From rollup-plugin-swc with MIT License 6 votes vote down vote up
build = async (
  options?: PluginOptions,
  {
    input = './fixture/index.js',
    otherRollupPlugins = [],
    sourcemap = false,
    dir = '.'
  }: {
    input?: string | string[]
    otherRollupPlugins?: RollupPlugin[]
    sourcemap?: boolean
    dir?: string
  } = {}
) => {
  const build = await rollup({
    input: [...(Array.isArray(input) ? input : [input])].map((v) => path.resolve(dir, v)),
    plugins: [...otherRollupPlugins, swc(options)]
  });
  const { output } = await build.generate({ format: 'esm', sourcemap });
  return output;
}
Example #12
Source File: build.ts    From rollup-plugin-swc with MIT License 6 votes vote down vote up
async function main() {
  const external = [...deps, ...module.builtinModules];

  async function build() {
    const bundle = await rollup({
      input: './src/index.ts',
      external,
      plugins: [swc(defineRollupSwcOption({
        jsc: {
          target: 'es2019'
        }
      }))]
    });

    return Promise.all([
      bundle.write({ file: './dist/index.js', format: 'cjs' }),
      bundle.write({ file: './dist/index.mjs', format: 'esm' })
    ]);
  }

  async function createDtsFile() {
    const bundle = await rollup({
      input: './src/index.ts',
      external,
      plugins: [dts()]
    });

    return bundle.write({ file: './dist/index.d.ts' });
  }

  return Promise.all([build(), createDtsFile()]);
}
Example #13
Source File: test.ts    From stencil-tailwind with MIT License 6 votes vote down vote up
test('sass: @apply (scss)', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/components/styles/apply.input.scss',
    plugins: [ tailwind() ]
  })
  const actual = await getCodeFromBundle(bundle)
  const expected = readFileSync('test/fixtures/components/styles/apply.output.js', 'utf-8');
  t.is(actual, expected)
})
Example #14
Source File: test.ts    From stencil-tailwind with MIT License 6 votes vote down vote up
test('inline class: literal', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/components/inline-class/literal.input.tsx',
    plugins: [ tailwind() ]
  })
  const actual = await getCodeFromBundle(bundle)
  const expected = readFileSync('test/fixtures/components/inline-class/literal.output.js', 'utf-8');
  t.is(actual, expected)
})
Example #15
Source File: test.ts    From stencil-tailwind with MIT License 6 votes vote down vote up
test('inline class: conditional expression', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/components/inline-class/cond-expression.input.tsx',
    plugins: [ tailwind() ]
  })
  const actual = await getCodeFromBundle(bundle)
  const expected = readFileSync('test/fixtures/components/inline-class/cond-expression.output.js', 'utf-8');
  t.is(actual, expected)
})
Example #16
Source File: test.ts    From stencil-tailwind with MIT License 6 votes vote down vote up
test('style decorator', async t => {
  const bundle = await rollup({
    input: 'test/fixtures/components/decorator/input.tsx',
    plugins: [ tailwind() ]
  })
  const actual = await getCodeFromBundle(bundle)
  const expected = readFileSync('test/fixtures/components/decorator/output.js', 'utf-8');
  t.is(actual, expected)
})
Example #17
Source File: createRollupPluginContexts.ts    From web with MIT License 5 votes vote down vote up
/**
 * Runs rollup with an empty module in order to capture the plugin context and
 * normalized options.
 * @param inputOptions
 */
export async function createRollupPluginContexts(
  inputOptions: InputOptions,
): Promise<RollupPluginContexts> {
  let normalizedInputOptions: NormalizedInputOptions | undefined = undefined;
  let pluginContext: PluginContext | undefined = undefined;
  let transformPluginContext: TransformPluginContext | undefined = undefined;

  await rollup({
    ...inputOptions,

    input: 'noop',

    plugins: [
      {
        name: 'noop',
        buildStart(options) {
          normalizedInputOptions = options;
        },
        resolveId(id) {
          pluginContext = this;
          return id;
        },
        load() {
          return '';
        },
        transform() {
          transformPluginContext = this;
          return null;
        },
      },
    ],
  });

  if (!normalizedInputOptions || !pluginContext || !transformPluginContext) {
    throw new TypeError();
  }

  return {
    normalizedInputOptions,
    pluginContext,
    transformPluginContext,
    minimalPluginContext: { meta: (pluginContext as PluginContext).meta },
  };
}
Example #18
Source File: transpiler.ts    From generator-react-sdk with Apache License 2.0 5 votes vote down vote up
/**
 * Transpile files in a given directory (and sub directory if recursive option are passed) and write it to an output directory, if no errors are thrown it completed successfully.
 * 
 * @param directory to transpile.
 * @param outputDir to write the transpiled files to.
 * @param options any extra options that should be passed.
 */
export async function transpileFiles(directory: string, outputDir: string, options?: TranspileFilesOptions) {
    const { files, dirs } = await getStatsInDir(directory);
    if (files.length) {
        /**
         * WHEN ADDING PLUGINS to transform the input keep in mind that 
         * IF any of these changes the actual original content in some way
         * the output is not able to produce accurate source map to the original file.
         * 
         * An example of this is using the `sourceMaps: 'inline'` configuration for the babel plugin.
         */
        const bundles = await rollup({
            input: files,
            onwarn: () => {},
            plugins: [
                babel({
                    cwd: ROOT_DIR,
                    babelHelpers: "bundled",
                    plugins: [
                        "source-map-support",
                    ],
                    presets: [
                        ["@babel/preset-env", {
                            targets: { node: "12.16" },
                        }],
                        ["@babel/preset-react", {
                            runtime: "automatic",
                        }],
                    ],
                })
            ],
        })
        await bundles.write({
            format: "commonjs",
            sourcemap: true,
            dir: outputDir,
            exports: "auto",
            paths: {
              'react/jsx-runtime': 'react/cjs/react-jsx-runtime.production.min',
            },
            sanitizeFileName: false,
        })
    }

    // Check if we should transpile all subdirs
    if (options?.recursive === true && dirs.length > 0) {
        for (const subdir of dirs) {
            const subdirPath = Path.parse(subdir);
            await transpileFiles(subdir, Path.resolve(outputDir, subdirPath.base), options);
        }
    }
}
Example #19
Source File: rollup.ts    From reactant with MIT License 5 votes vote down vote up
generateBundledModules = async ({
  inputFile,
  outputFile,
  format,
  name,
  banner,
}: GenerateOption) => {
  console.log(`Generating bundle:`);
  console.log(chalk.grey(`-> ${outputFile}`));
  const isUmd = format === 'umd';
  const plugins = [
    resolvePlugin(),
    commonjsPlugin({
      namedExports: {
        react: Object.keys(require('react')),
        'react-dom': Object.keys(require('react-dom')),
        'react-is': Object.keys(require('react-is')),
        'react-router-dom': Object.keys(require('react-router-dom')),
        'react-redux': Object.keys(require('react-redux')),
        redux: Object.keys(require('redux')),
        inversify: Object.keys(require('inversify')),
      },
    }),
  ];
  plugins.push(
    replacePlugin({
      __DEV__: isUmd ? 'false' : "process.env.NODE_ENV !== 'production'",
    }),
    terserPlugin()
  );
  try {
    const { dependencies = {}, devDependencies = {} } = require(path.resolve(
      outputFile,
      '../../package.json'
    ));
    const external = Object.keys({ ...dependencies, ...devDependencies });
    const bundle = await rollup({
      input: inputFile,
      external,
      plugins,
    });
    await bundle.write({
      file: outputFile,
      format,
      exports: 'named',
      name: isUmd ? name : undefined,
      banner,
    });
    console.log(chalk.green(`Succeed to generate ${outputFile} bundle.\n`));
  } catch (e) {
    console.log(chalk.red(`Failed to generate ${outputFile} bundle.\n`));
    console.log(chalk.red(e));
  }
}
Example #20
Source File: compile.ts    From mantine with MIT License 5 votes vote down vote up
export default async function compile(config: RollupOptions) {
  const build = await rollup(config);
  const outputs: OutputOptions[] = Array.isArray(config.output) ? config.output : [config.output];

  return Promise.all(outputs.map((output) => build.write(output)));
}
Example #21
Source File: rollup-plugin-html.test.ts    From web with MIT License 4 votes vote down vote up
describe('rollup-plugin-html', () => {
  it('can build with an input path as input', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: require.resolve('./fixtures/rollup-plugin-html/index.html'),
          rootDir,
        }),
      ],
    };
    const bundle = await 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(stripNewlines(getAsset(output, 'index.html').source)).to.equal(
      '<html><head></head><body><h1>hello world</h1>' +
        '<script type="module" src="./entrypoint-a.js"></script>' +
        '<script type="module" src="./entrypoint-b.js"></script>' +
        '</body></html>',
    );
  });

  it('can build with html file as rollup input', async () => {
    const config = {
      input: require.resolve('./fixtures/rollup-plugin-html/index.html'),
      plugins: [rollupPluginHTML({ rootDir })],
    };
    const bundle = await 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(stripNewlines(getAsset(output, 'index.html').source)).to.equal(
      '<html><head></head><body><h1>hello world</h1>' +
        '<script type="module" src="./entrypoint-a.js"></script>' +
        '<script type="module" src="./entrypoint-b.js"></script>' +
        '</body></html>',
    );
  });

  it('will retain attributes on script tags', async () => {
    const config = {
      input: require.resolve('./fixtures/rollup-plugin-html/retain-attributes.html'),
      plugins: [rollupPluginHTML({ rootDir })],
    };
    const bundle = await 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(stripNewlines(getAsset(output, 'retain-attributes.html').source)).to.equal(
      '<html><head></head><body><h1>hello world</h1>' +
        '<script type="module" src="./entrypoint-a.js" keep-this-attribute=""></script>' +
        '<script type="module" src="./entrypoint-b.js"></script>' +
        '</body></html>',
    );
  });

  it('can build with pure html file as rollup input', async () => {
    const config = {
      input: require.resolve('./fixtures/rollup-plugin-html/pure-index.html'),
      plugins: [rollupPluginHTML({ rootDir })],
    };
    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);
    expect(stripNewlines(getAsset(output, 'pure-index.html').source)).to.equal(
      '<html><head></head><body><h1>hello world</h1></body></html>',
    );
  });

  it('can build with multiple pure html inputs', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: [
            require.resolve('./fixtures/rollup-plugin-html/pure-index.html'),
            require.resolve('./fixtures/rollup-plugin-html/pure-index2.html'),
          ],
          rootDir,
        }),
      ],
    };
    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);
    expect(stripNewlines(getAsset(output, 'pure-index.html').source)).to.equal(
      '<html><head></head><body><h1>hello world</h1></body></html>',
    );
    expect(stripNewlines(getAsset(output, 'pure-index2.html').source)).to.equal(
      '<html><head></head><body><h1>hey there</h1></body></html>',
    );
  });

  it('can build with html string as input', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: {
            name: 'index.html',
            html: '<h1>Hello world</h1><script type="module" src="./entrypoint-a.js"></script>',
          },
          rootDir,
        }),
      ],
    };

    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);
    expect(output.length).to.equal(2);
    expect(stripNewlines(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('resolves paths relative to virtual html filename', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: {
            name: 'pages/index.html',
            html: '<h1>Hello world</h1><script type="module" src="../entrypoint-a.js"></script>',
          },
          rootDir,
        }),
      ],
    };

    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);
    expect(output.length).to.equal(2);
    expect(stripNewlines(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>',
    );
  });

  it('can build with inline modules', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          rootDir,
          input: {
            name: 'index.html',
            html: '<h1>Hello world</h1><script type="module">import "./entrypoint-a.js";</script>',
          },
        }),
      ],
    };

    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);
    expect(output.length).to.equal(2);
    const hash = '5ec680a4efbb48ae254268ab1defe610';
    const { code: appCode } = getChunk(output, `inline-module-${hash}.js`);
    expect(appCode).to.include("console.log('entrypoint-a.js');");
    expect(stripNewlines(getAsset(output, 'index.html').source)).to.equal(
      '<html><head></head><body><h1>Hello world</h1>' +
        `<script type="module" src="./inline-module-${hash}.js"></script>` +
        '</body></html>',
    );
  });

  it('resolves inline module imports relative to the HTML file', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: require.resolve('./fixtures/rollup-plugin-html/foo/foo.html'),
          rootDir,
        }),
      ],
    };
    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);
    expect(output.length).to.equal(2);
    const { code: appCode } = getChunk(output, 'inline-module-1b13383486c70d87f4e2585ff87b147c.js');
    expect(appCode).to.include("console.log('foo');");
  });

  it('can build transforming final output', async () => {
    const config = {
      input: require.resolve('./fixtures/rollup-plugin-html/entrypoint-a.js'),
      plugins: [
        rollupPluginHTML({
          rootDir,
          input: {
            html: '<h1>Hello world</h1><script type="module" src="./entrypoint-a.js"></script>',
          },
          transformHtml(html) {
            return html.replace('Hello world', 'Goodbye world');
          },
        }),
      ],
    };
    const bundle = await 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: require.resolve('./fixtures/rollup-plugin-html/entrypoint-a.js'),
      plugins: [
        rollupPluginHTML({
          rootDir,
          input: {
            html: '<h1>Hello world</h1><script type="module" src="./entrypoint-a.js"></script>',
          },
          publicPath: '/static/',
        }),
      ],
    };
    const bundle = await 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: require.resolve('./fixtures/rollup-plugin-html/entrypoint-a.js'),
      plugins: [
        rollupPluginHTML({
          rootDir,
          input: {
            name: 'pages/index.html',
            html: '<h1>Hello world</h1><script type="module" src="../entrypoint-a.js"></script>',
          },
          publicPath: '/static/',
        }),
      ],
    };
    const bundle = await 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 = rollupPluginHTML({
      rootDir,
      input: {
        html: '<h1>Hello world</h1><script type="module" src="./entrypoint-a.js"></script>',
      },
      publicPath: '/static/',
    });
    const config = {
      input: require.resolve('./fixtures/rollup-plugin-html/entrypoint-a.js'),
      plugins: [plugin],
    };
    const build = await rollup(config);
    const bundleA = build.generate({
      format: 'system',
      dir: 'dist',
      plugins: [plugin.api.addOutput('legacy')],
    });
    const bundleB = build.generate({
      format: 'es',
      dir: 'dist',
      plugins: [plugin.api.addOutput('modern')],
    });
    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/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: [
        rollupPluginHTML({
          rootDir,
          input: {
            html: '<h1>Hello world</h1><script type="module" src="./entrypoint-a.js"></script>',
          },
        }),
        rollupPluginHTML({
          rootDir,
          input: {
            name: 'foo.html',
            html: '<html><body><h1>foo.html</h1></body></html>',
          },
        }),
      ],
    };
    const bundle = await 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, '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></body></html>',
    );
  });

  it('can build with multiple html inputs', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          rootDir,
          input: [
            {
              name: 'page-a.html',
              html: `<h1>Page A</h1><script type="module" src="./entrypoint-a.js"></script>`,
            },
            {
              name: 'page-b.html',
              html: `<h1>Page B</h1><script type="module" src="./entrypoint-b.js"></script>`,
            },
            {
              name: 'page-c.html',
              html: `<h1>Page C</h1><script type="module" src="./entrypoint-c.js"></script>`,
            },
          ],
        }),
      ],
    };
    const bundle = await 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('can use a glob to build multiple pages', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          rootDir,
          input: 'pages/**/*.html',
        }),
      ],
    };

    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);
    const pageA = getAsset(output, 'page-a.html').source;
    const pageB = getAsset(output, 'page-b.html').source;
    const pageC = getAsset(output, 'page-c.html').source;
    expect(output.length).to.equal(7);
    expect(getChunk(output, 'page-a.js')).to.exist;
    expect(getChunk(output, 'page-b.js')).to.exist;
    expect(getChunk(output, 'page-c.js')).to.exist;
    expect(pageA).to.include('<p>page-a.html</p>');
    expect(pageA).to.include('<script type="module" src="./page-a.js"></script>');
    expect(pageA).to.include('<script type="module" src="./shared.js"></script>');
    expect(pageB).to.include('<p>page-b.html</p>');
    expect(pageB).to.include('<script type="module" src="./page-b.js"></script>');
    expect(pageB).to.include('<script type="module" src="./shared.js"></script>');
    expect(pageC).to.include('<p>page-c.html</p>');
    expect(pageC).to.include('<script type="module" src="./page-c.js"></script>');
    expect(pageC).to.include('<script type="module" src="./shared.js"></script>');
  });

  it('can exclude globs', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: 'exclude/**/*.html',
          exclude: '**/partial.html',
          rootDir,
        }),
      ],
    };

    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);
    expect(output.length).to.equal(2);
  });

  it('creates unique inline script names', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          rootDir,
          input: [
            {
              name: 'foo/index.html',
              html: '<h1>Page A</h1><script type="module">console.log("A")</script>',
            },
            {
              name: 'bar/index.html',
              html: '<h1>Page B</h1><script type="module">console.log("B")</script>',
            },
            {
              name: 'x.html',
              html: '<h1>Page C</h1><script type="module">console.log("C")</script>',
            },
          ],
        }),
      ],
    };
    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);
    expect(output.length).to.equal(6);
    expect(getChunk(output, 'inline-module-b8667c926d8a16ee8b4499492c1726ed.js')).to.exist;
    expect(getChunk(output, 'inline-module-c91911481b66e7483731d4de5df616a6.js')).to.exist;
    expect(getChunk(output, 'inline-module-fbf0242ebea027b7392472c19328791d.js')).to.exist;
    expect(getAsset(output, 'foo/index.html').source).to.equal(
      '<html><head></head><body><h1>Page A</h1><script type="module" src="../inline-module-b8667c926d8a16ee8b4499492c1726ed.js"></script></body></html>',
    );
    expect(getAsset(output, 'bar/index.html').source).to.equal(
      '<html><head></head><body><h1>Page B</h1><script type="module" src="../inline-module-c91911481b66e7483731d4de5df616a6.js"></script></body></html>',
    );
    expect(getAsset(output, 'x.html').source).to.equal(
      '<html><head></head><body><h1>Page C</h1><script type="module" src="./inline-module-fbf0242ebea027b7392472c19328791d.js"></script></body></html>',
    );
  });

  it('deduplicates common modules', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          rootDir,
          input: [
            {
              name: 'a.html',
              html: '<h1>Page A</h1><script type="module">console.log("A")</script>',
            },
            {
              name: 'b.html',
              html: '<h1>Page B</h1><script type="module">console.log("A")</script>',
            },
            {
              name: 'c.html',
              html: '<h1>Page C</h1><script type="module">console.log("A")</script>',
            },
          ],
        }),
      ],
    };
    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);
    expect(output.length).to.equal(4);
    expect(getChunk(output, 'inline-module-b8667c926d8a16ee8b4499492c1726ed.js')).to.exist;
    expect(getAsset(output, 'a.html').source).to.equal(
      '<html><head></head><body><h1>Page A</h1><script type="module" src="./inline-module-b8667c926d8a16ee8b4499492c1726ed.js"></script></body></html>',
    );
    expect(getAsset(output, 'b.html').source).to.equal(
      '<html><head></head><body><h1>Page B</h1><script type="module" src="./inline-module-b8667c926d8a16ee8b4499492c1726ed.js"></script></body></html>',
    );
    expect(getAsset(output, 'c.html').source).to.equal(
      '<html><head></head><body><h1>Page C</h1><script type="module" src="./inline-module-b8667c926d8a16ee8b4499492c1726ed.js"></script></body></html>',
    );
  });

  it('outputs the hashed entrypoint name', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          rootDir,
          input: {
            html:
              '<h1>Hello world</h1>' + `<script type="module" src="./entrypoint-a.js"></script>`,
          },
        }),
      ],
    };
    const bundle = await rollup(config);
    const { output } = await bundle.generate({
      ...outputConfig,
      entryFileNames: '[name]-[hash].js',
    });
    expect(output.length).to.equal(2);
    const entrypoint = output.find(f =>
      // @ts-ignore
      f.facadeModuleId.endsWith('entrypoint-a.js'),
    ) as OutputChunk;
    // 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: [
        rollupPluginHTML({
          rootDir,
          input: {
            name: 'pages/index.html',
            html: '<h1>Hello world</h1><script type="module" src="../entrypoint-a.js"></script>',
          },
        }),
      ],
    };
    const bundle = await 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>',
    );
  });

  it('can change HTML root directory', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          rootDir: path.join(__dirname, 'fixtures'),
          input: {
            name: 'rollup-plugin-html/pages/index.html',
            html: '<h1>Hello world</h1><script type="module" src="../entrypoint-a.js"></script>',
          },
        }),
      ],
    };
    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);
    expect(output.length).to.equal(2);
    expect(getAsset(output, 'rollup-plugin-html/pages/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 get the input with getInputs()', async () => {
    // default filename
    const pluginA = rollupPluginHTML({ input: { html: 'Hello world' } });
    // filename inferred from input filename
    const pluginB = rollupPluginHTML({
      input: require.resolve('./fixtures/rollup-plugin-html/my-page.html'),
    });
    // filename explicitly set
    const pluginC = rollupPluginHTML({
      input: {
        name: 'pages/my-other-page.html',
        path: require.resolve('./fixtures/rollup-plugin-html/index.html'),
      },
    });
    await rollup({
      input: require.resolve('./fixtures/rollup-plugin-html/entrypoint-a.js'),
      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('pages/my-other-page.html');
  });

  it('supports other plugins injecting a transform function', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          rootDir,
          input: require.resolve('./fixtures/rollup-plugin-html/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('</body>', '<!-- injected --></body>'),
            );
          },
        } as Plugin,
      ],
    };
    const bundle = await 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(stripNewlines(getAsset(output, 'index.html').source)).to.equal(
      '<html><head></head><body><h1>hello world</h1>' +
        '<script type="module" src="./entrypoint-a.js"></script>' +
        '<script type="module" src="./entrypoint-b.js"></script>' +
        '<!-- injected --></body></html>',
    );
  });

  it('includes referenced assets in the bundle', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: {
            html: `<html>
<head>
<link rel="apple-touch-icon" sizes="180x180" href="./image-a.png" />
<link rel="icon" type="image/png" sizes="32x32" href="./image-b.png" />
<link rel="manifest" href="./webmanifest.json" />
<link rel="mask-icon" href="./image-a.svg" color="#3f93ce" />
<link rel="stylesheet" href="./styles.css" />
<link rel="stylesheet" href="./foo/x.css" />
<link rel="stylesheet" href="./foo/bar/y.css" />
</head>
<body>
<img src="./image-c.png" />
<div>
<img src="./image-b.svg" />
</div>
</body>
</html>`,
          },
          rootDir: path.join(__dirname, 'fixtures', 'assets'),
        }),
      ],
    };

    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);
    expect(output.length).to.equal(11);
    const expectedAssets = [
      'image-c.png',
      'webmanifest.json',
      'image-a.svg',
      'styles.css',
      'x.css',
      'y.css',
      'image-b.svg',
    ];

    for (const name of expectedAssets) {
      const asset = getAsset(output, name);
      expect(asset).to.exist;
      expect(asset.source).to.exist;
    }

    const outputHtml = getAsset(output, 'index.html').source;
    expect(outputHtml).to.include(
      '<link rel="apple-touch-icon" sizes="180x180" href="assets/image-a.png">',
    );
    expect(outputHtml).to.include(
      '<link rel="icon" type="image/png" sizes="32x32" href="assets/image-b.png">',
    );
    expect(outputHtml).to.include('<link rel="manifest" href="assets/webmanifest.json">');
    expect(outputHtml).to.include(
      '<link rel="mask-icon" href="assets/image-a.svg" color="#3f93ce">',
    );
    expect(outputHtml).to.include('<link rel="stylesheet" href="assets/styles-ed723e17.css">');
    expect(outputHtml).to.include('<link rel="stylesheet" href="assets/x-58ef5070.css">');
    expect(outputHtml).to.include('<link rel="stylesheet" href="assets/y-4f2d398e.css">');
    expect(outputHtml).to.include('<img src="assets/image-c-23edadf6.png">');
    expect(outputHtml).to.include('<img src="assets/image-b-ee32b49e.svg">');
  });

  it('deduplicates static assets with similar names', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: {
            html: `<html>
<head>
<link rel="icon" type="image/png" sizes="32x32" href="./foo.svg" />
<link rel="mask-icon" href="./x/foo.svg" color="#3f93ce" />
</head>
</html>`,
          },
          rootDir: path.join(__dirname, 'fixtures', 'assets'),
        }),
      ],
    };

    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);

    expect(stripNewlines(getAsset(output, 'index.html').source)).to.equal(
      '<html><head>' +
        '<link rel="icon" type="image/png" sizes="32x32" href="assets/foo.svg">' +
        '<link rel="mask-icon" href="assets/foo1.svg" color="#3f93ce">' +
        '</head><body></body></html>',
    );
  });

  it('static and hashed asset nodes can reference the same files', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: {
            html: `<html>
<head>
<link rel="icon" type="image/png" sizes="32x32" href="./foo.svg">
<img src="./foo.svg">
</head>
</html>`,
          },
          rootDir: path.join(__dirname, 'fixtures', 'assets'),
        }),
      ],
    };

    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);

    expect(stripNewlines(getAsset(output, 'index.html').source)).to.equal(
      '<html><head><link rel="icon" type="image/png" sizes="32x32" href="assets/foo.svg"></head>' +
        '<body><img src="assets/foo-81034cb4.svg"></body></html>',
    );
  });

  it('deduplicates common assets', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: {
            html: `<html>
<body>
<link rel="stylesheet" href="./image-a.png">
<img src="./image-a.png">
<img src="./image-a.png">
</body>
</html>`,
          },
          rootDir: path.join(__dirname, 'fixtures', 'assets'),
        }),
      ],
    };

    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);

    expect(stripNewlines(getAsset(output, 'index.html').source)).to.equal(
      '<html><head></head><body>' +
        '<link rel="stylesheet" href="assets/image-a-9c3a45f9.png">' +
        '<img src="assets/image-a-9c3a45f9.png">' +
        '<img src="assets/image-a-9c3a45f9.png">' +
        '</body></html>',
    );
  });

  it('deduplicates common assets across HTML files', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: [
            {
              name: 'page-a.html',
              html: `<html>
  <body>
  <img src="./image-a.png">
  </body>
  </html>`,
            },
            {
              name: 'page-b.html',
              html: `<html>
  <body>
  <link rel="stylesheet" href="./image-a.png">
  </body>
  </html>`,
            },
            {
              name: 'page-c.html',
              html: `<html>
  <body>
  <link rel="stylesheet" href="./image-a.png">
  <img src="./image-a.png">
  </body>
  </html>`,
            },
          ],
          rootDir: path.join(__dirname, 'fixtures', 'assets'),
        }),
      ],
    };

    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);

    expect(stripNewlines(getAsset(output, 'page-a.html').source)).to.equal(
      '<html><head></head><body>' +
        '  <img src="assets/image-a-9c3a45f9.png">' +
        '    </body></html>',
    );

    expect(stripNewlines(getAsset(output, 'page-b.html').source)).to.equal(
      '<html><head></head><body>' +
        '  <link rel="stylesheet" href="assets/image-a-9c3a45f9.png">' +
        '    </body></html>',
    );

    expect(stripNewlines(getAsset(output, 'page-c.html').source)).to.equal(
      '<html><head></head><body>' +
        '  <link rel="stylesheet" href="assets/image-a-9c3a45f9.png">' +
        '  <img src="assets/image-a-9c3a45f9.png">' +
        '    </body></html>',
    );
  });

  it('can turn off extracting assets', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          extractAssets: false,
          input: {
            html: `<html>
<body>
<img src="./image-c.png" />
<link rel="stylesheet" href="./styles.css" />
<img src="./image-b.svg" />
</body>
</html>`,
          },
          rootDir: path.join(__dirname, 'fixtures', 'assets'),
        }),
      ],
    };

    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);

    expect(output.length).to.equal(2);
    expect(stripNewlines(getAsset(output, 'index.html').source)).to.equal(
      '<html><head></head><body><img src="./image-c.png"><link rel="stylesheet" href="./styles.css"><img src="./image-b.svg"></body></html>',
    );
  });

  it('can inject a CSP meta tag for inline scripts', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: require.resolve('./fixtures/rollup-plugin-html/csp-page-a.html'),
          rootDir,
          strictCSPInlineScripts: true,
        }),
      ],
    };
    const bundle = await 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(stripNewlines(getAsset(output, 'csp-page-a.html').source)).to.equal(
      '<html><head>' +
        "<meta http-equiv=\"Content-Security-Policy\" content=\"script-src 'self' 'sha256-k0fj3IHUtZNziFbz6LL40uxkFlr28beNcMKKtp5+EwE=' 'sha256-UJadfRwzUCb1ajAJFfAPl8NTvtyiHtltKG/12veER70=';\">" +
        '</head><body><h1>hello world</h1>' +
        "<script>console.log('foo');</script>" +
        "<script>console.log('bar');</script>" +
        '<script type="module" src="./entrypoint-a.js"></script>' +
        '<script type="module" src="./entrypoint-b.js"></script>' +
        '</body></html>',
    );
  });

  it('can add to an existing CSP meta tag for inline scripts', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: require.resolve('./fixtures/rollup-plugin-html/csp-page-b.html'),
          rootDir,
          strictCSPInlineScripts: true,
        }),
      ],
    };
    const bundle = await 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(stripNewlines(getAsset(output, 'csp-page-b.html').source)).to.equal(
      '<html><head>' +
        "<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'self'; prefetch-src 'self'; upgrade-insecure-requests; style-src 'self' 'unsafe-inline'; script-src 'self' 'sha256-k0fj3IHUtZNziFbz6LL40uxkFlr28beNcMKKtp5+EwE=' 'sha256-UJadfRwzUCb1ajAJFfAPl8NTvtyiHtltKG/12veER70=';\">" +
        '</head><body><h1>hello world</h1>' +
        "<script>console.log('foo');</script>" +
        "<script>console.log('bar');</script>" +
        '<script type="module" src="./entrypoint-a.js"></script>' +
        '<script type="module" src="./entrypoint-b.js"></script>' +
        '</body></html>',
    );
  });

  it('can add to an existing CSP meta tag for inline scripts even if script-src is already there', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: require.resolve('./fixtures/rollup-plugin-html/csp-page-c.html'),
          rootDir,
          strictCSPInlineScripts: true,
        }),
      ],
    };
    const bundle = await 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(stripNewlines(getAsset(output, 'csp-page-c.html').source)).to.equal(
      '<html><head>' +
        "<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'self'; prefetch-src 'self'; upgrade-insecure-requests; style-src 'self' 'unsafe-inline'; script-src 'self' 'sha256-k0fj3IHUtZNziFbz6LL40uxkFlr28beNcMKKtp5+EwE=' 'sha256-UJadfRwzUCb1ajAJFfAPl8NTvtyiHtltKG/12veER70=';\">" +
        '</head><body><h1>hello world</h1>' +
        "<script>console.log('foo');</script>" +
        "<script>console.log('bar');</script>" +
        '<script type="module" src="./entrypoint-a.js"></script>' +
        '<script type="module" src="./entrypoint-b.js"></script>' +
        '</body></html>',
    );
  });

  it('can inject a service worker registration script if injectServiceWorker and serviceWorkerPath are provided', async () => {
    const serviceWorkerPath = path.join(
      // @ts-ignore
      path.resolve(outputConfig.dir),
      'service-worker.js',
    );

    const config = {
      plugins: [
        rollupPluginHTML({
          input: '**/*.html',
          rootDir: path.join(__dirname, 'fixtures', 'inject-service-worker'),
          flattenOutput: false,
          injectServiceWorker: true,
          serviceWorkerPath,
        }),
      ],
    };
    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);

    function extractServiceWorkerPath(src: string) {
      const registerOpen = src.indexOf(".register('");
      const registerClose = src.indexOf("')", registerOpen + 11);
      return src.substring(registerOpen + 11, registerClose);
    }

    expect(extractServiceWorkerPath(getAsset(output, 'index.html').source)).to.equal(
      'service-worker.js',
    );
    expect(
      extractServiceWorkerPath(getAsset(output, path.join('sub-with-js', 'index.html')).source),
    ).to.equal(`../service-worker.js`);
    expect(
      extractServiceWorkerPath(getAsset(output, path.join('sub-pure-html', 'index.html')).source),
    ).to.equal(`../service-worker.js`);
  });

  it('does support a absolutePathPrefix to allow for sub folder deployments', async () => {
    const config = {
      plugins: [
        rollupPluginHTML({
          input: {
            html: `<html>
<body>
<img src="/my-prefix/x/foo.svg" />
<link rel="stylesheet" href="../styles.css" />
<img src="../image-b.svg" />
</body>
</html>`,
            name: 'x/index.html',
          },
          rootDir: path.join(__dirname, 'fixtures', 'assets'),
          absolutePathPrefix: '/my-prefix/',
        }),
      ],
    };

    const bundle = await rollup(config);
    const { output } = await bundle.generate(outputConfig);

    expect(stripNewlines(getAsset(output, 'x/index.html').source)).to.equal(
      [
        '<html><head></head><body>',
        '<img src="../assets/foo-c9db7cc0.svg">',
        '<link rel="stylesheet" href="../assets/styles-ed723e17.css">',
        '<img src="../assets/image-b-ee32b49e.svg">',
        '</body></html>',
      ].join(''),
    );
  });
});
Example #22
Source File: extractMachines.ts    From xstate-codegen with MIT License 4 votes vote down vote up
extractMachines = async (
  filePath: string,
): Promise<ExtractedMachine[]> => {
  const resolvedFilePath = path.resolve(cwd, filePath);

  const build = await rollup({
    input: resolvedFilePath,
    external: (id) => !/^(\.|\/|\w:)/.test(id),
    plugins: [
      nodeResolvePlugin({
        extensions,
      }),
      babelPlugin({
        babelHelpers: 'bundled',
        extensions,
        plugins: [
          '@babel/plugin-transform-typescript',
          '@babel/plugin-proposal-optional-chaining',
          '@babel/plugin-proposal-nullish-coalescing-operator',
          (babel: typeof babelCore) => {
            return {
              name: 'xstate-codegen-machines-extractor',
              visitor: {
                ImportDeclaration(
                  path: babelCore.NodePath<babelCore.types.ImportDeclaration>,
                  state: babelCore.PluginPass,
                ) {
                  if (
                    state.filename !== resolvedFilePath ||
                    path.node.source.value !== '@xstate/compiled'
                  ) {
                    return;
                  }

                  const imports = getImports(babel, path);
                  const referencePathsByImportName = getReferencePathsByImportName(
                    path.scope,
                    imports,
                  );

                  if (!referencePathsByImportName) {
                    return;
                  }

                  /**
                   * Other plugins that run before babel-plugin-macros might use path.replace, where a path is
                   * put into its own replacement. Apparently babel does not update the scope after such
                   * an operation. As a remedy, the whole scope is traversed again with an empty "Identifier"
                   * visitor - this makes the problem go away.
                   *
                   * See: https://github.com/kentcdodds/import-all.macro/issues/7
                   */
                  state.file.scope.path.traverse({
                    Identifier() {},
                  });

                  macro({
                    path,
                    references: referencePathsByImportName,
                    state,
                    babel,
                    // hack to make this call accepted by babel-plugin-macros
                    isBabelMacrosCall: true,
                  });
                },
              },
            };
          },
        ],
      }),
    ],
  });
  const output = await build.generate({
    format: 'cjs',
    exports: 'named',
  });
  const chunk = output.output[0];
  const { code } = chunk;

  // dance with those unique ids is not really needed, at least right now
  // loading CJS modules is synchronous
  // once we start to support loading ESM this won't hold true anymore
  let uniqueId = generateUniqueId(compiledOutputs);

  try {
    compiledOutputs[uniqueId] = code;
    const fakeFileName = path.join(
      path.dirname(resolvedFilePath),
      `${path
        .basename(resolvedFilePath)
        .replace(/\./g, '-')}-${uniqueId}.xstate.js`,
    );
    const module = new Module(fakeFileName);
    (module as any).load(fakeFileName);

    return [
      ...getCreatedExports('createMachine', module.exports),
      ...getCreatedExports('Machine', module.exports),
    ];
  } finally {
    delete compiledOutputs[uniqueId];
  }
}
Example #23
Source File: buildCjs.ts    From gitmars with GNU General Public License v3.0 4 votes vote down vote up
export async function buildCjs() {
    const externals = [
        'js-cool',
        '@gitmars/core',
        '@gitmars/docs',
        '@gitmars/server',
        '@gitmars/ui'
    ]
    const builds = pkgs.map(
        async ({
            globals = {},
            name,
            external = [],
            submodules,
            iife,
            build,
            cjs,
            mjs,
            dts,
            target,
            exportType = 'auto'
        }) => {
            // if (build === false) return
            const pkg = require(resolve(PACKAGE, name, 'package.json'))
            const banner =
                '/*!\n' +
                ' * ' +
                pkg.name +
                ' v' +
                pkg.version +
                '\n' +
                ' * ' +
                pkg.description +
                '\n' +
                ' * (c) 2021-' +
                new Date().getFullYear() +
                ' saqqdy<https://github.com/saqqdy> \n' +
                ' * Released under the MIT License.\n' +
                ' */'
            // const deps = Object.keys(pkg.dependencies || {})
            const iifeGlobals = {
                'js-cool': 'JsCool',
                '@gitmars/utils': 'EslintSets',
                '@gitmars/core': 'EslintSets',
                ...globals
            }
            const iifeName = 'Gitmars'
            const fileList = excludeFiles(
                glob.sync('**/*.ts', {
                    cwd: resolve(PACKAGE, name, 'src'),
                    ignore: ['node_modules'],
                    // absolute: true,
                    onlyFiles: true
                })
            )

            // submodules
            // if (submodules) {
            //     functionNames.push(
            //         ...glob
            //             .sync('*/index.ts', {
            //                 cwd: resolve(`packages/${name}`)
            //             })
            //             .map(i => i.split('/')[0])
            //     )
            // }
            for (const fn of fileList) {
                const input = resolve(PACKAGE, name, 'src', fn)

                const writeOptions: OutputOptions[] = []
                // output mjs
                if (mjs !== false) {
                    writeOptions.push({
                        file: resolve(
                            PACKAGE,
                            name,
                            'es',
                            fn.replace(/\.ts$/, '.mjs')
                        ),
                        exports: exportType,
                        banner,
                        format: 'es'
                    })
                }
                // output cjs
                if (cjs !== false) {
                    writeOptions.push({
                        file: resolve(
                            PACKAGE,
                            name,
                            'lib',
                            fn.replace(/\.ts$/, '.js')
                        ),
                        exports: exportType,
                        banner,
                        format: 'cjs'
                    })
                }
                // output iife
                if (iife !== false) {
                    writeOptions.push(
                        {
                            file: resolve(
                                PACKAGE,
                                name,
                                'dist',
                                fn.replace(/\.ts$/, 'iife.js')
                            ),
                            format: 'iife',
                            // exports: 'named',
                            // exports: exportType,
                            name: iifeName,
                            extend: true,
                            globals: iifeGlobals,
                            banner,
                            plugins: [
                                // injectEslintSetsCore,
                            ]
                        },
                        {
                            file: resolve(
                                PACKAGE,
                                name,
                                'dist',
                                fn.replace(/\.ts$/, 'iife.min.js')
                            ),
                            format: 'iife',
                            // exports: 'named',
                            // exports: exportType,
                            name: iifeName,
                            extend: true,
                            globals: iifeGlobals,
                            plugins: [
                                // injectEslintSetsCore,
                                minify({
                                    minify: true
                                }),
                                bannerPlugin(),
                                filesize
                            ]
                        }
                    )
                }

                const rollupConfig = {
                    input,
                    plugins: [
                        alias({
                            entries: [
                                {
                                    find: /^@\//,
                                    replacement: resolve(PACKAGE, name, 'src')
                                }
                            ],
                            customResolver: nodeResolve() as ResolverObject
                        }),
                        nodeResolve(),
                        json,
                        commonjs,
                        shebang(),
                        esbuild(),
                        // target ? esbuild({ target }) : esbuild(),
                        filesize
                    ],
                    external: generateExternal({ name, input }, [
                        ...externals,
                        ...external
                    ])
                }
                const bundle = await rollup(rollupConfig)
                await Promise.all(
                    writeOptions.map(option => bundle.write(option))
                )

                // dts
                if (dts !== false) {
                    const rollupDtsConfig = {
                        input,
                        plugins: [nodeExternals(), dtsPlugin],
                        external: [...externals, ...external]
                    }
                    const writeEsmDtsOptions: OutputOptions[] = [
                        {
                            file: resolve(
                                PACKAGE,
                                name,
                                'es',
                                fn.replace(/\.ts$/, '.d.ts')
                            ),
                            // exports: 'auto',
                            // exports: exportType,
                            // banner,
                            format: 'es'
                        }
                    ]
                    const writeCjsDtsOptions: OutputOptions[] = [
                        {
                            file: resolve(
                                PACKAGE,
                                name,
                                'lib',
                                fn.replace(/\.ts$/, '.d.ts')
                            ),
                            // exports: 'auto',
                            // exports: exportType,
                            // banner,
                            format: 'es'
                        }
                    ]
                    const dtsBundle = await rollup(rollupDtsConfig)
                    await Promise.all([
                        writeEsmDtsOptions.map(option =>
                            dtsBundle.write(option)
                        ),
                        writeCjsDtsOptions.map(option =>
                            dtsBundle.write(option)
                        )
                    ])
                }
            }
        }
    )
    await Promise.all(builds)
}
Example #24
Source File: index.ts    From svelte-site-jp with MIT License 4 votes vote down vote up
describe('custom-elements', function() {
	// Note: Increase the timeout in preparation for restarting Chromium due to SIGSEGV.
	this.timeout(10000);
	let svelte;
	let server;
	let browser;
	let code;

	function create_server() {
		return new Promise((fulfil, reject) => {
			const server = http.createServer((req, res) => {
				if (req.url === '/') {
					res.end(page);
				}

				if (req.url === '/bundle.js') {
					res.end(code);
				}
			});

			server.on('error', reject);

			server.listen('6789', () => {
				fulfil(server);
			});
		});
	}

	async function launchPuppeteer() {
		return await retryAsync(() => puppeteer.launch());
	}

	before(async () => {
		svelte = loadSvelte();
		console.log('[custom-element] Loaded Svelte');
		server = await create_server();
		console.log('[custom-element] Started server');
		browser = await launchPuppeteer();
		console.log('[custom-element] Launched puppeteer browser');
	});

	after(async () => {
		if (server) server.close();
		if (browser) await browser.close();
	});

	fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
		if (dir[0] === '.') return;

		const solo = /\.solo$/.test(dir);
		const skip = /\.skip$/.test(dir);
		const internal = path.resolve('internal/index.mjs');
		const index = path.resolve('index.mjs');
		const warnings = [];

		(solo ? it.only : skip ? it.skip : it)(dir, async () => {
			const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`);
			const expected_warnings = config.warnings || [];

			const bundle = await rollup({
				input: `${__dirname}/samples/${dir}/test.js`,
				plugins: [
					{
						resolveId(importee) {
							if (importee === 'svelte/internal' || importee === './internal') {
								return internal;
							}

							if (importee === 'svelte') {
								return index;
							}
						},

						transform(code, id) {
							if (id.endsWith('.svelte')) {
								const compiled = svelte.compile(code.replace(/\r/g, ''), {
									customElement: true,
									dev: config.dev
								});

								compiled.warnings.forEach(w => warnings.push(w));

								return compiled.js;
							}
						}
					},

					virtual({
						assert
					})
				]
			});

			const result = await bundle.generate({ format: 'iife', name: 'test' });
			code = result.output[0].code;

			function assertWarnings() {
				if (expected_warnings) {
					deepEqual(warnings.map(w => ({
						code: w.code,
						message: w.message,
						pos: w.pos,
						start: w.start,
						end: w.end
					})), expected_warnings);
				}
			}

			browser = await executeBrowserTest(
				browser,
				launchPuppeteer,
				assertWarnings,
				() => {
					console.log(addLineNumbers(code));
					assertWarnings();
				});
		});
	});
});
Example #25
Source File: index.ts    From svelte-site-jp with MIT License 4 votes vote down vote up
describe('runtime', () => {
	before(() => {
		process.on('unhandledRejection', unhandledRejection_handler);
		svelte = loadSvelte(false);
		svelte$ = loadSvelte(true);

		require.extensions['.svelte'] = function(module, filename) {
			const options = Object.assign({
				filename
			}, compileOptions);

			const { js: { code } } = compile(fs.readFileSync(filename, 'utf-8').replace(/\r/g, ''), options);

			return module._compile(code, filename);
		};

		return setupHtmlEqual();
	});
	after(() => process.removeListener('unhandledRejection', unhandledRejection_handler));

	const failed = new Set();

	function runTest(dir, hydrate, from_ssr_html) {
		if (dir[0] === '.') return;

		const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`);
		const solo = config.solo || /\.solo/.test(dir);

		if (hydrate && config.skip_if_hydrate) return;
		if (hydrate && from_ssr_html && config.skip_if_hydrate_from_ssr) return;

		if (solo && process.env.CI) {
			throw new Error('Forgot to remove `solo: true` from test');
		}

		const testName = `${dir} ${hydrate ? `(with hydration${from_ssr_html ? ' from ssr rendered html' : ''})` : ''}`;
		(config.skip ? it.skip : solo ? it.only : it)(testName, (done) => {
			if (failed.has(dir)) {
				// this makes debugging easier, by only printing compiled output once
				throw new Error('skipping test, already failed');
			}

			unhandled_rejection = null;

			compile = (config.preserveIdentifiers ? svelte : svelte$).compile;

			const cwd = path.resolve(`${__dirname}/samples/${dir}`);

			compileOptions = config.compileOptions || {};
			compileOptions.format = 'cjs';
			compileOptions.sveltePath = sveltePath;
			compileOptions.hydratable = hydrate;
			compileOptions.immutable = config.immutable;
			compileOptions.accessors = 'accessors' in config ? config.accessors : true;

			cleanRequireCache();

			let mod;
			let SvelteComponent;

			let unintendedError = null;

			const window = env();

			glob('**/*.svelte', { cwd }).forEach(file => {
				if (file[0] === '_') return;

				const dir = `${cwd}/_output/${hydrate ? 'hydratable' : 'normal'}`;
				const out = `${dir}/${file.replace(/\.svelte$/, '.js')}`;

				if (fs.existsSync(out)) {
					fs.unlinkSync(out);
				}

				mkdirp(dir);

				try {
					const { js } = compile(
						fs.readFileSync(`${cwd}/${file}`, 'utf-8').replace(/\r/g, ''),
						{
							...compileOptions,
							filename: file
						}
					);

					fs.writeFileSync(out, js.code);
				} catch (err) {
					// do nothing
				}
			});

			Promise.resolve()
				.then(() => {
					// hack to support transition tests
					clear_loops();

					const raf = {
						time: 0,
						callback: null,
						tick: now => {
							raf.time = now;
							if (raf.callback) raf.callback();
						}
					};
					set_now(() => raf.time);
					set_raf(cb => {
						raf.callback = () => {
							raf.callback = null;
							cb(raf.time);
							flush();
						};
					});

					try {
						mod = require(`./samples/${dir}/main.svelte`);
						SvelteComponent = mod.default;
					} catch (err) {
						showOutput(cwd, compileOptions, compile); // eslint-disable-line no-console
						throw err;
					}

					// Put things we need on window for testing
					window.SvelteComponent = SvelteComponent;

					const target = window.document.querySelector('main');

					if (hydrate && from_ssr_html) {
						// ssr into target
						compileOptions.generate = 'ssr';
						cleanRequireCache();
						const SsrSvelteComponent = require(`./samples/${dir}/main.svelte`).default;
						const { html } = SsrSvelteComponent.render(config.props);
						target.innerHTML = html;
						delete compileOptions.generate;
					} else {
						target.innerHTML = '';
					}

					if (config.before_test) config.before_test();

					const warnings = [];
					const warn = console.warn;
					console.warn = warning => {
						warnings.push(warning);
					};

					const options = Object.assign({}, {
						target,
						hydrate,
						props: config.props,
						intro: config.intro
					}, config.options || {});

					const component = new SvelteComponent(options);

					console.warn = warn;

					if (config.error) {
						unintendedError = true;
						throw new Error('Expected a runtime error');
					}

					if (config.warnings) {
						assert.deepEqual(warnings, config.warnings);
					} else if (warnings.length) {
						unintendedError = true;
						throw new Error('Received unexpected warnings');
					}

					if (config.html) {
						assert.htmlEqual(target.innerHTML, config.html);
					}

					if (config.test) {
						return Promise.resolve(config.test({
							assert,
							component,
							mod,
							target,
							window,
							raf,
							compileOptions
						})).then(() => {
							component.$destroy();

							if (unhandled_rejection) {
								throw unhandled_rejection;
							}
						});
					} else {
						component.$destroy();
						assert.htmlEqual(target.innerHTML, '');

						if (unhandled_rejection) {
							throw unhandled_rejection;
						}
					}
				})
				.catch(err => {
					if (config.error && !unintendedError) {
						if (typeof config.error === 'function') {
							config.error(assert, err);
						} else {
							assert.equal(err.message, config.error);
						}
					} else {
						throw err;
					}
				}).catch(err => {
					failed.add(dir);
					showOutput(cwd, compileOptions, compile); // eslint-disable-line no-console
					throw err;
				})
				.catch(err => {
					// print a clickable link to open the directory
					err.stack += `\n\ncmd-click: ${path.relative(process.cwd(), cwd)}/main.svelte`;
					done(err);
					throw err;
				})
				.then(() => {
					if (config.show) {
						showOutput(cwd, compileOptions, compile);
					}

					flush();

					if (config.after_test) config.after_test();
					done();
				});
		});
	}

	fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
		runTest(dir, false);
		runTest(dir, true, false);
		runTest(dir, true, true);
	});

	async function create_component(src = '<div></div>') {
		const { js } = svelte$.compile(src, {
			format: 'esm',
			name: 'SvelteComponent',
			dev: true
		});

		const bundle = await rollup({
			input: 'main.js',
			plugins: [
				virtual({
					'main.js': js.code
				}),
				{
					name: 'svelte-packages',
					resolveId: (importee) => {
						if (importee.startsWith('svelte/')) {
							return importee.replace('svelte', process.cwd()) + '/index.mjs';
						}
					}
				}
			]
		});

		const result = await bundle.generate({
			format: 'iife',
			name: 'App'
		});

		return eval(
			`(function () { ${result.output[0].code}; return App; }())`
		);
	}

	it('fails if options.target is missing in dev mode', async () => {
		const App = await create_component();

		assert.throws(() => {
			new App();
		}, /'target' is a required option/);
	});

	it('fails if options.hydrate is true but the component is non-hydratable', async () => {
		const App = await create_component();

		assert.throws(() => {
			new App({
				target: { childNodes: [] },
				hydrate: true
			});
		}, /options.hydrate only works if the component was compiled with the `hydratable: true` option/);
	});
});
Example #26
Source File: index.ts    From svelte-site-jp with MIT License 4 votes vote down vote up
describe('runtime (puppeteer)', function() {
	// Note: Increase the timeout in preparation for restarting Chromium due to SIGSEGV.
	this.timeout(10000);
	before(async () => {
		svelte = loadSvelte(false);
		console.log('[runtime-puppeteer] Loaded Svelte');
		server = await create_server();
		console.log('[runtime-puppeteer] Started server');
		browser = await launchPuppeteer();
		console.log('[runtime-puppeteer] Launched puppeteer browser');
	});

	after(async () => {
		if (server) server.close();
		if (browser) await browser.close();
	});

	const failed = new Set();

	function runTest(dir, hydrate) {
		if (dir[0] === '.') return;

		const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`);
		const solo = config.solo || /\.solo/.test(dir);
		const skip = config.skip || /\.skip/.test(dir);

		if (hydrate && config.skip_if_hydrate) return;

		if (solo && process.env.CI) {
			throw new Error('Forgot to remove `solo: true` from test');
		}

		(skip ? it.skip : solo ? it.only : it)(`${dir} ${hydrate ? '(with hydration)' : ''}`, async () => {
			if (failed.has(dir)) {
				// this makes debugging easier, by only printing compiled output once
				throw new Error('skipping test, already failed');
			}

			const warnings = [];

			const bundle = await rollup({
				input: 'main',
				plugins: [
					{
						name: 'testing-runtime-puppeteer',
						resolveId(importee) {
							if (importee === 'svelte/internal' || importee === './internal') {
								return internal;
							}

							if (importee === 'svelte') {
								return index;
							}

							if (importee === 'main') {
								return 'main';
							}
						},
						load(id) {
							if (id === 'main') {
								return `
									import SvelteComponent from ${JSON.stringify(path.join(__dirname, 'samples', dir, 'main.svelte'))};
									import config from ${JSON.stringify(path.join(__dirname, 'samples', dir, '_config.js'))};
									import * as assert from 'assert';

									export default async function (target) {
										let unhandled_rejection = false;
										function unhandled_rejection_handler(event) {
											unhandled_rejection = event.reason;
										}
										window.addEventListener('unhandledrejection', unhandled_rejection_handler);

										try {
											if (config.before_test) config.before_test();

											const options = Object.assign({}, {
												target,
												hydrate: ${String(!!hydrate)},
												props: config.props,
												intro: config.intro
											}, config.options || {});

											const component = new SvelteComponent(options);

											if (config.html) {
												assert.htmlEqual(target.innerHTML, config.html);
											}

											if (config.test) {
												await config.test({
													assert,
													component,
													target,
													window,
												});

												component.$destroy();

												if (unhandled_rejection) {
													throw unhandled_rejection;
												}
											} else {
												component.$destroy();
												assert.htmlEqual(target.innerHTML, '');

												if (unhandled_rejection) {
													throw unhandled_rejection;
												}
											}

											if (config.after_test) config.after_test();
										} catch (error) {
											if (config.error) {
												assert.equal(err.message, config.error);
											} else {
												throw error;
											}
										} finally {
											window.removeEventListener('unhandledrejection', unhandled_rejection_handler);
										}
									}
								`;
							}
							return null;
						},
						transform(code, id) {
							if (id.endsWith('.svelte')) {
								const compiled = svelte.compile(code.replace(/\r/g, ''), {
									...config.compileOptions,
									hydratable: hydrate,
									immutable: config.immutable,
									accessors: 'accessors' in config ? config.accessors : true
								});

								const out_dir = `${__dirname}/samples/${dir}/_output/${hydrate ? 'hydratable' : 'normal'}`;
								const out = `${out_dir}/${path.basename(id).replace(/\.svelte$/, '.js')}`;

								if (fs.existsSync(out)) {
									fs.unlinkSync(out);
								}

								mkdirp(out_dir);
								fs.writeFileSync(out, compiled.js.code, 'utf8');

								compiled.warnings.forEach(w => warnings.push(w));

								return compiled.js;
							}
						}
					},
					virtual({ assert })
				]
			});

			const result = await bundle.generate({ format: 'iife', name: 'test' });
			code = result.output[0].code;

			function assertWarnings() {
				if (config.warnings) {
					deepEqual(warnings.map(w => ({
						code: w.code,
						message: w.message,
						pos: w.pos,
						start: w.start,
						end: w.end
					})), config.warnings);
				} else if (warnings.length) {
					failed.add(dir);
					/* eslint-disable no-unsafe-finally */
					throw new Error('Received unexpected warnings');
				}
			}

			browser = await executeBrowserTest(
				browser,
				launchPuppeteer,
				assertWarnings,
				(err) => {
					failed.add(dir);
					prettyPrintPuppeteerAssertionError(err.message);
					assertWarnings();
				});
		});
	}

	fs.readdirSync(`${__dirname}/samples`).forEach(dir => {
		runTest(dir, false);
		runTest(dir, true);
	});
});