How to get known Windows folder path with Node - node.js

I need to access per-machine configuration data in my Node application running on Windows. I've found this documentation for how to find the location:
Where Should I Store my Data and Configuration Files if I Target Multiple OS Versions?
So, in my case, I would like to get the path for CSIDL_COMMON_APPDATA (or FOLDERID_ProgramData). However, the examples are all in C, and I would prefer to not have to write a C extension for this.
Is there any other way to access these paths from Node, or should I just hardcode them?

After doing a bit of research, I've found that it's possible to call the relevant Windows API proc. (SHGetKnownFolderPath) to get these folder locations, see docs at: https://msdn.microsoft.com/en-us/library/windows/desktop/bb762188(v=vs.85).aspx.
We call the APi using the FFI npm module: https://www.npmjs.com/package/ffi.
It is possible to find the GUIDs for any known folder here:
https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457(v=vs.85).aspx
Here is a script that finds the location of several common folders,
some of the code is a little hacky, but is easily cleaned up.
const ffi = require('ffi');
const ref = require('ref');
const shell32 = new ffi.Library('Shell32', {
SHGetKnownFolderPath: ['int', [ ref.refType('void'), 'int', ref.refType('void'), ref.refType(ref.refType("char"))]]
});
function parseGUID(guidStr) {
var fields = guidStr.split('-');
var a1 = [];
for(var i = 0; i < fields.length; i++) {
var a2 = [...Buffer.from(fields[i], 'hex')];
if (i < 3) a2 = a2.reverse();
a1 = a1.concat(a2);
}
return new Buffer(a1);
}
function getWindowsKnownFolderPath(pathGUID) {
let guidPtr = parseGUID(pathGUID);
guidPtr.type = ref.types.void;
let pathPtr = ref.alloc(ref.refType(ref.refType("void")));
let status = shell32.SHGetKnownFolderPath(guidPtr, 0, ref.NULL, pathPtr);
if (status !== 0) {
return "Error occurred getting path: " + status;
}
let pathStr = ref.readPointer(pathPtr, 0, 200);
return pathStr.toString('ucs2').substring(0, (pathStr.indexOf('\0\0') + 1)/2);
}
// See this link for a complete list: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457(v=vs.85).aspx
const WindowsKnownFolders = {
ProgramData: "62AB5D82-FDC1-4DC3-A9DD-070D1D495D97",
Windows: "F38BF404-1D43-42F2-9305-67DE0B28FC23",
ProgramFiles: "905E63B6-C1BF-494E-B29C-65B732D3D21A",
Documents: "FDD39AD0-238F-46AF-ADB4-6C85480369C7"
}
// Enumerate common folders.
for(let [k,v] of Object.entries(WindowsKnownFolders)) {
console.log(`${k}: `, getWindowsKnownFolderPath(v));
}

Related

Importing customized module into renderer.js in Electron app

I have a customized module that carries some functions that I would like to use in renderer.js. I tried the following ways of importing but it does not work (in fact, it causes some of the other functions in renderer.js to not execute as well.
const supp = require('./supp.js')
const supp = require('./supp')
const supp = require('supp.js')
const supp = require(path.join(__dirname, '/supp.js')
import 'supp'
supp.js sits in the same folder level as renderer.js and main.js. If someone could advise? Thanks.
Update: below is all the code in file supp.js
const pinOneInGrp = (itemID, grpName, itemColor, grpColor) => {
let item = document.getElementById(itemID);
let grpItems = document.getElementsByClassName(grpName);
for(var i = 0; i < grpItems.length;i++) {
grpItems[i].style.background-color = grpColor
}
item.style.background-color = itemColor;
}
module.exports = {
pinOneInGrp
}
If one of the import or require lines above is included at the top of renderer.js, none of the subsequent actions in renderer.js is executed. For example, there is a ipc.send() and ipc.on() action right after the import / require line. These two do not send (and hence, receive back) from the main.js.
The code you've posted contains a typo. The error it's throwing (which you most probably can't see) is a SyntaxError, because you cannot subtract color (which is undefined) from grpItems[i].style.background and then assign to it. Thus, you simply have to correct your for-loo from
for (var i = 0; i < grpItems.length; i++) {
grpItems[i].style.background-color = grpColor;
}
to
for (var i = 0; i < grpItems.length; i++) {
grpItems[i].style.backgroundColor = grpColor;
}
(And the same goes for the style assignment right below the for-loop!)
Note that all CSS properties which are spelt with a hyphen when in a stylesheet must use camelCase as they otherwise would denote a subtraction, which is causing your problems. Also, this behaviour is explained in Mozilla's Developer Network Web API reference, specifically under "Setting styles".

Passed function is not well-serializable

I am using Puppeteer to scrape the web from a file template that contains the data of an order.
For this, I am using a puppeteer evaluation function, which works correctly while the file is in .js
However, when the "pkg" package is used to compile the .exe file or evaluate to execute and initiate a return or error: "The passed function is not quite serializable!"
Below is the code:
const dados = {name: 'foo', year: 1}
await page.evaluate(dados => {
let dom = document.querySelector('body');
const tags = Object.keys(dados);
for (let i = 0; i < tags.length; i++) {
const tag = tags[i];
dom.innerHTML = dom.innerHTML.split(`{{${tag}}}`).join(dados[tag]);
}
}, dados);
I try to add --public argument with pkg.
Like: pkg start.js -t node14-win-x64 --public
Then I can freely use ElementHandle.evaluate( (elem)=> elem.textContent );
With the manual of pkg, the --public means that : speed up and disclose the sources of top-level project.
BTW
To fixup cannot find chrome binary file
browser = await puppeteer.launch({
executablePath: "node_modules/puppeteer/.local-chromium/win64-782078/chrome-win/chrome.exe"
});
(The path above can set anywhere as you like.)
To fixup start.exe cannot run
Sometime the output binary exe cannot executable. It
always popup a new cmd prompt window when we enter start.exe. (or just double click.)
Just delete the output exe then rerun pkg
Check the code whether is runnable with node start.js or not
The easiest solution for me is to wrap it with eval() :
async getText(selector: string) {
await this.page.waitForSelector(selector);
let text = await eval(`this.page.$eval('${selector}', el => el.textContent)`)
return text;
}
or this:
await eval(`this.page.evaluate(
(selector) => { (document.querySelector(selector).value = ''); },
selector);`);
I had this exact issue relating to puppeteer and pkg. For some reason pkg doesn't correctly interpret the contents of the callback. The solution that worked for me was to pass a string to evaluate rather than a function:
Change:
const dados = {name: 'foo', year: 1}
await page.evaluate(dados => {
let dom = document.querySelector('body');
const tags = Object.keys(dados);
for (let i = 0; i < tags.length; i++) {
const tag = tags[i];
dom.innerHTML = dom.innerHTML.split(`{{${tag}}}`).join(dados[tag]);
}
}, dados);
to
await page.evaluate(`
(() => {
const dados = {name: 'foo', year: 1};
let dom = document.querySelector('body');
const tags = Object.keys(dados);
for (let i = 0; i < tags.length; i++) {
const tag = tags[i];
dom.innerHTML = dom.innerHTML.split(`{{${tag}}}`).join(dados[tag]);
}
// return dom to do something with the data in node
return dom.innerHTML
})()`);
This answer on github suggests an alternative solution - using the pkg api to inject the callback at compile time, however it didn't work for me.

How to include a static mapping file as part of a nodeJS module?

Assume that I have a nodeJS module whose index.js is as below
module.exports = function() {
var map = {},
CSV_FILE = "./input.csv",
_loadCSV = function() {
var fs = require('fs'),
parse = require('csv-parse'),
rawCSVData = fs.readFileSync(CSV_FILE).toString(),
i,
item;
parse(rawCSVData, {columns: true}, function(err, data) {
for (i = 0; i < data.length; i++) {
item = data[i];
map[item.key] = item.value;
}
});
},
_init = function() {
_loadCSV();
};
// Init
_init();
// Public
return {
/**
* getValue
*/
getValue: function(key) {
return map[key];
}
};
};
Now, everything works fine if I test locally. However, when I install this module in another project I get below error.
fs.js:549 return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
^
Error: ENOENT: no such file or directory, open 'input.csv' at Error (native)
Is it possible to include a static mapping file as part of a nodeJS module that is used in module initialization?
Your problem is this line CSV_FILE = "./input.csv". It works locally because the script you're executing (index.js) is in the same directory as the input.csv-file. However, when you install it as a dependency, the input.csv-file is actually somewhere in ./node_modules/your-module/input.csv, hence your new index.js can't see any ./input.csv-file since it's not located in the same directory as the calling script.
There are two ways to solve this, the first one being the smartest in my opinion.
Do not distribute the input.csv-file. This is a very bad approach to building modules, and you should rather change your code so that your module accepts a path to a .csv-file that it loads. However your module may need static data, but in those cases it's smarter to just convert it to a JavaScript Object and include it directly.
Simply change one line of code,
from CSV_FILE = "./input.csv"
to CSV_FILE = __dirname + "/input.csv"
See documentation for __dirname

count images in local folder WinRT

I am new in WinRT ,
Is it possible to count number of images in Assest Folder . So that we can do few operation over it .
Presently i am making a small app.
Thanks in advance
try,
var folder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync("Assets");
var files = await folder.GetFilesAsync();
and get the files count as
var filesCount = files.Count;
and you can get the files count of specific extension as
var pngFileCount = files.Where(file => file.FileType == ".png").Select(f => f).ToList().Count;
Hope this will helps you :)
Here you go.
var folder = await Package.Current.InstalledLocation.GetFolderAsync("Assets");
var options = new QueryOptions { FileTypeFilter = { ".png", ".jpg" } };
var query = folder.CreateFileQueryWithOptions(options);
var files = await query.GetFilesAsync();
foreach (var file in files)
{
// TODO
}
I want to point out that this works in Windows but not Windows Phone. Not yet.
Best is luck.

Get latest files inside a folder

i have a folder stucture like
var/
testfolder1/
myfile.rb
data.rb
testfolder2/
home.rb
sub.rb
sample.rb
rute.rb
inside var folder contains subfolders(testfolder1,testfolder2) and some files(sample.rb,rute.rb)
in the following code returing a josn object that contains folders and files inside the var folder
like
{
'0': ['sample.rb', 'rute.rb'],
testfolder1: ['myfile.rb',
'data.rb',
],
testfolder2: ['home.rb',
'sub.rb',
]
}
code
var scriptsWithGroup = {};
fs.readdir('/home/var/', function(err, subfolder) {
if(err) return context.sendJson({}, 200);
var scripts = [];
for (var j = 0; j < subfolder.length; j++) {
var scriptsInFolder = [];
if(fs.lstatSync(scriptPath + subfolder[j]).isDirectory()) {
fs.readdirSync(scriptPath + subfolder[j]).forEach(function(file) {
if (file.substr(file.length - 3) == '.rb')
scriptsInFolder.push(file);
});
scriptsWithGroup[subfolder[j]] = scriptsInFolder;
} else {
if (subfolder[j].substr(subfolder[j].length - 3) == '.rb')
scripts.push(subfolder[j]);
}
}
scriptsWithGroup["0"] = scripts;
console.log(scriptsWithGroup)
context.sendJson(scriptsWithGroup, 200);
});
What i need is i want to return the latest modified or created files.here i only use 2 files inside folders it contains lots of files.so i want to return latest created ones
I'm going to assume here that you want only the most recent two files. If you actually want them all, just sorted, just remove the slice portion of this:
scriptsInFolder = scriptsInFolder.sort(function(a, b) {
// or mtime, if you're only wanting file changes and not file attribute changes
var time1 = fs.statSync(a).ctime;
var time2 = fs.statSync(b).ctime;
if (time1 < time2) return -1;
if (time1 > time2) return 1;
return 0;
}).slice(0, 2);
I'll add, however, that it's typically considered best practice not to use to the synchronize fs methods (e.g. fs.statSync). If you're able to install async, that would be a good alternative approach.
const fs = require('fs');
const path = require('path');
const getMostRecentFile = (dir) => {
const files = orderReccentFiles(dir);
return files.length ? files[0] : undefined;
};
const orderReccentFiles = (dir) => {
return fs.readdirSync(dir)
.filter(file => fs.lstatSync(path.join(dir, file)).isFile())
.map(file => ({ file, mtime: fs.lstatSync(path.join(dir, file)).mtime }))
.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
};
const dirPath = '<PATH>';
console.log(getMostRecentFile(dirPath));

Resources