Skip to content

Commit

Permalink
feat: support glob patterns (#151)
Browse files Browse the repository at this point in the history
We use a weird mix of multiple glob patterns and ignores which means we parse the available files in an inefficient way.

Instead just use a single glob pattern which can do everything we need.

Fixes ipfs/js-ipfs#2885

BREAKING CHANGE: the globSource call signature has changed and no longer supports the recursive or ignore options
  • Loading branch information
achingbrain authored Sep 22, 2021
1 parent 8279445 commit d626de1
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 202 deletions.
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ jobs:
test-react-native-android:
runs-on: macos-latest
needs: check
continue-on-error: true
steps:
- uses: actions/checkout@v2
- run: npm install
Expand Down
116 changes: 22 additions & 94 deletions src/files/glob-source.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,131 +7,59 @@ const Path = require('path')
const errCode = require('err-code')

/**
* Create an async iterator that yields paths that match requested file paths.
* Create an async iterator that yields paths that match requested glob pattern
*
* @param {Iterable<string> | AsyncIterable<string> | string} paths - File system path(s) to glob from
* @param {string} cwd - The directory to start matching the pattern in
* @param {string} pattern - Glob pattern to match
* @param {Object} [options] - Optional options
* @param {boolean} [options.recursive] - Recursively glob all paths in directories
* @param {boolean} [options.hidden] - Include .dot files in matched paths
* @param {Array<string>} [options.ignore] - Glob paths to ignore
* @param {boolean} [options.followSymlinks] - follow symlinks
* @param {boolean} [options.preserveMode] - preserve mode
* @param {boolean} [options.preserveMtime] - preserve mtime
* @param {number} [options.mode] - mode to use - if preserveMode is true this will be ignored
* @param {import('ipfs-unixfs').MtimeLike} [options.mtime] - mtime to use - if preserveMtime is true this will be ignored
* @yields {Object} File objects in the form `{ path: String, content: AsyncIterator<Buffer> }`
*/
module.exports = async function * globSource (paths, options) {
module.exports = async function * globSource (cwd, pattern, options) {
options = options || {}

if (typeof paths === 'string') {
paths = [paths]
if (typeof pattern !== 'string') {
throw errCode(
new Error('Pattern must be a string'),
'ERR_INVALID_PATH',
{ pattern }
)
}

const globSourceOptions = {
recursive: options.recursive,
glob: {
dot: Boolean(options.hidden),
ignore: Array.isArray(options.ignore) ? options.ignore : [],
follow: options.followSymlinks != null ? options.followSymlinks : true
}
if (!Path.isAbsolute(cwd)) {
cwd = Path.resolve(process.cwd(), cwd)
}

// Check the input paths comply with options.recursive and convert to glob sources
for await (const path of paths) {
if (typeof path !== 'string') {
throw errCode(
new Error('Path must be a string'),
'ERR_INVALID_PATH',
{ path }
)
}
const globOptions = Object.assign({}, {
nodir: false,
realpath: false,
absolute: true,
dot: Boolean(options.hidden),
follow: options.followSymlinks != null ? options.followSymlinks : true
})

const absolutePath = Path.resolve(process.cwd(), path)
const stat = await fsp.stat(absolutePath)
const prefix = Path.dirname(absolutePath)
for await (const p of glob(cwd, pattern, globOptions)) {
const stat = await fsp.stat(p)

let mode = options.mode

if (options.preserveMode) {
// @ts-ignore
mode = stat.mode
}

let mtime = options.mtime

if (options.preserveMtime) {
// @ts-ignore
mtime = stat.mtime
}

if (stat.isDirectory()) {
yield {
path: `/${Path.basename(path)}`,
mode,
mtime
}
}

yield * toGlobSource({
path,
type: stat.isDirectory() ? 'dir' : 'file',
prefix,
mode,
mtime,
preserveMode: options.preserveMode,
preserveMtime: options.preserveMtime
}, globSourceOptions)
}
}

// @ts-ignore
async function * toGlobSource ({ path, type, prefix, mode, mtime, preserveMode, preserveMtime }, options) {
options = options || {}

const baseName = Path.basename(path)

if (type === 'file') {
yield {
path: `/${baseName.replace(prefix, '')}`,
content: fs.createReadStream(Path.isAbsolute(path) ? path : Path.join(process.cwd(), path)),
mode,
mtime
}

return
}

if (type === 'dir' && !options.recursive) {
throw errCode(
new Error(`'${path}' is a directory and recursive option not set`),
'ERR_DIR_NON_RECURSIVE',
{ path }
)
}

const globOptions = Object.assign({}, options.glob, {
cwd: path,
nodir: false,
realpath: false,
absolute: true
})

for await (const p of glob(path, '**/*', globOptions)) {
const stat = await fsp.stat(p)

if (preserveMode || preserveMtime) {
if (preserveMode) {
mode = stat.mode
}

if (preserveMtime) {
mtime = stat.mtime
}
}

yield {
path: toPosix(p.replace(prefix, '')),
path: toPosix(p.replace(cwd, '')),
content: stat.isFile() ? fs.createReadStream(p) : undefined,
mode,
mtime
Expand Down
Loading

0 comments on commit d626de1

Please sign in to comment.