|
1 | 1 | exports.convert = convert; |
2 | | -exports.getWatcher = getWatcher; |
| 2 | +exports.dispose = dispose; |
3 | 3 |
|
| 4 | +var sassCompiler = require('./compiler'); |
4 | 5 | var spawn = require('child_process').spawn; |
5 | | -var fs = require('fs'); |
6 | 6 | var path = require('path'); |
7 | | -var choki = require('chokidar'); |
8 | | -var watcher = null; |
9 | | -var watchPromisesChain = Promise.resolve(); |
10 | 7 |
|
11 | | -function convert(logger, projectDir, appDir, options) { |
| 8 | +var watcherProcess = null; |
| 9 | + |
| 10 | +function convert(logger, projectDir, appDir, appResourcesDir, options) { |
12 | 11 | options = options || {}; |
13 | | - var sassPath = getSassPath(logger); |
14 | 12 | var data = { |
15 | | - sassPath, |
16 | 13 | projectDir, |
17 | 14 | appDir, |
18 | | - logger, |
19 | | - options |
| 15 | + appResourcesDir, |
| 16 | + logger |
20 | 17 | }; |
21 | 18 |
|
22 | 19 | if (options.watch) { |
23 | 20 | createWatcher(data); |
| 21 | + return; |
24 | 22 | } |
25 | 23 |
|
26 | | - return spawnNodeSass(data); |
27 | | -} |
28 | | - |
29 | | -function getWatcher() { |
30 | | - return watcher; |
| 24 | + return sassCompiler.compile(data); |
31 | 25 | } |
32 | 26 |
|
33 | 27 | function createWatcher(data) { |
34 | | - var appDir = data.appDir; |
35 | | - var watcherOptions = { |
36 | | - ignoreInitial: true, |
37 | | - cwd: appDir, |
38 | | - awaitWriteFinish: { |
39 | | - pollInterval: 100, |
40 | | - stabilityThreshold: 300 |
41 | | - }, |
42 | | - ignored: ['**/.*', '.*'] // hidden files |
43 | | - }; |
| 28 | + if (watcherProcess) { |
| 29 | + return; |
| 30 | + } |
| 31 | + |
| 32 | + watcherProcess = spawn(process.execPath, [ path.join(__dirname, "./watcher.js"), JSON.stringify({appDir: data.appDir, appResourcesDir: data.appResourcesDir, projectDir: data.projectDir })], { stdio: ["ignore", "ignore", "ignore", "ipc"] }); |
44 | 33 |
|
45 | | - watcher = choki.watch(['**/*.scss', '**/*.sass'], watcherOptions) |
46 | | - .on('all', (event, filePath) => { |
47 | | - watchPromisesChain = watchPromisesChain |
48 | | - .then(() => spawnNodeSass(data)) |
49 | | - .catch(err => { |
50 | | - if (!err.stopExecution && err.errorAsWarning) { |
51 | | - data.logger.warn(err.message); |
52 | | - } else { |
53 | | - throw err; |
54 | | - } |
55 | | - }); |
56 | | - }); |
| 34 | + watcherProcess.on('error', error => { |
| 35 | + throw new Error(error); |
| 36 | + }); |
| 37 | + |
| 38 | + watcherProcess.on('message', message => { |
| 39 | + if (message && message.logLevel) { |
| 40 | + data.logger[message.logLevel](message.message); |
| 41 | + } |
| 42 | + }); |
57 | 43 | } |
58 | 44 |
|
59 | | -function getSassPath(logger) { |
60 | | - var sassPath = require.resolve('node-sass/bin/node-sass'); |
61 | | - if (fs.existsSync(sassPath)) { |
62 | | - logger.info('Found peer node-sass'); |
63 | | - } else { |
64 | | - throw new Error('node-sass installation local to project was not found. Install by executing `npm install node-sass`.'); |
| 45 | +function dispose() { |
| 46 | + if (watcherProcess && watcherProcess.connected) { |
| 47 | + watcherProcess.disconnect(); |
| 48 | + watcherProcess = null; |
65 | 49 | } |
66 | | - |
67 | | - return sassPath; |
68 | 50 | } |
69 | 51 |
|
70 | | -function spawnNodeSass(data) { |
71 | | - return new Promise(function (resolve, reject) { |
72 | | - var sassPath = data.sassPath, |
73 | | - projectDir = data.projectDir, |
74 | | - appDir = data.appDir, |
75 | | - logger = data.logger, |
76 | | - options = data.options; |
77 | | - |
78 | | - var importerPath = path.join(__dirname, "importer.js"); |
79 | 52 |
|
80 | | - // Node SASS Command Line Args (https://github.com/sass/node-sass#command-line-interface) |
81 | | - // --ouput : Output directory |
82 | | - // --output-style : CSS output style (nested | expanded | compact | compresed) |
83 | | - // -q : Supress log output except on error |
84 | | - // --follow : Follow symlinked directories |
85 | | - // -r : Recursively watch directories or files |
86 | | - // --watch : Watch a directory or file |
87 | | - var nodeArgs = [sassPath, appDir, '--output', appDir, '--output-style', 'compressed', '-q', '--follow', '--importer', importerPath]; |
88 | | - logger.trace(process.execPath, nodeArgs.join(' ')); |
89 | | - |
90 | | - var env = Object.create( process.env ); |
91 | | - env.PROJECT_DIR = projectDir; |
92 | | - env.APP_DIR = appDir; |
93 | | - |
94 | | - var currentSassProcess = spawn(process.execPath, nodeArgs, { env: env }); |
95 | | - |
96 | | - var isResolved = false; |
97 | | - |
98 | | - currentSassProcess.stdout.on('data', function (data) { |
99 | | - var stringData = data.toString(); |
100 | | - logger.info(stringData); |
101 | | - }); |
102 | | - |
103 | | - currentSassProcess.stderr.on('data', function (err) { |
104 | | - var message = ''; |
105 | | - var stringData = err.toString(); |
106 | | - |
107 | | - try { |
108 | | - var parsed = JSON.parse(stringData); |
109 | | - message = parsed.formatted || parsed.message || stringData; |
110 | | - } catch (e) { |
111 | | - message = err.toString(); |
112 | | - } |
113 | | - |
114 | | - logger.info(message); |
115 | | - }); |
116 | | - |
117 | | - currentSassProcess.on('error', function (err) { |
118 | | - logger.info(err.message); |
119 | | - if (!isResolved) { |
120 | | - isResolved = true; |
121 | | - err.errorAsWarning = true; |
122 | | - err.stopExecution = false; |
123 | | - reject(err); |
124 | | - } |
125 | | - }); |
126 | | - |
127 | | - // TODO: Consider using close event instead of exit |
128 | | - currentSassProcess.on('exit', function (code, signal) { |
129 | | - currentSassProcess = null; |
130 | | - if (!isResolved) { |
131 | | - isResolved = true; |
132 | | - if (code === 0) { |
133 | | - resolve(); |
134 | | - } else { |
135 | | - var error = new Error('SASS compiler failed with exit code ' + code); |
136 | | - error.errorAsWarning = true; |
137 | | - error.stopExecution = false; |
138 | | - reject(error); |
139 | | - } |
140 | | - } |
141 | | - }); |
142 | | - }); |
143 | | -} |
0 commit comments