All files / src/url index.js

100% Statements 39/39
100% Branches 24/24
100% Functions 5/5
100% Lines 38/38

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 1141x 1x 1x   1x 1x               1x               37x   29x 29x   29x   29x     41x 2x   39x 39x 39x     39x 20x     20x 20x       20x           20x                   20x 20x   19x 19x 19x 19x 19x 19x   39x 39x       4x   3x 4x 4x 4x                       5x                                  
import path from "path";
import mime from "mime-types";
import { createFilter } from "@rollup/pluginutils";
 
const { posix, sep } = path;
const defaultInclude = [
  "**/*.svg",
  "**/*.png",
  "**/*.jp(e)?g",
  "**/*.gif",
  "**/*.webp",
];
 
export default function url(options = {}) {
  const {
    limit = 14 * 1024,
    include = defaultInclude,
    exclude,
    publicPath = "",
    emitFiles = true,
    fileName = "[hash][extname]",
    updateExport = (data) => data,
    // @typedef {import('../fs-iface').IsomorphicRollupFs} IsomorphicRollupFs
  } = options;
  const filter = createFilter(include, exclude);
 
  const copies = Object.create(null);
 
  return {
    name: "url",
    async load(id) {
      if (!filter(id)) {
        return null;
      }
      this.addWatchFile(id);
      const stats = await this.fs.stat(id);
      const content = await this.fs.readFile(id);
 
      let data;
      if ((limit && stats.size > limit) || limit === 0) {
        const hash = Buffer.from(await crypto.subtle.digest("SHA-1", content))
          .toString("hex")
          .substring(0, 16);
        const ext = path.extname(id);
        const name = path.basename(id, ext);
        // Determine the directory name of the file based
        // on either the relative path provided in options,
        // or the parent directory
        const relativeDir = options.sourceDir
          ? path.relative(options.sourceDir, path.dirname(id))
          : path.dirname(id).split(sep).pop();
 
        // Generate the output file name based on some string
        // replacement parameters
        const outputFileName = fileName
          .replace(/\[hash\]/g, hash)
          .replace(/\[extname\]/g, ext)
          // use `sep` for windows environments
          .replace(
            /\[dirname\]/g,
            relativeDir === "" ? "" : `${relativeDir}${sep}`,
          )
          .replace(/\[name\]/g, name);
        // Windows fix - exports must be in unix format
        data = `${publicPath}${outputFileName.split(sep).join(posix.sep)}`;
        copies[id] = outputFileName;
      } else {
        const mimetype = mime.lookup(id);
        const isSVG = mimetype === "image/svg+xml";
        const buffer = Buffer.from(content);
        data = isSVG ? encodeSVG(buffer) : buffer.toString("base64");
        const encoding = isSVG ? "" : ";base64";
        data = `data:${mimetype}${encoding},${data}`;
      }
      data = updateExport(data);
      return `export default "${data}"`;
    },
    generateBundle: async function write(outputOptions) {
      // Allow skipping saving files for server side builds.
      if (!emitFiles) return;
 
      for (const name of Object.keys(copies)) {
        const fileName = copies[name];
        const content = await this.fs.readFile(name);
        this.emitFile({
          type: "asset",
          fileName,
          source: content,
        });
      }
    },
  };
}
 
// https://github.com/filamentgroup/directory-encoder/blob/master/lib/svg-uri-encoder.js
function encodeSVG(buffer) {
  return (
    encodeURIComponent(
      buffer
        .toString("utf-8")
        // strip newlines and tabs
        .replace(/[\n\r]/gim, "")
        .replace(/\t/gim, " ")
        // strip comments
        .replace(/<!--(.*(?=-->))-->/gim, "")
        // replace
        .replace(/'/gim, "\\i"),
    )
      // encode brackets
      .replace(/\(/g, "%28")
      .replace(/\)/g, "%29")
  );
}