UNPKG

5.02 kBJavaScriptView Raw
1"use strict";
2
3/**
4 * Expand variables and return the resulting object.
5 * A variable is writtent like this `%VarName%`. And it set like
6 * this: `%VarName%: "blabla"`. The value of a variable can be of any
7 * type. Variables defined in the value of another variables are
8 * expanded only when the parent variable is expanded.
9 *
10 * @example:
11 * {View SECTION
12 * %Button%: {tfw.view.button type: %Type% }
13 * %Type%: primary
14 * [
15 * {ARTICLE class: thm-bg3 %Button%}
16 * {ARTICLE class: thm-bgSL %Button%}
17 * ]}
18 *
19 * @description
20 * It also possible to concatenate variables:
21 * @example
22 * view.attribs: {
23 * duration: {String}
24 * duration-selected: {Boolean true}
25 * level: {String}
26 * level-selected: {Boolean true}
27 * }
28 * %selected%: "-selected"
29 * %Component%: {tfw.view.checkbox value: {Bind %type%+%selected%} content: {Bind %type%}}
30 * [
31 * {%Component% %type%: duration}
32 * {%Component% %type%: level}
33 * ]
34 */
35module.exports = function ( def, moduleName, path ) {
36 try {
37 const
38 context = { vars: {}, moduleName, path },
39 preprocessedDef = process( context, def );
40 return preprocessedDef;
41 } catch ( ex ) {
42 throw Error( `...in boilerplate.preprocessor( ${moduleName}, ${path} )\n${ex}` );
43 }
44};
45
46const
47 RX_VAR_NAME = /^%[a-zA-Z]+%$/,
48 RX_VAR_NAME_CONCAT = /^%[a-zA-Z]+%(\+%[a-zA-Z]+%)+$/;
49
50/**
51 * Return a copy of `obj` after processing it.
52 */
53function process( ctx, obj ) {
54 try {
55 if ( Array.isArray( obj ) ) return processArray( ctx, obj );
56 switch ( typeof obj ) {
57 case "string":
58 return processString( ctx, obj );
59 case "object":
60 return processObject( ctx, obj );
61 default:
62 return obj;
63 }
64 } catch ( ex ) {
65 fatal( ctx, ex );
66 }
67}
68
69
70function processArray( ctx, arr ) {
71 try {
72 const preprocessedArray = arr.map( process.bind( null, ctx ) );
73 return preprocessedArray.filter( x => x !== undefined );
74 } catch ( ex ) {
75 throw Error( `...in processArray\n${ex}` );
76 }
77}
78
79
80function processString( ctx, str ) {
81 try {
82 if ( RX_VAR_NAME.test( str ) ) {
83 var value = ctx.vars[ str ];
84 if ( typeof value === 'undefined' )
85 throw "Undefined variable " + str + "!";
86 return value;
87 }
88 if ( RX_VAR_NAME_CONCAT.test( str ) ) {
89 var variables = str.split( "+" );
90 return variables.map( varName => {
91 var value = ctx.vars[ varName ];
92 if ( typeof value === 'undefined' )
93 throw "Undefined variable " + str + "!";
94 return value;
95 } ).join( "" );
96 } else {
97 return str;
98 }
99 } catch ( ex ) {
100 throw Error( `...in processString(${JSON.stringify( str )})\n${ex}` );
101 }
102}
103
104
105function processObject( ctx, obj ) {
106 try {
107 if ( obj === null ) return null;
108
109 if ( typeof obj[ 0 ] === 'string' && RX_VAR_NAME.test( obj[ 0 ] ) )
110 return processExtension( ctx, obj );
111 const preprocessedObject = {};
112 // Parse variable settings first.
113 for ( let key in obj ) {
114 if ( RX_VAR_NAME.test( key ) ) {
115 ctx.vars[ key ] = obj[ key ];
116 }
117 }
118 // Parse other attributes.
119 for ( let key in obj ) {
120 if ( RX_VAR_NAME.test( key ) ) continue;
121 const val = process( ctx, obj[ key ] );
122 preprocessedObject[ key ] = val;
123 }
124 return preprocessedObject;
125 } catch ( ex ) {
126 throw Error( `...in processObject()\n${ex}` );
127 }
128}
129
130
131/**
132 * @example
133 * {%Panel% %background%: "thm-bgP"}
134 */
135function processExtension( ctx, obj ) {
136 try {
137 const
138 varName = obj[ 0 ],
139 baseObject = makeCopyOf( ctx.vars[ varName ] );
140 if ( typeof baseObject === 'undefined' ) {
141 throw Error( `Undefined variable for extension {${varName}}!` );
142 }
143 // Parse variable settings first.
144 for ( const key in obj ) {
145 if ( RX_VAR_NAME.test( key ) ) {
146 ctx.vars[ key ] = obj[ key ];
147 }
148 }
149 // Parse other attributes.
150 for ( const key in obj ) {
151 if ( key === "0" ) continue;
152 if ( RX_VAR_NAME.test( key ) ) continue;
153 const val = process( ctx, obj[ key ] );
154 baseObject[ key ] = val;
155 }
156 return processObject( ctx, baseObject );
157 } catch ( ex ) {
158 throw Error( `...in processExtension()\n${ex}` );
159 }
160}
161
162
163function fatal( ctx, message ) {
164 throw "Error while preprocessig module " + ctx.module + ":\n" +
165 message + "\n" +
166 "Defined variables at this point:\n" +
167 Object.keys( ctx.vars )
168 .map( k => " " + k + ": " + JSON.stringify( ctx.vars[ k ] ) )
169 .join( "\n" );
170}
171
172
173function makeCopyOf( obj ) {
174 return JSON.parse( JSON.stringify( obj ) );
175}
\No newline at end of file