1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | const fs = require("fs");
|
8 | const path = require("path");
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | const makeFsPath = function makeFsPath(importPath) {
|
15 | let fsPath = importPath;
|
16 | if (path.sep !== "/") {
|
17 | fsPath = fsPath.replace(/\//, path.sep);
|
18 | }
|
19 | return fsPath;
|
20 | };
|
21 |
|
22 |
|
23 |
|
24 | const importOnce = function importOnce(data, done) {
|
25 | if (this._importOnceCache[data.file]) {
|
26 | done({
|
27 | contents: "",
|
28 | filename: `already-imported:${data.file}`,
|
29 | });
|
30 | }
|
31 | else {
|
32 | this._importOnceCache[data.file] = true;
|
33 | done(data);
|
34 | }
|
35 | };
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 | const getFileNames = function getFileNames(abstractName) {
|
46 | const names = [];
|
47 | let directory;
|
48 | let basename;
|
49 | if ([".scss", ".sass"].indexOf(path.extname(abstractName)) !== -1) {
|
50 | directory = path.dirname(abstractName);
|
51 | basename = path.basename(abstractName);
|
52 | ["", "_"].forEach((prefix) => {
|
53 | names.push(path.join(directory, prefix + basename));
|
54 | });
|
55 | }
|
56 | else if (path.extname(abstractName)) {
|
57 | names.push(abstractName);
|
58 | }
|
59 | else {
|
60 | directory = path.dirname(abstractName);
|
61 | basename = path.basename(abstractName);
|
62 |
|
63 | ["", "_"].forEach((prefix) => {
|
64 | [".scss", ".sass"].forEach((ext) => {
|
65 | names.push(path.join(directory, prefix + basename + ext));
|
66 | });
|
67 | });
|
68 |
|
69 | if (this.options.importOnce.index) {
|
70 | ["", "_"].forEach((prefix) => {
|
71 | [".scss", ".sass"].forEach((ext) => {
|
72 | names.push(path.join(abstractName, `${prefix}index${ext}`));
|
73 | });
|
74 | });
|
75 | }
|
76 |
|
77 | if (this.options.importOnce.css) {
|
78 | names.push(`${abstractName}.css`);
|
79 | }
|
80 | }
|
81 | return names;
|
82 | };
|
83 |
|
84 |
|
85 |
|
86 | const getIncludePaths = function getIncludePaths(uri) {
|
87 |
|
88 | const arr = this.options.includePaths.split(path.delimiter);
|
89 | const gfn = getFileNames.bind(this);
|
90 | let paths = [];
|
91 | arr.forEach((includePath) => {
|
92 | paths = paths.concat(gfn(path.resolve(process.cwd(), includePath, uri)));
|
93 | });
|
94 | return paths;
|
95 | };
|
96 |
|
97 |
|
98 |
|
99 | const parseJSON = function parseJSON(data, filename) {
|
100 | let fileReturn = `$${path.basename(filename).replace(path.extname(filename), "")}:`;
|
101 | let colors;
|
102 | let stringData = data.toString();
|
103 | stringData = stringData.replace(/{/g, "(");
|
104 | stringData = stringData.replace(/\[/g, "(");
|
105 | stringData = stringData.replace(/}/g, ")");
|
106 | stringData = stringData.replace(/]/g, ")");
|
107 | fileReturn += stringData;
|
108 | if (fileReturn.substr(fileReturn.length - 1) === "\n") {
|
109 | fileReturn = fileReturn.slice(0, -1);
|
110 | }
|
111 | fileReturn += ";";
|
112 |
|
113 |
|
114 |
|
115 | colors = fileReturn.match(/"(#([0-9a-f]{3}){1,2})"/g);
|
116 | if (colors) {
|
117 | colors.forEach((color) => {
|
118 | fileReturn = fileReturn.replace(color, color.slice(1, -1));
|
119 | });
|
120 | }
|
121 |
|
122 |
|
123 |
|
124 | colors = fileReturn.match(/"(rgb|rgba)\((\d{1,3}), (\d{1,3}), (\d{1,3})\)"/g);
|
125 | if (colors) {
|
126 | colors.forEach((color) => {
|
127 | fileReturn = fileReturn.replace(color, color.slice(1, -1));
|
128 | });
|
129 | }
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 | colors = fileReturn.match(/"(hsl|hsla)\((\d{1,3}), (\d{1,3}), (\d{1,3})\)"/g);
|
137 | if (colors) {
|
138 | colors.forEach((color) => {
|
139 | fileReturn = fileReturn.replace(color, color.slice(1, -1));
|
140 | });
|
141 | }
|
142 | return Buffer.from(fileReturn);
|
143 | };
|
144 |
|
145 |
|
146 |
|
147 |
|
148 | const readFirstFile = function readFirstFile(uri, fileNames, css, cb, examinedFiles) {
|
149 |
|
150 | const filename = fileNames.shift();
|
151 | examinedFiles = examinedFiles || [];
|
152 | examinedFiles.push(filename);
|
153 | fs.readFile(filename, (err, data) => {
|
154 | if (err) {
|
155 | if (fileNames.length) {
|
156 | readFirstFile(uri, fileNames, css, cb, examinedFiles);
|
157 | }
|
158 | else {
|
159 | cb(new Error(`Could not import \`${uri}\` from any of the following locations:
|
160 | \n ${examinedFiles.join("\n ")}`));
|
161 | }
|
162 | }
|
163 | else {
|
164 | if ([".js", ".json"].indexOf(path.extname(filename)) !== -1) {
|
165 | data = parseJSON(data, filename);
|
166 | }
|
167 | cb(null, {
|
168 | contents: data.toString(),
|
169 | file: filename,
|
170 | });
|
171 | }
|
172 | });
|
173 | };
|
174 |
|
175 | const readAbstractFile = function readAbstractFile(uri, abstractName, cb) {
|
176 | const gfn = getFileNames.bind(this);
|
177 | const gip = getIncludePaths.bind(this);
|
178 | const css = Object.assign({}, this.options.importOnce);
|
179 | let files = gfn(abstractName);
|
180 | if (this.options.includePaths) {
|
181 | files = files.concat(gip(uri));
|
182 | }
|
183 | readFirstFile(uri, files, css, cb);
|
184 | };
|
185 |
|
186 |
|
187 |
|
188 | function createImporter(nodeModules) {
|
189 | return function importer(uri, prev, done) {
|
190 | if (uri[0] === "~" && nodeModules) {
|
191 | uri = nodeModules + uri.slice(1);
|
192 | }
|
193 | const isRealFile = fs.existsSync(prev);
|
194 | const io = importOnce.bind(this);
|
195 | const raf = readAbstractFile.bind(this);
|
196 | let file;
|
197 |
|
198 | if (!this.options.importOnce) {
|
199 | this.options.importOnce = {};
|
200 | }
|
201 |
|
202 | if (!this.options.importOnce.index) {
|
203 | this.options.importOnce.index = false;
|
204 | }
|
205 |
|
206 | if (!this.options.importOnce.bower) {
|
207 | this.options.importOnce.bower = false;
|
208 | }
|
209 |
|
210 | if (!this.options.importOnce.css) {
|
211 | this.options.importOnce.css = false;
|
212 | }
|
213 |
|
214 | if (!this._importOnceCache) {
|
215 | this._importOnceCache = {};
|
216 | }
|
217 | if (isRealFile) {
|
218 | file = path.resolve(path.dirname(prev), makeFsPath(uri));
|
219 | raf(uri, file, (err, data) => {
|
220 | if (err) {
|
221 |
|
222 | console.log(err.toString());
|
223 | done({});
|
224 | }
|
225 | else {
|
226 | io(data, done);
|
227 | }
|
228 | });
|
229 | }
|
230 | else {
|
231 | raf(uri, process.cwd(), (err, data) => {
|
232 | if (err) {
|
233 |
|
234 |
|
235 | console.log(err.toString());
|
236 | done({});
|
237 | }
|
238 | else {
|
239 | io(data, done);
|
240 | }
|
241 | });
|
242 | }
|
243 | };
|
244 | }
|
245 |
|
246 |
|
247 |
|
248 | module.exports = (customNodeModulesPath = "") => {
|
249 | const pathHasEndWithSlash = customNodeModulesPath[customNodeModulesPath.length - 1] === "/";
|
250 | const newCustomNodeModulesPath = !pathHasEndWithSlash ? `${customNodeModulesPath}/` : customNodeModulesPath;
|
251 | return createImporter(newCustomNodeModulesPath);
|
252 | };
|