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.
Files changed (2) hide show
  1. package/index.ts +109 -70
  2. 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: object
9
- pages: object
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
- returnhtml(pagecontent: string | object) : string {
21
- let options = {
22
- ignoreAttributes: false,
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
- // debug
28
- if (Array.isArray(this.parsedJson["Root"]["Page"])) {
29
- this.parsedJson["Root"]["Page"].forEach((page: { [x: string]: { [x: string]: any } }) => {
30
- let route = page['@_route']
31
- let generatedhtml = Object.keys(page['Content']).map(tags => {
32
- if (typeof page['Content'][tags] === "string" && typeof page['Content'][tags].match === "function") {
33
- let match = page['Content'][tags].match(/{.*}/g)
34
- // debug
35
-
36
- if(match) {
37
- // debug
38
- let matched : string = match[0]
39
- matched = matched.replace("{", "")
40
- matched = matched.replace("}", "")
41
- writeFileSync("./runs.js", matched)
42
- let out = execSync("node runs.js").toString()
43
- // debug
44
- page['Content'][tags] = page['Content'][tags].replace(/{.*}/, out)
45
- }
46
- }
47
- // debug
48
- if( typeof page['Content'][tags] === "object" && !Array.isArray( page['Content'][tags])) {
49
- let gene = `<${tags}>` + Object.keys(page['Content'][tags]).map(key => {
50
- return `<${key}>${page['Content'][tags][key]}</${key}>`
51
- }).join('') + `</${tags}>`
52
- return gene
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
- // this.F = readFileSync(__dirname + "/" + name, { encoding: "utf-8"})
94
- this.returnhtml(this.F)
126
+ this.returnhtml()
95
127
  }
128
+
129
+ /**
130
+ * 簡易サーバを起動
131
+ */
96
132
  serve(port: number) {
97
- // debug
98
- let app = exp();
99
- (Object.keys(this.pages)).forEach(route => {
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-riner",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",