async render(data, pkg) {
    /** @type {Object<string, string>} */
    const assets = {};
    const readOptions = {encoding: 'utf8'};

    // add in script tag
    const jsName = `${data.name}.js`;
    const jsPath = path.join(data.dir, jsName);
    let jsSource = await fse.readFile(jsPath, {encoding: 'utf8'});
    jsSource = this.removeTypeComments(
      jsPath,
      this.transformJsSource(this.cloakSource(jsSource, data.cloak)),
    );
    data.js = {
      local: [],
      remote: [],
      source: jsSource,
    };

    let jsSources = jsSource;
    if (data.sources) {
      data.extraSources = await Promise.all(
        data.sources.map(async (sourceConfig) => {
          const fileName = sourceConfig.path;
          const extraSourcePath = path.join(data.dir, fileName);
          let source = await fse.readFile(extraSourcePath, readOptions);
          let ext = fileName.match(/\.(\w+)$/)[1];
          if (ext === 'mjs') {
            ext = 'js';
          }
          source = this.cloakSource(source, data.cloak);
          if (ext === 'js') {
            source = this.removeTypeComments(
              fileName,
              this.transformJsSource(source),
            );
            jsSources += '\n' + source;
          }
          assets[fileName] = source;
          return {
            name: sourceConfig.as || fileName,
            source: source,
            type: ext,
          };
        }),
      );
    }

    data.olVersion = pkg.version;
    data.pkgJson = JSON.stringify(
      {
        name: data.name,
        dependencies: getDependencies(jsSources, pkg),
        devDependencies: {
          vite: '^3.2.3',
        },
        scripts: {
          start: 'vite',
          build: 'vite build',
        },
      },
      null,
      2,
    );

    data.css = {
      local: [],
      remote: [],
      source: undefined,
    };

    // add additional resources
    if (data.resources) {
      data.resources.forEach((resource) => {
        const absoluteUrl = /^https?:\/\//.test(resource)
          ? resource
          : `https://openlayers.org/en/v${pkg.version}/examples/${resource}`;
        if (isJsRegEx.test(resource)) {
          if (!isTemplateJs.test(resource)) {
            data.js.local.push(resource);
          }
          data.js.remote.push(absoluteUrl);
        } else if (isCssRegEx.test(resource)) {
          if (!isTemplateCss.test(resource)) {
            data.css.local.push(resource);
          }
          data.css.remote.push(absoluteUrl);
        } else {
          throw new Error(
            `Invalid resource: '${resource}' is not .js or .css: ${data.filename}`,
          );
        }
      });
    }

    data.js.local.push(`${this.common}.js`, jsName);

    // check for example css
    const cssName = `${data.name}.css`;
    const cssPath = path.join(data.dir, cssName);
    try {
      assets[cssName] = await fse.readFile(cssPath, readOptions);
      data.css.local.push(cssName);
      data.css.source = this.ensureNewLineAtEnd(assets[cssName]);
    } catch {
      // pass, no css for this example
    }

    const templatePath = path.join(this.templates, data.layout);
    const templateSource = await fse.readFile(templatePath, readOptions);

    assets[data.filename] = handlebars.compile(templateSource)(data);
    return assets;
  }
@param {TemplateConfig} data Config for this example
@param {PackageJson} pkg Data from package.json
@return {Object<string, string>} Asset for this example