1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | var path_1 = require("path");
|
4 | var interfaces_1 = require("./util/interfaces");
|
5 | var errors_1 = require("./util/errors");
|
6 | var bundle_1 = require("./bundle");
|
7 | var fs_extra_1 = require("fs-extra");
|
8 | var config_1 = require("./util/config");
|
9 | var logger_1 = require("./logger/logger");
|
10 | var logger_sass_1 = require("./logger/logger-sass");
|
11 | var logger_diagnostics_1 = require("./logger/logger-diagnostics");
|
12 | var node_sass_1 = require("node-sass");
|
13 | var postcss = require("postcss");
|
14 | var autoprefixer = require("autoprefixer");
|
15 | function sass(context, configFile) {
|
16 | configFile = config_1.getUserConfigFile(context, taskInfo, configFile);
|
17 | var logger = new logger_1.Logger('sass');
|
18 | return sassWorker(context, configFile)
|
19 | .then(function (outFile) {
|
20 | context.sassState = interfaces_1.BuildState.SuccessfulBuild;
|
21 | logger.finish();
|
22 | return outFile;
|
23 | })
|
24 | .catch(function (err) {
|
25 | context.sassState = interfaces_1.BuildState.RequiresBuild;
|
26 | throw logger.fail(err);
|
27 | });
|
28 | }
|
29 | exports.sass = sass;
|
30 | function sassUpdate(changedFiles, context) {
|
31 | var configFile = config_1.getUserConfigFile(context, taskInfo, null);
|
32 | var logger = new logger_1.Logger('sass update');
|
33 | return sassWorker(context, configFile)
|
34 | .then(function (outFile) {
|
35 | context.sassState = interfaces_1.BuildState.SuccessfulBuild;
|
36 | logger.finish();
|
37 | return outFile;
|
38 | })
|
39 | .catch(function (err) {
|
40 | context.sassState = interfaces_1.BuildState.RequiresBuild;
|
41 | throw logger.fail(err);
|
42 | });
|
43 | }
|
44 | exports.sassUpdate = sassUpdate;
|
45 | function sassWorker(context, configFile) {
|
46 | var sassConfig = getSassConfig(context, configFile);
|
47 | var bundlePromise = [];
|
48 | if (!context.moduleFiles && !sassConfig.file) {
|
49 |
|
50 |
|
51 | bundlePromise.push(bundle_1.bundle(context));
|
52 | }
|
53 | return Promise.all(bundlePromise).then(function () {
|
54 | logger_diagnostics_1.clearDiagnostics(context, logger_diagnostics_1.DiagnosticsType.Sass);
|
55 |
|
56 | if (!sassConfig.outFile) {
|
57 | sassConfig.outFile = path_1.join(context.buildDir, sassConfig.outputFilename);
|
58 | }
|
59 | logger_1.Logger.debug("sass outFile: " + sassConfig.outFile);
|
60 |
|
61 | sassConfig.includePaths.unshift(path_1.join(context.srcDir));
|
62 | logger_1.Logger.debug("sass includePaths: " + sassConfig.includePaths);
|
63 |
|
64 | sassConfig.sortComponentPathsFn = (sassConfig.sortComponentPathsFn || defaultSortComponentPathsFn);
|
65 | sassConfig.sortComponentFilesFn = (sassConfig.sortComponentFilesFn || defaultSortComponentFilesFn);
|
66 | if (!sassConfig.file) {
|
67 |
|
68 |
|
69 |
|
70 |
|
71 | generateSassData(context, sassConfig);
|
72 | }
|
73 | else {
|
74 | sassConfig.file = config_1.replacePathVars(context, sassConfig.file);
|
75 | }
|
76 | return render(context, sassConfig);
|
77 | });
|
78 | }
|
79 | exports.sassWorker = sassWorker;
|
80 | function getSassConfig(context, configFile) {
|
81 | configFile = config_1.getUserConfigFile(context, taskInfo, configFile);
|
82 | return config_1.fillConfigDefaults(configFile, taskInfo.defaultConfigFile);
|
83 | }
|
84 | exports.getSassConfig = getSassConfig;
|
85 | function generateSassData(context, sassConfig) {
|
86 | |
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 | var moduleDirectories = [];
|
98 | if (context.moduleFiles) {
|
99 | context.moduleFiles.forEach(function (moduleFile) {
|
100 | var moduleDirectory = path_1.dirname(moduleFile);
|
101 | if (moduleDirectories.indexOf(moduleDirectory) < 0) {
|
102 | moduleDirectories.push(moduleDirectory);
|
103 | }
|
104 | });
|
105 | }
|
106 | logger_1.Logger.debug("sass moduleDirectories: " + moduleDirectories.length);
|
107 |
|
108 |
|
109 | var userSassVariableFiles = sassConfig.variableSassFiles.map(function (f) {
|
110 | return config_1.replacePathVars(context, f);
|
111 | });
|
112 |
|
113 | var componentSassFiles = getComponentSassFiles(moduleDirectories, context, sassConfig);
|
114 | logger_1.Logger.debug("sass userSassVariableFiles: " + userSassVariableFiles.length);
|
115 | logger_1.Logger.debug("sass componentSassFiles: " + componentSassFiles.length);
|
116 | var sassImports = userSassVariableFiles.concat(componentSassFiles).map(function (sassFile) { return '"' + sassFile.replace(/\\/g, '\\\\') + '"'; });
|
117 | if (sassImports.length) {
|
118 | sassConfig.data = "@charset \"UTF-8\"; @import " + sassImports.join(',') + ";";
|
119 | }
|
120 | }
|
121 | function getComponentSassFiles(moduleDirectories, context, sassConfig) {
|
122 | var collectedSassFiles = [];
|
123 | var componentDirectories = getComponentDirectories(moduleDirectories, sassConfig);
|
124 |
|
125 |
|
126 |
|
127 | var sortedComponentPaths = componentDirectories.sort(sassConfig.sortComponentPathsFn);
|
128 | sortedComponentPaths.forEach(function (componentPath) {
|
129 | addComponentSassFiles(componentPath, collectedSassFiles, context, sassConfig);
|
130 | });
|
131 | return collectedSassFiles;
|
132 | }
|
133 | function addComponentSassFiles(componentPath, collectedSassFiles, context, sassConfig) {
|
134 | var siblingFiles = getSiblingSassFiles(componentPath, sassConfig);
|
135 | if (!siblingFiles.length && componentPath.indexOf(path_1.sep + 'node_modules') === -1) {
|
136 |
|
137 | for (var k in sassConfig.directoryMaps) {
|
138 | if (sassConfig.directoryMaps.hasOwnProperty(k)) {
|
139 | var actualDirectory = config_1.replacePathVars(context, k);
|
140 | var mappedDirectory = config_1.replacePathVars(context, sassConfig.directoryMaps[k]);
|
141 | componentPath = componentPath.replace(actualDirectory, mappedDirectory);
|
142 | siblingFiles = getSiblingSassFiles(componentPath, sassConfig);
|
143 | if (siblingFiles.length) {
|
144 | break;
|
145 | }
|
146 | }
|
147 | }
|
148 | }
|
149 | if (siblingFiles.length) {
|
150 | siblingFiles = siblingFiles.sort(sassConfig.sortComponentFilesFn);
|
151 | siblingFiles.forEach(function (componentFile) {
|
152 | collectedSassFiles.push(componentFile);
|
153 | });
|
154 | }
|
155 | }
|
156 | function getSiblingSassFiles(componentPath, sassConfig) {
|
157 | try {
|
158 | return fs_extra_1.readdirSync(componentPath).filter(function (f) {
|
159 | return isValidSassFile(f, sassConfig);
|
160 | }).map(function (f) {
|
161 | return path_1.join(componentPath, f);
|
162 | });
|
163 | }
|
164 | catch (ex) {
|
165 |
|
166 | return [];
|
167 | }
|
168 | }
|
169 | function isValidSassFile(filename, sassConfig) {
|
170 | for (var i = 0; i < sassConfig.includeFiles.length; i++) {
|
171 | if (sassConfig.includeFiles[i].test(filename)) {
|
172 |
|
173 | for (var j = 0; j < sassConfig.excludeFiles.length; j++) {
|
174 | if (sassConfig.excludeFiles[j].test(filename)) {
|
175 |
|
176 | logger_1.Logger.debug("sass excluded: " + filename);
|
177 | return false;
|
178 | }
|
179 | }
|
180 | return true;
|
181 | }
|
182 | }
|
183 | return false;
|
184 | }
|
185 | function getComponentDirectories(moduleDirectories, sassConfig) {
|
186 |
|
187 |
|
188 | return moduleDirectories.filter(function (moduleDirectory) {
|
189 |
|
190 | moduleDirectory = moduleDirectory.replace(/\\/g, '/');
|
191 | for (var i = 0; i < sassConfig.excludeModules.length; i++) {
|
192 | if (moduleDirectory.indexOf('/node_modules/' + sassConfig.excludeModules[i] + '/') > -1) {
|
193 | return false;
|
194 | }
|
195 | }
|
196 | return true;
|
197 | });
|
198 | }
|
199 | function render(context, sassConfig) {
|
200 | return new Promise(function (resolve, reject) {
|
201 | sassConfig.omitSourceMapUrl = false;
|
202 | if (sassConfig.sourceMap) {
|
203 | sassConfig.sourceMapContents = true;
|
204 | }
|
205 | node_sass_1.render(sassConfig, function (sassError, sassResult) {
|
206 | var diagnostics = logger_sass_1.runSassDiagnostics(context, sassError);
|
207 | if (diagnostics.length) {
|
208 | logger_diagnostics_1.printDiagnostics(context, logger_diagnostics_1.DiagnosticsType.Sass, diagnostics, true, true);
|
209 |
|
210 | reject(new errors_1.BuildError('Failed to render sass to css'));
|
211 | }
|
212 | else {
|
213 |
|
214 | renderSassSuccess(context, sassResult, sassConfig).then(function (outFile) {
|
215 | resolve(outFile);
|
216 | }).catch(function (err) {
|
217 | reject(new errors_1.BuildError(err));
|
218 | });
|
219 | }
|
220 | });
|
221 | });
|
222 | }
|
223 | function renderSassSuccess(context, sassResult, sassConfig) {
|
224 | if (sassConfig.autoprefixer) {
|
225 |
|
226 | var autoPrefixerMapOptions = false;
|
227 | if (sassConfig.sourceMap) {
|
228 | autoPrefixerMapOptions = {
|
229 | inline: false,
|
230 | prev: generateSourceMaps(sassResult, sassConfig)
|
231 | };
|
232 | }
|
233 | var postcssOptions = {
|
234 | to: path_1.basename(sassConfig.outFile),
|
235 | map: autoPrefixerMapOptions,
|
236 | from: void 0
|
237 | };
|
238 | logger_1.Logger.debug("sass, start postcss/autoprefixer");
|
239 | var postCssPlugins = [autoprefixer(sassConfig.autoprefixer)];
|
240 | if (sassConfig.postCssPlugins) {
|
241 | postCssPlugins = sassConfig.postCssPlugins.concat(postCssPlugins);
|
242 | }
|
243 | return postcss(postCssPlugins)
|
244 | .process(sassResult.css, postcssOptions).then(function (postCssResult) {
|
245 | postCssResult.warnings().forEach(function (warn) {
|
246 | logger_1.Logger.warn(warn.toString());
|
247 | });
|
248 | var apMapResult = null;
|
249 | if (sassConfig.sourceMap && postCssResult.map) {
|
250 | logger_1.Logger.debug("sass, parse postCssResult.map");
|
251 | apMapResult = generateSourceMaps(postCssResult, sassConfig);
|
252 | }
|
253 | logger_1.Logger.debug("sass: postcss/autoprefixer completed");
|
254 | return writeOutput(context, sassConfig, postCssResult.css, apMapResult);
|
255 | });
|
256 | }
|
257 |
|
258 | var sassMapResult = generateSourceMaps(sassResult, sassConfig);
|
259 | return writeOutput(context, sassConfig, sassResult.css.toString(), sassMapResult);
|
260 | }
|
261 | function generateSourceMaps(sassResult, sassConfig) {
|
262 |
|
263 |
|
264 | if (sassResult.map) {
|
265 | logger_1.Logger.debug("sass, generateSourceMaps");
|
266 |
|
267 | var sassMap = JSON.parse(sassResult.map.toString());
|
268 |
|
269 | var sassMapFile = sassMap.file.replace(/^stdout$/, 'stdin');
|
270 |
|
271 | var sassFileSrc = sassConfig.outFile;
|
272 |
|
273 | var sassFileSrcPath_1 = path_1.dirname(sassFileSrc);
|
274 | if (sassFileSrcPath_1) {
|
275 |
|
276 | var sourceFileIndex_1 = sassMap.sources.indexOf(sassMapFile);
|
277 | sassMap.sources = sassMap.sources.map(function (source, index) {
|
278 | return (index === sourceFileIndex_1) ? source : path_1.join(sassFileSrcPath_1, source);
|
279 | });
|
280 | }
|
281 |
|
282 | sassMap.sources = sassMap.sources.filter(function (src) {
|
283 | if (src !== 'stdin') {
|
284 | return src;
|
285 | }
|
286 | });
|
287 | return sassMap;
|
288 | }
|
289 | }
|
290 | function writeOutput(context, sassConfig, cssOutput, sourceMap) {
|
291 | var mappingsOutput = JSON.stringify(sourceMap);
|
292 | return new Promise(function (resolve, reject) {
|
293 | logger_1.Logger.debug("sass start write output: " + sassConfig.outFile);
|
294 | var buildDir = path_1.dirname(sassConfig.outFile);
|
295 | fs_extra_1.ensureDirSync(buildDir);
|
296 | fs_extra_1.writeFile(sassConfig.outFile, cssOutput, function (cssWriteErr) {
|
297 | if (cssWriteErr) {
|
298 | reject(new errors_1.BuildError("Error writing css file, " + sassConfig.outFile + ": " + cssWriteErr));
|
299 | }
|
300 | else {
|
301 | logger_1.Logger.debug("sass saved output: " + sassConfig.outFile);
|
302 | if (mappingsOutput) {
|
303 |
|
304 |
|
305 | var sourceMapPath_1 = path_1.join(buildDir, path_1.basename(sassConfig.outFile) + '.map');
|
306 | logger_1.Logger.debug("sass start write css map: " + sourceMapPath_1);
|
307 | fs_extra_1.writeFile(sourceMapPath_1, mappingsOutput, function (mapWriteErr) {
|
308 | if (mapWriteErr) {
|
309 | logger_1.Logger.error("Error writing css map file, " + sourceMapPath_1 + ": " + mapWriteErr);
|
310 | }
|
311 | else {
|
312 | logger_1.Logger.debug("sass saved css map: " + sourceMapPath_1);
|
313 | }
|
314 | });
|
315 | }
|
316 |
|
317 |
|
318 | resolve(sassConfig.outFile);
|
319 | }
|
320 | });
|
321 | });
|
322 | }
|
323 | function defaultSortComponentPathsFn(a, b) {
|
324 | var aIndexOfNodeModules = a.indexOf('node_modules');
|
325 | var bIndexOfNodeModules = b.indexOf('node_modules');
|
326 | if (aIndexOfNodeModules > -1 && bIndexOfNodeModules > -1) {
|
327 | return (a > b) ? 1 : -1;
|
328 | }
|
329 | if (aIndexOfNodeModules > -1 && bIndexOfNodeModules === -1) {
|
330 | return -1;
|
331 | }
|
332 | if (aIndexOfNodeModules === -1 && bIndexOfNodeModules > -1) {
|
333 | return 1;
|
334 | }
|
335 | return (a > b) ? 1 : -1;
|
336 | }
|
337 | function defaultSortComponentFilesFn(a, b) {
|
338 | var aPeriods = a.split('.').length;
|
339 | var bPeriods = b.split('.').length;
|
340 | var aDashes = a.split('-').length;
|
341 | var bDashes = b.split('-').length;
|
342 | if (aPeriods > bPeriods) {
|
343 | return 1;
|
344 | }
|
345 | else if (aPeriods < bPeriods) {
|
346 | return -1;
|
347 | }
|
348 | if (aDashes > bDashes) {
|
349 | return 1;
|
350 | }
|
351 | else if (aDashes < bDashes) {
|
352 | return -1;
|
353 | }
|
354 | return (a > b) ? 1 : -1;
|
355 | }
|
356 | var taskInfo = {
|
357 | fullArg: '--sass',
|
358 | shortArg: '-s',
|
359 | envVar: 'IONIC_SASS',
|
360 | packageConfig: 'ionic_sass',
|
361 | defaultConfigFile: 'sass.config'
|
362 | };
|