GithubHelp home page GithubHelp logo

make-dir's Introduction

make-dir codecov

Make a directory and its parents if needed - Think mkdir -p

You probably want the built-in fsPromises.mkdir('…', {recursive: true}) instead.

Advantages over mkdirp

  • Promise API (Async/await ready!)
  • Fixes many mkdirp issues: #96 #70 #66
  • CI-tested on macOS, Linux, and Windows
  • Actively maintained
  • Doesn't bundle a CLI
  • Uses the native fs.mkdir/mkdirSync recursive option in Node.js >=10.12.0 unless overridden

Install

npm install make-dir

Usage

$ pwd
/Users/sindresorhus/fun
$ tree
.
import makeDirectory from 'make-dir';

const path = await makeDirectory('unicorn/rainbow/cake');

console.log(path);
//=> '/Users/sindresorhus/fun/unicorn/rainbow/cake'
$ tree
.
└── unicorn
    └── rainbow
        └── cake

Multiple directories:

import makeDirectory from 'make-dir';

const paths = await Promise.all([
	makeDirectory('unicorn/rainbow'),
	makeDirectory('foo/bar')
]);

console.log(paths);
/*
[
	'/Users/sindresorhus/fun/unicorn/rainbow',
	'/Users/sindresorhus/fun/foo/bar'
]
*/

API

makeDirectory(path, options?)

Returns a Promise for the path to the created directory.

makeDirectory.sync(path, options?)

Returns the path to the created directory.

path

Type: string

Directory to create.

options

Type: object

mode

Type: integer
Default: 0o777

Directory permissions.

fs

Type: object
Default: import fs from 'node:fs'

Use a custom fs implementation. For example graceful-fs.

Using a custom fs implementation will block the use of the native recursive option if fs.mkdir or fs.mkdirSync is not the native function.

Related

make-dir's People

Contributors

bendingbender avatar brandon93s avatar cclauss avatar coreyfarrell avatar gurucomkz avatar papb avatar realityking avatar ryanzim avatar sindresorhus avatar striezel avatar thisconnect avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

make-dir's Issues

NPM package.json not updated

It seems the semver dependency on the package.json from npm is ver 6.0.0 (moderate severity vulnerability) instead of 7.3.2
"dependencies": {
"semver": "^6.0.0"
},

The correct version should be this one (from GitHub):
"dependencies": {
"semver": "^7.3.2"
},

Accept file `URL` instances

Currently, the makeDir function doesn't accept a file URL instance:

import makeDir from 'make-dir';

await makeDir(new URL('foo/', import.meta.url));

Results in the following error:

TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received an instance of URL

This makes make-dir harder to work with within Node.js ESM modules.

Users will run into this gotcha more frequently as most Node.js APIs that work with filesystem paths accept file URL instances, including mkdir.

In the meantime, we have to do this workaround:

import { fileURLToPath } from 'url';
import makeDir from 'make-dir';

await makeDir(fileURLToPath(new URL('foo/', import.meta.url)));

3.x release supporting recent semver?

I realise that with v4 now being released the 3.x series is no longer the latest, however a number of users are still stuck on the 3.x series due to the Node version requirement. (For my use-case the chain is: cspell 6.x -> configstore 5.x -> make-dir 3.x; each package along the chain has its own reasons for not picking up a major version bump of the dependent package; streetsidesoftware/cspell#4594, yeoman/configstore#89).

As far as I can tell the 6.x to 7.x version bump of semver isn't breaking to the usages in make-dir, so I'm expecting that cherry-picking 777eed3 then releasing a 3.x would suffice. Would you be up for doing that? (I'd be happy to create a PR if that's useful, though given the size of the change I'm assuming it's not particularly so)

Add an empty option

I need to make a directory and I need to always have it empty. It will almost never exist, so I don't want to run rimraf each time before making the directory. That's a redundant fs operation.

Instead, could makeDir have an empty option? If empty is true, then an exception is thrown here:

make-dir/index.js

Lines 90 to 93 in 6d029fe

const stats = await stat(pth);
if (!stats.isDirectory()) {
throw new Error('The path is not a directory');
}

Or alternatively, some option that doesn't silence the EEXIST exceptions. Like opts.throwOnExists

A third possible solution could be that makeDir returns something that tells us if the directory already existed. Like it could return { alreadyExisted: true }

Stop using process.umask()

Refs: nodejs/node#32321

Summary: process.umask() (no args) will be deprecated and removed.

Reviewing this module, it uses process.umask() in roughly this pattern:

const mode = 0o777 & ~process.umask();
fs.mkdirSync(dir, mode);

Computing the file mode that way is superfluous because the operating system applies the umask anyway. It can be replaced with just this:

fs.mkdirSync(dir, 0o777);

import * as makeDir doesn't work in 2.1.0

Hey there! 👋

Seems there's a problem with TypeScript typings in 2.1.0:

This code doesn't compile:

import * as makeDir from 'make-dir';

await makeDir(dir);

TypeScript says:

Cannot invoke an expression whose type lacks a call signature.

If to change import to import makeDir from 'make-dir' it works fine though 🤔

[Feature Request] Add the posibility to create files

Hi,

It would be awesome if this library could create files. You could pass a route ending in a file and make-dir could create the folders and the file.

Example:

./components/index.ts

The result should be the creation of components folder if it doesnt exist and the file index.ts

Thanks for your work!

ExperimentalWarning: The fs.promises API is experimental

$ node
> process.emitWarning = () => { throw new Error('foo') }
[Function]
> require('make-dir')('/tmp/x')
Promise {
  <pending>,
  domain:
   Domain {
     domain: null,
     _events:
      { removeListener: [Function: updateExceptionCapture],
        newListener: [Function: updateExceptionCapture],
        error: [Function: debugDomainError] },
     _eventsCount: 3,
     _maxListeners: undefined,
     members: [] } }
> repl:1
process.emitWarning = () => { throw new Error('foo') }
                              ^

Error: foo
    at process.emitWarning (repl:1:37)
    at emitWarning (internal/process/promises.js:72:11)
    at emitPromiseRejectionWarnings (internal/process/promises.js:105:9)
    at process._tickCallback (internal/process/next_tick.js:69:34)

I suggest to use util.promisify instead pify (might be polyfill) and then something like:

const fsP = {}

for (const method of [mkdir, 'stat']) {
  fsP[method] = promisify(fs[method])
}

make-dir should preserve EACCES error

Reproduction step on macOS

mkdir -m 0100 foo
node -e "require('fs').mkdirSync('foo/bar')"
# Error: EACCES

node -e "require('make-dir').sync('foo/bar')"
# Error: ENOENT

Expect: make-dir should align to fs and throw EACCES error.
Actual: The error codes differ.

Additional Context: babel/babel#11130

Changing return value of Node.js recursive mkdir

Per nodejs/node#31530 node.js will soon return the first directory created rather than returning the requested directory. So if ./tmp/ does not exist the following script:

console.log(fs.mkdirSync('tmp/subdir', {recursive: true}));
console.log(fs.mkdirSync('tmp/subdir', {recursive: true}));

This will print tmp then undefined instead of printing tmp/subdir (also the result is never path.resolve'd). Knowing the first directory created can be useful when you need to set permissions or ownership of the created directories.

The node.js change itself will be masked by make-dir which returns path.resolve(input) but I think it would be useful for make-dir to emulate this new functionality. Maybe need to wait until it's known what versions of node.js will get this new functionality so useNativeRecursiveOption can be updated to ensure native is only used if it provides the result?

Inconsistency between async and sync implementations

In the sync implementation, we make a statSync call, and if it fails, the original mkdirSync error is thrown:

make-dir/index.js

Lines 137 to 143 in 9de6474

try {
if (!options.fs.statSync(pth).isDirectory()) {
throw new Error('The path is not a directory');
}
} catch (_) {
throw error;
}

However, in the async implementation, we have no error handling for the stat call, and any failure there would bubble up:

make-dir/index.js

Lines 84 to 87 in 9de6474

const stats = await stat(pth);
if (!stats.isDirectory()) {
throw error;
}

In an earlier (pre-async/await) version, we properly swallowed any stat errors, and threw the original error, like the sync implementation:

make-dir/index.js

Lines 76 to 80 in 379001f

return stat(pth)
.then(stats => stats.isDirectory() ? pth : Promise.reject())
.catch(() => {
throw error;
});

I don't have a test case to show if this actually creates an inconsistency for the end user, but it was just noticed in review, and I thought I'd report upstream.

umask warnings

Hello. If your package really needs process.umask() without args (deprecated), then you may use get-umask module. It suppresses those annoying warnings. And yeah, it's written by me.
If you would like to get any help, I'll make pull request tomorrow.

`process.umask()` is not available inside worker thread

This line makes the worker throw instantly:

mode: 0o777 & (~process.umask()),

// make-dir.js
require('make-dir');
// make-dir-worker.js
const {Worker} = require('worker_threads');

new Worker(require.resolve('./make-dir'));
$ node -v
v10.14.1
$ node --experimental-worker make-dir-worker.js

events.js:167
      throw er; // Unhandled 'error' event
      ^
TypeError: process.umask is not a function
    at Object.<anonymous> (/Users/simen/Development/jest/node_modules/make-dir/index.js:7:26)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
    at Function.Module._load (internal/modules/cjs/loader.js:529:3)
    at Module.require (internal/modules/cjs/loader.js:636:17)
    at require (internal/modules/cjs/helpers.js:20:18)
    at Object.<anonymous> (/Users/simen/Development/jest/make-dir.js:1:63)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
Emitted 'error' event at:
    at Worker.[kOnErrorMessage] (internal/worker.js:334:10)
    at Worker.[kOnMessage] (internal/worker.js:344:37)
    at MessagePort.Worker.(anonymous function).on (internal/worker.js:281:57)
    at MessagePort.emit (events.js:182:13)
    at MessagePort.onmessage (internal/worker.js:86:8)

Add option to disable absolute path resolution (useful for alternate fs)

https://github.com/RangerMauve/dat-fetch uses make-dir to write to a separate filesystem by passing in the fs object. The calls to path.resolve however still operate in the context of the filesystem that nodejs is running in and so prepends the current working directory to some paths. These paths are not valid in the context of the separate filesystem being operated upon by make-dir and so there are unwanted intermediate folders being created. (see this issue if interested).

If I made a PR to add a no-resolve option is it possible that this would be merged or should I just fork the package?

Try Catch problems

Some of the code in this is preventing installation

client/node_modules/fs-extra/lib/mkdirs/make-dir.js:85 } catch { ^

There are a number of try catch statements that don't have any parenthesis.
I'm not sure if try catches are allowed without parenthesis but in this case it is not work.
I've tried a few fixes that have been suggested such as updating node version, updating the version of the packages that are installing make-dir but no good.

Thanks

Jason

Failing tests ERR_INVALID_ARG_TYPE (Windows?)

Two of the tests are failing for me. My machine runs windows 10 and node 9.4, in case that's relevant.

Here's the output from npm run test:

C:\Users\redacted\workspace\make-dir (master -> origin) ([email protected])
λ npm run test

> [email protected] test C:\Users\redacted\workspace\make-dir
> xo && nyc ava


  index.js:29:2
  ‼  29:2  Unexpected todo comment.  no-warning-comments

  1 warning

  19 passed
  2 failed

  async » handles null bytes in path

  C:\Users\redacted\workspace\make-dir\test\async.js:73

   72:   const err = await t.throws(m(dir), /null bytes/);
   73:   t.regex(err.code, /ERR_INVALID_ARG_VALUE|ENOENT/);
   74: });

  Value must match expression:

  'ERR_INVALID_ARG_TYPE'

  Regular expression:

  /ERR_INVALID_ARG_VALUE|ENOENT/

  err.code
  => 'ERR_INVALID_ARG_TYPE'

  err
  => Error [ERR_INVALID_ARG_TYPE] (NodeError) {
    message: 'The "path" argument must be of type string without null bytes. Received type string',
  }



  sync » handles null bytes in path

  C:\Users\redacted\workspace\make-dir\test\sync.js:76

   75:   }, /null bytes/);
   76:   t.regex(err.code, /ERR_INVALID_ARG_VALUE|ENOENT/);
   77: });

  Value must match expression:

  'ERR_INVALID_ARG_TYPE'

  Regular expression:

  /ERR_INVALID_ARG_VALUE|ENOENT/

  err.code
  => 'ERR_INVALID_ARG_TYPE'

  err
  => Error [ERR_INVALID_ARG_TYPE] (NodeError) {
    message: 'The "path" argument must be of type string without null bytes. Received type string',
  }

  Test.t [as fn] (test/sync.js:76:4)
  processEmit [as emit] (node_modules/nyc/node_modules/signal-exit/index.js:155:32)
  processEmit [as emit] (node_modules/nyc/node_modules/signal-exit/index.js:155:32)
----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |       95 |      100 |      100 |                   |
 index.js |      100 |       95 |      100 |      100 |                14 |
----------|----------|----------|----------|----------|-------------------|
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] test: `xo && nyc ava`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] test script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\redacted\AppData\Roaming\npm-cache\_logs\2018-05-14T07_17_46_490Z-debug.log

Stale process.umask() value

I'm working on jprichardson/node-fs-extra#619; and experimenting with switching our current implementation (an internal fork of [email protected]) to an internal fork of make-dir. In testing against our existing test suite, I discovered what seems to be a bug, and thought I'd report it upstream here.

The default mode is based on process.umask(), which is set here:

mode: 0o777 & (~process.umask()),

However, this is one static value, based on the process.umask() value at require-time. If the user sets the umask with process.umask(value) after requiring make-dir, the new umask will not be respected.

Fix for this would be to fetch defaults in a function, called at runtime, to get a fresh process.umask() call for every method call. Happy to submit a PR if this is confirmed as a bug.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.