Node.js - How to grab the class names from a .scss file - node.js

I wanted to ask if anyone knows of a good solution for how to use node.js to look in a .scss file and grab all the classes listed and to then put them in either an object or an array?

The thing with this is that you are going to need the sass folder to be available to you server, this is not a recommended practice since you only publish the css compiled file, there is no need to also publish the dev assets.
However if you do so, you will need to read .scss file using node and from there use a regex to match the .class strings inside the file.
This will make the reading of the file:
var fs = require('fs');
function readSassFile () {
fs.readFile('./public/scss/components/_styles.scss', 'utf8', function (err, data) {
if (err) {
console.log(err);
return;
}
regexArray(data);
});
}
As you can see, at the end if the readFile retrieves the file with success, I'm calling a function regexArray() and sending the data of the file loaded.
In the regexArray function you need to define a regex to evaluate the string of the file loaded.
function regexArray (data) {
var re = /\.\S*/g;
var m;
var classArray = [];
while ((m = re.exec(data)) !== null) {
if (m.index === re.lastIndex) {
re.lastIndex++;
}
classArray.push(m[0]);
}
console.log(classArray);
}
the var re is the regular expression matching any string starting with a . and ending with a non-whitespace character which will match your css class names.
then we evaluate the m variable when is different from null and store the results in the array classArray, then you can log it to see the results.
I made the test with the path that is in the fs.readFile method, you can change it for you own path.

Related

How can I read a file in node js, find all instances of a function and then extract each function's argument?

I'm trying to write a node script that identifies unused translation strings in my React project.
First, I want to get a list of all the translations that are used. To do this, I am getting a list of each JS file in my /src/components folder and then reading the file.
My translation strings look like this: t('some.translation.key'), so basically, I want to identify each instance of t('...') using RegEx and then get the key in between those parentheses (i.e. "some.translation.key"). From there, I should be able to compare the keys to the ones in my translation JSON file and remove the ones that aren't being used.
unused.js
const path = require('path');
const fs = require('fs');
let files = [];
// https://stackoverflow.com/a/63111390/2262604
function getFiles(dir) {
fs.readdirSync(dir).forEach(file => {
const absolute = path.join(dir, file);
if (fs.statSync(absolute).isDirectory()) {
getFiles(absolute);
} else {
if (absolute.includes('.js')) {
files.push(absolute);
}
}
});
return files;
}
function getTranslations() {
const pathComponents = path.join(__dirname, '../../src/components');
// get all js files in components directory
const files = getFiles(pathComponents);
const translationKeys = [];
// for each js file
for(let i = 0; i < files.length; i++) {
// read contents of file
const contents = fs.readFileSync(files[i]).toString();
// search contents for all instances of t('...')
// and get the key between the parentheses
}
}
getTranslations();
How can I use RegEx to find all instances of t('...') in contents and then extract the ... string between the parentheses?
Yes, you could use a regular expression:
for (const [, str] of contents.matchAll(/\bt\(['"](.*?)['"]\)/g)) {
console.log('t called with string argument:', str)
}
However, with regular expressions the problem will be that they don't understand the code and would cause trouble with matching strings that contain ( ) or \' themselves, have issues with concatenated strings or extra whitespace, etc., and you'd then also get the contents literally, including possible escape sequences.
A more robust way would be to create an AST (abstract syntax tree) from the code and look for calls to t in it.
A popular AST parser would be acorn. There is also the supplementary module acorn-walk that helps walking through the whole syntax tree without building your own recursive algorithm.
import acorn from 'acorn'
import walk from 'acorn-walk'
// Example
const contents = "function a () { if (123) { t('hello') } return t('world') }"
// The arguments to acorn.parse would have to be adjusted based
// on what kind of syntax your files can use.
const result = acorn.parse(contents, {ecmaVersion: 2020})
walk.full(result, node => {
if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 't') {
if (node.arguments.length === 1 && node.arguments[0].type === 'Literal' && typeof node.arguments[0].value === 'string') {
// This is for the case `t` is called with a single string
// literal as argument.
console.log('t called with string argument:', node.arguments[0].value)
} else {
// In case you have things like template literals as well,
// or multiple arguments, you'd need to handle them here too.
console.log('t called with unknown arguments:', node.arguments)
}
}
})
// Will output:
// t called with string argument: hello
// t called with string argument: world

modify nodejs require() to search for .min.js

O/S is ubuntu 16, node version is 4.2.6.
I have source / development code and run / distribution code, the source.js files are minified and mangled to create equivalent source.min.js files, and I would like for node js require to automatically search for .min.js files as well as .js files.
But as I have a lot of files, I would prefer not to have to go through every require in every file and instead modify the built-in require() function.
This is a very simple implementation of a stand alone function, but how can I modify the built-in function to behave the same way ?
function require(file){
try{return require(file)}
catch(e){return require(file+='.min.js')}
}
You can achieve this by modifying prototype function require of Module class and apply it globally
Here is how you can do it :
var pathModule = require('path');
var assert = require('assert').ok;
module.constructor.prototype.require = function (path) {
var self = this;
assert(typeof path === 'string', 'path must be a string');
assert(path, 'missing path');
try {
return self.constructor._load(path, self);
} catch (err) {
// if module not found, we have nothing to do, simply throw it back.
if (err.code === 'MODULE_NOT_FOUND') {
throw err;
}
// resolve the path to get absolute path
path = pathModule.resolve(__dirname, path+".min.js")
// Write to log or whatever
console.log('Error in file: ' + path);
}
}

check the type of files is present or not using nodejs

I want to find the type of files which is present or not, I am using nodejs, fs. Here is my code
var location = '**/*.js';
log(fs.statSync(location).isFile());
which always returns the error.
Error: ENOENT, no such file or directory '**/*.js'
How to I find the files is present or not. Thanks in Advance.
node doesn't have support for globbing (**/*.js) built-in. You'll need to either recursively walk the directories and iterate over the array of file names to find the file types you want, or use something like node-glob.
Using recusrive-readdir-sync
var recursiveReadSync = require('recursive-readdir-sync'),
files;
files = recursiveReadSync('./');
files.forEach(function (fileName) {
if (fileName.search(/\.js$/g) !== -1) {
console.log("Found a *.js file");
}
});
Using node-glob:
var glob = require("glob")
glob("**/*.js", function (er, files) {
files.forEach(function (fileName) {
if (fileName.search(/\.js$/g) !== -1) {
console.log("Found a *.js file");
}
});
node.js dose not support "glob" wildcards by default. You can use external package like this one

Using Grunt to Replace Text in a File

I'm trying to get Grunt to replace a path reference and I'm not sure what I'm doing wrong. This looks like it should work. Essentially, I'm copying a Bootstrap file up a directory and changing the #import paths, so I'm just trying to replace 'bootstrap/' with the new destination path 'MY/NEW/DEST/PATH/bootstrap'. I don't want to use a module for something as straight forward as this, seems needless. Everything works but the replace.
var destFilePath = path.join(basePath, file);
// Does the file already exist?
if (!grunt.file.exists(destFilePath)) {
// Copy Bootstrap source #import file to destination
grunt.file.copy(
// Node API join to keep this cross-platform
path.join(basePath, 'bootstrap/_bootstrap.scss'),
destFilePath
);
// Require node filesystem module, since not a global
var fs = require('fs');
// Replace #import paths to be relative to new destination
fs.readFile(destFilePath, 'utf8', function(err, data) {
// Check for any errs during read
if (err) {
return grunt.log.write(err);
}
var result = data.replace('/bootstrap\//g', 'bootstrap/bootstrap/');
fs.writeFile(destFilePath, result, 'utf8', function(err) {
return grunt.log.write(err);
});
});
}
You wrapped your regex in quotes - don't do that and it should work fine:
var result = data.replace(/bootstrap\//g, 'bootstrap/bootstrap/');

How to ignore hidden files in fs.readdir result

In node application i need to get all the files in the directory except that hidden files.
I have tried fs.readdir but it displaying hidden files also.
Using the regex from this answer, this simple solution removes all hidden files from the result:
fs.readdir('/path/to/directory', (err, list) => {
list = list.filter(item => !(/(^|\/)\.[^\/\.]/g).test(item));
// Your code
});
You can use:
fs.readdir('/path/to/directory', function(err, list) {
list.forEach(function (filename) {
if(! /^\..*/.test(filename)) {
// display files
}
});
});
The promise way
const fs = require('fs').promises;
const readdir = path => {
return fs
.readdir(path)
.then(list => list.filter(item => !/(^|\/)\.[^/.]/g.test(item)));
};
I use junk package to ignore hidden files.
var fs = require('fs');
var junk = require('junk');
fs.readdir('path', function (err, files) {
console.log(files.filter(junk.not));
});
Using a regular expression like the solution provided may remove some filesystems but not all files. And using the "Junk" module is using regular expressions on some defined system filenames. So the solution is simply to write a personal script managing the different cases by adding cases as they are discovered.

Resources