|
|
@@ -193,30 +193,86 @@ var globSync = require('glob').sync; |
|
|
|
const { readdir } = require("fs").promises |
|
|
|
|
|
|
|
// Directory shallow walk and do perform on each dir. |
|
|
|
const dirs = async (perform, path) => { |
|
|
|
for (const dir of await readdir(path || selectedinstance.root, { withFileTypes: true })) { |
|
|
|
if (dir.isDirectory()) perform(dir) |
|
|
|
const dirs = async (perform, src, target, options) => { |
|
|
|
var rs = [] |
|
|
|
for (const dir of await readdir(src || selectedinstance.root, { withFileTypes: true })) { |
|
|
|
if (dir.isDirectory()) rs.push( perform(dir, src, target, options) ) |
|
|
|
} |
|
|
|
return any(rs) |
|
|
|
} |
|
|
|
|
|
|
|
var fsMove = async function(from, to, options) { |
|
|
|
|
|
|
|
if(!options?.innernode) { |
|
|
|
var exists = fs.existsSync(from); |
|
|
|
var stats = exists && fs.statSync(from); |
|
|
|
var isDirectory = exists && stats.isDirectory(); |
|
|
|
} |
|
|
|
else { |
|
|
|
var isDirectory = options.innernode.isDirectory() |
|
|
|
} |
|
|
|
|
|
|
|
if(!isDirectory) return fs.renameSync(from, to) |
|
|
|
|
|
|
|
fs.mkdirSync(to, { recursive : true }) |
|
|
|
|
|
|
|
var rs = []; |
|
|
|
for (const innernode of await readdir(from, { withFileTypes: true })) { |
|
|
|
rs.push( fsMove( path.join(from, innernode.name), path.join(to, innernode.name), { innernode } ) ) |
|
|
|
} |
|
|
|
|
|
|
|
return any(rs) |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// PB : TODO -- Should return a bunch of promises to wait for... |
|
|
|
var copyrecursive = function(src, dest, perform, args) { |
|
|
|
var exists = fs.existsSync(src); |
|
|
|
var stats = exists && fs.statSync(src); |
|
|
|
var isDirectory = exists && stats.isDirectory(); |
|
|
|
if (isDirectory) { |
|
|
|
fs.mkdirSync(dest); |
|
|
|
dirs( function(childItemName) { |
|
|
|
copyrecursive(path.join(src, childItemName), |
|
|
|
path.join(dest, childItemName)); |
|
|
|
}, src ) |
|
|
|
// fs.readdirSync(src).forEach(function(childItemName) { |
|
|
|
// copyrecursive(path.join(src, childItemName), |
|
|
|
// path.join(dest, childItemName)); |
|
|
|
// }); |
|
|
|
} else { |
|
|
|
fs.copyFileSync(src, dest); |
|
|
|
var copyrecursive = async function(src, target, options) { |
|
|
|
|
|
|
|
if(!options?.innernode) { |
|
|
|
var exists = fs.existsSync(src); |
|
|
|
var stats = exists && fs.statSync(src); |
|
|
|
var isDirectory = exists && stats.isDirectory(); |
|
|
|
} |
|
|
|
else { |
|
|
|
var isDirectory = options.innernode.isDirectory() |
|
|
|
} |
|
|
|
|
|
|
|
if(!isDirectory) return fs.copyFileSync(src, target) |
|
|
|
|
|
|
|
fs.mkdirSync(target, { recursive : true }) |
|
|
|
|
|
|
|
var rs = []; |
|
|
|
for (const innernode of await readdir(src, { withFileTypes: true })) { |
|
|
|
rs.push( copyrecursive( path.join(src, innernode.name), path.join(target, innernode.name), { innernode } ) |
|
|
|
// .then(()=>{ |
|
|
|
// return any(actions.map( a => ()=>a(dir, { isDirectory : true}) )) |
|
|
|
// }) |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
return any(rs) |
|
|
|
|
|
|
|
|
|
|
|
// return fswalk( src, {}, [ |
|
|
|
// (src, options)=> { |
|
|
|
// if(options.innernode.isDirectory()) fs.mkdirSync(options.target, { recursive : true }) |
|
|
|
// else fs.copyFileSync(src, options.target) |
|
|
|
// } |
|
|
|
// ]) |
|
|
|
|
|
|
|
// var exists = fs.existsSync(src); |
|
|
|
// var stats = exists && fs.statSync(src); |
|
|
|
// var isDirectory = exists && stats.isDirectory(); |
|
|
|
// if (isDirectory) { |
|
|
|
// fs.mkdirSync(dest, { recursive : true }); |
|
|
|
// return dirs( function(childItemName) { |
|
|
|
// return copyrecursive(path.join(src, childItemName.name), |
|
|
|
// path.join(dest, childItemName.name)); |
|
|
|
// }, src ) |
|
|
|
// } else { |
|
|
|
// fs.copyFileSync(src, dest); |
|
|
|
// } |
|
|
|
}; |
|
|
|
|
|
|
|
// var renamerecursive = function(from, to){ |
|
|
@@ -239,67 +295,129 @@ var copyrecursive = function(src, dest, perform, args) { |
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
var regexreplaceall = function(content, strOrregexp, substitutes){ |
|
|
|
var regexreplaceall = function(content, ps){ |
|
|
|
var repmatches |
|
|
|
var replaced = content; |
|
|
|
while (repmatches = strOrregexp.exec(content)) { |
|
|
|
while (repmatches = ps.strOrregexp.exec(content)) { |
|
|
|
|
|
|
|
if(content.length > 0) { |
|
|
|
var replacement = repmatches[0] |
|
|
|
for(var m = 1; m < repmatches.length; m++) { |
|
|
|
replacement = replacement.replace(matches[i], substitutes[i-1]) |
|
|
|
replacement = replacement.replace(repmatches[m], ps.substitutes[m-1]) |
|
|
|
} |
|
|
|
replaced.replace( repmatches[0], replacement ) |
|
|
|
|
|
|
|
replaced = replaced.replace( repmatches[0], replacement ) |
|
|
|
} |
|
|
|
} |
|
|
|
return replaced |
|
|
|
} |
|
|
|
|
|
|
|
// filesystem walk |
|
|
|
const fswalk = async (pathToWalk, options, actions) => { |
|
|
|
// options = options || { depthfirst : true } |
|
|
|
|
|
|
|
if(!options.innernode) { |
|
|
|
var exists = fs.existsSync(pathToWalk); |
|
|
|
var stats = exists && fs.statSync(pathToWalk); |
|
|
|
var isDirectory = exists && stats.isDirectory(); |
|
|
|
} |
|
|
|
else { |
|
|
|
var isDirectory = options.innernode.isDirectory() |
|
|
|
} |
|
|
|
|
|
|
|
if(!isDirectory) return any(actions.map( a => ()=>a( pathToWalk, utils.assign({}, options |
|
|
|
, { pathToWalk, innernode : options.innernode || { isDirectory : function(){ return true } } }) ) )) |
|
|
|
|
|
|
|
var rs = []; |
|
|
|
for (const innernode of await readdir(pathToWalk, { withFileTypes: true })) { |
|
|
|
rs.push( fswalk( path.join(pathToWalk, innernode.name), utils.assign({}, options, {pathToWalk, innernode }), actions ) |
|
|
|
// .then(()=>{ |
|
|
|
// return any(actions.map( a => ()=>a(innernode, { isDirectory : true}) )) |
|
|
|
// }) |
|
|
|
) |
|
|
|
} |
|
|
|
// Wait for the walk to complete before renaming directories. |
|
|
|
Array.prototype.push.apply(rs, actions.map( a => ()=>a( pathToWalk, utils.assign({}, options |
|
|
|
, { pathToWalk, innernode : options.innernode || { isDirectory : function(){ return true } } } ) ) )) |
|
|
|
|
|
|
|
return any(rs) |
|
|
|
} |
|
|
|
|
|
|
|
var fsrecurse = function(target, options, actions, root) { |
|
|
|
var exists = fs.existsSync(target); |
|
|
|
var stats = exists && fs.statSync(target); |
|
|
|
var isDirectory = exists && stats.isDirectory(); |
|
|
|
if (isDirectory) { |
|
|
|
dirs( function(childItemName) { |
|
|
|
fsrecurse(path.join(target, childItemName) |
|
|
|
var tasks = [] |
|
|
|
tasks.push( dirs( function(childItemName) { |
|
|
|
return fsrecurse(path.join(target, childItemName.name) |
|
|
|
, options, actions, root); |
|
|
|
}, target ) |
|
|
|
}, target )) |
|
|
|
return any(tasks) |
|
|
|
} else { |
|
|
|
var prevActionResult = null; |
|
|
|
actions.forEach( (action) => prevActionResult = action.apply(null, target, options) ) |
|
|
|
return any( actions.map( (action) => prevActionResult = action.apply(null, target, options) )) |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
var fslink = function(target, options){ fs.linkSync( options.from, options.to ) } |
|
|
|
// var fslink = function(to, options){ try { fs.linkSync( to, options.from ) } catch(e) { throw e } } |
|
|
|
var fslink = function(to, options){ fs.symlinkSync( to, options.from /*, type = "junction"*/ ) } |
|
|
|
|
|
|
|
var fsrename = function(target, options){ |
|
|
|
var replaced = target; // PB : TODO -- streaming and async.. |
|
|
|
// PB : TODO -- streaming and async.. target could be a stream a string or a filesystem item or a tree node... |
|
|
|
|
|
|
|
var targetname = options.innernode?.name || path.parse(target).base; |
|
|
|
var replaced = targetname; |
|
|
|
|
|
|
|
options.patternsubstitutions.forEach( ps => { replaced = regexreplaceall( replaced, ps ) }) |
|
|
|
if(replaced && replaced !== target) fs.renameSync(target, replaced) |
|
|
|
return replaced |
|
|
|
if(replaced && replaced !== targetname) { |
|
|
|
var tr = target.replace( targetname, replaced ) |
|
|
|
fs.renameSync(target, tr) |
|
|
|
return tr; |
|
|
|
} |
|
|
|
else return replaced |
|
|
|
} |
|
|
|
|
|
|
|
var fscontentreplace = function( target, options ){ |
|
|
|
// do many replacements in one shot in the file. |
|
|
|
if(options.processingtype === 'inplace' && options.sourcetype === 'filesystem' ) { |
|
|
|
var content = fs.readFileSync(target) // PB : TODO -- streaming and async.. |
|
|
|
if(options.processingtype === 'inplace' && options.sourcetype === 'filesystem' && !options.innernode.isDirectory() ) { |
|
|
|
var content = fs.readFileSync(target, { encoding: 'utf8' }) // PB : TODO -- streaming and async.. |
|
|
|
options.patternsubstitutions.forEach( ps => { content = regexreplaceall( content, ps ) }) |
|
|
|
fs.writeFileSync(target, content) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var templatelink = function(target, options){ |
|
|
|
var targetPath = target.replace(options.workingtarget, options.destinationroot); |
|
|
|
var targetPathBaseDir = targetPath.split("/").pop() |
|
|
|
|
|
|
|
var linkTobase = regexreplaceall( target, /(__link__)(__base__)-server-lib/, [ '', options.base] ) |
|
|
|
var linkFromName = regexreplaceall( target, /(__link____base__-server-lib)/, [ '' ] ) |
|
|
|
var linkToName = regexreplaceall( linkFromName, new Regexp(targetPathBaseDir), [ linkTobase ] ) |
|
|
|
var targetSubPath = target.replace(options.workingtarget + '/', '' ); |
|
|
|
var targetSubPathsplit = targetSubPath.split("/") |
|
|
|
var targetPathBaseDir = `${targetSubPathsplit[0]}/${targetSubPathsplit[1]}` |
|
|
|
|
|
|
|
var linkToSubPathReplacement = `${options.base}-server-lib/common` |
|
|
|
|
|
|
|
// var rgxp = new RegExp(`(__link__)${options.base}-server-lib`, "g") |
|
|
|
var rgxp = new RegExp(`(__link__${options.base}-server-lib)`, "g") |
|
|
|
var linkToSubPath = targetSubPath |
|
|
|
var b = target.match(rgxp) |
|
|
|
var linkFromName = regexreplaceall( target, { strOrregexp : rgxp, substitutes : [ '' ] } ) |
|
|
|
linkFromName = linkFromName.replace(options.workingtarget, options.destinationroot) |
|
|
|
|
|
|
|
if(linkTobase) { // Only if the pattern matches which is link instruction. |
|
|
|
|
|
|
|
// PB : DONT DLELETE EVERYTHING ??? Only the linkable ones... Or use an add strategy without deleting... |
|
|
|
fs.unlinkSync( target ); fslink( { from : linkFromName, to : linkToName } ) |
|
|
|
if( b ) { |
|
|
|
// Only if the pattern matches which is link instruction. |
|
|
|
linkToSubPath = regexreplaceall( linkToSubPath, { strOrregexp : rgxp, substitutes : [ '' ] } ) |
|
|
|
} |
|
|
|
|
|
|
|
options.patternsubstitutions.forEach( ps => { linkToSubPath = regexreplaceall( linkToSubPath, ps ) }) |
|
|
|
var linkToName = `${options.destinationroot}/${linkToSubPath}` |
|
|
|
|
|
|
|
if(b){ |
|
|
|
linkToSubPath = linkToSubPath.replace( targetPathBaseDir, linkToSubPathReplacement) |
|
|
|
|
|
|
|
// PB : DONT DELETE EVERYTHING ??? Only the linkable ones... Or use an add strategy without deleting... |
|
|
|
fs.unlinkSync( target ); |
|
|
|
fslink( `${options.destinationroot}/${linkToSubPath}`, { from : linkFromName } ) |
|
|
|
} |
|
|
|
else { |
|
|
|
fsMove( `${options.workingtarget}/${linkToSubPath}`, linkToName ) |
|
|
|
// fsMove( `${options.workingtarget}/${linkToSubPath}`, linkFromName ) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@@ -2145,6 +2263,8 @@ var op = { |
|
|
|
|
|
|
|
console.log('elxr g model [modelname] => generate a model named [modelname]'); |
|
|
|
console.log('elxr g vmodel [modelname] => generate a model named [modelname]'); |
|
|
|
console.log('elxr g vmodel [modelname] --server=true => generate only a server model named [modelname]'); |
|
|
|
console.log('elxr g vmodel [modelname] --all=true => default generate server and client model named [modelname]'); |
|
|
|
console.log('elxr g => regenerate all known models'); |
|
|
|
return |
|
|
|
} |
|
|
@@ -2247,6 +2367,7 @@ var op = { |
|
|
|
} |
|
|
|
|
|
|
|
, 'vmodel' : (options)=>{ |
|
|
|
// node --inspect-brk elxr/index.js g vmodel qlabresults --server=true |
|
|
|
options = options || { |
|
|
|
generate : processedArgs['all'] ? 'all' : processedArgs['server'] ? 'server' : processedArgs['client'] ? 'client' : null |
|
|
|
} |
|
|
@@ -2256,32 +2377,50 @@ var op = { |
|
|
|
var name = processedArgs._[2]; |
|
|
|
if(!name) return 'No name specified for generation' |
|
|
|
var templateverse = `${selectedinstance.root}/bbhverse/__universe__/vmodel` |
|
|
|
// var templateverse = `${selectedinstance.root}/bbhverse/__universe__/vmodel/__base__-server-lib/common/lib` |
|
|
|
|
|
|
|
var workingtarget = `${selectedinstance.root}/.elxr/run-${runtimestamp}/temp/vmodelworkingdir` |
|
|
|
var destination = `${selectedinstance.root}` |
|
|
|
|
|
|
|
// Create a tmp working copy for staging the changes. |
|
|
|
copyrecursive(templateverse, workingtarget) |
|
|
|
|
|
|
|
var patternsubstitutions = [ { strOrregexp : /(__name__)/, substitutes : [name]} ] |
|
|
|
var options = { source : templateverse, target : workingtarget, destinationroot : destination, workingtarget |
|
|
|
, processingtype : 'inplace', sourcetype : 'filesystem', patternsubstitutions } |
|
|
|
fsrecurse( workingtarget, options |
|
|
|
, [ function(workingtarget, options){ |
|
|
|
|
|
|
|
// A series of tasks to be executed on each pattern in patternsubstitutions. |
|
|
|
return [ |
|
|
|
// PB : TODO -- Contentreplace is better addressed in memory instead of file IO so do all regexps in sequence in one shot and then rename. |
|
|
|
(filename, options)=>{ fscontentreplace( filename, options ) } |
|
|
|
, (from, options)=>{ fsrename( from, options ) } |
|
|
|
].map( t => t(workingtarget, options) ) |
|
|
|
}] |
|
|
|
) |
|
|
|
|
|
|
|
// Post process links in place. |
|
|
|
fsrecurse( workingtarget, options, [ templatelink ] ) |
|
|
|
return copyrecursive(templateverse, workingtarget).then( r => { |
|
|
|
|
|
|
|
var base = 'chess' // -- The server base from where this instance was dervied from. |
|
|
|
var patternsubstitutions = [ |
|
|
|
{ strOrregexp : /(__name__)/g, substitutes : [name]} |
|
|
|
, { strOrregexp : /(__instance__)/g, substitutes : [selectedinstance.instanceName]} |
|
|
|
, { strOrregexp : /(__base__)/g, substitutes : [base]} |
|
|
|
, { strOrregexp : /(__microservice__)/g, substitutes : [selectedinstance.microservice || selectedinstance.instanceName]} |
|
|
|
, { strOrregexp : /(__microclient__)/g, substitutes : [selectedinstance.microclient || `${selectedinstance.instanceName}-client` || 'client']} // PB : TODO -- elixir-client etc... use a config to find the microclient.. |
|
|
|
] |
|
|
|
var options = { source : templateverse, target : workingtarget |
|
|
|
, base , destinationroot : destination, workingtarget |
|
|
|
, processingtype : 'inplace', sourcetype : 'filesystem', patternsubstitutions } |
|
|
|
|
|
|
|
var actions = [ |
|
|
|
fswalk( workingtarget, options |
|
|
|
, [ function(t, o){ |
|
|
|
|
|
|
|
// A series of tasks to be executed on each pattern in patternsubstitutions. |
|
|
|
return any([ |
|
|
|
// PB : TODO -- Contentreplace is better addressed in memory instead of file IO so do all regexps in sequence in one shot and then rename. |
|
|
|
()=>{ return fscontentreplace( t, o ) } |
|
|
|
, ()=>{ return fsrename( t, o ) } |
|
|
|
]) |
|
|
|
}] ) |
|
|
|
// Post process links in place. |
|
|
|
, () => dirs((innernode, s, t, o ) => fswalk( path.join(workingtarget, innernode.name), utils.assign({}, o, { innernode }), [ templatelink ] ) |
|
|
|
, workingtarget, null, options) |
|
|
|
// Move to the final destination. Whole tree should move only top level folders are required. |
|
|
|
// , () => fswalk( workingtarget, options, [ fsrename ]) |
|
|
|
// , () => dirs( ( child, src, target, options )=>{ return fs.renameSync(path.join(src, child.name), path.join(target, child.name) ) } |
|
|
|
// , workingtarget, destination ) |
|
|
|
] |
|
|
|
|
|
|
|
return any(actions); |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
// Move to the final destination. Whole tree should move only top level folders are required. |
|
|
|
dirs(( child )=>{ return fs.renameSync(path.join(workingtarget, child), path.join(destination, child) ) }, workingtarget ) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@@ -4510,7 +4649,9 @@ var startElxr = function() { |
|
|
|
, ...detectedinstanceoptions.slice(-2), promptkeys ) |
|
|
|
// promptkeys = utils.assign(promptkeys, clioverrides) |
|
|
|
|
|
|
|
if(cmdprompts.instanceName) { |
|
|
|
// startElxr requires instance // Independent cmds should have already been bypassed. |
|
|
|
|
|
|
|
// if(cmdprompts.instanceName) { |
|
|
|
// not an instanceless cmd. |
|
|
|
console.dir(selectedinstance) |
|
|
|
try { |
|
|
@@ -4598,8 +4739,8 @@ var startElxr = function() { |
|
|
|
initinstances(selectedinstance) |
|
|
|
return selectedinstance |
|
|
|
}) |
|
|
|
} |
|
|
|
else return Promise.resolve(true) |
|
|
|
// } |
|
|
|
// else return Promise.resolve(true) |
|
|
|
}) |
|
|
|
.then(()=>{ |
|
|
|
runconfig = { NODE_ENV: selectedinstance.node_env } |