nsp-server-pages 0.2.0 → 0.2.2
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 +2 -1
- package/cjs/src/app.js +6 -6
- package/cjs/src/parser/attr.js +4 -4
- package/cjs/src/parser/jsp.js +2 -2
- package/cjs/src/parser/tag.js +20 -16
- package/cjs/src/store.js +36 -0
- package/cjs/src/taglib.js +30 -2
- package/esm/src/app.js +6 -6
- package/esm/src/parser/attr.js +4 -4
- package/esm/src/parser/jsp.js +2 -2
- package/esm/src/parser/tag.js +20 -16
- package/esm/src/store.js +32 -0
- package/esm/src/taglib.js +30 -2
- package/package.json +2 -2
- package/types/index.d.ts +8 -1
- package/cjs/src/stack-store.js +0 -31
- package/esm/src/stack-store.js +0 -27
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ NSP JavaScript Server Pages for Node.js
|
|
|
11
11
|
- `${ f:h("tag") }` - custom taglib static function call
|
|
12
12
|
- `<ns:tag attr="${ expression }"/>` - custom taglib action tag
|
|
13
13
|
- `<%-- comments --%>` - comments in JSP just ignored
|
|
14
|
-
- See TypeScript declaration
|
|
14
|
+
- See [TypeScript declaration files](https://github.com/kawanet/nsp-server-pages/tree/main/types/) for API detail.
|
|
15
15
|
|
|
16
16
|
## SYNOPSIS
|
|
17
17
|
|
|
@@ -156,6 +156,7 @@ app.use("/", async (req, res, next) => {
|
|
|
156
156
|
- https://github.com/kawanet/nsp-server-pages
|
|
157
157
|
- https://github.com/kawanet/nsp-jstl-taglib
|
|
158
158
|
- https://github.com/kawanet/nsp-struts1-taglib
|
|
159
|
+
- https://github.com/kawanet/nsp-seasar2-taglib
|
|
159
160
|
- https://github.com/apache/tomcat
|
|
160
161
|
|
|
161
162
|
## LICENSE
|
package/cjs/src/app.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.App = void 0;
|
|
4
|
-
const
|
|
4
|
+
const bundle_js_1 = require("./bundle.js");
|
|
5
|
+
const catch_js_1 = require("./catch.js");
|
|
6
|
+
const concat_js_1 = require("./concat.js");
|
|
5
7
|
const loaders_js_1 = require("./loaders.js");
|
|
8
|
+
const mount_js_1 = require("./mount.js");
|
|
6
9
|
const jsp_js_1 = require("./parser/jsp.js");
|
|
7
|
-
const
|
|
8
|
-
const bundle_js_1 = require("./bundle.js");
|
|
10
|
+
const store_js_1 = require("./store.js");
|
|
9
11
|
const taglib_js_1 = require("./taglib.js");
|
|
10
|
-
const concat_js_1 = require("./concat.js");
|
|
11
|
-
const stack_store_js_1 = require("./stack-store.js");
|
|
12
12
|
class App {
|
|
13
13
|
constructor(options) {
|
|
14
14
|
this.loaders = [];
|
|
@@ -85,7 +85,7 @@ class App {
|
|
|
85
85
|
const map = (context[storeKey] ??= new Map());
|
|
86
86
|
let value = map.get(key);
|
|
87
87
|
if (value == null) {
|
|
88
|
-
value = new
|
|
88
|
+
value = new store_js_1.Store();
|
|
89
89
|
map.set(key, value);
|
|
90
90
|
}
|
|
91
91
|
return value;
|
package/cjs/src/parser/attr.js
CHANGED
|
@@ -53,9 +53,9 @@ class Attr {
|
|
|
53
53
|
_toJS(option) {
|
|
54
54
|
const { app } = this;
|
|
55
55
|
const { indent } = app.options;
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const nextLF =
|
|
56
|
+
const SP = option?.SP ?? (("string" === typeof indent) ? indent : (+indent ? " ".repeat(+indent) : ""));
|
|
57
|
+
const LF = option?.LF ?? "\n";
|
|
58
|
+
const nextLF = LF + SP;
|
|
59
59
|
const keys = this.keys();
|
|
60
60
|
const items = keys.map(key => {
|
|
61
61
|
if (!isSafeKey(key)) {
|
|
@@ -69,7 +69,7 @@ class Attr {
|
|
|
69
69
|
return 'null';
|
|
70
70
|
const js = items.join(`,${nextLF}`);
|
|
71
71
|
const trailingComma = (keys.length > 1) ? "," : "";
|
|
72
|
-
return `{${nextLF}${js}${trailingComma}${
|
|
72
|
+
return `{${nextLF}${js}${trailingComma}${LF}}`;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
exports.Attr = Attr;
|
package/cjs/src/parser/jsp.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.jspToJS = exports.JSP = void 0;
|
|
4
|
+
const store_js_1 = require("../store.js");
|
|
4
5
|
const scriptlet_js_1 = require("./scriptlet.js");
|
|
5
|
-
const stack_store_js_1 = require("../stack-store.js");
|
|
6
6
|
const tag_js_1 = require("./tag.js");
|
|
7
7
|
/**
|
|
8
8
|
* Parser for JSP document
|
|
@@ -44,7 +44,7 @@ const insideRE = `[^"']|${stringRE}`;
|
|
|
44
44
|
const tagRegExp = new RegExp(`(</?${nameRE}:(?:${insideRE})*?>)|(<%(?:${insideRE})*?%>)`, "s");
|
|
45
45
|
const jspToJS = (app, src, option) => {
|
|
46
46
|
const root = new tag_js_1.Tag(app);
|
|
47
|
-
const tree = new
|
|
47
|
+
const tree = new store_js_1.Store(root);
|
|
48
48
|
const array = src.split(tagRegExp);
|
|
49
49
|
for (let i = 0; i < array.length; i++) {
|
|
50
50
|
const i3 = i % 3;
|
package/cjs/src/parser/tag.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Tag = void 0;
|
|
4
|
-
const text_js_1 = require("./text.js");
|
|
5
4
|
const attr_js_1 = require("./attr.js");
|
|
5
|
+
const text_js_1 = require("./text.js");
|
|
6
6
|
const emptyText = {
|
|
7
7
|
'""': true,
|
|
8
8
|
"''": true,
|
|
@@ -34,13 +34,14 @@ class Tag {
|
|
|
34
34
|
getBodyJS(option) {
|
|
35
35
|
const { app } = this;
|
|
36
36
|
const { indent, trimSpaces, vName } = app.options;
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const nextLF =
|
|
37
|
+
const SP = option?.SP ?? (("string" === typeof indent) ? indent : (+indent ? " ".repeat(+indent) : ""));
|
|
38
|
+
const LF = option?.LF ?? "\n";
|
|
39
|
+
const nextLF = LF + SP;
|
|
40
|
+
const nextOption = { SP, LF: nextLF };
|
|
40
41
|
const { children } = this;
|
|
41
42
|
const args = children.map(item => {
|
|
42
43
|
if (isTranspiler(item)) {
|
|
43
|
-
return item.toJS(
|
|
44
|
+
return item.toJS(nextOption);
|
|
44
45
|
}
|
|
45
46
|
else if (!/\S/.test(item)) {
|
|
46
47
|
// item with only whitespace
|
|
@@ -53,7 +54,7 @@ class Tag {
|
|
|
53
54
|
item = item.replace(/^[ \t]+/s, " ");
|
|
54
55
|
item = item.replace(/[ \t]+$/s, " ");
|
|
55
56
|
}
|
|
56
|
-
let js = new text_js_1.Text(app, item).toJS(
|
|
57
|
+
let js = new text_js_1.Text(app, item).toJS(nextOption);
|
|
57
58
|
if (/\(.+?\)|\$\{.+?}/s.test(js)) {
|
|
58
59
|
js = `${vName} => ${js}`; // array function
|
|
59
60
|
}
|
|
@@ -75,11 +76,11 @@ class Tag {
|
|
|
75
76
|
args[idx] += ",";
|
|
76
77
|
}
|
|
77
78
|
else if (idx === last && isComment) {
|
|
78
|
-
args[idx] +=
|
|
79
|
+
args[idx] += LF;
|
|
79
80
|
}
|
|
80
81
|
});
|
|
81
82
|
const bodyL = /^`\n/s.test(args.at(0)) ? "" : nextLF;
|
|
82
|
-
const bodyR = /(\n`|[)\s])$/s.test(args.at(-1)) ? "" :
|
|
83
|
+
const bodyR = /(\n`|[)\s])$/s.test(args.at(-1)) ? "" : LF;
|
|
83
84
|
return bodyL + args.join(nextLF) + bodyR;
|
|
84
85
|
}
|
|
85
86
|
/**
|
|
@@ -98,13 +99,13 @@ class Tag {
|
|
|
98
99
|
const commentJS = this.getCommentJS(option);
|
|
99
100
|
const attr = new attr_js_1.Attr(app, this.src);
|
|
100
101
|
const body = this.getBodyJS(option);
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const nextLF =
|
|
104
|
-
const
|
|
102
|
+
const SP = option?.SP ?? (("string" === typeof indent) ? indent : (+indent ? " ".repeat(+indent) : ""));
|
|
103
|
+
const LF = option?.LF ?? "\n";
|
|
104
|
+
const nextLF = LF + SP;
|
|
105
|
+
const nextOption = { SP, LF: (body ? nextLF : LF) };
|
|
105
106
|
const type = `parse.tag.${tagName}`;
|
|
106
|
-
const def = { app, name: tagName, attr, body, LF:
|
|
107
|
-
const tagJS = app.process(type, def) ?? this.getTagJS(def,
|
|
107
|
+
const def = { app, name: tagName, attr, body, LF: LF, nextLF };
|
|
108
|
+
const tagJS = app.process(type, def) ?? this.getTagJS(def, nextOption);
|
|
108
109
|
return commentJS ? commentJS + tagJS : tagJS;
|
|
109
110
|
}
|
|
110
111
|
getCommentJS(option) {
|
|
@@ -120,10 +121,13 @@ class Tag {
|
|
|
120
121
|
const attrRaw = def.attr.toJS(option);
|
|
121
122
|
// transpile attributes to array function if they include variables
|
|
122
123
|
const hasVars = /\(.+?\)|\$\{.+?}/s.test(attrRaw);
|
|
123
|
-
const
|
|
124
|
+
const attrArg = hasVars ? `, ${vName} => (${attrRaw})` : `, ${attrRaw}`;
|
|
124
125
|
const nameJS = JSON.stringify(tagName);
|
|
125
126
|
const hasAttr = /:/.test(attrRaw);
|
|
126
|
-
|
|
127
|
+
let bodyArg = def.body;
|
|
128
|
+
if (bodyArg)
|
|
129
|
+
bodyArg = ((/^\n/.test(bodyArg)) ? "," : ", ") + bodyArg;
|
|
130
|
+
const restJS = bodyArg ? (attrArg + bodyArg) : (hasAttr ? attrArg : "");
|
|
127
131
|
return `${nspName}.tag(${nameJS}${restJS})`;
|
|
128
132
|
}
|
|
129
133
|
}
|
package/cjs/src/store.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Store = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The Store class was implemented using a stack {stack: P[]} at first.
|
|
6
|
+
* Now it was changed to a linked list to allow access to the parent.
|
|
7
|
+
*/
|
|
8
|
+
class Store {
|
|
9
|
+
constructor(value) {
|
|
10
|
+
this.item = { value };
|
|
11
|
+
}
|
|
12
|
+
open(value) {
|
|
13
|
+
this.item = { parent: this.item, value };
|
|
14
|
+
}
|
|
15
|
+
close() {
|
|
16
|
+
const item = this.item;
|
|
17
|
+
this.item = item.parent;
|
|
18
|
+
return item.value;
|
|
19
|
+
}
|
|
20
|
+
get() {
|
|
21
|
+
return this.item.value;
|
|
22
|
+
}
|
|
23
|
+
set(value) {
|
|
24
|
+
this.item.value = value;
|
|
25
|
+
}
|
|
26
|
+
find(test) {
|
|
27
|
+
let item = this.item;
|
|
28
|
+
while (item) {
|
|
29
|
+
if (test(item.value)) {
|
|
30
|
+
return item.value;
|
|
31
|
+
}
|
|
32
|
+
item = item.parent;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.Store = Store;
|
package/cjs/src/taglib.js
CHANGED
|
@@ -2,17 +2,45 @@
|
|
|
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 isTagCon = (v) => ("function" === typeof v?.prototype?.render);
|
|
6
|
+
const tagConToTagFn = (Tag) => {
|
|
7
|
+
return (tag) => {
|
|
8
|
+
return (context) => {
|
|
9
|
+
const result = new Tag(tag, context).render();
|
|
10
|
+
if (result)
|
|
11
|
+
return result;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
};
|
|
5
15
|
function addTagLib(tagLibDef) {
|
|
6
16
|
const { fnMap, tagMap } = this;
|
|
7
17
|
const { ns, fn, tag } = tagLibDef;
|
|
8
18
|
if (fn) {
|
|
9
19
|
for (const name in fn) {
|
|
10
|
-
|
|
20
|
+
const impl = fn[name];
|
|
21
|
+
if (typeof impl === "function") {
|
|
22
|
+
// FnFn is called with App instance as this
|
|
23
|
+
fnMap.set(`${ns}:${name}`, impl.bind(this));
|
|
24
|
+
}
|
|
25
|
+
else if (impl != null) {
|
|
26
|
+
throw new Error(`Invalid taglib implementation: \${${ns}:${name}()}`);
|
|
27
|
+
}
|
|
11
28
|
}
|
|
12
29
|
}
|
|
13
30
|
if (tag) {
|
|
14
31
|
for (const name in tag) {
|
|
15
|
-
|
|
32
|
+
const impl = tag[name];
|
|
33
|
+
if (isTagCon(impl)) {
|
|
34
|
+
// NSP.TagCon
|
|
35
|
+
tagMap.set(`${ns}:${name}`, tagConToTagFn(impl));
|
|
36
|
+
}
|
|
37
|
+
else if (typeof impl === "function") {
|
|
38
|
+
// NSP.TagFn
|
|
39
|
+
tagMap.set(`${ns}:${name}`, impl);
|
|
40
|
+
}
|
|
41
|
+
else if (impl != null) {
|
|
42
|
+
throw new Error(`Invalid taglib implementation: <${ns}:${name}>`);
|
|
43
|
+
}
|
|
16
44
|
}
|
|
17
45
|
}
|
|
18
46
|
}
|
package/esm/src/app.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { bundle } from "./bundle.js";
|
|
2
|
+
import { catchFn } from "./catch.js";
|
|
3
|
+
import { concat } from "./concat.js";
|
|
2
4
|
import { FileLoader, JsLoader, JspLoader } from "./loaders.js";
|
|
5
|
+
import { load, mount } from "./mount.js";
|
|
3
6
|
import { JSP } from "./parser/jsp.js";
|
|
4
|
-
import {
|
|
5
|
-
import { bundle } from "./bundle.js";
|
|
7
|
+
import { Store } from "./store.js";
|
|
6
8
|
import { addTagLib, prepareTag } from "./taglib.js";
|
|
7
|
-
import { concat } from "./concat.js";
|
|
8
|
-
import { StackStore } from "./stack-store.js";
|
|
9
9
|
export class App {
|
|
10
10
|
constructor(options) {
|
|
11
11
|
this.loaders = [];
|
|
@@ -82,7 +82,7 @@ export class App {
|
|
|
82
82
|
const map = (context[storeKey] ??= new Map());
|
|
83
83
|
let value = map.get(key);
|
|
84
84
|
if (value == null) {
|
|
85
|
-
value = new
|
|
85
|
+
value = new Store();
|
|
86
86
|
map.set(key, value);
|
|
87
87
|
}
|
|
88
88
|
return value;
|
package/esm/src/parser/attr.js
CHANGED
|
@@ -50,9 +50,9 @@ export class Attr {
|
|
|
50
50
|
_toJS(option) {
|
|
51
51
|
const { app } = this;
|
|
52
52
|
const { indent } = app.options;
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const nextLF =
|
|
53
|
+
const SP = option?.SP ?? (("string" === typeof indent) ? indent : (+indent ? " ".repeat(+indent) : ""));
|
|
54
|
+
const LF = option?.LF ?? "\n";
|
|
55
|
+
const nextLF = LF + SP;
|
|
56
56
|
const keys = this.keys();
|
|
57
57
|
const items = keys.map(key => {
|
|
58
58
|
if (!isSafeKey(key)) {
|
|
@@ -66,7 +66,7 @@ export class Attr {
|
|
|
66
66
|
return 'null';
|
|
67
67
|
const js = items.join(`,${nextLF}`);
|
|
68
68
|
const trailingComma = (keys.length > 1) ? "," : "";
|
|
69
|
-
return `{${nextLF}${js}${trailingComma}${
|
|
69
|
+
return `{${nextLF}${js}${trailingComma}${LF}}`;
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
const UNESCAPE = {
|
package/esm/src/parser/jsp.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { Store } from "../store.js";
|
|
1
2
|
import { Scriptlet } from "./scriptlet.js";
|
|
2
|
-
import { StackStore } from "../stack-store.js";
|
|
3
3
|
import { Tag } from "./tag.js";
|
|
4
4
|
/**
|
|
5
5
|
* Parser for JSP document
|
|
@@ -40,7 +40,7 @@ const insideRE = `[^"']|${stringRE}`;
|
|
|
40
40
|
const tagRegExp = new RegExp(`(</?${nameRE}:(?:${insideRE})*?>)|(<%(?:${insideRE})*?%>)`, "s");
|
|
41
41
|
export const jspToJS = (app, src, option) => {
|
|
42
42
|
const root = new Tag(app);
|
|
43
|
-
const tree = new
|
|
43
|
+
const tree = new Store(root);
|
|
44
44
|
const array = src.split(tagRegExp);
|
|
45
45
|
for (let i = 0; i < array.length; i++) {
|
|
46
46
|
const i3 = i % 3;
|
package/esm/src/parser/tag.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Text } from "./text.js";
|
|
2
1
|
import { Attr } from "./attr.js";
|
|
2
|
+
import { Text } from "./text.js";
|
|
3
3
|
const emptyText = {
|
|
4
4
|
'""': true,
|
|
5
5
|
"''": true,
|
|
@@ -31,13 +31,14 @@ export class Tag {
|
|
|
31
31
|
getBodyJS(option) {
|
|
32
32
|
const { app } = this;
|
|
33
33
|
const { indent, trimSpaces, vName } = app.options;
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const nextLF =
|
|
34
|
+
const SP = option?.SP ?? (("string" === typeof indent) ? indent : (+indent ? " ".repeat(+indent) : ""));
|
|
35
|
+
const LF = option?.LF ?? "\n";
|
|
36
|
+
const nextLF = LF + SP;
|
|
37
|
+
const nextOption = { SP, LF: nextLF };
|
|
37
38
|
const { children } = this;
|
|
38
39
|
const args = children.map(item => {
|
|
39
40
|
if (isTranspiler(item)) {
|
|
40
|
-
return item.toJS(
|
|
41
|
+
return item.toJS(nextOption);
|
|
41
42
|
}
|
|
42
43
|
else if (!/\S/.test(item)) {
|
|
43
44
|
// item with only whitespace
|
|
@@ -50,7 +51,7 @@ export class Tag {
|
|
|
50
51
|
item = item.replace(/^[ \t]+/s, " ");
|
|
51
52
|
item = item.replace(/[ \t]+$/s, " ");
|
|
52
53
|
}
|
|
53
|
-
let js = new Text(app, item).toJS(
|
|
54
|
+
let js = new Text(app, item).toJS(nextOption);
|
|
54
55
|
if (/\(.+?\)|\$\{.+?}/s.test(js)) {
|
|
55
56
|
js = `${vName} => ${js}`; // array function
|
|
56
57
|
}
|
|
@@ -72,11 +73,11 @@ export class Tag {
|
|
|
72
73
|
args[idx] += ",";
|
|
73
74
|
}
|
|
74
75
|
else if (idx === last && isComment) {
|
|
75
|
-
args[idx] +=
|
|
76
|
+
args[idx] += LF;
|
|
76
77
|
}
|
|
77
78
|
});
|
|
78
79
|
const bodyL = /^`\n/s.test(args.at(0)) ? "" : nextLF;
|
|
79
|
-
const bodyR = /(\n`|[)\s])$/s.test(args.at(-1)) ? "" :
|
|
80
|
+
const bodyR = /(\n`|[)\s])$/s.test(args.at(-1)) ? "" : LF;
|
|
80
81
|
return bodyL + args.join(nextLF) + bodyR;
|
|
81
82
|
}
|
|
82
83
|
/**
|
|
@@ -95,13 +96,13 @@ export class Tag {
|
|
|
95
96
|
const commentJS = this.getCommentJS(option);
|
|
96
97
|
const attr = new Attr(app, this.src);
|
|
97
98
|
const body = this.getBodyJS(option);
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
const nextLF =
|
|
101
|
-
const
|
|
99
|
+
const SP = option?.SP ?? (("string" === typeof indent) ? indent : (+indent ? " ".repeat(+indent) : ""));
|
|
100
|
+
const LF = option?.LF ?? "\n";
|
|
101
|
+
const nextLF = LF + SP;
|
|
102
|
+
const nextOption = { SP, LF: (body ? nextLF : LF) };
|
|
102
103
|
const type = `parse.tag.${tagName}`;
|
|
103
|
-
const def = { app, name: tagName, attr, body, LF:
|
|
104
|
-
const tagJS = app.process(type, def) ?? this.getTagJS(def,
|
|
104
|
+
const def = { app, name: tagName, attr, body, LF: LF, nextLF };
|
|
105
|
+
const tagJS = app.process(type, def) ?? this.getTagJS(def, nextOption);
|
|
105
106
|
return commentJS ? commentJS + tagJS : tagJS;
|
|
106
107
|
}
|
|
107
108
|
getCommentJS(option) {
|
|
@@ -117,10 +118,13 @@ export class Tag {
|
|
|
117
118
|
const attrRaw = def.attr.toJS(option);
|
|
118
119
|
// transpile attributes to array function if they include variables
|
|
119
120
|
const hasVars = /\(.+?\)|\$\{.+?}/s.test(attrRaw);
|
|
120
|
-
const
|
|
121
|
+
const attrArg = hasVars ? `, ${vName} => (${attrRaw})` : `, ${attrRaw}`;
|
|
121
122
|
const nameJS = JSON.stringify(tagName);
|
|
122
123
|
const hasAttr = /:/.test(attrRaw);
|
|
123
|
-
|
|
124
|
+
let bodyArg = def.body;
|
|
125
|
+
if (bodyArg)
|
|
126
|
+
bodyArg = ((/^\n/.test(bodyArg)) ? "," : ", ") + bodyArg;
|
|
127
|
+
const restJS = bodyArg ? (attrArg + bodyArg) : (hasAttr ? attrArg : "");
|
|
124
128
|
return `${nspName}.tag(${nameJS}${restJS})`;
|
|
125
129
|
}
|
|
126
130
|
}
|
package/esm/src/store.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Store class was implemented using a stack {stack: P[]} at first.
|
|
3
|
+
* Now it was changed to a linked list to allow access to the parent.
|
|
4
|
+
*/
|
|
5
|
+
export class Store {
|
|
6
|
+
constructor(value) {
|
|
7
|
+
this.item = { value };
|
|
8
|
+
}
|
|
9
|
+
open(value) {
|
|
10
|
+
this.item = { parent: this.item, value };
|
|
11
|
+
}
|
|
12
|
+
close() {
|
|
13
|
+
const item = this.item;
|
|
14
|
+
this.item = item.parent;
|
|
15
|
+
return item.value;
|
|
16
|
+
}
|
|
17
|
+
get() {
|
|
18
|
+
return this.item.value;
|
|
19
|
+
}
|
|
20
|
+
set(value) {
|
|
21
|
+
this.item.value = value;
|
|
22
|
+
}
|
|
23
|
+
find(test) {
|
|
24
|
+
let item = this.item;
|
|
25
|
+
while (item) {
|
|
26
|
+
if (test(item.value)) {
|
|
27
|
+
return item.value;
|
|
28
|
+
}
|
|
29
|
+
item = item.parent;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
package/esm/src/taglib.js
CHANGED
|
@@ -1,15 +1,43 @@
|
|
|
1
1
|
import { toXML } from "to-xml";
|
|
2
|
+
const isTagCon = (v) => ("function" === typeof v?.prototype?.render);
|
|
3
|
+
const tagConToTagFn = (Tag) => {
|
|
4
|
+
return (tag) => {
|
|
5
|
+
return (context) => {
|
|
6
|
+
const result = new Tag(tag, context).render();
|
|
7
|
+
if (result)
|
|
8
|
+
return result;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
};
|
|
2
12
|
export function addTagLib(tagLibDef) {
|
|
3
13
|
const { fnMap, tagMap } = this;
|
|
4
14
|
const { ns, fn, tag } = tagLibDef;
|
|
5
15
|
if (fn) {
|
|
6
16
|
for (const name in fn) {
|
|
7
|
-
|
|
17
|
+
const impl = fn[name];
|
|
18
|
+
if (typeof impl === "function") {
|
|
19
|
+
// FnFn is called with App instance as this
|
|
20
|
+
fnMap.set(`${ns}:${name}`, impl.bind(this));
|
|
21
|
+
}
|
|
22
|
+
else if (impl != null) {
|
|
23
|
+
throw new Error(`Invalid taglib implementation: \${${ns}:${name}()}`);
|
|
24
|
+
}
|
|
8
25
|
}
|
|
9
26
|
}
|
|
10
27
|
if (tag) {
|
|
11
28
|
for (const name in tag) {
|
|
12
|
-
|
|
29
|
+
const impl = tag[name];
|
|
30
|
+
if (isTagCon(impl)) {
|
|
31
|
+
// NSP.TagCon
|
|
32
|
+
tagMap.set(`${ns}:${name}`, tagConToTagFn(impl));
|
|
33
|
+
}
|
|
34
|
+
else if (typeof impl === "function") {
|
|
35
|
+
// NSP.TagFn
|
|
36
|
+
tagMap.set(`${ns}:${name}`, impl);
|
|
37
|
+
}
|
|
38
|
+
else if (impl != null) {
|
|
39
|
+
throw new Error(`Invalid taglib implementation: <${ns}:${name}>`);
|
|
40
|
+
}
|
|
13
41
|
}
|
|
14
42
|
}
|
|
15
43
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nsp-server-pages",
|
|
3
3
|
"description": "NSP JavaScript Server Pages for Node.js",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"author": "@kawanet",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/kawanet/nsp-server-pages/issues"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"@rollup/plugin-node-resolve": "^15.2.1",
|
|
15
15
|
"@types/mocha": "^10.0.1",
|
|
16
|
-
"@types/node": "^20.5.
|
|
16
|
+
"@types/node": "^20.5.7",
|
|
17
17
|
"mocha": "^10.2.0",
|
|
18
18
|
"typescript": "^5.2.2"
|
|
19
19
|
},
|
package/types/index.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export declare namespace NSP {
|
|
|
19
19
|
|
|
20
20
|
type TagFn<A, T = any> = (tag: TagDef<A, T>) => (NodeFn<T> | VoidFn<T>);
|
|
21
21
|
|
|
22
|
+
type TagCon<A, T = any> = { new(tag: NSP.TagDef<A>, context: T): TagClass };
|
|
23
|
+
|
|
22
24
|
type LoaderFn = (path: string) => Promise<NodeFn<any> | undefined>;
|
|
23
25
|
|
|
24
26
|
type Strings = string | Promise<string> | Strings[];
|
|
@@ -166,7 +168,11 @@ export declare namespace NSP {
|
|
|
166
168
|
/**
|
|
167
169
|
* tags
|
|
168
170
|
*/
|
|
169
|
-
tag?: { [name: string]: TagFn<any> };
|
|
171
|
+
tag?: { [name: string]: TagFn<any> | TagCon<any> };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
interface TagClass {
|
|
175
|
+
render(): string | Promise<string> | void | Promise<void>;
|
|
170
176
|
}
|
|
171
177
|
|
|
172
178
|
interface StackStore<P> {
|
|
@@ -198,6 +204,7 @@ export declare namespace NSP {
|
|
|
198
204
|
|
|
199
205
|
interface ToJSOption {
|
|
200
206
|
LF?: string;
|
|
207
|
+
SP?: string;
|
|
201
208
|
}
|
|
202
209
|
|
|
203
210
|
interface Transpiler {
|
package/cjs/src/stack-store.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
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/esm/src/stack-store.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
export class StackStore {
|
|
2
|
-
constructor(value) {
|
|
3
|
-
this.stack = [];
|
|
4
|
-
if (arguments.length) {
|
|
5
|
-
this.set(value);
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
open(value) {
|
|
9
|
-
this.stack.unshift(value);
|
|
10
|
-
}
|
|
11
|
-
close() {
|
|
12
|
-
return this.stack.shift();
|
|
13
|
-
}
|
|
14
|
-
get() {
|
|
15
|
-
return this.stack[0];
|
|
16
|
-
}
|
|
17
|
-
set(value) {
|
|
18
|
-
this.stack[0] = value;
|
|
19
|
-
}
|
|
20
|
-
find(test) {
|
|
21
|
-
for (const data of this.stack) {
|
|
22
|
-
if (test(data)) {
|
|
23
|
-
return data;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|