Permission denied when running fs-plus module - node.js

I'm writing package for Atom editor.
Can anyone help with persmission denied error I get when using fs-plus makeTree method in my package when running it on Mac? No errors on Windows.
I use fs-plus, the same module that Atom uses in its tree-view package (and tree-view works on Mac).
UPD: Adding screenshot, and some code:
New Folder option shown on the picture is implemented using the same module - fs-plus, and it works on Mac. I use just the same module, and the same method (fs-plus.makeTree) to create directory, but my implementation fails with Permission denied error.
My code:
import util from 'util';
import { sep } from 'path';
import fs from 'fs-plus';
const makeTreeAsync = util.promisify(fs.makeTree);
createTutorial(data) {
const { initialPath } = this;
const { fileName } = data;
const filePath = `${initialPath}${sep}${fileName}${sep}${fileName}.md`;
return makeTreeAsync(fileName)
.then(() => writeFileAsync(filePath, this.getContent(data)))
.then(() => filePath);
},
code from tree-view package:
path = require 'path'
fs = require 'fs-plus'
onConfirm: (newPath) ->
newPath = newPath.replace(/\s+$/, '') # Remove trailing whitespace
endsWithDirectorySeparator = newPath[newPath.length - 1] is path.sep
unless path.isAbsolute(newPath)
// some path preprocessing
return unless newPath
try
if fs.existsSync(newPath)
#showError("'#{newPath}' already exists.")
else if #isCreatingFile
// some code, we are not interested in as we 're creating directory
else
fs.makeTreeSync(newPath)
#emitter.emit('did-create-directory', newPath)
#cancel()
catch error
#showError("#{error.message}.")
Important note: my package is installed manually (copied to ~/<username>/.atom/packages) and then run npm i

After some time of debugging, I have a solution. I tried to use native fs.mkdir with no luck, but after I added mode as the second argument - it worked.
const _0777 = parseInt('0777', 8);
const mode = _0777 & (~process.umask());
fs.mkdir(<folderPath>, mode, () => {});
Hope, this will help someone.

Related

How to check node_modules folder is there or not in my angular project

I am trying to check node_modules folder is there or not in my angular2 project using nodejs but facing struggle. I am new in nodejs So I do not know how to do it. Anybody can help me?
Note : ps -> project name
You can also check angular 2 project foldere structure: https://tutorialslink.com/Article_img/Blog_image/689d5f69-71b5-462e-8809-121fa9fd96bc.png
const fullPath = path.dirname("c://mamp/htdocs/projects/ps/src/app/app.component.html");
const regexResp = /^(.*?)node_modules/.exec(fullPath);
const nodemodulesPath = regexResp ? regexResp[1] : fullPath;
console.log(nodemodulesPath );
Try the fs(filesystem) module of nodejs
const fs = require('fs');
// fs.statSync(path) method returns the instance of fs.Stats
try {
const stats = fs.statSync("c://mamp/htdocs/projects/ps/node_modules");
// stats.isDirectory() returns true if file path is Directory, otherwise returns false.
console.log('is directory ? ' + stats.isDirectory());
} catch(e) {
console.log("Could not find node modules")
}
Read more about this module here https://nodejs.org/api/fs.html

Can you pull in excludes/includes options in Typescript Compiler API?

I'm trying use the Typescript API to do some custom compilation (using plugins, etc.).
When compiling with the command line, typescript will automatically take into account not only the compilerOptions, but also the excludes key, as well as the types key (among other things).
The Typescript API documentation is almost non-existent, but I've tried manually doing a deep search and excluding these files from the total file list passed into the createProgram method. However, this has taken a toll on our build performance, and I think the Typescript compiler has a much more efficient solution.
Here's what I have in my compile.ts file.
import ts from 'typescript';
import keysTransformer from 'ts-transformer-keys/transformer';
import tsConfig from './tsconfig.json';
import fs from 'fs';
const deepFindTsFiles = (dir: string, fileList?: string[]) => {
dir = dir.endsWith('/') ? dir : dir + '/'
const files = fs.readdirSync(dir);
fileList = fileList || [];
files.forEach((file) => {
console.log('file', dir + file);
console.log(tsConfig.exclude.includes(dir + file));
if (fs.statSync(dir + file).isDirectory() && !tsConfig.exclude.includes(dir + file)) {
fileList = deepFindTsFiles(dir + file + '/', fileList);
}
else if (!tsConfig.exclude.includes(dir + file) && file.split('.')[1].includes('ts')) {
fileList!.push(dir + file);
}
});
return fileList;
};
const fileList = deepFindTsFiles(tsConfig.compilerOptions.rootDir);
// the compiler options parsed from tsconfig.json
const { options } = ts.parseJsonConfigFileContent(
tsConfig,
ts.sys,
__dirname
);
// createProgram doesn't seem to have an option for excludes/includes/types
const program = ts.createProgram(fileList, options);
const transformers = {
before: [keysTransformer(program)],
after: []
};
program.emit(undefined, undefined, undefined, false, transformers);
The code above properly compiles my source code and applies the proper plugins, however it's much slower than using the command line tools.
What's passed into the createProgram method is just the compilerOptions object in my tsconfig.json file, not the files to exclude. It does not seem like the createProgram method takes any arguments like this.
But, surely there is? Right? I have to imagine that this is possible. But, the documentation is severely lacking
I managed to figure out how to get the list of fileNames using the tsconfig.json and the TS compiler API.
const configPath = ts.findConfigFile(
"./", /*searchPath*/
ts.sys.fileExists,
"tsconfig.json"
);
if (!configPath) throw new Error("Could not find a valid 'tsconfig.json'.");
const configFile = ts.readJsonConfigFile(configPath, ts.sys.readFile);
const { fileNames } = ts.parseJsonSourceFileConfigFileContent(
configFile,
ts.sys,
'./'
);
console.log(fileNames)
This takes into account the includes and excludes specified in the tsconfig.json.
This might not be the best way to do this but I have been struggling to figure this out for a while.

Alternative for __dirname in Node.js when using ES6 modules

I use the flag --experimental-modules when running my Node application in order to use ES6 modules.
However when I use this flag the metavariable __dirname is not available. Is there an alternative way to get the same string that is stored in __dirname that is compatible with this mode?
As of Node.js 10.12 there's an alternative that doesn't require creating multiple files and handles special characters in filenames across platforms:
import { dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
The most standardized way in 2021
import { URL } from 'url'; // in Browser, the URL in native accessible on window
const __filename = new URL('', import.meta.url).pathname;
// Will contain trailing slash
const __dirname = new URL('.', import.meta.url).pathname;
And forget about join to create paths from the current file, just use the URL
const pathToAdjacentFooFile = new URL('./foo.txt', import.meta.url).pathname;
const pathToUpperBarFile = new URL('../bar.json', import.meta.url).pathname;
For Node 10.12 +...
Assuming you are working from a module, this solution should work, and also gives you __filename support as well
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
The nice thing is that you are also only two lines of code away from supporting require() for CommonJS modules. For that you would add:
import { createRequireFromPath } from 'module';
const require = createRequireFromPath(__filename);
In most cases, using what is native to Node.js (with ES Modules), not external resources, the use of __filename and __dirname for most cases can be totally unnecessary. Most (if not all) of the native methods for reading (streaming) supports the new URL + import.meta.url, exactly as the official documentation itself suggests:
No __filename or __dirname
No JSON Module Loading
No require.resolve
As you can see in the description of the methods, the path parameter shows the supported formats, and in them include the <URL>, examples:
Method
path param supports
fs.readFile(path[, options], callback)
<string>, <Buffer>, <URL>, <integer>
fs.readFileSync(path[, options])
<string>, <Buffer>, <URL>, <integer>
fs.readdir(path[, options], callback)
<string>, <Buffer>, <URL>
fs.readdirSync(path[, options])
<string>, <Buffer>, <URL>, <integer>
fsPromises.readdir(path[, options])
<string>, <Buffer>, <URL>
fsPromises.readFile(path[, options])
<string>, <Buffer>, <URL>, <FileHandle>
So with new URL('<path or file>', import.meta.url) it solves and you don't need to be treating strings and creating variables to be concatenated later.
Examples:
See how it is possible to read a file at the same level as the script without needing __filename or any workaround:
import { readFileSync } from 'fs';
const output = readFileSync(new URL('./foo.txt', import.meta.url));
console.log(output.toString());
List all files in the script directory:
import { readdirSync } from 'fs';
readdirSync(new URL('./', import.meta.url)).forEach((dirContent) => {
console.log(dirContent);
});
Note: In the examples I used the synchronous functions just to make it easier to copy and execute.
If the intention is to make a "own log" (or something similar) that will depend on third parties, it is worth some things done manually, but within the language and Node.js this is not necessary, with ESMODULES it is totally possible not to depend on either __filename and neither __dirname, since native resources with new URL with already solve it.
Note that if you are interested in using something like require at strategic times and need the absolute path from the main script, you can use module.createRequire(filename) (Node.js v12.2.0 + only) combined with import.meta.url to load scripts at levels other than the current script level, as this already helps to avoid the need for __dirname, an example using import.meta.url with module.createRequire:
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
// foo-bar.js is a CommonJS module.
const fooBar = require('./foo-bar');
fooBar();
Source from foo-bar.js:
module.exports = () => {
console.log('hello world!');
};
Which is similar to using without "ECMAScript modules":
const fooBar = require('./foo-bar');
There have been proposals about exposing these variables through import.meta, but for now, you need a hacky workaround that I found here:
// expose.js
module.exports = {__dirname};
// use.mjs
import expose from './expose.js';
const {__dirname} = expose;
I used:
import path from 'path';
const __dirname = path.resolve(path.dirname(decodeURI(new URL(import.meta.url).pathname)));
decodeURI was important: used spaces and other stuff within the path on my test system.
path.resolve() handles relative urls.
edit:
fix to support windows (/C:/... => C:/...):
import path from 'path';
const __dirname = (() => {let x = path.dirname(decodeURI(new URL(import.meta.url).pathname)); return path.resolve( (process.platform == "win32") ? x.substr(1) : x ); })();
I made this module es-dirname that will return the current script dirname.
import dirname from 'es-dirname'
console.log(dirname())
It works both in CommonJs scripts and in ES Modules both on Windows and Linux.
Open an issue there if have an error as the script has been working so far in my projects but it might fail in some other cases. For this reason do not use it in a production environment. And this is a temporary solution as I am sure the Node.js team will release a robust way to do it in a near future.
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// do not use the following code which is bad for CJK characters
const __filename = new URL('', import.meta.url).pathname;
import path from 'path';
const __dirname = path.join(path.dirname(decodeURI(new URL(import.meta.url).pathname))).replace(/^\\([A-Z]:\\)/, "$1");
This code also works on Windows. (the replacement is safe on other platforms, since path.join returns back-slash separators only on Windows)
Since other answers, while useful, don't cover both cross-platform cases (Windows POSIX) and/or path resolution other than the __dirname or __filename and it's kind of verbose to repeat this kind of code everywhere:
import { dirname, join } from 'path'
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const somePath = join(__dirname, '../some-dir-or-some-file')
I just published a NPM package called esm-path to help with this kind of recurring task, hoping it can also be useful to others.
It's documented but here how to use it:
import { getAbsolutePath } from 'esm-path'
const currentDirectoryPath = getAbsolutePath(import.meta.url)
console.log(currentDirectoryPath)
const parentDirectoryPath = getAbsolutePath(import.meta.url, '..')
console.log(parentDirectoryPath)
// Adapt the relative path to your case
const packageJsonFilePath = getAbsolutePath(import.meta.url, '../package.json')
console.log(packageJsonFilePath)
// Adapt the relative path to your case
const packageJsonFilePath = getAbsolutePath(import.meta.url, '..' , 'package.json')
console.log(packageJsonFilePath)
Just use path.resolve() method.
import { resolve } from 'path';
app.use('/public/uploads', express.static(resolve('public', 'uploads')))
I use this option, since the path starts with file:// just remove that part.
const __filename = import.meta.url.slice(7);
const __dirname = import.meta.url.slice(7, import.meta.url.lastIndexOf("/"));
As Geoff pointed out the following code returns not the module's path but working directory.
import path from 'path';
const __dirname = path.resolve();
works with --experimental-modules
create a file called root-dirname.js in your project root with this in it:
import { dirname } from 'path'
const dn = dirname(new URL(import.meta.url).hostname)
const __dirname = process.platform === 'win32' ? dn.substr(1) : dn // remove the leading slash on Windows
export const rootDirname = __dirname
Then just import rootDirname when you want the path to the project root folder.
Other than that, Rudolf Gröhling's answer is also correct.
You can use the stack from a new Error(). The error doesn't need to be thrown, and won't stop program execution either. The first line of the stack will always be the error and its message, with the second line being the file the which the error was invoked from.
Since this is a method (which is probably in a util.js file), the real location of the getDirname() call is actually the third line of the error stack.
export const getDirname = () => {
// get the stack
const { stack } = new Error();
// get the third line (the original invoker)
const invokeFileLine = stack.split(`\n`)[2];
// match the file URL from file://(.+)/ and get the first capturing group
// the (.+) is a greedy quantifier and will make the RegExp expand to the largest match
const __dirname = invokeFileLine.match(/file:\/\/(.+)\//)[1];
return __dirname;
};
another option
import {createRequire} from 'module'; // need node v12.2.0
const require = createRequire(import.meta.url);
const __dirname = require.resolve.paths('.')[0];
I have also published a package on NPM called cross-dirname (forked from es-dirname). The package is tested with Node.js (ESM and CJS), Deno and GJS.
Example:
import dirname from 'cross-dirname'
console.log(dirname())
Agree or disagree with the use of global, I found this to be the easiest way to remember and refactor existing code.
Put somewhere early in your code execution:
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
global.___filename = (path) => {
return fileURLToPath(path);
};
global.___dirname = (path) => {
return dirname(global.___filename(path));
};
And then in whichever file you need dirname or filename:
___filename(import.meta.url)
___dirname(import.meta.url)
Of course if we had macros, I wouldn't need to pass import.meta.url, perhaps there's an improvement.
process.cwd()
From documentation:
The process.cwd() method returns the current working directory of the
Node.js process.

Using absolute path in Angular 2 + Electron

I started messing around with Angular 2 and Electron, and used this starter package. Everything seems to be going well, I'm even able to use the node fs package to read from a directory.
The problem I'm having is that I can't seem to be able to use an absolute path for the readdirSync() method. It only takes a relative path.
I did find this in the docs for fs that you can use the URL package to show an absolute path for readdirSync(), like so:
const fs = require('fs');
const { URL } = require('url');
// file:///C:/tmp/hello => C:\tmp\hello
fs.readFileSync(new URL('file:///C:/tmp/hello'));
That looks great, but in my Angular 2/TypeScript/Electron world that doesn't seem to work. Mine looks like this:
import { Injectable } from "#angular/core";
import {readdirSync} from "fs";
import { URL } from "url";
#Injectable()
export class LinkDocRetrieverService {
getFiles():Array<String> {
// read through all the directories to get all the files and folder names
let u = new URL("file:///C:/SAFE/MISC");
let x = readdirSync(u);
console.log("files retrieved="+ x.length);
let files: Array<string> = [];
x.forEach(f => {
files.push(f);
});
return files;
}
}
Both intellisense and runtime both tell me that
[ts] Argument of type 'URL' is not assignable to parameter of type
'string | Buffer'. Type 'URL' is not assignable to type 'Buffer'.
Property 'write' is missing in type 'URL'.
I would have expected the syntax to be the same, but no luck.
I have read a lot of posts stating that they can't run the fs package in Angular 2 + Typescript, but it works well for me, as long as I use a relative path. I just need help getting the absolute path to work. Thanks in advance.
Documentation states that URL support was introduced in v7.6.0 and is experimental. Electron uses v7.4.0, thus you can not use URL with fs yet.
fs.readFileSync('C:/tmp/hello')
Should work just fine.

Check package version at runtime in nodejs?

I have some of my entries in package.json defined as "*"
"dependencies": {
"express": "4.*",
"passport": "*",
"body-parser": "*",
"express-error-handler": "*"
},
I wan't to freeze those values to the current version. How can I know what version my packages are at run time? I don't mind checking one by one since I don't have many of them :)
BTW: I cannot do npm list --depth=0 because I cannot access the vm directly (PaaS restriction), just the logs.
You can use the fs module to read the directories in the node_modules directory and then read package.json in each of them.
var fs = require('fs');
var dirs = fs.readdirSync('node_modules');
var data = {};
dirs.forEach(function(dir) {
try{
var file = 'node_modules/' + dir + '/package.json';
var json = require(file);
var name = json.name;
var version = json.version;
data[name] = version;
}catch(err){}
});
console.debug(data['express']); //= 4.11.2
Just in case if you need the version on the front-end, there is an npm package just for this and it can be used both on client-side and server-side.
global-package-version
You can use it in your code like this
import globalPackageVersion from 'global-package-version';
// package name is 'lodash'
globalPackageVersion(require('lodash/package.json'));
// You can type 'packageVersion' in browser console to check lodash version
// => packageVersion = { lodash: '4.7.2'}
packageVersion becomes a global object when used in server side and becomes a window object when used on the client side. Works well with webpack and all other bundling tools.
Disclaimer: I am the author of this package :)
I've 'modernised' a bit #laggingreflex answer, this works on ES6+, node 10, tested on a lambda running in aws. It's an endpoint from an express app.
const fs = require("fs");
module.exports.dependencies = async (_req, res) => {
const dirs = fs.readdirSync("./node_modules");
const modulesInfo = dirs.reduce((acc, dir) => {
try {
const file = `${dir}/package.json`;
const { name, version } = require(file);
return { ...acc, [name]: version };
} catch (err) {}
}, {});
res.status(200).json(modulesInfo);
};
The accepted solution can be improved upon in both terms of performance and stability:
1: the package name IS THE directory. In typically cases where you are looking for a specific package, you do not need to load every module.
2: this code will not run on all os due to the way the paths are formed
3: using require means the path needs to be relative to the current file (this would only work if your file is located at the top of your project folder & along side node_modules). In most cases, using readFile or readFileSync is a easier approach.
const fs = require('fs');
const path = require('path');
const dirs = fs.readdirSync('node_modules');
const data = {};
//add ones you care about
const trackedPackages = ['express', 'passport', 'body-parser'];
dirs.forEach(function(dir) {
if(trackedPackages.indexOf(dir) > -1){
try{
const json = JSON.parse(
fs.readFileSync(path.join('node_modules', dir, 'package.json'), 'utf8')
);
data[dir] = json.version;
}catch(e){
console.log(`failed to read/parse package.json for ${dir}`, e);
}
}
});
console.debug(data['express']); //= 4.11.2

Resources