Files
Unify/framework/server/filemanager.js
2025-12-25 11:16:59 +01:00

679 lines
12 KiB
JavaScript

/*
Copyright (c) 2020, 2023, The Unified Company.
This code is part of Unify.
This program is free software; you can redistribute it and/or modify
it under the terms of the ESA Software Community License - Strong Copyleft LICENSE,
as published by the ESA.
See the ESA Software Community License - Strong Copyleft LICENSE, for more details.
https://unifyjs.org
*/
import file from './file.js';
import path from 'path';
import fs from 'fs-extra';
import fse from "fs-extra";
import tools from '../unify/tools.js';
import multiExtender from './multiExtender.js';
import objectParser from './objectParser.js';
import cacheDisabler from './cacheDisabler.js';
export default class filemanager{
applications = new Array();
files = new Array();
directories = new Array();
replacements = new Array();
cores = new Array();
pragmas = new Array();
allFiles = new Array();
storeSource = true;
cache_folder_depth = 0;
multiExtender = new multiExtender();
objectParser = new objectParser();
cacheDisabler = new cacheDisabler();
crossPlatformPath( path ) {
return tools.slash( path );
}
constructor( cache_directory ) {
if( !cache_directory ) {
return false;
}
this.cache_directory = this.crossPlatformPath( cache_directory );
this.writeRandomString()
}
async writeRandomString() {
this.randomString = String(Math.floor(Math.random() * 1000 )).padStart(4, '0');
this.cacheDisabler.randomString = this.randomString;
await fs.writeFileSync( "./framework/configs/randomString.json", this.randomString );
}
routeDirectory( directory, relativeDirectory ) {
var path = "framework";
var subPath = "unify";
var files = fs.readdirSync( directory );
for (var i = 0; i < files.length; i++) {
var fileName = files[i];
var fileLocation = relativeDirectory + fileName;
this.replace( fileLocation );
}
}
addCustomReplacements() {
this.addReplacement( "/elements/", "/"+this.cache_directory+"elements/");
this.addReplacement( "/user/", "/"+this.cache_directory+"user/" );
this.replace( "/engine/");
this.replace( "/3rdparty/");
this.routeDirectory( "framework/unify", "/unify/" );
this.routeDirectory( "framework/server", "/server/" );
this.routeDirectory( "framework/client", "/client/" );
this.routeDirectory( "framework/unify/sql", "/unify/sql/" );
this.routeDirectory( "framework/unify/math", "/unify/math/" );
}
replace( source ) {
var target = "/framework" + source;
this.addReplacement(source, target);
}
addReplacement( source, target ) {
var replacement = new Object();
replacement.source = source;
replacement.target = target;
this.replacements.push( replacement );
}
readFileListCache( filesJson ) {
this.files = filesJson;
}
getFiles() {
return this.files;
}
writeFileListCache() {
var json = "export default " + JSON.stringify( this.files ) + ";";
fs.writeFileSync( "./framework/configs/files.js", json );
var json = "export default " + JSON.stringify( this.applications ) + ";";
fs.writeFileSync( "./framework/configs/applications.js", json );
}
async updateCache() {
await this.findApplicationFiles( "./application");
await this.emptyCacheDirectory();
await this.createDirectories();
await this.writeFiles();
}
addReplacement( source, target ) {
var replacement = new Object();
replacement.source = source;
replacement.target = target;
this.replacements.push( replacement );
}
async createDir( path ) {
fse.ensureDirSync( path );
}
async emptyCacheDirectory() {
fs.emptyDirSync( this.cache_directory );
}
async createDirectory( directory ) {
if( this.temporary_cache_directory ) {
//console.log(path.resolve( this.temporary_cache_directory + directory ));
fs.ensureDirSync( path.resolve( this.temporary_cache_directory + directory ) );
} else {
fs.ensureDirSync( path.resolve( this.cache_directory + directory ) );
}
}
async createDirectories() {
var directories = this.directories.reverse();
for( var c = 0; c < directories.length; c++ ) {
var directory = directories[c];
this.createDirectory( directory );
}
return true;
}
definePragma( pragma ) {
this.pragmas.push( pragma );
}
cleanURL( source ) {
var token = "//";
for ( var i = 0; i < 4; i++ ) {
token += "/";
source = source.replaceAll( token, "/" );
}
source = source.replaceAll('..//', '../');
return source;
}
definePragmas( source ) {
for ( var i = 0; i < this.pragmas.length; i++ ) {
var pragma = this.pragmas[i];
source = "\n\n#define " + pragma.toUpperCase() + " \n\n" + source;
}
return source;
}
writeFileToDisk( source, path, filename ) {
if( this.temporary_cache_directory ) {
fs.writeFileSync( this.temporary_cache_directory + path + "/" + filename, source );
} else {
fs.writeFileSync( this.cache_directory + path + "/" + filename, source );
}
}
composeFile( file, side ) {
var path = file.path;
var filename = file.name;
var source = file.parsedSource;
var customMethodPreFixes = new Array("node", "state");
if( source ) {
source = this.cleanURL( source );
source = this.objectParser.compose( source, path + "/" + filename, customMethodPreFixes );
if( side == "client" ) {
source = this.cacheDisabler.parseSource( source, path + "/" + filename );
}
source = this.multiExtender.enableMultiExtend( source, file.depth );
source = this.definePragmas( source );
this.writeFileToDisk( source, path, filename );
}
}
async writeFiles( side ) {
var files = this.files;
for( var c = 0; c < files.length; c++ ) {
var file = files[c];
this.composeFile( file, side );
}
}
async renewCache( directory, files ) {
for( var c = 0; c < files.length; c++ ) {
var filename = files[c];
var filePath = this.cache_directory + "/" + filename;
console.log( "Refresh cache:", directory, filename );
filePath = path.resolve( filePath )
var source = await fs.readFileSync( filePath, 'utf8');
source = this.cacheDisabler.parseSource( source );
await fs.writeFileSync( filePath, source );
}
}
async findApplicationFiles( directory, depth = 0, parentFile ) {
var files = await fs.readdirSync( directory );
for( var c = 0; c<files.length; c++ ) {
var filename = files[c];
if( filename != ".DS_Store" ) {
var newFile = await this.processFile( filename, directory, depth, parentFile );
if( parentFile ) {
parentFile.add( newFile );
}
}
}
return parentFile;
}
normalizePath( directory ) {
if( process.platform == "win32" ) {
var separator = "\\";
} else {
var separator = "/";
}
var dirParts = directory.split( separator );
dirParts.shift();
return dirParts.join( separator );
}
writeCacheHistory() {
fs.writeFileSync( path.resolve("./framework/cache/platforms/cachedFiles.json"), JSON.stringify( this.allFiles ) );
}
filterFile( file, previousFiles, index ) {
var name = file.name;
var searchIndex = previousFiles.findIndex( ( file ) => file.name == name );
if( searchIndex != -1 ) {
var previousFile = previousFiles[ searchIndex ];
if( previousFile.dateModified.toString() == file.dateModified.toString() ) {
delete this.files[ index ]
}
}
}
filterFiles( source ) {
var previousFiles = JSON.parse( source );
var files = this.files;
for ( var index = 0; index < files.length; index++ ) {
var file = files[ index ];
this.filterFile( file, previousFiles, index )
}
}
filterModified() {
if( !fs.existsSync( path.resolve("./framework/cache/platforms/cachedFiles.json") ) ) {
return false;
}
var source = fs.readFileSync( path.resolve("./framework/cache/platforms/cachedFiles.json"), "utf8" );
if( !source ) {
return false;
}
this.filterFiles( source );
this.files = this.files.filter( n => n );
}
getFileType( stats ) {
if( stats.isDirectory() ) {
return "directory";
}
if( stats.isFile() ) {
return "file";
}
}
createFileObject( directory, filename, depth ) {
var filePath = path.join( directory, filename );
var stats = fs.statSync( filePath );
var newFile = new file();
newFile.name = filename;
newFile.path = this.normalizePath( directory );
newFile.filePath = filePath;
newFile.depth = depth;
newFile.dateModified = stats.ctime.toString();
newFile.type = this.getFileType( stats );
return newFile;
}
async processDirectory( newFile, parentFile ) {
const excludeDirectorys = new Array("assets", ".DS_Store");
if( !excludeDirectorys.includes( newFile.name ) ) {
if( parentFile ) {
await this.findApplicationFiles( newFile.filePath, newFile.depth + 1, newFile );
} else {
await this.findApplicationFiles( newFile.filePath, newFile.depth + 1 );
this.directories.push( this.normalizePath( newFile.filePath ) );
}
}
}
async processNormalFile( newFile, directory, parentFile ) {
if( newFile.name == "application.js" ) {
if( !parentFile ) {
var fileThree = await this.findApplicationFiles( directory, newFile.depth + 1, newFile );
this.applications.push( fileThree );
}
}
if( this.storeSource ) {
this.processSource( newFile, newFile.filePath, newFile.depth );
}
}
async handleFile( newFile, parentFile, directory ) {
var fileParts = newFile.name.split(".");
if( newFile.type == "directory" ) {
await this.processDirectory( newFile, parentFile );
} else {
await this.processNormalFile( newFile, directory, parentFile );
}
}
async processFile( filename, directory, depth, parentFile = false ) {
var newFile = this.createFileObject( directory, filename, depth );
await this.handleFile( newFile, parentFile, directory, depth );
if( !parentFile ) {
this.files.push( newFile );
this.allFiles.push( newFile )
}
return newFile;
}
async processSource( newFile, filePath, depth ) {
newFile.source = await fs.readFileSync( filePath, 'utf8' );
var source = this.cleanURL( newFile.source );
newFile.source = source;
newFile.parsedSource = this.parseSource( source , depth );
return file;
}
parseSourceLine( code, replacement, depth ) {
var source = replacement.source;
var target = replacement.target;
var relativeTarget = this.computeRelativePath( depth ) + target;
code = code.replaceAll( '"' + source, '"' + relativeTarget );
code = code.replaceAll( "'" + source, "'" + relativeTarget );
return code;
}
parseSource( code, depth ) {
code = "\n\nimport extender from '/unify/extender.js';\n\n " + code;
var replacements = this.replacements;
for( var c = 0; c < replacements.length;c++ ) {
var replacement = replacements[c];
code = this.parseSourceLine( code, replacement, depth );
}
code = this.cleanURL( code );
return code;
}
computeRelativePath( depth ){
var backString = "";
var path = this.cache_directory;
var parts = path.split("/")
parts = parts.filter(n => n)
if( parts[0] == "." ) {
parts.shift();
}
var totalDepth = parts.length + depth;
for (var i = 0; i < totalDepth; i++) {
backString += "../";
}
return backString;
}
}