nsp-server-pages 0.1.1 → 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.
Files changed (40) hide show
  1. package/cjs/index.js +6 -1
  2. package/cjs/package.json +1 -0
  3. package/cjs/src/app.js +20 -20
  4. package/cjs/src/catch.js +10 -9
  5. package/cjs/src/loaders.js +1 -1
  6. package/cjs/src/mount.js +6 -6
  7. package/cjs/src/parser/attr.js +92 -0
  8. package/{src/parse-el.js → cjs/src/parser/el.js} +15 -28
  9. package/cjs/src/parser/jsp.js +88 -0
  10. package/cjs/src/parser/scriptlet.js +47 -0
  11. package/cjs/src/parser/tag.js +130 -0
  12. package/{src/parse-text.js → cjs/src/parser/text.js} +13 -25
  13. package/cjs/src/stack-store.js +31 -0
  14. package/cjs/src/taglib.js +7 -7
  15. package/esm/index.js +2 -0
  16. package/esm/package.json +4 -0
  17. package/{src → esm/src}/app.js +19 -19
  18. package/{src → esm/src}/catch.js +10 -9
  19. package/{src → esm/src}/mount.js +6 -6
  20. package/esm/src/parser/attr.js +88 -0
  21. package/{cjs/src/parse-el.js → esm/src/parser/el.js} +11 -32
  22. package/esm/src/parser/jsp.js +83 -0
  23. package/esm/src/parser/scriptlet.js +43 -0
  24. package/esm/src/parser/tag.js +126 -0
  25. package/{cjs/src/parse-text.js → esm/src/parser/text.js} +9 -29
  26. package/esm/src/stack-store.js +27 -0
  27. package/{src → esm/src}/taglib.js +7 -7
  28. package/package.json +14 -13
  29. package/types/hooks.d.ts +103 -0
  30. package/{index.d.ts → types/index.d.ts} +62 -40
  31. package/cjs/src/parse-attr.js +0 -91
  32. package/cjs/src/parse-jsp.js +0 -206
  33. package/cjs/src/parse-scriptlet.js +0 -71
  34. package/index.js +0 -1
  35. package/src/parse-attr.js +0 -87
  36. package/src/parse-jsp.js +0 -201
  37. package/src/parse-scriptlet.js +0 -67
  38. /package/{src → esm/src}/bundle.js +0 -0
  39. /package/{src → esm/src}/concat.js +0 -0
  40. /package/{src → esm/src}/loaders.js +0 -0
package/cjs/index.js CHANGED
@@ -1 +1,6 @@
1
- exports.createNSP = require("./src/app.js").createNSP;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createNSP = void 0;
4
+ const app_js_1 = require("./src/app.js");
5
+ const createNSP = (options) => new app_js_1.App(options);
6
+ exports.createNSP = createNSP;
package/cjs/package.json CHANGED
@@ -1,3 +1,4 @@
1
1
  {
2
+ "main": "./index.js",
2
3
  "type": "commonjs"
3
4
  }
package/cjs/src/app.js CHANGED
@@ -1,21 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createNSP = void 0;
3
+ exports.App = void 0;
4
4
  const mount_js_1 = require("./mount.js");
5
5
  const loaders_js_1 = require("./loaders.js");
6
- const parse_jsp_js_1 = require("./parse-jsp.js");
6
+ const jsp_js_1 = require("./parser/jsp.js");
7
7
  const catch_js_1 = require("./catch.js");
8
8
  const bundle_js_1 = require("./bundle.js");
9
9
  const taglib_js_1 = require("./taglib.js");
10
10
  const concat_js_1 = require("./concat.js");
11
- const createNSP = (options) => new App(options);
12
- exports.createNSP = createNSP;
11
+ const stack_store_js_1 = require("./stack-store.js");
13
12
  class App {
14
13
  constructor(options) {
15
14
  this.loaders = [];
16
15
  this.tagMap = new Map();
17
16
  this.fnMap = new Map();
18
- this.listeners = new Map;
17
+ this.hooks = new Map();
19
18
  this.options = options = Object.create(options || null);
20
19
  if (!options.vName)
21
20
  options.vName = "v";
@@ -25,12 +24,12 @@ class App {
25
24
  options.storeKey = "#nsp";
26
25
  }
27
26
  hook(type, fn) {
28
- this.listeners.set(type, fn);
27
+ this.hooks.set(type, fn);
29
28
  }
30
- process(type, arg) {
31
- const fn = this.listeners.get(type);
29
+ process(type, ...args) {
30
+ const fn = this.hooks.get(type);
32
31
  if (fn)
33
- return fn(arg);
32
+ return fn.apply(this, args);
34
33
  }
35
34
  log(message) {
36
35
  const logger = this.options.logger || console;
@@ -46,11 +45,11 @@ class App {
46
45
  return fn;
47
46
  }
48
47
  addTagLib(tagLibDef) {
49
- (0, taglib_js_1.addTagLib)(this, tagLibDef);
48
+ taglib_js_1.addTagLib.call(this, tagLibDef);
50
49
  }
51
50
  tag(name, attr, ..._) {
52
51
  const bodyFn = (0, bundle_js_1.bundle)(arguments, 2);
53
- const tagFn = (0, taglib_js_1.prepareTag)(this, name, attr, bodyFn);
52
+ const tagFn = taglib_js_1.prepareTag.call(this, name, attr, bodyFn);
54
53
  return (0, catch_js_1.catchFn)(this, tagFn);
55
54
  }
56
55
  bundle(..._) {
@@ -58,37 +57,38 @@ class App {
58
57
  return (0, catch_js_1.catchFn)(this, fn);
59
58
  }
60
59
  parse(src) {
61
- return (0, parse_jsp_js_1.parseJSP)(this, src);
60
+ return new jsp_js_1.JSP(this, src);
62
61
  }
63
62
  mount(path, fn) {
64
- return (0, mount_js_1.mount)(this, path, fn);
63
+ return mount_js_1.mount.call(this, path, fn);
65
64
  }
66
65
  load(path) {
67
- return (0, mount_js_1.load)(this, path);
66
+ return mount_js_1.load.call(this, path);
68
67
  }
69
68
  loadJS(file) {
70
- const loader = this.jsLoader || (this.jsLoader = new loaders_js_1.JsLoader(this));
69
+ const loader = (this.jsLoader ??= new loaders_js_1.JsLoader(this));
71
70
  return loader.load(file);
72
71
  }
73
72
  loadJSP(file) {
74
- const loader = this.jspLoader || (this.jspLoader = new loaders_js_1.JspLoader(this));
73
+ const loader = (this.jspLoader ??= new loaders_js_1.JspLoader(this));
75
74
  return loader.load(file);
76
75
  }
77
76
  loadFile(file) {
78
- const loader = this.fileLoader || (this.fileLoader = new loaders_js_1.FileLoader(this));
77
+ const loader = (this.fileLoader ??= new loaders_js_1.FileLoader(this));
79
78
  return loader.load(file);
80
79
  }
81
- store(context, key, initFn) {
80
+ store(context, key) {
82
81
  if ("object" !== typeof context && context == null) {
83
82
  throw new Error("Context must be an object");
84
83
  }
85
84
  const { storeKey } = this.options;
86
- const map = context[storeKey] || (context[storeKey] = new Map());
85
+ const map = (context[storeKey] ??= new Map());
87
86
  let value = map.get(key);
88
87
  if (value == null) {
89
- value = initFn();
88
+ value = new stack_store_js_1.StackStore();
90
89
  map.set(key, value);
91
90
  }
92
91
  return value;
93
92
  }
94
93
  }
94
+ exports.App = App;
package/cjs/src/catch.js CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.catchFn = void 0;
4
- const to_xml_1 = require("to-xml");
5
4
  const isPromise = (v) => v && (typeof v.catch === "function");
6
- const escapeError = (e) => (0, to_xml_1.toXML)({ "#": (e?.message || String(e)) });
7
5
  const catchFn = (app, fn) => {
8
6
  return context => {
9
7
  try {
@@ -21,16 +19,19 @@ const catchFn = (app, fn) => {
21
19
  function errorHandler(e) {
22
20
  // just throw the error if it's already handled
23
21
  if (context != null) {
24
- const data = app.store(context, "error", () => ({}));
25
- if (data.error === e)
22
+ const store = app.store(context, "error");
23
+ if (store.find(err => (err === e))) {
26
24
  throw e;
27
- data.error = e;
25
+ }
26
+ store.open(e);
28
27
  }
29
- // call the error handler
28
+ // call the error hook
30
29
  const result = app.process("error", e, context);
31
- if (result != null)
32
- return result;
33
- return `<!--\n[ERR] ${escapeError(e)}\n-->`;
30
+ // if the hook returns nothing, throw the error
31
+ if (result == null)
32
+ throw e;
33
+ // if the hook returns a string, show it
34
+ return result;
34
35
  }
35
36
  };
36
37
  };
@@ -65,7 +65,7 @@ class JsLoader extends BaseLoader {
65
65
  })(async (file) => {
66
66
  const app = this.appRef.deref();
67
67
  app.log(`loading: ${file}`);
68
- const module = await import(file);
68
+ const module = await Promise.resolve(`${file}`).then(s => require(s));
69
69
  const name = getName(file);
70
70
  const fn = module[name];
71
71
  if (typeof fn !== "function") {
package/cjs/src/mount.js CHANGED
@@ -1,18 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.load = exports.mount = void 0;
4
- const mount = (app, match, fn) => {
4
+ function mount(match, fn) {
5
5
  const test = ("string" !== typeof match) ? match : {
6
6
  test: ((path) => path.startsWith(match))
7
7
  };
8
- app.loaders.push(!test ? fn : path => {
8
+ this.loaders.push(!test ? fn : path => {
9
9
  if (test.test(path))
10
10
  return fn(path);
11
11
  });
12
- };
12
+ }
13
13
  exports.mount = mount;
14
- const load = async (app, path) => {
15
- const { loaders } = app;
14
+ async function load(path) {
15
+ const { loaders } = this;
16
16
  const search = path.replace(/^[^?]*\??/, "");
17
17
  path = path.replace(/\?.*$/, "");
18
18
  path = path.replace(/^\/*/, "/");
@@ -30,5 +30,5 @@ const load = async (app, path) => {
30
30
  }
31
31
  return fn(context);
32
32
  };
33
- };
33
+ }
34
34
  exports.load = load;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Attr = void 0;
4
+ const text_js_1 = require("./text.js");
5
+ const isSafeKey = (key) => /^[A-Za-z_]\w+$/.test(key);
6
+ /**
7
+ * Parser for HTML tag attributes <tagName attr="value"/>
8
+ */
9
+ class Attr {
10
+ constructor(app, src) {
11
+ this.app = app;
12
+ this.src = app.process("before.parse.attr", src) ?? src;
13
+ }
14
+ /**
15
+ * Transpile HTML tag attributes to JavaScript source code
16
+ */
17
+ toJS(option) {
18
+ const { app, src } = this;
19
+ const js = app.process("parse.attr", src) ?? this._toJS(option);
20
+ return app.process("after.parse.attr", js) ?? js;
21
+ }
22
+ keys() {
23
+ return Object.keys(this.getIndex());
24
+ }
25
+ get(key) {
26
+ return this.getIndex()[key];
27
+ }
28
+ getIndex() {
29
+ let { index, src } = this;
30
+ if (index)
31
+ return index;
32
+ index = this.index = {};
33
+ if (!src)
34
+ return index;
35
+ src = src.replace(/^\s*<\S+\s*/s, "");
36
+ src = src.replace(/\s*\/?>\s*$/s, "");
37
+ src.replace(/([^\s='"]+)(\s*=(?:\s*"([^"]*)"|\s*'([^']*)'|([^\s='"]*)))?/g, (_, key, eq, v1, v2, v3) => {
38
+ if (eq) {
39
+ const value = unescapeXML(v1 || v2 || v3 || "");
40
+ index[key] = new text_js_1.Text(this.app, value).toJS({});
41
+ }
42
+ else {
43
+ const value = true;
44
+ index[key] = String(value);
45
+ }
46
+ return "";
47
+ });
48
+ return index;
49
+ }
50
+ /**
51
+ * Transpile HTML tag attributes to JavaScript source code
52
+ */
53
+ _toJS(option) {
54
+ const { app } = this;
55
+ const { indent } = app.options;
56
+ const spaces = +indent ? " ".repeat(+indent) : (indent ?? "");
57
+ const currentLF = option?.LF ?? "\n";
58
+ const nextLF = currentLF + spaces;
59
+ const keys = this.keys();
60
+ const items = keys.map(key => {
61
+ if (!isSafeKey(key)) {
62
+ key = JSON.stringify(key);
63
+ }
64
+ const value = this.get(key);
65
+ return `${key}: ${value}`;
66
+ });
67
+ // no arguments
68
+ if (!keys.length)
69
+ return 'null';
70
+ const js = items.join(`,${nextLF}`);
71
+ const trailingComma = (keys.length > 1) ? "," : "";
72
+ return `{${nextLF}${js}${trailingComma}${currentLF}}`;
73
+ }
74
+ }
75
+ exports.Attr = Attr;
76
+ const UNESCAPE = {
77
+ "&amp;": "&",
78
+ "&lt;": "<",
79
+ "&gt;": ">",
80
+ "&apos;": "'",
81
+ "&quot;": '"'
82
+ };
83
+ const unescapeXML = (str) => {
84
+ return str?.replace(/(&(?:lt|gt|amp|apos|quot|#(?:\d{1,6}|x[0-9a-fA-F]{1,5}));)/g, (str) => {
85
+ if (str[1] === "#") {
86
+ const code = (str[2] === "x") ? parseInt(str.substring(3), 16) : parseInt(str.substr(2), 10);
87
+ if (code > -1)
88
+ return String.fromCharCode(code);
89
+ }
90
+ return UNESCAPE[str] || str;
91
+ });
92
+ };
@@ -1,3 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EL = void 0;
1
4
  const trim = (str) => str.replace(/^\s+/s, "").replace(/\s+$/s, "");
2
5
  const wordMap = {
3
6
  and: "&&",
@@ -30,38 +33,23 @@ const itemRegExp = new RegExp(`(${itemRE})`, "s");
30
33
  /**
31
34
  * Simplified transformer for expression language
32
35
  */
33
- export const parseEL = (app, src) => new ElParser(app, src);
34
- class ElParser {
36
+ class EL {
35
37
  constructor(app, src) {
36
38
  this.app = app;
37
- this.src = src;
38
- //
39
- }
40
- /**
41
- * Compile ${EL} to JavaScript function instance
42
- */
43
- toFn() {
44
- const { app } = this;
45
- const { nspName, vName } = app.options;
46
- const js = this.toJS();
47
- try {
48
- const fn = Function(nspName, vName, `return ${js}`);
49
- return (context) => fn(app, context);
50
- }
51
- catch (e) {
52
- app.log("ElParser: " + js?.substring(0, 1000));
53
- throw e;
54
- }
39
+ src = trim(src);
40
+ this.src = app.process("before.parse.el", src) ?? src;
55
41
  }
56
42
  /**
57
43
  * Transpile ${EL} to JavaScript source code
58
44
  */
59
- toJS(_) {
60
- const { app } = this;
61
- const { nullish, prefilter, postfilter } = app.options;
62
- let src = trim(this.src);
63
- if (prefilter)
64
- src = prefilter(src);
45
+ toJS(option) {
46
+ const { app, src } = this;
47
+ const js = app.process("parse.el", src) ?? this._toJS(option);
48
+ return app.process("after.parse.el", js) ?? js;
49
+ }
50
+ _toJS(_) {
51
+ const { app, src } = this;
52
+ const { nullish } = app.options;
65
53
  if (src == null)
66
54
  return 'null';
67
55
  const array = src.split(itemRegExp);
@@ -98,8 +86,7 @@ class ElParser {
98
86
  }
99
87
  js = `${js} ?? ""`;
100
88
  }
101
- if (postfilter)
102
- js = postfilter(js);
103
89
  return js;
104
90
  }
105
91
  }
92
+ exports.EL = EL;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.jspToJS = exports.JSP = void 0;
4
+ const scriptlet_js_1 = require("./scriptlet.js");
5
+ const stack_store_js_1 = require("../stack-store.js");
6
+ const tag_js_1 = require("./tag.js");
7
+ /**
8
+ * Parser for JSP document
9
+ */
10
+ class JSP {
11
+ constructor(app, src) {
12
+ this.app = app;
13
+ this.src = app.process("before.parse.jsp", src) ?? src;
14
+ }
15
+ /**
16
+ * Transpile JSP document to JavaScript source code
17
+ */
18
+ toJS(option) {
19
+ const { app, src } = this;
20
+ const js = app.process("parse.jsp", src) ?? (0, exports.jspToJS)(app, src, option);
21
+ return app.process("after.parse.jsp", js) ?? js;
22
+ }
23
+ /**
24
+ * Compile JSP document to JavaScript function
25
+ */
26
+ toFn() {
27
+ const { app } = this;
28
+ const { nspName } = app.options;
29
+ const js = this.toJS();
30
+ try {
31
+ const fn = Function(nspName, `return ${js}`);
32
+ return fn(app);
33
+ }
34
+ catch (e) {
35
+ app.log("JspParser: " + js?.substring(0, 1000));
36
+ throw e;
37
+ }
38
+ }
39
+ }
40
+ exports.JSP = JSP;
41
+ const nameRE = `[A-Za-z][A-Za-z0-9]*`;
42
+ const stringRE = `"(?:\\\\[.]|[^\\\\"])*"|'(?:\\\\[.]|[^\\\\'])*'`;
43
+ const insideRE = `[^"']|${stringRE}`;
44
+ const tagRegExp = new RegExp(`(</?${nameRE}:(?:${insideRE})*?>)|(<%(?:${insideRE})*?%>)`, "s");
45
+ const jspToJS = (app, src, option) => {
46
+ const root = new tag_js_1.Tag(app);
47
+ const tree = new stack_store_js_1.StackStore(root);
48
+ const array = src.split(tagRegExp);
49
+ for (let i = 0; i < array.length; i++) {
50
+ const i3 = i % 3;
51
+ let str = array[i];
52
+ if (i3 === 1 && str) {
53
+ // taglib
54
+ const tag = new tag_js_1.Tag(app, str);
55
+ // close-tag
56
+ if (tag.isClose()) {
57
+ const closed = tree.close();
58
+ if (!closed) {
59
+ throw new Error(`invalid closing tag: </${tag.tagName}>`);
60
+ }
61
+ if (closed.tagName !== tag.tagName) {
62
+ throw new Error(`invalid closing tag: <${closed.tagName}></${tag.tagName}>`);
63
+ }
64
+ continue;
65
+ }
66
+ tree.get().append(tag);
67
+ // open-tag
68
+ if (tag.isOpen()) {
69
+ tree.open(tag);
70
+ }
71
+ }
72
+ else if (i3 === 2 && str) {
73
+ // <% scriptlet %>
74
+ const item = new scriptlet_js_1.Scriptlet(app, str);
75
+ tree.get().append(item);
76
+ }
77
+ else if (i3 === 0) {
78
+ // text node
79
+ tree.get().append(str);
80
+ }
81
+ }
82
+ const closed = tree.close();
83
+ if (closed !== root) {
84
+ throw new Error(`invalid closing tag: </${closed?.tagName}>`);
85
+ }
86
+ return root.toJS(option);
87
+ };
88
+ exports.jspToJS = jspToJS;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Scriptlet = void 0;
4
+ const typeMap = {
5
+ "<%-": "comment",
6
+ "<%@": "directive",
7
+ "<%!": "declaration",
8
+ "<%=": "expression",
9
+ };
10
+ /**
11
+ * Parser for Directive, Declaration, Scriptlet
12
+ * <%-- comment --%>
13
+ * <%@ directive %>
14
+ * <%! declaration(s) %>
15
+ * <% scriptlet %>
16
+ * <%= expression %>
17
+ */
18
+ class Scriptlet {
19
+ constructor(app, src) {
20
+ this.app = app;
21
+ const type = this.type = typeMap[src.substring(0, 3)] || "scriptlet";
22
+ this.src = app.process(`before.parse.${type}`, src) ?? src;
23
+ }
24
+ /**
25
+ * Transpile <% scriptlet %> to JavaScript source code
26
+ */
27
+ toJS(option) {
28
+ const { app, src, type } = this;
29
+ const js = app.process(`parse.${type}`, src) ?? this._toJS(option);
30
+ return app.process(`after.parse.${type}`, js) ?? js;
31
+ }
32
+ _toJS(option) {
33
+ const { app, type } = this;
34
+ const { nspName, vName } = app.options;
35
+ const currentLF = option?.LF ?? "\n";
36
+ let { src } = this;
37
+ if (type === "comment") {
38
+ src = src.replace(/[ \t]*[\r\n]+/sg, `${currentLF}// `);
39
+ return `// ${src}`;
40
+ }
41
+ app.log(`${type} found: ${src?.substring(0, 1000)}`);
42
+ src = /`|\$\{/.test(src) ? JSON.stringify(src) : "`" + src + "`";
43
+ src = `${vName} => ${nspName}.process("${type}", ${src}, ${vName})`;
44
+ return src;
45
+ }
46
+ }
47
+ exports.Scriptlet = Scriptlet;
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Tag = void 0;
4
+ const text_js_1 = require("./text.js");
5
+ const attr_js_1 = require("./attr.js");
6
+ const emptyText = {
7
+ '""': true,
8
+ "''": true,
9
+ "``": true,
10
+ "null": true,
11
+ "undefined": true,
12
+ "": true,
13
+ };
14
+ const isTranspiler = (v) => ("function" === typeof v?.toJS);
15
+ /**
16
+ * Root node or an taglib node
17
+ */
18
+ class Tag {
19
+ constructor(app, src) {
20
+ this.app = app;
21
+ this.src = src;
22
+ this.children = [];
23
+ this.tagName = src?.match(/^<\/?([^\s=/>]+)/)?.[1];
24
+ }
25
+ append(node) {
26
+ this.children.push(node);
27
+ }
28
+ isOpen() {
29
+ return !/\/\s*>$/.test(this.src);
30
+ }
31
+ isClose() {
32
+ return /^<\//.test(this.src);
33
+ }
34
+ getBodyJS(option) {
35
+ const { app } = this;
36
+ const { indent, trimSpaces, vName } = app.options;
37
+ const spaces = +indent ? " ".repeat(+indent) : (indent ?? "");
38
+ const currentLF = option?.LF ?? "\n";
39
+ const nextLF = currentLF + spaces;
40
+ const { children } = this;
41
+ const args = children.map(item => {
42
+ if (isTranspiler(item)) {
43
+ return item.toJS({ LF: nextLF });
44
+ }
45
+ else if (!/\S/.test(item)) {
46
+ // item with only whitespace
47
+ return (trimSpaces !== false) ? '""' : JSON.stringify(item);
48
+ }
49
+ else {
50
+ if (trimSpaces !== false) {
51
+ item = item.replace(/^\s*[\r\n]/s, "\n");
52
+ item = item.replace(/\s*[\r\n]\s*$/s, "\n");
53
+ item = item.replace(/^[ \t]+/s, " ");
54
+ item = item.replace(/[ \t]+$/s, " ");
55
+ }
56
+ let js = new text_js_1.Text(app, item).toJS({ LF: nextLF });
57
+ if (/\(.+?\)|\$\{.+?}/s.test(js)) {
58
+ js = `${vName} => ${js}`; // array function
59
+ }
60
+ return js;
61
+ }
62
+ }).filter(v => !emptyText[v]);
63
+ // empty body
64
+ if (!children.length) {
65
+ return "";
66
+ }
67
+ // keep a single empty string at least if all arguments are trimmed
68
+ if (!args.length) {
69
+ args.push('""');
70
+ }
71
+ const last = args.length - 1;
72
+ args.forEach((v, idx) => {
73
+ const isComment = /^\/\/[^\n]*$/s.test(v);
74
+ if (idx !== last && !isComment) {
75
+ args[idx] += ",";
76
+ }
77
+ else if (idx === last && isComment) {
78
+ args[idx] += currentLF;
79
+ }
80
+ });
81
+ const bodyL = /^`\n/s.test(args.at(0)) ? "" : nextLF;
82
+ const bodyR = /(\n`|[)\s])$/s.test(args.at(-1)) ? "" : currentLF;
83
+ return bodyL + args.join(nextLF) + bodyR;
84
+ }
85
+ /**
86
+ * Transpile JSP document to JavaScript source code
87
+ */
88
+ toJS(option) {
89
+ const { app, tagName } = this;
90
+ const { indent, nspName } = app.options;
91
+ // root element
92
+ if (!tagName) {
93
+ const bodyJS = this.getBodyJS(option);
94
+ return `${nspName}.bundle(${bodyJS})`; // root element
95
+ }
96
+ if (this.isClose())
97
+ return; // invalid
98
+ const commentJS = this.getCommentJS(option);
99
+ const attr = new attr_js_1.Attr(app, this.src);
100
+ const body = this.getBodyJS(option);
101
+ const spaces = +indent ? " ".repeat(+indent) : (indent ?? "");
102
+ const currentLF = option?.LF ?? "\n";
103
+ const nextLF = currentLF + spaces;
104
+ const tagOption = { LF: (body ? nextLF : currentLF) };
105
+ const type = `parse.tag.${tagName}`;
106
+ const def = { app, name: tagName, attr, body, LF: currentLF, nextLF };
107
+ const tagJS = app.process(type, def) ?? this.getTagJS(def, tagOption);
108
+ return commentJS ? commentJS + tagJS : tagJS;
109
+ }
110
+ getCommentJS(option) {
111
+ const { app, src } = this;
112
+ if (!app.options.comment)
113
+ return;
114
+ const currentLF = option?.LF ?? "\n";
115
+ return `// ${src?.replace(/\s*[\r\n]\s*/g, " ") ?? ""}${currentLF}`;
116
+ }
117
+ getTagJS(def, option) {
118
+ const { app, tagName } = this;
119
+ const { nspName, vName } = app.options;
120
+ const attrRaw = def.attr.toJS(option);
121
+ // transpile attributes to array function if they include variables
122
+ const hasVars = /\(.+?\)|\$\{.+?}/s.test(attrRaw);
123
+ const attrJS = hasVars ? `${vName} => (${attrRaw})` : attrRaw;
124
+ const nameJS = JSON.stringify(tagName);
125
+ const hasAttr = /:/.test(attrRaw);
126
+ const restJS = def.body ? (`, ${attrJS}, ${def.body}`) : (hasAttr ? `, ${attrJS}` : "");
127
+ return `${nspName}.tag(${nameJS}${restJS})`;
128
+ }
129
+ }
130
+ exports.Tag = Tag;