From 9817704e81651cc89080d7848298c6bbf98d41e4 Mon Sep 17 00:00:00 2001 From: Greg Magolan Date: Mon, 27 May 2019 00:51:08 -0700 Subject: [PATCH] Vendor in removeNPMAbsolutePaths (#763) Fixes npm_install target cycle Bazel crash --- BUILD.bazel | 1 + internal/node/node_repositories.bzl | 13 - internal/node/yarn.lock | 13 - internal/npm_install/npm_install.bzl | 2 +- internal/npm_install/package.json | 7 - internal/npm_install/yarn.lock | 7 - internal/npm_package/BUILD.bazel | 1 - .../removeNPMAbsolutePaths/BUILD.bazel | 13 + .../juanjoDiaz/removeNPMAbsolutePaths/LICENSE | 21 ++ .../removeNPMAbsolutePaths/README.md | 77 +++++ .../bin/removeNPMAbsolutePaths | 49 +++ .../removeNPMAbsolutePaths/package.json | 46 +++ .../removeNPMAbsolutePaths/src/errno.js | 310 ++++++++++++++++++ .../src/removeNPMAbsolutePaths.js | 132 ++++++++ 14 files changed, 650 insertions(+), 42 deletions(-) delete mode 100644 internal/node/yarn.lock delete mode 100644 internal/npm_install/package.json delete mode 100644 internal/npm_install/yarn.lock create mode 100644 third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/BUILD.bazel create mode 100644 third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/LICENSE create mode 100644 third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/README.md create mode 100755 third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/bin/removeNPMAbsolutePaths create mode 100644 third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/package.json create mode 100644 third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/src/errno.js create mode 100644 third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/src/removeNPMAbsolutePaths.js diff --git a/BUILD.bazel b/BUILD.bazel index 1016667921..4e5489edea 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -46,6 +46,7 @@ npm_package( "//third_party/github.com/inikulin/parse5:package_contents", "//third_party/github.com/gjtorikian/isBinaryFile:package_contents", "//third_party/github.com/jhermsmeier/browserify-named-amd:package_contents", + "//third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths:package_contents", "//third_party/github.com/source-map:package_contents", "//third_party/github.com/source-map-support:package_contents", "//internal:package_contents", diff --git a/internal/node/node_repositories.bzl b/internal/node/node_repositories.bzl index cd7fe15d98..5708c621dc 100644 --- a/internal/node/node_repositories.bzl +++ b/internal/node/node_repositories.bzl @@ -563,19 +563,6 @@ def node_repositories( package_json = package_json, ) - _maybe( - yarn_install, - name = "build_bazel_rules_nodejs_npm_install_deps", - package_json = "@build_bazel_rules_nodejs//internal/npm_install:package.json", - yarn_lock = "@build_bazel_rules_nodejs//internal/npm_install:yarn.lock", - # Just here as a smoke test for this attribute - prod_only = True, - # Do not symlink node_modules as when used in downstream repos we should not create - # node_modules folders in the @build_bazel_rules_nodejs external repository. This is - # not supported by managed_directories. - symlink_node_modules = False, - ) - _maybe( yarn_install, name = "build_bazel_rules_nodejs_rollup_deps", diff --git a/internal/node/yarn.lock b/internal/node/yarn.lock deleted file mode 100644 index d222d59a06..0000000000 --- a/internal/node/yarn.lock +++ /dev/null @@ -1,13 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -source-map-support@0.4.18: - version "0.4.18" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" - dependencies: - source-map "^0.5.6" - -source-map@^0.5.6: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" diff --git a/internal/npm_install/npm_install.bzl b/internal/npm_install/npm_install.bzl index 13fe4be6c8..c290ab4fa1 100644 --- a/internal/npm_install/npm_install.bzl +++ b/internal/npm_install/npm_install.bzl @@ -237,7 +237,7 @@ cd "{root}" && "{npm}" {npm_args} if result.return_code: fail("npm_install failed: %s (%s)" % (result.stdout, result.stderr)) - remove_npm_absolute_paths = Label("@build_bazel_rules_nodejs_npm_install_deps//:node_modules/removeNPMAbsolutePaths/bin/removeNPMAbsolutePaths") + remove_npm_absolute_paths = Label("//third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths:bin/removeNPMAbsolutePaths") # removeNPMAbsolutePaths is run on node_modules after npm install as the package.json files # generated by npm are non-deterministic. They contain absolute install paths and other private diff --git a/internal/npm_install/package.json b/internal/npm_install/package.json deleted file mode 100644 index 91a0491890..0000000000 --- a/internal/npm_install/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "description": "runtime dependencies for npm_install", - "devDependencies": {}, - "dependencies": { - "removeNPMAbsolutePaths": "1.0.4" - } -} diff --git a/internal/npm_install/yarn.lock b/internal/npm_install/yarn.lock deleted file mode 100644 index 7617b4a769..0000000000 --- a/internal/npm_install/yarn.lock +++ /dev/null @@ -1,7 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -removeNPMAbsolutePaths@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/removeNPMAbsolutePaths/-/removeNPMAbsolutePaths-1.0.4.tgz#cc657a861485fc55344717972ebf4c453d775d21" diff --git a/internal/npm_package/BUILD.bazel b/internal/npm_package/BUILD.bazel index bfe11b2217..7318073caa 100644 --- a/internal/npm_package/BUILD.bazel +++ b/internal/npm_package/BUILD.bazel @@ -19,7 +19,6 @@ nodejs_binary( ], entry_point = "build_bazel_rules_nodejs/internal/npm_package/packager.js", install_source_map_support = False, - node_modules = "@build_bazel_rules_nodejs_npm_install_deps//:node_modules", visibility = ["//visibility:public"], ) diff --git a/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/BUILD.bazel b/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/BUILD.bazel new file mode 100644 index 0000000000..de05b0c7cf --- /dev/null +++ b/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/BUILD.bazel @@ -0,0 +1,13 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +# Downloaded from https://github.com/juanjoDiaz/removeNPMAbsolutePaths/archive/v1.0.4.tar.gz +# Timestamp: 2019-05-27 +# SHA256: 3e8309c823660df6edeb4580f1d2df06ebfec0d65e196c46b488f53e644bd7aa +exports_files(["LICENSE"]) + +filegroup( + name = "package_contents", + srcs = glob(["**/*"]), +) diff --git a/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/LICENSE b/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/LICENSE new file mode 100644 index 0000000000..ccf7370070 --- /dev/null +++ b/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Juanjo Diaz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/README.md b/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/README.md new file mode 100644 index 0000000000..8e7ae0f017 --- /dev/null +++ b/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/README.md @@ -0,0 +1,77 @@ +[![NPM version][npm-image]][npm-url] +[![build status][travis-image]][travis-url] +[![Test coverage][coveralls-image]][coveralls-url] +[![Downloads][downloads-image]][downloads-url] + +# removeNPMAbsolutePaths + +removeNPMAbsolutePaths is a small utility to remove the fields that npm adds to the modules in `node_modules` containing local aboslute paths. + +It has been noted that the `package.json` of modules in the `node_modules` folder contain some extra fields like `_args` and `where` which contain the absolute path of the module. According to NPM those fields are not even used. + +The problem comes when you are planning to package your application using electron, NW.js or similar and distribute it. You might not want to distribute files containing absolute paths within your computer. + +A feature request has been raised to NPM to fix this issue but they have made clear they don't plan to fix this. + - https://github.com/npm/npm/issues/12110 (feature request) + - https://github.com/npm/npm/issues/10393 (discussion about the topic) + +## Using removeNPMAbsolutePaths + +removeNPMAbsolutePaths simply loop through all the files in the given folder, open the files called `package.json` and remove all the fields starting with an underscore (`_`). + +You can install removeNPMAbsolutePaths globally and use it from the command line +```Javascript +npm install -g removeNPMAbsolutePaths +removeNPMAbsolutePaths '' +``` +or use it from whithin your code +```Javascript +var removeNPMAbsolutePaths = require('removeNPMAbsolutePaths'); +removeNPMAbsolutePaths('') + .then(results => results.forEach(result => { + // Print only information about files that couldn't be processed + if (!result.success) { + console.log(result.err.message); + } + })) + .catch(err => console.log(err.message)); +``` +Using `removeNPMAbsolutePaths` from within Javascript returns a promise containing information about all the folders and files processed and whether they where successfully processed and rewritten or not. + +### Options +removeNPMAbsolutePaths can be configured using tags. Tags can be added to the command line commands: +```Javascript +removeNPMAbsolutePaths '' --tag1 --tag2 +``` +or passed programmatically in an options object +```Javascript +removeNPMAbsolutePaths('', { tag1: true, tag2: false}); +``` + +#### force +removeNPMAbsolutePaths only rewrite to disk the files that it modifies. Passing the `--force` tag will rewritte all the files even if they haven't been modfied. This might be useful if you want all the package.json files to have always exactly the same styling for example for hashing. + +#### fields +removeNPMAbsolutePaths by default removes all fields starting with `_`. Passing the `--fields` tag followed by a list of field names you want removed will cause it to remove only those ones you list. This might be useful if only some of the fields in package.json are bothering you. + +```Javascript +removeNPMAbsolutePaths '' --fields _where _args +``` + +```Javascript +removeNPMAbsolutePaths('', { tag1: true, fields: ['_where', '_args']}); +``` + + +## License +MIT + + +[npm-image]: https://img.shields.io/npm/v/removeNPMAbsolutePaths.svg?style=flat-square +[npm-url]: https://www.npmjs.com/package/removeNPMAbsolutePaths +[travis-image]: https://img.shields.io/travis/juanjoDiaz/removeNPMAbsolutePaths/master.svg?style=flat-square +[travis-url]: https://travis-ci.org/juanjoDiaz/removeNPMAbsolutePaths +[coveralls-image]: https://img.shields.io/coveralls/juanjoDiaz/removeNPMAbsolutePaths/master.svg?style=flat-square +[coveralls-url]: https://coveralls.io/github/juanjoDiaz/removeNPMAbsolutePaths?branch=master +[downloads-image]: https://img.shields.io/npm/dm/removeNPMAbsolutePaths.svg?style=flat-square +[downloads-url]: https://www.npmjs.com/package/removeNPMAbsolutePaths diff --git a/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/bin/removeNPMAbsolutePaths b/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/bin/removeNPMAbsolutePaths new file mode 100755 index 0000000000..08aa4c873e --- /dev/null +++ b/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/bin/removeNPMAbsolutePaths @@ -0,0 +1,49 @@ +#!/usr/bin/env node + +/* eslint-disable no-console */ + +'use strict'; + +const removeNPMAbsolutePaths = require('../src/removeNPMAbsolutePaths'); + +if (process.argv < 1) { + console.log('Invalid argument.', 'The first argument should be the path to a directory or a package.json file.'); +} + +const args = process.argv.slice(2); +const folder = args[0]; +const opts = { + force: false, +}; + +const ignoredOptions = []; + +for (let i = 1; i < args.length; i += 1) { + const arg = args[i]; + switch (arg) { + case '--force': + opts.force = true; + break; + case '--fields': + opts.fields = opts.fields || []; + while (args[i + 1] && args[i + 1].slice(0, 2) !== '--') { + opts.fields.push(args[i += 1]); + } + break; + default: + ignoredOptions.push(arg); + break; + } +} + +if (ignoredOptions.length) { + console.warn(`The following options are unknown and will be ignored:\n${ignoredOptions.join('\n')}`); +} + +removeNPMAbsolutePaths(folder, opts) + .then(results => results.forEach((result) => { + if (!result.success) { + console.log(result.err.message); + } + })) + .catch(err => console.log(err.message)); diff --git a/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/package.json b/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/package.json new file mode 100644 index 0000000000..da0f3ff358 --- /dev/null +++ b/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/package.json @@ -0,0 +1,46 @@ +{ + "name": "removeNPMAbsolutePaths", + "version": "1.0.4", + "description": "Remove the fields containing local aboslute paths created by NPM", + "keywords": [ + "npm", + "modules" + ], + "main": "src/removeNPMAbsolutePaths.js", + "scripts": { + "lint": "eslint bin/removeNPMAbsolutePaths src test", + "test": "mocha test", + "test-with-coverage": "nyc --reporter=text mocha test", + "travis": "npm run lint && npm run test-with-coverage", + "coveralls": "nyc report --reporter=text-lcov | coveralls" + }, + "author": "Juanjo Diaz ", + "repository": { + "type": "git", + "url": "git://github.com/juanjoDiaz/removeNPMAbsolutePaths" + }, + "bugs": { + "url": "https://github.com/removeNPMAbsolutePaths/issues", + "email": "juanjo.diazmo@gmail.com" + }, + "license": "MIT", + "preferGlobal": "true", + "bin": { + "removeNPMAbsolutePaths": "bin/removeNPMAbsolutePaths" + }, + "devDependencies": { + "chai": "^4.1.1", + "chai-as-promised": "^7.1.1", + "coveralls": "^2.13.1", + "eslint": "^4.4.1", + "eslint-config-airbnb-base": "^11.3.1", + "eslint-plugin-import": "^2.7.0", + "mocha": "^3.5.0", + "nyc": "^11.1.0", + "sinon": "^3.2.0", + "sinon-chai": "^2.12.0" + }, + "engines": { + "node": ">=4.0.0" + } +} diff --git a/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/src/errno.js b/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/src/errno.js new file mode 100644 index 0000000000..d5ba943153 --- /dev/null +++ b/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/src/errno.js @@ -0,0 +1,310 @@ +'use strict'; + +const all = [ + { + errno: -2, + code: 'ENOENT', + description: 'No such file or directory', + }, + { + errno: -1, + code: 'UNKNOWN', + description: 'Unknown error', + }, + { + errno: 0, + code: 'OK', + description: 'Success', + }, + { + errno: 1, + code: 'EOF', + description: 'End of file', + }, + { + errno: 2, + code: 'EADDRINFO', + description: 'Getaddrinfo error', + }, + { + errno: 3, + code: 'EACCES', + description: 'Permission denied', + }, + { + errno: 4, + code: 'EAGAIN', + description: 'Resource temporarily unavailable', + }, + { + errno: 5, + code: 'EADDRINUSE', + description: 'Address already in use', + }, + { + errno: 6, + code: 'EADDRNOTAVAIL', + description: 'Address not available', + }, + { + errno: 7, + code: 'EAFNOSUPPORT', + description: 'Address family not supported', + }, + { + errno: 8, + code: 'EALREADY', + description: 'Connection already in progress', + }, + { + errno: 9, + code: 'EBADF', + description: 'Bad file descriptor', + }, + { + errno: 10, + code: 'EBUSY', + description: 'Resource busy or locked', + }, + { + errno: 11, + code: 'ECONNABORTED', + description: 'Software caused connection abort', + }, + { + errno: 12, + code: 'ECONNREFUSED', + description: 'Connection refused', + }, + { + errno: 13, + code: 'ECONNRESET', + description: 'Connection reset by peer', + }, + { + errno: 14, + code: 'EDESTADDRREQ', + description: 'Destination address required', + }, + { + errno: 15, + code: 'EFAULT', + description: 'Bad address in system call argument', + }, + { + errno: 16, + code: 'EHOSTUNREACH', + description: 'Host is unreachable', + }, + { + errno: 17, + code: 'EINTR', + description: 'Interrupted system call', + }, + { + errno: 18, + code: 'EINVAL', + description: 'Invalid argument', + }, + { + errno: 19, + code: 'EISCONN', + description: 'Socket is already connected', + }, + { + errno: 20, + code: 'EMFILE', + description: 'Too many open files', + }, + { + errno: 21, + code: 'EMSGSIZE', + description: 'Message too long', + }, + { + errno: 22, + code: 'ENETDOWN', + description: 'Network is down', + }, + { + errno: 23, + code: 'ENETUNREACH', + description: 'Network is unreachable', + }, + { + errno: 24, + code: 'ENFILE', + description: 'File table overflow', + }, + { + errno: 25, + code: 'ENOBUFS', + description: 'No buffer space available', + }, + { + errno: 26, + code: 'ENOMEM', + description: 'Not enough memory', + }, + { + errno: 27, + code: 'ENOTDIR', + description: 'Not a directory', + }, + { + errno: 28, + code: 'EISDIR', + description: 'Illegal operation on a directory', + }, + { + errno: 29, + code: 'ENONET', + description: 'Machine is not on the network', + }, + { + errno: 31, + code: 'ENOTCONN', + description: 'Socket is not connected', + }, + { + errno: 32, + code: 'ENOTSOCK', + description: 'Socket operation on non-socket', + }, + { + errno: 33, + code: 'ENOTSUP', + description: 'Operation not supported on socket', + }, + { + errno: 34, + code: 'ENOENT', + description: 'No such file or directory', + }, + { + errno: 35, + code: 'ENOSYS', + description: 'Function not implemented', + }, + { + errno: 36, + code: 'EPIPE', + description: 'Broken pipe', + }, + { + errno: 37, + code: 'EPROTO', + description: 'Protocol error', + }, + { + errno: 38, + code: 'EPROTONOSUPPORT', + description: 'Protocol not supported', + }, + { + errno: 39, + code: 'EPROTOTYPE', + description: 'Protocol wrong type for socket', + }, + { + errno: 40, + code: 'ETIMEDOUT', + description: 'Connection timed out', + }, + { + errno: 41, + code: 'ECHARSET', + description: 'Invalid Unicode character', + }, + { + errno: 42, + code: 'EAIFAMNOSUPPORT', + description: 'Address family for hostname not supported', + }, + { + errno: 44, + code: 'EAISERVICE', + description: 'Servname not supported for ai_socktype', + }, + { + errno: 45, + code: 'EAISOCKTYPE', + description: 'Ai_socktype not supported', + }, + { + errno: 46, + code: 'ESHUTDOWN', + description: 'Cannot send after transport endpoint shutdown', + }, + { + errno: 47, + code: 'EEXIST', + description: 'File already exists', + }, + { + errno: 48, + code: 'ESRCH', + description: 'No such process', + }, + { + errno: 49, + code: 'ENAMETOOLONG', + description: 'Name too long', + }, + { + errno: 50, + code: 'EPERM', + description: 'Operation not permitted', + }, + { + errno: 51, + code: 'ELOOP', + description: 'Too many symbolic links encountered', + }, + { + errno: 52, + code: 'EXDEV', + description: 'Cross-device link not permitted', + }, + { + errno: 53, + code: 'ENOTEMPTY', + description: 'Directory not empty', + }, + { + errno: 54, + code: 'ENOSPC', + description: 'No space left on device', + }, + { + errno: 55, + code: 'EIO', + description: 'I/O error', + }, + { + errno: 56, + code: 'EROFS', + description: 'Read-only file system', + }, + { + errno: 57, + code: 'ENODEV', + description: 'No such device', + }, + { + errno: 58, + code: 'ESPIPE', + description: 'Invalid seek', + }, + { + errno: 59, + code: 'ECANCELED', + description: 'Operation canceled', + }, +]; + +module.exports = {}; + +all.forEach((error) => { + module.exports[error.errno] = error.description; +}); diff --git a/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/src/removeNPMAbsolutePaths.js b/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/src/removeNPMAbsolutePaths.js new file mode 100644 index 0000000000..dd01ae1125 --- /dev/null +++ b/third_party/github.com/juanjoDiaz/removeNPMAbsolutePaths/src/removeNPMAbsolutePaths.js @@ -0,0 +1,132 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const errno = require('./errno'); + +function createError(message, rootErr) { + const err = new Error(message + (rootErr.errno ? ` (${errno[rootErr.errno]})` : '')); + err.cause = rootErr; + return err; +} + +function getStats(filePath) { + return new Promise((resolve, reject) => { + fs.stat(filePath, (err, stats) => { + if (err) { + reject(createError(`Can't read directory/file at "${filePath}"`, err)); + return; + } + + resolve(stats); + }); + }); +} + +function processFile(filePath, opts) { + return new Promise((resolve, reject) => { + fs.readFile(filePath, 'utf8', (err, data) => { + if (err) { + reject(createError(`Can't read file at "${filePath}"`, err)); + return; + } + + resolve(data); + }); + }) + .then((data) => { + let writeFile = false; + let obj; + try { + obj = JSON.parse(data); + } catch (err) { + throw createError(`Malformed package.json file at "${filePath}"`, err); + } + + Object.keys(obj).forEach((key) => { + const shouldBeDeleted = opts.fields ? (opts.fields.indexOf(key) !== -1) : (key[0] === '_'); + if (shouldBeDeleted) { + delete obj[key]; + writeFile = true; + } + }); + + if (writeFile || opts.force) { + return new Promise((resolve, reject) => { + fs.writeFile(filePath, JSON.stringify(obj, null, ' '), (err) => { + if (err) { + reject(createError(`Can't write processed file to "${filePath}"`, err)); + return; + } + + resolve({ rewritten: true }); + }); + }); + } + + return { rewritten: false }; + }) + .then(r => ({ filePath, rewritten: r.rewritten, success: true }), + err => ({ filePath, err, success: false })); +} + +function processDir(dirPath, opts) { + return new Promise((resolve, reject) => { + fs.readdir(dirPath, (err, files) => { + if (err) { + reject(createError(`Can't read directory at "${dirPath}"`, err)); + return; + } + + resolve(files); + }); + }) + .then(files => Promise.all(files.map((fileName) => { + const filePath = path.join(dirPath, fileName); + + return getStats(filePath) + .then((stats) => { + if (stats.isDirectory()) { + return processDir(filePath, opts); + } else if (fileName === 'package.json') { + return processFile(filePath, opts); + } + return undefined; + }); + }))) + .then(results => results.reduce((arr, value) => { + if (!value) { + return arr; + } + + if (value.constructor === Array) { + return arr.concat(value); + } + + arr.push(value); + return arr; + }, [{ dirPath, success: true }])) + .catch(err => [{ dirPath, err, success: false }]); +} + +function removeNPMAbsolutePaths(filePath, opts) { + opts = opts || {}; // eslint-disable-line no-param-reassign + + if (!filePath) { + return Promise.reject(new Error('Missing path. The first argument should be the path to a directory or a package.json file.')); + } + + return getStats(filePath) + .then((stats) => { + if (stats.isDirectory()) { + return processDir(filePath, opts); + } else if (path.basename(filePath) === 'package.json') { + return processFile(filePath, opts) + .then(result => [result]); + } + + throw new Error('Invalid path provided. The path should be a directory or a package.json file.'); + }); +} + +module.exports = removeNPMAbsolutePaths;