From 101ab65abb737ed499ab4041faa9d03aa5e3586a Mon Sep 17 00:00:00 2001 From: Divyansh Joshi Date: Tue, 16 Jun 2026 13:37:30 +0530 Subject: [PATCH 1/3] feat: implement custom glob utility using node:fs --- lib/glob.js | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 lib/glob.js diff --git a/lib/glob.js b/lib/glob.js new file mode 100644 index 0000000..5c8eecb --- /dev/null +++ b/lib/glob.js @@ -0,0 +1,85 @@ +'use strict' + +const fs = require('node:fs/promises') +const path = require('node:path') + +function globToRegex (pattern) { + let p = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&') + p = p.replace(/\*\*\//g, '___GLOBSTAR___') + p = p.replace(/\*\*/g, '.*') + p = p.replace(/\*/g, '[^/]*') + p = p.replace(/___GLOBSTAR___/g, '(?:.*/)?') + return new RegExp('^' + p + '$') +} + +function isIgnored(relativePath, ignorePatterns) { + const patterns = [].concat(ignorePatterns) + return patterns.some(pattern => globToRegex(pattern).test(relativePath)) +} + +async function findFiles (dir, opts, baseDir = dir) { + const files = [] + let list + try { + list = await fs.readdir(dir, { withFileTypes: true }) + } catch (err) { + return files + } + + for (const entry of list) { + const fullPath = path.join(dir, entry.name) + const relativePath = path.relative(baseDir, fullPath).split(path.win32.sep).join(path.posix.sep) + + const isDotFile = entry.name.startsWith('.') + if (isDotFile && !opts.serveDotFiles) { + continue + } + + if (opts.globIgnore && isIgnored(relativePath, opts.globIgnore)) { + continue + } + + let isDirectory = entry.isDirectory() + let isFile = entry.isFile() + + if (entry.isSymbolicLink()) { + try { + const stat = await fs.stat(fullPath) + isDirectory = stat.isDirectory() + isFile = stat.isFile() + } catch (err) { + continue + } + } + + if (isDirectory) { + files.push(...(await findFiles(fullPath, opts, baseDir))) + } else if (isFile) { + files.push(relativePath) + } + } + return files +} + +async function glob (pattern, options, cb) { + if (typeof options === 'function') { + cb = options + options = {} + } + const opts = { + serveDotFiles: options.dot, + globIgnore: options.ignore + } + + if (typeof cb === 'function') { + findFiles(options.cwd, opts).then( + files => cb(null, files), + err => cb(err) + ) + return + } + + return findFiles(options.cwd, opts) +} + +module.exports = { glob } From f9b24102e4ba46d1d20cdd78a34c2d885f541a1a Mon Sep 17 00:00:00 2001 From: Divyansh Joshi Date: Tue, 16 Jun 2026 13:37:32 +0530 Subject: [PATCH 2/3] refactor: replace external glob dependency with custom glob utility --- index.js | 2 +- package.json | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 7adaa99..259d9aa 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,7 @@ const path = require('node:path') const { fileURLToPath } = require('node:url') const { statSync } = require('node:fs') -const { glob } = require('glob') +const { glob } = require('./lib/glob') const fp = require('fastify-plugin') const send = require('@fastify/send') const encodingNegotiator = require('@fastify/accept-negotiator') diff --git a/package.json b/package.json index 2729191..9c9499e 100644 --- a/package.json +++ b/package.json @@ -63,8 +63,7 @@ "@fastify/send": "^4.0.0", "content-disposition": "^1.0.1", "fastify-plugin": "^5.0.0", - "fastq": "^1.17.1", - "glob": "^13.0.0" + "fastq": "^1.17.1" }, "devDependencies": { "@fastify/compress": "^8.0.0", From f92378635068c12077b4cb3f7ed3709839a687ed Mon Sep 17 00:00:00 2001 From: Divyansh Joshi Date: Tue, 16 Jun 2026 13:37:35 +0530 Subject: [PATCH 3/3] test: update proxyquire mock and add test cases for custom glob helper --- test/static.test.js | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/test/static.test.js b/test/static.test.js index de2d413..a806f6c 100644 --- a/test/static.test.js +++ b/test/static.test.js @@ -2517,10 +2517,16 @@ test('if dotfiles are properly served according to plugin options', async (t) => test('register with failing glob handler', async (t) => { const fastifyStatic = proxyquire.noCallThru()('../', { - glob: function globStub (_pattern, _options, cb) { - process.nextTick(function () { - return cb(new Error('mock glob error')) - }) + './lib/glob': { + glob: function globStub (_pattern, _options, cb) { + if (typeof cb === 'function') { + process.nextTick(function () { + cb(new Error('mock glob error')) + }) + return + } + return Promise.reject(new Error('mock glob error')) + } } }) @@ -4150,3 +4156,32 @@ test('register with wildcard false and globIgnore', async t => { await response.text() }) }) + +test('custom glob helper coverage', async (t) => { + const { glob } = require('../lib/glob') + + await t.test('glob with non-existent directory returns empty array', async (t) => { + const files = await glob('**/**', { cwd: './non-existent-directory' }) + t.assert.deepStrictEqual(files, []) + }) + + await t.test('glob with callback and options', async (t) => { + await new Promise((resolve, reject) => { + glob('**/**', { cwd: './non-existent-directory' }, (err, files) => { + if (err) return reject(err) + t.assert.deepStrictEqual(files, []) + resolve() + }) + }) + }) + + await t.test('glob with callback and no options', async (t) => { + await new Promise((resolve, reject) => { + glob('**/**', (err, files) => { + if (err) return reject(err) + t.assert.deepStrictEqual(files, []) + resolve() + }) + }) + }) +})