Skip to content

Commit 7dfd685

Browse files
author
Fatme
authored
Merge pull request #77 from NativeScript/fatme/fix-ctrl-c
Fix non-working Ctrl+c on windows
2 parents 5be72e2 + 2e5d2b1 commit 7dfd685

File tree

7 files changed

+193
-128
lines changed

7 files changed

+193
-128
lines changed

src/lib/after-watch.js

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
var converter = require('./converter');
22

33
module.exports = function ($logger) {
4-
var watcher = converter.getWatcher();
5-
if (watcher) {
6-
$logger.info("Stopping nativescript-dev-sass watcher");
7-
watcher.close();
8-
}
9-
}
4+
converter.dispose();
5+
}

src/lib/before-prepare.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ module.exports = function ($logger, $projectData, $usbLiveSyncService) {
88
return;
99
}
1010

11-
return converter.convert($logger, $projectData.projectDir, $projectData.appDirectoryPath);
12-
}
11+
return converter.convert($logger, $projectData.projectDir, $projectData.appDirectoryPath, $projectData.appResourcesDirectoryPath);
12+
}

src/lib/compiler.js

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
exports.compile = compile;
2+
3+
var fs = require("fs");
4+
var path = require('path');
5+
var spawn = require("child_process").spawn;
6+
var LogProvider = require('./log-provider');
7+
8+
var currentSassProcess = null;
9+
10+
function compile(data) {
11+
if (currentSassProcess) {
12+
return Promise.resolve();
13+
}
14+
15+
return new Promise((res, rej) => {
16+
var projectDir = data.projectDir,
17+
appDir = data.appDir;
18+
19+
var logger = new LogProvider(data.logger);
20+
21+
var sassPath = require.resolve('node-sass/bin/node-sass');
22+
if (fs.existsSync(sassPath)) {
23+
logger.info("Found peer node-sass");
24+
} else {
25+
isResolved = true;
26+
rej(new Error('node-sass installation local to project was not found. Install by executing `npm install node-sass`.'));
27+
}
28+
29+
var isResolved = false;
30+
var resolve = () => {
31+
if (isResolved) {
32+
return;
33+
}
34+
35+
isResolved = true;
36+
res(currentSassProcess);
37+
}
38+
var reject = err => {
39+
if (isResolved) {
40+
return;
41+
}
42+
43+
isResolved = true;
44+
45+
err.errorAsWarning = true;
46+
err.stopExecution = false;
47+
48+
rej(err);
49+
}
50+
51+
// Node SASS Command Line Args (https://github.com/sass/node-sass#command-line-interface)
52+
// --ouput : Output directory
53+
// --output-style : CSS output style (nested | expanded | compact | compresed)
54+
// -q : Supress log output except on error
55+
// --follow : Follow symlinked directories
56+
// -r : Recursively watch directories or files
57+
// --watch : Watch a directory or file
58+
var nodeArgs = [sassPath, appDir, '--output', appDir, '--output-style', 'compressed', '-q', '--follow', '--importer', path.join(__dirname, "importer.js")];
59+
logger.trace(process.execPath, nodeArgs.join(' '));
60+
61+
var env = Object.create(process.env);
62+
env.PROJECT_DIR = projectDir;
63+
env.APP_DIR = appDir;
64+
65+
currentSassProcess = spawn(process.execPath, nodeArgs, { env: env });
66+
67+
currentSassProcess.stdout.on('data', data => {
68+
var stringData = data.toString();
69+
logger.info(stringData);
70+
});
71+
72+
currentSassProcess.stderr.on('data', error => {
73+
var message = '';
74+
var stringData = error.toString();
75+
76+
try {
77+
var parsed = JSON.parse(stringData);
78+
message = parsed.formatted || parsed.message || stringData;
79+
} catch (e) {
80+
message = error.toString();
81+
}
82+
83+
logger.info(message);
84+
});
85+
86+
currentSassProcess.on('error', error => {
87+
logger.info(err.message);
88+
reject(error);
89+
});
90+
91+
currentSassProcess.on('exit', (code, signal) => {
92+
if (code === 0) {
93+
resolve();
94+
} else {
95+
reject(new Error(`SASS compiler failed with exit code ${code}`));
96+
}
97+
98+
currentSassProcess = null;
99+
});
100+
});
101+
}

src/lib/converter.js

+27-118
Original file line numberDiff line numberDiff line change
@@ -1,143 +1,52 @@
11
exports.convert = convert;
2-
exports.getWatcher = getWatcher;
2+
exports.dispose = dispose;
33

4+
var sassCompiler = require('./compiler');
45
var spawn = require('child_process').spawn;
5-
var fs = require('fs');
66
var path = require('path');
7-
var choki = require('chokidar');
8-
var watcher = null;
9-
var watchPromisesChain = Promise.resolve();
107

11-
function convert(logger, projectDir, appDir, options) {
8+
var watcherProcess = null;
9+
10+
function convert(logger, projectDir, appDir, appResourcesDir, options) {
1211
options = options || {};
13-
var sassPath = getSassPath(logger);
1412
var data = {
15-
sassPath,
1613
projectDir,
1714
appDir,
18-
logger,
19-
options
15+
appResourcesDir,
16+
logger
2017
};
2118

2219
if (options.watch) {
2320
createWatcher(data);
21+
return;
2422
}
2523

26-
return spawnNodeSass(data);
27-
}
28-
29-
function getWatcher() {
30-
return watcher;
24+
return sassCompiler.compile(data);
3125
}
3226

3327
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"] });
4433

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+
});
5743
}
5844

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;
6549
}
66-
67-
return sassPath;
6850
}
6951

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");
7952

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-
}

src/lib/log-provider.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const INFO_LOG_LEVEL = "info";
2+
const TRACE_LOG_LEVEL = "trace";
3+
const WARN_LOG_LEVEL = "warn";
4+
5+
module.exports = function(logger) {
6+
this.info = (message) => {
7+
this.logData({logLevel: INFO_LOG_LEVEL, message});
8+
}
9+
10+
this.trace = (message) => {
11+
this.logData({logLevel: TRACE_LOG_LEVEL, message});
12+
}
13+
14+
this.warn = (message) => {
15+
this.logData({logLevel: WARN_LOG_LEVEL, message});
16+
}
17+
18+
this.logData = (data) => {
19+
if (logger) {
20+
return logger[data.logLevel](data.message);
21+
}
22+
23+
process.send(data);
24+
}
25+
}

src/lib/watch.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ module.exports = function (logger, projectData, usbLiveSyncService, hookArgs) {
99
}
1010
}
1111

12-
return converter.convert(logger, projectData.projectDir, projectData.appDirectoryPath, { watch: true });
13-
}
12+
return converter.convert(logger, projectData.projectDir, projectData.appDirectoryPath, projectData.appResourcesDirectoryPath, { watch: true });
13+
}

src/lib/watcher.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
var choki = require('chokidar');
2+
var path = require('path');
3+
var compiler = require('./compiler');
4+
var LogProvider = require('./log-provider');
5+
6+
var args = JSON.parse(process.argv[2]);
7+
var appDir = args.appDir;
8+
var projectDir = args.projectDir;
9+
var appResourcesDir = args.appResourcesDir;
10+
var watchPromisesChain = Promise.resolve();
11+
12+
var watcherOptions = {
13+
ignoreInitial: true,
14+
cwd: appDir,
15+
awaitWriteFinish: {
16+
pollInterval: 100,
17+
stabilityThreshold: 300
18+
},
19+
ignored: ['**/.*', '.*', appResourcesDir] // hidden files and App_Resources folder
20+
};
21+
22+
watcher = choki.watch('**/*.s[ac]ss', watcherOptions)
23+
.on('all', (event, filePath) => {
24+
watchPromisesChain = watchPromisesChain
25+
.then(() => compiler.compile({appDir, projectDir}))
26+
.catch(err => {
27+
if (!err.stopExecution && err.errorAsWarning) {
28+
var logger = new LogProvider(null);
29+
logger.warn(err.message);
30+
} else {
31+
throw err;
32+
}
33+
});
34+
});

0 commit comments

Comments
 (0)