1 | import {createFilter} from "rollup-pluginutils"
|
2 | import mkdirp from 'mkdirp'
|
3 | import mime from "mime"
|
4 | import crypto from "crypto"
|
5 | import path from "path"
|
6 | import fs from "fs"
|
7 |
|
8 | const defaultInclude = [
|
9 | "**/*.svg",
|
10 | "**/*.png",
|
11 | "**/*.jpg",
|
12 | "**/*.gif",
|
13 | ]
|
14 |
|
15 | export 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 |
|
46 |
|
47 |
|
48 | const relativeDir = options.sourceDir
|
49 | ? path.relative(options.sourceDir, path.dirname(id))
|
50 | : path.dirname(id).split(path.sep).pop()
|
51 |
|
52 |
|
53 |
|
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 |
|
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 |
|
84 |
|
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 |
|
93 | function promise(fn, ...args) {
|
94 | return new Promise((resolve, reject) =>
|
95 | fn(...args, (err, res) =>
|
96 | err ? reject(err) : resolve(res)))
|
97 | }
|
98 |
|
99 | function 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 |
|
111 | function encodeSVG(buffer) {
|
112 | return encodeURIComponent(buffer.toString("utf-8")
|
113 |
|
114 | .replace(/[\n\r]/gmi, "")
|
115 | .replace(/\t/gmi, " ")
|
116 |
|
117 | .replace(/<!\-\-(.*(?=\-\->))\-\->/gmi, "")
|
118 |
|
119 | .replace(/'/gmi, "\\i"))
|
120 |
|
121 | .replace(/\(/g, "%28").replace(/\)/g, "%29")
|
122 | }
|
123 |
|
124 |
|
125 | function mkpath(path, err) {
|
126 | return fs.mkdir(path, { recursive: true }, err);
|
127 | }
|