First commit
This commit is contained in:
828
framework/server/hotSwapping.js
Normal file
828
framework/server/hotSwapping.js
Normal file
@@ -0,0 +1,828 @@
|
||||
/*
|
||||
|
||||
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 fs from 'fs';
|
||||
|
||||
import path from 'path';
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
|
||||
import filemanager from './filemanager.js';
|
||||
|
||||
import unify from '../unify/unify.js';
|
||||
|
||||
import tools from '../unify/tools.js';
|
||||
|
||||
import colors from '../unify/consoleColors.js';
|
||||
|
||||
import groupsFlat from './themeGroups.js';
|
||||
|
||||
import cacheManager from "./scripts/cacheManager.js";
|
||||
|
||||
import { gpp } from '../node_modules/node-gpp/child.js';
|
||||
|
||||
import simplePath from '../unify/simplePath.js';
|
||||
|
||||
|
||||
|
||||
export default class hotSwap{
|
||||
|
||||
|
||||
allPlatformFiles = new Array();
|
||||
|
||||
cache = new cacheManager();
|
||||
|
||||
gpp = new gpp();
|
||||
|
||||
|
||||
constructor( clients ) {
|
||||
|
||||
this.clients = clients;
|
||||
|
||||
this.create();
|
||||
|
||||
this.createPlatformCachePath();
|
||||
|
||||
this.createGlobals();
|
||||
|
||||
}
|
||||
|
||||
createGlobals() {
|
||||
|
||||
if( !global.visibleElements ) {
|
||||
|
||||
global.visibleElements = new Array();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
createPlatformCachePath() {
|
||||
|
||||
this.pathToPlatforms = new simplePath();
|
||||
|
||||
this.pathToPlatforms.beginWithSlash = false;
|
||||
|
||||
this.pathToPlatforms.endWithSlash = true;
|
||||
|
||||
this.pathToPlatforms.add( "framework" )
|
||||
|
||||
this.pathToPlatforms.add( "cache" )
|
||||
|
||||
this.pathToPlatforms.add( "platforms" )
|
||||
|
||||
}
|
||||
|
||||
setSocketManager( socketManager ) {
|
||||
|
||||
this.socketManager = socketManager;
|
||||
|
||||
}
|
||||
|
||||
createInheritenceCache() {
|
||||
|
||||
}
|
||||
|
||||
async spawnNodemonInstance() {
|
||||
|
||||
|
||||
var files = new filemanager( "./application/" );
|
||||
|
||||
await files.findApplicationFiles( "./application");
|
||||
|
||||
|
||||
var directories = files.directories;
|
||||
|
||||
var that = this;
|
||||
|
||||
for (var i = 0; i < directories.length; i++) {
|
||||
|
||||
var directory = directories[ i ];
|
||||
|
||||
var pathToFile = new simplePath();
|
||||
|
||||
pathToFile.beginWithSlash = false;
|
||||
|
||||
pathToFile.endWithSlash = true;
|
||||
|
||||
pathToFile.add( "application" )
|
||||
|
||||
pathToFile.add( directory )
|
||||
|
||||
|
||||
let directoryPath = path.resolve( pathToFile.resolve() );
|
||||
|
||||
//console.log("Watch directory:", i, colors.green( directoryPath ) );
|
||||
|
||||
|
||||
var callback = function( eventType, filename ) {
|
||||
|
||||
var filePath = directoryPath + "/" + filename;
|
||||
|
||||
console.log("file changed ", eventType, filePath );
|
||||
|
||||
that.message( eventType, filePath );
|
||||
|
||||
};
|
||||
|
||||
if ( fs.existsSync( directoryPath ) ) {
|
||||
|
||||
fs.watch( directoryPath, callback )
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
spawnNodemon() {
|
||||
|
||||
this.createInheritenceCache();
|
||||
|
||||
try {
|
||||
|
||||
this.spawnNodemonInstance();
|
||||
|
||||
} catch ( error ) {
|
||||
|
||||
//console.error("Nodemon Crashed, Instantiating a new instance of nodemon..", error);
|
||||
|
||||
this.spawnNodemonInstance();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
create() {
|
||||
|
||||
//if( global.clusterID == 1 ) {
|
||||
|
||||
var app = this.spawnNodemon();
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
async message( event, file ) {
|
||||
|
||||
if( event == "change" ) {
|
||||
|
||||
//var files = event.data;
|
||||
|
||||
this.updateFiles( file );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getPlatformCachePath() {
|
||||
|
||||
var cachePath = new simplePath();
|
||||
|
||||
cachePath.beginWithSlash = false;
|
||||
|
||||
cachePath.add( "framework" );
|
||||
|
||||
cachePath.add( "cache" );
|
||||
|
||||
cachePath.add( "platforms" );
|
||||
|
||||
cachePath.add( global.os );
|
||||
|
||||
cachePath.add( global.device );
|
||||
|
||||
cachePath.add( global.tint );
|
||||
|
||||
|
||||
return cachePath.resolve();
|
||||
|
||||
}
|
||||
|
||||
getPathParts( file ) {
|
||||
|
||||
var rootDir = tools.slash( path.resolve("./") ).replace("C:/", "c:/");
|
||||
|
||||
var fullPath = file.replace( rootDir, "" );
|
||||
|
||||
var pathParts = fullPath.split("/");
|
||||
|
||||
return pathParts;
|
||||
|
||||
}
|
||||
|
||||
async processSource( fileObject, filePath ) {
|
||||
|
||||
var clientFiles = new filemanager();
|
||||
|
||||
clientFiles.cache_directory = this.getPlatformCachePath();
|
||||
|
||||
clientFiles.addCustomReplacements();
|
||||
|
||||
await clientFiles.processSource( fileObject, filePath, fileObject.depth - 2 )
|
||||
|
||||
}
|
||||
|
||||
getRelativePath( pathParts ) {
|
||||
|
||||
var relativeApplicationPath = pathParts.join("/").replace("/application/", "");
|
||||
|
||||
return tools.slash( relativeApplicationPath );
|
||||
|
||||
}
|
||||
|
||||
async createFileObject( filePath ) {
|
||||
|
||||
var slashedFilePath = tools.slash( filePath );
|
||||
|
||||
var pathParts = this.getPathParts( slashedFilePath );
|
||||
|
||||
|
||||
var fileObject = new Object();
|
||||
|
||||
fileObject.name = pathParts.pop();
|
||||
|
||||
fileObject.depth = pathParts.length;
|
||||
|
||||
fileObject.path = this.getRelativePath( pathParts );
|
||||
|
||||
fileObject.type = "file";
|
||||
|
||||
fileObject.files = new Array();
|
||||
|
||||
|
||||
await this.processSource( fileObject, slashedFilePath );
|
||||
|
||||
|
||||
return fileObject;
|
||||
|
||||
}
|
||||
|
||||
updateClientObjects( newInstance, clientObject ) {
|
||||
|
||||
//console.log( "nodeMethods", clientObject );
|
||||
|
||||
var nodeMethods = newInstance.__nodeMethods.split(",");
|
||||
|
||||
|
||||
|
||||
for ( var j = 0; j < nodeMethods.length; j++ ) {
|
||||
|
||||
var nodeMethodName = nodeMethods[j];
|
||||
|
||||
clientObject[ nodeMethodName ] = newInstance[nodeMethodName];
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateMethodByClient( newInstance, client, className ) {
|
||||
|
||||
var classObjects = client.classObjects;
|
||||
|
||||
var clientObject = classObjects[ className ];
|
||||
|
||||
|
||||
if( clientObject ) {
|
||||
|
||||
this.updateClientObjects( newInstance, clientObject );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateClients( newInstance, clients, className ) {
|
||||
|
||||
//for ( var i = 0; i < clients.length; i++ ) {
|
||||
|
||||
for ( const [ key, client ] of Object.entries( clients ) ) {
|
||||
|
||||
//var client = clients[ i ];
|
||||
|
||||
this.updateMethodByClient( newInstance, client, className );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async updateMethods( newInstance, clients ) {
|
||||
|
||||
var className = newInstance.getClassName();
|
||||
|
||||
if( global.mode == "development") {
|
||||
|
||||
this.updateClients( newInstance, clients, className );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getAllExtendedObjects( extendObjects, pathsArray ) {
|
||||
|
||||
//console.log("extendObjects", extendObjects, pathsArray);
|
||||
|
||||
for ( var c = 0; c < extendObjects.length - 1; c++ ) {
|
||||
|
||||
var objectName = extendObjects[c];
|
||||
|
||||
var extendedObject = global.classObjects[ objectName ]
|
||||
|
||||
if( extendedObject ) {
|
||||
|
||||
pathsArray.push( extendedObject.__sourcePath );
|
||||
|
||||
} else {
|
||||
|
||||
console.error( "Class not found in core.classObjects.", objectName );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getExtendedDependencyPathsOfObject( object, pathsArray ) {
|
||||
|
||||
//var visible = global.visibleElements.includes( object.getClassName() );
|
||||
|
||||
//if( visible ) {
|
||||
|
||||
pathsArray.push( object.__sourcePath );
|
||||
|
||||
var extendObjects = object.getExtends();
|
||||
|
||||
extendObjects = tools.removeDuplicates( extendObjects );
|
||||
|
||||
this.getAllExtendedObjects( extendObjects, pathsArray );
|
||||
|
||||
//console.log("extendObjects", extendObjects);
|
||||
|
||||
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
getExtendedDependencyPaths( objects ) {
|
||||
|
||||
var pathsArray = new Array();
|
||||
|
||||
for ( var i = 0; i < objects.length; i++ ) {
|
||||
|
||||
var object = objects[i];
|
||||
|
||||
//console.log("getExtendedDependencyPaths", object.__sourcePath );
|
||||
|
||||
this.getExtendedDependencyPathsOfObject( object, pathsArray );
|
||||
|
||||
}
|
||||
|
||||
pathsArray = tools.removeDuplicates( pathsArray );
|
||||
|
||||
return pathsArray;
|
||||
|
||||
}
|
||||
|
||||
reloadPathCache( paths, group ) {
|
||||
|
||||
for ( var c = 0; c < paths.length; c++ ) {
|
||||
|
||||
var cachePath = this.pathToPlatforms.clone();
|
||||
|
||||
cachePath.beginWithSlash = true;
|
||||
|
||||
cachePath.add( "Original" );
|
||||
|
||||
cachePath.add( group.path );
|
||||
|
||||
cachePath.add( paths[ c ] );
|
||||
|
||||
this.allPlatformFiles.push( cachePath.resolve() );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async reloadAllThemeCache( groups, paths ) {
|
||||
|
||||
for ( var i = 0; i < groups.length; i++ ) {
|
||||
|
||||
var group = groups[i];
|
||||
|
||||
this.reloadPathCache( paths, group );
|
||||
|
||||
await this.renewPlatform( group.name, group.path, paths );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
createApplicationPath( path, filename ) {
|
||||
|
||||
var fullPath = this.pathToPlatforms.clone();
|
||||
|
||||
fullPath.absolute = true;
|
||||
|
||||
fullPath.add( global.os )
|
||||
|
||||
fullPath.add( global.device )
|
||||
|
||||
fullPath.add( global.tint )
|
||||
|
||||
fullPath.add( path )
|
||||
|
||||
fullPath.add( filename + "?disableCache=" + Math.random() )
|
||||
|
||||
|
||||
return fullPath.resolve();
|
||||
}
|
||||
|
||||
async processDependencies( fileObject, groupsFlatSelection ) {
|
||||
|
||||
var path = this.createApplicationPath( fileObject.path, fileObject.name );
|
||||
|
||||
var importPromise = import( "file://" + path );
|
||||
|
||||
var that = this;
|
||||
|
||||
importPromise.catch( error => {
|
||||
|
||||
console.log("Error loading dynamic import. Your code contains an error.", error)
|
||||
|
||||
} ).then( async function( module ) {
|
||||
|
||||
if( !module ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
var newInstance = new module.default();
|
||||
|
||||
await that.updateInstance( newInstance, groupsFlatSelection );
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
async prepareInstance( newInstance ) {
|
||||
|
||||
if( !newInstance ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
unify.extend( newInstance );
|
||||
|
||||
if( !newInstance.getClassName ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
await this.updateMethods( newInstance, this.clients );
|
||||
|
||||
return newInstance;
|
||||
|
||||
}
|
||||
|
||||
getDependencieObjects( newInstance ) {
|
||||
|
||||
var className = newInstance.getClassName();
|
||||
|
||||
//console.log("dependencieMap", className, Object.keys( global.dependencieMap ) ) ;
|
||||
|
||||
var dependencyObjects = global.dependencieMap[ className ];
|
||||
|
||||
if( dependencyObjects ) {
|
||||
|
||||
return dependencyObjects;
|
||||
|
||||
} else {
|
||||
|
||||
return new Array();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
async updateInstance( newInstance, groupsFlatSelection ) {
|
||||
|
||||
var newInstance = await this.prepareInstance( newInstance );
|
||||
|
||||
if( !newInstance ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
var dependencyObjects = this.getDependencieObjects( newInstance )
|
||||
|
||||
var pathsArray = this.getExtendedDependencyPaths( dependencyObjects );
|
||||
|
||||
await this.reloadAllThemeCache( groupsFlatSelection, pathsArray )
|
||||
|
||||
}
|
||||
|
||||
getAbsolutePathByFile( file, customPath ) {
|
||||
|
||||
var absolutePath = this.pathToPlatforms.clone();
|
||||
|
||||
absolutePath.beginWithSlash = true;
|
||||
|
||||
absolutePath.add( "Original" );
|
||||
|
||||
if( customPath ) {
|
||||
|
||||
absolutePath.add( customPath );
|
||||
|
||||
} else {
|
||||
|
||||
absolutePath.add( "Server" );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
absolutePath.add( file.path );
|
||||
|
||||
absolutePath.add( file.name );
|
||||
|
||||
return absolutePath.resolve();
|
||||
|
||||
}
|
||||
|
||||
addPlatformPaths( files, customPath = false ) {
|
||||
|
||||
for ( var i = 0; i < files.length; i++ ) {
|
||||
|
||||
var file = files[i];
|
||||
|
||||
var absolutePath = this.getAbsolutePathByFile( file, customPath );
|
||||
|
||||
if( file.type == "file" ) {
|
||||
|
||||
this.allPlatformFiles.push( absolutePath );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async processServerFiles( filesArray ) {
|
||||
|
||||
var cachePath = tools.slash( "framework/cache/platforms/Original/Server/" );
|
||||
|
||||
var server_files = new filemanager( cachePath );
|
||||
|
||||
server_files.definePragma("SERVER");
|
||||
|
||||
server_files.definePragma("server_cache");
|
||||
|
||||
server_files.addCustomReplacements();
|
||||
|
||||
server_files.files = filesArray;
|
||||
|
||||
this.addPlatformPaths( filesArray );
|
||||
|
||||
await server_files.writeFiles();
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
async updateConfig() {
|
||||
|
||||
var cachePath = tools.slash( "framework/server_cache/" );
|
||||
|
||||
var server_files = new filemanager( cachePath );
|
||||
|
||||
await server_files.findApplicationFiles( "./application");
|
||||
|
||||
var applications = server_files.applications;
|
||||
|
||||
var application = await this.cache.importApplication( applications[0].path );
|
||||
|
||||
|
||||
this.cache.getApplicationProperties( application )
|
||||
|
||||
await this.cache.writeApplicationPropertiesToFile();
|
||||
|
||||
}
|
||||
|
||||
createGroupPath() {
|
||||
|
||||
var groupPath = new simplePath();
|
||||
|
||||
groupPath.endWithSlash = false;
|
||||
|
||||
|
||||
groupPath.add( global.os )
|
||||
|
||||
groupPath.add( global.device )
|
||||
|
||||
groupPath.add( global.tint )
|
||||
|
||||
return groupPath.resolve();
|
||||
|
||||
}
|
||||
|
||||
createGroup() {
|
||||
|
||||
var groups = new Array( groupsFlat[5] );
|
||||
|
||||
//group.path = this.createGroupPath();
|
||||
|
||||
groups[0].path = this.createGroupPath();
|
||||
|
||||
return groups;
|
||||
|
||||
}
|
||||
|
||||
async processThemeGroups( groups, filesArray ) {
|
||||
|
||||
for ( var i = 0; i < groups.length; i++ ) {
|
||||
|
||||
var group = groups[i];
|
||||
|
||||
await this.processPlatform( group.name, group.path, filesArray );//filesArray );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async writeCachePathsToJSON() {
|
||||
|
||||
var jsonFiles = JSON.stringify( this.allPlatformFiles );
|
||||
|
||||
var path = tools.slash("framework/cache/platforms/files.json");
|
||||
|
||||
await fs.writeFileSync( path, jsonFiles, "utf8" );
|
||||
|
||||
}
|
||||
|
||||
async updateFile( file ) {
|
||||
|
||||
var fileObject = await this.createFileObject( file ) ;
|
||||
|
||||
var themeGroups = this.createGroup();
|
||||
|
||||
var filesArray = new Array( fileObject );
|
||||
|
||||
|
||||
await this.processThemeGroups( themeGroups, filesArray );
|
||||
|
||||
await this.processServerFiles( filesArray );
|
||||
|
||||
await this.updateConfig();
|
||||
|
||||
await this.writeCachePathsToJSON();
|
||||
|
||||
await this.gpp.convert_files();
|
||||
|
||||
//console.log("themeGroups", themeGroups);
|
||||
|
||||
await this.processDependencies( fileObject, themeGroups );
|
||||
|
||||
console.log( colors.green( "Updated file:" ), colors.green( file ) );
|
||||
|
||||
this.sendFileToClient( fileObject );
|
||||
|
||||
}
|
||||
|
||||
async updateFiles( file ) {
|
||||
|
||||
//for (var i = 0; i < files.length; i++) {
|
||||
|
||||
// var file = files[i];
|
||||
|
||||
await this.updateFile( file );
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
sendFileToClient( fileObject ) {
|
||||
|
||||
var clients = this.clients;
|
||||
|
||||
var parameters = new Object();
|
||||
|
||||
parameters.file = fileObject;
|
||||
|
||||
parameters.action = "updateFile";
|
||||
|
||||
console.log( "send updated file to client", clients.length );
|
||||
|
||||
for ( const [ key, client ] of Object.entries( clients ) ) {
|
||||
|
||||
|
||||
|
||||
client.debugMessage( parameters )
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
reloadPathCache( paths, group ) {
|
||||
|
||||
for ( var c = 0; c < paths.length; c++ ) {
|
||||
|
||||
var cachePath = this.pathToPlatforms.clone();
|
||||
|
||||
cachePath.beginWithSlash = true;
|
||||
|
||||
cachePath.add( "Original" );
|
||||
|
||||
cachePath.add( group.path );
|
||||
|
||||
cachePath.add( paths[ c ] );
|
||||
|
||||
this.allPlatformFiles.push( cachePath.resolve() );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
definePragmas( files, path ) {
|
||||
|
||||
var terms = path.split("/");
|
||||
|
||||
terms.shift();
|
||||
|
||||
for ( var i = 0; i < terms.length; i++ ) {
|
||||
|
||||
files.definePragma( terms[i] );
|
||||
|
||||
}
|
||||
|
||||
files.definePragma("CLIENT");
|
||||
|
||||
}
|
||||
|
||||
getThemePath( path, isOriginal = false ) {
|
||||
|
||||
var fullPath = this.pathToPlatforms.clone();
|
||||
|
||||
if( isOriginal ) {
|
||||
|
||||
fullPath.add( "Original" );
|
||||
|
||||
}
|
||||
|
||||
fullPath.add( path );
|
||||
|
||||
return fullPath.resolve();
|
||||
|
||||
}
|
||||
|
||||
async processPlatform( name, path, filesArray ) {
|
||||
|
||||
var themePath = this.getThemePath( path );
|
||||
|
||||
var files = new filemanager( themePath );
|
||||
|
||||
files.addCustomReplacements();
|
||||
|
||||
this.definePragmas( files, path );
|
||||
|
||||
files.files = filesArray;
|
||||
|
||||
files.temporary_cache_directory = this.getThemePath( path, true );
|
||||
|
||||
await files.writeFiles("client");
|
||||
|
||||
this.addPlatformPaths( files.files, path )
|
||||
|
||||
}
|
||||
|
||||
async renewPlatform( name, path, filesArray ) {
|
||||
|
||||
var themePath = this.getThemePath( path, true );
|
||||
|
||||
//console.log("getThemePath", path);
|
||||
|
||||
var files = new filemanager( themePath );
|
||||
|
||||
await files.renewCache( path, filesArray );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user