Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/node_modules/write-file-atomic/lib/index.js')
-rw-r--r--deps/npm/node_modules/write-file-atomic/lib/index.js268
1 files changed, 268 insertions, 0 deletions
diff --git a/deps/npm/node_modules/write-file-atomic/lib/index.js b/deps/npm/node_modules/write-file-atomic/lib/index.js
new file mode 100644
index 00000000000..9a7d183aecb
--- /dev/null
+++ b/deps/npm/node_modules/write-file-atomic/lib/index.js
@@ -0,0 +1,268 @@
+'use strict'
+module.exports = writeFile
+module.exports.sync = writeFileSync
+module.exports._getTmpname = getTmpname // for testing
+module.exports._cleanupOnExit = cleanupOnExit
+
+const fs = require('fs')
+const MurmurHash3 = require('imurmurhash')
+const onExit = require('signal-exit')
+const path = require('path')
+const isTypedArray = require('is-typedarray')
+const typedArrayToBuffer = require('typedarray-to-buffer')
+const { promisify } = require('util')
+const activeFiles = {}
+
+// if we run inside of a worker_thread, `process.pid` is not unique
+/* istanbul ignore next */
+const threadId = (function getId () {
+ try {
+ const workerThreads = require('worker_threads')
+
+ /// if we are in main thread, this is set to `0`
+ return workerThreads.threadId
+ } catch (e) {
+ // worker_threads are not available, fallback to 0
+ return 0
+ }
+})()
+
+let invocations = 0
+function getTmpname (filename) {
+ return filename + '.' +
+ MurmurHash3(__filename)
+ .hash(String(process.pid))
+ .hash(String(threadId))
+ .hash(String(++invocations))
+ .result()
+}
+
+function cleanupOnExit (tmpfile) {
+ return () => {
+ try {
+ fs.unlinkSync(typeof tmpfile === 'function' ? tmpfile() : tmpfile)
+ } catch (_) {}
+ }
+}
+
+function serializeActiveFile (absoluteName) {
+ return new Promise(resolve => {
+ // make a queue if it doesn't already exist
+ if (!activeFiles[absoluteName]) {
+ activeFiles[absoluteName] = []
+ }
+
+ activeFiles[absoluteName].push(resolve) // add this job to the queue
+ if (activeFiles[absoluteName].length === 1) {
+ resolve()
+ } // kick off the first one
+ })
+}
+
+// https://github.com/isaacs/node-graceful-fs/blob/master/polyfills.js#L315-L342
+function isChownErrOk (err) {
+ if (err.code === 'ENOSYS') {
+ return true
+ }
+
+ const nonroot = !process.getuid || process.getuid() !== 0
+ if (nonroot) {
+ if (err.code === 'EINVAL' || err.code === 'EPERM') {
+ return true
+ }
+ }
+
+ return false
+}
+
+async function writeFileAsync (filename, data, options = {}) {
+ if (typeof options === 'string') {
+ options = { encoding: options }
+ }
+
+ let fd
+ let tmpfile
+ /* istanbul ignore next -- The closure only gets called when onExit triggers */
+ const removeOnExitHandler = onExit(cleanupOnExit(() => tmpfile))
+ const absoluteName = path.resolve(filename)
+
+ try {
+ await serializeActiveFile(absoluteName)
+ const truename = await promisify(fs.realpath)(filename).catch(() => filename)
+ tmpfile = getTmpname(truename)
+
+ if (!options.mode || !options.chown) {
+ // Either mode or chown is not explicitly set
+ // Default behavior is to copy it from original file
+ const stats = await promisify(fs.stat)(truename).catch(() => {})
+ if (stats) {
+ if (options.mode == null) {
+ options.mode = stats.mode
+ }
+
+ if (options.chown == null && process.getuid) {
+ options.chown = { uid: stats.uid, gid: stats.gid }
+ }
+ }
+ }
+
+ fd = await promisify(fs.open)(tmpfile, 'w', options.mode)
+ if (options.tmpfileCreated) {
+ await options.tmpfileCreated(tmpfile)
+ }
+ if (isTypedArray(data)) {
+ data = typedArrayToBuffer(data)
+ }
+ if (Buffer.isBuffer(data)) {
+ await promisify(fs.write)(fd, data, 0, data.length, 0)
+ } else if (data != null) {
+ await promisify(fs.write)(fd, String(data), 0, String(options.encoding || 'utf8'))
+ }
+
+ if (options.fsync !== false) {
+ await promisify(fs.fsync)(fd)
+ }
+
+ await promisify(fs.close)(fd)
+ fd = null
+
+ if (options.chown) {
+ await promisify(fs.chown)(tmpfile, options.chown.uid, options.chown.gid).catch(err => {
+ if (!isChownErrOk(err)) {
+ throw err
+ }
+ })
+ }
+
+ if (options.mode) {
+ await promisify(fs.chmod)(tmpfile, options.mode).catch(err => {
+ if (!isChownErrOk(err)) {
+ throw err
+ }
+ })
+ }
+
+ await promisify(fs.rename)(tmpfile, truename)
+ } finally {
+ if (fd) {
+ await promisify(fs.close)(fd).catch(
+ /* istanbul ignore next */
+ () => {}
+ )
+ }
+ removeOnExitHandler()
+ await promisify(fs.unlink)(tmpfile).catch(() => {})
+ activeFiles[absoluteName].shift() // remove the element added by serializeSameFile
+ if (activeFiles[absoluteName].length > 0) {
+ activeFiles[absoluteName][0]() // start next job if one is pending
+ } else {
+ delete activeFiles[absoluteName]
+ }
+ }
+}
+
+function writeFile (filename, data, options, callback) {
+ if (options instanceof Function) {
+ callback = options
+ options = {}
+ }
+
+ const promise = writeFileAsync(filename, data, options)
+ if (callback) {
+ promise.then(callback, callback)
+ }
+
+ return promise
+}
+
+function writeFileSync (filename, data, options) {
+ if (typeof options === 'string') {
+ options = { encoding: options }
+ } else if (!options) {
+ options = {}
+ }
+ try {
+ filename = fs.realpathSync(filename)
+ } catch (ex) {
+ // it's ok, it'll happen on a not yet existing file
+ }
+ const tmpfile = getTmpname(filename)
+
+ if (!options.mode || !options.chown) {
+ // Either mode or chown is not explicitly set
+ // Default behavior is to copy it from original file
+ try {
+ const stats = fs.statSync(filename)
+ options = Object.assign({}, options)
+ if (!options.mode) {
+ options.mode = stats.mode
+ }
+ if (!options.chown && process.getuid) {
+ options.chown = { uid: stats.uid, gid: stats.gid }
+ }
+ } catch (ex) {
+ // ignore stat errors
+ }
+ }
+
+ let fd
+ const cleanup = cleanupOnExit(tmpfile)
+ const removeOnExitHandler = onExit(cleanup)
+
+ let threw = true
+ try {
+ fd = fs.openSync(tmpfile, 'w', options.mode || 0o666)
+ if (options.tmpfileCreated) {
+ options.tmpfileCreated(tmpfile)
+ }
+ if (isTypedArray(data)) {
+ data = typedArrayToBuffer(data)
+ }
+ if (Buffer.isBuffer(data)) {
+ fs.writeSync(fd, data, 0, data.length, 0)
+ } else if (data != null) {
+ fs.writeSync(fd, String(data), 0, String(options.encoding || 'utf8'))
+ }
+ if (options.fsync !== false) {
+ fs.fsyncSync(fd)
+ }
+
+ fs.closeSync(fd)
+ fd = null
+
+ if (options.chown) {
+ try {
+ fs.chownSync(tmpfile, options.chown.uid, options.chown.gid)
+ } catch (err) {
+ if (!isChownErrOk(err)) {
+ throw err
+ }
+ }
+ }
+
+ if (options.mode) {
+ try {
+ fs.chmodSync(tmpfile, options.mode)
+ } catch (err) {
+ if (!isChownErrOk(err)) {
+ throw err
+ }
+ }
+ }
+
+ fs.renameSync(tmpfile, filename)
+ threw = false
+ } finally {
+ if (fd) {
+ try {
+ fs.closeSync(fd)
+ } catch (ex) {
+ // ignore close errors at this stage, error may have closed fd already.
+ }
+ }
+ removeOnExitHandler()
+ if (threw) {
+ cleanup()
+ }
+ }
+}