roll-right 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +467 -219
- package/bin/breakup.js +0 -1
- package/bin/index.js +181 -93
- package/docs/rr-background.md +35 -0
- package/historical/old_index.js +95 -0
- package/index.js +0 -6
- package/lib/PreStagingSubsitutions.js +64 -0
- package/lib/SkelToTemplate.js +4005 -0
- package/lib/TemplateToPreStaging.js +433 -0
- package/lib/bundle_directives.js +48 -0
- package/lib/html_directives.js +52 -0
- package/lib/mod_utils.js +1 -2
- package/lib/phase1.js +16 -70
- package/lib/phase2.js +13 -11
- package/lib/rr_utils.js +7 -15
- package/lib/sys_utils.js +36 -0
- package/lib/utils.js +350 -42
- package/package.json +10 -7
- package/test/index.js +30 -2
- package/testme.txt +3885 -0
- package/tools/genpage.js +34 -11
- package/tools/subst_to_vars.js +48 -0
|
@@ -0,0 +1,4005 @@
|
|
|
1
|
+
const {PathManager} = require('extra-file-class')
|
|
2
|
+
|
|
3
|
+
const fos = require('extra-file-class')()
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const TESTING = true
|
|
7
|
+
|
|
8
|
+
const {base_patterns_mod} = require('../lib/html_directives')
|
|
9
|
+
const {bundle_inclusion_transform,link_inclusion_transform} = require('../lib/bundle_directives')
|
|
10
|
+
|
|
11
|
+
let page_or_worker_context = {} // maps skeletons "skeleton_src" to contexts for links in hmtl or javascript inclusion mechanisms
|
|
12
|
+
|
|
13
|
+
const crypto = require('crypto')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
let ParseUtils = require('../lib/utils')
|
|
17
|
+
let parse_util = new ParseUtils()
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* This class is made for organizing the code in this utility.
|
|
21
|
+
*
|
|
22
|
+
* This class has to do with phase 1 operations.
|
|
23
|
+
*
|
|
24
|
+
* Using the direction of an input file, such as generate.json in [websites]/template-configs/,
|
|
25
|
+
* this method will read skeleton files describing the structure of web pages and output templates.
|
|
26
|
+
* It will also attempt to move javascript files into collections for rollup into bundles to be loaded
|
|
27
|
+
* via deferred loading into a browser like contexts.
|
|
28
|
+
*
|
|
29
|
+
*/
|
|
30
|
+
class SkelToTemplate {
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
* Takes in the configuration object, which describes which files are to be loaded as skeletons
|
|
35
|
+
* and which files are to be output to the directories of concerns.
|
|
36
|
+
*
|
|
37
|
+
* The constructor calls upon the PathManager obect to expand out a set of abbreviations used
|
|
38
|
+
* for file names in the skeleton processing.
|
|
39
|
+
*
|
|
40
|
+
* Default directories for finding types of file are expanded to absolute paths and placed in their maps.
|
|
41
|
+
* Top level directories, those with special keys (part of the skeleton language) are expanded as well.
|
|
42
|
+
*
|
|
43
|
+
* @param {object} conf
|
|
44
|
+
*/
|
|
45
|
+
constructor(conf) {
|
|
46
|
+
//
|
|
47
|
+
this.paths = null
|
|
48
|
+
if ( typeof conf.inputs === 'object' ) {
|
|
49
|
+
this.paths = new PathManager(conf.inputs)
|
|
50
|
+
}
|
|
51
|
+
//
|
|
52
|
+
this.remove_block_comments = true
|
|
53
|
+
if ( typeof conf.remove_block_comments === 'boolean' ) {
|
|
54
|
+
this.remove_block_comments = conf.remove_block_comments
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//
|
|
58
|
+
this.global_variable_values = conf.global_variable_values ? conf.global_variable_values : {}
|
|
59
|
+
this.vars_unset_in_run = {}
|
|
60
|
+
this.app_skel_vars = conf.app_skel_vars ? conf.app_skel_vars : {}
|
|
61
|
+
//
|
|
62
|
+
this.top_level_parsed = conf.top_level_parsed
|
|
63
|
+
//
|
|
64
|
+
this.ext_default_dirs = Object.assign({},conf.ext_default_dirs)
|
|
65
|
+
this.top_dir_locations = Object.assign({},conf.top_dir_locations)
|
|
66
|
+
this.find_concerns = Object.assign({},conf["use_case<[targets.dir]>"])
|
|
67
|
+
this.target_dir_key = "targets.dir"
|
|
68
|
+
this.find_in_group = {
|
|
69
|
+
"targets.dir" : (obj) => obj.targets.dir
|
|
70
|
+
}
|
|
71
|
+
//
|
|
72
|
+
this.created_dir = conf['@target'] // will often appear in the top level of project directory
|
|
73
|
+
if ( typeof this.created_dir !== 'string' ) {
|
|
74
|
+
let msg = "missging terminal (leaf) dir name in field '@target'"
|
|
75
|
+
console.log(msg)
|
|
76
|
+
throw new Error(msg)
|
|
77
|
+
}
|
|
78
|
+
//
|
|
79
|
+
// expand the directories
|
|
80
|
+
// these directories are assumed to exist
|
|
81
|
+
let ext_default_dir = this.ext_default_dirs
|
|
82
|
+
let top_dir_locations = this.top_dir_locations
|
|
83
|
+
|
|
84
|
+
for ( let [ext,edd] of Object.entries(ext_default_dir) ) {
|
|
85
|
+
let edd_path = this.paths.compile_one_path(edd)
|
|
86
|
+
ext_default_dir[ext] = edd_path
|
|
87
|
+
}
|
|
88
|
+
//
|
|
89
|
+
for ( let [script_key,tdl] of Object.entries(top_dir_locations) ) {
|
|
90
|
+
let tdl_path = this.paths.compile_one_path(tdl)
|
|
91
|
+
top_dir_locations[script_key] = tdl_path
|
|
92
|
+
}
|
|
93
|
+
//
|
|
94
|
+
this.outputs = false
|
|
95
|
+
let outputs = conf.outputs
|
|
96
|
+
if ( outputs && Array.isArray(outputs) ) {
|
|
97
|
+
outputs = JSON.parse(JSON.stringify(outputs))
|
|
98
|
+
for ( let ogroup of outputs ) {
|
|
99
|
+
let targets = ogroup.targets // targets
|
|
100
|
+
if ( typeof targets !== 'object' || Array.isArray(targets) ) {
|
|
101
|
+
let msg = "each output group in the 'output' array is expected to have a map 'targets' specifying asset name to asset directory"
|
|
102
|
+
console.log(msg)
|
|
103
|
+
throw new Error(msg)
|
|
104
|
+
}
|
|
105
|
+
let skeletons = ogroup.skeletons // skeletons
|
|
106
|
+
if ( typeof skeletons !== 'object' || Array.isArray(skeletons) ) {
|
|
107
|
+
let msg = "each output group in the 'output' array is expected to have a map 'skeletons' specifying termplace file names to source skeletons"
|
|
108
|
+
console.log(msg)
|
|
109
|
+
throw new Error(msg)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
this.outputs = outputs
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
this.range_pattern = new RegExp(/.*\<(\d),(\d+)\>\<\</)
|
|
116
|
+
this.list_pattern = new RegExp(/.*\<(\w+)\>\<\</)
|
|
117
|
+
//
|
|
118
|
+
this.var_spec_pattern = new RegExp(/^\$\@\{(\w+)\}\$files\:\:(.*)$/)
|
|
119
|
+
this.var_spec_copy_pattern = new RegExp(/^\$\@\{(\w+)\}\$verbatim$/)
|
|
120
|
+
this.var_spec_value_pattern = new RegExp(/^\$\@\{(\w+)\}\$\<\<(.+)$/)
|
|
121
|
+
//
|
|
122
|
+
this.var_spec_other_pattern = new RegExp(/^\$\@\{(\w+)\}\$(\w+)\:\:(.*)$/)
|
|
123
|
+
|
|
124
|
+
// ||||--------------------------build_param_tree $@{mushroom}$icons::mushroom-menu-icon.svg
|
|
125
|
+
// build_param_tree files::params::nav_bar_V.smplt $@{mushroom}$icons::mushroom-menu-icon.svg<<
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
this.cross_type_directory = new RegExp(/^(\w+)\<(\w+)\>\:\:(.*)$/)
|
|
129
|
+
this.executable_pattern = new RegExp(/\>\>.*\<\</)
|
|
130
|
+
this.entry_starter = new RegExp(/^(\w+)\:\:/)
|
|
131
|
+
this.sibling_type_directory_match = new RegExp(/^\[([\w-]+)\]\/(.*)$/)
|
|
132
|
+
|
|
133
|
+
this.concerns_directory_redirect_match = new RegExp(/^\[(([\w-]+)\<([\w]+)\>)\]\/(.*)$/)
|
|
134
|
+
|
|
135
|
+
this.start_of_list = new RegExp(/^\@list\<(\w+)\>\<\{/)
|
|
136
|
+
|
|
137
|
+
this.ternary_check = new RegExp(/^(.+)\?(.+)\:(.*)/)
|
|
138
|
+
this.var_pattern = new RegExp(/\@\{([\w\%]+)\}/g)
|
|
139
|
+
this.var_set_expr_pattern = new RegExp(/\{([\w\=\+\-\d]+)\}/)
|
|
140
|
+
|
|
141
|
+
this.basic_function_call_match = new RegExp(/f\@(\w[\w\d]*)\{(\w[\w\d]*)\}/)
|
|
142
|
+
this.import_entry_match = new RegExp(/\$\$(\w+)\:\:.+\.\w+\<\</)
|
|
143
|
+
|
|
144
|
+
this.generic_ogroup_target_dotted_path = new RegExp(/\{\{\{\s*(\@\{\[(\w+)\.(\w+)\]\}).*/)
|
|
145
|
+
|
|
146
|
+
this.entry_remap_map = {
|
|
147
|
+
"files" : { "source" : "html", "type" : "tmplt"},
|
|
148
|
+
"css" : { "source" : "css", "type" : "css"}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this.name_drops_db = {}
|
|
152
|
+
//
|
|
153
|
+
this.delay_file_loading_queue = []
|
|
154
|
+
//
|
|
155
|
+
this.tracking_skel_calc_usage = {}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// {{{ @{[targets.dir]}/scripts/site_app.js }}}
|
|
159
|
+
pull_parent_dir_dotted_target_dir(form,ogroup) {
|
|
160
|
+
let parts_holder = this.generic_ogroup_target_dotted_path.exec(form)
|
|
161
|
+
if ( parts_holder ) {
|
|
162
|
+
//console.dir(parts_holder)
|
|
163
|
+
let substkey = parts_holder[1]
|
|
164
|
+
let f1 = parts_holder[2]
|
|
165
|
+
let f2 = parts_holder[3]
|
|
166
|
+
let otarget = ogroup[f1]
|
|
167
|
+
let abstract_path = otarget[f2]
|
|
168
|
+
//
|
|
169
|
+
abstract_path = this.paths.compile_one_path(abstract_path)
|
|
170
|
+
//
|
|
171
|
+
let rform = form.replace(substkey,abstract_path)
|
|
172
|
+
//
|
|
173
|
+
if ( otarget ) return rform
|
|
174
|
+
}
|
|
175
|
+
return false
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
*
|
|
180
|
+
* @param {string} project_dir
|
|
181
|
+
*/
|
|
182
|
+
set_project_directory(project_dir) {
|
|
183
|
+
this.project_dir = project_dir
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
*
|
|
188
|
+
* @param {string} entry_directive
|
|
189
|
+
* @returns {pair} - a two element array with ary[0] being the true directory name and ary[1] being the file type (extension)
|
|
190
|
+
*/
|
|
191
|
+
entry_directive_location_remap(entry_directive) {
|
|
192
|
+
let remap = this.entry_remap_map[entry_directive]
|
|
193
|
+
if ( remap ) {
|
|
194
|
+
return Object.values(remap)
|
|
195
|
+
}
|
|
196
|
+
return [entry_directive,entry_directive]
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
*
|
|
201
|
+
* @param {string} script_line
|
|
202
|
+
* @returns
|
|
203
|
+
*/
|
|
204
|
+
script_specialization(script_line) {
|
|
205
|
+
let after_def = script_line.split("<<")[1]
|
|
206
|
+
after_def = after_def.trim()
|
|
207
|
+
return after_def
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
*
|
|
213
|
+
* If a script has some variable from configuration.
|
|
214
|
+
* Then, there should be a substitution variable that will be set
|
|
215
|
+
* as a special case in the use of the skeleton.
|
|
216
|
+
*
|
|
217
|
+
* The specialization will for the script to be inlined.
|
|
218
|
+
*
|
|
219
|
+
* @param {string} script_line
|
|
220
|
+
* @returns {boolean}
|
|
221
|
+
*/
|
|
222
|
+
script_no_bundle(script_line) {
|
|
223
|
+
let after_def = this.script_specialization(script_line)
|
|
224
|
+
if ( after_def.length ) return false
|
|
225
|
+
return true
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
*
|
|
230
|
+
* @param {*} script_line
|
|
231
|
+
* @param {*} script
|
|
232
|
+
* @returns
|
|
233
|
+
*/
|
|
234
|
+
structural_substitution(script_line,script) {
|
|
235
|
+
let after_def = this.script_specialization(script_line)
|
|
236
|
+
if ( after_def[0] === '@' ) {
|
|
237
|
+
let value = ""
|
|
238
|
+
let config_var = after_def.substring(after_def.indexOf('{') + 1,after_def.lastIndexOf('}')-1)
|
|
239
|
+
let dat_struct = this.get_data(config_var)
|
|
240
|
+
if ( after_def[1] === '#' ) {
|
|
241
|
+
value = 0
|
|
242
|
+
if ( Array.isArray(dat_struct) ) {
|
|
243
|
+
value = dat_struct.length
|
|
244
|
+
} else {
|
|
245
|
+
value = Object.keys(dat_struct).length
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
//
|
|
249
|
+
}
|
|
250
|
+
script = script.replace(`{{${config_var}}}`,value)
|
|
251
|
+
}
|
|
252
|
+
return script
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* For each concern, makes sure that the output directory exists under the directory for the concern
|
|
258
|
+
* determined by the scheme estabished by the target's 'dir_form'
|
|
259
|
+
*
|
|
260
|
+
* @param {object} targets - a set of directory
|
|
261
|
+
* @param {object} skeletons - a map of skeletons path names to skeleton forms to be expanded
|
|
262
|
+
* @param {string} created_dir - the leaf directory to be created
|
|
263
|
+
*/
|
|
264
|
+
async ensure_all_concerns_template_directories(targets,skeletons) {
|
|
265
|
+
//
|
|
266
|
+
let dir_form = targets.dir_form
|
|
267
|
+
let concerns = targets.concerns
|
|
268
|
+
//
|
|
269
|
+
dir_form = dir_form.replace("@target",this.created_dir)
|
|
270
|
+
//
|
|
271
|
+
// Here, the directories which will receive template copies for an
|
|
272
|
+
// asset stack will be created if they do not exists.
|
|
273
|
+
for ( let concern of concerns ) {
|
|
274
|
+
//
|
|
275
|
+
let top_out_dir = dir_form.replace("@concern",concern)
|
|
276
|
+
//
|
|
277
|
+
let promises = []
|
|
278
|
+
for ( let [opath,skeleton] of Object.entries(skeletons) ) {
|
|
279
|
+
let skel_output_path = `${top_out_dir}${opath}`
|
|
280
|
+
let sko_path = this.paths.compile_one_path(skel_output_path)
|
|
281
|
+
let sk_path = this.paths.compile_one_path(skeleton)
|
|
282
|
+
skeletons[opath] = sk_path
|
|
283
|
+
promises.push(fos.ensure_directories(sko_path,"",true))
|
|
284
|
+
}
|
|
285
|
+
await Promise.all(promises)
|
|
286
|
+
//
|
|
287
|
+
}
|
|
288
|
+
//
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* makes sure that directories receiving the generated assets exists
|
|
294
|
+
* and are structured according to the configuration.
|
|
295
|
+
*
|
|
296
|
+
* The configuration loaded from a file such as "generate.json" has fields describing directories
|
|
297
|
+
* and concerns and global variables. Global variables reflect the specialization for a run on skeletons.
|
|
298
|
+
*
|
|
299
|
+
* There is one field "outputs" which describes directories where templates and final renditions will be stored
|
|
300
|
+
* for different concern/file pairings. Each element of the outputs has three main fields for grouping
|
|
301
|
+
* this information:
|
|
302
|
+
*
|
|
303
|
+
* targets - description of output directory schemes and a list of a concerns (webstites/apps) that will use the outputs
|
|
304
|
+
* skeletons - a map of out template names to
|
|
305
|
+
* name_parameters -- variations on skeletal structure from a database of these structures, with defintions for
|
|
306
|
+
* final substitutions
|
|
307
|
+
*
|
|
308
|
+
* this method makes use of targets and skeletons
|
|
309
|
+
*
|
|
310
|
+
*/
|
|
311
|
+
async prepare_directories() {
|
|
312
|
+
//
|
|
313
|
+
let outputs = this.outputs
|
|
314
|
+
for ( let ogroup of outputs ) {
|
|
315
|
+
let targets = ogroup.targets // targets
|
|
316
|
+
let skeletons = ogroup.skeletons // skeletons
|
|
317
|
+
await this.ensure_all_concerns_template_directories(targets,skeletons)
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
*
|
|
324
|
+
* Loads the skeleton files. Creates a map from output names to skeleton source names.
|
|
325
|
+
* Returns the map.
|
|
326
|
+
*
|
|
327
|
+
* @returns {pair}
|
|
328
|
+
*/
|
|
329
|
+
async load_skeletons() {
|
|
330
|
+
//
|
|
331
|
+
let outputs = this.outputs
|
|
332
|
+
let all_skeletons = {}
|
|
333
|
+
//
|
|
334
|
+
for ( let ogroup of outputs ) {
|
|
335
|
+
let skeletons = ogroup.skeletons // skeletons mentioned in the ogroup
|
|
336
|
+
ogroup.skels_processed = {} // will get the unique skels in this ogroup and create a place to store their processed parts
|
|
337
|
+
//
|
|
338
|
+
for ( let [fky,file] of Object.entries(skeletons) ) { // the field key is the desired output
|
|
339
|
+
//
|
|
340
|
+
let fpath = this.paths.compile_one_path(file)
|
|
341
|
+
let p = all_skeletons[fpath] // a skeleton absolute path name
|
|
342
|
+
if ( p === undefined ) {
|
|
343
|
+
all_skeletons[fpath] = {
|
|
344
|
+
"original" : "",
|
|
345
|
+
"ops" : { // ops map
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
//
|
|
350
|
+
if ( ogroup.skels_processed[file] === undefined ) { // one for any of the selected skeletons (may select in duplicate)
|
|
351
|
+
let config_vars = ogroup.targets.uses_config_vars
|
|
352
|
+
config_vars = (config_vars ? config_vars : "default") // the skeleton ops apply to an ogroup
|
|
353
|
+
ogroup.skels_processed[file] = { // allows to map from template to output
|
|
354
|
+
"ablsolute_path" : fpath, // a key to the loaded data as well
|
|
355
|
+
"skel_vars_key" : config_vars,
|
|
356
|
+
"final" : "" // final output not yet determined
|
|
357
|
+
}
|
|
358
|
+
all_skeletons[fpath].ops[config_vars] = "" // on preparation across many entries retrievable from ogroup by:
|
|
359
|
+
// all_skeletons[ogroup.skels_processed[file].ablsolute_path][ogroup.skels_processed[file].skel_vars_key]
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
//
|
|
363
|
+
let tdir = ogroup.targets.dir_form
|
|
364
|
+
ogroup.concerns_to_out_dirs = {}
|
|
365
|
+
//
|
|
366
|
+
for ( let crn of ogroup.targets.concerns ) {
|
|
367
|
+
let crn_tdir = tdir.replace("@concern",crn)
|
|
368
|
+
crn_tdir = crn_tdir.replace("@target",this.created_dir)
|
|
369
|
+
ogroup.concerns_to_out_dirs[crn] = crn_tdir
|
|
370
|
+
}
|
|
371
|
+
//
|
|
372
|
+
}
|
|
373
|
+
//
|
|
374
|
+
let data_promises = []
|
|
375
|
+
let skel_keys = Object.keys(all_skeletons)
|
|
376
|
+
for ( let fpath of skel_keys ) {
|
|
377
|
+
data_promises.push(fos.load_data_at_path(fpath))
|
|
378
|
+
}
|
|
379
|
+
let data_list = await Promise.all(data_promises)
|
|
380
|
+
let n = data_list.length
|
|
381
|
+
//
|
|
382
|
+
for ( let i = 0; i < n; i++ ) {
|
|
383
|
+
all_skeletons[skel_keys[i]].original = data_list[i]
|
|
384
|
+
}
|
|
385
|
+
//
|
|
386
|
+
return all_skeletons
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
*
|
|
392
|
+
* @param {string} file
|
|
393
|
+
* @returns {object}
|
|
394
|
+
*/
|
|
395
|
+
async load_name_drops_db(file) {
|
|
396
|
+
this.name_drops_db = {}
|
|
397
|
+
let name_db_loc = this.paths.get_path("[names]")
|
|
398
|
+
let name_db_file = file ? file : `${name_db_loc}/name-drop.db`
|
|
399
|
+
if ( file ) {
|
|
400
|
+
return await fos.load_json_data_at_path(name_db_file)
|
|
401
|
+
}
|
|
402
|
+
this.name_drops_db = await fos.load_json_data_at_path(name_db_file)
|
|
403
|
+
return this.name_drops_db
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* The data provided is expected to be a string which may or may not include subfiles or the kind of
|
|
409
|
+
* variable used in skeleton processing.
|
|
410
|
+
*
|
|
411
|
+
* This looks to see if there is at least one instance of some structured text indicating that
|
|
412
|
+
* further procesing may be done (especially in a recursive fashion).
|
|
413
|
+
*
|
|
414
|
+
* @param {string} data
|
|
415
|
+
* @returns {number}
|
|
416
|
+
*/
|
|
417
|
+
check_recursive_data(data) {
|
|
418
|
+
if ( !data ) return false
|
|
419
|
+
//
|
|
420
|
+
if ( data.indexOf("$$file") >= 0 ) {
|
|
421
|
+
return 1
|
|
422
|
+
}
|
|
423
|
+
if ( data.indexOf("$$icons") >= 0 ) {
|
|
424
|
+
return 2
|
|
425
|
+
}
|
|
426
|
+
if ( data.indexOf("$$css") >= 0 ) {
|
|
427
|
+
return 3
|
|
428
|
+
}
|
|
429
|
+
if ( data.indexOf("@{") >= 0 ) {
|
|
430
|
+
return 4
|
|
431
|
+
}
|
|
432
|
+
if ( this.executable_pattern.test(data) ) {
|
|
433
|
+
return 5
|
|
434
|
+
}
|
|
435
|
+
return false
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* The range parameter is a structure that will have lb (lower bound)
|
|
441
|
+
* and ub (upper bound) filled out if the control string `ctrl_str`
|
|
442
|
+
* matches the pattern. The pattern will be of the fomr `prefix`<lb,ub>`postfix`
|
|
443
|
+
*
|
|
444
|
+
* @param {string} ctrl_str
|
|
445
|
+
* @param {object} range
|
|
446
|
+
* @returns {boolean}
|
|
447
|
+
*/
|
|
448
|
+
has_range_expr(ctrl_str,range) {
|
|
449
|
+
let result = this.range_pattern.exec(ctrl_str)
|
|
450
|
+
if ( result ) {
|
|
451
|
+
range.lb = parseInt(result[1])
|
|
452
|
+
range.ub = parseInt(result[2])
|
|
453
|
+
return true
|
|
454
|
+
}
|
|
455
|
+
return false
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* The `var_holder` parameter is an array that will
|
|
461
|
+
* contain the bracket identifier
|
|
462
|
+
* if the control string `ctrl_str`
|
|
463
|
+
* matches the pattern. The pattern will be of the form `prefix`<identifier>`postfix`
|
|
464
|
+
*
|
|
465
|
+
* @param {string} ctrl_str
|
|
466
|
+
* @param {Array} var_holder
|
|
467
|
+
* @returns {boolean}
|
|
468
|
+
*/
|
|
469
|
+
has_list_expr(ctrl_str,var_holder) {
|
|
470
|
+
let result = this.list_pattern.exec(ctrl_str)
|
|
471
|
+
if ( result ) {
|
|
472
|
+
var_holder[0] = result[1]
|
|
473
|
+
return true
|
|
474
|
+
}
|
|
475
|
+
return false
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
*
|
|
482
|
+
* @param {string} part_key
|
|
483
|
+
* @param {string} param_str
|
|
484
|
+
* @returns {object}
|
|
485
|
+
*/
|
|
486
|
+
|
|
487
|
+
// console.log("||||--------------------------build_param_tree",key_part)
|
|
488
|
+
// console.log("build_param_tree",part_key,param_str)
|
|
489
|
+
|
|
490
|
+
async build_param_tree(part_key,param_str) {
|
|
491
|
+
//
|
|
492
|
+
if ( param_str[0] === '{' ) {
|
|
493
|
+
param_str = param_str.substring(1,param_str.lastIndexOf('}'))
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
let var_tree = {}
|
|
497
|
+
let easy_access_parse = {}
|
|
498
|
+
|
|
499
|
+
let sep_point = param_str.indexOf("<<")
|
|
500
|
+
if ( sep_point > 0 ) {
|
|
501
|
+
//
|
|
502
|
+
let key_part = param_str.substring(0,sep_point)
|
|
503
|
+
let rest = param_str.substring(sep_point+2)
|
|
504
|
+
//console.log("||||--------------------------build_param_tree",key_part)
|
|
505
|
+
//
|
|
506
|
+
do {
|
|
507
|
+
let check = this.var_spec_pattern.exec(key_part)
|
|
508
|
+
if ( check ) {
|
|
509
|
+
let sub_tree = {}
|
|
510
|
+
if ( rest.length ) {
|
|
511
|
+
rest = await this.capture_param_sub_call(part_key,rest,sub_tree)
|
|
512
|
+
}
|
|
513
|
+
if ( Object.keys(sub_tree).length === 0 ) {
|
|
514
|
+
sub_tree = false
|
|
515
|
+
}
|
|
516
|
+
let extracted_var = check[1]
|
|
517
|
+
let the_file = check[2]
|
|
518
|
+
//
|
|
519
|
+
easy_access_parse[extracted_var] = "%file%"
|
|
520
|
+
//
|
|
521
|
+
let data = ""
|
|
522
|
+
let parts_of_key = part_key.split('::')
|
|
523
|
+
if ( parts_of_key.length > 1 ) {
|
|
524
|
+
let [path_finder,ftype] = this.entry_directive_location_remap(parts_of_key[0])
|
|
525
|
+
let locus = this.top_dir_locations[path_finder]
|
|
526
|
+
if ( locus ) {
|
|
527
|
+
if ( the_file.indexOf('::') > 0 ) {
|
|
528
|
+
the_file = the_file.substring(the_file.indexOf('::') + 2)
|
|
529
|
+
}
|
|
530
|
+
let file_path = `${locus}/${the_file}`
|
|
531
|
+
data = await fos.load_data_at_path(file_path)
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
var_tree[extracted_var] = {
|
|
535
|
+
"file" : the_file,
|
|
536
|
+
"tree" : sub_tree,
|
|
537
|
+
"data" : data
|
|
538
|
+
}
|
|
539
|
+
if ( this.check_recursive_data(data) ) {
|
|
540
|
+
let map_value = var_tree[extracted_var]
|
|
541
|
+
map_value.recursive = await this.get_files_and_vars(part_key,data)
|
|
542
|
+
}
|
|
543
|
+
} else if ( check = this.var_spec_copy_pattern.exec(key_part) ) {
|
|
544
|
+
//
|
|
545
|
+
let data = ""
|
|
546
|
+
if ( rest.length ) {
|
|
547
|
+
let next = this.find_next(rest)
|
|
548
|
+
if ( next ) {
|
|
549
|
+
data = rest.substring(0,next)
|
|
550
|
+
rest = rest.substring(next)
|
|
551
|
+
data = data.substring(1,data.lastIndexOf('}'))
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
let extracted_var = check[1]
|
|
555
|
+
easy_access_parse[extracted_var] = "%string%"
|
|
556
|
+
var_tree[extracted_var] = {
|
|
557
|
+
"file" : false,
|
|
558
|
+
"tree" : false,
|
|
559
|
+
"data" : data
|
|
560
|
+
}
|
|
561
|
+
//
|
|
562
|
+
} else if ( check = this.var_spec_value_pattern.exec(param_str) ) {
|
|
563
|
+
let data = ""
|
|
564
|
+
if ( rest.length ) {
|
|
565
|
+
let next = this.find_next(rest)
|
|
566
|
+
if ( next ) {
|
|
567
|
+
data = rest.substring(0,next)
|
|
568
|
+
rest = rest.substring(next)
|
|
569
|
+
data = data.substring(1,data.lastIndexOf('}'))
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
//
|
|
573
|
+
let extracted_var = check[1]
|
|
574
|
+
data = check[2]
|
|
575
|
+
let val_type = isNaN(parseInt(data)) ? "%string%" : "%number%"
|
|
576
|
+
easy_access_parse[extracted_var] = val_type
|
|
577
|
+
var_tree[extracted_var] = {
|
|
578
|
+
"file" : false,
|
|
579
|
+
"tree" : false,
|
|
580
|
+
"data" : data
|
|
581
|
+
}
|
|
582
|
+
//
|
|
583
|
+
} else {
|
|
584
|
+
if ( rest.length ) {
|
|
585
|
+
let next = this.find_next(rest)
|
|
586
|
+
if ( next ) {
|
|
587
|
+
rest = rest.substring(next)
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
check = this.var_spec_other_pattern.exec(key_part)
|
|
592
|
+
if ( check = this.var_spec_other_pattern.exec(key_part) ) {
|
|
593
|
+
let extracted_var = check[1]
|
|
594
|
+
let path_finder = check[2]
|
|
595
|
+
let the_file = check[3]
|
|
596
|
+
|
|
597
|
+
let data = ""
|
|
598
|
+
let locus = this.top_dir_locations[path_finder]
|
|
599
|
+
|
|
600
|
+
if ( locus ) {
|
|
601
|
+
if ( the_file.indexOf('::') > 0 ) {
|
|
602
|
+
the_file = the_file.substring(the_file.indexOf('::') + 2)
|
|
603
|
+
}
|
|
604
|
+
let file_path = `${locus}/${the_file}`
|
|
605
|
+
data = await fos.load_data_at_path(file_path)
|
|
606
|
+
}
|
|
607
|
+
var_tree[extracted_var] = {
|
|
608
|
+
"file" : the_file,
|
|
609
|
+
"tree" : false,
|
|
610
|
+
"data" : data
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
}
|
|
615
|
+
//
|
|
616
|
+
if ( rest.length ) {
|
|
617
|
+
sep_point = rest.indexOf("<<")
|
|
618
|
+
if ( sep_point ) {
|
|
619
|
+
key_part = rest.substring(0,sep_point)
|
|
620
|
+
rest = rest.substring(sep_point+2)
|
|
621
|
+
} else break
|
|
622
|
+
} else break;
|
|
623
|
+
|
|
624
|
+
} while ( key_part.length )
|
|
625
|
+
//
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
if ( Object.keys(var_tree).length === 0 ) {
|
|
629
|
+
return false
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
let par_state = `_params<${Object.keys(easy_access_parse).join(',')}>`
|
|
633
|
+
var_tree[par_state] = easy_access_parse
|
|
634
|
+
return var_tree
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
*
|
|
640
|
+
* @param {string} rest
|
|
641
|
+
* @returns {Number} -- the location of the next
|
|
642
|
+
*/
|
|
643
|
+
find_next(rest) {
|
|
644
|
+
if ( rest[0] === '{' ) { // handle recursive object searchs
|
|
645
|
+
let depth = 0
|
|
646
|
+
let index = 1
|
|
647
|
+
while ( depth > -1 ) {
|
|
648
|
+
let c = rest[index]
|
|
649
|
+
if ( c === '{' ) {
|
|
650
|
+
depth++
|
|
651
|
+
} else if ( c === '}' ) {
|
|
652
|
+
depth--
|
|
653
|
+
}
|
|
654
|
+
index++
|
|
655
|
+
}
|
|
656
|
+
return index
|
|
657
|
+
} else {
|
|
658
|
+
return 0
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
*
|
|
666
|
+
* @param {string} rest
|
|
667
|
+
* @param {object} sub_tree -- gets populated with object information
|
|
668
|
+
*/
|
|
669
|
+
async capture_param_sub_call(part_key,rest,sub_tree) {
|
|
670
|
+
let next = this.find_next(rest)
|
|
671
|
+
if ( next ) {
|
|
672
|
+
let param_block = rest.substring(0,next)
|
|
673
|
+
rest = rest.substring(next)
|
|
674
|
+
let a_tree = await this.build_param_tree(part_key,param_block)
|
|
675
|
+
for ( let ky in a_tree ) {
|
|
676
|
+
sub_tree[ky] = a_tree[ky]
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
return rest
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* remove syntax markers that must not occur in the final output
|
|
685
|
+
*
|
|
686
|
+
* @param {string} str
|
|
687
|
+
* @returns {string}
|
|
688
|
+
*/
|
|
689
|
+
clean_verbatim(str) {
|
|
690
|
+
str = str.trim()
|
|
691
|
+
if ( str[0] === '{' ) {
|
|
692
|
+
str = str.substring(1)
|
|
693
|
+
let i = str.lastIndexOf('}')
|
|
694
|
+
if ( i > 0 ) {
|
|
695
|
+
str = str.substring(0,i)
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
return str
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
*
|
|
703
|
+
* This method is a map call on the array, that filters 'files::loop::' into parsing operations and returns all else
|
|
704
|
+
* in its original state.
|
|
705
|
+
*
|
|
706
|
+
* There are two types of loop structure supported, ranges and lists.
|
|
707
|
+
*
|
|
708
|
+
* The range construct maps the input into an expanded list of elements indexed from start to end of the range.
|
|
709
|
+
* Before returning, this method flattens the list to incorporate the exanded range. The range construct is identified
|
|
710
|
+
* by a pair within backets `<range_pair>`, where `range_pair` is a comma separated sequence of two numbers,
|
|
711
|
+
* lower bound to upper bound.
|
|
712
|
+
*
|
|
713
|
+
* The list construct provides a variable (expected to be used in the list expansion file) instead of a range.
|
|
714
|
+
* That is, instead of a `range_pair` inside brackets `<>`, there will be a variable name, e.g. '<el>', where
|
|
715
|
+
* 'el' would mean an element of the list. The list construct will provide a list in a JSON parseable structure
|
|
716
|
+
* listed after '<<', the end of step specifier symbol. The struct must be a complete structure prior to the next
|
|
717
|
+
* '$$' in the table. The structure will be held for later use in the `maybe_params` structure. A 'part key' will
|
|
718
|
+
* be used to index the data list for this sort of element. (Note: the part key will be a sub skeleton that takes
|
|
719
|
+
* a list as input.)
|
|
720
|
+
*
|
|
721
|
+
* Note: this method stops short of loading subtemplate files, so the list construct will not expand the list.
|
|
722
|
+
*
|
|
723
|
+
* Note: Sequence expansion conceptually assigns space to a sequence of elements in markup. These are structural changes,
|
|
724
|
+
* expansions, of a skeleton. These are step elements that claim space, but do not produce a string to be input to anything.
|
|
725
|
+
* These space grabbers can take input from items that produce strings. (See parameters for string producing items).
|
|
726
|
+
*
|
|
727
|
+
* @param {Array} data_parts
|
|
728
|
+
* @param {object} maybe_params
|
|
729
|
+
* @returns {Array}
|
|
730
|
+
*/
|
|
731
|
+
sequence_expansion(data_parts,maybe_params) {
|
|
732
|
+
data_parts = data_parts.map((a_part) => {
|
|
733
|
+
if ( a_part.indexOf("files::") === 0 ) {
|
|
734
|
+
if ( a_part.indexOf("files::loop::") === 0 ) {
|
|
735
|
+
let range = {
|
|
736
|
+
lb: 0,
|
|
737
|
+
ub: 0
|
|
738
|
+
}
|
|
739
|
+
a_part = parse_util.remove_spaces(a_part)
|
|
740
|
+
a_part = parse_util.remove_white(a_part)
|
|
741
|
+
// RANGE
|
|
742
|
+
if ( this.has_range_expr(a_part.substring("files::loop::".length),range) ) {
|
|
743
|
+
let replacer = `<${range.lb},${range.ub}>`
|
|
744
|
+
let lines = []
|
|
745
|
+
for ( let i = range.lb; i <= range.ub; i++ ) {
|
|
746
|
+
let this_part = a_part.replace(replacer,`${i}`)
|
|
747
|
+
this_part = this_part.replace("::loop","") // removes the loop specification leaving behind other types (e.g. calc)
|
|
748
|
+
lines.push(this_part)
|
|
749
|
+
}
|
|
750
|
+
return lines
|
|
751
|
+
}
|
|
752
|
+
} else if ( a_part.indexOf("files::list::") === 0 ) {
|
|
753
|
+
let var_holder = []
|
|
754
|
+
if ( this.has_list_expr(a_part.substring("files::list::".length,a_part.indexOf('[')),var_holder) ) {
|
|
755
|
+
// ELEMENTS
|
|
756
|
+
let parts = a_part.split('<<')
|
|
757
|
+
let part_key = parts.shift()
|
|
758
|
+
part_key = part_key.replace("::list","")
|
|
759
|
+
part_key = part_key.substring(0,part_key.indexOf('<'))
|
|
760
|
+
let part_def = parts.shift()
|
|
761
|
+
let def = part_def
|
|
762
|
+
try {
|
|
763
|
+
def = JSON.parse(part_def)
|
|
764
|
+
} catch (e){}
|
|
765
|
+
//
|
|
766
|
+
let pars = {}
|
|
767
|
+
pars[var_holder[0]] = "list"
|
|
768
|
+
pars[`_type<${var_holder[0]}>`] = def
|
|
769
|
+
maybe_params[part_key] = pars
|
|
770
|
+
return part_key
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
//
|
|
775
|
+
return a_part
|
|
776
|
+
})
|
|
777
|
+
data_parts = parse_util.flatten(data_parts)
|
|
778
|
+
return data_parts
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
*
|
|
784
|
+
* @param {string} a_part
|
|
785
|
+
* @returns {boolean}
|
|
786
|
+
*/
|
|
787
|
+
has_verbatim_params(a_part) {
|
|
788
|
+
return a_part.indexOf('$verbatim') > 0
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
*
|
|
793
|
+
* @param {string} a_part
|
|
794
|
+
* @param {object} verbatim_blocks
|
|
795
|
+
* @returns
|
|
796
|
+
*/
|
|
797
|
+
block_verabtim_parameters(a_part,verbatim_blocks) {
|
|
798
|
+
let var_lines = a_part.split("$@{")
|
|
799
|
+
let n = var_lines.length
|
|
800
|
+
for ( let l = 1; l < n; l++ ) {
|
|
801
|
+
let maybe_verbatim = var_lines[l]
|
|
802
|
+
if ( this.has_verbatim_params(maybe_verbatim) ) {
|
|
803
|
+
let verby = maybe_verbatim.split("<<")
|
|
804
|
+
let vdata = verby[1].trim()
|
|
805
|
+
let [s,e] = [0,0]
|
|
806
|
+
if ( vdata[0] === '{' ) {
|
|
807
|
+
s = 1
|
|
808
|
+
e = vdata.lastIndexOf('}')
|
|
809
|
+
vdata = vdata.substring(s,e)
|
|
810
|
+
}
|
|
811
|
+
let ky = `v${l}`
|
|
812
|
+
verbatim_blocks[ky] = vdata.trim()
|
|
813
|
+
var_lines[l] = maybe_verbatim.replace(vdata,`@{${ky}}`)
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
return var_lines.join("$@{")
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
*
|
|
821
|
+
* @param {string} a_part
|
|
822
|
+
* @param {object} verbatim_blocks
|
|
823
|
+
* @returns {string}
|
|
824
|
+
*/
|
|
825
|
+
unblock_verabtim_parameters(a_part,verbatim_blocks) {
|
|
826
|
+
for ( let ky in verbatim_blocks ) {
|
|
827
|
+
let data = verbatim_blocks[ky]
|
|
828
|
+
a_part = a_part.replace(`@{${ky}}`,data)
|
|
829
|
+
}
|
|
830
|
+
return a_part
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* This method is a map call on the array, that filters 'files::params::' into parsing operations and returns all else
|
|
835
|
+
* in its original state.
|
|
836
|
+
*
|
|
837
|
+
* A custom sytnax is used for the passing of parameters to a subskeleton and should occur after the end of step specifier symbol, '<<'.
|
|
838
|
+
* The parameter specification looks a little bit like JSON, but it is not.
|
|
839
|
+
* The specifier is demarcated by braces, '{}', and within the braces, sub-step specifiers are given.
|
|
840
|
+
* The sub-step has similar structure to a step, but instead of starting with a 'start of section' symbol, i.e. "$$", it starts
|
|
841
|
+
* with variable assignment of the sub-section. The variable assignment step indicator includes a subsitution variable identifier
|
|
842
|
+
* between the '$'s of '$$' pair. For example, the form `$@{lr_div}$` will assign the rest of the step specifier to the variable
|
|
843
|
+
* 'lr_div` to be found in the sub template file requiring the variables.
|
|
844
|
+
*
|
|
845
|
+
* The variable assigned steps are listed sequentially for the parameterized sub template. Each variable assigned step
|
|
846
|
+
* may be any one of the step types indicating a file to be loaded into skeletons. As such the parameterized step
|
|
847
|
+
* may be recursive (maximally what is typed into the top level skeleton). So, a tree of parameterizations is constructed
|
|
848
|
+
* and returned.
|
|
849
|
+
*
|
|
850
|
+
* Note: Parameter files demand string input from a calling file. The step version takes up structural space, while
|
|
851
|
+
* the `variable assignment step indicator` version produces a string for input.
|
|
852
|
+
*
|
|
853
|
+
* @param {Array} data_parts
|
|
854
|
+
* @param {object} maybe_params
|
|
855
|
+
* @returns {Array}
|
|
856
|
+
*/
|
|
857
|
+
async capture_parameters(data_parts,maybe_params) {
|
|
858
|
+
|
|
859
|
+
let promisory = data_parts.map(async (a_part) => { // map and return an array of promises
|
|
860
|
+
if ( (a_part.indexOf("files::") === 0) || (a_part.indexOf("script::") === 0) ) { ///
|
|
861
|
+
if ( (a_part.indexOf("files::params::") === 0) || (a_part.indexOf("script::params::") === 0) ) { //
|
|
862
|
+
let verbatim_blocks = false
|
|
863
|
+
if ( this.has_verbatim_params(a_part) ) {
|
|
864
|
+
verbatim_blocks = {}
|
|
865
|
+
a_part = this.block_verabtim_parameters(a_part,verbatim_blocks)
|
|
866
|
+
}
|
|
867
|
+
a_part = parse_util.remove_white(a_part)
|
|
868
|
+
if ( verbatim_blocks !== false ) {
|
|
869
|
+
a_part = this.unblock_verabtim_parameters(a_part,verbatim_blocks)
|
|
870
|
+
}
|
|
871
|
+
let parts = a_part.split('<<{')
|
|
872
|
+
let part_key = parts.shift()
|
|
873
|
+
let part_def = parts.join('<<{')
|
|
874
|
+
//
|
|
875
|
+
maybe_params[part_key] = await this.build_param_tree(part_key,part_def.substring(0,part_def.lastIndexOf('}')))
|
|
876
|
+
//
|
|
877
|
+
return part_key
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return a_part
|
|
881
|
+
})
|
|
882
|
+
data_parts = await Promise.all(promisory)
|
|
883
|
+
return data_parts
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* pull_out_files
|
|
889
|
+
* @param {Array} data_parts
|
|
890
|
+
* @returns {Array}
|
|
891
|
+
*/
|
|
892
|
+
pull_out_files(data_parts) {
|
|
893
|
+
|
|
894
|
+
let just_files = data_parts.filter((a_part) => {
|
|
895
|
+
return (a_part.indexOf("files::") === 0)
|
|
896
|
+
})
|
|
897
|
+
return just_files.map((a_part) => {
|
|
898
|
+
return ( a_part.substring("files::".length) )
|
|
899
|
+
})
|
|
900
|
+
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
*
|
|
906
|
+
* @param {*} data_parts
|
|
907
|
+
* @returns
|
|
908
|
+
*/
|
|
909
|
+
pull_out_css(data_parts) {
|
|
910
|
+
|
|
911
|
+
let just_files = data_parts.filter((a_part) => {
|
|
912
|
+
return (a_part.indexOf("css::") === 0)
|
|
913
|
+
})
|
|
914
|
+
return just_files.map((a_part) => {
|
|
915
|
+
return ( a_part.substring("css::".length) )
|
|
916
|
+
})
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* pull_out_script
|
|
922
|
+
* @param {Array} data_parts
|
|
923
|
+
* @returns {Array}
|
|
924
|
+
*/
|
|
925
|
+
pull_out_script(data_parts) {
|
|
926
|
+
let just_scripts = data_parts.filter((a_part) => {
|
|
927
|
+
return (a_part.indexOf("script::") === 0)
|
|
928
|
+
})
|
|
929
|
+
return just_scripts.map((a_part) => {
|
|
930
|
+
return ( a_part.substring("script::".length) )
|
|
931
|
+
})
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
*
|
|
937
|
+
* @param {Array} data_parts
|
|
938
|
+
* @returns {Array}
|
|
939
|
+
*/
|
|
940
|
+
pull_out_bundles(data_parts) {
|
|
941
|
+
let just_bundles = data_parts.filter((a_part) => {
|
|
942
|
+
return (a_part.indexOf("bundle::") === 0)
|
|
943
|
+
})
|
|
944
|
+
return just_bundles.map((a_part) => {
|
|
945
|
+
return ( a_part.substring("bundle::".length) )
|
|
946
|
+
})
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
|
|
950
|
+
/**
|
|
951
|
+
* pull_out_html_directives
|
|
952
|
+
* @param {Array} data_parts
|
|
953
|
+
* @returns {Array}
|
|
954
|
+
*/
|
|
955
|
+
pull_out_html_directives(data_parts) {
|
|
956
|
+
let just_html_d = data_parts.filter((a_part) => {
|
|
957
|
+
return (a_part.indexOf("html:") === 0)
|
|
958
|
+
})
|
|
959
|
+
return just_html_d
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* Returns an esimation of the size of the object.
|
|
965
|
+
*
|
|
966
|
+
* @param {object|string|Array} o_value
|
|
967
|
+
* @returns {number}
|
|
968
|
+
*/
|
|
969
|
+
figure_size(o_value) {
|
|
970
|
+
if ( typeof o_value === "string" ) {
|
|
971
|
+
return o_value.length
|
|
972
|
+
} else if ( Array.isArray(o_value) ) {
|
|
973
|
+
return o_value.length
|
|
974
|
+
} else if ( typeof o_value === "object" ) {
|
|
975
|
+
return Object.keys(o_value).length
|
|
976
|
+
}
|
|
977
|
+
return 0
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
/**
|
|
982
|
+
*
|
|
983
|
+
* @param {Array} data_parts -- array of sections describing file to include along with alfterations related to configuration
|
|
984
|
+
* @param {string} which_config_vars
|
|
985
|
+
* @returns {Array}
|
|
986
|
+
*/
|
|
987
|
+
config_var_transformations(data_parts,which_config_vars) {
|
|
988
|
+
//
|
|
989
|
+
let vars = this.app_skel_vars[which_config_vars] // a single key `uses_config_vars` from the header identifies the variable/value group for the particular skeleton
|
|
990
|
+
if ( vars && (typeof vars === "object")) {
|
|
991
|
+
for ( let [vname,value] of Object.entries(vars) ) {
|
|
992
|
+
let o_value = value // original
|
|
993
|
+
if ( typeof value !== "string" ) {
|
|
994
|
+
value = JSON.stringify(value)
|
|
995
|
+
}
|
|
996
|
+
let finder = `@!{${vname}}`
|
|
997
|
+
let count_finder = `@#{${vname}}`
|
|
998
|
+
//
|
|
999
|
+
data_parts = data_parts.map((el) => { // alter the skeleton (an update)
|
|
1000
|
+
if ( el.indexOf(finder) > 0 ) {
|
|
1001
|
+
el = el.replace(finder,value)
|
|
1002
|
+
}
|
|
1003
|
+
if ( el.indexOf(count_finder) > 0 ) {
|
|
1004
|
+
el = el.replace(count_finder,this.figure_size(o_value))
|
|
1005
|
+
}
|
|
1006
|
+
return el
|
|
1007
|
+
})
|
|
1008
|
+
//
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
//
|
|
1012
|
+
return data_parts
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
|
|
1016
|
+
/**
|
|
1017
|
+
*
|
|
1018
|
+
* @param {object} data_parts
|
|
1019
|
+
* @returns
|
|
1020
|
+
*/
|
|
1021
|
+
async skeleton_parts_extraction(data_parts) {
|
|
1022
|
+
let files = []
|
|
1023
|
+
let css = []
|
|
1024
|
+
let scripts = []
|
|
1025
|
+
let bundles = []
|
|
1026
|
+
let maybe_params = {}
|
|
1027
|
+
//
|
|
1028
|
+
// The defaults can update the basic skeleton structure prior to skeleton processing (parsing, evaluating)
|
|
1029
|
+
// structure expansions of parts that will be replicated
|
|
1030
|
+
data_parts = await this.sequence_expansion(data_parts,maybe_params)
|
|
1031
|
+
// structure selected by operating on parameters specifically evaluated by configuration assignments
|
|
1032
|
+
data_parts = await this.capture_parameters(data_parts,maybe_params)
|
|
1033
|
+
//
|
|
1034
|
+
files = this.pull_out_files(data_parts) // look for the files that will be expanded beneath the top skeleton
|
|
1035
|
+
css = this.pull_out_css(data_parts) // look for the files that will be expanded beneath the top skeleton
|
|
1036
|
+
scripts = this.pull_out_script(data_parts) // pull out script files (remaining after bundle preparation)
|
|
1037
|
+
bundles = this.pull_out_bundles(data_parts)
|
|
1038
|
+
//
|
|
1039
|
+
return [data_parts,files,css,scripts,bundles,maybe_params]
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
/**
|
|
1044
|
+
*
|
|
1045
|
+
* Walks through the map of all loaded skeletons, parsing them into sections.
|
|
1046
|
+
* Sections are demarcated by the beginning of section symbol '$$'.
|
|
1047
|
+
* This method splits the string made from the skel file data (mapped by a key in all_skeletons) into sections,
|
|
1048
|
+
* splitting on '$$', and processes each section separately for structure only.
|
|
1049
|
+
*
|
|
1050
|
+
* This method parses each skeleton file by first creating an array of sections.
|
|
1051
|
+
* After the first split, the sections array is the skeleton as a list of parseable file specifications and uses.
|
|
1052
|
+
*
|
|
1053
|
+
* This method operates on each section in order to determine if further primary parsing is needed.
|
|
1054
|
+
* In some cases, the sections describe loops or the inclusions of files with parameters. This method will expand loops,
|
|
1055
|
+
* and create data structure to line up parameters with future evaluations, handled by methods called after this one.
|
|
1056
|
+
*
|
|
1057
|
+
* The first alteration of the sections is a variable substitution.
|
|
1058
|
+
* The first section of the file is often a specialization of the project configuration file read in by the main command line.
|
|
1059
|
+
* The configuration may specify a set of variable subsitutions that may be performed prior to any other section processing.
|
|
1060
|
+
* This method updates each section (elements in `data_parts`) with a variable subsitution.
|
|
1061
|
+
*
|
|
1062
|
+
* After taking care of any variable substitutions, this method removes comments from the sections.
|
|
1063
|
+
*
|
|
1064
|
+
* Next, the loops and parameters are processed. (note `maybe_params` is an empty object used to capcture the parsing data)
|
|
1065
|
+
* These processes may alter the skeletal structure by altering the array `data_parts`. In the case of loops, elements
|
|
1066
|
+
* may be added to the array. In the case of parameters, the section is returned with all spaces removed, and the
|
|
1067
|
+
* structure `maybe_params` will contain an entire parameter tree, needed for passing values down into a sub-skeleton template.
|
|
1068
|
+
*
|
|
1069
|
+
* Finally, this method makes arrays of markup imports ('files') and language imports ('scripts') for later access and processing.
|
|
1070
|
+
*
|
|
1071
|
+
* All the arrays and structures created for a skeleton file are returned as part of a map of the file names to the
|
|
1072
|
+
* a structure with a field for each one of the arrays or structures.
|
|
1073
|
+
*
|
|
1074
|
+
* `skel_to_concerns` maps a skeleton file to a list of concerns that use it.
|
|
1075
|
+
* Each concern will be identified in an ogroup, where the configuration `app_skel_vars` entry
|
|
1076
|
+
* will be be called out for the variables values used in calculating the skeleton for the concerns
|
|
1077
|
+
* sharing the ogroup. The ogroup.targets.uses_config_vars will identify the variable evaluations
|
|
1078
|
+
* to apply to the skeletons they use.
|
|
1079
|
+
*
|
|
1080
|
+
*
|
|
1081
|
+
* @param {object} all_skeletons - `skel file path` to file contents.
|
|
1082
|
+
* @param {object} skel_to_concerns - allows for determining splits due to structure preferences
|
|
1083
|
+
* @returns {object} - a map of `skel file path` to objects where each object contains parsing data structures
|
|
1084
|
+
*/
|
|
1085
|
+
async section_parsing(all_skeletons) {
|
|
1086
|
+
let section_data = {}
|
|
1087
|
+
//
|
|
1088
|
+
for ( let [sky,data_m] of Object.entries(all_skeletons) ) { // data is a string (the contents of the skeleton file)
|
|
1089
|
+
//
|
|
1090
|
+
let data = data_m.original
|
|
1091
|
+
// data_parts an array
|
|
1092
|
+
let data_parts = data.split('$$') // all sectional definitions start with $$ (split makes leading section empty)
|
|
1093
|
+
let no_data = (data_parts.length < 2) //
|
|
1094
|
+
if ( no_data ) continue
|
|
1095
|
+
//
|
|
1096
|
+
section_data[sky] = {}
|
|
1097
|
+
let skel_ops = data_m.ops
|
|
1098
|
+
//
|
|
1099
|
+
data_parts.shift() // the first element is expected to be empty or white space or some comment (throw away)
|
|
1100
|
+
data_m.shifted_data = data_parts
|
|
1101
|
+
//
|
|
1102
|
+
data_parts = data_parts.map((el) => { return parse_util.clear_comments(el.trim()).trim() })
|
|
1103
|
+
//
|
|
1104
|
+
//
|
|
1105
|
+
let def_check = data_parts[0]
|
|
1106
|
+
let def_defaults = false
|
|
1107
|
+
if ( def_check.indexOf("defs:") === 0 ) { // if present, this becomes a structure.
|
|
1108
|
+
def_defaults = def_check
|
|
1109
|
+
data_parts.shift() // will process defaults
|
|
1110
|
+
data_m.shifted_data = data_parts
|
|
1111
|
+
//
|
|
1112
|
+
data_parts = [].concat(data_parts) // make a copy
|
|
1113
|
+
//
|
|
1114
|
+
try {
|
|
1115
|
+
def_defaults = JSON.parse(def_defaults.substring("defs:".length))
|
|
1116
|
+
} catch(e) {
|
|
1117
|
+
def_defaults = undefined
|
|
1118
|
+
}
|
|
1119
|
+
// make a default entry for this skeleton
|
|
1120
|
+
if ( def_defaults ) { // defaults from the skeleton file $$defs
|
|
1121
|
+
let cvars_ky = def_defaults?.uses_config_vars // a name found in a configuration
|
|
1122
|
+
if ( typeof cvars_ky === "string" ) {
|
|
1123
|
+
//
|
|
1124
|
+
// use configuration variable values to put in values for skeleton level variables
|
|
1125
|
+
data_parts = this.config_var_transformations(data_parts,cvars_ky)
|
|
1126
|
+
// next expand sequences and get separate imports for markup and script
|
|
1127
|
+
let [dps,files,css,scripts,bundles,maybe_params] = await this.skeleton_parts_extraction(data_parts)
|
|
1128
|
+
data_parts = dps
|
|
1129
|
+
section_data[sky]["defaults"] = {
|
|
1130
|
+
"defaults" : def_defaults,
|
|
1131
|
+
"final" : {
|
|
1132
|
+
"markup" : false,
|
|
1133
|
+
"scripts" : false
|
|
1134
|
+
},
|
|
1135
|
+
"skeleton" : data_parts,
|
|
1136
|
+
"files" : files,
|
|
1137
|
+
"css" : css,
|
|
1138
|
+
"scripts" : scripts,
|
|
1139
|
+
"bundles" : bundles,
|
|
1140
|
+
"parameterized" : maybe_params
|
|
1141
|
+
}
|
|
1142
|
+
//
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// all the ops assigned to this skeleton from configuration...
|
|
1148
|
+
for ( let op_ky in skel_ops) {
|
|
1149
|
+
if ( section_data[sky][op_ky] === undefined ) {
|
|
1150
|
+
//
|
|
1151
|
+
data_parts = data_m.shifted_data
|
|
1152
|
+
//
|
|
1153
|
+
// use configuration variable values to put in values for skeleton level variables
|
|
1154
|
+
data_parts = this.config_var_transformations(data_parts,op_ky)
|
|
1155
|
+
// next expand sequences and get separate imports for markup and script
|
|
1156
|
+
let [dps,files,css,scripts,bundles,maybe_params] = await this.skeleton_parts_extraction(data_parts)
|
|
1157
|
+
data_parts = dps
|
|
1158
|
+
section_data[sky][op_ky] = {
|
|
1159
|
+
"defaults" : def_defaults,
|
|
1160
|
+
"final" : {
|
|
1161
|
+
"markup" : false,
|
|
1162
|
+
"scripts" : false
|
|
1163
|
+
},
|
|
1164
|
+
"skeleton" : data_parts,
|
|
1165
|
+
"files" : files,
|
|
1166
|
+
"css" : css,
|
|
1167
|
+
"scripts" : scripts,
|
|
1168
|
+
"bundles" : bundles,
|
|
1169
|
+
"parameterized" : maybe_params
|
|
1170
|
+
}
|
|
1171
|
+
//
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// console.dir(section_data,{depth : 6})
|
|
1177
|
+
// process.exit(0)
|
|
1178
|
+
|
|
1179
|
+
return section_data // return the whole table
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
|
|
1183
|
+
/**
|
|
1184
|
+
*
|
|
1185
|
+
* Outputs to each concern the variable forms table, yielding default substitutions.
|
|
1186
|
+
* If the developer chooses to change these before final template generation he may.
|
|
1187
|
+
*
|
|
1188
|
+
*
|
|
1189
|
+
* Does not output an entire DB for each page that is being generated.
|
|
1190
|
+
* This outputs a selection for each template.
|
|
1191
|
+
*
|
|
1192
|
+
*
|
|
1193
|
+
*
|
|
1194
|
+
* `
|
|
1195
|
+
"name_parameters" : {
|
|
1196
|
+
"db" : "[names]/name-drop.db",
|
|
1197
|
+
"parameter_values" : "[websites]/@concern/@target/name-drop.json",
|
|
1198
|
+
"index.tmplt" : [ "contact_box", "about_box", "topicBox_<1,3>", "thankyou_box", "register" ],
|
|
1199
|
+
"login.tmplt" : [ "contact_box", "thankyou_box", "login" ]
|
|
1200
|
+
}
|
|
1201
|
+
`
|
|
1202
|
+
*
|
|
1203
|
+
*/
|
|
1204
|
+
async name_parameters_output() {
|
|
1205
|
+
//
|
|
1206
|
+
let outputs = this.outputs
|
|
1207
|
+
for ( let ogroup of outputs ) {
|
|
1208
|
+
//
|
|
1209
|
+
let name_parameters = ogroup.name_parameters ? true : false
|
|
1210
|
+
if ( name_parameters ) {
|
|
1211
|
+
let concerns = ogroup.targets.concerns
|
|
1212
|
+
//
|
|
1213
|
+
for ( let concern of concerns ) {
|
|
1214
|
+
name_parameters = Object.assign({},ogroup.name_parameters) // actually its an object
|
|
1215
|
+
let drops_db = this.name_drops_db // The template db that allows for specializations to be figured for the concerns output
|
|
1216
|
+
if ( name_parameters.db ) {
|
|
1217
|
+
let name_db = this.paths.compile_one_path(name_parameters.db)
|
|
1218
|
+
drops_db = await this.load_name_drops_db(name_db) // loading this each time (useful after specialization)
|
|
1219
|
+
delete name_parameters.db
|
|
1220
|
+
}
|
|
1221
|
+
let output = name_parameters.parameter_values ? this.paths.compile_one_path(name_parameters.parameter_values) : false
|
|
1222
|
+
if ( output ) {
|
|
1223
|
+
output = output.replace('@concern',concern)
|
|
1224
|
+
output = output.replace("@target",this.created_dir)
|
|
1225
|
+
delete name_parameters.parameter_values // delete one more non file name key
|
|
1226
|
+
let value_object = {} // collect for each template
|
|
1227
|
+
for ( let [tmplt_ky,db_keys] of Object.entries(name_parameters) ) {
|
|
1228
|
+
if ( tmplt_ky === 'db' ) continue // added this for clarity during validation
|
|
1229
|
+
if ( tmplt_ky === 'parameter_values' ) continue // added this for clarity during validation
|
|
1230
|
+
if ( tmplt_ky.indexOf('.tmplt') < 0 ) continue // handle case not planned for at this time
|
|
1231
|
+
// tmplt_ky has to be the name of a tempalte file
|
|
1232
|
+
value_object[tmplt_ky] = {} // vars we are getting for this template
|
|
1233
|
+
for ( let form_name of db_keys ) { // maybe it's a form or some display
|
|
1234
|
+
value_object[tmplt_ky][form_name] = drops_db[form_name]
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
output = this.paths.resolve(output)
|
|
1238
|
+
await fos.write_out_pretty_json(output,value_object,4)
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
//
|
|
1242
|
+
}
|
|
1243
|
+
//
|
|
1244
|
+
}
|
|
1245
|
+
//
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
|
|
1249
|
+
|
|
1250
|
+
|
|
1251
|
+
/**
|
|
1252
|
+
*
|
|
1253
|
+
* This is for gathering stats about script usage.
|
|
1254
|
+
*
|
|
1255
|
+
* @param {object} transform_1
|
|
1256
|
+
* @returns {object}
|
|
1257
|
+
*/
|
|
1258
|
+
coalesce_scripts(transform_1) { // now has a nother level (concerns)
|
|
1259
|
+
//
|
|
1260
|
+
let scripts_occurences = {}
|
|
1261
|
+
for ( let op_map of Object.values(transform_1) ) {
|
|
1262
|
+
for ( let dpart of Object.values(op_map) ) {
|
|
1263
|
+
let scripts = dpart.scripts
|
|
1264
|
+
if ( scripts.length ) {
|
|
1265
|
+
for ( let script of scripts ) {
|
|
1266
|
+
let p = scripts_occurences[script]
|
|
1267
|
+
scripts_occurences[script] = p ? p + 1 : 1
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
//
|
|
1273
|
+
return scripts_occurences
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
|
|
1277
|
+
/**
|
|
1278
|
+
*
|
|
1279
|
+
* @param {object} sorted_stats
|
|
1280
|
+
* @returns {object}
|
|
1281
|
+
*/
|
|
1282
|
+
partition_stats(sorted_stats) {
|
|
1283
|
+
let partitions = []
|
|
1284
|
+
//
|
|
1285
|
+
let bdiff = Object.values(sorted_stats)
|
|
1286
|
+
let n = bdiff.length
|
|
1287
|
+
let diffs = []
|
|
1288
|
+
for ( let i = 0; i < n-1; i++ ) {
|
|
1289
|
+
let v0 = bdiff[i]
|
|
1290
|
+
let v1 = bdiff[i+1]
|
|
1291
|
+
diffs.push(v1-v0)
|
|
1292
|
+
}
|
|
1293
|
+
let last_big_diff = 0
|
|
1294
|
+
partitions.push([])
|
|
1295
|
+
let p_index = 0
|
|
1296
|
+
let skeys = Object.keys(sorted_stats)
|
|
1297
|
+
for ( let i = 0; i < n-1; i++ ) {
|
|
1298
|
+
let d = diffs[i]
|
|
1299
|
+
if ( d > last_big_diff ) {
|
|
1300
|
+
d = last_big_diff
|
|
1301
|
+
partitions.push([])
|
|
1302
|
+
p_index++
|
|
1303
|
+
}
|
|
1304
|
+
partitions[p_index].push(skeys[i])
|
|
1305
|
+
}
|
|
1306
|
+
partitions[p_index].push(skeys[n-1])
|
|
1307
|
+
return partitions
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
|
|
1311
|
+
/**
|
|
1312
|
+
*
|
|
1313
|
+
* @param {object} script_stats
|
|
1314
|
+
* @param {number} threshold
|
|
1315
|
+
* @returns {object}
|
|
1316
|
+
*/
|
|
1317
|
+
partition(script_stats) {
|
|
1318
|
+
|
|
1319
|
+
let sorted_stats = {}
|
|
1320
|
+
//
|
|
1321
|
+
// sort by key prefix to get a grouping of functional parts
|
|
1322
|
+
sorted_stats = parse_util.key_sort(script_stats,(ky) => {
|
|
1323
|
+
let slash = ky.indexOf('/')
|
|
1324
|
+
if ( slash > 0 ) {
|
|
1325
|
+
let tester = ky.substring(0,ky.indexOf('/'));
|
|
1326
|
+
return tester
|
|
1327
|
+
} else return ky
|
|
1328
|
+
})
|
|
1329
|
+
//
|
|
1330
|
+
|
|
1331
|
+
// now put common prefixes into buckets
|
|
1332
|
+
let prefix_buckets = {}
|
|
1333
|
+
let bucket_keys = Object.keys(sorted_stats)
|
|
1334
|
+
for ( let ky of bucket_keys ) {
|
|
1335
|
+
if ( ky[0] === '[' ) {
|
|
1336
|
+
let bucket_ky = ky.substring(0,ky.indexOf('/'))
|
|
1337
|
+
let bucket = prefix_buckets[bucket_ky]
|
|
1338
|
+
if ( bucket === undefined ) {
|
|
1339
|
+
bucket = {}
|
|
1340
|
+
prefix_buckets[bucket_ky] = bucket
|
|
1341
|
+
}
|
|
1342
|
+
bucket[ky] = sorted_stats[ky]
|
|
1343
|
+
} else {
|
|
1344
|
+
let bucket = prefix_buckets["[script]"]
|
|
1345
|
+
if ( bucket === undefined ) {
|
|
1346
|
+
bucket = {}
|
|
1347
|
+
prefix_buckets["[script]"] = bucket
|
|
1348
|
+
}
|
|
1349
|
+
bucket[ky] = sorted_stats[ky]
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
// before partitioning the sorted stats buckets,
|
|
1353
|
+
// make sure they are sorted by score
|
|
1354
|
+
//
|
|
1355
|
+
//console.log("sorted_stats")
|
|
1356
|
+
let nested_partitions = {}
|
|
1357
|
+
|
|
1358
|
+
for ( let [ky,bucket] of Object.entries(prefix_buckets) ) {
|
|
1359
|
+
//
|
|
1360
|
+
let keys = Object.keys(bucket)
|
|
1361
|
+
keys.sort((k1,k2) => {
|
|
1362
|
+
let v1 = bucket[k1]
|
|
1363
|
+
let v2 = bucket[k2]
|
|
1364
|
+
return v1 - v2
|
|
1365
|
+
})
|
|
1366
|
+
let sorted_bucket = {}
|
|
1367
|
+
for ( let bky of keys ) {
|
|
1368
|
+
sorted_bucket[bky] = bucket[bky]
|
|
1369
|
+
}
|
|
1370
|
+
//console.log(ky)
|
|
1371
|
+
//console.dir(sorted_bucket)
|
|
1372
|
+
//
|
|
1373
|
+
nested_partitions[ky] = this.partition_stats(sorted_bucket)
|
|
1374
|
+
}
|
|
1375
|
+
//
|
|
1376
|
+
|
|
1377
|
+
return nested_partitions
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
|
|
1381
|
+
/**
|
|
1382
|
+
* Partitions files into directories for later use in putting code into
|
|
1383
|
+
* bundles, which will be requested in headers.
|
|
1384
|
+
*
|
|
1385
|
+
* prep_script_directories is one of the first operations in skeleton skeleton_parsing.
|
|
1386
|
+
*
|
|
1387
|
+
* Each skeleton lists a number of scripts that make resulting applications work in general.
|
|
1388
|
+
*
|
|
1389
|
+
* Previously, all of the scripts were placed into the HTML file. But, that results in inefficient loading
|
|
1390
|
+
* and management.
|
|
1391
|
+
*
|
|
1392
|
+
* @param {object} partitions
|
|
1393
|
+
*/
|
|
1394
|
+
async prep_script_directories(partitions) {
|
|
1395
|
+
|
|
1396
|
+
for ( let pky in partitions ) {
|
|
1397
|
+
console.log("prep_script_directories",pky)
|
|
1398
|
+
if ( pky.indexOf(">") > 1 ) {
|
|
1399
|
+
console.log("\tskipping")
|
|
1400
|
+
} else {
|
|
1401
|
+
console.log("DATA")
|
|
1402
|
+
let bare_ky = pky.replace('[','').replace(']','')
|
|
1403
|
+
let script_src_dir = this.top_dir_locations[bare_ky]
|
|
1404
|
+
console.log("script source: ",script_src_dir,">> bare_ky:: ",bare_ky)
|
|
1405
|
+
let script_grouping_dir = `${this.project_dir}bundle_src/${bare_ky}`
|
|
1406
|
+
script_grouping_dir = this.paths.resolve(script_grouping_dir)
|
|
1407
|
+
console.log("script output: ", script_grouping_dir)
|
|
1408
|
+
await fos.ensure_directories(script_grouping_dir)
|
|
1409
|
+
let subdr = 'A'
|
|
1410
|
+
for ( let file_list of partitions[pky] ) {
|
|
1411
|
+
if ( file_list.length ) {
|
|
1412
|
+
let part_dir = `${script_grouping_dir}/${subdr}`
|
|
1413
|
+
await fos.ensure_directories(part_dir)
|
|
1414
|
+
for ( let file of file_list ) {
|
|
1415
|
+
file = file.replace(`${pky}/`,'').replace('<<','')
|
|
1416
|
+
let fpath = `${part_dir}/${file}`
|
|
1417
|
+
let spath = `${script_src_dir}/${file}`
|
|
1418
|
+
if ( file.indexOf('/') > 0 ) {
|
|
1419
|
+
console.log("ENSURE:",part_dir)
|
|
1420
|
+
let parts = file.split('/')
|
|
1421
|
+
parts.pop()
|
|
1422
|
+
parts = parts.join('/')
|
|
1423
|
+
await fos.ensure_directories(`${part_dir}/${parts}`)
|
|
1424
|
+
}
|
|
1425
|
+
console.log(spath)
|
|
1426
|
+
console.log(fpath)
|
|
1427
|
+
await fos.file_copier(spath,fpath)
|
|
1428
|
+
}
|
|
1429
|
+
subdr = parse_util.next_char(subdr)
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
|
|
1437
|
+
/**
|
|
1438
|
+
*
|
|
1439
|
+
* @param {object} transformed
|
|
1440
|
+
* @param {object} script_stats
|
|
1441
|
+
*/
|
|
1442
|
+
update_script_stats_usage(transformed,script_stats) {
|
|
1443
|
+
//
|
|
1444
|
+
let outputs = this.outputs
|
|
1445
|
+
|
|
1446
|
+
let top_level_skel_counts = {}
|
|
1447
|
+
|
|
1448
|
+
for ( let output of outputs ) {
|
|
1449
|
+
let n_concerns = output.targets.concerns.length
|
|
1450
|
+
let skeletons = output.skeletons
|
|
1451
|
+
let mid_level_skel_counts = {}
|
|
1452
|
+
for ( let skel of Object.values(skeletons) ) {
|
|
1453
|
+
let p = mid_level_skel_counts[skel]
|
|
1454
|
+
mid_level_skel_counts[skel] = p ? p + 1 : 1
|
|
1455
|
+
}
|
|
1456
|
+
for ( let [skel,count] of Object.entries(mid_level_skel_counts) ) {
|
|
1457
|
+
let p = top_level_skel_counts[skel]
|
|
1458
|
+
top_level_skel_counts[skel] = p ? p + count*n_concerns : count*n_concerns
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
|
|
1463
|
+
for ( let [skel,count] of Object.entries(top_level_skel_counts) ) {
|
|
1464
|
+
|
|
1465
|
+
let usages = transformed[skel]
|
|
1466
|
+
if ( usages ) {
|
|
1467
|
+
|
|
1468
|
+
for ( let script of usages.scripts ) {
|
|
1469
|
+
let p = script_stats[script]
|
|
1470
|
+
if ( p ) {
|
|
1471
|
+
script_stats[script] = p*count
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
|
|
1479
|
+
/**
|
|
1480
|
+
*
|
|
1481
|
+
* @param {string} step_entry
|
|
1482
|
+
* @param {Array} cross_directory_check - optional
|
|
1483
|
+
* @returns {Array} - a tripple [file name, token indicating a compiled path, the type of file]
|
|
1484
|
+
*/
|
|
1485
|
+
get_file_data_descriptor(step_entry,cross_directory_check) {
|
|
1486
|
+
//
|
|
1487
|
+
let file_name = false
|
|
1488
|
+
let path_finder = false
|
|
1489
|
+
let entry_type = false
|
|
1490
|
+
let entry_directive = false
|
|
1491
|
+
//
|
|
1492
|
+
let file_entry_starter = !(cross_directory_check) ? this.entry_starter.exec(step_entry) : false
|
|
1493
|
+
if ( !file_entry_starter ) {
|
|
1494
|
+
file_entry_starter = cross_directory_check ? cross_directory_check : this.cross_type_directory.exec(step_entry)
|
|
1495
|
+
if ( file_entry_starter ) {
|
|
1496
|
+
entry_directive = file_entry_starter[1]
|
|
1497
|
+
entry_type = file_entry_starter[2]
|
|
1498
|
+
file_name = file_entry_starter[3]
|
|
1499
|
+
if ( file_name.indexOf('.') < 0 ) {
|
|
1500
|
+
file_name = `${file_name}.${entry_type}`
|
|
1501
|
+
}
|
|
1502
|
+
if ( entry_type === 'js' ) entry_type = "script"
|
|
1503
|
+
if ( entry_type === 'tjs' ) entry_type = "script"
|
|
1504
|
+
}
|
|
1505
|
+
} else {
|
|
1506
|
+
entry_directive = file_entry_starter[1]
|
|
1507
|
+
file_name = step_entry.substring((entry_directive).length + 2)
|
|
1508
|
+
}
|
|
1509
|
+
if ( entry_directive ) {
|
|
1510
|
+
let [pf,et] = this.entry_directive_location_remap(entry_directive)
|
|
1511
|
+
path_finder = pf
|
|
1512
|
+
entry_type = (entry_type && (et !== entry_type)) ? entry_type : et
|
|
1513
|
+
}
|
|
1514
|
+
//
|
|
1515
|
+
return [file_name,path_finder,entry_type]
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
|
|
1519
|
+
/**
|
|
1520
|
+
* Looks at the entry specifiers and determines the absolute location of a file to load.
|
|
1521
|
+
* If successful, this returns an a key-value object with three keys, type, file, data, corresponding to
|
|
1522
|
+
* the type of the file, the file path, and the data stored in the file (a string)
|
|
1523
|
+
*
|
|
1524
|
+
* @param {string} step_entry
|
|
1525
|
+
* @param {Array} cross_directory_check - optional .. the result of a RegExp,exec if it is provide
|
|
1526
|
+
* @returns {object|string}
|
|
1527
|
+
*/
|
|
1528
|
+
async entry_loading(step_entry,cross_directory_check) {
|
|
1529
|
+
//
|
|
1530
|
+
let map_value = {}
|
|
1531
|
+
let [file_name,path_finder,entry_type] = this.get_file_data_descriptor(step_entry,cross_directory_check)
|
|
1532
|
+
//
|
|
1533
|
+
//
|
|
1534
|
+
if ( file_name ) {
|
|
1535
|
+
let file_path = this.top_dir_locations[path_finder]
|
|
1536
|
+
if ( file_name[0] === '[' ) {
|
|
1537
|
+
let entry_match = this.sibling_type_directory_match.exec(file_name)
|
|
1538
|
+
if ( entry_match ) {
|
|
1539
|
+
let path_f = entry_match[1]
|
|
1540
|
+
file_path = this.top_dir_locations[path_f]
|
|
1541
|
+
file_name = entry_match[2]
|
|
1542
|
+
} else {
|
|
1543
|
+
// handle special cases where the developer has custom leaf code
|
|
1544
|
+
// use the syntax to get information for constructing the directory name.
|
|
1545
|
+
// Do not load a file. That will be done in a second phase.
|
|
1546
|
+
if ( file_name.indexOf('<') > 0 ) {
|
|
1547
|
+
entry_match = this.concerns_directory_redirect_match.exec(file_name)
|
|
1548
|
+
if ( entry_match ) {
|
|
1549
|
+
let search_form = entry_match[1]
|
|
1550
|
+
let use_case = entry_match[2]
|
|
1551
|
+
let p_finder = entry_match[3]
|
|
1552
|
+
let file_form = this.find_concerns[search_form]
|
|
1553
|
+
let file_path = file_name.substring(file_name.indexOf(']')+1)
|
|
1554
|
+
let data = `{{{ @{${file_form}}${file_path} }}}`
|
|
1555
|
+
//console.log(file_name,path_finder,entry_type,file_form,use_case,p_finder,data)
|
|
1556
|
+
map_value = {
|
|
1557
|
+
"search_form" : search_form,
|
|
1558
|
+
"file" : file_path,
|
|
1559
|
+
"use_case" : use_case,
|
|
1560
|
+
"kernel" : p_finder,
|
|
1561
|
+
"type" : entry_type,
|
|
1562
|
+
"data" : data
|
|
1563
|
+
}
|
|
1564
|
+
return map_value
|
|
1565
|
+
}
|
|
1566
|
+
} else {
|
|
1567
|
+
return "not_handled - a"
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
file_path = `${file_path}/${file_name}`
|
|
1572
|
+
let data = await fos.load_data_at_path(file_path)
|
|
1573
|
+
let datawc = ""
|
|
1574
|
+
if ( data ) {
|
|
1575
|
+
datawc = "" + data
|
|
1576
|
+
data = parse_util.clear_comments(data)
|
|
1577
|
+
} else {
|
|
1578
|
+
data = "NO DATA"
|
|
1579
|
+
datawc = data
|
|
1580
|
+
}
|
|
1581
|
+
map_value = {
|
|
1582
|
+
"type" : entry_type,
|
|
1583
|
+
"file" : file_path,
|
|
1584
|
+
"data" : data
|
|
1585
|
+
}
|
|
1586
|
+
if ( this.check_recursive_data(data) ) {
|
|
1587
|
+
map_value.recursive = await this.get_files_and_vars(step_entry,data)
|
|
1588
|
+
}
|
|
1589
|
+
//
|
|
1590
|
+
this.shared_entries[step_entry] = Object.assign({},map_value)
|
|
1591
|
+
return map_value
|
|
1592
|
+
}
|
|
1593
|
+
return "not hanlded - b"
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
|
|
1597
|
+
|
|
1598
|
+
/**
|
|
1599
|
+
*
|
|
1600
|
+
* @param {string} data
|
|
1601
|
+
* @param {object} carrier - an object with the filed `var`
|
|
1602
|
+
* @returns
|
|
1603
|
+
*/
|
|
1604
|
+
list_start(data,carrier) {
|
|
1605
|
+
let dat_match = this.start_of_list.exec(data)
|
|
1606
|
+
if ( dat_match ) {
|
|
1607
|
+
carrier.var = dat_match[1]
|
|
1608
|
+
return true
|
|
1609
|
+
}
|
|
1610
|
+
return false
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
|
|
1614
|
+
/**
|
|
1615
|
+
*
|
|
1616
|
+
* @param {object | boolean} params_def
|
|
1617
|
+
* @param {string} data
|
|
1618
|
+
* @returns {object}
|
|
1619
|
+
*/
|
|
1620
|
+
errant_variable_extraction(params_def,data) {
|
|
1621
|
+
//
|
|
1622
|
+
let eve_results = {}
|
|
1623
|
+
let occurrences = data.match(this.var_pattern)
|
|
1624
|
+
//
|
|
1625
|
+
if ( !occurrences ) return undefined
|
|
1626
|
+
//
|
|
1627
|
+
occurrences = occurrences.map((occ) => {
|
|
1628
|
+
return occ.replace("@{","").replace("}","")
|
|
1629
|
+
})
|
|
1630
|
+
//
|
|
1631
|
+
if ( occurrences.length ) {
|
|
1632
|
+
for ( let occur of occurrences ) {
|
|
1633
|
+
let atype = ""
|
|
1634
|
+
if ( occur.indexOf('%') > 0 ) {
|
|
1635
|
+
atype = occur.split('%')[1]
|
|
1636
|
+
atype = '%' + atype + '%'
|
|
1637
|
+
}
|
|
1638
|
+
eve_results[occur] = atype
|
|
1639
|
+
}
|
|
1640
|
+
if ( !params_def ) {
|
|
1641
|
+
params_def = eve_results
|
|
1642
|
+
} else {
|
|
1643
|
+
for ( let ky of Object.keys(eve_results) ) {
|
|
1644
|
+
if ( !(ky in params_def) ) {
|
|
1645
|
+
params_def[ky] = eve_results[ky]
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
return params_def
|
|
1650
|
+
}
|
|
1651
|
+
//
|
|
1652
|
+
return undefined
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
|
|
1656
|
+
/**
|
|
1657
|
+
* Handles the inclusion of recursive scripts call by skeletons and templates or s-templates
|
|
1658
|
+
* Some sub files may start with parameter definitions or element definitions
|
|
1659
|
+
* Others may have executables
|
|
1660
|
+
*
|
|
1661
|
+
* This method pulls out the definitions which must be associated with values, structures or basic values.
|
|
1662
|
+
* The final roster of executable, variables, parameter defs, list element defs, are returned in a structure
|
|
1663
|
+
* which will be used later during an evaluation phase.
|
|
1664
|
+
*
|
|
1665
|
+
* @param {string} step_entry
|
|
1666
|
+
* @param {string} data
|
|
1667
|
+
* @returns {object}
|
|
1668
|
+
*/
|
|
1669
|
+
async get_files_and_vars(step_entry,data) {
|
|
1670
|
+
//
|
|
1671
|
+
if ( data.indexOf('//') >= 0 ) {
|
|
1672
|
+
data = parse_util.clear_comments(data)
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
let params_def = false
|
|
1676
|
+
let carrier = {}
|
|
1677
|
+
let _is_loop = false
|
|
1678
|
+
|
|
1679
|
+
let lines = []
|
|
1680
|
+
if ( data.startsWith("@params<") ) { // will be at the top of a file (parameter defs for matching calling skeleton)
|
|
1681
|
+
//
|
|
1682
|
+
lines = data.split("\n")
|
|
1683
|
+
let first_line = lines.shift()
|
|
1684
|
+
first_line = parse_util.remove_spaces(first_line)
|
|
1685
|
+
let end_def = first_line.indexOf("}>")
|
|
1686
|
+
while ( (end_def < 0) && lines.length ) {
|
|
1687
|
+
let next_line = lines.shift()
|
|
1688
|
+
next_line = parse_util.remove_spaces(next_line)
|
|
1689
|
+
first_line += '\n' + next_line
|
|
1690
|
+
end_def = next_line.indexOf("}>")
|
|
1691
|
+
}
|
|
1692
|
+
//
|
|
1693
|
+
let flines = first_line.split('\n')
|
|
1694
|
+
first_line = flines.join("")
|
|
1695
|
+
|
|
1696
|
+
// @params<{lr_div:file,logout:file}>
|
|
1697
|
+
let var_defs = first_line.replace("@params<","")
|
|
1698
|
+
var_defs = var_defs.replace(">","")
|
|
1699
|
+
var_defs = var_defs.replace("{",'{\"')
|
|
1700
|
+
var_defs = var_defs.replace("}",'\"}')
|
|
1701
|
+
//
|
|
1702
|
+
let colon_split = var_defs.split(":")
|
|
1703
|
+
var_defs = colon_split.join('\":\"')
|
|
1704
|
+
let comma_split = var_defs.split(",")
|
|
1705
|
+
var_defs = comma_split.join('\",\"')
|
|
1706
|
+
//
|
|
1707
|
+
try {
|
|
1708
|
+
params_def = JSON.parse(var_defs)
|
|
1709
|
+
} catch(e) {}
|
|
1710
|
+
//
|
|
1711
|
+
} else if ( this.list_start(data,carrier) ) { // will be at the top of file for matching list element components (one at a time)
|
|
1712
|
+
//
|
|
1713
|
+
lines = data.split("\n")
|
|
1714
|
+
let first_line = lines.shift()
|
|
1715
|
+
first_line = parse_util.remove_spaces(first_line)
|
|
1716
|
+
let end_def = first_line.indexOf("}>")
|
|
1717
|
+
while ( (end_def < 0) && lines.length ) {
|
|
1718
|
+
let next_line = lines.shift()
|
|
1719
|
+
next_line = parse_util.remove_spaces(next_line)
|
|
1720
|
+
first_line += '\n' + next_line
|
|
1721
|
+
end_def = next_line.indexOf("}>")
|
|
1722
|
+
}
|
|
1723
|
+
let var_defs = first_line.substring(first_line.indexOf('<{') + 1,first_line.indexOf('}>') + 1)
|
|
1724
|
+
//
|
|
1725
|
+
var_defs = var_defs.replace("{",'{\"')
|
|
1726
|
+
var_defs = var_defs.replace("}",'\"}')
|
|
1727
|
+
//
|
|
1728
|
+
let colon_split = var_defs.split("<-")
|
|
1729
|
+
var_defs = colon_split.join('\":\"')
|
|
1730
|
+
let comma_split = var_defs.split(",")
|
|
1731
|
+
var_defs = comma_split.join('\",\"')
|
|
1732
|
+
try {
|
|
1733
|
+
params_def = JSON.parse(var_defs)
|
|
1734
|
+
} catch(e) {}
|
|
1735
|
+
//
|
|
1736
|
+
_is_loop = true
|
|
1737
|
+
params_def._var_name = carrier.var
|
|
1738
|
+
//
|
|
1739
|
+
} else {
|
|
1740
|
+
// no parameter or list defs, so just pull in any variables, schemes and the like
|
|
1741
|
+
// that may be needed to decide on final structure or explicit values
|
|
1742
|
+
// when a template is being generated
|
|
1743
|
+
let executables = this.extract_excecutables(data)
|
|
1744
|
+
params_def = this.errant_variable_extraction(params_def,data)
|
|
1745
|
+
return { _is_loop, params_def, executables }
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
// parameters or loop assignments have been found, so
|
|
1749
|
+
// finish the collection of variables and possible executable operations.
|
|
1750
|
+
params_def = this.errant_variable_extraction(params_def,data)
|
|
1751
|
+
//
|
|
1752
|
+
let executables = []
|
|
1753
|
+
if ( lines.length ) {
|
|
1754
|
+
let rest = lines.join('\n')
|
|
1755
|
+
executables = this.extract_excecutables(rest)
|
|
1756
|
+
}
|
|
1757
|
+
//
|
|
1758
|
+
return { _is_loop, params_def, executables }
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
|
|
1762
|
+
|
|
1763
|
+
/**
|
|
1764
|
+
* A ternary conditional is a type of executable
|
|
1765
|
+
* with a means for picking a particular path of construction.
|
|
1766
|
+
* This method creates the structure that evaluation may use
|
|
1767
|
+
* to yield a particular structure given the setting of
|
|
1768
|
+
* configuration variables.
|
|
1769
|
+
* @param {string} parseable
|
|
1770
|
+
* @param {object} exec_report
|
|
1771
|
+
* @returns {boolean}
|
|
1772
|
+
*/
|
|
1773
|
+
ternary_conditional(parseable,exec_report) {
|
|
1774
|
+
|
|
1775
|
+
let ternary = this.ternary_check.exec(parseable)
|
|
1776
|
+
if ( ternary ) {
|
|
1777
|
+
let cond = ternary[1].trim()
|
|
1778
|
+
//
|
|
1779
|
+
let first = ""
|
|
1780
|
+
let second = ""
|
|
1781
|
+
let third = ""
|
|
1782
|
+
//
|
|
1783
|
+
if ( parseable.indexOf("::") < 0 ) {
|
|
1784
|
+
first = ternary[2]
|
|
1785
|
+
second = ternary[3]
|
|
1786
|
+
third = false
|
|
1787
|
+
} else {
|
|
1788
|
+
let prest = parseable.substring(parseable.indexOf('?') + 1)
|
|
1789
|
+
prest = prest.split('::')
|
|
1790
|
+
//
|
|
1791
|
+
first = prest.shift()
|
|
1792
|
+
second = prest.shift()
|
|
1793
|
+
third = prest.shift()
|
|
1794
|
+
//
|
|
1795
|
+
if ( first.indexOf(':') > 0 ) {
|
|
1796
|
+
let fparts = first.split(':')
|
|
1797
|
+
first = fparts[0]
|
|
1798
|
+
second = fparts[1] + "::" + second
|
|
1799
|
+
if ( third ) {
|
|
1800
|
+
second = second + "::" + third
|
|
1801
|
+
}
|
|
1802
|
+
} else if ( second && (second.indexOf(':') >= 0) ) {
|
|
1803
|
+
let sparts = second.split(':')
|
|
1804
|
+
first = first + "::" + sparts[0]
|
|
1805
|
+
second = sparts[1]
|
|
1806
|
+
if ( third ) {
|
|
1807
|
+
second = second + "::" + third
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
//
|
|
1812
|
+
let pos = first.trim()
|
|
1813
|
+
let neg = second.trim()
|
|
1814
|
+
|
|
1815
|
+
if ( pos === "@nothing" ) pos = ""
|
|
1816
|
+
if ( neg === "@nothing" ) neg = ""
|
|
1817
|
+
|
|
1818
|
+
let variable = parse_util.extract_var(cond)
|
|
1819
|
+
|
|
1820
|
+
exec_report.condition = {cond,variable}
|
|
1821
|
+
exec_report.positive_exec = {pos}
|
|
1822
|
+
exec_report.negative_exec = {neg}
|
|
1823
|
+
//
|
|
1824
|
+
return true
|
|
1825
|
+
}
|
|
1826
|
+
return false
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
|
|
1830
|
+
|
|
1831
|
+
|
|
1832
|
+
/**
|
|
1833
|
+
*
|
|
1834
|
+
* @param {string} parseable
|
|
1835
|
+
* @returns
|
|
1836
|
+
*/
|
|
1837
|
+
parse_executable(parseable) {
|
|
1838
|
+
//
|
|
1839
|
+
//console.log("parse_executable",parseable)
|
|
1840
|
+
let exec = {
|
|
1841
|
+
"replace" : parseable,
|
|
1842
|
+
"condition" : true,
|
|
1843
|
+
"positive_exec" : parseable.trim(),
|
|
1844
|
+
"negative_exec" : "",
|
|
1845
|
+
}
|
|
1846
|
+
parseable = parseable.replace(">>","").trim()
|
|
1847
|
+
parseable = parseable.substring(0,parseable.lastIndexOf("<<")).trim()
|
|
1848
|
+
if ( this.ternary_conditional(parseable,exec) ) {
|
|
1849
|
+
//
|
|
1850
|
+
let an_import = this.seek_imports(exec.positive_exec.pos)
|
|
1851
|
+
if ( an_import ) {
|
|
1852
|
+
exec.positive_exec.replace = exec.positive_exec.neg
|
|
1853
|
+
exec.positive_exec.file = an_import
|
|
1854
|
+
}
|
|
1855
|
+
an_import = this.seek_imports(exec.negative_exec.neg)
|
|
1856
|
+
if ( an_import ) {
|
|
1857
|
+
exec.negative_exec.replace = exec.negative_exec.neg
|
|
1858
|
+
exec.negative_exec.file = an_import
|
|
1859
|
+
}
|
|
1860
|
+
//
|
|
1861
|
+
}
|
|
1862
|
+
return exec
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
|
|
1866
|
+
/**
|
|
1867
|
+
* seek_imports
|
|
1868
|
+
*
|
|
1869
|
+
* @param {string} maybe_imports
|
|
1870
|
+
* @returns {Array | boolean}
|
|
1871
|
+
*/
|
|
1872
|
+
seek_imports(maybe_imports) {
|
|
1873
|
+
let type = this.check_recursive_data(maybe_imports)
|
|
1874
|
+
let rest = maybe_imports
|
|
1875
|
+
//
|
|
1876
|
+
let imports = []
|
|
1877
|
+
let i = 0
|
|
1878
|
+
while ( (type >= 1) && (type <= 3) && rest.length ) {
|
|
1879
|
+
let entry_loc = rest.indexOf('$$')
|
|
1880
|
+
if ( entry_loc < 0 ) break;
|
|
1881
|
+
//
|
|
1882
|
+
let replacer = rest.indexOf("<<") >= 0 ? rest.substring(entry_loc,rest.indexOf("<<") + 2) : undefined
|
|
1883
|
+
let step_entry = rest.substring(entry_loc + 2)
|
|
1884
|
+
rest = rest.substring(entry_loc + 2 + step_entry.indexOf("::"))
|
|
1885
|
+
|
|
1886
|
+
if ( step_entry.length ) {
|
|
1887
|
+
if ( step_entry.indexOf("<<") > 0 ) {
|
|
1888
|
+
step_entry = step_entry.substring(0,step_entry.indexOf("<<"))
|
|
1889
|
+
rest = rest.substring(rest.indexOf("<<")+2)
|
|
1890
|
+
}
|
|
1891
|
+
//
|
|
1892
|
+
let [file_name, path_finder, entry_type] = this.get_file_data_descriptor(step_entry)
|
|
1893
|
+
//
|
|
1894
|
+
let map_value = {
|
|
1895
|
+
"replace" : replacer,
|
|
1896
|
+
"type" : entry_type,
|
|
1897
|
+
"file" : file_name,
|
|
1898
|
+
"path_finder" : path_finder,
|
|
1899
|
+
"data" : false
|
|
1900
|
+
}
|
|
1901
|
+
//
|
|
1902
|
+
imports.push(map_value)
|
|
1903
|
+
this.delay_file_loading_queue.push(map_value)
|
|
1904
|
+
}
|
|
1905
|
+
if ( rest.indexOf('<<') > 0 ) {
|
|
1906
|
+
type = this.check_recursive_data(rest)
|
|
1907
|
+
} else break
|
|
1908
|
+
}
|
|
1909
|
+
if ( imports.length ) return imports
|
|
1910
|
+
return false
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
/**
|
|
1914
|
+
* extract_excecutables
|
|
1915
|
+
*
|
|
1916
|
+
* builds up the lists of executables and imports that are called out in a file.
|
|
1917
|
+
*
|
|
1918
|
+
*
|
|
1919
|
+
* @param {string} data_form
|
|
1920
|
+
* @returns {object}
|
|
1921
|
+
*/
|
|
1922
|
+
extract_excecutables(data_form) {
|
|
1923
|
+
//
|
|
1924
|
+
let execs = []
|
|
1925
|
+
let imports = []
|
|
1926
|
+
if ( data_form.indexOf('>>') >= 0 ) {
|
|
1927
|
+
let parts = data_form.split('>>')
|
|
1928
|
+
for ( let i = 1; i < parts.length; i++ ) {
|
|
1929
|
+
let p = parts[i]
|
|
1930
|
+
let skip = 2
|
|
1931
|
+
let reattach_end = "<<"
|
|
1932
|
+
if ( p.indexOf("<<<<") > 0 ) { // executable with import
|
|
1933
|
+
p = p.substring(0,p.indexOf("<<<<"))
|
|
1934
|
+
reattach_end = "<<<<"
|
|
1935
|
+
skip = 4
|
|
1936
|
+
} else {
|
|
1937
|
+
p = p.substring(0,p.indexOf("<<"))
|
|
1938
|
+
}
|
|
1939
|
+
parts[i] = p.substring(p.indexOf("<<") + skip)
|
|
1940
|
+
p = this.parse_executable(">>" + p + reattach_end)
|
|
1941
|
+
execs.push(p)
|
|
1942
|
+
}
|
|
1943
|
+
for ( let i = 1; i < parts.length; i++ ) {
|
|
1944
|
+
let an_import = this.seek_imports(parts[i])
|
|
1945
|
+
if ( an_import ) {
|
|
1946
|
+
imports = imports.concat(an_import)
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
} else {
|
|
1950
|
+
imports = this.seek_imports(data_form)
|
|
1951
|
+
}
|
|
1952
|
+
return {execs,imports}
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
//
|
|
1956
|
+
// case 1:
|
|
1957
|
+
// @params<{lr_div:file,logout:file}>
|
|
1958
|
+
//
|
|
1959
|
+
// case 2:
|
|
1960
|
+
// @list<el><{ group_name <- el[1], SOURCE-LINK <- el[2], FRAME-ACTIONS <- el[3]}>
|
|
1961
|
+
// ...
|
|
1962
|
+
// <@el>
|
|
1963
|
+
// ...
|
|
1964
|
+
// </@el>
|
|
1965
|
+
//
|
|
1966
|
+
// case 3:
|
|
1967
|
+
// >>@{FRAME-ACTIONS} ? @{FRAME-ACTIONS} : @nothing <<
|
|
1968
|
+
//
|
|
1969
|
+
// case 4:
|
|
1970
|
+
// @{group_name}
|
|
1971
|
+
//
|
|
1972
|
+
// case 5:
|
|
1973
|
+
// >> any action at all <<
|
|
1974
|
+
// EXAMPLE: >> @p = 2 + 4; put @p here; put @p after next div; <<
|
|
1975
|
+
// EXAMPLE: >> @p = 2 + 4; @bubble = @p; << //late @bubble appears in the html (text)
|
|
1976
|
+
//
|
|
1977
|
+
// case 6:
|
|
1978
|
+
// $$icons::mushroom-menu-icon.svg
|
|
1979
|
+
// OR $$`path-finder`::`file-stem`.`ext`
|
|
1980
|
+
//
|
|
1981
|
+
|
|
1982
|
+
|
|
1983
|
+
is_language_section_control(step_entry) {
|
|
1984
|
+
return false
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
/**
|
|
1988
|
+
* // generalization for later
|
|
1989
|
+
* @param {string} step_entry
|
|
1990
|
+
* @returns {string}
|
|
1991
|
+
*/
|
|
1992
|
+
extract_lang_controller_key(step_entry) {
|
|
1993
|
+
return step_entry.substring(0,step_entry.indexOf(':'))
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
|
|
1997
|
+
|
|
1998
|
+
/**
|
|
1999
|
+
* has_calc(entry_data)
|
|
2000
|
+
*/
|
|
2001
|
+
|
|
2002
|
+
has_calc(entry_data) {
|
|
2003
|
+
//
|
|
2004
|
+
if ( entry_data?.key_values ) {
|
|
2005
|
+
//
|
|
2006
|
+
for ( let fcall of Object.values(entry_data.key_values) ) {
|
|
2007
|
+
if ( (fcall === "f@incr{$}") || (fcall === "f@init{$}") ) {
|
|
2008
|
+
continue
|
|
2009
|
+
}
|
|
2010
|
+
let parse_call = this.basic_function_call_match.exec(fcall)
|
|
2011
|
+
if ( parse_call ) {
|
|
2012
|
+
return true
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
//
|
|
2016
|
+
} else {
|
|
2017
|
+
// console.log("has_calc")
|
|
2018
|
+
// console.dir(entry_data)
|
|
2019
|
+
}
|
|
2020
|
+
//
|
|
2021
|
+
return false
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
|
|
2025
|
+
/**
|
|
2026
|
+
*
|
|
2027
|
+
* @param {object} entry_data
|
|
2028
|
+
* @returns {Array}
|
|
2029
|
+
*/
|
|
2030
|
+
gather_calculations(entry_data) {
|
|
2031
|
+
let all_calls = []
|
|
2032
|
+
for ( let [vname,fcall] of Object.entries(entry_data.key_values) ) {
|
|
2033
|
+
if ( (fcall === "f@incr{$}") || (fcall === "f@init{$}") ) {
|
|
2034
|
+
continue
|
|
2035
|
+
}
|
|
2036
|
+
let parse_call = this.basic_function_call_match.exec(fcall)
|
|
2037
|
+
if ( parse_call ) {
|
|
2038
|
+
let one_call = {}
|
|
2039
|
+
one_call.set_var = vname
|
|
2040
|
+
one_call.func = parse_call[1]
|
|
2041
|
+
one_call.param = parse_call[2]
|
|
2042
|
+
//
|
|
2043
|
+
all_calls.push(one_call)
|
|
2044
|
+
} else {
|
|
2045
|
+
let one_call = {}
|
|
2046
|
+
one_call.set_var = vname
|
|
2047
|
+
one_call.func = "copy"
|
|
2048
|
+
one_call.param = fcall // actually just a string
|
|
2049
|
+
//
|
|
2050
|
+
all_calls.push(one_call)
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
return all_calls
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
|
|
2057
|
+
/**
|
|
2058
|
+
*
|
|
2059
|
+
* @param {object} entry_data
|
|
2060
|
+
* @param {string} fcall
|
|
2061
|
+
* @param {string} caller_stem
|
|
2062
|
+
*/
|
|
2063
|
+
make_call(entry_data,fcall,caller_stem) {
|
|
2064
|
+
let func = fcall.func
|
|
2065
|
+
if ( (typeof entry_data.evaluations) !== "object" ) {
|
|
2066
|
+
entry_data.evaluations = {}
|
|
2067
|
+
}
|
|
2068
|
+
switch ( func ) {
|
|
2069
|
+
case "name" : {
|
|
2070
|
+
let target = fcall.param
|
|
2071
|
+
if ( target === "parent" ) {
|
|
2072
|
+
entry_data.evaluations[fcall.set_var] = caller_stem
|
|
2073
|
+
}
|
|
2074
|
+
break;
|
|
2075
|
+
}
|
|
2076
|
+
case "copy" : {
|
|
2077
|
+
entry_data.evaluations[fcall.set_var] = fcall.param
|
|
2078
|
+
break
|
|
2079
|
+
}
|
|
2080
|
+
default: {
|
|
2081
|
+
break
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
|
|
2087
|
+
|
|
2088
|
+
/**
|
|
2089
|
+
*
|
|
2090
|
+
* @param {object} entry_data
|
|
2091
|
+
* @returns {boolean}
|
|
2092
|
+
*/
|
|
2093
|
+
has_incrementer(entry_data) {
|
|
2094
|
+
//
|
|
2095
|
+
if ( typeof entry_data.key_values === "object" ) {
|
|
2096
|
+
let values = Object.values(entry_data.key_values)
|
|
2097
|
+
for ( let val of values ) {
|
|
2098
|
+
if ( (val === "f@incr{$}") || (val === "f@init{$}") ) {
|
|
2099
|
+
return true
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
//
|
|
2104
|
+
return false
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
|
|
2108
|
+
/**
|
|
2109
|
+
*
|
|
2110
|
+
* @param {object} incrementer_set
|
|
2111
|
+
* @param {string} step_entry
|
|
2112
|
+
* @param {object} entry_data
|
|
2113
|
+
*/
|
|
2114
|
+
add_to_incrementers(incrementer_set,entry_data,var_set_expr) {
|
|
2115
|
+
//
|
|
2116
|
+
let incr_vname = false
|
|
2117
|
+
let start_val = false
|
|
2118
|
+
let op = ""
|
|
2119
|
+
if ( var_set_expr.length ) {
|
|
2120
|
+
let unloader = this.var_set_expr_pattern.exec(var_set_expr)
|
|
2121
|
+
if ( unloader ) {
|
|
2122
|
+
let vexpr = unloader[1]
|
|
2123
|
+
if ( vexpr.indexOf("=") > 0 ) {
|
|
2124
|
+
let pieces = vexpr.split('=')
|
|
2125
|
+
incr_vname = pieces[0]
|
|
2126
|
+
start_val = parseInt(pieces[1].trim())
|
|
2127
|
+
op = '='
|
|
2128
|
+
} else if ( vexpr.indexOf("++") > 0 ) {
|
|
2129
|
+
incr_vname = vexpr.substring(0,vexpr.indexOf("++"))
|
|
2130
|
+
op = "++"
|
|
2131
|
+
} else if ( vexpr.indexOf("--") > 0 ) {
|
|
2132
|
+
incr_vname = vexpr.substring(0,vexpr.indexOf("--"))
|
|
2133
|
+
op = "--"
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
if ( (typeof entry_data === "object") && (typeof entry_data.key_values === "object") ) {
|
|
2138
|
+
for ( let [vname, form] of Object.entries(entry_data.key_values) ) {
|
|
2139
|
+
if ( (form === "f@incr{$}") || (form === "f@init{$}") ) {
|
|
2140
|
+
let ky_vname = incr_vname ? incr_vname : vname
|
|
2141
|
+
entry_data.op = op
|
|
2142
|
+
if ( ky_vname in incrementer_set ) {
|
|
2143
|
+
let incr_descr = incrementer_set[ky_vname]
|
|
2144
|
+
if ( form === "f@init{$}" ) {
|
|
2145
|
+
incr_descr.starter = entry_data
|
|
2146
|
+
incr_descr.start_val = start_val
|
|
2147
|
+
} else {
|
|
2148
|
+
incr_descr.list.push(entry_data)
|
|
2149
|
+
}
|
|
2150
|
+
} else {
|
|
2151
|
+
incrementer_set[ky_vname] = {
|
|
2152
|
+
"print_vname" : ky_vname,
|
|
2153
|
+
"apply_to" : vname,
|
|
2154
|
+
"start_val" : start_val,
|
|
2155
|
+
"starter" : entry_data,
|
|
2156
|
+
"list" : [entry_data]
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
return true
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
return false
|
|
2164
|
+
//
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
|
|
2168
|
+
/**
|
|
2169
|
+
*
|
|
2170
|
+
* @param {string} sk_key
|
|
2171
|
+
* @param {string} step_entry
|
|
2172
|
+
*/
|
|
2173
|
+
add_tracking_for_calc(sk_key,step_entry) {
|
|
2174
|
+
let track_ky = step_entry.substring(step_entry.lastIndexOf(':') + 1,step_entry.indexOf("${"))
|
|
2175
|
+
let calc_entries = this.tracking_skel_calc_usage[sk_key]
|
|
2176
|
+
if ( !calc_entries ) {
|
|
2177
|
+
calc_entries = {}
|
|
2178
|
+
this.tracking_skel_calc_usage[sk_key] = calc_entries
|
|
2179
|
+
calc_entries[track_ky] = 0
|
|
2180
|
+
}
|
|
2181
|
+
if ( calc_entries[track_ky] === undefined ) {
|
|
2182
|
+
calc_entries[track_ky] = 0
|
|
2183
|
+
}
|
|
2184
|
+
calc_entries[track_ky]++
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
|
|
2188
|
+
/**
|
|
2189
|
+
{
|
|
2190
|
+
$@{scroll_section_count}$::@#{groups}
|
|
2191
|
+
}
|
|
2192
|
+
*
|
|
2193
|
+
* @param {string} params
|
|
2194
|
+
*/
|
|
2195
|
+
evaluation_map(params) {
|
|
2196
|
+
params = params.substring(params.indexOf('{')+1,params.lastIndexOf('}')).trim()
|
|
2197
|
+
let parlist = params.split('\n').map( el => el.trim() )
|
|
2198
|
+
parlist = parlist.map( (el) => {
|
|
2199
|
+
return el.split('<<')
|
|
2200
|
+
})
|
|
2201
|
+
let evals = {}
|
|
2202
|
+
for ( let ev_pair of parlist ) {
|
|
2203
|
+
let [ky_form, val] = ev_pair
|
|
2204
|
+
let ky = ky_form.replace('$@{',"")
|
|
2205
|
+
ky = ky.replace('}$',"")
|
|
2206
|
+
if ( (val.indexOf('@!{') === 0) || (val.indexOf('@#{') === 0) ) { val = '0' }
|
|
2207
|
+
evals[ky] = val
|
|
2208
|
+
}
|
|
2209
|
+
return evals
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
|
|
2213
|
+
/**
|
|
2214
|
+
*
|
|
2215
|
+
* @param {object} script_spec
|
|
2216
|
+
*/
|
|
2217
|
+
early_evaluations(script_spec) {
|
|
2218
|
+
// @params<{scroll_section_count : %number%}>
|
|
2219
|
+
let data = script_spec.data
|
|
2220
|
+
|
|
2221
|
+
data = data.substring(data.indexOf("@params<{") + "@params<{".length)
|
|
2222
|
+
data = data.substring(data.indexOf('}>') + 2)
|
|
2223
|
+
script_spec.data = data.trim()
|
|
2224
|
+
|
|
2225
|
+
this.conditionless_evaluations_substitution(script_spec)
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
|
|
2229
|
+
/**
|
|
2230
|
+
* Builds the data structures describing the entry
|
|
2231
|
+
* updates sk_map, where data stuctures are stored for a particular skeleton.
|
|
2232
|
+
*
|
|
2233
|
+
* This method parses a skeleton file, creating as a result a data structure that captures the intent of
|
|
2234
|
+
* structural defition which may be some sort of markup (e.g. HTML). The output of this method may be
|
|
2235
|
+
* used subsequently by methods that prepare a template in the markup language.
|
|
2236
|
+
*
|
|
2237
|
+
* Need to add $$bundle section
|
|
2238
|
+
*
|
|
2239
|
+
* A skeleton file is broken down into sections. Each section is demarcated by a start of section token
|
|
2240
|
+
* which indicates section type as well as some parameters that may go into the calculation of the section
|
|
2241
|
+
* structure.
|
|
2242
|
+
*
|
|
2243
|
+
* A section starts with syntactic separator `$$`.
|
|
2244
|
+
* The '$$' symbol must be followed by a type directive, which is then followed by one or two ':' (colons).
|
|
2245
|
+
* One colon indicates a named element of the chosen markup language which will be substituted by the tool.
|
|
2246
|
+
* Two colons indicates that the type directive and information in the section will be part of some calculation
|
|
2247
|
+
* specified by the section that is used to generate the final template markup. In many cases, the calculation
|
|
2248
|
+
* indicates that a file should be loaded, where the file is a pre-template which will structurally expanded
|
|
2249
|
+
* according to the section parameters.
|
|
2250
|
+
*
|
|
2251
|
+
* Following colons are further speciations of the section indicating parametarization and actions.
|
|
2252
|
+
* Each of these has a type name and may be followed by colons as well. The final specifier must be followed by
|
|
2253
|
+
* the syntactic indicator `<<`. This is an end of directive indicator. This line terminator may be followed by
|
|
2254
|
+
* more information encapsulated within braces '{','}'
|
|
2255
|
+
*
|
|
2256
|
+
* The information in braces may specify calculations, parameter substitutions, or loop calculations.
|
|
2257
|
+
*
|
|
2258
|
+
* Information after the final top level brace '}' and the start of the next section '$$' is likely to be ignored
|
|
2259
|
+
* or treated as comments.
|
|
2260
|
+
*
|
|
2261
|
+
*
|
|
2262
|
+
* Adds fields skeleton_map and incrementer_set
|
|
2263
|
+
*
|
|
2264
|
+
*
|
|
2265
|
+
* Here are the type of sections supported by the program to date:
|
|
2266
|
+
*
|
|
2267
|
+
* $$defs:: -- These are variable definitions and overrides relative to the configuration for the run
|
|
2268
|
+
* $$bundle:: -- One of these indicates a bundled script that needs to be included in the header as a deferred file.
|
|
2269
|
+
* $$html: -- These are HTML markups indicating start of larger sections such as hearders, bodies, scripts, style, etc.
|
|
2270
|
+
* $$css:: -- The file name following this is a css file stored in the css directory of the alphas
|
|
2271
|
+
* $$files:: -- These are files to be found in the directory for the markup, e.g. html for HTML
|
|
2272
|
+
* $$files<*>:: -- These are files of a different type than the markup by in the same directory. For instance '*' may be replace with 'js'
|
|
2273
|
+
* $$script:: -- These are files in the scripting language. Files that remain in a skeleton after preprocessing for bundling will be written into the template.
|
|
2274
|
+
* $$verbatim:: -- A section that will be left alone and included precisely as is
|
|
2275
|
+
* $$template:: -- Similar to verbatim, but it expected to contain variables for use in substitutions, some of which may be files loaded recursively
|
|
2276
|
+
* $$icon::
|
|
2277
|
+
*
|
|
2278
|
+
* The $$files:: section may either be a simple directive indicating a file to put into the place of the file, or it may be
|
|
2279
|
+
* a directive indicating how a file will be used:
|
|
2280
|
+
*
|
|
2281
|
+
* $$files::|file path|<<
|
|
2282
|
+
* $$files::params::|file path|<<{|parameter descriptor|}
|
|
2283
|
+
*
|
|
2284
|
+
* $$files::calc::|file path|<<${|calculation code|}
|
|
2285
|
+
* $$files::calc::|file path|<<${|calculation code|}
|
|
2286
|
+
*
|
|
2287
|
+
* $$files::calc::contact_box<<${box_i=100} -- this is an example of a calculation that initializes a variable
|
|
2288
|
+
* $$files::calc::another_box<<${box_i++} -- this is an example of a calculation that advances a variable
|
|
2289
|
+
*
|
|
2290
|
+
* $$files::loop:: -- Loops cause sections of markup to be generated in number, sequentially and may be iterates of a loop index of lists of elements
|
|
2291
|
+
*
|
|
2292
|
+
* $$files::loop::elements::|file path with an element indicator, e.g. '<el>' |<<${|an expression itemizing loop elements|}
|
|
2293
|
+
* $$files::loop::calc::|file path with an index range, e.g. '<start,stop>'|><<${ a calculation advancing the loop index }
|
|
2294
|
+
*
|
|
2295
|
+
* $$javascript:start_worker<<
|
|
2296
|
+
* $$javascript:end_worker<<
|
|
2297
|
+
*
|
|
2298
|
+
* // the following is a `skel_def` ::
|
|
2299
|
+
* `{
|
|
2300
|
+
"defaults" : def_defaults,
|
|
2301
|
+
"final" : {
|
|
2302
|
+
"markup" : false,
|
|
2303
|
+
"scripts" : false
|
|
2304
|
+
},
|
|
2305
|
+
"skeleton" : data_parts,
|
|
2306
|
+
"files" : files,
|
|
2307
|
+
"scripts" : scripts,
|
|
2308
|
+
"bundles" : bundles,
|
|
2309
|
+
"parameterized" : maybe_params
|
|
2310
|
+
}`
|
|
2311
|
+
*
|
|
2312
|
+
* @param {object} skeleton
|
|
2313
|
+
* @param {object} sk_map
|
|
2314
|
+
* @param {object} incrementer_set
|
|
2315
|
+
*/
|
|
2316
|
+
async build_entry_parsing(sk_key,skeleton,skeleton_src,sk_map,incrementer_set,concern_rewrites) {
|
|
2317
|
+
let lang_spec_count = 0
|
|
2318
|
+
for ( let step_entry of skeleton ) {
|
|
2319
|
+
step_entry = step_entry.replace('<<','')
|
|
2320
|
+
// now get its value depending on its tyle
|
|
2321
|
+
// HTML
|
|
2322
|
+
if ( step_entry.startsWith('html:') ) { // handle the import of html snippets that start parts of the file
|
|
2323
|
+
lang_spec_count++
|
|
2324
|
+
let html_map = base_patterns_mod['html:']
|
|
2325
|
+
let ky = step_entry.substring('html:'.length)
|
|
2326
|
+
step_entry = step_entry.replace('html:',`html(${lang_spec_count}):`)
|
|
2327
|
+
sk_map[step_entry] = html_map[ky]
|
|
2328
|
+
// OTHER THAN HTML
|
|
2329
|
+
} else if ( this.is_language_section_control(step_entry) ) { // a generalization of html: for later
|
|
2330
|
+
lang_spec_count++
|
|
2331
|
+
let lang_key = this.extract_lang_controller_key(step_entry)
|
|
2332
|
+
let lang_map = base_patterns_mod[lang_key]
|
|
2333
|
+
let ky = step_entry.substring(lang_key.length)
|
|
2334
|
+
step_entry = step_entry.replace(lang_key,`${lang_key}(${lang_spec_count}):`)
|
|
2335
|
+
sk_map[step_entry] = lang_map[ky]
|
|
2336
|
+
// BUNDLE - a link definition (CDN connection perhaps)
|
|
2337
|
+
} else if ( step_entry.startsWith('bundle::') ) { // a bundled file -- used to generate the <link deferred... construct
|
|
2338
|
+
let str = step_entry.substring(('bundle::').length)
|
|
2339
|
+
let hashed = crypto.hash('sha1',str) // cryp]==to
|
|
2340
|
+
sk_map[`bundle::${hashed}`] = bundle_inclusion_transform(str,page_or_worker_context[skeleton_src])
|
|
2341
|
+
// LINK - a link definition (possibly with compression)
|
|
2342
|
+
} else if ( step_entry.startsWith('link<') ) { // a bundled file -- used to generate the <link deferred... construct
|
|
2343
|
+
let str = step_entry.substring(('link').length)
|
|
2344
|
+
let hashed = crypto.hash('sha1',str) // cryp]==to
|
|
2345
|
+
sk_map[`link::${hashed}`] = link_inclusion_transform(str,page_or_worker_context[skeleton_src])
|
|
2346
|
+
// JAVASCRIPT -- sepcial directives for JavaScript in special modules, workers, etc.
|
|
2347
|
+
} else if ( step_entry.startsWith('javascript:')) {
|
|
2348
|
+
let str = step_entry.substring(('bundle::').length)
|
|
2349
|
+
sk_map[str] = "" // for now they just disappear
|
|
2350
|
+
// VERBATIM -- text does not change
|
|
2351
|
+
} else if ( step_entry.startsWith('verbatim::') ) { // parts of the file to leave alone and include in the final
|
|
2352
|
+
let str = step_entry.substring(('verbatim::').length)
|
|
2353
|
+
let hashed = crypto.hash('sha1',str) // crypto
|
|
2354
|
+
sk_map[`verbatim::${hashed}`] = this.clean_verbatim(str)
|
|
2355
|
+
// ELSE
|
|
2356
|
+
} else {
|
|
2357
|
+
let entry = this.shared_entries[step_entry]
|
|
2358
|
+
if ( entry && typeof entry === "object" ) { // previously created informaton -- just copy it
|
|
2359
|
+
sk_map[step_entry] = structuredClone(entry)
|
|
2360
|
+
} else {
|
|
2361
|
+
// FILES with operations -- resulting in targeted markdown in template files
|
|
2362
|
+
if ( step_entry.startsWith('files::') || step_entry.startsWith('files<') ||step_entry.startsWith('css::') ) {
|
|
2363
|
+
// FILES::CALC - custom calculation for file use
|
|
2364
|
+
if ( step_entry.startsWith('files::calc::') ) {
|
|
2365
|
+
//
|
|
2366
|
+
sk_map[step_entry] = "name" // the default is to indicate that the entry names a type of section
|
|
2367
|
+
//
|
|
2368
|
+
if ( step_entry.indexOf("_<") < 0 ) { // looking for a specific syntax associated with a file name
|
|
2369
|
+
// this syntax tells processing that a range will be used in replicating a region
|
|
2370
|
+
let var_set_expr = ""
|
|
2371
|
+
let db = this.name_drops_db // handle the syntax (specific to this rep here) for variable management
|
|
2372
|
+
let db_ky = step_entry.substring("files::calc::".length)
|
|
2373
|
+
if ( db_ky.indexOf("$") > 0 ) { // says that there is an incrementer variable perhaps previously defined
|
|
2374
|
+
let db_ky_parts = db_ky.split("$")
|
|
2375
|
+
db_ky = db_ky_parts[0] // expose the key into the name_drop db
|
|
2376
|
+
var_set_expr = db_ky_parts[1] // get the variable update expressions
|
|
2377
|
+
}
|
|
2378
|
+
let entry_data = db[db_ky] // the db stores operational characteristics of named skeletal parts
|
|
2379
|
+
let back_ref_ky = db_ky
|
|
2380
|
+
if ( typeof entry_data === "object" ) { // In the db the key may be vanilla like the one in use
|
|
2381
|
+
sk_map[step_entry] = Object.assign({},entry_data)
|
|
2382
|
+
} else {
|
|
2383
|
+
back_ref_ky = db_ky // otherwise, it may indicate an abstraction of the range.
|
|
2384
|
+
db_ky = db_ky.substring(0,db_ky.lastIndexOf('_') + 1)
|
|
2385
|
+
entry_data = db[db_ky]
|
|
2386
|
+
sk_map[step_entry] = Object.assign({},entry_data)
|
|
2387
|
+
}
|
|
2388
|
+
// add a tracker for this calculator, which may be used in other steps
|
|
2389
|
+
this.add_tracking_for_calc(sk_key,step_entry)
|
|
2390
|
+
//
|
|
2391
|
+
entry_data = sk_map[step_entry]
|
|
2392
|
+
if ( entry_data.file ) { // delay loading the file ... the field "file" will likely refer to a '.tmplt' file.
|
|
2393
|
+
entry_data.path_finder = "html" // add this field, it is the path to a directoy in alpha-copious
|
|
2394
|
+
this.delay_file_loading_queue.push(entry_data)
|
|
2395
|
+
}
|
|
2396
|
+
// makes a link between the structure generation operations and incrementer operations
|
|
2397
|
+
if ( this.has_incrementer(entry_data) ) { // keep structures of incrementers for updates
|
|
2398
|
+
this.add_to_incrementers(incrementer_set,entry_data,var_set_expr)
|
|
2399
|
+
}
|
|
2400
|
+
// calculations taken from the DB provide delay_file_loadingoperations more complex than increment
|
|
2401
|
+
if ( this.has_calc(entry_data) ) {
|
|
2402
|
+
let calls = this.gather_calculations(entry_data) // gets parameters and op components together
|
|
2403
|
+
for ( let a_call of calls ) { // for this skeletal step make all the calls gathered -- uses local data
|
|
2404
|
+
this.make_call(entry_data,a_call,back_ref_ky)
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
//
|
|
2408
|
+
}
|
|
2409
|
+
//
|
|
2410
|
+
//
|
|
2411
|
+
// FILES::PARAMS - load files taking parameters
|
|
2412
|
+
// CSS::PARAMS
|
|
2413
|
+
} else if ( step_entry.startsWith('files::params::') || step_entry.startsWith('css::params::') ) {
|
|
2414
|
+
let loadable_entry = step_entry.replace("::params","")
|
|
2415
|
+
sk_map[step_entry] = await this.entry_loading(loadable_entry)
|
|
2416
|
+
// console.log(step_entry)
|
|
2417
|
+
// console.dir(sk_map[step_entry],{ depth : 6})
|
|
2418
|
+
// FILES::ELEMENTS - files operating on lists of elements
|
|
2419
|
+
// CSS::ELEMENTS
|
|
2420
|
+
} else if ( step_entry.startsWith('files::elements::') || step_entry.startsWith('css::elements::') ) {
|
|
2421
|
+
let loadable_entry = step_entry.replace("::elements","")
|
|
2422
|
+
sk_map[step_entry] = await this.entry_loading(loadable_entry)
|
|
2423
|
+
// FILES
|
|
2424
|
+
// CSS -- nothing special = copy and paste
|
|
2425
|
+
} else {
|
|
2426
|
+
// console.log("CALLING ENTRY LOADING",step_entry)
|
|
2427
|
+
sk_map[step_entry] = await this.entry_loading(step_entry)
|
|
2428
|
+
// console.log("DONE CALL -- ENTRY LOADING",step_entry)
|
|
2429
|
+
}
|
|
2430
|
+
// TEMPLATE
|
|
2431
|
+
} else if ( step_entry.startsWith('template::') ) {
|
|
2432
|
+
// similar to verbatim in that the script won't be changed much.
|
|
2433
|
+
// however, templates may be loaded recursively, resulting in a tree for future assembly
|
|
2434
|
+
let data = step_entry.substring(('template::').length)
|
|
2435
|
+
data = data.trim()
|
|
2436
|
+
let brace_i = data.indexOf('{')
|
|
2437
|
+
let brace_n = data.lastIndexOf('}')
|
|
2438
|
+
data = data.substring(brace_i,brace_n-1)
|
|
2439
|
+
data = parse_util.clear_comments(data) // take out this human readable info that interferes with machine processing
|
|
2440
|
+
//
|
|
2441
|
+
sk_map[step_entry] = {
|
|
2442
|
+
"type" : "template",
|
|
2443
|
+
"data" : data
|
|
2444
|
+
}
|
|
2445
|
+
if ( this.check_recursive_data(data) ) { // handle recursion -- build tree
|
|
2446
|
+
sk_map[step_entry].recursive = await this.get_files_and_vars(step_entry,data) // subfile processing
|
|
2447
|
+
}
|
|
2448
|
+
// SCRIPT
|
|
2449
|
+
} else if ( step_entry.startsWith('script::') ) { // parse out the script section
|
|
2450
|
+
// scripts are preprocessed for bundling
|
|
2451
|
+
// bundled scripts will be removed from the skeleton and the bundle line will be added
|
|
2452
|
+
// for each bundle created from the scripts of the current skeleton
|
|
2453
|
+
// remaining scripts will be added to the template files generated from the skeleton
|
|
2454
|
+
// Scripts that remain may contain variables for customization and final substitution
|
|
2455
|
+
//
|
|
2456
|
+
if ( step_entry.startsWith('script::params') ) {
|
|
2457
|
+
let loadable_entry = step_entry.replace("::params","")
|
|
2458
|
+
sk_map[step_entry] = await this.entry_loading(loadable_entry)
|
|
2459
|
+
console.log(step_entry)
|
|
2460
|
+
console.dir(sk_map[step_entry],{ depth : 6})
|
|
2461
|
+
|
|
2462
|
+
// let loadable_entry = step_entry.replace("::params","")
|
|
2463
|
+
// loadable_entry = loadable_entry.substring(0,loadable_entry.indexOf("{"))
|
|
2464
|
+
// //
|
|
2465
|
+
// let script_spec = await this.entry_loading(loadable_entry) // load the script (it may be output in the template)
|
|
2466
|
+
// let params = step_entry.substring(step_entry.indexOf("{")).trim()
|
|
2467
|
+
// script_spec.evaluations = this.evaluation_map(params)
|
|
2468
|
+
// //
|
|
2469
|
+
// this.early_evaluations(script_spec)
|
|
2470
|
+
// //
|
|
2471
|
+
// console.log("SCRIPT WITH PARAMETERS:: ",step_entry)
|
|
2472
|
+
// console.dir(script_spec)
|
|
2473
|
+
// sk_map[step_entry] = script_spec
|
|
2474
|
+
} else {
|
|
2475
|
+
let script_spec = await this.entry_loading(step_entry) // load the script (it may be output in the template)
|
|
2476
|
+
let data_form = script_spec.data
|
|
2477
|
+
if ( (typeof script_spec.kernel !== "undefined") ) {
|
|
2478
|
+
concern_rewrites[script_spec.file] = structuredClone(script_spec)
|
|
2479
|
+
concern_rewrites[script_spec.file].entry = step_entry
|
|
2480
|
+
console.log("FIX UP KERNEL",script_spec.kernel,sk_key,data_form)
|
|
2481
|
+
// let sk_c = Object.assign({},this.skel_to_concerns[sk_key].concerns)
|
|
2482
|
+
// for ( let [crn,usages] of Object.entries(sk_c) ) {
|
|
2483
|
+
// let dr = usages.dir
|
|
2484
|
+
// dr = dr.replace('@kernel',script_spec.kernel)
|
|
2485
|
+
// dr = this.paths.compile_one_path(dr)
|
|
2486
|
+
// usages.dir = dr
|
|
2487
|
+
// let dir_key = '@{[targets.dir]}'
|
|
2488
|
+
// let dat = data_form.replace(dir_key,dr)
|
|
2489
|
+
// if ( dat === data_form ) {
|
|
2490
|
+
// dir_key = `@{[targets.dir]/${script_spec.use_case}}`
|
|
2491
|
+
// dat = data_form.replace(dir_key,dr)
|
|
2492
|
+
// }
|
|
2493
|
+
// usages.data = dat
|
|
2494
|
+
// }
|
|
2495
|
+
|
|
2496
|
+
// script_spec.customizations = sk_c
|
|
2497
|
+
// //
|
|
2498
|
+
}
|
|
2499
|
+
sk_map[step_entry] = script_spec
|
|
2500
|
+
}
|
|
2501
|
+
//
|
|
2502
|
+
} else {
|
|
2503
|
+
console.log("NOT HANDLED YET: ",step_entry)
|
|
2504
|
+
sk_map[step_entry] = ""
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
|
|
2512
|
+
/**
|
|
2513
|
+
*
|
|
2514
|
+
* @param {*} skeleton_src
|
|
2515
|
+
*/
|
|
2516
|
+
async leaf_html_directives(skeleton_src) { // map: file name -> structured skeleton information
|
|
2517
|
+
//
|
|
2518
|
+
//
|
|
2519
|
+
// console.log("leaf_html_directives ------------------------------------------------------------------->> ")
|
|
2520
|
+
// console.dir(skeleton_src,{ depth: 6})
|
|
2521
|
+
//
|
|
2522
|
+
this.shared_entries = {}
|
|
2523
|
+
// `skeleton_src` -- previously loaded `sk_key` is the file name, `skel_def` is the file contents (structured)
|
|
2524
|
+
// adding an `sk_map` to each `sk_def`
|
|
2525
|
+
for ( let [sk_key, op_map ] of Object.entries(skeleton_src) ) {
|
|
2526
|
+
for ( let [op, skel_def ] of Object.entries(op_map) ) {
|
|
2527
|
+
let sk_map = {}
|
|
2528
|
+
let incrementer_set = {} // keep track of the variables used to increment repeated sections
|
|
2529
|
+
let skeleton = skel_def.skeleton
|
|
2530
|
+
let concern_rewrites = {}
|
|
2531
|
+
await this.build_entry_parsing(sk_key,skeleton,skeleton_src,sk_map,incrementer_set,concern_rewrites)
|
|
2532
|
+
skel_def.skeleton_map = sk_map
|
|
2533
|
+
skel_def.incrementer_set = incrementer_set
|
|
2534
|
+
skel_def.concern_rewrites = concern_rewrites
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
|
|
2540
|
+
|
|
2541
|
+
/**
|
|
2542
|
+
* Called after other processing during the preparation phase, but before writing out the results of preparation.
|
|
2543
|
+
*
|
|
2544
|
+
* There is a queue, `delay_file_loading_queue`, that hold structures with empty data components and with file names.
|
|
2545
|
+
* The elements of the queue were processed during synchronous analysis of text and time was not alloted to
|
|
2546
|
+
* loading the data.
|
|
2547
|
+
*
|
|
2548
|
+
* `delay_file_loading_queue` receives elements from `seek_imports` and from parsing `files::calc` sections,
|
|
2549
|
+
* which refer to the name_drop.dbs.
|
|
2550
|
+
*
|
|
2551
|
+
* This loads the data asynchronously from the files on the queue, parallelizing the loading as much as possible
|
|
2552
|
+
* with a Promise.all.
|
|
2553
|
+
*
|
|
2554
|
+
*/
|
|
2555
|
+
async delay_file_loading() {
|
|
2556
|
+
let q = this.delay_file_loading_queue
|
|
2557
|
+
let name_to_data = {}
|
|
2558
|
+
//
|
|
2559
|
+
if ( q.length ) {
|
|
2560
|
+
for ( let el of q ) {
|
|
2561
|
+
if ( el.file ) {
|
|
2562
|
+
name_to_data[el.file] = el.path_finder
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
//
|
|
2566
|
+
let loader_promises = []
|
|
2567
|
+
for ( let [file,path_finder] of Object.entries(name_to_data) ) {
|
|
2568
|
+
let file_path = this.top_dir_locations[path_finder]
|
|
2569
|
+
let file_name = `${file_path}/${file}`
|
|
2570
|
+
let p = fos.load_data_at_path(file_name)
|
|
2571
|
+
loader_promises.push(p)
|
|
2572
|
+
}
|
|
2573
|
+
let loader_data = await Promise.all(loader_promises)
|
|
2574
|
+
let keys = Object.keys(name_to_data)
|
|
2575
|
+
for ( let dat of loader_data ) {
|
|
2576
|
+
let key = keys.shift()
|
|
2577
|
+
name_to_data[key] = dat
|
|
2578
|
+
}
|
|
2579
|
+
//
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
return name_to_data
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
|
|
2586
|
+
|
|
2587
|
+
/**
|
|
2588
|
+
* Support (helper) for evaluating the delayed queue.
|
|
2589
|
+
* (see delayed queue documentation)
|
|
2590
|
+
*
|
|
2591
|
+
* Handles simple subsitutions that can be done with reducing forms.
|
|
2592
|
+
*
|
|
2593
|
+
* @param {object} entry_data
|
|
2594
|
+
* @returns {boolean}
|
|
2595
|
+
*/
|
|
2596
|
+
conditionless_evaluations_substitution(entry_data) {
|
|
2597
|
+
let data = entry_data.data
|
|
2598
|
+
|
|
2599
|
+
//console.log("conditionless_evaluations_substitution", data)
|
|
2600
|
+
|
|
2601
|
+
if ( !data ) return false
|
|
2602
|
+
if ( data.indexOf("?") > 0 ) {
|
|
2603
|
+
if ( this.ternary_check.test(data) ) {
|
|
2604
|
+
return false
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2608
|
+
let evals = entry_data.evaluations
|
|
2609
|
+
|
|
2610
|
+
if ( (typeof evals === "object") && Object.keys(evals).length ) {
|
|
2611
|
+
for ( let [vky, vval] of Object.entries(evals) ) {
|
|
2612
|
+
let var_form = `@{${vky}}`
|
|
2613
|
+
data = parse_util.subst(data,var_form,vval)
|
|
2614
|
+
}
|
|
2615
|
+
entry_data.data = data
|
|
2616
|
+
}
|
|
2617
|
+
return true
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
|
|
2621
|
+
|
|
2622
|
+
/**
|
|
2623
|
+
* Goes through the delayed file queue looking for evaluations that
|
|
2624
|
+
* can be substituted into the data.
|
|
2625
|
+
*
|
|
2626
|
+
* Makes a backup copy of the substitution form. The backup will be made if there are previously
|
|
2627
|
+
* analyzed recursions or if there are reductions.
|
|
2628
|
+
*
|
|
2629
|
+
* If there are evaluations, the `conditionless_evaluations_substitution` will be applied.
|
|
2630
|
+
*
|
|
2631
|
+
* @param {object} name_to_data
|
|
2632
|
+
*/
|
|
2633
|
+
evaluate_delayed_queue(name_to_data) {
|
|
2634
|
+
let q = this.delay_file_loading_queue
|
|
2635
|
+
if ( q && q.length ) {
|
|
2636
|
+
for ( let el of q ) {
|
|
2637
|
+
el.data = "" + name_to_data[el.file] // data first enters into the entry object here
|
|
2638
|
+
if ( el.evaluations || el.recursive ) { // only backup files that have skeletal level evaluations
|
|
2639
|
+
el.backup_data = "" + el.data // if the user runs an intermediate step, then the original data will be utilized
|
|
2640
|
+
}
|
|
2641
|
+
if ( el.evaluations ) {
|
|
2642
|
+
this.conditionless_evaluations_substitution(el)
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
|
|
2649
|
+
|
|
2650
|
+
/**
|
|
2651
|
+
* Given a list of conditions and variable evaluations,
|
|
2652
|
+
* this method selects a branch of the condition's
|
|
2653
|
+
* antecedant to be used as the substitution for the condition's test variable
|
|
2654
|
+
* in the data of the containing element.
|
|
2655
|
+
*
|
|
2656
|
+
* @param {Array} conds
|
|
2657
|
+
* @param {object} params
|
|
2658
|
+
* @returns {Array}
|
|
2659
|
+
*/
|
|
2660
|
+
conds_reduction(conds,params) {
|
|
2661
|
+
//
|
|
2662
|
+
//
|
|
2663
|
+
let reduced_conds = []
|
|
2664
|
+
for ( let cond of conds ) {
|
|
2665
|
+
//
|
|
2666
|
+
let vname = cond.condition.variable
|
|
2667
|
+
let val = params[vname]
|
|
2668
|
+
let vform = cond.condition.cond
|
|
2669
|
+
let replacer = ""
|
|
2670
|
+
if ( val && (typeof val === "string") && val.length > 0 ) {
|
|
2671
|
+
replacer = cond.positive_exec.pos
|
|
2672
|
+
replacer = parse_util.subst(replacer,vform,val)
|
|
2673
|
+
if ( cond.positive_exec.file && cond.positive_exec.file[0].data ) {
|
|
2674
|
+
replacer = cond.positive_exec.file[0].data
|
|
2675
|
+
}
|
|
2676
|
+
} else {
|
|
2677
|
+
replacer = cond.negative_exec.neg
|
|
2678
|
+
replacer = parse_util.subst(replacer,vform,val)
|
|
2679
|
+
if ( cond.negative_exec.file && cond.negative_exec.file[0].data ) {
|
|
2680
|
+
replacer = cond.negative_exec.file[0].data
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
//
|
|
2684
|
+
let a_reduction = {
|
|
2685
|
+
"replace" : cond.replace,
|
|
2686
|
+
"replacer" : replacer
|
|
2687
|
+
}
|
|
2688
|
+
reduced_conds.push(a_reduction)
|
|
2689
|
+
}
|
|
2690
|
+
|
|
2691
|
+
return reduced_conds
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2694
|
+
|
|
2695
|
+
/**
|
|
2696
|
+
*
|
|
2697
|
+
* @param {string} data
|
|
2698
|
+
* @param {Array} subst_list
|
|
2699
|
+
* @param {string} index_var
|
|
2700
|
+
*/
|
|
2701
|
+
list_map_to_substs(data,subst_list,index_var) {
|
|
2702
|
+
//
|
|
2703
|
+
let loop_start_len = index_var.length + "<@>".length
|
|
2704
|
+
let loop_body_start = data.indexOf(`<@${index_var}>`) + loop_start_len
|
|
2705
|
+
let loop_body_end = data.indexOf(`</@${index_var}>`)
|
|
2706
|
+
|
|
2707
|
+
for ( let asubst of subst_list ) {
|
|
2708
|
+
//
|
|
2709
|
+
let loop_body = data.substring(loop_body_start,loop_body_end)
|
|
2710
|
+
//
|
|
2711
|
+
let params = asubst.params_def
|
|
2712
|
+
let conds = asubst.conds
|
|
2713
|
+
//
|
|
2714
|
+
for ( let acond of conds ) {
|
|
2715
|
+
loop_body = parse_util.subst(loop_body,acond.replace,acond.replacer)
|
|
2716
|
+
}
|
|
2717
|
+
for ( let param in params ) {
|
|
2718
|
+
let var_form = `@{${param}}`
|
|
2719
|
+
let val = params[param]
|
|
2720
|
+
loop_body = parse_util.subst(loop_body,var_form,val)
|
|
2721
|
+
}
|
|
2722
|
+
//
|
|
2723
|
+
asubst.data = loop_body
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2726
|
+
}
|
|
2727
|
+
|
|
2728
|
+
/**
|
|
2729
|
+
*
|
|
2730
|
+
* @param {string} val
|
|
2731
|
+
*/
|
|
2732
|
+
value_is_custom_type(val) {
|
|
2733
|
+
if ( val === '%config%' ) {
|
|
2734
|
+
return true
|
|
2735
|
+
}
|
|
2736
|
+
return false
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
/**
|
|
2740
|
+
*
|
|
2741
|
+
* @param {string} var_form
|
|
2742
|
+
* @param {string} val
|
|
2743
|
+
* @returns {string}
|
|
2744
|
+
*/
|
|
2745
|
+
value_from_custom_source(var_form,val) {
|
|
2746
|
+
if ( this.global_variable_values ) {
|
|
2747
|
+
if ( val === '%config%' ) {
|
|
2748
|
+
let global_conf_map = this.global_variable_values.var_to_value
|
|
2749
|
+
let vky = var_form.replace(val,"").replace("@{","").replace("}","")
|
|
2750
|
+
val = global_conf_map[vky]
|
|
2751
|
+
return val
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
return "TESTVAL"
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
|
|
2758
|
+
/**
|
|
2759
|
+
* map_to_substs
|
|
2760
|
+
*
|
|
2761
|
+
* @param {string} data
|
|
2762
|
+
* @param {Array} subst_list
|
|
2763
|
+
*/
|
|
2764
|
+
map_to_substs(data,subst_list) {
|
|
2765
|
+
//
|
|
2766
|
+
for ( let asubst of subst_list ) {
|
|
2767
|
+
//
|
|
2768
|
+
let params = asubst.params_def
|
|
2769
|
+
let conds = asubst.conds
|
|
2770
|
+
//
|
|
2771
|
+
let changed_data = "" + data
|
|
2772
|
+
//
|
|
2773
|
+
for ( let acond of conds ) {
|
|
2774
|
+
changed_data = parse_util.subst(changed_data,acond.replace,acond.replacer)
|
|
2775
|
+
}
|
|
2776
|
+
for ( let param in params ) {
|
|
2777
|
+
let var_form = `@{${param}}`
|
|
2778
|
+
let val = params[param]
|
|
2779
|
+
if ( this.value_is_custom_type(val) ) {
|
|
2780
|
+
val = this.value_from_custom_source(var_form,val)
|
|
2781
|
+
}
|
|
2782
|
+
changed_data = parse_util.subst(changed_data,var_form,val)
|
|
2783
|
+
}
|
|
2784
|
+
//
|
|
2785
|
+
if ( parse_util.has_parameter_block(changed_data) ) {
|
|
2786
|
+
changed_data = parse_util.remove_parameter_block(changed_data)
|
|
2787
|
+
}
|
|
2788
|
+
//
|
|
2789
|
+
asubst.data = changed_data
|
|
2790
|
+
}
|
|
2791
|
+
//
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2794
|
+
|
|
2795
|
+
/**
|
|
2796
|
+
*
|
|
2797
|
+
* @param {string} entry_ky
|
|
2798
|
+
* @param {object} param_descr
|
|
2799
|
+
*/
|
|
2800
|
+
go_deep(entry_ky,param_descr) {
|
|
2801
|
+
//
|
|
2802
|
+
param_descr.backup_data = "" + param_descr.data
|
|
2803
|
+
//console.dir(param_descr.tree)
|
|
2804
|
+
//
|
|
2805
|
+
let keys = Object.keys(param_descr.tree)
|
|
2806
|
+
|
|
2807
|
+
//
|
|
2808
|
+
let parameter_key = keys.find((ky) => { // find all list types
|
|
2809
|
+
if ( ky.startsWith("_params<") ) {
|
|
2810
|
+
return true
|
|
2811
|
+
} else {
|
|
2812
|
+
return false
|
|
2813
|
+
}
|
|
2814
|
+
})
|
|
2815
|
+
//
|
|
2816
|
+
if ( parameter_key ) {
|
|
2817
|
+
let pars = parameter_key.replace("_params<","").replace(">","").split(",")
|
|
2818
|
+
for ( let par of pars ) {
|
|
2819
|
+
if ( typeof param_descr.tree[par] === "object" ) {
|
|
2820
|
+
let pdescr = param_descr.tree[par]
|
|
2821
|
+
if ( typeof pdescr.tree === "object" ) {
|
|
2822
|
+
// console.log("go_deep",entry_ky)
|
|
2823
|
+
// console.dir(param_descr)
|
|
2824
|
+
|
|
2825
|
+
this.go_deep(entry_ky,pdescr)
|
|
2826
|
+
}
|
|
2827
|
+
if ( pdescr.recursive ) {
|
|
2828
|
+
this.executable_condition_processing(pdescr)
|
|
2829
|
+
this.tree_conditional_reduction(pdescr)
|
|
2830
|
+
pdescr.data = this.join_executables(pdescr)
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2837
|
+
|
|
2838
|
+
/**
|
|
2839
|
+
*
|
|
2840
|
+
* @param {object} pdescr
|
|
2841
|
+
* @returns {string}
|
|
2842
|
+
*/
|
|
2843
|
+
join_executables(pdescr) {
|
|
2844
|
+
let reductions = pdescr.subst_recursive
|
|
2845
|
+
if ( reductions && Array.isArray(reductions) ) {
|
|
2846
|
+
let data_only = reductions.map((red) => {
|
|
2847
|
+
let next_data = red.data
|
|
2848
|
+
if ( !next_data || (typeof next_data !== 'string')) return ""
|
|
2849
|
+
else return next_data.trim()
|
|
2850
|
+
})
|
|
2851
|
+
return data_only.join("\n")
|
|
2852
|
+
}
|
|
2853
|
+
return ""
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
|
|
2857
|
+
/**
|
|
2858
|
+
*
|
|
2859
|
+
* @param {object} transformed
|
|
2860
|
+
*/
|
|
2861
|
+
conditional_evaluations(transformed) {
|
|
2862
|
+
for ( let [sk_name, op_map ] of Object.entries(transformed) ) {
|
|
2863
|
+
for ( let [op, skel ] of Object.entries(op_map) ) {
|
|
2864
|
+
let sk_map = skel.skeleton_map
|
|
2865
|
+
if ( typeof sk_map !== "object" ) continue
|
|
2866
|
+
if ( skel.parameterized ) {
|
|
2867
|
+
for ( let [entry_ky,desciptor] of Object.entries(skel.parameterized) ) {
|
|
2868
|
+
let keys = Object.keys(desciptor)
|
|
2869
|
+
let list_key = keys.find((ky) => { // find all list types
|
|
2870
|
+
if ( ky.startsWith("_type<") ) {
|
|
2871
|
+
return true
|
|
2872
|
+
} else {
|
|
2873
|
+
return false
|
|
2874
|
+
}
|
|
2875
|
+
})
|
|
2876
|
+
let parameter_key = keys.find((ky) => { // find all list types
|
|
2877
|
+
if ( ky.startsWith("_params<") ) {
|
|
2878
|
+
return true
|
|
2879
|
+
} else {
|
|
2880
|
+
return false
|
|
2881
|
+
}
|
|
2882
|
+
})
|
|
2883
|
+
//
|
|
2884
|
+
if ( list_key ) {
|
|
2885
|
+
let entry = sk_map[entry_ky]
|
|
2886
|
+
if ( entry?.subst_recursive && entry?.conds ) {
|
|
2887
|
+
for ( let evalr of entry.subst_recursive ) {
|
|
2888
|
+
let params = evalr.params_def
|
|
2889
|
+
evalr.conds = this.conds_reduction(entry.conds,params)
|
|
2890
|
+
}
|
|
2891
|
+
let data = entry.data
|
|
2892
|
+
entry.backup_data = "" + data
|
|
2893
|
+
this.list_map_to_substs(data,entry.subst_recursive,entry.recursive.params_def._var_name)
|
|
2894
|
+
} else {
|
|
2895
|
+
continue
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
if ( parameter_key ) {
|
|
2899
|
+
let pars = parameter_key.replace("_params<","").replace(">","").split(",")
|
|
2900
|
+
for ( let par of pars ) {
|
|
2901
|
+
let pdescr = desciptor[par]
|
|
2902
|
+
if ( typeof pdescr.tree === "object" ) {
|
|
2903
|
+
this.go_deep(entry_ky,pdescr)
|
|
2904
|
+
}
|
|
2905
|
+
if ( pdescr.recursive ) {
|
|
2906
|
+
this.executable_condition_processing(pdescr)
|
|
2907
|
+
this.tree_conditional_reduction(pdescr)
|
|
2908
|
+
pdescr.data = this.join_executables(pdescr)
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
|
|
2918
|
+
|
|
2919
|
+
|
|
2920
|
+
/**
|
|
2921
|
+
*
|
|
2922
|
+
* @param {string} data
|
|
2923
|
+
* @returns
|
|
2924
|
+
*/
|
|
2925
|
+
has_import_def(data) {
|
|
2926
|
+
let match_def = this.import_entry_match.exec(data)
|
|
2927
|
+
if ( match_def ) {
|
|
2928
|
+
return match_def[1]
|
|
2929
|
+
}
|
|
2930
|
+
return false
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
/**
|
|
2934
|
+
*
|
|
2935
|
+
* @param {object} transformed
|
|
2936
|
+
*/
|
|
2937
|
+
lift_remaining_imports(transformed) {
|
|
2938
|
+
for ( let [sk_name, op_map ] of Object.entries(transformed) ) {
|
|
2939
|
+
for ( let [op, skel ] of Object.entries(op_map) ) {
|
|
2940
|
+
|
|
2941
|
+
let sk_map = skel.skeleton_map
|
|
2942
|
+
if ( typeof sk_map !== "object" ) continue
|
|
2943
|
+
for ( let [step_entry,entry] of Object.entries(sk_map) ) {
|
|
2944
|
+
|
|
2945
|
+
|
|
2946
|
+
let data = entry?.data
|
|
2947
|
+
if ( typeof data === "string" ) {
|
|
2948
|
+
let import_type = this.has_import_def(data)
|
|
2949
|
+
if ( typeof import_type === "string" ) {
|
|
2950
|
+
//
|
|
2951
|
+
if ( entry.recursive && entry.recursive.executables && entry.recursive.executables.imports ) {
|
|
2952
|
+
let imps = entry.recursive.executables.imports
|
|
2953
|
+
for ( let imp of imps ) {
|
|
2954
|
+
let repl = imp.replace
|
|
2955
|
+
let value = imp.data // should already be loaded
|
|
2956
|
+
//
|
|
2957
|
+
data = parse_util.subst(data,repl,value)
|
|
2958
|
+
//
|
|
2959
|
+
}
|
|
2960
|
+
entry.data = data
|
|
2961
|
+
}
|
|
2962
|
+
//
|
|
2963
|
+
}
|
|
2964
|
+
} else {
|
|
2965
|
+
if ( entry === undefined ) {
|
|
2966
|
+
console.log("lift_remaining_imports",sk_name,step_entry)
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
//
|
|
2973
|
+
}
|
|
2974
|
+
|
|
2975
|
+
|
|
2976
|
+
/**
|
|
2977
|
+
*
|
|
2978
|
+
* @param {*} entry
|
|
2979
|
+
* @param {*} data
|
|
2980
|
+
* @returns
|
|
2981
|
+
*/
|
|
2982
|
+
entry_is_list(entry,data) {
|
|
2983
|
+
if ( entry.recursive && entry.recursive._is_loop ) {
|
|
2984
|
+
return true
|
|
2985
|
+
}
|
|
2986
|
+
let found_loop = data.startsWith("@list<")
|
|
2987
|
+
return found_loop
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
/**
|
|
2991
|
+
*
|
|
2992
|
+
* @param {*} data
|
|
2993
|
+
* @param {*} ref
|
|
2994
|
+
* @returns
|
|
2995
|
+
*/
|
|
2996
|
+
remove_loop_header(data,ref) {
|
|
2997
|
+
let stop_marker = `<@${ref}>`
|
|
2998
|
+
let stop_point = data.indexOf(stop_marker)
|
|
2999
|
+
if ( stop_point > 0 ) {
|
|
3000
|
+
let update_data = data.substring(stop_point + stop_marker.length)
|
|
3001
|
+
let end_marker = `</@${ref}>`
|
|
3002
|
+
update_data = update_data.replace(end_marker,"")
|
|
3003
|
+
return update_data
|
|
3004
|
+
}
|
|
3005
|
+
|
|
3006
|
+
return data
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
/**
|
|
3010
|
+
*
|
|
3011
|
+
* @param {object} transformed
|
|
3012
|
+
*/
|
|
3013
|
+
loop_finalization(transformed) {
|
|
3014
|
+
for ( let [sk_name, op_map ] of Object.entries(transformed) ) {
|
|
3015
|
+
for ( let [op, skel ] of Object.entries(op_map) ) {
|
|
3016
|
+
let sk_map = skel.skeleton_map
|
|
3017
|
+
if ( typeof sk_map !== "object" ) continue
|
|
3018
|
+
for ( let [step_entry,entry] of Object.entries(sk_map) ) {
|
|
3019
|
+
let data = entry.data
|
|
3020
|
+
if ( typeof data === "string" ) {
|
|
3021
|
+
if ( this.entry_is_list(entry,data) && skel.parameterized ) {
|
|
3022
|
+
//
|
|
3023
|
+
let skel_loop_vars = skel.parameterized[step_entry]
|
|
3024
|
+
let itr_name = entry.recursive.params_def._var_name
|
|
3025
|
+
let ref = skel_loop_vars[itr_name]
|
|
3026
|
+
if ( ref === "list" ) {
|
|
3027
|
+
let list_ky = `_type<${itr_name}>`
|
|
3028
|
+
let loop_list = skel_loop_vars[list_ky]
|
|
3029
|
+
let dat_tmplt = this.remove_loop_header(data,itr_name)
|
|
3030
|
+
let total_data = ""
|
|
3031
|
+
for ( let el of loop_list ) {
|
|
3032
|
+
let el_data = "" + dat_tmplt
|
|
3033
|
+
for ( let ky in el ) {
|
|
3034
|
+
let value = el[ky]
|
|
3035
|
+
let finder = entry.recursive.params_def[ky]
|
|
3036
|
+
el_data = parse_util.subst(el_data,finder,value)
|
|
3037
|
+
}
|
|
3038
|
+
total_data += el_data
|
|
3039
|
+
}
|
|
3040
|
+
entry.data = total_data
|
|
3041
|
+
}
|
|
3042
|
+
//
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
//
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
|
|
3052
|
+
|
|
3053
|
+
/**
|
|
3054
|
+
*
|
|
3055
|
+
* @param {Array} exec_list
|
|
3056
|
+
*/
|
|
3057
|
+
|
|
3058
|
+
find_conditionals(exec_list) {
|
|
3059
|
+
//
|
|
3060
|
+
if ( exec_list && Array.isArray(exec_list) ) {
|
|
3061
|
+
let conds_only = exec_list.filter((el) => {
|
|
3062
|
+
if ( el.condition && (typeof el.condition === "object") ) {
|
|
3063
|
+
return true
|
|
3064
|
+
}
|
|
3065
|
+
return false
|
|
3066
|
+
})
|
|
3067
|
+
return conds_only
|
|
3068
|
+
}
|
|
3069
|
+
return false
|
|
3070
|
+
//
|
|
3071
|
+
}
|
|
3072
|
+
|
|
3073
|
+
|
|
3074
|
+
/**
|
|
3075
|
+
*
|
|
3076
|
+
* @param {object} entry
|
|
3077
|
+
*/
|
|
3078
|
+
executable_condition_processing(entry) {
|
|
3079
|
+
let par_src = entry.recursive.params_def
|
|
3080
|
+
entry.subst_recursive = []
|
|
3081
|
+
//
|
|
3082
|
+
let subst_vals = Object.assign({},par_src)
|
|
3083
|
+
//
|
|
3084
|
+
entry.subst_recursive.push({ "params_def" : subst_vals })
|
|
3085
|
+
entry.conds = this.find_conditionals(entry.recursive.executables.execs)
|
|
3086
|
+
}
|
|
3087
|
+
|
|
3088
|
+
|
|
3089
|
+
|
|
3090
|
+
/**
|
|
3091
|
+
*
|
|
3092
|
+
* @param {object} entry
|
|
3093
|
+
*/
|
|
3094
|
+
tree_conditional_reduction(entry) {
|
|
3095
|
+
if ( entry.subst_recursive && entry.conds ) {
|
|
3096
|
+
for ( let evalr of entry.subst_recursive ) {
|
|
3097
|
+
let params = evalr.params_def
|
|
3098
|
+
evalr.conds = this.conds_reduction(entry.conds,params)
|
|
3099
|
+
}
|
|
3100
|
+
let data = entry.data
|
|
3101
|
+
entry.backup_data = "" + data
|
|
3102
|
+
this.map_to_substs(data,entry.subst_recursive)
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
|
|
3106
|
+
|
|
3107
|
+
/**
|
|
3108
|
+
*
|
|
3109
|
+
* @param {string} ptype
|
|
3110
|
+
*/
|
|
3111
|
+
is_tree_type(ptype) {
|
|
3112
|
+
if ( ptype[0] === '%' ) {
|
|
3113
|
+
if ( ptype === "%file%" ) {
|
|
3114
|
+
return true
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
return false
|
|
3118
|
+
}
|
|
3119
|
+
|
|
3120
|
+
|
|
3121
|
+
/**
|
|
3122
|
+
*
|
|
3123
|
+
* @param {string} ptype
|
|
3124
|
+
*/
|
|
3125
|
+
is_const_type(ptype) {
|
|
3126
|
+
if ( ptype[0] === '%' ) {
|
|
3127
|
+
if ( ptype === "%number%" ) {
|
|
3128
|
+
return true
|
|
3129
|
+
}
|
|
3130
|
+
if ( ptype === "%string%" ) {
|
|
3131
|
+
return true
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
return false
|
|
3135
|
+
}
|
|
3136
|
+
|
|
3137
|
+
/**
|
|
3138
|
+
*
|
|
3139
|
+
* This method gets the parameter def from the skeleton
|
|
3140
|
+
* by mapping with the entry text and the parameter name, two levels.
|
|
3141
|
+
* The data for the parameter will be taken out. (The data is likely data loaded from a file,
|
|
3142
|
+
* a sub skeleton.) If the data is a string, and the parameter is not a tree, the data will
|
|
3143
|
+
* be returned. Otherwise, the tree will be processed.
|
|
3144
|
+
*
|
|
3145
|
+
* The tree will be processed if recursive data has been figured for the tree.
|
|
3146
|
+
* The data object supplied by the recursive section may contain parameters and/or
|
|
3147
|
+
* executables.
|
|
3148
|
+
*
|
|
3149
|
+
*
|
|
3150
|
+
* When the data is returned, and the data is know to be from a file, any parameter block introduced
|
|
3151
|
+
* by the file will be removed. The parameter block is removed after substitutions for variables in
|
|
3152
|
+
* the data.
|
|
3153
|
+
*
|
|
3154
|
+
*
|
|
3155
|
+
* @param {object} skel
|
|
3156
|
+
* @param {string} entry_ky
|
|
3157
|
+
* @param {string} pname
|
|
3158
|
+
* @returns {string}
|
|
3159
|
+
*/
|
|
3160
|
+
data_from_parameter(skel,entry_ky,pname) {
|
|
3161
|
+
let p_entry = skel.parameterized[entry_ky]
|
|
3162
|
+
let will_return_data = ""
|
|
3163
|
+
if ( p_entry ) {
|
|
3164
|
+
let p_def = p_entry[pname]
|
|
3165
|
+
let data = p_def.data
|
|
3166
|
+
if ( typeof data === "string" ) {
|
|
3167
|
+
if ( p_def.tree ) {
|
|
3168
|
+
let rec = p_def.recursive
|
|
3169
|
+
if ( rec ) {
|
|
3170
|
+
let dst_pars = rec.params_def
|
|
3171
|
+
if ( dst_pars ) {
|
|
3172
|
+
for ( let par in dst_pars ) {
|
|
3173
|
+
if ( !(p_def.tree[par].tree) && (p_def.tree[par].recursive === undefined) ) {
|
|
3174
|
+
dst_pars[par] = p_def.tree[par].data
|
|
3175
|
+
} else {
|
|
3176
|
+
if ( typeof p_def.tree[par].recursive === "object" ) {
|
|
3177
|
+
let pdescr = p_def.tree[par]
|
|
3178
|
+
this.executable_condition_processing(pdescr)
|
|
3179
|
+
this.tree_conditional_reduction(pdescr)
|
|
3180
|
+
pdescr.data = this.join_executables(pdescr)
|
|
3181
|
+
dst_pars[par] = pdescr.data
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
if ( rec.executables ) {
|
|
3187
|
+
let pdescr = p_def
|
|
3188
|
+
this.executable_condition_processing(pdescr)
|
|
3189
|
+
this.tree_conditional_reduction(pdescr)
|
|
3190
|
+
pdescr.data = this.join_executables(pdescr)
|
|
3191
|
+
}
|
|
3192
|
+
will_return_data = p_def.data
|
|
3193
|
+
}
|
|
3194
|
+
} else {
|
|
3195
|
+
will_return_data = data
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
if ( parse_util.has_parameter_block(will_return_data) ) {
|
|
3200
|
+
will_return_data = parse_util.remove_parameter_block(will_return_data)
|
|
3201
|
+
}
|
|
3202
|
+
return will_return_data
|
|
3203
|
+
}
|
|
3204
|
+
|
|
3205
|
+
|
|
3206
|
+
|
|
3207
|
+
/**
|
|
3208
|
+
*
|
|
3209
|
+
* Updates the skeleton map, sk map, by figuring the data of an entry.
|
|
3210
|
+
* The data field will be placed into a file in the final steps.
|
|
3211
|
+
*
|
|
3212
|
+
* The skeleton map is obtained from a skeleton data structure belonging
|
|
3213
|
+
* to an operation version (e.g. default, shops, boxy, mixed... )
|
|
3214
|
+
*
|
|
3215
|
+
* This method considers the possibity that the sk map has not been constructed for some entries.
|
|
3216
|
+
*
|
|
3217
|
+
* If the sk map has been constructed, and if an entry has a 'recursive' field,
|
|
3218
|
+
* the method will attempt to update the param def entry of the recursive structure stored in the field.
|
|
3219
|
+
* Also, it may carry out any executions if there is an executable object.
|
|
3220
|
+
*
|
|
3221
|
+
* The parameter def has fields of different types. A tree type is one that allows for the reduction of values
|
|
3222
|
+
* across different files. A const type should have values already figured and stored in the parameterized
|
|
3223
|
+
* section of a the skeleton data structure.
|
|
3224
|
+
*
|
|
3225
|
+
* In the case of the tree type, the update of the parameter def object (by parameter) is handed off
|
|
3226
|
+
* to `data_from_parameter`.
|
|
3227
|
+
*
|
|
3228
|
+
* In the case of the constant types, the `evaluations` parameter is developed until a call to `early_evaluations`
|
|
3229
|
+
* can be made.
|
|
3230
|
+
*
|
|
3231
|
+
*
|
|
3232
|
+
* @param {object} transformed
|
|
3233
|
+
*/
|
|
3234
|
+
up_prop_data_to_vars(transformed) {
|
|
3235
|
+
for ( let [sk_name, op_map ] of Object.entries(transformed) ) {
|
|
3236
|
+
for ( let [op, skel ] of Object.entries(op_map) ) {
|
|
3237
|
+
let sk_map = skel.skeleton_map
|
|
3238
|
+
if ( typeof sk_map !== "object" ) continue
|
|
3239
|
+
for ( let [entry_ky,desciptor] of Object.entries(sk_map) ) {
|
|
3240
|
+
if ( desciptor?.recursive ) {
|
|
3241
|
+
let rec = desciptor.recursive
|
|
3242
|
+
desciptor.backup_data = "" + desciptor.data
|
|
3243
|
+
if ( rec.params_def ) {
|
|
3244
|
+
//
|
|
3245
|
+
for ( let [pname,ptype] of Object.entries(rec.params_def) ) {
|
|
3246
|
+
if ( this.is_tree_type(ptype) ) {
|
|
3247
|
+
let pdat = this.data_from_parameter(skel,entry_ky,pname)
|
|
3248
|
+
rec.params_def[pname] = pdat
|
|
3249
|
+
} else if ( this.is_const_type(ptype) ) {
|
|
3250
|
+
//
|
|
3251
|
+
if ( desciptor.evaluations === undefined ) {
|
|
3252
|
+
desciptor.evaluations = Object.assign({},rec.params_def)
|
|
3253
|
+
}
|
|
3254
|
+
let evals = desciptor.evaluations
|
|
3255
|
+
if ( skel.parameterized[entry_ky] ) {
|
|
3256
|
+
evals[pname] = skel.parameterized[entry_ky][pname].data
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
//
|
|
3261
|
+
if ( desciptor.evaluations ) {
|
|
3262
|
+
this.early_evaluations(desciptor)
|
|
3263
|
+
}
|
|
3264
|
+
//
|
|
3265
|
+
}
|
|
3266
|
+
if ( rec.executables ) {
|
|
3267
|
+
let pdescr = desciptor
|
|
3268
|
+
this.executable_condition_processing(pdescr)
|
|
3269
|
+
this.tree_conditional_reduction(pdescr)
|
|
3270
|
+
let ddat = this.join_executables(pdescr)
|
|
3271
|
+
if ( parse_util.has_parameter_block(ddat) ) {
|
|
3272
|
+
ddat = parse_util.remove_parameter_block(ddat)
|
|
3273
|
+
}
|
|
3274
|
+
desciptor.data = ddat
|
|
3275
|
+
}
|
|
3276
|
+
} else {
|
|
3277
|
+
if ( desciptor === undefined ) {
|
|
3278
|
+
console.log("up_prop_data_to_vars",sk_name,entry_ky)
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
}
|
|
3285
|
+
|
|
3286
|
+
|
|
3287
|
+
|
|
3288
|
+
/**
|
|
3289
|
+
*
|
|
3290
|
+
* @param {object} transformed
|
|
3291
|
+
*/
|
|
3292
|
+
list_to_skeletal_variable_assignment(transformed) {
|
|
3293
|
+
for ( let [sk_key, op_map ] of Object.entries(transformed) ) {
|
|
3294
|
+
for ( let [op, skel ] of Object.entries(op_map) ) {
|
|
3295
|
+
let sk_map = skel.skeleton_map
|
|
3296
|
+
if ( typeof sk_map !== "object" ) continue
|
|
3297
|
+
if ( skel.parameterized ) {
|
|
3298
|
+
for ( let [entry_ky,desciptor] of Object.entries(skel.parameterized) ) {
|
|
3299
|
+
let keys = Object.keys(desciptor)
|
|
3300
|
+
let list_key = keys.find((ky) => {
|
|
3301
|
+
if ( ky.startsWith("_type<") ) {
|
|
3302
|
+
return true
|
|
3303
|
+
} else {
|
|
3304
|
+
return false
|
|
3305
|
+
}
|
|
3306
|
+
})
|
|
3307
|
+
//
|
|
3308
|
+
if ( list_key ) {
|
|
3309
|
+
let entry = sk_map[entry_ky]
|
|
3310
|
+
if ( entry?.recursive ) {
|
|
3311
|
+
//console.log("list_to_skeletal_variable_assignment",entry.recursive.params_def)
|
|
3312
|
+
} else {
|
|
3313
|
+
continue
|
|
3314
|
+
}
|
|
3315
|
+
let el_var = list_key.replace("_type<","").replace(">","").trim()
|
|
3316
|
+
if ( desciptor[el_var] === "list" ) {
|
|
3317
|
+
let alist = desciptor[list_key]
|
|
3318
|
+
if ( Array.isArray(alist) ) {
|
|
3319
|
+
let par_src = entry.recursive.params_def
|
|
3320
|
+
entry.subst_recursive = []
|
|
3321
|
+
for ( let var_vals of alist ) {
|
|
3322
|
+
// entry.subst_recursive
|
|
3323
|
+
let subst_vals = {}
|
|
3324
|
+
for ( let ky in par_src ) {
|
|
3325
|
+
subst_vals[ky] = var_vals[ky]
|
|
3326
|
+
}
|
|
3327
|
+
entry.subst_recursive.push({ "params_def" : subst_vals })
|
|
3328
|
+
entry.conds = this.find_conditionals(entry.recursive.executables.execs)
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
//
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
|
|
3340
|
+
|
|
3341
|
+
|
|
3342
|
+
/**
|
|
3343
|
+
*
|
|
3344
|
+
* @param {object} transformed
|
|
3345
|
+
*/
|
|
3346
|
+
incremental_evaluations(transformed) {
|
|
3347
|
+
//
|
|
3348
|
+
for ( let [sk_key, op_map ] of Object.entries(transformed) ) {
|
|
3349
|
+
for ( let [op, skel ] of Object.entries(op_map) ) {
|
|
3350
|
+
if ( typeof skel.incrementer_set === "object" ) {
|
|
3351
|
+
for ( let [ky_vname,incr_descr] of Object.entries(skel.incrementer_set) ) {
|
|
3352
|
+
let subst_var = `@{${incr_descr.apply_to}}`
|
|
3353
|
+
let i = parseInt(incr_descr.start_val)
|
|
3354
|
+
let n = incr_descr.list.length
|
|
3355
|
+
let stepper = incr_descr.starter
|
|
3356
|
+
//
|
|
3357
|
+
let data = stepper.data
|
|
3358
|
+
stepper.data = parse_util.subst(data,subst_var,i)
|
|
3359
|
+
let l = incr_descr.list
|
|
3360
|
+
for ( let k = 0; k < n; k++ ) {
|
|
3361
|
+
let next = l[k]
|
|
3362
|
+
if ( next !== stepper ) {
|
|
3363
|
+
i++
|
|
3364
|
+
let data = next.data
|
|
3365
|
+
next.data = parse_util.subst(data,subst_var,i)
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
//
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3375
|
+
|
|
3376
|
+
/**
|
|
3377
|
+
*
|
|
3378
|
+
* @param {object} transformed
|
|
3379
|
+
*/
|
|
3380
|
+
finalize_markup(transformed) {
|
|
3381
|
+
for ( let [sk_name, op_map ] of Object.entries(transformed) ) {
|
|
3382
|
+
for ( let [op, skel ] of Object.entries(op_map) ) {
|
|
3383
|
+
let sk_map = skel.skeleton_map
|
|
3384
|
+
if ( typeof sk_map !== "object" ) continue
|
|
3385
|
+
let fdata = ""
|
|
3386
|
+
for ( let [entry_ky,desciptor] of Object.entries(sk_map) ) {
|
|
3387
|
+
if ( entry_ky.indexOf("script::") >= 0 ) {
|
|
3388
|
+
// the script should be loaded
|
|
3389
|
+
fdata += (desciptor.data ? desciptor.data : "") + "\n"
|
|
3390
|
+
if ( this.remove_block_comments ) {
|
|
3391
|
+
fdata = parse_util.clear_block_comments(fdata)
|
|
3392
|
+
}
|
|
3393
|
+
} else {
|
|
3394
|
+
if ( typeof desciptor === "string" ) {
|
|
3395
|
+
fdata += desciptor
|
|
3396
|
+
} else if ( typeof desciptor === "object" ) {
|
|
3397
|
+
fdata += desciptor.data ? desciptor.data : ""
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3402
|
+
skel.final.markup = fdata
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3406
|
+
|
|
3407
|
+
|
|
3408
|
+
/**
|
|
3409
|
+
*
|
|
3410
|
+
* for the call tosection_parsing(all_skeletons)
|
|
3411
|
+
* The one parameter `all_skeletons` is a map from file names to skeleton ascii.
|
|
3412
|
+
*
|
|
3413
|
+
* This method parses the skeleton ascii, managing the document structure,
|
|
3414
|
+
* creating lists of leaf files specified in the text and extracting skeleton files
|
|
3415
|
+
* in order to add them to the map `all_skeletons`.
|
|
3416
|
+
*
|
|
3417
|
+
* Skeletons are parsed into sections. HTML structure is permitted in a skeleton.
|
|
3418
|
+
* Variables are allowed as $$<variable name> forms, where <variable name> is an identifier.
|
|
3419
|
+
*
|
|
3420
|
+
* Parsing includes restructuring files based on configuration variables.
|
|
3421
|
+
* Each concern may have different requirments (formulaic variations on layout, including number of sections,
|
|
3422
|
+
* grid mapping, topic structure, etc.)
|
|
3423
|
+
*
|
|
3424
|
+
* @param {object} all_skeletons
|
|
3425
|
+
* @param {object} skel_to_concerns -- each skeleton serves a set of concerns
|
|
3426
|
+
*/
|
|
3427
|
+
async skeleton_parsing(all_skeletons) {
|
|
3428
|
+
// expand and parse all skeletons being parsed in the batch
|
|
3429
|
+
let transform_1 = await this.section_parsing(all_skeletons)
|
|
3430
|
+
//
|
|
3431
|
+
// // This script parsing may be obsolete after a short stay here.
|
|
3432
|
+
// // preprocessing the skeletons has worked better.
|
|
3433
|
+
// // this may still be good for shared scripts that may be better embedded. But, some handling of multiple uses may help
|
|
3434
|
+
// let script_stats = this.coalesce_scripts(transform_1) // keeping track of scripts that are parsed and may be requested by any number of skeletons
|
|
3435
|
+
// this.update_script_stats_usage(transform_1,script_stats)
|
|
3436
|
+
|
|
3437
|
+
// let occurence_partition = this.partition(script_stats)
|
|
3438
|
+
// console.dir(occurence_partition)
|
|
3439
|
+
// await this.prep_script_directories(occurence_partition)
|
|
3440
|
+
|
|
3441
|
+
//
|
|
3442
|
+
|
|
3443
|
+
// Now, get ready for operating on the html inclusions specified by a skeleton.
|
|
3444
|
+
// Note: some loops will have been expanded leaving behind other directive (e.g. calc)
|
|
3445
|
+
await this.leaf_html_directives(transform_1)
|
|
3446
|
+
|
|
3447
|
+
let name_to_data = await this.delay_file_loading()
|
|
3448
|
+
|
|
3449
|
+
this.evaluate_delayed_queue(name_to_data)
|
|
3450
|
+
|
|
3451
|
+
this.incremental_evaluations(transform_1)
|
|
3452
|
+
|
|
3453
|
+
this.list_to_skeletal_variable_assignment(transform_1)
|
|
3454
|
+
|
|
3455
|
+
this.up_prop_data_to_vars(transform_1)
|
|
3456
|
+
|
|
3457
|
+
this.conditional_evaluations(transform_1)
|
|
3458
|
+
|
|
3459
|
+
this.lift_remaining_imports(transform_1)
|
|
3460
|
+
|
|
3461
|
+
this.loop_finalization(transform_1)
|
|
3462
|
+
|
|
3463
|
+
this.finalize_markup(transform_1)
|
|
3464
|
+
|
|
3465
|
+
// just to keep a record of what has taken place in this method
|
|
3466
|
+
let str = JSON.stringify(transform_1,null,4)
|
|
3467
|
+
await fos.write_out_string(this.top_level_parsed,str)
|
|
3468
|
+
|
|
3469
|
+
return transform_1
|
|
3470
|
+
}
|
|
3471
|
+
|
|
3472
|
+
|
|
3473
|
+
|
|
3474
|
+
|
|
3475
|
+
// ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
|
|
3476
|
+
|
|
3477
|
+
/**
|
|
3478
|
+
* This method needs the outputs stored in the configuration file.
|
|
3479
|
+
* Each concern gets a reverse map of the ogroup skeleton map, which mapped template file names to skeleton inputs
|
|
3480
|
+
*
|
|
3481
|
+
* @param {object} skel_matrix
|
|
3482
|
+
*/
|
|
3483
|
+
collect_concerns(outputs_field_of_conf) {
|
|
3484
|
+
let concerns_map = {}
|
|
3485
|
+
for ( let ogroup of outputs_field_of_conf ) { // an ogroup
|
|
3486
|
+
let concerns = ogroup.targets.concerns // targets ... concerns array (peculiar to this ogroup)
|
|
3487
|
+
for ( let concern of concerns ) {
|
|
3488
|
+
let mapped_c = concerns_map[concern] // the concern may appear in more than one ogroup, but that may not be a good idea considering ops
|
|
3489
|
+
if ( !mapped_c ) {
|
|
3490
|
+
mapped_c = {}
|
|
3491
|
+
concerns_map[concern] = mapped_c
|
|
3492
|
+
}
|
|
3493
|
+
let skeletons = ogroup.skeletons // output file to skeleton
|
|
3494
|
+
let r_skeletons = parse_util.reverse_map(skeletons)
|
|
3495
|
+
for ( let skeleton in r_skeletons ) { // the skeletone is a "file" name
|
|
3496
|
+
if ( mapped_c[skeleton] === undefined ) mapped_c[skeleton] = {} // not one to one
|
|
3497
|
+
mapped_c[skeleton] = Object.assign(mapped_c[skeleton],r_skeletons[skeleton]) // r_skeletons[skeleton] is an object (mapping not one to one)
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
|
|
3502
|
+
return concerns_map
|
|
3503
|
+
}
|
|
3504
|
+
|
|
3505
|
+
|
|
3506
|
+
async load_file_or_leave_it_as_path(concern_replace) {
|
|
3507
|
+
console.log(concern_replace)
|
|
3508
|
+
let fname = concern_replace.replace("{{{","").replace("}}}","").trim()
|
|
3509
|
+
let data = await fos.load_data_at_path(fname)
|
|
3510
|
+
if ( data && (typeof data === "string") ) {
|
|
3511
|
+
return data
|
|
3512
|
+
}
|
|
3513
|
+
return concern_replace
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
|
|
3517
|
+
/**
|
|
3518
|
+
*
|
|
3519
|
+
* @param {Array} concerns
|
|
3520
|
+
* @param {string} dir_form
|
|
3521
|
+
* @param {string} tfile
|
|
3522
|
+
* @param {string} template
|
|
3523
|
+
*/
|
|
3524
|
+
async output_ogroup_template(concerns,dir_form,tfile,template,concern_rewrites) {
|
|
3525
|
+
let promises = []
|
|
3526
|
+
for ( let concern of concerns ) {
|
|
3527
|
+
let top_out_dir = dir_form.replace("@concern",concern)
|
|
3528
|
+
let t_output_path = `${top_out_dir}${tfile}`
|
|
3529
|
+
let t_path = this.paths.compile_one_path(t_output_path)
|
|
3530
|
+
if ( concern_rewrites ) {
|
|
3531
|
+
for ( let rewrite_rule of Object.values(concern_rewrites) ) {
|
|
3532
|
+
let concern_replace = rewrite_rule.replace.replace("@concern",concern)
|
|
3533
|
+
concern_replace = await this.load_file_or_leave_it_as_path(concern_replace)
|
|
3534
|
+
template = template.replaceAll(rewrite_rule.find,concern_replace)
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
console.log("output_ogroup_template",t_path)
|
|
3538
|
+
promises.push(fos.write_out_string(t_path,template))
|
|
3539
|
+
}
|
|
3540
|
+
//
|
|
3541
|
+
await Promise.all(promises)
|
|
3542
|
+
}
|
|
3543
|
+
|
|
3544
|
+
|
|
3545
|
+
/**
|
|
3546
|
+
*
|
|
3547
|
+
* @param {object} parsed_skels
|
|
3548
|
+
*/
|
|
3549
|
+
async generate_all_concerns_templates(parsed_skels) {
|
|
3550
|
+
//
|
|
3551
|
+
let outputs = this.outputs
|
|
3552
|
+
for ( let ogroup of outputs ) {
|
|
3553
|
+
for ( let [tfile,file] of Object.entries(ogroup.skeletons) ) {
|
|
3554
|
+
//
|
|
3555
|
+
let skel_compiled_path = ogroup.skels_processed[file].ablsolute_path
|
|
3556
|
+
let op_key = ogroup.skels_processed[file].skel_vars_key
|
|
3557
|
+
op_key = op_key ? op_key : "defaults"
|
|
3558
|
+
let skel_data = parsed_skels[skel_compiled_path][op_key] // This is the stuff for template generation
|
|
3559
|
+
|
|
3560
|
+
let template = skel_data.final.markup // set in `final_markup`
|
|
3561
|
+
//
|
|
3562
|
+
if ( skel_data.concern_rewrites ) {
|
|
3563
|
+
let okeys = Object.keys(skel_data.concern_rewrites)
|
|
3564
|
+
if ( okeys.length ) { //{ '/site_app.js': '{{{ @{[targets.dir]}/scripts/site_app.js }}}' }
|
|
3565
|
+
for ( let ky of okeys ) {
|
|
3566
|
+
console.log("<1>generate_all_concerns_templates","concern_rewrites.....")
|
|
3567
|
+
let rewrites = skel_data.concern_rewrites[ky]
|
|
3568
|
+
//
|
|
3569
|
+
//console.dir(rewrites)
|
|
3570
|
+
let form = rewrites.data
|
|
3571
|
+
let generic_dir_spec = this.pull_parent_dir_dotted_target_dir(form,ogroup)
|
|
3572
|
+
if ( generic_dir_spec ) {
|
|
3573
|
+
generic_dir_spec = generic_dir_spec.replace("@kernel",rewrites.kernel)
|
|
3574
|
+
// let extraneous_this_impl = "/@kernel"
|
|
3575
|
+
// generic_dir_spec = generic_dir_spec.replace(extraneous_this_impl,"")
|
|
3576
|
+
console.log("<2>generate_all_concerns_templates","concern_rewrites.....",generic_dir_spec, form)
|
|
3577
|
+
skel_data.concern_rewrites[ky] = {
|
|
3578
|
+
"find" : form,
|
|
3579
|
+
"replace" : generic_dir_spec
|
|
3580
|
+
}
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3583
|
+
} else {
|
|
3584
|
+
skel_data.concern_rewrites = false
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
//
|
|
3588
|
+
// now make sure each concern gets the template file to be generated buy ogroup
|
|
3589
|
+
let targets = ogroup.targets // targets
|
|
3590
|
+
let dir_form = targets.dir_form
|
|
3591
|
+
dir_form = dir_form.replace("@target",this.created_dir)
|
|
3592
|
+
|
|
3593
|
+
await this.output_ogroup_template(targets.concerns,dir_form,tfile,template,skel_data.concern_rewrites )
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
//
|
|
3597
|
+
let concerns = this.collect_concerns(this.outputs)
|
|
3598
|
+
return concerns
|
|
3599
|
+
}
|
|
3600
|
+
|
|
3601
|
+
|
|
3602
|
+
/**
|
|
3603
|
+
* These entries appear in the name_drop.db type of file for a particular page template of a particular concern.
|
|
3604
|
+
*
|
|
3605
|
+
* So, for instance, [websites]/@concern/templates/index.tmplt will have a name_drop type of db stored with it, as such:
|
|
3606
|
+
* websites]/@concern/templates/index_calc.db
|
|
3607
|
+
*
|
|
3608
|
+
* The following would be the example of an entry:
|
|
3609
|
+
*
|
|
3610
|
+
* `"about_box" : {
|
|
3611
|
+
"name": "about",
|
|
3612
|
+
"content": {
|
|
3613
|
+
"type": "@<type>",
|
|
3614
|
+
"file": "./about.@<type>"
|
|
3615
|
+
}
|
|
3616
|
+
}`
|
|
3617
|
+
*
|
|
3618
|
+
* It is expected to be updated in the following sort of way:
|
|
3619
|
+
*
|
|
3620
|
+
* "name" : "about",
|
|
3621
|
+
"content" : {
|
|
3622
|
+
"svg" : "",
|
|
3623
|
+
"file" : "./about.svg"
|
|
3624
|
+
}
|
|
3625
|
+
*
|
|
3626
|
+
* The update is done prior to running the second stage of roll-right. (It could be html instead of svg.)
|
|
3627
|
+
*
|
|
3628
|
+
* @param {string} concern
|
|
3629
|
+
* @param {object} track_map
|
|
3630
|
+
*/
|
|
3631
|
+
update_calc_tracking(concern,track_map) {
|
|
3632
|
+
for ( let tk in track_map ) {
|
|
3633
|
+
let namer = tk.replace("Box","").replace("_box","")
|
|
3634
|
+
track_map[tk] = {
|
|
3635
|
+
"name" : namer,
|
|
3636
|
+
"content" : {
|
|
3637
|
+
"type" : "@<type>",
|
|
3638
|
+
"file" : `./${namer}.@<type>`
|
|
3639
|
+
}
|
|
3640
|
+
}
|
|
3641
|
+
}
|
|
3642
|
+
|
|
3643
|
+
track_map["_track_control"] = {
|
|
3644
|
+
"concern" : concern,
|
|
3645
|
+
"edited" : false,
|
|
3646
|
+
"mod-date" : Date.now(),
|
|
3647
|
+
"create-date" : Date.now()
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3651
|
+
|
|
3652
|
+
/**
|
|
3653
|
+
* If a user has already created a `_calc.db` file,
|
|
3654
|
+
* this method will inform the caller if that is the case and also
|
|
3655
|
+
* if the `_track_control` structure has an `edited` field set to true.
|
|
3656
|
+
*
|
|
3657
|
+
* As such, this affects the user's work process. A user wishing to keep edits must
|
|
3658
|
+
* set the `edited` field to true. Otherwise, the file may be overwritten, which is usually
|
|
3659
|
+
* not desirable, except occasionally. As such, the user should be able to toggle this field
|
|
3660
|
+
* in any application that manages this sort of file.
|
|
3661
|
+
*
|
|
3662
|
+
* @param {string} calc_tracking_file -- a path to a concerns calc DB file.
|
|
3663
|
+
* @returns {boolean}
|
|
3664
|
+
*/
|
|
3665
|
+
async existing_calc_track(calc_tracking_file) {
|
|
3666
|
+
let tracker = await fos.load_json_data_at_path(calc_tracking_file)
|
|
3667
|
+
if ( tracker ) {
|
|
3668
|
+
if ( tracker._track_control.edited ) {
|
|
3669
|
+
return tracker
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3672
|
+
return false
|
|
3673
|
+
}
|
|
3674
|
+
|
|
3675
|
+
|
|
3676
|
+
/**
|
|
3677
|
+
* This method takes an existing tracking file contents for a `_calc.db` region
|
|
3678
|
+
* and includes it in, and overwrites, the generic `_calc.db` found in the alpha directories
|
|
3679
|
+
* provided the user (admin).
|
|
3680
|
+
*
|
|
3681
|
+
* @param {object} new_tracking
|
|
3682
|
+
* @param {object} saved_track_map
|
|
3683
|
+
* @returns {boolean}
|
|
3684
|
+
*/
|
|
3685
|
+
update_tracker_with_new(new_tracking,saved_track_map) {
|
|
3686
|
+
for ( let tky in new_tracking ) {
|
|
3687
|
+
if ( saved_track_map[tky] ) continue
|
|
3688
|
+
saved_track_map[tky] = new_tracking[tky]
|
|
3689
|
+
}
|
|
3690
|
+
return saved_track_map
|
|
3691
|
+
}
|
|
3692
|
+
|
|
3693
|
+
|
|
3694
|
+
/**
|
|
3695
|
+
* For reentry after compilation.
|
|
3696
|
+
* Load the ogroups file which should have the template files already processed
|
|
3697
|
+
* ahead of applying updates to the sectional database.
|
|
3698
|
+
*/
|
|
3699
|
+
async load_ogroups_intermediate_files() {
|
|
3700
|
+
//
|
|
3701
|
+
let ogroups_file = `[websites]/${this.project_dir}/ogroups_intermediate_files.json`
|
|
3702
|
+
ogroups_file = this.paths.compile_one_path(ogroups_file)
|
|
3703
|
+
this.outputs = await fos.load_json_data_at_path(ogroups_file)
|
|
3704
|
+
//
|
|
3705
|
+
// console.dir(this.outputs,{ depth : 6 })
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3708
|
+
/**
|
|
3709
|
+
*
|
|
3710
|
+
* makes sure that directories receiving the generated assets exists
|
|
3711
|
+
* and are structure according to the configuration.
|
|
3712
|
+
*
|
|
3713
|
+
* Writes out template files each with a '.tmplt' suffix.
|
|
3714
|
+
* Writes out a manifest to these files, concerns_to_files.json.
|
|
3715
|
+
*
|
|
3716
|
+
* Writes out db files for calculated regions. Outputs a file for each template.
|
|
3717
|
+
* The DB files can be used later to override the generic database stored in the alpha.
|
|
3718
|
+
*
|
|
3719
|
+
* Creates one top level file to keep track of the individual, template associated, files.
|
|
3720
|
+
* That is `concerns_named.db`
|
|
3721
|
+
*
|
|
3722
|
+
* All of the individual template and DB files can be output to the 'template' directories of the concerns.
|
|
3723
|
+
* The top level file can be output to the directory commanding all concerns, typically "template-configs"
|
|
3724
|
+
* in `[websites]` directory.
|
|
3725
|
+
*
|
|
3726
|
+
* @param {object} concerns_to_files
|
|
3727
|
+
*/
|
|
3728
|
+
|
|
3729
|
+
async write_templates_op_data() {
|
|
3730
|
+
//
|
|
3731
|
+
let concerns_to_files = this.collect_concerns(this.outputs)
|
|
3732
|
+
//
|
|
3733
|
+
let concerns_file = `[websites]/${this.project_dir}/concerns_to_files.json`
|
|
3734
|
+
concerns_file = this.paths.compile_one_path(concerns_file)
|
|
3735
|
+
await fos.write_out_pretty_json(concerns_file,concerns_to_files,4)
|
|
3736
|
+
//
|
|
3737
|
+
let ogroups_file = concerns_file.replace("concerns_to_files.json","ogroups_intermediate_files.json")
|
|
3738
|
+
ogroups_file = this.paths.compile_one_path(ogroups_file)
|
|
3739
|
+
await fos.write_out_pretty_json(ogroups_file,this.outputs,8)
|
|
3740
|
+
//
|
|
3741
|
+
// get the name of the type level file
|
|
3742
|
+
let db_locations = concerns_file.replace("concerns_to_files.json","concerns_named.db")
|
|
3743
|
+
let concerns_db_files = {}
|
|
3744
|
+
parse_util.copy_keys(concerns_db_files,concerns_to_files,"object")
|
|
3745
|
+
//
|
|
3746
|
+
//
|
|
3747
|
+
let outputs = this.outputs
|
|
3748
|
+
for ( let ogroup of outputs ) {
|
|
3749
|
+
let targets = ogroup.targets // targets
|
|
3750
|
+
let dir_form = targets.dir_form
|
|
3751
|
+
//
|
|
3752
|
+
dir_form = dir_form.replace("@target",this.created_dir)
|
|
3753
|
+
for ( let concern of targets.concerns ) {
|
|
3754
|
+
//
|
|
3755
|
+
let db_output = concerns_db_files[concern]
|
|
3756
|
+
//
|
|
3757
|
+
let top_out_dir = dir_form.replace("@concern",concern)
|
|
3758
|
+
//
|
|
3759
|
+
let sk_maps = concerns_to_files[concern]
|
|
3760
|
+
let promises = []
|
|
3761
|
+
// console.log(">> >> >>")
|
|
3762
|
+
// console.log(concern)
|
|
3763
|
+
// console.dir(Object.keys(sk_maps))
|
|
3764
|
+
for ( let sk in sk_maps ) {
|
|
3765
|
+
let opairs = sk_maps[sk]
|
|
3766
|
+
for ( let tfile in opairs ) {
|
|
3767
|
+
//
|
|
3768
|
+
let t_output_path = `${top_out_dir}${tfile}`
|
|
3769
|
+
let t_path = this.paths.compile_one_path(t_output_path)
|
|
3770
|
+
//
|
|
3771
|
+
// console.log("write_templates",dir_form,">>",t_path) // outputs the template here.
|
|
3772
|
+
// promises.push(fos.write_out_string(t_path,template))
|
|
3773
|
+
//
|
|
3774
|
+
if ( this.tracking_skel_calc_usage[sk] ) {
|
|
3775
|
+
let track_map = this.tracking_skel_calc_usage[sk]
|
|
3776
|
+
let calc_tracking_file = t_path
|
|
3777
|
+
this.update_calc_tracking(concern,track_map)
|
|
3778
|
+
calc_tracking_file = calc_tracking_file.replace('.tmplt',`_calc.db`)
|
|
3779
|
+
let tracker = false
|
|
3780
|
+
// Checks to see if there are custom definitions set by the user.
|
|
3781
|
+
if ( tracker = await this.existing_calc_track(calc_tracking_file) ) {
|
|
3782
|
+
// Given there are such definition, use them.
|
|
3783
|
+
track_map = this.update_tracker_with_new(track_map,tracker)
|
|
3784
|
+
}
|
|
3785
|
+
// output the cacluation sections DB for each top level file.
|
|
3786
|
+
promises.push(fos.write_out_pretty_json(calc_tracking_file,track_map,4))
|
|
3787
|
+
db_output[calc_tracking_file] = 1 // add to the high level manifest pointing to these files
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
//
|
|
3792
|
+
await Promise.all(promises)
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
//
|
|
3796
|
+
// output the top level file used to find the DBs
|
|
3797
|
+
// this is a map of concerns to db files
|
|
3798
|
+
if ( Object.keys(this.tracking_skel_calc_usage).length ) {
|
|
3799
|
+
await fos.write_out_pretty_json(db_locations,concerns_db_files,4)
|
|
3800
|
+
}
|
|
3801
|
+
//
|
|
3802
|
+
return concerns_to_files
|
|
3803
|
+
}
|
|
3804
|
+
|
|
3805
|
+
|
|
3806
|
+
|
|
3807
|
+
/**
|
|
3808
|
+
*
|
|
3809
|
+
* @param {object} el_var_map - var name and types
|
|
3810
|
+
* @param {object} el - var name and values
|
|
3811
|
+
* @param {string} target_data
|
|
3812
|
+
* @return {string}
|
|
3813
|
+
*
|
|
3814
|
+
*/
|
|
3815
|
+
var_substitution(el_var_map,el,target_data) {
|
|
3816
|
+
//
|
|
3817
|
+
let td_update = "" + target_data
|
|
3818
|
+
for ( let [ky,vtype] of el_var_map ) {
|
|
3819
|
+
let val = el[ky]
|
|
3820
|
+
if ( typeof val === vtype ) {
|
|
3821
|
+
td_update = parse_util.subst(td_update,`@{${ky}}`,val)
|
|
3822
|
+
delete el[ky] // may test to see if something is not sets
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
//
|
|
3826
|
+
return td_update
|
|
3827
|
+
}
|
|
3828
|
+
|
|
3829
|
+
|
|
3830
|
+
/**
|
|
3831
|
+
*
|
|
3832
|
+
* @param {object} resolver
|
|
3833
|
+
* @returns {string}
|
|
3834
|
+
*/
|
|
3835
|
+
assemble_subfile(resolver) {
|
|
3836
|
+
// return ""
|
|
3837
|
+
let {file,tree,data,recursive} = resolver
|
|
3838
|
+
//
|
|
3839
|
+
let tranformed_data = "" + data
|
|
3840
|
+
let var_assigns = this.parameterized_block_unification(file,recursive,tree)
|
|
3841
|
+
if ( var_assigns && (typeof var_assigns === "object") ) {
|
|
3842
|
+
//
|
|
3843
|
+
let executables = recursive.executables
|
|
3844
|
+
let required_values = recursive.params_def
|
|
3845
|
+
if ( executables && required_values ) {
|
|
3846
|
+
for ( let execu of executables ) {
|
|
3847
|
+
let replace_form = execu.replace
|
|
3848
|
+
let cond = execu.condition
|
|
3849
|
+
let vname = cond.variable
|
|
3850
|
+
let case_to_use = ""
|
|
3851
|
+
if ( required_values[vname] !== undefined ) {
|
|
3852
|
+
case_to_use = execu.positive_exec.pos
|
|
3853
|
+
} else {
|
|
3854
|
+
case_to_use = execu.negative_exec.neg
|
|
3855
|
+
}
|
|
3856
|
+
tranformed_data = tranformed_data.replace(replace_form,case_to_use)
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
//
|
|
3860
|
+
tranformed_data = this.var_substitution(resolver,var_assigns,tranformed_data)
|
|
3861
|
+
}
|
|
3862
|
+
//
|
|
3863
|
+
return tranformed_data
|
|
3864
|
+
}
|
|
3865
|
+
|
|
3866
|
+
|
|
3867
|
+
/**
|
|
3868
|
+
* This is a test...
|
|
3869
|
+
*
|
|
3870
|
+
* @param {object} vset
|
|
3871
|
+
* @param {string} data
|
|
3872
|
+
*/
|
|
3873
|
+
resolve_with_configuration(vset,data) {
|
|
3874
|
+
let vsource = Object.assign({},this.global_variable_values)
|
|
3875
|
+
let t_data = this.var_substitution(vset,vsource,data)
|
|
3876
|
+
//
|
|
3877
|
+
this.vars_unset_in_run = Object.assign({},this.vars_unset_in_run,vsource)
|
|
3878
|
+
//
|
|
3879
|
+
return t_data
|
|
3880
|
+
}
|
|
3881
|
+
|
|
3882
|
+
|
|
3883
|
+
/**
|
|
3884
|
+
* This is supposed to be the starting point for calling variable instantiation
|
|
3885
|
+
* on the blocks at the skeleton level. The level_1_parameterized_file parameter
|
|
3886
|
+
* should be the expansion that is the value keyed by the file name in the skeleton map.
|
|
3887
|
+
*
|
|
3888
|
+
* @param {string} file_key
|
|
3889
|
+
* @param {object} level_1_parameterized_file
|
|
3890
|
+
* @param {object} parameterization_map
|
|
3891
|
+
*/
|
|
3892
|
+
parameterized_block_unification(file_key,level_1_parameterized_file,parameterization_map) {
|
|
3893
|
+
let file_inputs = parameterization_map[file_key] // skeletal level parameterizations from loaded subfiles (e.g. key is file at the skeleton level)
|
|
3894
|
+
let l1pf = level_1_parameterized_file // already got it when getting file key (a description of what is in the file at the skeleton level)
|
|
3895
|
+
try {
|
|
3896
|
+
let required_values = l1pf.recursive.params_def // variable to types (sibling to executable if present)
|
|
3897
|
+
let executables = l1pf.recursive.executables
|
|
3898
|
+
for ( let [vname,vtype] of Object.entries(required_values) ) {
|
|
3899
|
+
if ( vname[0] === '_' ) continue
|
|
3900
|
+
let resolver = file_inputs[vname] //
|
|
3901
|
+
if ( resolver[vtype] ) {
|
|
3902
|
+
let resolved_data = ""
|
|
3903
|
+
switch( vtype ) {
|
|
3904
|
+
case "file" : {
|
|
3905
|
+
if ( resolver.tree ) {
|
|
3906
|
+
resolved_data = this.assemble_subfile(resolver)
|
|
3907
|
+
} else {
|
|
3908
|
+
resolved_data = resolver.data
|
|
3909
|
+
}
|
|
3910
|
+
break;
|
|
3911
|
+
}
|
|
3912
|
+
case "list" : {
|
|
3913
|
+
let vtype_def = `_type<${vname}>`
|
|
3914
|
+
let el_var_map = required_values[vtype_def]
|
|
3915
|
+
//
|
|
3916
|
+
let data_tmplt = l1pf.data
|
|
3917
|
+
let start_loop = `<${vname}>`
|
|
3918
|
+
let end_loop = `</${vname}>`
|
|
3919
|
+
let loop_part = data_tmplt.substring(data_tmplt.indexOf(start_loop),data_tmplt.indexOf(end_loop))
|
|
3920
|
+
let dlist = resolver[vtype]
|
|
3921
|
+
for ( let el of dlist ) {
|
|
3922
|
+
let el_subst = this.var_substitution(el_var_map,el,loop_part)
|
|
3923
|
+
if ( el_subst ) {
|
|
3924
|
+
resolved_data += el_subst
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
//
|
|
3928
|
+
break;
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
|
|
3932
|
+
required_values[vname] = resolved_data
|
|
3933
|
+
}
|
|
3934
|
+
}
|
|
3935
|
+
|
|
3936
|
+
let data = l1pf.data
|
|
3937
|
+
if ( executables ) {
|
|
3938
|
+
for ( let execu of executables ) {
|
|
3939
|
+
let replace_form = execu.replace
|
|
3940
|
+
let cond = execu.condition
|
|
3941
|
+
let vname = cond.variable
|
|
3942
|
+
let case_to_use = ""
|
|
3943
|
+
if ( required_values[vname] !== undefined ) {
|
|
3944
|
+
case_to_use = execu.positive_exec.pos
|
|
3945
|
+
} else {
|
|
3946
|
+
case_to_use = execu.negative_exec.neg
|
|
3947
|
+
}
|
|
3948
|
+
data = data.replace(replace_form,case_to_use)
|
|
3949
|
+
}
|
|
3950
|
+
l1pf.data = data
|
|
3951
|
+
}
|
|
3952
|
+
l1pf.data = this.var_substitution(required_values,required_values,data)
|
|
3953
|
+
|
|
3954
|
+
data = l1pf.data
|
|
3955
|
+
let vset = {}
|
|
3956
|
+
vset = this.errant_variable_extraction(vset,data)
|
|
3957
|
+
if ( vset && Object.keys(vset).length ) {
|
|
3958
|
+
l1pf.data = this.resolve_with_configuration(vset,data)
|
|
3959
|
+
}
|
|
3960
|
+
|
|
3961
|
+
} catch (e) {}
|
|
3962
|
+
}
|
|
3963
|
+
|
|
3964
|
+
|
|
3965
|
+
|
|
3966
|
+
/**
|
|
3967
|
+
*
|
|
3968
|
+
* load the skeletons identified in the top level configuration.
|
|
3969
|
+
* Use `load_skeletons` to get the files and a list of output keys (file names)
|
|
3970
|
+
* which will be the templates stored with the concerns.
|
|
3971
|
+
*
|
|
3972
|
+
* The `out_name_list` will be a list whose index elements correspond to the index
|
|
3973
|
+
* of output objects in the configure `output` object. a list of concerns can be found
|
|
3974
|
+
* in the targets field of one of the output objects. For each conern the skeletons
|
|
3975
|
+
* they require will be gathered.
|
|
3976
|
+
*
|
|
3977
|
+
* During the process of gathering, skeletons per concern, the keleton unification process
|
|
3978
|
+
* will look recursively for skeleton components of higher level files and add them to the list
|
|
3979
|
+
* of skeletons per concern.
|
|
3980
|
+
*
|
|
3981
|
+
* The `skeleton_unification` method may coallese imports of JavaScript into directories meant to be
|
|
3982
|
+
* targeted by bundlers. (Some source code is destined to the web page.)
|
|
3983
|
+
*
|
|
3984
|
+
*/
|
|
3985
|
+
async skeleton_unification() {
|
|
3986
|
+
//
|
|
3987
|
+
// load relevant files (identified by the configuration) // skel_to_concerns is also global member
|
|
3988
|
+
let all_skeletons = await this.load_skeletons() // get the files and create `skel_to_concerns` map
|
|
3989
|
+
await this.load_name_drops_db() // loads this db only
|
|
3990
|
+
//
|
|
3991
|
+
// skel_to_concerns is added because each concern will have different structural demands
|
|
3992
|
+
// where page structure can be derived from cannonical descriptions (later maybe AI normalization)
|
|
3993
|
+
let parsed_skels = await this.skeleton_parsing(all_skeletons) // injest structure of all skeletons in this map
|
|
3994
|
+
await this.name_parameters_output() // after parsing deal with the database entries for each skeleton and make
|
|
3995
|
+
// a version of the DB which may be customized before taking the next step (phase 2)
|
|
3996
|
+
|
|
3997
|
+
return parsed_skels
|
|
3998
|
+
}
|
|
3999
|
+
|
|
4000
|
+
|
|
4001
|
+
}
|
|
4002
|
+
|
|
4003
|
+
|
|
4004
|
+
|
|
4005
|
+
module.exports = SkelToTemplate
|