/* 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 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; } }