/* * Copyright 2018 resin.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict' const _ = require('lodash') const path = require('path') const Bluebird = require('bluebird') const fs = Bluebird.promisifyAll(require('fs')) /* eslint-disable lodash/prefer-lodash-method */ /** * @summary Get file metadata * @function * @private * * @param {String} dirname - directory name * @param {String} [basename] - custom basename to append * @returns {Object} file metadata * * @example * try { * const file = files.getFileMetadataSync('/home/user') * console.log(`Is ${file.basename} a directory? ${file.isDirectory}`) * } catch (error) { * console.error(error) * } */ exports.getFileMetadataSync = (dirname, basename = '') => { // TODO(Shou): use path.parse object information here const fullpath = path.join(dirname, basename) const pathObj = path.parse(fullpath) // TODO(Shou): this is not true for Windows, figure out Windows hidden files const isHidden = pathObj.base.startsWith('.') const stats = fs.statSync(fullpath) return { basename: pathObj.base, dirname: pathObj.dir, fullpath, extension: pathObj.ext.replace('.', ''), name: pathObj.name, isDirectory: stats.isDirectory(), isHidden, size: stats.size } } /** * @summary Get file metadata asynchronously * @function * @private * * @param {String} fullpath - full path * @returns {Promise} promise of file metadata * * @example * files.getFileMetadataAsync('/home/user').then((file) => { * console.log(`Is ${file.basename} a directory? ${file.isDirectory}`) * }) */ exports.getFileMetadataAsync = (fullpath) => { const pathObj = path.parse(fullpath) // NOTE(Shou): this is not true for Windows const isHidden = pathObj.base.startsWith('.') return fs.statAsync(fullpath).then((stats) => { return { basename: pathObj.base, dirname: pathObj.dir, fullpath, extension: pathObj.ext.replace('.', ''), name: pathObj.name, isDirectory: stats.isDirectory(), isHidden, size: stats.size } }) } /** * @summary Get file metadata for a list of filenames * @function * @public * * @description Note that this omits any file that errors * * @param {String} dirname - directory path * @param {Array} basenames - file names * @returns {Promise>} promise of file objects * * @example * files.getAllFilesMetadataAsync(os.homedir(), [ 'file1.txt', 'file2.txt' ]) */ exports.getAllFilesMetadataAsync = (dirname, basenames) => { return Bluebird.all(basenames.map((basename) => { const metadata = exports.getFileMetadataAsync(path.join(dirname, basename)) return metadata.reflect() })).reduce((fileMetas, inspection) => { if (inspection.isFulfilled()) { return fileMetas.concat(inspection.value()) } return fileMetas }, []) } /** * @summary Split a path on it's separator(s) * @function * @public * * @param {String} fullpath - full path to split * @param {Array} [subpaths] - this param shouldn't normally be used * @returns {Array} * * @example * console.log(splitPath(path.join(os.homedir(), 'Downloads')) * // Linux * > [ '/', 'home', 'user', 'Downloads' ] * // Windows * > [ 'C:', 'Users', 'user', 'Downloads' ] */ exports.splitPath = (fullpath, subpaths = []) => { const { base, dir, root } = path.parse(fullpath) const isAbsolute = path.isAbsolute(fullpath) // Takes care of 'relative/path' if (!isAbsolute && dir === '') { return [ base ].concat(subpaths) // Takes care of '/' } else if (isAbsolute && base === '') { return [ root ].concat(subpaths) } return exports.splitPath(dir, [ base ].concat(subpaths)) } /** * @summary Get all subpaths contained in a path * @function * @private * * @param {String} fullpath - path string * @returns {Array} - all subpaths as file objects * * @example * const subpaths = files.subpaths('/home/user/Downloads') * console.log(subpaths.map(file => file.fullpath)) * // Linux/macOS * > [ '/', '/home', '/home/user', '/home/user/Downloads' ] * // Windows * > [ 'C:', 'Users', 'user', 'Downloads' ] */ exports.subpaths = (fullpath) => { if (!_.isString(fullpath)) { return null } const dirs = exports.splitPath(fullpath) return _.map(dirs, (dir, index) => { // eslint-disable-next-line no-magic-numbers const subdir = dirs.slice(0, index + 1) return exports.getFileMetadataSync(path.join(...subdir)) }) }