UNPKG

struct-fsraw

Version:

Creates a structured representation of a directory, including file metadata, file size, symlinks.

166 lines (142 loc) 5.11 kB
/* ******************************************************* * struct-fsraw * * @license * * Apache-2.0 * * Copyright 2015-2025 Alex Stevovich * * 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. * * @meta * * package_name: struct-fsraw * file_name: src/index.js * purpose: Core functionality and exports combined. * * @system * * generated_on: 2025-03-15T02:26:35.619Z * certified_version: 1.0.0 * file_uuid: 5eb475a8-731e-4363-a052-cefbfd2e23ea * file_size: 5114 bytes * file_hash: 276048f2fcd32d43f21643335b4499862eea47cf7ae0437c4e8775524830c21d * mast_hash: 249f825a123d38329cecb352dadd75bf0bbf20ede1008775768ab08b10c85fef * generated_by: preamble on npm! * * [Preamble Metadata] ********************************************************/ import fs from 'fs/promises'; import path from 'path'; // Key mapping function to dynamically get the correct key name function getKey(keys, key) { return keys?.[key] || key; // Use mapped key if available, otherwise use full key } export function getKeys() { return { path: 'p', type: 't', symlink: 'sl', symlink_target: 'sl_t', symlink_resolved: 'sl_r', children: 'c', size: 's', created_at: 'm_c', modified_at: 'm_m', }; } export async function struct(dir, options = {}, depth = 0) { if (!dir) { throw new Error("The 'dir' parameter is required."); } const { meta = false, size = false, recursive = false, prune = false, keys = {}, // Custom key mapping object } = options; // **Kick off fs.lstat early** const statsPromise = fs.lstat(dir); // **Start readdir if at depth 0 or recursive mode is enabled** const dirContentsPromise = depth === 0 || recursive ? fs.readdir(dir).catch(() => null) : null; // **Await file metadata** let stats; try { stats = await statsPromise; } catch (err) { console.error(`Error reading file stats: ${dir}`, err); return null; } const symlink = stats.isSymbolicLink(); const displayPath = path.resolve(dir); // **Start symlink resolution asynchronously** const symlinkTargetPromise = symlink ? fs.realpath(dir).catch(() => null) : Promise.resolve(null); // **Construct file object with dynamic keys** const fileObject = { [getKey(keys, 'path')]: displayPath, [getKey(keys, 'type')]: stats.isDirectory() ? 'd' : 'f', [getKey(keys, 'symlink')]: symlink, }; // **Await symlink resolution in parallel** const symlink_target = await symlinkTargetPromise; if (!prune || symlink) { fileObject[getKey(keys, 'symlink_target')] = symlink_target; fileObject[getKey(keys, 'symlink_resolved')] = symlink_target ? path.resolve(symlink_target) : null; } if (size) { fileObject[getKey(keys, 'size')] = stats.size; } if (meta) { fileObject[getKey(keys, 'created_at')] = stats.birthtime; fileObject[getKey(keys, 'modified_at')] = stats.mtime; } if (symlink) { return fileObject; // **Return early for symlinks (no recursion)** } // **Ensure directories always have `children: []` when recursive is false** if (stats.isDirectory()) { fileObject[getKey(keys, 'children')] = []; // Default to empty array const dirContents = await dirContentsPromise; if (dirContents) { fileObject[getKey(keys, 'children')] = await Promise.all( dirContents.map(async (child) => { const childPath = path.join(dir, child); if (recursive) { return struct(childPath, options, depth + 1); } // **Only include top-level files & directories, no recursion** const childStats = await fs.lstat(childPath); return { [getKey(keys, 'path')]: path.resolve(childPath), [getKey(keys, 'type')]: childStats.isDirectory() ? 'd' : 'f', [getKey(keys, 'symlink')]: childStats.isSymbolicLink(), [getKey(keys, 'children')]: childStats.isDirectory() ? [] : undefined, // Ensure directories always have empty `children` }; }), ); } } return fileObject; } export default struct;