UNPKG

3.92 kBJavaScriptView Raw
1import {createFilter} from "rollup-pluginutils"
2import mkdirp from 'mkdirp'
3import mime from "mime"
4import crypto from "crypto"
5import path from "path"
6import fs from "fs"
7
8const defaultInclude = [
9 "**/*.svg",
10 "**/*.png",
11 "**/*.jpg",
12 "**/*.gif",
13]
14
15export default function url(options = {}) {
16 const {
17 limit = 14 * 1024,
18 include = defaultInclude,
19 exclude,
20 publicPath = "",
21 emitFiles = true,
22 fileName = "[hash][extname]"
23 } = options
24 const filter = createFilter(include, exclude)
25
26 const copies = Object.create(null)
27
28 return {
29 load(id) {
30 if (!filter(id)) {
31 return null
32 }
33 return Promise.all([
34 promise(fs.stat, id),
35 promise(fs.readFile, id),
36 ]).then(([stats, buffer]) => {
37 let data
38 if ((limit && stats.size > limit) || limit === 0) {
39 const hash = crypto.createHash("sha1")
40 .update(buffer)
41 .digest("hex")
42 .substr(0, 16)
43 const ext = path.extname(id)
44 const name = path.basename(id, ext)
45 // Determine the directory name of the file based
46 // on either the relative path provided in options,
47 // or the parent directory
48 const relativeDir = options.sourceDir
49 ? path.relative(options.sourceDir, path.dirname(id))
50 : path.dirname(id).split(path.sep).pop()
51
52 // Generate the output file name based on some string
53 // replacement parameters
54 const outputFileName = fileName
55 .replace(/\[hash\]/g, hash)
56 .replace(/\[extname\]/g, ext)
57 .replace(/\[dirname\]/g, `${relativeDir}/`)
58 .replace(/\[name\]/g, name)
59 data = `${publicPath}${outputFileName}`
60 copies[id] = outputFileName
61 } else {
62 const mimetype = mime.getType(id)
63 const isSVG = mimetype === "image/svg+xml"
64 data = isSVG
65 ? encodeSVG(buffer)
66 : buffer.toString("base64")
67 const encoding = isSVG ? "" : ";base64"
68 data = `data:${mimetype}${encoding},${data}`
69 }
70 return `export default "${data}"`
71 })
72 },
73 generateBundle: async function write(outputOptions) {
74 // Allow skipping saving files for server side builds.
75 if (!emitFiles) return
76
77 const base = options.destDir || outputOptions.dir || path.dirname(outputOptions.file)
78
79 await promise(mkdirp, base)
80
81 return Promise.all(Object.keys(copies).map(async name => {
82 const output = copies[name]
83 // Create a nested directory if the fileName pattern contains
84 // a directory structure
85 const outputDirectory = path.join(base, path.dirname(output))
86 await promise(mkpath, outputDirectory)
87 return copy(name, path.join(base, output))
88 }))
89 }
90 }
91}
92
93function promise(fn, ...args) {
94 return new Promise((resolve, reject) =>
95 fn(...args, (err, res) =>
96 err ? reject(err) : resolve(res)))
97}
98
99function copy(src, dest) {
100 return new Promise((resolve, reject) => {
101 const read = fs.createReadStream(src)
102 read.on("error", reject)
103 const write = fs.createWriteStream(dest)
104 write.on("error", reject)
105 write.on("finish", resolve)
106 read.pipe(write)
107 })
108}
109
110// https://github.com/filamentgroup/directory-encoder/blob/master/lib/svg-uri-encoder.js
111function encodeSVG(buffer) {
112 return encodeURIComponent(buffer.toString("utf-8")
113 // strip newlines and tabs
114 .replace(/[\n\r]/gmi, "")
115 .replace(/\t/gmi, " ")
116 // strip comments
117 .replace(/<!\-\-(.*(?=\-\->))\-\->/gmi, "")
118 // replace
119 .replace(/'/gmi, "\\i"))
120 // encode brackets
121 .replace(/\(/g, "%28").replace(/\)/g, "%29")
122}
123
124// use fs.mkdir to instead of mkpath package, see https://github.com/jrajav/mkpath/issues/6
125function mkpath(path, err) {
126 return fs.mkdir(path, { recursive: true }, err);
127}