Search code examples
node.jsnpmnode-modulesnode-sassnpm-scripts

In my package.json file after installing node, I want to have a command that can automatically compile my any of my sass files using one command?


So, to outline what I want, in my script i want to have only one command. So, I want to be able to pass an argument into the code I am running like so :

"scripts": {
"compile:'variable'": "node-sass sass/variable.scss css/variable.css -w" }

Is there a way to do something like this in npm so that I can pass an argument into my command that I would run? In the command line I would like to compile in a simple way. I would, say if I wanted to compile a file called "home". So, I want to be able to type into the command line :

npm run compile:home

Then, this, I would want to get the following command taken through the package.json and the running the node-sass to compile it:

"node-sass scss/home.scss css/home.css -w"

If you have any questions feel free to comment.


Solution

  • Unfortunately, npm-scripts has no built-in way to achieve your requirement in a way that runs successfully cross-platform.

    However, a couple of ways to meet your requirement are as follows (...you probably want Solution B):


    Solution A:

    If you only have a few .scss file to compile to .css then the simplest solution would be to create a npm-script for each file. For example:

    "scripts": {
      "compile:home": "node-sass sass/home.scss css/home.css -w",
      "compile:quxx": "node-sass sass/quux.scss css/quxx.css -w"
    }
    

    This will allow you to run each script using the command syntax that you require, i.e.

    $ npm run compile:home
    $ npm run compile:quxx
    

    However, this is probably not ideal, as I guess you have lots of .scss files - hence your question about utilizing a variable in the script.


    Solution B:

    A solution that works cross-platform, (and enables you to define the filename as an argument via the CLI), is to write a nodejs utility script. The nodejs script can then be invoked via the scripts section of your package.json.

    The following provides the necessary steps to achieve this:

    1. Create a nodejs script as follows: Let's name the file sass-to-css.js.

      sass-to-css.js

      const fs = require('fs');
      const path = require('path');
      const execSync = require('child_process').execSync;
      
      // Error when no filename argument provided.
      if (!process.argv[2]) {
        console.log('\x1b[31mERR!\x1b[0m Trailing filename arg must be provided.');
        process.exit(1);
      }
      
      const fileName = process.argv[2];
      const srcPath = `${path.join('sass', fileName)}.scss`;
      
      // Check source .scss file exists.
      if (!fs.existsSync(srcPath)) {
        console.log('\x1b[31mERR!\x1b[0m Path cannot be found: %s', srcPath);
        process.exit(1);
      }
      
      // Path to node-sass executable in node_modules directory.
      const executablePath = path.join('node_modules', '.bin', 'node-sass');
      
      // Destimation path for resultant .css file.
      const destPath = `${path.join('css', fileName)}.css`;
      
      // The command to be invoked.
      const cmd = `${executablePath} ${srcPath} ${destPath} -w`;
      
      // Execute the command.
      execSync(cmd, {
        cwd: process.cwd()
      });
      
    2. Save sass-to-css.js in the root of your projects directory, in the same folder where package.json is stored. For example:

      .
      ├── ...
      ├── node_modules
      │   └── ...
      ├── package.json
      ├── sass
      │   ├── home.scss
      │   └── ...
      └── sass-to-css.js    <--
      
    3. Then in the scripts section of your package.json add the following script:

      "scripts": {
        "compile": "node sass-to-css"
      }
      
    4. To invoke the script via your CLI you enter something like the following command(s):

      $ npm run compile home
      $ npm run compile foo
      $ npm run compile bar
      

      Note: the last argument provided is the filename, (without the file extension), of the .scss file that you want to compile. Namely home, foo, bar in the above example. A space must exist between the compile part and the filename/argument.

      The syntax to run the command is not exactly as you requested, but it's close !

    Additional notes:

    1. The pertinent parts of sass-to-css.js are:

      • It utilizes nodes process.argv to obtain the filename/argument entered via CLI.
      • The paths for the source (.scss) and destination (.css) files are contructed using nodes path.join().
      • child_process.execSync() is utilized to execute the appropriate node-sass command.
      • It checks whether the mandatory filename argument has been provided, and also checks whether the resultant filepath for the .scss file exists using fs.existsSync(path).
    2. sass-to-css.js currently utilizes Template literals (a feature of ES6). If your running an older version of nodejs that doesn't support this sytax then use the ES5 version of the script below instead:

      ES5 version of sass-to-css.js

      var fs = require('fs');
      var path = require('path');
      var execSync = require('child_process').execSync;
      
      // Error when no filename argument provided.
      if (!process.argv[2]) {
        console.log('\x1b[31mERR!\x1b[0m Trailing filename arg must be provided.');
        process.exit(1);
      }
      
      var fileName = process.argv[2];
      var srcPath = path.join('sass', fileName) + '.scss';
      
      // Check source .scss file exists.
      if (!fs.existsSync(srcPath)) {
        console.log('\x1b[31mERR!\x1b[0m Path cannot be found: %s', srcPath);
        process.exit(1);
      }
      
      // Path to node-sass executable in node_modules directory.
      var executablePath = path.join('node_modules', '.bin', 'node-sass');
      
      // Destimation path for resultant .css file.
      var destPath = path.join('css', fileName) + '.css';
      
      // The command to be invoked.
      var cmd = executablePath + ' ' + srcPath + ' ' + destPath + ' -w';
      
      // Execute the command.
      execSync(cmd, {
        cwd: process.cwd()
      });