From d6ef1aec5b6da5bfc73db4fe4228713c7d5a6357 Mon Sep 17 00:00:00 2001 From: Peter Staev Date: Thu, 23 Mar 2017 17:06:49 +0200 Subject: [PATCH] add watch capability --- lib/before-prepare.js | 6 ++ lib/converter.js | 139 +++++++++++++++++++----------------------- lib/watch.js | 5 ++ package.json | 7 ++- 4 files changed, 81 insertions(+), 76 deletions(-) create mode 100644 lib/watch.js diff --git a/lib/before-prepare.js b/lib/before-prepare.js index c066b1d..45d31b3 100644 --- a/lib/before-prepare.js +++ b/lib/before-prepare.js @@ -1,5 +1,11 @@ var converter = require('./converter'); module.exports = function ($logger, $projectData, $usbLiveSyncService) { + var liveSync = $usbLiveSyncService.isInitialized; + var bundle = $projectData.$options.bundle; + if (liveSync || bundle) { + return; + } + return converter.convert($logger, $projectData.projectDir); } diff --git a/lib/converter.js b/lib/converter.js index dcde277..d33055c 100644 --- a/lib/converter.js +++ b/lib/converter.js @@ -1,88 +1,77 @@ exports.convert = convert; +var spawn = require('child_process').spawn; var fs = require('fs'); var path = require('path'); -var sass = require('node-sass'); -var glob = require('glob'); + function convert(logger, projectDir, options) { - return new Promise(function (resolve, reject) { - options = options || {}; - - var sassFilesPath = path.join(projectDir, 'app/**/*.scss'); - var sassImportPaths = [ - path.join(projectDir, 'app/'), - path.join(projectDir, 'node_modules/') - ]; - //console.log("SASS Import Path", sassImportPaths); - - var sassFiles = glob.sync(sassFilesPath, { follow: true }).filter(function(filePath){ - var path = filePath; - var parts = path.split('/'); - var filename = parts[parts.length - 1]; - return path.indexOf("App_Resources") === -1 && filename.indexOf("_") !== 0; - }); - - if(sassFiles.length === 0){ - //No sass files in project; skip parsing - resolve(); - } else { - var i = 0; - var loopSassFilesAsync = function(sassFiles){ - parseSass(sassFiles[i], sassImportPaths, function(e){ - if(e !== undefined){ - //Error in the LESS parser; Reject promise - reject(Error(sassFiles[i] + ' SASS CSS pre-processing failed. Error: ' + e)); - } - - i++; //Increment loop counter - - if(i < sassFiles.length){ - loopSassFilesAsync(sassFiles); - } else { - //All files have been processed; Resolve promise - resolve(); - } + return new Promise(function (resolve, reject) { + options = options || {}; + + var peerSassPath = path.join(__dirname, '../../node-sass'); + var sassPath = path.join(peerSassPath, 'bin/node-sass'); + var appDir = path.join(projectDir, "app"); + + if (fs.existsSync(sassPath)) { + try { + logger.info('Found peer node-sass'); + } catch (err) { } + } else { + throw Error('node-sass installation local to project was not found. Install by executing `npm install node-sass`.'); + } + + var nodeArgs = [sassPath, appDir, '--output', appDir, '--output-style', 'compressed']; + if (options.watch) { + nodeArgs.push('-r', '--watch'); + } + + logger.trace(process.execPath, nodeArgs.join(' ')); + var sass = spawn(process.execPath, nodeArgs); + + var isResolved = false; + var watchResolveTimeout; + sass.stdout.on('data', function (data) { + var stringData = data.toString(); + logger.info(stringData); }); - } - - loopSassFilesAsync(sassFiles); - } - }); -} -function parseSass(filePath, importPaths, callback){ - var sassFileContent = fs.readFileSync(filePath, { encoding: 'utf8'}); - var cssFilePath = filePath.replace('.scss', '.css'); + sass.stderr.on('data', function (err) { + var message = ''; + var stringData = err.toString(); + try { + var parsed = JSON.parse(stringData); + message = parsed.formatted || parsed.message || stringData; + } catch (e) { + message = err.toString(); + } + logger.info(message); + }); - if(sassFileContent.trim().length === 0) { - // No SASS content write an empty file - fs.writeFile(cssFilePath, '', 'utf8', function(){ - callback(); - }); - return; - } + sass.on('error', function (err) { + logger.info(err.message); + if (!isResolved) { + isResolved = true; + reject(err); + } + }); - sass.render({ - data: sassFileContent, - includePaths: importPaths, - outFile: cssFilePath, - outputStyle: 'compressed' - }, function (e, output) { - if(e) { - //Callback with error - callback(e); - } - - if(output && output.css){ - output = output.css; - } else { - output = ''; - } + // TODO: Consider using close event instead of exit + sass.on('exit', function (code, signal) { + if (!isResolved) { + isResolved = true; + if (code === 0) { + resolve(); + } else { + reject(Error('SASS compiler failed with exit code ' + code)); + } + } + }); - fs.writeFile(cssFilePath, output, 'utf8', function(){ - //File done writing - callback(); + // SASS does not recompile on watch, so directly resolve. + if (options.watch && !isResolved) { + isResolved = true; + resolve(); + } }); - }); } diff --git a/lib/watch.js b/lib/watch.js new file mode 100644 index 0000000..cc533c3 --- /dev/null +++ b/lib/watch.js @@ -0,0 +1,5 @@ +var converter = require('./converter'); + +module.exports = function ($logger, $projectData, $usbLiveSyncService) { + return converter.convert($logger, $projectData.projectDir, { watch: true }); +} diff --git a/package.json b/package.json index b8ee7a4..482da96 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nativescript-dev-sass", - "version": "0.4.2", + "version": "0.5.0", "description": "SASS CSS pre-processor for NativeScript projects.", "scripts": { "test": "exit 0", @@ -18,6 +18,11 @@ "type": "after-prepare", "script": "lib/after-prepare.js", "inject": true + }, + { + "type": "before-watch", + "script": "lib/watch.js", + "inject": true } ] },