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.
Files changed (2) hide show
  1. package/index.ts +121 -67
  2. 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: object
9
- pages: object
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
- returnhtml(pagecontent: string | object) : string {
21
- let options = {
22
- ignoreAttributes: false,
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
- // debug
43
+
44
+ // Root.Page が配列かどうかで分岐
28
45
  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
- }
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
- 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)
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
- // debug
81
- return `<${tags}>${page['Content'][tags]}</${tags}>`
82
- }).join('')
83
- this.pages[route] = generatedhtml
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
- // this.F = readFileSync(__dirname + "/" + name, { encoding: "utf-8"})
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
- // debug
98
- let app = exp();
99
- (Object.keys(this.pages)).forEach(route => {
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-riner",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",