1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | var tslib_1 = require("tslib");
|
4 | var fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
5 | var os_1 = tslib_1.__importDefault(require("os"));
|
6 | var path_1 = tslib_1.__importDefault(require("path"));
|
7 | var globs_1 = tslib_1.__importDefault(require("globs"));
|
8 | var helpers_1 = require("./helpers");
|
9 | var IMPORT_PATTERN = /@import\s+['"](.+)['"];/g;
|
10 | var COMMENT_PATTERN = /\/\/.*$/gm;
|
11 | var MULTILINE_COMMENT_PATTERN = /\/\*[\s\S]*?\*\//g;
|
12 | var DEFAULT_FILE_EXTENSION = ".scss";
|
13 | var ALLOWED_FILE_EXTENSIONS = [".scss", ".css"];
|
14 | var NODE_MODULES = "node_modules";
|
15 | var TILDE = "~";
|
16 | var Bundler = (function () {
|
17 | function Bundler(fileRegistry, projectDirectory) {
|
18 | if (fileRegistry === void 0) { fileRegistry = {}; }
|
19 | this.fileRegistry = fileRegistry;
|
20 | this.projectDirectory = projectDirectory;
|
21 |
|
22 | this.usedImports = {};
|
23 |
|
24 | this.importsByFile = {};
|
25 | }
|
26 | Bundler.prototype.bundle = function (file, dedupeGlobs, includePaths, ignoredImports) {
|
27 | if (dedupeGlobs === void 0) { dedupeGlobs = []; }
|
28 | if (includePaths === void 0) { includePaths = []; }
|
29 | if (ignoredImports === void 0) { ignoredImports = []; }
|
30 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
31 | var contentPromise, dedupeFilesPromise, _a, content, dedupeFiles, ignoredImportsRegEx, _b;
|
32 | return tslib_1.__generator(this, function (_c) {
|
33 | switch (_c.label) {
|
34 | case 0:
|
35 | _c.trys.push([0, 3, , 4]);
|
36 | if (this.projectDirectory != null) {
|
37 | file = path_1.default.resolve(this.projectDirectory, file);
|
38 | }
|
39 | return [4 , fs_extra_1.default.access(file)];
|
40 | case 1:
|
41 | _c.sent();
|
42 | contentPromise = fs_extra_1.default.readFile(file, "utf-8");
|
43 | dedupeFilesPromise = this.globFilesOrEmpty(dedupeGlobs);
|
44 | return [4 , Promise.all([contentPromise, dedupeFilesPromise])];
|
45 | case 2:
|
46 | _a = _c.sent(), content = _a[0], dedupeFiles = _a[1];
|
47 | ignoredImportsRegEx = ignoredImports.map(function (ignoredImport) { return new RegExp(ignoredImport); });
|
48 | return [2 , this._bundle(file, content, dedupeFiles, includePaths, ignoredImportsRegEx)];
|
49 | case 3:
|
50 | _b = _c.sent();
|
51 | return [2 , {
|
52 | filePath: file,
|
53 | found: false
|
54 | }];
|
55 | case 4: return [2 ];
|
56 | }
|
57 | });
|
58 | });
|
59 | };
|
60 | Bundler.prototype.isExtensionExists = function (importName) {
|
61 | return ALLOWED_FILE_EXTENSIONS.some(function (extension) { return importName.indexOf(extension) !== -1; });
|
62 | };
|
63 | Bundler.prototype._bundle = function (filePath, content, dedupeFiles, includePaths, ignoredImports) {
|
64 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
65 | var dirname, importsPromises, imports, bundleResult, shouldCheckForDedupes, currentImports, _i, imports_1, imp, contentToReplace, currentImport, impContent, _a, bundledImport, childImports, timesUsed;
|
66 | var _this = this;
|
67 | return tslib_1.__generator(this, function (_b) {
|
68 | switch (_b.label) {
|
69 | case 0:
|
70 |
|
71 | content = this.removeImportsFromComments(content);
|
72 |
|
73 | filePath = path_1.default.resolve(filePath);
|
74 | dirname = path_1.default.dirname(filePath);
|
75 | if (this.fileRegistry[filePath] == null) {
|
76 | this.fileRegistry[filePath] = content;
|
77 | }
|
78 | importsPromises = helpers_1.matchAll(content, IMPORT_PATTERN).map(function (match) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
79 | var importName, ignored, fullPath, tilde, importData;
|
80 | return tslib_1.__generator(this, function (_a) {
|
81 | switch (_a.label) {
|
82 | case 0:
|
83 | importName = match[1];
|
84 |
|
85 | if (!this.isExtensionExists(importName)) {
|
86 | importName += DEFAULT_FILE_EXTENSION;
|
87 | }
|
88 | ignored = ignoredImports.findIndex(function (ignoredImportRegex) { return ignoredImportRegex.test(importName); }) !== -1;
|
89 | tilde = importName.startsWith(TILDE);
|
90 | if (tilde && this.projectDirectory != null) {
|
91 | importName = "./" + NODE_MODULES + "/" + importName.substr(TILDE.length, importName.length);
|
92 | fullPath = path_1.default.resolve(this.projectDirectory, importName);
|
93 | }
|
94 | else {
|
95 | fullPath = path_1.default.resolve(dirname, importName);
|
96 | }
|
97 | importData = {
|
98 | importString: match[0],
|
99 | tilde: tilde,
|
100 | path: importName,
|
101 | fullPath: fullPath,
|
102 | found: false,
|
103 | ignored: ignored
|
104 | };
|
105 | return [4 , this.resolveImport(importData, includePaths)];
|
106 | case 1:
|
107 | _a.sent();
|
108 | return [2 , importData];
|
109 | }
|
110 | });
|
111 | }); });
|
112 | return [4 , Promise.all(importsPromises)];
|
113 | case 1:
|
114 | imports = _b.sent();
|
115 | bundleResult = {
|
116 | filePath: filePath,
|
117 | found: true
|
118 | };
|
119 | shouldCheckForDedupes = dedupeFiles != null && dedupeFiles.length > 0;
|
120 | currentImports = [];
|
121 | _i = 0, imports_1 = imports;
|
122 | _b.label = 2;
|
123 | case 2:
|
124 | if (!(_i < imports_1.length)) return [3 , 11];
|
125 | imp = imports_1[_i];
|
126 | contentToReplace = void 0;
|
127 | currentImport = void 0;
|
128 | if (!!imp.found) return [3 , 3];
|
129 |
|
130 | currentImport = {
|
131 | filePath: imp.fullPath,
|
132 | tilde: imp.tilde,
|
133 | found: false,
|
134 | ignored: imp.ignored
|
135 | };
|
136 | return [3 , 9];
|
137 | case 3:
|
138 | if (!(this.usedImports[imp.fullPath] == null)) return [3 , 8];
|
139 |
|
140 | this.usedImports[imp.fullPath] = 1;
|
141 | if (!(this.fileRegistry[imp.fullPath] == null)) return [3 , 5];
|
142 | return [4 , fs_extra_1.default.readFile(imp.fullPath, "utf-8")];
|
143 | case 4:
|
144 | _a = _b.sent();
|
145 | return [3 , 6];
|
146 | case 5:
|
147 | _a = this.fileRegistry[imp.fullPath];
|
148 | _b.label = 6;
|
149 | case 6:
|
150 | impContent = _a;
|
151 | return [4 , this._bundle(imp.fullPath, impContent, dedupeFiles, includePaths, ignoredImports)];
|
152 | case 7:
|
153 | bundledImport = _b.sent();
|
154 |
|
155 | this.fileRegistry[imp.fullPath] = bundledImport.bundledContent;
|
156 |
|
157 | currentImport = bundledImport;
|
158 | return [3 , 9];
|
159 | case 8:
|
160 |
|
161 |
|
162 | if (this.usedImports != null) {
|
163 | this.usedImports[imp.fullPath]++;
|
164 | }
|
165 | childImports = [];
|
166 | if (this.importsByFile != null) {
|
167 | childImports = this.importsByFile[imp.fullPath];
|
168 | }
|
169 |
|
170 | currentImport = {
|
171 | filePath: imp.fullPath,
|
172 | tilde: imp.tilde,
|
173 | found: true,
|
174 | imports: childImports
|
175 | };
|
176 | _b.label = 9;
|
177 | case 9:
|
178 | if (imp.ignored) {
|
179 | if (this.usedImports[imp.fullPath] > 1) {
|
180 | contentToReplace = "";
|
181 | }
|
182 | else {
|
183 | contentToReplace = imp.importString;
|
184 | }
|
185 | }
|
186 | else {
|
187 |
|
188 | contentToReplace = this.fileRegistry[imp.fullPath];
|
189 |
|
190 | if (contentToReplace == null) {
|
191 |
|
192 | contentToReplace = "/*** IMPORTED FILE NOT FOUND ***/" + os_1.default.EOL + imp.importString + "/*** --- ***/";
|
193 | }
|
194 |
|
195 | if (shouldCheckForDedupes && this.usedImports != null) {
|
196 | timesUsed = this.usedImports[imp.fullPath];
|
197 | if (dedupeFiles.indexOf(imp.fullPath) !== -1 && timesUsed != null && timesUsed > 1) {
|
198 |
|
199 | contentToReplace = "";
|
200 |
|
201 | currentImport.deduped = true;
|
202 | }
|
203 | }
|
204 | }
|
205 |
|
206 | content = this.replaceLastOccurance(content, imp.importString, contentToReplace);
|
207 |
|
208 | currentImports.push(currentImport);
|
209 | _b.label = 10;
|
210 | case 10:
|
211 | _i++;
|
212 | return [3 , 2];
|
213 | case 11:
|
214 |
|
215 | bundleResult.bundledContent = content;
|
216 | bundleResult.imports = currentImports;
|
217 | if (this.importsByFile != null) {
|
218 | this.importsByFile[filePath] = currentImports;
|
219 | }
|
220 | return [2 , bundleResult];
|
221 | }
|
222 | });
|
223 | });
|
224 | };
|
225 | Bundler.prototype.replaceLastOccurance = function (content, importString, contentToReplace) {
|
226 | var index = content.lastIndexOf(importString);
|
227 | return content.slice(0, index) + content.slice(index).replace(importString, contentToReplace);
|
228 | };
|
229 | Bundler.prototype.removeImportsFromComments = function (text) {
|
230 | var patterns = [COMMENT_PATTERN, MULTILINE_COMMENT_PATTERN];
|
231 | for (var _i = 0, patterns_1 = patterns; _i < patterns_1.length; _i++) {
|
232 | var pattern = patterns_1[_i];
|
233 | text = text.replace(pattern, function (x) { return x.replace(IMPORT_PATTERN, ""); });
|
234 | }
|
235 | return text;
|
236 | };
|
237 | Bundler.prototype.resolveImport = function (importData, includePaths) {
|
238 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
239 | var error_1, underscoredDirname, underscoredBasename, underscoredFilePath, underscoreErr_1, remainingIncludePaths;
|
240 | return tslib_1.__generator(this, function (_a) {
|
241 | switch (_a.label) {
|
242 | case 0:
|
243 | if (this.fileRegistry[importData.fullPath]) {
|
244 | importData.found = true;
|
245 | return [2 , importData];
|
246 | }
|
247 | _a.label = 1;
|
248 | case 1:
|
249 | _a.trys.push([1, 3, , 8]);
|
250 | return [4 , fs_extra_1.default.access(importData.fullPath)];
|
251 | case 2:
|
252 | _a.sent();
|
253 | importData.found = true;
|
254 | return [3 , 8];
|
255 | case 3:
|
256 | error_1 = _a.sent();
|
257 | underscoredDirname = path_1.default.dirname(importData.fullPath);
|
258 | underscoredBasename = path_1.default.basename(importData.fullPath);
|
259 | underscoredFilePath = path_1.default.join(underscoredDirname, "_" + underscoredBasename);
|
260 | _a.label = 4;
|
261 | case 4:
|
262 | _a.trys.push([4, 6, , 7]);
|
263 | return [4 , fs_extra_1.default.access(underscoredFilePath)];
|
264 | case 5:
|
265 | _a.sent();
|
266 | importData.fullPath = underscoredFilePath;
|
267 | importData.found = true;
|
268 | return [3 , 7];
|
269 | case 6:
|
270 | underscoreErr_1 = _a.sent();
|
271 |
|
272 | if (includePaths.length) {
|
273 |
|
274 | importData.fullPath = path_1.default.resolve(includePaths[0], importData.path);
|
275 | remainingIncludePaths = includePaths.slice(1);
|
276 | return [2 , this.resolveImport(importData, remainingIncludePaths)];
|
277 | }
|
278 | return [3 , 7];
|
279 | case 7: return [3 , 8];
|
280 | case 8: return [2 , importData];
|
281 | }
|
282 | });
|
283 | });
|
284 | };
|
285 | Bundler.prototype.globFilesOrEmpty = function (globsList) {
|
286 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
287 | return tslib_1.__generator(this, function (_a) {
|
288 | return [2 , new Promise(function (resolve, reject) {
|
289 | if (globsList == null || globsList.length === 0) {
|
290 | resolve([]);
|
291 | return;
|
292 | }
|
293 | globs_1.default(globsList, function (error, files) {
|
294 | if (error != null) {
|
295 | reject(error);
|
296 | }
|
297 | var fullPaths = files.map(function (file) { return path_1.default.resolve(file); });
|
298 | resolve(fullPaths);
|
299 | });
|
300 | })];
|
301 | });
|
302 | });
|
303 | };
|
304 | return Bundler;
|
305 | }());
|
306 | exports.Bundler = Bundler;
|