Skip to content

Commit

Permalink
Fix to work with special characters. refs #27
Browse files Browse the repository at this point in the history
  • Loading branch information
Ricard Fíguls Mateu committed Dec 24, 2022
1 parent fbf89c8 commit 853fd81
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 26 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ NPM Module to recursive read directory async (non blocking). Returns Promise. Co
>Compatible with Javascript and Typescript projects (with types)
>Works with invalid filenames (special characters)
## Installation
For normal usage into a project, you must install as a NPM dependency. The next command will do all the work:
```
Expand Down Expand Up @@ -110,9 +112,12 @@ The function will return an object and never throw an error. All errors will be
[
{
"name":"item_name",
"nameb":<Buffer .. .. ..>,
"title":"item_name",
"path":"/absolute/path/to/item",
"pathb":<Buffer .. .. ..>,
"fullname":"/absolute/path/to/item/item_name",
"fullnameb":<Buffer .. .. ..>,
"extension":"",
"isDirectory": true,
"stats":{
Expand All @@ -121,9 +126,12 @@ The function will return an object and never throw an error. All errors will be
},
{
"name":"file.txt",
"nameb":<Buffer .. .. ..>,
"title":"file",
"path":"/absolute/path/to/item/item_name",
"pathb":<Buffer .. .. ..>,
"fullname":"/absolute/path/to/item/item_name/file.txt",
"fullnameb":<Buffer .. .. ..>,
"extension":".txt",
"isDirectory": false,
"data": "base64/utf8/etc.",
Expand All @@ -133,9 +141,12 @@ The function will return an object and never throw an error. All errors will be
},
{
"name":"UCASE.JPEG",
"nameb":<Buffer .. .. ..>,
"title":"UCASE",
"path":"/absolute/path/to/item/item_name",
"pathb":<Buffer .. .. ..>,
"fullname":"/absolute/path/to/item/item_name/UCASE.JPEG",
"fullnameb":<Buffer .. .. ..>,
"extension":".jpeg",
"isDirectory": false,
"data": "base64/utf8/etc.",
Expand All @@ -150,19 +161,25 @@ The same example for TREE mode:
[
{
"name":"item_name",
"nameb":<Buffer .. .. ..>,
"title":"item_name",
"path":"/absolute/path/to/item",
"pathb":<Buffer .. .. ..>,
"fullname":"/absolute/path/to/item/item_name",
"fullnameb":<Buffer .. .. ..>,
"isDirectory": true,
"stats":{

},
"content": [
{
"name":"file.txt",
"nameb":<Buffer .. .. ..>,
"title":"file",
"path":"/absolute/path/to/item/item_name",
"pathb":<Buffer .. .. ..>,
"fullname":"/absolute/path/to/item/item_name/file.txt",
"fullnameb":<Buffer .. .. ..>,
"extension":".txt",
"isDirectory": false,
"data": "base64/utf8/etc.",
Expand All @@ -172,9 +189,12 @@ The same example for TREE mode:
},
{
"name":"UCASE.JPEG",
"nameb":<Buffer .. .. ..>,
"title":"UCASE",
"path":"/absolute/path/to/item/item_name",
"pathb":<Buffer .. .. ..>,
"fullname":"/absolute/path/to/item/item_name/UCASE.JPEG",
"fullnameb":<Buffer .. .. ..>,
"extension":".jpeg",
"isDirectory": false,
"data": "base64/utf8/etc.",
Expand Down Expand Up @@ -215,18 +235,24 @@ For errors with files and folders, the error will be added to the item like this
[
{
"name":"item_name",
"nameb":<Buffer .. .. ..>,
"title":"item_name",
"path":"/absolute/path/to/item",
"pathb":<Buffer .. .. ..>,
"fullname":"/absolute/path/to/item/item_name",
"fullnameb":<Buffer .. .. ..>,
"error":{

}
},
{
"name":"file.txt",
"nameb":<Buffer .. .. ..>,
"title":"file",
"path":"/absolute/path/to/item",
"pathb":<Buffer .. .. ..>,
"fullname":"/absolute/path/to/item/file.txt",
"fullnameb":<Buffer .. .. ..>,
"error":{

}
Expand Down
30 changes: 20 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,18 @@ export interface IOptions {
export interface IBase {
/** The filename of the file */
name: string,
/** The filename of the file (buffer version) */
nameb: Buffer,
/** The title of the file (no extension) */
title: string,
/** The path of the file */
path: string,
/** The path of the file (buffer version) */
pathb: Buffer,
/** The fullname of the file (path & name & extension) */
fullname: string,
/** The fullname of the file (path & name & extension buffer version) */
fullnameb: Buffer,
/** The extension of the file with dot in lowercase */
extension?: string,
/** The depth of current content */
Expand Down Expand Up @@ -219,9 +225,9 @@ let pathSimbol = '/';
* @returns {Promise<fs.Stats>} stat object information
* @async
*/
export async function stat(file:string): Promise<_fs.Stats> {
export async function stat(buffer:Buffer): Promise<_fs.Stats> {
return new Promise(function(resolve, reject) {
FS.stat(file, function(err: any, stats: _fs.Stats) {
FS.stat(buffer, function(err: any, stats: _fs.Stats) {
if (err) {
reject(err);
} else {
Expand All @@ -239,7 +245,7 @@ export async function stat(file:string): Promise<_fs.Stats> {
* @returns {Promise<string>} data content string (base64 format by default)
* @async
*/
export async function readFile(file: string, encoding: BufferEncoding|undefined = 'base64'): Promise<string> {
export async function readFile(file: Buffer, encoding: BufferEncoding|undefined = 'base64'): Promise<string> {
return new Promise(function(resolve, reject) {
FS.readFile(file, { encoding }, function(err: any, data: string) {
if (err) {
Expand Down Expand Up @@ -298,7 +304,7 @@ function addOptionalKeys(obj:IBase, file: string, settings: IOptions, deep: numb
* @returns void
*/
function read(rpath: string, data: any, settings: IOptions, deep: number, resolve: any, reject: any) {
FS.readdir(rpath, function(error: any, files: string[]) {
FS.readdir(rpath, 'buffer', function(error: any, files: Buffer[]) {
// If error reject them
if (error) {
reject(error);
Expand All @@ -309,14 +315,18 @@ function read(rpath: string, data: any, settings: IOptions, deep: number, resolv
};
// Iterate through elements (files and folders)
for (const file of files) {
const path = rpath + (rpath.endsWith(pathSimbol) ? '' : pathSimbol);
const obj:IBase = {
name: file,
title: removeExt(file),
name: file.toString(),
nameb: file,
title: removeExt(file.toString()),
path: rpath,
fullname: rpath + (rpath.endsWith(pathSimbol) ? '' : pathSimbol) + file,
pathb: Buffer.from(rpath),
fullname: path + file.toString(),
fullnameb: Buffer.concat([Buffer.from(path), file]),
};
if (checkItem(obj.fullname, settings)) {
addOptionalKeys(obj, file, settings, deep);
addOptionalKeys(obj, file.toString(), settings, deep);
data.push(obj);
}
}
Expand Down Expand Up @@ -479,13 +489,13 @@ async function statDir(
async function statDirItem(
collection:(IFile|IFolder)[], i: number, settings: IOptions, progress: Function|undefined, deep: number,
):Promise<(IFile|IFolder)[]> {
const stats = await stat(collection[i].fullname);
const stats = await stat(collection[i].fullnameb);
collection[i].isDirectory = stats.isDirectory();
if (settings.stats) {
(collection[i] as IFile).stats = stats;
}
if (settings.readContent && !collection[i].isDirectory) {
(collection[i] as IFile).data = await readFile(collection[i].fullname, settings.encoding);
(collection[i] as IFile).data = await readFile(collection[i].fullnameb, settings.encoding);
}
if (collection[i].isDirectory && settings.recursive) {
const item: IFolder = collection[i];
Expand Down
50 changes: 34 additions & 16 deletions test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
import assert from 'assert';
import path from 'path';
import * as rra from '../src/index';

function createDir(route:string) {
const fs = require('fs-extra');
if(!fs.existsSync(route)) {
fs.mkdirSync(route);
}
}
// UT testing
describe('load', function() {
// Creating test structure
const fs = require('fs-extra');
const root_path = Buffer.from('./test');
const path_sep = Buffer.from('/');
const fname = Buffer.from('842E747874',"hex");
const fullpath_buf = Buffer.concat([root_path, path_sep, fname]);
fs.writeFileSync(fullpath_buf ,"invalidFilename");

try {
fs.removeSync('./test/test/');
} catch (err) {
// ignore
}
fs.mkdirSync('./test/test/');
fs.mkdirSync('./test/test/folder1/');
fs.mkdirSync('./test/test/folder1/subfolder1/');
fs.mkdirSync('./test/test/folder1/subfolder1/subsubf1/');
createDir('./test/test/');
createDir('./test/test/folder1/');
createDir('./test/test/folder1/subfolder1/');
createDir('./test/test/folder1/subfolder1/subsubf1/');
createDir('./test/test/folder2/');
createDir('./test/test/folder2/subfolder2/');
createDir('./test/test/folder3/');
fs.writeFileSync('./test/test/folder1/file1.TXT', 'some');
fs.writeFileSync('./test/test/folder1/subfolder1/subsubf1/subfile1.txt', 'something');
fs.mkdirSync('./test/test/folder2/');
fs.mkdirSync('./test/test/folder2/subfolder2/');
fs.mkdirSync('./test/test/folder3/');
fs.writeFileSync('./test/test/folder3/file3.txt', 'some');
fs.writeFileSync('./test/test/folder3/noext', '1234');

Expand Down Expand Up @@ -226,7 +239,7 @@ describe('usage', function() {
assert.equal(prom[0].data, 'c29tZXRoaW5n', 'unexpected unencoded data: "' + prom[0].data + '"');
});
it('should return base64 data if undefined in parameter', async function() {
const prom:any = await rra.readFile('./test/test/folder1/subfolder1/subsubf1/subfile1.txt');
const prom:any = await rra.readFile(Buffer.from('./test/test/folder1/subfolder1/subsubf1/subfile1.txt'));
assert.equal(prom, 'c29tZXRoaW5n', 'unexpected response data: "' + prom[0].data + '"');
});
it('should return title and extension as expected', async function() {
Expand Down Expand Up @@ -306,7 +319,7 @@ describe('error control', function() {
it('controlled error for stat (must throw error)', async function() {
let isOk = false;
try {
await rra.stat('./test/test/inexistent.file');
await rra.stat(Buffer.from('./test/test/inexistent.file'));
} catch (error) {
isOk = true;
}
Expand All @@ -315,7 +328,7 @@ describe('error control', function() {
it('controlled error for readFile (must throw error)', async function() {
let isOk = false;
try {
await rra.readFile('./test/test/inexistent.file');
await rra.readFile(Buffer.from('./test/test/inexistent.file'));
} catch (error) {
isOk = true;
}
Expand All @@ -325,13 +338,11 @@ describe('error control', function() {
it('controlled error for exceptions - part 1: subtree fatality', async function() {
let isOk = false;
try {
let count = 0;
const res = await rra.list('./test', function progressUserCallback() {
// fake failure.
count++;
if (count === 3) throw new Error('boom!');
const res = await rra.list('./test/test', function progressUserCallback(obj:any) {
if (obj.name === 'noext') throw new Error('boom!');
});
if (!res.error && res[2].error && !res[0].error) {
const theError = res.filter((item:any) => item.error)
if (!res.error && theError[0].name === 'noext' && theError.length === 1 && res.length > 1 ) {
isOk = true;
};
assert.equal(isOk, true, 'unexpected behavior (no json with error)');
Expand Down Expand Up @@ -376,4 +387,11 @@ describe('error control', function() {
});
assert.equal(result, true, 'no custom property detected!');
});

it('should read invalid filenames (buffer used)', async () => {
const list = await rra.list('./test', { readContent: true, encoding: 'utf8' });
console.log(list);
const invalidItem = list.find((item:any) => item.data === 'invalidFilename');
assert.equal(invalidItem !== undefined, true, 'unable to read incorrect filenames');
});
});

0 comments on commit 853fd81

Please sign in to comment.