nadesiko3 3.7.19 → 3.7.21

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 (52) hide show
  1. package/batch/command.txt +115 -113
  2. package/core/package.json +2 -2
  3. package/core/src/README.md +98 -0
  4. package/core/src/nako_ast.mts +1 -0
  5. package/core/src/nako_core_version.mjs +2 -2
  6. package/core/src/nako_core_version.mts +2 -2
  7. package/core/src/nako_gen.mjs +54 -0
  8. package/core/src/nako_gen.mts +61 -0
  9. package/core/src/nako_parser3.mjs +45 -7
  10. package/core/src/nako_parser3.mts +43 -7
  11. package/core/test/array_test.mjs +16 -0
  12. package/core/test/func_call.mjs +11 -0
  13. package/core/test/func_scope.mjs +112 -0
  14. package/package.json +21 -27
  15. package/release/_hash.txt +36 -36
  16. package/release/_script-tags.txt +16 -16
  17. package/release/command.json +1 -1
  18. package/release/command.json.js +1 -1
  19. package/release/command_cnako3.json +1 -1
  20. package/release/command_list.json +1 -1
  21. package/release/edit_main.js +2 -2
  22. package/release/edit_main.js.map +3 -3
  23. package/release/editor.js +2 -2
  24. package/release/plugin_datetime.js +1 -1
  25. package/release/plugin_datetime.js.map +3 -3
  26. package/release/plugin_markup.js +28 -28
  27. package/release/plugin_markup.js.map +3 -3
  28. package/release/version.js +2 -2
  29. package/release/version_main.js +2 -2
  30. package/release/version_main.js.map +2 -2
  31. package/release/wnako3.js +85 -77
  32. package/release/wnako3.js.map +3 -3
  33. package/release/wnako3webworker.js +106 -98
  34. package/release/wnako3webworker.js.map +3 -3
  35. package/src/cnako3mod.mjs +1 -2
  36. package/src/cnako3mod.mts +1 -2
  37. package/src/nako_version.mjs +2 -2
  38. package/src/nako_version.mts +2 -2
  39. package/src/plugin_httpserver.mjs +184 -13
  40. package/src/plugin_httpserver.mts +176 -10
  41. package/src/plugin_node.mjs +0 -1
  42. package/src/plugin_node.mts +2 -3
  43. package/src/wnako3mod.mjs +3 -0
  44. package/src/wnako3mod.mts +3 -0
  45. package/test/common/wnako3mod_test.mjs +48 -0
  46. package/test/node/package_json_test.mjs +17 -0
  47. package/test/node/plugin_httpserver_test.mjs +239 -0
  48. package/tools/nako3edit/html/edit.html +21 -0
  49. package/tools/nako3edit/html/nako3edit.css +10 -0
  50. package/tools/nako3edit/index.mjs +15 -4
  51. package/tools/nako3server/index.mjs +15 -4
  52. package/tools/nako3server/index.nako3 +4 -2
@@ -0,0 +1,239 @@
1
+ /* eslint-disable no-undef */
2
+ import assert from 'assert'
3
+ import path from 'path'
4
+ import fs from 'fs'
5
+ import http from 'http'
6
+ import os from 'os'
7
+ import { NakoCompiler } from '../../core/src/nako3.mjs'
8
+ import PluginHttpServer from '../../src/plugin_httpserver.mjs'
9
+
10
+ // __dirname のために
11
+ import url from 'url'
12
+ const __filename = url.fileURLToPath(import.meta.url)
13
+ const __dirname = path.dirname(__filename)
14
+
15
+ describe('plugin_httpserver_test', () => {
16
+ let nako
17
+ let serverDp
18
+
19
+ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
20
+
21
+ beforeEach(() => {
22
+ serverDp = null
23
+ nako = new NakoCompiler()
24
+ // PluginHttpServerの登録。名前は 'plugin_httpserver.js' とする。
25
+ nako.addPluginFile('PluginHttpServer', 'plugin_httpserver.js', PluginHttpServer)
26
+ })
27
+
28
+ afterEach(async () => {
29
+ if (serverDp && serverDp.server) {
30
+ await new Promise((resolve) => {
31
+ serverDp.server.close(() => {
32
+ resolve()
33
+ })
34
+ })
35
+ }
36
+ })
37
+
38
+ it('クエリパラメータ付きのURLで静的ファイルが取得できること', async () => {
39
+ // テスト用のダミーファイルを作成
40
+ const tempDir = path.join(__dirname, 'fixtures_http')
41
+ if (!fs.existsSync(tempDir)) {
42
+ fs.mkdirSync(tempDir, { recursive: true })
43
+ }
44
+ const tempFile = path.join(tempDir, 'test.txt')
45
+ fs.writeFileSync(tempFile, 'hello world')
46
+
47
+ let port = 0
48
+ // `簡易HTTPサーバ起動時`を呼ぶ
49
+ const code = `
50
+ ●ダミー起動
51
+ 戻る。
52
+ ここまで。
53
+ 「ダミー起動」を${port}で簡易HTTPサーバ起動時。
54
+ 「/」を「${tempDir.replace(/\\/g, '/')}」に簡易HTTPサーバ静的パス指定。
55
+ `
56
+ const g = await nako.runAsync(code, 'main')
57
+ serverDp = g.__httpserver
58
+ await wait(100)
59
+ port = serverDp.server.address().port
60
+
61
+ // クエリパラメータ付きでリクエストを送る
62
+ const resText = await new Promise((resolve, reject) => {
63
+ http.get(`http://localhost:${port}/test.txt?foo=bar&baz=123`, (res) => {
64
+ let data = ''
65
+ res.on('data', (chunk) => { data += chunk })
66
+ res.on('end', () => { resolve(data) })
67
+ }).on('error', reject)
68
+ })
69
+
70
+ assert.strictEqual(resText, 'hello world')
71
+
72
+ // 一時ファイルを削除
73
+ try {
74
+ fs.unlinkSync(tempFile)
75
+ fs.rmdirSync(tempDir)
76
+ } catch (e) {}
77
+ })
78
+
79
+ it('POSTメソッドで送信されたJSONデータを取得できること', async () => {
80
+ let port = 0
81
+ const code = `
82
+ ●ダミー起動
83
+ 戻る。
84
+ ここまで。
85
+ ●受信処理
86
+ POSTデータ["message"]を簡易HTTPサーバ出力。
87
+ ここまで。
88
+ 「ダミー起動」を${port}で簡易HTTPサーバ起動時。
89
+ 「受信処理」を「/post-json」に簡易HTTPサーバ受信時。
90
+ `
91
+ const g = await nako.runAsync(code, 'main')
92
+ serverDp = g.__httpserver
93
+ await wait(100)
94
+ port = serverDp.server.address().port
95
+
96
+ const postData = JSON.stringify({ message: 'hello post json' })
97
+ const resText = await new Promise((resolve, reject) => {
98
+ const req = http.request({
99
+ hostname: 'localhost',
100
+ port: port,
101
+ path: '/post-json',
102
+ method: 'POST',
103
+ headers: {
104
+ 'Content-Type': 'application/json',
105
+ 'Content-Length': Buffer.byteLength(postData)
106
+ }
107
+ }, (res) => {
108
+ let data = ''
109
+ res.on('data', (chunk) => { data += chunk })
110
+ res.on('end', () => { resolve(data) })
111
+ })
112
+ req.on('error', reject)
113
+ req.write(postData)
114
+ req.end()
115
+ })
116
+
117
+ assert.strictEqual(resText, 'hello post json')
118
+ })
119
+
120
+ it('POSTメソッドで送信されたurlencodedデータを取得できること', async () => {
121
+ let port = 0
122
+ const code = `
123
+ ●ダミー起動
124
+ 戻る。
125
+ ここまで。
126
+ ●受信処理
127
+ POSTデータ["message"]を簡易HTTPサーバ出力。
128
+ ここまで。
129
+ 「ダミー起動」を${port}で簡易HTTPサーバ起動時。
130
+ 「受信処理」を「/post-urlencoded」に簡易HTTPサーバ受信時。
131
+ `
132
+ const g = await nako.runAsync(code, 'main')
133
+ serverDp = g.__httpserver
134
+ await wait(100)
135
+ port = serverDp.server.address().port
136
+
137
+ const postData = 'message=hello+post+urlencoded'
138
+ const resText = await new Promise((resolve, reject) => {
139
+ const req = http.request({
140
+ hostname: 'localhost',
141
+ port: port,
142
+ path: '/post-urlencoded',
143
+ method: 'POST',
144
+ headers: {
145
+ 'Content-Type': 'application/x-www-form-urlencoded',
146
+ 'Content-Length': Buffer.byteLength(postData)
147
+ }
148
+ }, (res) => {
149
+ let data = ''
150
+ res.on('data', (chunk) => { data += chunk })
151
+ res.on('end', () => { resolve(data) })
152
+ })
153
+ req.on('error', reject)
154
+ req.write(postData)
155
+ req.end()
156
+ })
157
+
158
+ assert.strictEqual(resText, 'hello post urlencoded')
159
+ })
160
+
161
+ it('POSTメソッドでファイルをアップロードしてFILESデータを取得できること', async () => {
162
+ const originalWriteFileSync = fs.writeFileSync
163
+ fs.writeFileSync = () => {
164
+ throw new Error('writeFileSync should not be used during upload handling')
165
+ }
166
+
167
+ let port = 0
168
+ const code = `
169
+ ●ダミー起動
170
+ 戻る。
171
+ ここまで。
172
+ ●受信処理
173
+ ファイル情報=FILESデータ[0]
174
+ もし、ファイル情報["path"]が空でなければ
175
+ ファイル名=ファイル情報["name"]
176
+ ファイルサイズ=ファイル情報["size"]
177
+ 「OK:{ファイル名}:{ファイルサイズ}」を簡易HTTPサーバ出力。
178
+ 違えば
179
+ 「NG」を簡易HTTPサーバ出力。
180
+ ここまで。
181
+ ここまで。
182
+ 「ダミー起動」を${port}で簡易HTTPサーバ起動時。
183
+ 「受信処理」を「/upload」に簡易HTTPサーバ受信時。
184
+ `
185
+ const g = await nako.runAsync(code, 'main')
186
+ serverDp = g.__httpserver
187
+ await wait(100)
188
+ port = serverDp.server.address().port
189
+
190
+ const boundary = '----TestBoundary'
191
+ const parts = [
192
+ `--${boundary}\r\n`,
193
+ `Content-Disposition: form-data; name="file1"; filename="hello.txt"\r\n`,
194
+ `Content-Type: text/plain\r\n\r\n`,
195
+ `hello world nako3 upload\r\n`,
196
+ `--${boundary}--\r\n`
197
+ ]
198
+ const postData = Buffer.from(parts.join(''))
199
+
200
+ const resText = await new Promise((resolve, reject) => {
201
+ const req = http.request({
202
+ hostname: 'localhost',
203
+ port: port,
204
+ path: '/upload',
205
+ method: 'POST',
206
+ headers: {
207
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
208
+ 'Content-Length': postData.length
209
+ }
210
+ }, (res) => {
211
+ let data = ''
212
+ res.on('data', (chunk) => { data += chunk })
213
+ res.on('end', () => { resolve(data) })
214
+ })
215
+ req.on('error', reject)
216
+ req.write(postData)
217
+ req.end()
218
+ })
219
+
220
+ try {
221
+ assert.strictEqual(resText, 'OK:hello.txt:24')
222
+
223
+ const uploadDir = path.join(os.tmpdir(), 'nako3-plugin_httpserver_upload')
224
+ if (fs.existsSync(uploadDir)) {
225
+ const files = fs.readdirSync(uploadDir)
226
+ for (const file of files) {
227
+ try {
228
+ fs.unlinkSync(path.join(uploadDir, file))
229
+ } catch (e) {}
230
+ }
231
+ try {
232
+ fs.rmdirSync(uploadDir)
233
+ } catch (e) {}
234
+ }
235
+ } finally {
236
+ fs.writeFileSync = originalWriteFileSync
237
+ }
238
+ })
239
+ })
@@ -64,6 +64,12 @@ FNAME="無題.nako3"
64
64
  Hの「(\[\d+m)」を「」に正規表現置換してHに代入。
65
65
  Hの「(\[.*?\])」を「<span style='color:red;font-weight:bold;'>$1</span>」に正規表現置換してHに代入。
66
66
  Hの「(『.*?』)」を「<span style='color:blue;font-weight:bold;'>$1</span>」に正規表現置換してHに代入。
67
+ H = 「
68
+ <div class="error">{H}</div>
69
+ <div style="text-align: right; margin-top: 8px;">
70
+ <button class="btn btn-sm" onclick="copyError()">エラーをコピー</button>
71
+ </div>
72
+
67
73
  ここまで。
68
74
  「#result」にHをDOMテキスト設定。
69
75
  ここまで。
@@ -168,4 +174,19 @@ FNAME="無題.nako3"
168
174
  </div>
169
175
  </div>
170
176
 
177
+ <!-- 補助スクリプト -->
178
+ <script>
179
+ function copyError() {
180
+ const errorText = document.querySelector('.error').innerText;
181
+ navigator.clipboard.writeText(errorText).then(() => {
182
+ alert('エラー内容をコピーしました');
183
+ }).catch(err => {
184
+ const errorMessage = (err && err.message) ? err.message : String(err);
185
+ console.error('エラー内容のコピーに失敗しました', err);
186
+ alert('エラー内容のコピーに失敗しました。ブラウザの権限設定や HTTPS 接続を確認してください。\n詳細: ' + errorMessage);
187
+ });
188
+ }
189
+
190
+ </script>
191
+
171
192
  </html>
@@ -91,3 +91,13 @@ h1 {
91
91
  text-align: left;
92
92
  border-radius: 0.4em;
93
93
  }
94
+
95
+ .error {
96
+ background-color: #fff0f0;
97
+ border: 1px solid #ff8080;
98
+ padding: 8px;
99
+ margin: 4px;
100
+ font-size: 1em;
101
+ text-align: left;
102
+ border-radius: 0.4em;
103
+ }
@@ -12,8 +12,8 @@ const __filename = url.fileURLToPath(import.meta.url)
12
12
  const __dirname = path.dirname(__filename)
13
13
 
14
14
  // CONST
15
- const SERVER_PORT = Number.parseInt(process.env.NAKO3EDIT_PORT || process.argv[2] || '8888', 10)
16
- const SERVER_HOST = process.env.NAKO3EDIT_HOST || undefined
15
+ const SERVER_PORT = resolveServerPort(8888)
16
+ const SERVER_HOST = process.env.NAKO3EDIT_HOST || 'localhost'
17
17
  const rootDir = path.resolve(__dirname)
18
18
  const releaseDir = path.resolve(path.join(__dirname, '../../release'))
19
19
  const isWin = process.platform === 'win32'
@@ -132,9 +132,8 @@ const server = http.createServer(function (req, res) {
132
132
  })
133
133
  // サーバを起動
134
134
  server.listen(SERVER_PORT, SERVER_HOST, function () {
135
- const url = 'http://localhost:' + SERVER_PORT
135
+ const url = 'http://' + SERVER_HOST + ':' + SERVER_PORT
136
136
  console.log('### 超簡易Webサーバが起動しました')
137
- console.log('### script: /tools/nako3edit/index.mjs')
138
137
  console.log('[URL]', url)
139
138
  if (process.env.NAKO3EDIT_OPEN !== '0') {
140
139
  opener(url)
@@ -355,3 +354,15 @@ function apiAddPlugins(res, params) {
355
354
  res.end('"[ERROR] プラグインの取得に失敗しました。"')
356
355
  }
357
356
  }
357
+
358
+ // ポート番号の解決
359
+ function resolveServerPort (defaultPort) {
360
+ const rawPort = process.env.NAKO3EDIT_PORT || process.argv[2] || defaultPort
361
+ const port = Number.parseInt(rawPort, 10)
362
+ if (Number.isNaN(port) || port < 0 || port > 65535) {
363
+ console.error(`[ERROR] 無効なポート番号です: ${rawPort}`)
364
+ console.error('[ERROR] 環境変数 PORT またはコマンドライン引数には 0 から 65535 の整数を指定してください。')
365
+ process.exit(1)
366
+ }
367
+ return port
368
+ }
@@ -12,8 +12,8 @@ const __filename = url.fileURLToPath(import.meta.url)
12
12
  const __dirname = path.dirname(__filename)
13
13
 
14
14
  // CONST
15
- const SERVER_PORT = Number.parseInt(process.env.PORT || process.argv[2] || '3000', 10)
16
- const SERVER_HOST = process.env.NAKO3SERVER_HOST || undefined
15
+ const SERVER_PORT = resolveServerPort(3000)
16
+ const SERVER_HOST = process.env.NAKO3SERVER_HOST || 'localhost'
17
17
  const rootDir = path.resolve(__dirname, '../../')
18
18
 
19
19
  // ライブラリがあるかチェック
@@ -86,9 +86,8 @@ const server = http.createServer(function (req, res) {
86
86
  })
87
87
  // サーバを起動
88
88
  server.listen(SERVER_PORT, SERVER_HOST, function () {
89
- const url = 'http://localhost:' + SERVER_PORT
89
+ const url = 'http://' + SERVER_HOST + ':' + SERVER_PORT
90
90
  console.log('### 超簡易Webサーバが起動しました')
91
- console.log('### script: /src/nako3server.mjs')
92
91
  console.log('[URL]', url)
93
92
  if (process.env.NAKO3SERVER_OPEN !== '0') {
94
93
  opener(url)
@@ -124,3 +123,15 @@ function isDir (pathName) {
124
123
  }
125
124
  return false
126
125
  }
126
+
127
+ // ポート番号を検証して、安全な値のみを利用する
128
+ function resolveServerPort (defaultPort) {
129
+ const rawPort = process.env.PORT || process.argv[2] || defaultPort
130
+ const port = Number.parseInt(rawPort, 10)
131
+ if (Number.isNaN(port) || port < 0 || port > 65535) {
132
+ console.error(`[ERROR] 無効なポート番号です: ${rawPort}`)
133
+ console.error('[ERROR] 環境変数 PORT またはコマンドライン引数には 0 から 65535 の整数を指定してください。')
134
+ process.exit(1)
135
+ }
136
+ return port
137
+ }
@@ -2,9 +2,11 @@
2
2
 
3
3
  ルート=母艦パスを「../../」で相対パス展開。
4
4
  DEMOパス=ルート&「/demo」
5
+ PORT=8888
5
6
 
6
- 8888で簡易HTTPサーバ起動時には
7
- 「起動しました」と表示。
7
+ PORTで簡易HTTPサーバ起動時には
8
+ 「起動しました。ブラウザで以下のURLにアクセスしてください。」と表示。
9
+ 「http://localhost:{PORT}」と表示。
8
10
  ここまで。
9
11
 
10
12
  # 挨拶