node-riner 1.2.2 → 1.3.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.
- package/index.ts +109 -70
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
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
|
+
*/
|
|
6
11
|
export default class Ride {
|
|
7
12
|
F: string
|
|
8
|
-
parsedJson:
|
|
9
|
-
pages:
|
|
13
|
+
parsedJson: any
|
|
14
|
+
pages: Record<string, string>
|
|
10
15
|
name: string
|
|
11
16
|
defaultinfo: string
|
|
17
|
+
|
|
12
18
|
constructor(name: string) {
|
|
13
19
|
this.F = ""
|
|
14
20
|
this.parsedJson = {}
|
|
@@ -17,96 +23,129 @@ export default class Ride {
|
|
|
17
23
|
this.defaultinfo = ""
|
|
18
24
|
}
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
/**
|
|
27
|
+
* 値を再帰的に HTML に変換するユーティリティ
|
|
28
|
+
* - 文字列: `{...}` を評価して置換(既存挙動を維持)
|
|
29
|
+
* - 配列: 各要素を <item> でラップして連結
|
|
30
|
+
* - オブジェクト: 各キーをタグ名として再帰的にレンダリング
|
|
31
|
+
*/
|
|
32
|
+
private renderValue(value: any): string {
|
|
33
|
+
// null / undefined
|
|
34
|
+
if (value === null || value === undefined) return ''
|
|
35
|
+
|
|
36
|
+
// 文字列処理({...} の評価を含む)
|
|
37
|
+
if (typeof value === 'string') {
|
|
38
|
+
// 複数マッチにも対応
|
|
39
|
+
const matches = value.match(/{.*?}/g)
|
|
40
|
+
if (matches) {
|
|
41
|
+
let newValue = value
|
|
42
|
+
matches.forEach(m => {
|
|
43
|
+
const code = m.slice(1, -1) // 中身を取り出す
|
|
44
|
+
try {
|
|
45
|
+
// 既存の実装を踏襲して runs.js に書き出して node で実行
|
|
46
|
+
writeFileSync("./runs.js", code)
|
|
47
|
+
const out = execSync("node runs.js").toString()
|
|
48
|
+
newValue = newValue.replace(m, out)
|
|
49
|
+
} catch (e) {
|
|
50
|
+
// 評価失敗時は空文字に置換(または元の {..} のままにする選択も可能)
|
|
51
|
+
newValue = newValue.replace(m, '')
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
return newValue
|
|
55
|
+
}
|
|
56
|
+
return value
|
|
23
57
|
}
|
|
58
|
+
|
|
59
|
+
// 配列: 各要素を再帰的に処理して <item> でラップ
|
|
60
|
+
if (Array.isArray(value)) {
|
|
61
|
+
return value.map((item: any) => `<item>${this.renderValue(item)}</item>`).join('')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// オブジェクト: キーごとにタグ化して再帰処理
|
|
65
|
+
if (typeof value === 'object') {
|
|
66
|
+
return Object.keys(value).map(key => {
|
|
67
|
+
// fast-xml-parser の場合、テキストノードはキーが '#text' や '_text' の可能性があるので考慮
|
|
68
|
+
if (key === '#text' || key === '_text') {
|
|
69
|
+
return this.renderValue(value[key])
|
|
70
|
+
}
|
|
71
|
+
return `<${key}>${this.renderValue(value[key])}</${key}>`
|
|
72
|
+
}).join('')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// その他(数値や真偽値など)はそのまま文字列化
|
|
76
|
+
return String(value)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* XML を解析して pages に HTML を格納
|
|
81
|
+
*/
|
|
82
|
+
returnhtml(): string {
|
|
83
|
+
const options = { ignoreAttributes: false }
|
|
24
84
|
const parser = new XMLParser(options);
|
|
85
|
+
|
|
25
86
|
try {
|
|
26
87
|
this.parsedJson = parser.parse(this.F)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}else if( Array.isArray( page['Content'][tags])) {
|
|
54
|
-
let gene = `<${tags}>` + page['Content'][tags].map(item => {
|
|
55
|
-
return `<item>${item}</item>`
|
|
56
|
-
}).join('') + `</${tags}>`
|
|
57
|
-
} else {
|
|
58
|
-
return `<${tags}>${page['Content'][tags]}</${tags}>`
|
|
59
|
-
}
|
|
60
|
-
}).join('')
|
|
61
|
-
this.pages[route] = `<title>${this.name}</title>` + generatedhtml
|
|
62
|
-
});
|
|
63
|
-
} else {
|
|
64
|
-
let page = this.parsedJson["Root"]["Page"]
|
|
65
|
-
let route = this.parsedJson["Root"]["Page"]['@_route']
|
|
66
|
-
let generatedhtml = Object.keys(page['Content']).map(tags => {
|
|
67
|
-
let match = page['Content'][tags].match(/{.*}/g)
|
|
68
|
-
// debug
|
|
69
|
-
|
|
70
|
-
if(match) {
|
|
71
|
-
// debug
|
|
72
|
-
let matched : string = match[0]
|
|
73
|
-
matched = matched.replace("{", "")
|
|
74
|
-
matched = matched.replace("}", "")
|
|
75
|
-
writeFileSync("./runs.js", matched)
|
|
76
|
-
let out = execSync("node runs.js").toString()
|
|
77
|
-
// debug
|
|
78
|
-
page['Content'][tags] = page['Content'][tags].replace(/{.*}/, out)
|
|
79
|
-
}
|
|
80
|
-
// debug
|
|
81
|
-
return `<${tags}>${page['Content'][tags]}</${tags}>`
|
|
82
|
-
}).join('')
|
|
83
|
-
this.pages[route] = generatedhtml
|
|
84
|
-
}
|
|
88
|
+
|
|
89
|
+
const root = this.parsedJson?.Root
|
|
90
|
+
if (!root) return ""
|
|
91
|
+
|
|
92
|
+
const pages = root["Page"]
|
|
93
|
+
if (!pages) return ""
|
|
94
|
+
|
|
95
|
+
// Page が配列か単一かを吸収して配列化
|
|
96
|
+
const pageArray = Array.isArray(pages) ? pages : [pages]
|
|
97
|
+
|
|
98
|
+
pageArray.forEach((page: any) => {
|
|
99
|
+
const route = page['@_route'] || '/'
|
|
100
|
+
|
|
101
|
+
// Content が存在しない場合は空にする
|
|
102
|
+
const content = page['Content'] || {}
|
|
103
|
+
|
|
104
|
+
// 各タグを再帰的にレンダリングして結合
|
|
105
|
+
const generatedhtml = Object.keys(content).map(tag => {
|
|
106
|
+
const value = content[tag]
|
|
107
|
+
const inner = this.renderValue(value)
|
|
108
|
+
return `<${tag}>${inner}</${tag}>`
|
|
109
|
+
}).join('')
|
|
110
|
+
|
|
111
|
+
// ページを格納(タイトルを付与)
|
|
112
|
+
this.pages[route] = `<title>${this.name}</title>` + generatedhtml
|
|
113
|
+
})
|
|
85
114
|
} catch (e) {
|
|
86
115
|
console.error("Whoops, an error occurred during analysis : " + e)
|
|
87
116
|
}
|
|
117
|
+
|
|
88
118
|
return ""
|
|
89
119
|
}
|
|
90
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Ride メソッド(ファイル読み込みと HTML 生成)
|
|
123
|
+
*/
|
|
91
124
|
Ride(name: string) {
|
|
92
125
|
this.F = readFileSync(path.join(__dirname, "../../", name), { encoding: "utf-8"})
|
|
93
|
-
|
|
94
|
-
this.returnhtml(this.F)
|
|
126
|
+
this.returnhtml()
|
|
95
127
|
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 簡易サーバを起動
|
|
131
|
+
*/
|
|
96
132
|
serve(port: number) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
133
|
+
const app = exp();
|
|
134
|
+
|
|
135
|
+
Object.keys(this.pages).forEach(route => {
|
|
100
136
|
app.get(route, (req, res) => {
|
|
101
|
-
// debug
|
|
102
137
|
res.send(this.pages[route])
|
|
103
138
|
})
|
|
104
139
|
})
|
|
140
|
+
|
|
105
141
|
app.listen(port, () => {
|
|
106
142
|
console.log(`Server is running on http://localhost:${port}`);
|
|
107
143
|
})
|
|
144
|
+
|
|
108
145
|
return app
|
|
109
|
-
}
|
|
146
|
+
}
|
|
147
|
+
|
|
110
148
|
config(options: object) {
|
|
149
|
+
// 将来の拡張ポイント
|
|
111
150
|
}
|
|
112
151
|
}
|