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