How to prevent yarn install from running twice in Makefile? - gnu-make

I have this in my Makefile:
node_modules: yarn.lock
yarn install --production=false
touch node_modules
yarn.lock: package.json
yarn install --production=false
touch yarn.lock
Basically, if the node_modules directory is missing (or someone has tampered with it by adding/removing files), or yarn.lock has been updated, then it should run yarn install to rebuild/integrity check the node_modules dir.
However, if yarn.lock is missing, it can be rebuilt from package.json, or if package.json is updated, then it should install and rebuild the lock file.
The problem is when both node_modules and yarn.lock are missing, then the same commands run twice.
How can I prevent this?
I can nearly get it to work by wrapping the directives in a conditional:
ifneq ("$(wildcard yarn.lock)","")
node_modules: yarn.lock
#yarn install --production=false
touch node_modules
yarn.lock: package.json
touch yarn.lock
else # yarn.lock does not exist
node_modules: yarn.lock
touch node_modules
yarn.lock: package.json
#yarn install --production=false
endif
Now if you touch package.json and then make node_modules and yarn.lock exists, then it'll subsequently touch yarn.lock which will cause node_modules to rebuild, just like I want.
However, if you touch package.json and then make yarn.lock, technically it should attempt a yarn install but it won't because I removed the command from this directive:
yarn.lock: package.json
touch yarn.lock
To prevent it from running twice in the former scenario.

To a first approximation, consider the approach illustrated here:
Makefile (1)
.PHONY: all clean
all: yarn.lock
yarn.lock: node_modules package.json
$(MAKE clean)
yarn install --production=false
node_modules:
mkdir -p $#
clean:
rm -fr node_modules yarn.lock
This will never run yarn install redundantly, and it's a somewhat more
robust solution than you're considering. I'll explain.
The one source item in the problem is the package.json. It is the sole
logical prerequisite of everything else and is not itself to be built.
The yarn.lock is a build-artefact whose production signifies that that
yarn install has been done successfully with respect to the snapshot
of package.json that existed when it was done. yarn install will
subsequently consider that the installation is up-to-date as long as yarn.lock
exists and has contents that "agree" with package.json by criteria
that are algorithmically embodied in yarn install.
So, viewed simplistically, the mission of this build is just to make yarn.lock
up-to-date with respect to package.json:
yarn.lock: package.json
yarn install --production=false
But it's actually more complicated. yarn.lock is the build target but
it isn't the only build-artefact and it isn't even one of the artefacts of
primary value. Those, of course, are whatever artefacts will populate
node-modules as a result of running yarn install.
So the primary build-artefacts appear as side effects of this build,
while the actual target, yarn.lock matters to us only as a token that
the primary artefacts, whatever they may be, have been made up-to-date
with the package.json.
And it is a frail token. Agencies can mess with the contents of
node_modules - adding files that shouldn't be there, deleting or
modifying ones that should - and yarn install won't do anything to
rectify it as long as it considers yarn.lock up-to-date with package.json,
by its own criteria.
You adverted to that fragility in explaining the suggested recipe:
node_modules: yarn.lock
yarn install --production=false
touch node_modules
if the node_modules directory is missing (or someone has tampered with it by
adding/removing files), or yarn.lock has been updated, then it should run yarn
install to rebuild/integrity check the node_modules dir.
But that rule is the wrong way round to be triggered by such tampering. The
tampering - if you're lucky - will update the modification time of node_modules.
But that will make it younger, not older, than yarn.lock, and won't fire
the recipe. The recipe is only good for the case in which node_modules does not exist.
That weakness is mitigated by the recipe:
yarn.lock: node_modules package.json
$(MAKE) clean
yarn install --production=false
If yarn.lock doesn't exist, or is out-of-date w.r.t to either node_modules
or package_json, we'll remake all the build-artefacts from scratch.
That's better, but brings with it a boot-strap problem, when neither of the
artefacts yarn.lock or node_modules exists but node_modules - which is to
be populated as a by product of making yarn.lock - is also a perquisite of yarn.lock.
It's a trivial problem however. The prerequisite for yarn.lock is the mere existence
of node_modules and it is satisfiable prior to making yarn.lock, and the
contents of node_modules, up-to-date - by just adding the recipe:
node_modules:
mkdir -p $#
With this, if node_modules ever doesn't exist, it will be created as
a prerequisite of yarn.lock, making it newer than yarn.lock, and requiring
yarn.lock and the primary build-artefacts to be made.
But...
This solution expresses the dependency relations essentially right and - as
a consequence - shows how yarn install never needs to be run redundantly. And
it corrects the wrong-way-round bug in your tamper-detection logic.
But is still falls short of strong tamper-detection.
The tamper-detection mechanism we've got is: Something happens in the node_modules
directory that makes its modification date later than that of yarn.lock. That
will detect some tampering, but not all tampering.
As a filesystem object, a directory is modified - and its modification time updated
- if and only if an immediate child object is added, removed, or renamed. So
the tamper-detection mechanism is blind to all events inside any subdirectory of
node_modules and to any modification of an existing file or subdirectory of
node_modules except renaming it. That leaves ample scope for messing up
the node_modules.
In that light, you might decide to:-
Stick
Flimsy tamper-detection is better than none. I don't want to use any
more expensive than this.
But you probably wouldn't. More likely alternatives:
Fold
Tamper-detection this flimsy is no better than none, so I'll fall back to:
yarn.lock: package.json
yarn install --production=false
I'll regard improper tampering as out-of-scope for my build. If it happens, something will break,
I'll notice it, make clean and try again.
Up the ante
I want strong tamper-detection.
Strong tamper-detection makes rather heavier lifting - but not much heavier.
You need to force a clean yarn install, or not, depending on the outcome of an
old-versus-new comparison between complete manifests of the contents of node_modules -
manifests informative enough that any material difference will show up.
A manifest detailing the pathname and modification time of every file in
node_modules is the best candidate. This manifest will contain the information that make
would need to know, and would get from the file system, if the elusive primary artefacts
of this build could be spelled out to it, and a change in that information relative to
its last recorded state is a reliable trigger to remake everything. Thus:
Makefile (2)
RM := rm -fr
MANIFEST_DIR := .manifest
LAST_MANIFEST := $(MANIFEST_DIR)/node_modules.last
NEW_MANIFEST := $(MANIFEST_DIR)/node_modules.peek
GEN_MANIFEST := find node_modules/ -exec stat -c '%n %y' {} \;
$(shell mkdir -p $(MANIFEST_DIR) node_modules)
$(if $(wildcard $(LAST_MANIFEST)),,$(shell touch $(LAST_MANIFEST)))
$(shell $(GEN_MANIFEST) > $(NEW_MANIFEST))
$(shell cmp -s $(LAST_MANIFEST) $(NEW_MANIFEST) || touch node_modules)
.PHONY: all clean
all: $(LAST_MANIFEST)
yarn.lock: node_modules package.json
$(RM) yarn.lock node_modules
yarn install --production=false
$(LAST_MANIFEST): yarn.lock
$(GEN_MANIFEST) > $#
clean:
$(RM) yarn.lock node_modules $(MANIFEST_DIR)
This develops Makefile (1) mainly with the unconditionally executed apparatus
at the top, which:-
Ensures (as before) we start with a node_modules directory, even if empty.
Ensures we start with a hidden directory (.manifest) for working in
and for persisting the latest manifest of node_modules. (Analogous to
the hidden .deps directory classically used for persisting autodependency files
in C/C++ makes).
Ensures we start with a persisted manifest, even if empty.
Generates a new, true, manifest snapshot with: find node_modules/ -exec stat -c '%n %y' {} \;,
which writes <filename> <modification_time> for each item under node_modules.
This snapshot is true of node_modules as it is, but not necessarily true
of node_modules as it should be. (Should it follow symlinks? - find -L ...?
No. Because make wouldn't follow symlinks for targets or prerequisites).
Compares the new, true, snapshot with the persisted manifest and, if
there is any difference, then touches node_modules.
This amounts to a build preamble that will update the modification
time of node_modules, or not, by a strong tamper-detecting test. Then
the build is much as before except that its target is no longer
yarn.lock, but a new persisted manifest, $(LAST_MANIFEST), that is always an
immediate post-yarn-install snapshot and accordingly dependent on
yarn.lock.
A workout for Makefile (2)
For a lab-rat package.json
I'll use:
{
"name": "node-js-sample",
"version": "0.2.0",
"description": "A sample Node.js app using Express 4",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.13.3"
},
"engines": {
"node": "4.0.0"
},
"repository": {
"type": "git",
"url": "https://github.com/heroku/node-js-sample"
},
"keywords": [
"node",
"heroku",
"express"
],
"author": "Mark Pundsack",
"contributors": [
"Zeke Sikelianos <zeke#sikelianos.com> (http://zeke.sikelianos.com)"
],
"license": "MIT"
}
Make from scratch
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.17s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Change nothing and remake
$ make
make: Nothing to be done for 'all'.
Touch node_modules only
$ touch node_modules/
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.01s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Touch package.json only
$ touch package.json
imk#imk-ThinkPad-T420:~/develop/so/make_prob$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.22s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Touch node_modules and package.json
$ touch package.json node_modules/
imk#imk-ThinkPad-T420:~/develop/so/make_prob$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.05s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Touch yarn.lock
$ touch yarn.lock
$ make
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Delete yarn.lock
$ rm yarn.lock
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.17s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Change a dependency in package.json
$ sed -i 's/4\.13\.3/4.15.3/' package.json
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.03s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Undo the change
$ sed -i 's/4\.15\.3/4.15.3/' package.json
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 2.35s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Touch an existing file in a subdirectory of node_modules
$ ls node_modules/vary/
HISTORY.md index.js LICENSE package.json README.md
$ touch node_modules/vary/README.md
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.02s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Add a file to a subdirectory of node_modules
$ touch node_modules/vary/interloper
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.20s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last
Remove a file from a subdirectory of node_modules
$ rm node_modules/vary/README.md
$ make
rm -fr yarn.lock node_modules
yarn install --production=false
yarn install v0.24.5
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 1.16s.
find node_modules/ -exec stat -c '%n %y' {} \; > .manifest/node_modules.last

Borrowing from Mike, I think this is robust enough for my needs:
BIN := node_modules/.bin
SRC_FILES := $(shell find src -name '*.js')
DIST_FILES := $(patsubst src/%,dist/%,$(SRC_FILES))
DIST_DIRS := $(sort $(dir $(DIST_FILES)))
# these are not files
.PHONY: all clean nuke
# disable default suffixes
.SUFFIXES:
all: $(DIST_FILES)
dist/%.js: src/%.js yarn.lock .babelrc | $(DIST_DIRS)
$(BIN)/babel $< -o $#
$(DIST_DIRS):
mkdir -p $#
yarn.lock: node_modules package.json
#yarn install --production=false --check-files
#touch -mr $(shell ls -Atd $? | head -1) $#
node_modules:
mkdir -p $#
clean:
rm -rf node_modules dist
nuke: clean
rm -rf yarn.lock
I added
#touch -mr $(shell ls -Atd $? | head -1) $#
Which updates the modification time of yarn.lock to the newer of node_modules or package.json. This is necessary for when those are touched but don't result in a lockfile change -- yarn install won't update the file which will cause yarn.lock to be perpetually out of date.
--check-files will force yarn install to actually reinstall any missing packages inside node_modules/, but it won't do a deep search. So if tamper some files inside of a package (rather than just deleting the whole package), this sort of change won't be caught.
The babel stuff is just an example of how I'm using this. It will recompile only the source files that have changed. Now I just have to run make in a freshly cloned project and it will bring everything up to date with one command.

Related

make - how to list googletest lib as dependency

I am able to build googletest and use it to run unit tests.
In MY makefile for my unit_test.cpp, I need to check whether googletest libs exist in ..\googletest\ and only if they do not exist, I want to run cmake and make for googletest.
Running cmake and make even if they are built takes 30 secs so I only want to run cmake and make if I actually have to.
QUESTION
In MY makefile for my unit_test.cpp, what could I list as a dependency so that building googletest only happens if actually necessary?
A "brute force" approach would be to list googletest\build\lib\libgtest.a but this would break if, however unlikely, googletest changes the output name from lib\ to libs\.
If you are worried about possible change of the file layout in the build directory, you can use cmake --install command and directly specify the subfolders for header files and libraries before running cmake configure step. CMAKE_INSTALL_PREFIX variable allows to specify install directory and CMAKE_INSTALL_INCLUDEDIR, CMAKE_INSTALL_LIBDIR variables allow to specify subfolders for header files and libraries, accordingly:
GOOGLETEST_SRC_DIR := googletest
GOOGLETEST_BUILD_CACHE_DIR := googletest_build_cache
GOOGLETEST_SDK_DIR := googletest_sdk
GOOGLETEST_SDK_INCLUDE_DIR := include
GOOGLETEST_SDK_LIB_DIR := lib
GOOGLETEST_LIB := gtest
GOOGLETEST_MAIN_LIB := $(GOOGLETEST_LIB)_main
UT := ut
UT_SRC := ut.cpp
default_target: $(UT)
$(GOOGLETEST_BUILD_CACHE_DIR) :
cmake \
-DCMAKE_INSTALL_PREFIX=$(shell pwd)/$(GOOGLETEST_SDK_DIR) \
-DCMAKE_INSTALL_LIBDIR=$(GOOGLETEST_SDK_LIB_DIR) \
-DCMAKE_INSTALL_INCLUDEDIR=$(GOOGLETEST_SDK_INCLUDE_DIR) \
-B $(GOOGLETEST_BUILD_CACHE_DIR) -S $(GOOGLETEST_SRC_DIR)
cmake --build $(GOOGLETEST_BUILD_CACHE_DIR) --parallel
touch $(GOOGLETEST_BUILD_CACHE_DIR)
$(GOOGLETEST_SDK_DIR): $(GOOGLETEST_BUILD_CACHE_DIR)
cmake --install $(GOOGLETEST_BUILD_CACHE_DIR)
touch $(GOOGLETEST_SDK_DIR)
$(UT): $(UT_SRC) $(GOOGLETEST_SDK_DIR)
g++ $(UT_SRC) -o $(UT) \
-I$(GOOGLETEST_SDK_DIR)/$(GOOGLETEST_SDK_INCLUDE_DIR) \
-L$(GOOGLETEST_SDK_DIR)/$(GOOGLETEST_SDK_LIB_DIR) \
-l$(GOOGLETEST_MAIN_LIB) -l$(GOOGLETEST_LIB)
clean:
rm -f $(UT)
rm -fR $(GOOGLETEST_BUILD_CACHE_DIR) $(GOOGLETEST_SDK_DIR)

WP composer installation and Unistallation with CLI

I have tried to make a theme, pre-packed from underscore.me, then I have gone the theme root folder. Then command: composer install then I have found 1 folder (vendor) and composer.lock file. Then I can check my project with composer lint:wpcs this command. Now, feel that this vendor & composer.lock file is unnecessary for me.
How can I uninstall them with command line without sudo rm -rf 'bla bla' this command.
Thank you

Buildroot: qt application with an empty install target

I am integrating my own Qt app into buildroot. *.mk file follows.
CATCHER_SITE=http://g7/gitlab/igorsk/catcher.git
CATCHER_SITE_METHOD=git
CATCHER_VERSION=master
CATCHER_DEPENDENCIES=qt
ifeq ($(BR2_PACKAGE_QT5),y)
CATCHER_QMAKE = $(QT5_QMAKE)
else
CATCHER_QMAKE = $(QT_QMAKE)
endif
define CATCHER_CONFIGURE_CMDS
(cd $(#D); $(TARGET_MAKE_ENV) $(CATCHER_QMAKE))
endef
define CATCHER_BUILD_CMDS
$(TARGET_MAKE_ENV) $(MAKE) -C $(#D)
endef
define CATCHER_INSTALL_TARGET_CMDS
$(TARGET_MAKE_ENV) $(MAKE) INSTALL_ROOT=$(TARGET_DIR) -C $(#D) install
endef
$(eval $(generic-package))
The app compiles, but it doesn't get installed to a target directory (and therefore to resulting rootfs ). During buildroot's "Installing to target" phase I get Nothing to be done for 'install'. message. buildroot/output/build/catcher-master/Makefile has emtpy install rule.
Buildroot 2015.08.1
Qt 4.8.7
The question is: why is this happening and how do I get my app installed to target?

How to force sbt to redownload dependencies when ivy cache corrupted

when ivy cache is corrupted I got the following error from sbt
[error] unresolved dependency: commons-codec#commons-codec;1.10: configuration not found in commons-codec#commons-codec;
1.10: 'master(compile)'. Missing configuration: 'compile'. It was required from com.typesafe.play#play_2.11;2.4.3 compile
if I delete the folder commons-codec in ivy cache and run sbt update, sbt will re-download the dependencies and everything will be fine.
Is there a way to tell sbt to delete the folder and re-download dependencies automatically?
It's pretty easy, just
rm -fr ~/.ivy2/cache # Or mv ~/.ivy2/cache ~/.ivy2/cache_bk
sbt update
Finally if you are in Intellij, File -> Invalidate Caches / Restart.
I just did the same thing 20 minutes ago. Probably not a bad thing either. I just saved a pretty big chunk of space on my mac.
Atom:~ me$ du -skh ./.iv*
349M ./.ivy2
1.0G ./.ivy2_bak
[Added 6-May-2021]
If you remove ~/.ivy2 and all your stuff still assembles, cleans, tests, etc. without re-downloading, you might be using another tool, like sdkman, that puts cached files in a different place. Wipe that cache like so.
pwd
~/Library/Caches/Coursier/v1/https/repo1.maven.org
mv ./maven2/ ./_maven2-backup
As a word of caution, it is probably best backup your cache files rather than just wiping them. There are cases, like internally developed bad packages, that you might need to copy over from the backup to the new download. Back it up, rebuild your project, then rm -fr the backup.
# empty the ivy cache if you have good network
# rm -rfv ~/.ivy2/cache/*
# or even better just backup it to sync it later on ...
# mv ~/.ivy2/cache ~/.ivy2/cache.`date "+%Y%m%d_%H%M%S`.bak
# remove all sbt lock files
find ~/.sbt ~/.ivy2 -name "*.lock" -print -delete
find ~/.sbt ~/.ivy2 -name "ivydata-*.properties" -print -delete
# remove all the class files
rm -fvr ~/.sbt/1.0/plugins/target
rm -fvr ~/.sbt/1.0/plugins/project/target
rm -fvr ~/.sbt/1.0/target
rm -fvr ~/.sbt/0.13/plugins/target
rm -fvr ~/.sbt/0.13/plugins/project/target
rm -fvr ~/.sbt/0.13/target
rm -fvr ./project/target
rm -fvr ./project/project/target
sbt clean update
Try removing the specific dependency that is causing the problem:
rm -rf ~/.ivy2/cache/commons-codec

How to compile or convert sass / scss to css with node-sass (no Ruby)?

I was struggling with setting up libsass as it wasn't as straight-forward as the Ruby based transpiler. Could someone explain how to:
install libsass?
use it from command line?
use it with task runners like gulp and grunt?
I have little experience with package managers and even less so with task runners.
I picked node-sass implementer for libsass because it is based on node.js.
Installing node-sass
(Prerequisite) If you don't have npm, install Node.js first.
$ npm install -g node-sass installs node-sass globally -g.
This will hopefully install all you need, if not read libsass at the bottom.
How to use node-sass from Command line and npm scripts
General format:
$ node-sass [options] <input.scss> [output.css]
$ cat <input.scss> | node-sass > output.css
Examples:
$ node-sass my-styles.scss my-styles.css compiles a single file manually.
$ node-sass my-sass-folder/ -o my-css-folder/ compiles all the files in a folder manually.
$ node-sass -w sass/ -o css/ compiles all the files in a folder automatically whenever the source file(s) are modified. -w adds a watch for changes to the file(s).
More usefull options like 'compression' # here. Command line is good for a quick solution, however, you can use task runners like Grunt.js or Gulp.js to automate the build process.
You can also add the above examples to npm scripts. To properly use npm scripts as an alternative to gulp read this comprehensive article # css-tricks.com especially read about grouping tasks.
If there is no package.json file in your project directory running $ npm init will create one. Use it with -y to skip the questions.
Add "sass": "node-sass -w sass/ -o css/" to scripts in package.json file. It should look something like this:
"scripts": {
"test" : "bla bla bla",
"sass": "node-sass -w sass/ -o css/"
}
$ npm run sass will compile your files.
How to use with gulp
$ npm install -g gulp installs Gulp globally.
If there is no package.json file in your project directory running $ npm init will create one. Use it with -y to skip the questions.
$ npm install --save-dev gulp installs Gulp locally. --save-dev adds gulp to devDependencies in package.json.
$ npm install gulp-sass --save-dev installs gulp-sass locally.
Setup gulp for your project by creating a gulpfile.js file in your project root folder with this content:
'use strict';
var gulp = require('gulp');
A basic example to transpile
Add this code to your gulpfile.js:
var gulp = require('gulp');
var sass = require('gulp-sass');
gulp.task('sass', function () {
gulp.src('./sass/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('./css'));
});
$ gulp sass runs the above task which compiles .scss file(s) in the sass folder and generates .css file(s) in the css folder.
To make life easier, let's add a watch so we don't have to compile it manually. Add this code to your gulpfile.js:
gulp.task('sass:watch', function () {
gulp.watch('./sass/**/*.scss', ['sass']);
});
All is set now! Just run the watch task:
$ gulp sass:watch
How to use with Node.js
As the name of node-sass implies, you can write your own node.js scripts for transpiling. If you are curious, check out node-sass project page.
What about libsass?
Libsass is a library that needs to be built by an implementer such as sassC or in our case node-sass. Node-sass contains a built version of libsass which it uses by default. If the build file doesn't work on your machine, it tries to build libsass for your machine. This process requires Python 2.7.x (3.x doesn't work as of today). In addition:
LibSass requires GCC 4.6+ or Clang/LLVM. If your OS is older, this version may not compile. On Windows, you need MinGW with GCC 4.6+ or VS 2013 Update 4+. It is also possible to build LibSass with Clang/LLVM on Windows.
The installation of these tools may vary on different OS.
Under Windows, node-sass currently supports VS2015 by default, if you only have VS2013 in your box and meet any error while running the command, you can define the version of VS by adding: --msvs_version=2013. This is noted on the node-sass npm page.
So, the safe command line that works on Windows with VS2013 is:
npm install --msvs_version=2013 gulp node-sass gulp-sass
npx node-sass input.scss out.css
In Windows 10 using node v6.11.2 and npm v3.10.10, in order to execute directly in any folder:
> node-sass [options] <input.scss> [output.css]
I only followed the instructions in node-sass Github:
Add node-gyp prerequisites by running as Admin in a Powershell (it takes a while):
> npm install --global --production windows-build-tools
In a normal command-line shell (Win+R+cmd+Enter) run:
> npm install -g node-gyp
> npm install -g node-sass
The -g places these packages under %userprofile%\AppData\Roaming\npm\node_modules. You may check that npm\node_modules\node-sass\bin\node-sass now exists.
Check if your local account (not the System) PATH environment variable contains:
%userprofile%\AppData\Roaming\npm
If this path is not present, npm and node may still run, but the modules bin files will not!
Close the previous shell and reopen a new one and run either > node-gyp or > node-sass.
Note:
The windows-build-tools may not be necessary (if no compiling is done? I'd like to read if someone made it without installing these tools), but it did add to the admin account the GYP_MSVS_VERSION environment variable with 2015 as a value.
I am also able to run directly other modules with bin files, such as > uglifyjs main.js main.min.js and > mocha

Resources