mirror of
https://github.com/balena-io/etcher.git
synced 2025-04-24 23:37:18 +00:00

We add a convenience module for file and path operations. Tests included. Change-Type: patch
189 lines
4.9 KiB
JavaScript
189 lines
4.9 KiB
JavaScript
/*
|
|
* 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<Object>} 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<String>} basenames - file names
|
|
* @returns {Promise<Array<Object>>} 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<String>} [subpaths] - this param shouldn't normally be used
|
|
* @returns {Array<String>}
|
|
*
|
|
* @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<Object>} - 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))
|
|
})
|
|
}
|