node-riner 1.2.2 → 1.2.3
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/index.ts +121 -67
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
|
+
// ...existing code...
|
|
1
2
|
import fs, { readFileSync, writeFileSync } from 'fs'
|
|
2
3
|
import { XMLParser } from 'fast-xml-parser'
|
|
3
4
|
import exp from 'express'
|
|
4
5
|
import path from 'path'
|
|
5
6
|
import { execSync } from 'node:child_process'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Ride クラス
|
|
10
|
+
* - XML をパースしてページごとの HTML を生成し、簡易サーバで配信する機能を備える。
|
|
11
|
+
* - 実行時に XML 内の `{...}` を runs.js に書き出して node で評価する仕組みを使用している(既存の挙動を維持)。
|
|
12
|
+
*/
|
|
6
13
|
export default class Ride {
|
|
7
|
-
F: string
|
|
8
|
-
parsedJson:
|
|
9
|
-
pages:
|
|
10
|
-
name: string
|
|
11
|
-
defaultinfo: string
|
|
14
|
+
F: string // 読み込んだ XML ファイルの内容(生の文字列)
|
|
15
|
+
parsedJson: any // fast-xml-parser によるパース結果(可変な構造のため any)
|
|
16
|
+
pages: Record<string, string> // ルートパス => 生成した HTML のマップ
|
|
17
|
+
name: string // サイト/アプリケーション名(<title> に利用)
|
|
18
|
+
defaultinfo: string // 将来の拡張用プレースホルダ
|
|
19
|
+
|
|
12
20
|
constructor(name: string) {
|
|
13
21
|
this.F = ""
|
|
14
22
|
this.parsedJson = {}
|
|
@@ -17,96 +25,142 @@ export default class Ride {
|
|
|
17
25
|
this.defaultinfo = ""
|
|
18
26
|
}
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
/**
|
|
29
|
+
* XML を解析して pages に HTML を格納する。
|
|
30
|
+
* - fast-xml-parser を使って XML を JSON に変換
|
|
31
|
+
* - Page が配列か単一かで処理を分岐
|
|
32
|
+
* - Content の各タグに対して文字列・配列・オブジェクトに応じて HTML を生成
|
|
33
|
+
* - `{...}` の中身は一時ファイル runs.js に書き出し node で実行、その出力で置換する(元の実装に合わせた動作)
|
|
34
|
+
*
|
|
35
|
+
* 注意: 現状は評価のために runs.js を書き込んで node で実行します。外部入力を評価すると危険です。
|
|
36
|
+
*/
|
|
37
|
+
returnhtml(): string {
|
|
38
|
+
const options = { ignoreAttributes: false }
|
|
24
39
|
const parser = new XMLParser(options);
|
|
40
|
+
|
|
25
41
|
try {
|
|
26
42
|
this.parsedJson = parser.parse(this.F)
|
|
27
|
-
|
|
43
|
+
|
|
44
|
+
// Root.Page が配列かどうかで分岐
|
|
28
45
|
if (Array.isArray(this.parsedJson["Root"]["Page"])) {
|
|
29
|
-
this.parsedJson["Root"]["Page"].forEach((page:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return
|
|
59
|
-
|
|
46
|
+
this.parsedJson["Root"]["Page"].forEach((page: any) => {
|
|
47
|
+
const route = page['@_route']
|
|
48
|
+
|
|
49
|
+
// Content の各 tag を HTML に変換
|
|
50
|
+
const generatedhtml = Object.keys(page['Content']).map(tags => {
|
|
51
|
+
// タグの値
|
|
52
|
+
let value = page['Content'][tags]
|
|
53
|
+
|
|
54
|
+
// 文字列内に { ... } があれば評価して置換する
|
|
55
|
+
if (typeof value === "string" && typeof value.match === "function") {
|
|
56
|
+
const match = value.match(/{.*}/g)
|
|
57
|
+
if (match) {
|
|
58
|
+
let matched: string = match[0]
|
|
59
|
+
matched = matched.replace("{", "").replace("}", "")
|
|
60
|
+
// runs.js に書き出して node で実行(既存の実装を保持)
|
|
61
|
+
writeFileSync("./runs.js", matched)
|
|
62
|
+
const out = execSync("node runs.js").toString()
|
|
63
|
+
value = value.replace(/{.*}/, out)
|
|
64
|
+
// 更新しておく
|
|
65
|
+
page['Content'][tags] = value
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// オブジェクト(ネストされたタグ群) -> タグでラップして出力
|
|
70
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
71
|
+
const inner = Object.keys(value).map(key => {
|
|
72
|
+
return `<${key}>${value[key]}</${key}>`
|
|
73
|
+
}).join('')
|
|
74
|
+
const gene = `<${tags}>${inner}</${tags}>`
|
|
75
|
+
return gene
|
|
76
|
+
}
|
|
77
|
+
// 配列 -> item タグで列挙
|
|
78
|
+
else if (Array.isArray(value)) {
|
|
79
|
+
const gene = `<${tags}>` + value.map((item: any) => {
|
|
80
|
+
return `<item>${item}</item>`
|
|
81
|
+
}).join('') + `</${tags}>`
|
|
82
|
+
return gene
|
|
83
|
+
}
|
|
84
|
+
// 文字列等 -> 単純にタグでラップ
|
|
85
|
+
else {
|
|
86
|
+
return `<${tags}>${value}</${tags}>`
|
|
87
|
+
}
|
|
60
88
|
}).join('')
|
|
89
|
+
|
|
90
|
+
// ページを格納(タイトルを付与)
|
|
61
91
|
this.pages[route] = `<title>${this.name}</title>` + generatedhtml
|
|
62
92
|
});
|
|
63
93
|
} else {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
// Page が単一オブジェクトの場合の処理
|
|
95
|
+
const page = this.parsedJson["Root"]["Page"]
|
|
96
|
+
const route = page['@_route']
|
|
97
|
+
|
|
98
|
+
const generatedhtml = Object.keys(page['Content']).map(tags => {
|
|
99
|
+
let value = page['Content'][tags]
|
|
100
|
+
|
|
101
|
+
if (typeof value === "string" && typeof value.match === "function") {
|
|
102
|
+
const match = value.match(/{.*}/g)
|
|
103
|
+
if (match) {
|
|
104
|
+
let matched: string = match[0]
|
|
105
|
+
matched = matched.replace("{", "").replace("}", "")
|
|
106
|
+
writeFileSync("./runs.js", matched)
|
|
107
|
+
const out = execSync("node runs.js").toString()
|
|
108
|
+
value = value.replace(/{.*}/, out)
|
|
109
|
+
page['Content'][tags] = value
|
|
110
|
+
}
|
|
79
111
|
}
|
|
80
|
-
|
|
81
|
-
return `<${tags}>${
|
|
82
|
-
|
|
83
|
-
|
|
112
|
+
|
|
113
|
+
return `<${tags}>${value}</${tags}>`
|
|
114
|
+
}).join('')
|
|
115
|
+
|
|
116
|
+
// 単一ページでもタイトルを付与する(配列処理と整合)
|
|
117
|
+
this.pages[route] = `<title>${this.name}</title>` + generatedhtml
|
|
84
118
|
}
|
|
85
119
|
} catch (e) {
|
|
120
|
+
// パースエラーなどの簡易ログ
|
|
86
121
|
console.error("Whoops, an error occurred during analysis : " + e)
|
|
87
122
|
}
|
|
123
|
+
|
|
124
|
+
// 現状の戻り値は未使用のため空文字を返す(将来は生成 HTML を返す等に変更可能)
|
|
88
125
|
return ""
|
|
89
126
|
}
|
|
90
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Ride メソッド(ファイル読み込みと HTML 生成を行う)
|
|
130
|
+
* @param name ファイル名(相対パス)
|
|
131
|
+
*/
|
|
91
132
|
Ride(name: string) {
|
|
133
|
+
// __dirname から上に遡ってファイルを読んでいる既存の記述を維持
|
|
92
134
|
this.F = readFileSync(path.join(__dirname, "../../", name), { encoding: "utf-8"})
|
|
93
|
-
|
|
94
|
-
this.returnhtml(this.F)
|
|
135
|
+
this.returnhtml()
|
|
95
136
|
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* 簡易サーバを起動し、生成済み pages をルーティングする
|
|
140
|
+
* @param port 起動ポート
|
|
141
|
+
* @returns express アプリインスタンス(テスト用途などで返す)
|
|
142
|
+
*/
|
|
96
143
|
serve(port: number) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
144
|
+
const app = exp();
|
|
145
|
+
|
|
146
|
+
// pages のキーをルートとして登録
|
|
147
|
+
Object.keys(this.pages).forEach(route => {
|
|
100
148
|
app.get(route, (req, res) => {
|
|
101
|
-
// debug
|
|
102
149
|
res.send(this.pages[route])
|
|
103
150
|
})
|
|
104
151
|
})
|
|
152
|
+
|
|
105
153
|
app.listen(port, () => {
|
|
106
154
|
console.log(`Server is running on http://localhost:${port}`);
|
|
107
155
|
})
|
|
156
|
+
|
|
108
157
|
return app
|
|
109
|
-
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* 設定用(未実装)
|
|
162
|
+
*/
|
|
110
163
|
config(options: object) {
|
|
164
|
+
// 将来の拡張ポイント
|
|
111
165
|
}
|
|
112
166
|
}
|