How do I make the Sass Importer combine files into one stylesheet? [duplicate] - node.js

This question already has answers here:
Import regular CSS file in SCSS file?
(15 answers)
Closed 7 years ago.
I recently found out about this feature in node-sass named importer, which allows you to handle any encounter with #import in your sass files.
I want to take advantage of this feature to import .css files from a npm package easily, similarly to Browserify.
Here's my sass file. (index.scss)
#import "normalize.css";
When I run this file through Sass and my importer, it should output this (supposed index.css):
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS and IE text size adjust after device orientation change,
* without disabling user zoom.
*/
html {
...
Instead, it outputs this (actual index.css):
#import url(/Users/me/workspace/project/node_modules/normalize.css/normalize.css);
What gives? Why is it that my importer only changes the file path instead of taking the file and combining it with the original file?
Here's my importer and gulp task that runs sass (gulpfile.babel.js):
import gulp from 'gulp';
import sass from 'gulp-sass';
import rename from 'gulp-rename';
import path from 'path';
import fs from 'fs';
let aliases = {};
function importNpmModule(url, file, done) {
// check if the path was already found and cached
if (aliases[url]) {
return done({ file: aliases[url] });
}
// look for modules installed through npm
try {
const basePath = path.resolve('node_modules', url);
const pkgInfo = path.join(basePath, 'package.json');
const info = JSON.parse(fs.readFileSync(pkgInfo));
const newPath = path.join(basePath, info.style);
aliases[url] = newPath; // cache this request
return done({ file: newPath });
} catch(e) {
// If module could not be found, return the original url
aliases[url] = url;
return done({ file:url });
}
}
gulp.task('sassify', () => {
return gulp.src('./index.scss')
.pipe(sass({ importer: inportNpmModule }))
.pipe(rename('index.css'))
.pipe(gulp.dest('public'));
});
EDIT: I also tried using an npm package named sass-module-importer, but it has the exact same problem that I have.

It's not your importer problem. You have this error because there is .css in Sass import. Just omit it.
If you have problems after that may be you should update to last version of node-sass or use some hacks like rename .css to .scss before compiling.

Related

extract 7zip files in Nodejs

I am trying to extract .7z files which is password protected.
In a particular folder path there are some .7z files format. First I have to extract all files in the same directory than I have to do another stuff with this files.
const path = require('path')
const fs = require('fs')
import { extractFull } from 'node-7z-forall';
const dirpath = path.join('C:/MyFolder/DATA')
fs.readdir(dirpath, function(err, files) {
const txtFiles = files.filter(el => path.extname(el) === '.7z')
console.log(txtFiles);
extractFull(txtFiles, 'C:/MyFolder/DATA', { p: 'admin123' } /* 7z options/switches */)
.progress(function (files) {
console.log('Some files are extracted: %s', files);
});
})
I am using node-7z-forall module but it is only working when I change the file format to .js to .mjs. in .mjs file format file extract smoothly .but in .js format it is not working.
error:
import { extractFull } from 'node-7z-forall';
^^^^^^
SyntaxError: Cannot use import statement outside a module
How to handle this error. Is it possible to work with in .js format instead of .mjs format?
I am new in nodejs. Please help!
the reason it errors, it that ".js" indicates a commonjs file which uses require() but a ".mjs" file indicates a module which uses the import syntax.
This is also where the error comes from because you try to use import in a non module.
You can prevent the error by simply importing the package using require():
const { extractFull } = require('node-7z-forall');

Rollup - incorrect path resolution

I want to use the library ebnf from NPM and create a bundle using rollup. Since the ebnf is installed to node_modules I also use the rollup plugin rollup-plugin-node-resolve.
The problem is that ebnf contains the code require('..') which - in my case - is resolved to dist in my case. Thus it seems .. is interpreted relative to the output file instead of being relative to the source file.
This is my rollup.config.js (taken from my test repo jneuendorf/rollup-broken-resolve):
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'cjs'
},
// name: 'MyModule',
plugins: [
resolve(),
commonjs(),
]
}
Is this a problem in rollup-plugin-node-resolve or am I doing something wrong?
Since some of the external libraries needed will still be available only as Common.js modules, you could also convert them to ES-Modules:
"Since most packages in your node_modules folder are probably legacy CommonJS rather than JavaScript modules, you may need to use rollup-plugin-commonjs"
https://github.com/rollup/rollup-plugin-commonjs
"Convert CommonJS modules to ES6, so they can be included in a Rollup bundle"
Just in case someone searching this issue on how to make #rollup/plugin-node-resolve (previously was rollup-plugin-node-resolve) to work with relative path. I just found the solution:
function resolve(file, origin) {
// Your way to resolve local include path
}
function pathResolve(options) {
return {
resolveId: function(file, origin) {
// Your local include path must either starts with `./` or `../`
if (file.startsWith('./') || file.startsWith('../')) {
// Return an absolute include path
return resolve(file, origin);
}
return null; // Continue to the next plugins!
}
};
}
Here is how to combine it with #rollup/plugin-node-resolve:
import {nodeResolve} from '#rollup/plugin-node-resolve';
function pathResolve(options) { /* ... */ }
export default {
// ...
plugins: [pathResolve(), nodeResolve()]
};

Minifined moment.js file with specific locale using Gulp and require.js?

When you get moment.js library from Bower, there are these minified packages:
moment.min.js - without locales
moment-with-locales.min.js - with all locales included (still quite big file)
My goal is to create ONE minified moment.js file that would include my needed locales using Gulp. Here is my Gulp code:
var config = {
//...
moment: 'bower_components/moment/moment.js',
momentLt: 'bower_components/moment/locale/lt.js',
}
gulp.task('vendor', ['clean-vendor', 'bower'], function () {
gulp.src([config.moment, config.momentLt]).
pipe(concat('moment-custom.js')).
pipe(rename({suffix: '.min'})).
pipe(uglify()).
pipe(gulp.dest('vendor/moment'));
});
File is combined and minified successfully. When I try to load this file using require.js, it is loaded successfully
require.config({
paths: {
"jquery": "vendor/jquery/jquery.min",
"moment": "vendor/moment/moment-custom.min",
}
});
(TypeScript)
import $ = require("jquery");
import moment = require("moment");
moment;
(JavaScript)
define(["require", "jquery", "moment"], function (require, $, moment) {
moment;
}
But locale is not working:
moment().locale(); => en
moment().locale('lt');
moment().locale(); => en
My considerations:
Is this my problem moment-custom.min.js file?
Do I load this file correctly using require?
Should I declare another require module for locale exclusively?

Importing Sass through npm

Currently in our Sass files we have something like the following:
#import "../../node_modules/some-module/sass/app";
This is bad, because we're not actually sure of the path: it could be ../node_modules, it could be ../../../../../node_modules, because of how npm installs stuff.
Is there a way in Sass that we can search up until we find node_modules? Or even a proper way of including Sass through npm?
If you are looking for a handy answer in 2017 and are using Webpack, this was the easiest I found.
Suppose your module path is like:
node_modules/some-module/sass/app
Then in your main scss file you can use:
#import "~some-module/sass/app";
Tilde operator shall resolve any import as a module.
As Oncle Tom mentioned, the new version of Sass has this new importer option, where every "import" you do on your Sass file will go first through this method. That means that you can then modify the actual url of this method.
I've used require.resolve to locate the actual module entry file.
Have a look at my gulp task and see if it helps you:
'use strict';
var path = require('path'),
gulp = require('gulp'),
sass = require('gulp-sass');
var aliases = {};
/**
* Will look for .scss|sass files inside the node_modules folder
*/
function npmModule(url, file, done) {
// check if the path was already found and cached
if(aliases[url]) {
return done({ file:aliases[url] });
}
// look for modules installed through npm
try {
var newPath = path.relative('./css', require.resolve(url));
aliases[url] = newPath; // cache this request
return done({ file:newPath });
} catch(e) {
// if your module could not be found, just return the original url
aliases[url] = url;
return done({ file:url });
}
}
gulp.task("style", function() {
return gulp.src('./css/app.scss')
.pipe(sass({ importer:npmModule }))
.pipe(gulp.dest('./css'));
});
Now let's say you installed inuit-normalize using node. You can simply "require" it on your Sass file:
#import "inuit-normalize";
I hope that helps you and others. Because adding relative paths is always a pain in the ass :)
You can add another includePaths to your render options.
Plain example
Snippet based on example from Oncle Tom.
var options = {
file: './sample.scss',
includePaths: [
path.join(__dirname, 'bower_components'), // bower
path.join(__dirname, 'node_modules') // npm
]
};
sass.render(options, function(err, result){
console.log(result.css.toString());
});
That should do. You can include the files from package using #import "my-cool-package/super-grid
Webpack and scss-loader example
{
test: /\.scss$/,
loader: 'style!css!autoprefixer?browsers=last 2 version!sass?outputStyle=expanded&sourceMap=true&sourceMapContents=true&includePaths[]=./node_modules'
},
Notice the last argument, includePaths has to be array. Keep in mind to use right format
You can use a Sass importer function to do so. Cf. https://github.com/sass/node-sass#importer--v200.
The following example illustrates node-sass#3.0.0 with node#0.12.2:
Install the bower dependency:
$ bower install sass-mq
$ npm install sass/node-sass#3.0.0-pre
The Sass file:
#import 'sass-mq/mq';
body {
#include mq($from: mobile) {
color: red;
}
#include mq($until: tablet) {
color: blue;
}
}
The node renderer file:
'use strict';
var sass = require('node-sass');
var path = require('path');
var fs = require('fs');
var options = {
file: './sample.scss',
importer: function bowerModule(url, file, done){
var bowerComponent = url.split(path.sep)[0];
if (bowerComponent !== url) {
fs.access(path.join(__dirname, 'bower_components', bowerComponent), fs.R_OK, function(err){
if (err) {
return done({ file: url });
}
var newUrl = path.join(__dirname, 'bower_components', url);
done({ file: newUrl });
})
}
else {
done({ file: url });
}
}
};
sass.render(options, function(err, result){
if (err) {
console.error(err);
return;
}
console.log(result.css.toString());
});
This one is simple and not recursive. The require.resolve function could help to deal with the tree – or wait until npm#3.0.0 to benefit from the flat dependency tree.
I made the sass-npm module specifically for this.
npm install sass-npm
In your SASS:
// Since node_modules/npm-module-name/style.scss exists, this will be imported.
#import "npm-module-name";
// Since just-a-sass-file isn't an installed npm module, it will be imported as a regular SCSS file.
#import "just-a-sass-file";
I normally use gulp-sass (which has the same 'importer' option as regular SASS)
var gulp = require('gulp'),
sass = require('gulp-sass'),
sassNpm = require('sass-npm')();
Then, in your .pipe(sass()), add the importer as an option:
.pipe(sass({
paths: ['public/scss'],
importer: sassNpm.importer,
}))
For dart-sass and commandline user at 2022, just use the --load-path option:
$ npx sass --load-path=node_modules
Important: the whole node_modules folder contains so much, just set it launch extremely slow in watch mode. Your should only set your package paths, eg:
$npx sass -w --load-path=node_modules/foo --load-path=node_modules/bar/scss
From offical docuumentation of Sass, adding ~ to imports should do the job.
However, for some reason it did'nt work for me, and sass compiler still complains that the module cannot be found.
Hence, I tried another method which worked for me without any issues. Here's the solution:
If you are compiling sass files directly from CLI try this:
sass src/main.scss dist/main.css --load-path=node_modules
If you are using npm and/or webpack for compiling sass files, add something like this to the scripts of package.json:
"scripts": {
...
"build": "sass src/main.scss dist/main.css --load-path=node_modules",
...
}
Then Run:
npm run build
Finally, import your modules like this:
#import "some-module/sass/app";
To wrap it up, adding --load-path=node_modules flag solved the issue permanently. For more information you can check:
sass --help

How do I get the path to the current script with Node.js?

How would I get the path to the script in Node.js?
I know there's process.cwd, but that only refers to the directory where the script was called, not of the script itself. For instance, say I'm in /home/kyle/ and I run the following command:
node /home/kyle/some/dir/file.js
If I call process.cwd(), I get /home/kyle/, not /home/kyle/some/dir/. Is there a way to get that directory?
I found it after looking through the documentation again. What I was looking for were the __filename and __dirname module-level variables.
__filename is the file name of the current module. This is the resolved absolute path of the current module file. (ex:/home/kyle/some/dir/file.js)
__dirname is the directory name of the current module. (ex:/home/kyle/some/dir)
So basically you can do this:
fs.readFile(path.resolve(__dirname, 'settings.json'), 'UTF-8', callback);
Use resolve() instead of concatenating with '/' or '\' else you will run into cross-platform issues.
Note: __dirname is the local path of the module or included script. If you are writing a plugin which needs to know the path of the main script it is:
require.main.filename
or, to just get the folder name:
require('path').dirname(require.main.filename)
Use __dirname!!
__dirname
The directory name of the current module. This the same as the path.dirname() of the __filename.
Example: running node example.js from /Users/mjr
console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr
https://nodejs.org/api/modules.html#modules_dirname
For ESModules you would want to use:
import.meta.url
This command returns the current directory:
var currentPath = process.cwd();
For example, to use the path to read the file:
var fs = require('fs');
fs.readFile(process.cwd() + "\\text.txt", function(err, data)
{
if(err)
console.log(err)
else
console.log(data.toString());
});
Node.js 10 supports ECMAScript modules, where __dirname and __filename are no longer available.
Then to get the path to the current ES module one has to use:
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
And for the directory containing the current module:
import { dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
When it comes to the main script it's as simple as:
process.argv[1]
From the Node.js documentation:
process.argv
An array containing the command line arguments. The first element will be 'node', the second element will be the path to the JavaScript file. The next elements will be any additional command line arguments.
If you need to know the path of a module file then use __filename.
var settings =
JSON.parse(
require('fs').readFileSync(
require('path').resolve(
__dirname,
'settings.json'),
'utf8'));
Every Node.js program has some global variables in its environment, which represents some information about your process and one of it is __dirname.
I know this is pretty old, and the original question I was responding to is marked as duplicate and directed here, but I ran into an issue trying to get jasmine-reporters to work and didn't like the idea that I had to downgrade in order for it to work. I found out that jasmine-reporters wasn't resolving the savePath correctly and was actually putting the reports folder output in jasmine-reporters directory instead of the root directory of where I ran gulp. In order to make this work correctly I ended up using process.env.INIT_CWD to get the initial Current Working Directory which should be the directory where you ran gulp. Hope this helps someone.
var reporters = require('jasmine-reporters');
var junitReporter = new reporters.JUnitXmlReporter({
savePath: process.env.INIT_CWD + '/report/e2e/',
consolidateAll: true,
captureStdout: true
});
Use the basename method of the path module:
var path = require('path');
var filename = path.basename(__filename);
console.log(filename);
Here is the documentation the above example is taken from.
As Dan pointed out, Node is working on ECMAScript modules with the "--experimental-modules" flag. Node 12 still supports __dirname and __filename as above.
If you are using the --experimental-modules flag, there is an alternative approach.
The alternative is to get the path to the current ES module:
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(new URL(import.meta.url));
And for the directory containing the current module:
import { fileURLToPath } from 'url';
import path from 'path';
const __dirname = path.dirname(fileURLToPath(new URL(import.meta.url)));
You can use process.env.PWD to get the current app folder path.
NodeJS exposes a global variable called __dirname.
__dirname returns the full path of the folder where the JavaScript file resides.
So, as an example, for Windows, if we create a script file with the following line:
console.log(__dirname);
And run that script using:
node ./innerFolder1/innerFolder2/innerFolder3/index.js
The output will be:
C:\Users...<project-directory>\innerFolder1\innerFolder2\innerFolder3
If you are using pkg to package your app, you'll find useful this expression:
appDirectory = require('path').dirname(process.pkg ? process.execPath : (require.main ? require.main.filename : process.argv[0]));
process.pkg tells if the app has been packaged by pkg.
process.execPath holds the full path of the executable, which is /usr/bin/node or similar for direct invocations of scripts (node test.js), or the packaged app.
require.main.filename holds the full path of the main script, but it's empty when Node runs in interactive mode.
__dirname holds the full path of the current script, so I'm not using it (although it may be what OP asks; then better use appDirectory = process.pkg ? require('path').dirname(process.execPath) : (__dirname || require('path').dirname(process.argv[0])); noting that in interactive mode __dirname is empty.
For interactive mode, use either process.argv[0] to get the path to the Node executable or process.cwd() to get the current directory.
index.js within any folder containing modules to export
const entries = {};
for (const aFile of require('fs').readdirSync(__dirname, { withFileTypes: true }).filter(ent => ent.isFile() && ent.name !== 'index.js')) {
const [ name, suffix ] = aFile.name.split('.');
entries[name] = require(`./${aFile.name}`);
}
module.exports = entries;
This will find all files in the root of the current directory, require and export every file present with the same export name as the filename stem.
If you want something more like $0 in a shell script, try this:
var path = require('path');
var command = getCurrentScriptPath();
console.log(`Usage: ${command} <foo> <bar>`);
function getCurrentScriptPath () {
// Relative path from current working directory to the location of this script
var pathToScript = path.relative(process.cwd(), __filename);
// Check if current working dir is the same as the script
if (process.cwd() === __dirname) {
// E.g. "./foobar.js"
return '.' + path.sep + pathToScript;
} else {
// E.g. "foo/bar/baz.js"
return pathToScript;
}
}

Resources