github.com/influx6/npkg@v0.8.8/njs/njs.go (about) 1 // Package njs exists to provide a simple javascript text based code generation, exposing 2 // composable functions that can be used to create generated javascript code. 3 // 4 // Code is taken from https://github.com/mistermoe/js-code-generator. 5 // 6 package njs 7 8 import ( 9 "regexp" 10 "strings" 11 12 "github.com/influx6/npkg/nerror" 13 ) 14 15 const ( 16 numTabs = 1 17 numSpacesPerTab = 4 18 ) 19 20 var alphabets = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", 21 "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"} 22 23 // Gen represents a generated code with attached input data. 24 type Gen struct { 25 Data interface{} 26 Code string 27 } 28 29 // WrapWithData will wrap provided code and attached 30 // giving data object into it returning a Gen object. 31 func WrapWithData(code string, data interface{}) Gen { 32 return Gen{Data: data, Code: code} 33 } 34 35 // Args returns a joined string containing all values as comma seperated 36 // values within returned string. 37 func Args(args ...string) string { 38 return strings.Join(args, ", ") 39 } 40 41 // Join joins collection of string with newlines. 42 func Join(cols ...string) string { 43 return JoinWith("\n", cols...) 44 } 45 46 // JoinWith joins giving collection of strings with combiner. 47 func JoinWith(combiner string, cols ...string) string { 48 return CleanCode(strings.Join(cols, combiner)) 49 } 50 51 type Producer func() string 52 type Action func(builder *strings.Builder) error 53 type ActionProducer func(builder *strings.Builder, producers ...Producer) 54 55 // ComposeProducer composes series of string producers into a single Action 56 // to be applied to a giving string.Builder instance. 57 func ComposeProducers(indent bool, producers ...Producer) Action { 58 return func(builder *strings.Builder) error { 59 var total = len(producers) 60 for index, producer := range producers { 61 if _, err := builder.WriteString(producer()); err != nil { 62 return nerror.WrapOnly(err) 63 } 64 if indent && index < total-1 { 65 builder.WriteString("\n") 66 } 67 } 68 return nil 69 } 70 } 71 72 // ComposeActions composes series of Actions into a single action 73 // to be applied to a giving string.Builder instance. 74 func ComposeActions(actions ...Action) Action { 75 return func(builder *strings.Builder) error { 76 for _, action := range actions { 77 if err := action(builder); err != nil { 78 return nerror.WrapOnly(err) 79 } 80 } 81 return nil 82 } 83 } 84 85 type Var struct { 86 Name string 87 Value string 88 } 89 90 /* 91 @param Object data { 92 {string} name, 93 {string} value (optional) 94 } 95 */ 96 func Variable(data Var) string { 97 var code = `var ` + data.Name + `` 98 if data.Value != "" { 99 return CleanCode(code + ` = ` + data.Value + `;`) 100 } 101 return CleanCode(code + `;`) 102 } 103 104 /* 105 @param Object data { 106 {string} name, 107 {string} value 108 } 109 */ 110 func ReAssignVariable(data Var) string { 111 return CleanCode(`` + data.Name + ` = ` + data.Value + `;`) 112 } 113 114 /* 115 @param Object data { 116 {string} name, 117 {string} value (Optional) 118 } 119 */ 120 func IncrementVariable(data Var) string { 121 var code string 122 if data.Value != "" { 123 code = `` + data.Name + ` += ` + data.Value + `;` 124 } else { 125 code = `` + data.Name + `++` 126 } 127 return CleanCode(code) 128 } 129 130 /* 131 @param Object data { 132 {string} name, 133 {string} value (Optional) 134 } 135 */ 136 func DecrementVariable(data Var) string { 137 var code string 138 if data.Value != "" { 139 code = `` + data.Name + ` -= ` + data.Value + `;` 140 } else { 141 code = `` + data.Name + `--` 142 } 143 return CleanCode(code) 144 } 145 146 type Func struct { 147 Name string 148 Args string 149 Body string 150 Async bool 151 Static bool 152 } 153 154 /* 155 @param Object data { 156 {string} name, 157 {array} args (Optional), 158 {bool} async 159 {func} body 160 } 161 @returns { 162 data: data, 163 code: code 164 } 165 */ 166 func VarFunction(data Func) string { 167 var code = `var ` + data.Name + ` = ` 168 if data.Async { 169 code += `async ` 170 } 171 code += `function(` + data.Args + `) {` + "\n" + 172 `` + data.Body + "\n" + `};` 173 return Indent(code) 174 } 175 176 /* 177 @param Object data { 178 {string} name, 179 {array} args (Optional), 180 {bool} async 181 {func} body 182 } 183 @returns { 184 data: data, 185 code: code 186 } 187 */ 188 func Function(data Func) string { 189 var code string 190 if data.Static { 191 code += `static ` 192 } 193 194 if data.Async { 195 code += `async ` 196 } 197 198 if !data.Static { 199 code += `function ` 200 } 201 202 code += data.Name + `(` + data.Args + `) {` + "\n" + 203 `` + data.Body + "\n" + `};` 204 return Indent(code) 205 } 206 207 func FunctionCall(data Func) string { 208 var code string 209 if data.Async { 210 code += `await ` 211 } 212 code += data.Name + `(` + data.Args + `);` 213 return CleanCode(code) 214 } 215 216 /* 217 @param Object data { 218 {string} type 219 {array} args 220 } 221 222 @returns Object { 223 {string} code 224 } 225 */ 226 func NewInstance(data struct { 227 Type string 228 Args string 229 }) string { 230 var code = `new ` + data.Type 231 code += `(` + data.Args + `);` 232 return CleanCode(code) 233 } 234 235 func JSONParse(args string) string { 236 var code = `JSON.parse(` + args + `);` 237 return Indent(code) 238 } 239 240 func JSONStringify(args string) string { 241 var code = `JSON.stringify(` + args + `);` 242 return Indent(code) 243 } 244 245 func ChainObject(objName string) string { 246 return objName + `.` 247 } 248 249 func Self() string { 250 return ChainObject(`self`) 251 } 252 253 func Window() string { 254 return ChainObject(`window`) 255 } 256 257 func ParseInt(args string) string { 258 return FunctionCall(Func{ 259 Name: "parseInt", 260 Args: args, 261 Body: "", 262 Async: false, 263 }) 264 } 265 266 func ParseFloat(args string) string { 267 return FunctionCall(Func{ 268 Name: "parseFloat", 269 Args: args, 270 Body: "", 271 Async: false, 272 }) 273 } 274 275 func Math() string { 276 return ChainObject(`Math`) 277 } 278 279 func This() string { 280 return ChainObject(`this`) 281 } 282 283 func Object(body string) string { 284 var code = `{` + "\n" + body + "\n" + `};` 285 return Indent(code) 286 } 287 288 type Obj struct { 289 DotNotation bool 290 PropName string 291 ObjName string 292 } 293 294 /* 295 @param Object data { 296 {string} objName (Optional. Defaults to "this") 297 {string} propName, 298 {string} value, 299 {boolean} dotNotation 300 } 301 */ 302 func ObjectPropertyName(data Obj) string { 303 var code string 304 if data.DotNotation == false { 305 if data.ObjName == "" { 306 code = `this["` 307 } else { 308 code = data.ObjName + `["` 309 } 310 311 code += data.PropName + `"] ` 312 return CleanCode(code) 313 } 314 315 if data.ObjName == "" { 316 code = `this.` + data.PropName 317 } else { 318 code = data.ObjName + `.` + data.PropName 319 } 320 321 return CleanCode(code) 322 } 323 324 type ObjWithValue struct { 325 Obj 326 Value string 327 } 328 329 /* 330 @param Object data { 331 {string} objName (Optional. Defaults to "this") 332 {string} propName, 333 {string} value, 334 {boolean} dotNotation 335 } 336 */ 337 func ObjectPropertyAssignment(data ObjWithValue) string { 338 var code string 339 if data.DotNotation == false { 340 if data.ObjName == "" { 341 code = `this["` 342 } else { 343 code = data.ObjName + `["` 344 } 345 346 code += data.PropName + `"] = ` + data.Value + `;` 347 //name = ``+ data.ObjName +`["`+ data.PropName +`"]`; 348 return CleanCode(code) 349 } 350 351 if data.ObjName == "" { 352 code = `this.` + data.PropName + ` = ` + data.Value + `;` 353 } else { 354 code = data.ObjName + `.` + data.PropName + ` = ` + data.Value + `;` 355 } 356 357 return CleanCode(code) 358 } 359 360 type ObjFuncCall struct { 361 ObjectName string 362 FuncName string 363 Args string 364 Async bool 365 } 366 367 type ObjFunc struct { 368 ObjFuncCall 369 Body string 370 } 371 372 /* 373 @param Object data { 374 {string} objName (Optional. Defaults to "this") 375 {string} funcName, 376 {array} args (Optional), 377 {bool} async 378 {func} body 379 } 380 */ 381 func ObjectFunction(data ObjFunc) string { 382 var code string 383 if data.ObjectName == "" { 384 code = `this.` + data.FuncName 385 } else { 386 code = data.ObjectName + `.` + data.FuncName 387 } 388 389 code += data.FuncName + ` = ` 390 if data.Async { 391 code += `async ` 392 } 393 394 code += `function(` + data.Args + `) {` + "\n" + 395 `` + data.Body + `` + "\n" + 396 `};` 397 398 return Indent(code) 399 } 400 401 /* 402 @param Object data { 403 {string} objName 404 {string} funcName, 405 {bool} async 406 {array} args 407 } 408 */ 409 func ObjectFunctionCall(data ObjFunc) string { 410 var code string 411 if data.Async { 412 code += `await ` 413 } 414 415 if data.ObjectName == "" { 416 code = `this.` + data.FuncName 417 } else { 418 code = data.ObjectName + `.` + data.FuncName 419 } 420 421 code += `(` + data.Args + `);` 422 return CleanCode(code) 423 } 424 425 /* 426 @param Object data { 427 {string} condition, 428 {func} body 429 } 430 */ 431 func IfStatement( 432 condition string, 433 body string, 434 ) string { 435 var code = `if (` + condition + `) {` + "\n" + 436 `` + body + `` + "\n" + 437 `}` 438 439 return Indent(code) 440 } 441 442 /* 443 @param Object data { 444 {string} condition, 445 {func} body 446 } 447 */ 448 func ElseIfStatement( 449 condition string, 450 body string, 451 ) string { 452 var code = `else if (` + condition + `) {` + "\n" + 453 `` + body + `` + "\n" + 454 `}` 455 456 return Indent(code) 457 } 458 459 /* 460 @param Object data { 461 {func} body 462 } 463 */ 464 func ElseStatement(body string) string { 465 var code = `else {` + "\n" + 466 `` + body + `` + "\n" + 467 `}` 468 469 return Indent(code) 470 } 471 472 type Loop struct { 473 StartCondition string 474 StopCondition string 475 IncrementAction string 476 Body string 477 } 478 479 /* 480 @param Object data { 481 {string} startCondition, 482 {string} stopCondition, 483 {string} incrementAction, 484 {func} body 485 } 486 */ 487 func ForLoop(data Loop) string { 488 var code = `for (` + data.StartCondition + `; ` + data.StopCondition + `; ` + data.IncrementAction + `) {` + "\n" + 489 `` + data.Body + `` + "\n" + 490 `}` 491 492 return Indent(code) 493 } 494 495 type Class struct { 496 Name string 497 Extends string 498 Body string 499 } 500 501 func ClassBlock(data Class) string { 502 var code = `class ` + data.Name 503 if data.Extends != "" { 504 code += ` extends ` + data.Extends + ` ` 505 } 506 code += ` {` + "\n" + data.Body + "\n" + `};` 507 return Indent(code) 508 } 509 510 /* 511 @param Object data { 512 {func} body, 513 } 514 */ 515 func TryBlock(body string) string { 516 var code = `try {` + "\n" + body + "\n" + `}` 517 return Indent(code) 518 } 519 520 /* 521 @param Object data { 522 {string} arg 523 {func} body, 524 } 525 */ 526 func CatchBlock( 527 exception string, 528 body string, 529 ) string { 530 var code = `catch(` 531 if exception != "" { 532 code += exception 533 } 534 535 code += `) {` + "\n" 536 code += `` + body + `` + "\n" + `}` 537 return Indent(code) 538 } 539 540 // Promise returns new Promise with resolve and reject function parameter. 541 func Promise(args string, body string) string { 542 var code = `new Promise((resolve, reject) => {` + "\n" + body + "\n" + `})` 543 return Indent(code) 544 } 545 546 func ManyPromise(promiseList string) string { 547 return Indent(`Promise.all([` + "\n" + promiseList + "\n" + `]);`) 548 } 549 550 func PromiseThen(args string, body string) string { 551 var code = `.then((` + args + `) => {` + "\n" + body + "\n" + `})` 552 return Indent(code) 553 } 554 555 func PromiseCatch(err string, body string) string { 556 var code = `.catch((` + err + `) => {` + "\n" + body + "\n" + `})` 557 return Indent(code) 558 } 559 560 /* 561 @param Object data { 562 {string} name 563 {array} args (optional) 564 } 565 */ 566 func ChainFunction(name string, args string) string { 567 return `.` + name + `(` + args + `)` 568 } 569 570 /* 571 @param Object data { 572 {string} value 573 } 574 */ 575 func ReturnStatement(value string) string { 576 return `return ` + value + `;` 577 } 578 579 /* 580 @param { array<string> } args 581 */ 582 func ConsoleLog(args ...string) string { 583 var code = `console.log(` + Args(args...) + `);` 584 return code + `);` 585 } 586 587 type TextBlock struct { 588 Text string 589 Block bool 590 } 591 592 /* 593 @param Object data { 594 {string} text, 595 {boolean} block (Optional. Defaults to false.) 596 } 597 */ 598 func Comment(data TextBlock) string { 599 if data.Block { 600 var code = `/*` + "\n" + 601 `` + data.Text + `` + "\n" + 602 `*/` 603 return Indent(code) 604 } 605 606 return `// ` + data.Text + `` 607 } 608 609 /* 610 @param {string} code 611 612 @description: 613 Takes a rendered code and indents lines 2 through (n - 2), 614 where n is the number of lines 615 616 @returns: {string} 617 */ 618 func Indent(code string) string { 619 var items = strings.Split(code, "\n") 620 var mapped = make([]string, len(items)) 621 for i := 0; i < len(items); i++ { 622 var line = items[i] 623 if i == 0 || i == len(items)-1 { 624 mapped = append(mapped, GenerateTabs(0)+CleanCode(line)) 625 continue 626 } 627 mapped = append(mapped, GenerateTabs(numTabs)+CleanCode(line)) 628 } 629 return strings.Join(mapped, "\n") 630 } 631 632 // AddIndent returns a new line feed character. 633 func AddIndent() string { 634 return "\n" 635 } 636 637 /* 638 @param: {integer} numTabs 639 @description: generates a string containing the number of tabs requested 640 @returns: {string} 641 */ 642 func GenerateTabs(numTabs int) string { 643 var tabs = "" 644 var tabCount = numSpacesPerTab * numTabs 645 for i := 0; i < tabCount; i += 1 { 646 tabs += " " 647 } 648 return tabs 649 } 650 651 var cleanRegExp = regexp.MustCompile(";{2,}") 652 653 /* 654 @param: {string} line 655 @description: Cleans a line of code. 656 @returns: {string} 657 */ 658 func CleanCode(line string) string { 659 return cleanRegExp.ReplaceAllString(line, ";") 660 } 661 662 var iteratorPos = 8 663 664 /* 665 UniqueIteratorName 666 @description: 667 returns a name for an iterator variable that hasn't been used. 668 An example of an iterator variable would be `var i` in a for loop. 669 This func Is used to prevent undesired behavior in a nested 670 loop. Starts at 'i' 671 672 @returns: {string} iterator 673 */ 674 func UniqueIteratorName() string { 675 var numChars = 1 676 var idx = iteratorPos 677 678 var iterator = "" 679 680 if iteratorPos >= 26 { 681 if iteratorPos%26 == 0 { 682 numChars = (iteratorPos / 26) + 1 683 } else { 684 numChars = iteratorPos / 26 685 } 686 idx = iteratorPos % 26 687 } 688 689 for i := 0; i < numChars; i++ { 690 iterator += alphabets[idx] 691 } 692 693 iteratorPos += 1 694 return iterator 695 } 696 697 // ResetIteratorPos resets the position of the current iterator to return back to 'i' 698 func ResetIteratorPos() { 699 iteratorPos = 8 700 }