library-ts/node/files/Files.ts

415 lines
8.6 KiB
TypeScript
Raw Normal View History

2025-03-31 12:00:55 +00:00
import { promises as fs } from "fs";
import * as path from "path";
import { RJLog } from "../log/RJLog";
import { PathReference } from "./PathReference";
import { DOMShim } from "../DOMShim";
import { DateMath } from "../../browser/date/DateMath";
export class Files
{
2025-11-10 17:41:48 +00:00
static escapePathFragment( unescapedPath: string, replacementCharacter: string = "_" ): string
{
let invalidChars = /[\\/:*?"<>|]/g;
let safe = unescapedPath.replace( invalidChars, replacementCharacter )
let reserved = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;
if ( reserved.test( safe ) )
{
safe = `_${safe}_`;
}
return safe;
}
2025-03-31 12:00:55 +00:00
static parentPath( filePath:string )
{
return path.dirname( filePath );
}
2025-11-10 17:41:48 +00:00
static async copy( from:string, to:string )
{
let stat = await fs.stat(from);
if ( stat.isDirectory() )
{
await fs.mkdir( to, { recursive: true } );
let entries = await fs.readdir( from );
for ( let entry of entries)
{
let srcPath = path.join( from, entry );
let destPath = path.join( to, entry );
await Files.copy(srcPath, destPath);
}
}
else
{
await fs.copyFile(from, to);
}
}
2025-03-31 12:00:55 +00:00
static async forAllIn( filePath:string, filter:(p:PathReference)=>Promise<boolean> = null, action:(p:PathReference)=>Promise<void> = null ):Promise<PathReference[]>
{
let files = await fs.readdir( filePath );
let root = new PathReference( filePath );
let pathReferences = files.map( f => root.createRelative( f ) );
if ( filter )
{
let filteredPaths = [];
for ( let p of pathReferences )
{
if ( await filter( p ) )
{
filteredPaths.push( p );
}
}
pathReferences = filteredPaths;
};
for ( let p of pathReferences )
{
let isDirectory = await p.isDirectory();
if ( isDirectory )
{
await this.forAllIn( p.absolutePath, filter, action );
}
}
if ( action )
{
for ( let p of pathReferences )
{
await action( p );
}
}
return Promise.resolve( pathReferences );
}
static async forDirectChildrenIn( filePath:string, filter:(p:PathReference)=>Promise<boolean> = null, action:(p:PathReference)=>Promise<void> = null ):Promise<PathReference[]>
{
let files = await fs.readdir( filePath );
let root = new PathReference( filePath );
let pathReferences = files.map( f => root.createRelative( f ) );
if ( filter )
{
let filteredPaths = [];
for ( let p of pathReferences )
{
if ( await filter( p ) )
{
filteredPaths.push( p );
}
}
pathReferences = filteredPaths;
};
if ( action )
{
for ( let p of pathReferences )
{
await action( p );
}
}
return Promise.resolve( pathReferences );
}
static joinPaths( pathFragments:string[] )
{
return path.join.apply( path, pathFragments );
}
static async existsIn( directoryPath:string, fileName:string ):Promise<boolean>
{
let combinedPath = Files.joinPaths( [ directoryPath, fileName ] );
let result = await Files.exists( combinedPath );
return Promise.resolve( result );
}
static async getStatistics( filePath:string )
{
return fs.stat( filePath );
}
static async getModificationDate( filePath:string ):Promise<Date>
{
let stats = await this.getStatistics( filePath );
if ( ! stats )
{
return Promise.resolve( null );
}
return new Date( stats.mtimeMs );
}
static async isNewerThan( filePath:string, date:Date ):Promise<boolean>
{
let mDate = await this.getModificationDate( filePath );
return Promise.resolve( DateMath.isAfter( mDate, date ) );
}
static async isDirectory( filePath:string ):Promise<boolean>
{
try
{
let stats = await fs.stat( filePath );
return Promise.resolve( stats !== null && stats !== undefined && stats.isDirectory() );
}
catch( e )
{
return false;
}
return Promise.resolve( false );
}
static async isFile( filePath:string ):Promise<boolean>
{
try
{
let stats = await fs.stat( filePath );
return Promise.resolve( stats !== null && stats !== undefined && stats.isFile() );
}
catch( e )
{
return false;
}
return Promise.resolve( false );
}
static async isSymbolicLink( filePath:string ):Promise<boolean>
{
try
{
let stats = await fs.stat( filePath );
return Promise.resolve( stats !== null && stats !== undefined && stats.isSymbolicLink() );
}
catch( e )
{
return false;
}
return Promise.resolve( false );
}
static async exists( filePath:string ):Promise<boolean>
{
try
{
let stats = await fs.stat( filePath );
return Promise.resolve( stats !== null && stats !== undefined );
}
catch( e )
{
return false;
}
return Promise.resolve( false );
}
static async ensureDirectoryExists( path:string ):Promise<void>
{
let exists = await Files.exists( path );
if ( exists )
{
return Promise.resolve();
}
await fs.mkdir( path, { recursive: true } );
return Promise.resolve();
}
static async ensureParentDirectoryExists( filePath:string ):Promise<void>
{
let path = Files.parentPath( filePath );
let exists = await Files.exists( path );
if ( exists )
{
return Promise.resolve();
}
2025-11-10 17:41:48 +00:00
await fs.mkdir( path, { recursive:true } );
2025-03-31 12:00:55 +00:00
return Promise.resolve();
}
static async deleteFile( filePath:string ):Promise<boolean>
{
try
{
await fs.unlink( filePath );
}
catch( e )
{
return Promise.resolve( false );
}
return Promise.resolve( true );
}
static async loadUTF8( filePath:string ):Promise<string>
{
try
{
let data = await fs.readFile( filePath );
let stringData = data.toString();
return Promise.resolve( stringData );
}
catch ( exception )
{
RJLog.log( exception );
}
return Promise.resolve( null );
}
static async moveFile( oldPath:string, newPath:string )
{
try
{
await fs.rename( oldPath, newPath );
}
catch ( e )
{
await fs.copyFile( oldPath, newPath );
await fs.unlink( oldPath );
}
}
static async loadJSON<T>( filePath:string ):Promise<T>
{
let text = await Files.loadUTF8( filePath );
if ( text === null )
{
return Promise.resolve( null );
}
try
{
let jsonObject = JSON.parse( text );
return Promise.resolve( jsonObject as T );
}
catch ( exception )
{
RJLog.log( exception );
}
return Promise.resolve( null );
}
static async saveUTF8( filePath:string, text:string ):Promise<boolean>
{
try
{
await fs.writeFile( filePath, text );
return Promise.resolve( true );
}
catch ( exception )
{
RJLog.log( exception );
}
return Promise.resolve( false );
}
2025-11-10 17:41:48 +00:00
static async saveJSON<T>( filePath:string, data:T, pretty:boolean = true ):Promise<boolean>
2025-03-31 12:00:55 +00:00
{
try
{
2025-11-10 17:41:48 +00:00
let jsonData = pretty ? JSON.stringify( data, null, " " ) : JSON.stringify( data );
2025-03-31 12:00:55 +00:00
let result = await Files.saveUTF8( filePath, jsonData );
return Promise.resolve( result );
}
catch( e )
{
RJLog.log( e );
}
return Promise.resolve( false );
}
static async saveXML( filePath:string, rootElement:Element ):Promise<void>
{
let domShim = DOMShim.$;
let nodeName = rootElement.nodeName;
let xmlHeader = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>` + "\n";
let serializedXML = `${xmlHeader}<${nodeName}>${rootElement.innerHTML}</${nodeName}>` ;
//RJLog.log( rootElement, serializedXML );
await Files.saveUTF8( filePath, serializedXML );
return Promise.resolve();
}
static async loadXML( filePath:string ):Promise<Document>
{
let domShim = DOMShim.$;
let stringData = await Files.loadUTF8( filePath );
let parser = new DOMParser();
let xmlDoc = parser.parseFromString( stringData, "text/xml" );
return xmlDoc;
}
static async loadHTML( filePath:string ):Promise<Document>
{
let domShim = DOMShim.$;
let stringData = await Files.loadUTF8( filePath );
let parser = new DOMParser();
let xmlDoc = parser.parseFromString( stringData, "text/html" );
return xmlDoc;
}
static async saveHTML( filePath:string, rootElement:Element ):Promise<void>
{
let domShim = DOMShim.$;
await Files.saveUTF8( filePath, rootElement.outerHTML );
return Promise.resolve();
}
}