Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(treeu): flat mapper now accepts 'constructor' and 'result' as valid keys #1524

Merged
merged 6 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/silver-clocks-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@httpx/treeu": patch
---

Fix FlatTreeWsMapper.toTreeNodes(), allow 'result' as a valid key name
6 changes: 6 additions & 0 deletions .changeset/thin-toys-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@httpx/treeu": patch
---

Fix FlatTreeWsMapper.toTreeNodes(), allow 'constructor' as a valid key name

2 changes: 2 additions & 0 deletions packages/treeu/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"bench-watch": "vitest bench",
"docgen": "run-s docgen-typedoc",
"docgen-typedoc": "rimraf ./docs/api && typedoc --plugin typedoc-plugin-markdown --out ./docs/api",
"create-fixtures": "tsx ./scripts/create-fixtures.ts",
"check-dist": "run-s check-dist-esm check-dist-cjs",
"check-dist-cjs": "es-check --not './dist/*.map.js' -v es2022 './dist/**/*.cjs'",
"check-dist-esm": "es-check --not './dist/*.map.js' -v es2022 --module './dist/**/*.mjs'",
Expand Down Expand Up @@ -91,6 +92,7 @@
"rollup": "4.31.0",
"size-limit": "11.1.6",
"tsup": "8.3.5",
"tsx": "4.19.2",
"typedoc": "0.27.6",
"typedoc-plugin-markdown": "4.4.1",
"typescript": "5.7.3",
Expand Down
85 changes: 85 additions & 0 deletions packages/treeu/scripts/create-fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { type Stats, statSync } from 'node:fs';
import path from 'node:path';

import { globSync } from 'tinyglobby';

import { DfsTreeSearch } from '../src';
import {
type FlatTreeWsMap,
FlatTreeWsMapper,
} from '../src/mapper/flat-tree-ws-mapper';

const baseDir = path.dirname(path.dirname(process.cwd()));
console.log(baseDir);

const files = globSync(['**/*', '!.*', '!**/.cache'], {
cwd: baseDir,
absolute: false,
dot: true,
expandDirectories: true,
onlyDirectories: false,
onlyFiles: false,
});

type PathStats = {
type:
| 'file'
| 'directory'
| 'socket'
| 'fifo'
| 'symlink'
| 'block-device'
| 'character-device';
size: number;
atime: Date;
mtime: Date;
ctime: Date;
birthtime: Date;
};

const flat: FlatTreeWsMap<PathStats> = new Map();

const getFileType = (stats: Stats): PathStats['type'] => {
switch (true) {
case stats.isFile():
return 'file';
case stats.isDirectory():
return 'directory';
case stats.isFIFO():
return 'fifo';
case stats.isSocket():
return 'socket';
case stats.isBlockDevice():
return 'block-device';
case stats.isCharacterDevice():
return 'character-device';
case stats.isSymbolicLink():
return 'symlink';
default:
throw new Error('Unsupported file type');
}
};

for (const file of files) {
const stats = statSync(path.join(baseDir, file));
const fileWithSeparator = file.replace(/\/$/, '');
flat.set(fileWithSeparator, {
type: getFileType(stats),
size: stats.size,
atime: stats.atime,
ctime: stats.ctime,
birthtime: stats.birthtime,
mtime: stats.mtime,
});
}

console.log(flat.keys());
// throw new Error('cool');
const tree = new FlatTreeWsMapper<PathStats>().toTreeNodesOrThrow(flat, {
separator: '/',
});

const search = new DfsTreeSearch<PathStats>(tree);
const first = search.findOne((node) => node.id.includes('.d.ts'));

console.log(first);
31 changes: 26 additions & 5 deletions packages/treeu/src/mapper/__tests__/flat-tree-ws-mapper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@ type CustomValue =
| { type: 'file'; size: number };

describe('FlatTreeWsMapper', () => {
const validFlatTreeWs: FlatTreeWs<CustomValue, string> = new Map([
const validFlatTreeWs: FlatTreeWs<CustomValue> = new Map([
['file1.ts', { type: 'file', size: 10 }],
['file2.ts', { type: 'file', size: 20 }],
['folder1', { type: 'folder' }],
['folder1/file1.ts', { type: 'file', size: 30 }],

['folder2', { type: 'folder' }],
['folder2/file1.ts', { type: 'file', size: 40 }],
['folder2/subfolder1', { type: 'folder' }],
['folder2/subfolder1/file1.ts', { type: 'file', size: 50 }],
['folder3', { type: 'folder' }],

// Testing reserved "constructor" keyword
['constructor', { type: 'folder' }],
['constructor/constructor.d.ts', { type: 'file', size: 50 }],
]);

const validTreeNodes: TreeNode<CustomValue>[] = [
Expand Down Expand Up @@ -96,12 +100,22 @@ describe('FlatTreeWsMapper', () => {
],
},
{
id: 'folder3',
id: 'constructor',
parentId: null,
value: {
type: 'folder',
},
children: [],
children: [
{
id: 'constructor/constructor.d.ts',
parentId: 'constructor',
value: {
size: 50,
type: 'file',
},
children: [],
},
],
},
];

Expand All @@ -123,11 +137,18 @@ describe('FlatTreeWsMapper', () => {
},
],
[
'folder3',
'constructor',
{
type: 'folder',
},
],
[
'constructor/constructor.d.ts',
{
size: 50,
type: 'file',
},
],
[
'folder1/file1.ts',
{
Expand Down
40 changes: 32 additions & 8 deletions packages/treeu/src/mapper/flat-tree-ws-mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type FlatTreeWsParams = {
separator: string;
};

// eslint-disable-next-line sonarjs/redundant-type-aliases
type FlatTreeWsUniqueKey = string;

export type FlatTreeWsMap<
Expand All @@ -22,11 +23,21 @@ export type FlatTreeWs<
TKey extends FlatTreeWsUniqueKey = string,
> = FlatTreeWsMap<TValue, TKey> | FlatTreeWsRecord<TValue, TKey>;

const sym = Symbol('@@result');
/*
type CollectorContext<
TValue extends TreeNodeValue | undefined = undefined,
TKey extends string = string,
> = Record<'result', TreeNode<TValue, TKey>[]> & Record<string, unknown>;
*/

interface CollectorContext<
TValue extends TreeNodeValue | undefined = undefined,
TKey extends string = string,
> {
[sym]: TreeNode<TValue, TKey>[];
[key: string]: unknown;
}
export const flatTreeWsMapperErrors = {
toTreeNodes: {
parsedErrorMsg: `Can't convert the flat tree to tree nodes`,
Expand Down Expand Up @@ -59,7 +70,7 @@ export class FlatTreeWsMapper<
): TreeMapperResult<TValue, TKey> => {
const { separator } = params;

const collector: CollectorContext<TValue, TKey> = { result: [] };
const collector: CollectorContext<TValue, TKey> = { [sym]: [] };

// eslint-disable-next-line no-restricted-syntax
const d = data instanceof Map ? data : new Map(Object.entries(data));
Expand All @@ -81,14 +92,19 @@ export class FlatTreeWsMapper<
`${flatTreeWsMapperErrors.toTreeNodes.issues.SPLIT_EMPTY_KEY}`
);
}
if (!(name in context)) {
context[name] = { result: [] };
if (!Object.hasOwn(context, name)) {
Object.defineProperty(context, name, {
value: { [sym]: [] },
writable: false,
});

const parents = splitted.slice(0, -1) as TKey[];
const parentId = (parents.length > 0
? parents.join(separator)
: null) as unknown as TKey | null;
const children = (context[name] as CollectorContext<TValue, TKey>)
.result;
const children = (context[name] as CollectorContext<TValue, TKey>)[
sym
];
const node: TreeNode<TValue, TKey> = {
id: trimmedKey,
parentId: parentId,
Expand All @@ -97,7 +113,10 @@ export class FlatTreeWsMapper<
if (value !== undefined) {
node.value = value as TValue;
}
context.result.push(node);
if (!Array.isArray(context[sym])) {
throw new TypeError(JSON.stringify(node));
}
context[sym].push(node);
}
context = context[name] as CollectorContext<TValue, TKey>;
}
Expand All @@ -111,7 +130,7 @@ export class FlatTreeWsMapper<
}
return {
success: true,
treeNodes: collector.result,
treeNodes: collector[sym],
};
};

Expand All @@ -124,7 +143,12 @@ export class FlatTreeWsMapper<
): TreeNode<TValue, TKey>[] => {
const result = this.toTreeNodes(data, params);
if (!result.success) {
throw new Error(result.message);
const { message, issues } = result;
const errors =
issues.length > 0
? issues.map((issue) => issue.message).join(', ')
: null;
throw new Error(`${message} (${errors})`);
}
return result.treeNodes;
};
Expand Down
Loading
Loading