@adobe/aem-cli 16.15.12 → 16.16.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/CHANGELOG.md +15 -0
- package/README.md +21 -0
- package/package.json +7 -7
- package/packages/browser-injectables/package-lock.json +22 -74
- package/packages/browser-injectables/package.json +1 -1
- package/src/server/HelixServer.js +108 -44
- package/src/server/utils.js +250 -0
- package/src/up.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# [16.16.0](https://github.com/adobe/helix-cli/compare/v16.15.13...v16.16.0) (2025-11-13)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add .plain.html support for HTML folder serving ([#2631](https://github.com/adobe/helix-cli/issues/2631)) ([2186d4c](https://github.com/adobe/helix-cli/commit/2186d4c330f97db86849b9388e119b7d599c9fc1))
|
|
7
|
+
|
|
8
|
+
## [16.15.13](https://github.com/adobe/helix-cli/compare/v16.15.12...v16.15.13) (2025-11-11)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **deps:** update dependency ini to v6 ([7c37973](https://github.com/adobe/helix-cli/commit/7c37973c45d7d6956bbc3200d46c2015d7e483fe))
|
|
14
|
+
* **deps:** update external fixes ([#2630](https://github.com/adobe/helix-cli/issues/2630)) ([8671a13](https://github.com/adobe/helix-cli/commit/8671a131f4cb1e3d29e318cfb7c3b3326d2cea77))
|
|
15
|
+
|
|
1
16
|
## [16.15.12](https://github.com/adobe/helix-cli/compare/v16.15.11...v16.15.12) (2025-10-30)
|
|
2
17
|
|
|
3
18
|
|
package/README.md
CHANGED
|
@@ -74,6 +74,26 @@ This feature is especially helpful when:
|
|
|
74
74
|
- Monitoring client-side behavior during development
|
|
75
75
|
- Working with AI coding assistants that need visibility into both server and client logs
|
|
76
76
|
|
|
77
|
+
### html folder for content preview
|
|
78
|
+
|
|
79
|
+
The `--html-folder` option enables serving HTML files without extensions, useful for previewing content changes when you don't have access to the authoring system.
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
$ aem up --html-folder content
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
This enables two features:
|
|
86
|
+
|
|
87
|
+
1. **Extension-less URLs**: Access `/content/page` to serve `content/page.html`
|
|
88
|
+
2. **Plain HTML with metadata**: Create `content/page.plain.html` files that are automatically wrapped with proper HTML structure and metadata processing
|
|
89
|
+
|
|
90
|
+
#### Plain HTML files (.plain.html)
|
|
91
|
+
|
|
92
|
+
Plain HTML files contain only the main content and an optional metadata block. The CLI automatically:
|
|
93
|
+
- Wraps content in `<html><head><body><header><main><footer>` structure
|
|
94
|
+
- Merges in `head.html` content
|
|
95
|
+
- The metadata block is removed from the rendered content and converted to meta tags in the `<head>`.
|
|
96
|
+
|
|
77
97
|
### setting up a self-signed cert for using https
|
|
78
98
|
|
|
79
99
|
1. create the certificate
|
|
@@ -161,6 +181,7 @@ If present, `ALL_PROXY` is used as fallback if there is no other match.
|
|
|
161
181
|
| `--livereload` | `AEM_LIVERELOAD` | `true` | Enable automatic reloading of modified sources in browser. |
|
|
162
182
|
| `--no-livereload` | `AEM_NO_LIVERELOAD` | `false` | Disable live-reload. |
|
|
163
183
|
| `--forward-browser-logs` | `AEM_FORWARD_BROWSER_LOGS` | `false` | Forward browser console logs to terminal. |
|
|
184
|
+
| `--html-folder` | `AEM_HTML_FOLDER` | undefined | Serve HTML files from folder without extensions. Supports .html and .plain.html files. |
|
|
164
185
|
| `--open` | `AEM_OPEN` | `/` | Open a browser window at specified path after server start. |
|
|
165
186
|
| `--no-open` | `AEM_NO_OPEN` | `false` | Disable automatic opening of browser window. |
|
|
166
187
|
| `--tls-key` | `AEM_TLS_KEY` | undefined | Path to .key file (for enabling TLS) |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/aem-cli",
|
|
3
|
-
"version": "16.
|
|
3
|
+
"version": "16.16.0",
|
|
4
4
|
"description": "AEM CLI",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"http-proxy-agent": "7.0.2",
|
|
65
65
|
"https-proxy-agent": "7.0.6",
|
|
66
66
|
"ignore": "7.0.5",
|
|
67
|
-
"ini": "
|
|
68
|
-
"isomorphic-git": "1.34.
|
|
67
|
+
"ini": "6.0.0",
|
|
68
|
+
"isomorphic-git": "1.34.1",
|
|
69
69
|
"jose": "6.1.0",
|
|
70
70
|
"livereload-js": "4.0.2",
|
|
71
71
|
"node-fetch": "3.3.2",
|
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
"yargs": "18.0.0"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
|
-
"@adobe/eslint-config-helix": "3.0.
|
|
85
|
-
"@eslint/config-helpers": "0.4.
|
|
84
|
+
"@adobe/eslint-config-helix": "3.0.13",
|
|
85
|
+
"@eslint/config-helpers": "0.4.1",
|
|
86
86
|
"@semantic-release/changelog": "6.0.3",
|
|
87
87
|
"@semantic-release/git": "10.0.1",
|
|
88
88
|
"c8": "10.1.3",
|
|
@@ -90,11 +90,11 @@
|
|
|
90
90
|
"esmock": "2.7.3",
|
|
91
91
|
"husky": "9.1.7",
|
|
92
92
|
"junit-report-builder": "5.1.1",
|
|
93
|
-
"lint-staged": "16.2.
|
|
93
|
+
"lint-staged": "16.2.6",
|
|
94
94
|
"mocha": "11.7.4",
|
|
95
95
|
"mocha-multi-reporters": "1.5.1",
|
|
96
96
|
"nock": "13.5.6",
|
|
97
|
-
"semantic-release": "25.0.
|
|
97
|
+
"semantic-release": "25.0.1",
|
|
98
98
|
"semantic-release-discord-bot": "^1.1.0",
|
|
99
99
|
"sinon": "21.0.0"
|
|
100
100
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@adobe/eslint-config-helix": "^3.0.9",
|
|
16
|
-
"@eslint/config-helpers": "0.4.
|
|
16
|
+
"@eslint/config-helpers": "0.4.1",
|
|
17
17
|
"@esm-bundle/chai": "4.3.4-fix.0",
|
|
18
18
|
"@web/test-runner": "0.20.2",
|
|
19
19
|
"@web/test-runner-junit-reporter": "0.8.0",
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"node_modules/@adobe/eslint-config-helix": {
|
|
25
|
-
"version": "3.0.
|
|
26
|
-
"resolved": "https://registry.npmjs.org/@adobe/eslint-config-helix/-/eslint-config-helix-3.0.
|
|
27
|
-
"integrity": "sha512-
|
|
25
|
+
"version": "3.0.13",
|
|
26
|
+
"resolved": "https://registry.npmjs.org/@adobe/eslint-config-helix/-/eslint-config-helix-3.0.13.tgz",
|
|
27
|
+
"integrity": "sha512-p5FJfGhQVaMGAfwweyh1R46MJi7Pi0XNaZLKH4X7lBA8mgT7oTV9l48H7ifWB+R5rRl2XpsvsZCM2q2HfDRDoA==",
|
|
28
28
|
"dev": true,
|
|
29
29
|
"license": "Apache-2.0",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@eslint/config-helpers": "0.4.
|
|
31
|
+
"@eslint/config-helpers": "0.4.1",
|
|
32
32
|
"eslint-plugin-import": "2.32.0",
|
|
33
33
|
"globals": "16.4.0"
|
|
34
34
|
},
|
|
@@ -70,7 +70,6 @@
|
|
|
70
70
|
"integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
|
|
71
71
|
"dev": true,
|
|
72
72
|
"license": "MIT",
|
|
73
|
-
"peer": true,
|
|
74
73
|
"dependencies": {
|
|
75
74
|
"eslint-visitor-keys": "^3.4.3"
|
|
76
75
|
},
|
|
@@ -90,7 +89,6 @@
|
|
|
90
89
|
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
|
|
91
90
|
"dev": true,
|
|
92
91
|
"license": "Apache-2.0",
|
|
93
|
-
"peer": true,
|
|
94
92
|
"engines": {
|
|
95
93
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
|
96
94
|
},
|
|
@@ -104,7 +102,6 @@
|
|
|
104
102
|
"integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
|
|
105
103
|
"dev": true,
|
|
106
104
|
"license": "MIT",
|
|
107
|
-
"peer": true,
|
|
108
105
|
"engines": {
|
|
109
106
|
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
|
|
110
107
|
}
|
|
@@ -115,7 +112,6 @@
|
|
|
115
112
|
"integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
|
|
116
113
|
"dev": true,
|
|
117
114
|
"license": "Apache-2.0",
|
|
118
|
-
"peer": true,
|
|
119
115
|
"dependencies": {
|
|
120
116
|
"@eslint/object-schema": "^2.1.6",
|
|
121
117
|
"debug": "^4.3.1",
|
|
@@ -126,9 +122,9 @@
|
|
|
126
122
|
}
|
|
127
123
|
},
|
|
128
124
|
"node_modules/@eslint/config-helpers": {
|
|
129
|
-
"version": "0.4.
|
|
130
|
-
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.
|
|
131
|
-
"integrity": "sha512-
|
|
125
|
+
"version": "0.4.1",
|
|
126
|
+
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz",
|
|
127
|
+
"integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==",
|
|
132
128
|
"dev": true,
|
|
133
129
|
"license": "Apache-2.0",
|
|
134
130
|
"dependencies": {
|
|
@@ -157,7 +153,6 @@
|
|
|
157
153
|
"integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
|
|
158
154
|
"dev": true,
|
|
159
155
|
"license": "MIT",
|
|
160
|
-
"peer": true,
|
|
161
156
|
"dependencies": {
|
|
162
157
|
"ajv": "^6.12.4",
|
|
163
158
|
"debug": "^4.3.2",
|
|
@@ -182,7 +177,6 @@
|
|
|
182
177
|
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
|
|
183
178
|
"dev": true,
|
|
184
179
|
"license": "MIT",
|
|
185
|
-
"peer": true,
|
|
186
180
|
"engines": {
|
|
187
181
|
"node": ">=18"
|
|
188
182
|
},
|
|
@@ -196,7 +190,6 @@
|
|
|
196
190
|
"integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==",
|
|
197
191
|
"dev": true,
|
|
198
192
|
"license": "MIT",
|
|
199
|
-
"peer": true,
|
|
200
193
|
"engines": {
|
|
201
194
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
|
202
195
|
},
|
|
@@ -210,7 +203,6 @@
|
|
|
210
203
|
"integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
|
|
211
204
|
"dev": true,
|
|
212
205
|
"license": "Apache-2.0",
|
|
213
|
-
"peer": true,
|
|
214
206
|
"engines": {
|
|
215
207
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
|
216
208
|
}
|
|
@@ -221,7 +213,6 @@
|
|
|
221
213
|
"integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==",
|
|
222
214
|
"dev": true,
|
|
223
215
|
"license": "Apache-2.0",
|
|
224
|
-
"peer": true,
|
|
225
216
|
"dependencies": {
|
|
226
217
|
"@eslint/core": "^0.16.0",
|
|
227
218
|
"levn": "^0.4.1"
|
|
@@ -253,7 +244,6 @@
|
|
|
253
244
|
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
|
|
254
245
|
"dev": true,
|
|
255
246
|
"license": "Apache-2.0",
|
|
256
|
-
"peer": true,
|
|
257
247
|
"engines": {
|
|
258
248
|
"node": ">=18.18.0"
|
|
259
249
|
}
|
|
@@ -264,7 +254,6 @@
|
|
|
264
254
|
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
|
|
265
255
|
"dev": true,
|
|
266
256
|
"license": "Apache-2.0",
|
|
267
|
-
"peer": true,
|
|
268
257
|
"dependencies": {
|
|
269
258
|
"@humanfs/core": "^0.19.1",
|
|
270
259
|
"@humanwhocodes/retry": "^0.4.0"
|
|
@@ -279,7 +268,6 @@
|
|
|
279
268
|
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
|
|
280
269
|
"dev": true,
|
|
281
270
|
"license": "Apache-2.0",
|
|
282
|
-
"peer": true,
|
|
283
271
|
"engines": {
|
|
284
272
|
"node": ">=12.22"
|
|
285
273
|
},
|
|
@@ -294,7 +282,6 @@
|
|
|
294
282
|
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
|
|
295
283
|
"dev": true,
|
|
296
284
|
"license": "Apache-2.0",
|
|
297
|
-
"peer": true,
|
|
298
285
|
"engines": {
|
|
299
286
|
"node": ">=18.18"
|
|
300
287
|
},
|
|
@@ -1414,7 +1401,6 @@
|
|
|
1414
1401
|
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
|
|
1415
1402
|
"dev": true,
|
|
1416
1403
|
"license": "MIT",
|
|
1417
|
-
"peer": true,
|
|
1418
1404
|
"peerDependencies": {
|
|
1419
1405
|
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
1420
1406
|
}
|
|
@@ -1435,7 +1421,6 @@
|
|
|
1435
1421
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
|
1436
1422
|
"dev": true,
|
|
1437
1423
|
"license": "MIT",
|
|
1438
|
-
"peer": true,
|
|
1439
1424
|
"dependencies": {
|
|
1440
1425
|
"fast-deep-equal": "^3.1.1",
|
|
1441
1426
|
"fast-json-stable-stringify": "^2.0.0",
|
|
@@ -1494,8 +1479,7 @@
|
|
|
1494
1479
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
|
1495
1480
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
|
1496
1481
|
"dev": true,
|
|
1497
|
-
"license": "Python-2.0"
|
|
1498
|
-
"peer": true
|
|
1482
|
+
"license": "Python-2.0"
|
|
1499
1483
|
},
|
|
1500
1484
|
"node_modules/array-back": {
|
|
1501
1485
|
"version": "3.1.0",
|
|
@@ -1940,7 +1924,6 @@
|
|
|
1940
1924
|
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
|
1941
1925
|
"dev": true,
|
|
1942
1926
|
"license": "MIT",
|
|
1943
|
-
"peer": true,
|
|
1944
1927
|
"engines": {
|
|
1945
1928
|
"node": ">=6"
|
|
1946
1929
|
}
|
|
@@ -2363,8 +2346,7 @@
|
|
|
2363
2346
|
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
|
2364
2347
|
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
|
|
2365
2348
|
"dev": true,
|
|
2366
|
-
"license": "MIT"
|
|
2367
|
-
"peer": true
|
|
2349
|
+
"license": "MIT"
|
|
2368
2350
|
},
|
|
2369
2351
|
"node_modules/deepmerge": {
|
|
2370
2352
|
"version": "4.3.1",
|
|
@@ -2493,7 +2475,8 @@
|
|
|
2493
2475
|
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1508733.tgz",
|
|
2494
2476
|
"integrity": "sha512-QJ1R5gtck6nDcdM+nlsaJXcelPEI7ZxSMw1ujHpO1c4+9l+Nue5qlebi9xO1Z2MGr92bFOQTW7/rrheh5hHxDg==",
|
|
2495
2477
|
"dev": true,
|
|
2496
|
-
"license": "BSD-3-Clause"
|
|
2478
|
+
"license": "BSD-3-Clause",
|
|
2479
|
+
"peer": true
|
|
2497
2480
|
},
|
|
2498
2481
|
"node_modules/diff": {
|
|
2499
2482
|
"version": "5.2.0",
|
|
@@ -2968,7 +2951,6 @@
|
|
|
2968
2951
|
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
|
|
2969
2952
|
"dev": true,
|
|
2970
2953
|
"license": "BSD-2-Clause",
|
|
2971
|
-
"peer": true,
|
|
2972
2954
|
"dependencies": {
|
|
2973
2955
|
"esrecurse": "^4.3.0",
|
|
2974
2956
|
"estraverse": "^5.2.0"
|
|
@@ -2986,7 +2968,6 @@
|
|
|
2986
2968
|
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
|
|
2987
2969
|
"dev": true,
|
|
2988
2970
|
"license": "Apache-2.0",
|
|
2989
|
-
"peer": true,
|
|
2990
2971
|
"engines": {
|
|
2991
2972
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
|
2992
2973
|
},
|
|
@@ -3000,7 +2981,6 @@
|
|
|
3000
2981
|
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
|
|
3001
2982
|
"dev": true,
|
|
3002
2983
|
"license": "BSD-2-Clause",
|
|
3003
|
-
"peer": true,
|
|
3004
2984
|
"dependencies": {
|
|
3005
2985
|
"acorn": "^8.15.0",
|
|
3006
2986
|
"acorn-jsx": "^5.3.2",
|
|
@@ -3033,7 +3013,6 @@
|
|
|
3033
3013
|
"integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
|
|
3034
3014
|
"dev": true,
|
|
3035
3015
|
"license": "BSD-3-Clause",
|
|
3036
|
-
"peer": true,
|
|
3037
3016
|
"dependencies": {
|
|
3038
3017
|
"estraverse": "^5.1.0"
|
|
3039
3018
|
},
|
|
@@ -3047,7 +3026,6 @@
|
|
|
3047
3026
|
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
|
|
3048
3027
|
"dev": true,
|
|
3049
3028
|
"license": "BSD-2-Clause",
|
|
3050
|
-
"peer": true,
|
|
3051
3029
|
"dependencies": {
|
|
3052
3030
|
"estraverse": "^5.2.0"
|
|
3053
3031
|
},
|
|
@@ -3168,8 +3146,7 @@
|
|
|
3168
3146
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
|
3169
3147
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
|
3170
3148
|
"dev": true,
|
|
3171
|
-
"license": "MIT"
|
|
3172
|
-
"peer": true
|
|
3149
|
+
"license": "MIT"
|
|
3173
3150
|
},
|
|
3174
3151
|
"node_modules/fast-fifo": {
|
|
3175
3152
|
"version": "1.3.2",
|
|
@@ -3213,16 +3190,14 @@
|
|
|
3213
3190
|
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
|
3214
3191
|
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
|
3215
3192
|
"dev": true,
|
|
3216
|
-
"license": "MIT"
|
|
3217
|
-
"peer": true
|
|
3193
|
+
"license": "MIT"
|
|
3218
3194
|
},
|
|
3219
3195
|
"node_modules/fast-levenshtein": {
|
|
3220
3196
|
"version": "2.0.6",
|
|
3221
3197
|
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
|
|
3222
3198
|
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
|
|
3223
3199
|
"dev": true,
|
|
3224
|
-
"license": "MIT"
|
|
3225
|
-
"peer": true
|
|
3200
|
+
"license": "MIT"
|
|
3226
3201
|
},
|
|
3227
3202
|
"node_modules/fastq": {
|
|
3228
3203
|
"version": "1.19.1",
|
|
@@ -3250,7 +3225,6 @@
|
|
|
3250
3225
|
"integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
|
|
3251
3226
|
"dev": true,
|
|
3252
3227
|
"license": "MIT",
|
|
3253
|
-
"peer": true,
|
|
3254
3228
|
"dependencies": {
|
|
3255
3229
|
"flat-cache": "^4.0.0"
|
|
3256
3230
|
},
|
|
@@ -3290,7 +3264,6 @@
|
|
|
3290
3264
|
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
|
|
3291
3265
|
"dev": true,
|
|
3292
3266
|
"license": "MIT",
|
|
3293
|
-
"peer": true,
|
|
3294
3267
|
"dependencies": {
|
|
3295
3268
|
"locate-path": "^6.0.0",
|
|
3296
3269
|
"path-exists": "^4.0.0"
|
|
@@ -3308,7 +3281,6 @@
|
|
|
3308
3281
|
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
|
|
3309
3282
|
"dev": true,
|
|
3310
3283
|
"license": "MIT",
|
|
3311
|
-
"peer": true,
|
|
3312
3284
|
"dependencies": {
|
|
3313
3285
|
"flatted": "^3.2.9",
|
|
3314
3286
|
"keyv": "^4.5.4"
|
|
@@ -3322,8 +3294,7 @@
|
|
|
3322
3294
|
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
|
|
3323
3295
|
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
|
|
3324
3296
|
"dev": true,
|
|
3325
|
-
"license": "ISC"
|
|
3326
|
-
"peer": true
|
|
3297
|
+
"license": "ISC"
|
|
3327
3298
|
},
|
|
3328
3299
|
"node_modules/for-each": {
|
|
3329
3300
|
"version": "0.3.5",
|
|
@@ -3518,7 +3489,6 @@
|
|
|
3518
3489
|
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
|
3519
3490
|
"dev": true,
|
|
3520
3491
|
"license": "ISC",
|
|
3521
|
-
"peer": true,
|
|
3522
3492
|
"dependencies": {
|
|
3523
3493
|
"is-glob": "^4.0.3"
|
|
3524
3494
|
},
|
|
@@ -3799,7 +3769,6 @@
|
|
|
3799
3769
|
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
|
|
3800
3770
|
"dev": true,
|
|
3801
3771
|
"license": "MIT",
|
|
3802
|
-
"peer": true,
|
|
3803
3772
|
"dependencies": {
|
|
3804
3773
|
"parent-module": "^1.0.0",
|
|
3805
3774
|
"resolve-from": "^4.0.0"
|
|
@@ -3817,7 +3786,6 @@
|
|
|
3817
3786
|
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
|
|
3818
3787
|
"dev": true,
|
|
3819
3788
|
"license": "MIT",
|
|
3820
|
-
"peer": true,
|
|
3821
3789
|
"engines": {
|
|
3822
3790
|
"node": ">=0.8.19"
|
|
3823
3791
|
}
|
|
@@ -4446,7 +4414,6 @@
|
|
|
4446
4414
|
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
|
4447
4415
|
"dev": true,
|
|
4448
4416
|
"license": "MIT",
|
|
4449
|
-
"peer": true,
|
|
4450
4417
|
"dependencies": {
|
|
4451
4418
|
"argparse": "^2.0.1"
|
|
4452
4419
|
},
|
|
@@ -4459,24 +4426,21 @@
|
|
|
4459
4426
|
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
|
4460
4427
|
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
|
|
4461
4428
|
"dev": true,
|
|
4462
|
-
"license": "MIT"
|
|
4463
|
-
"peer": true
|
|
4429
|
+
"license": "MIT"
|
|
4464
4430
|
},
|
|
4465
4431
|
"node_modules/json-schema-traverse": {
|
|
4466
4432
|
"version": "0.4.1",
|
|
4467
4433
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
|
4468
4434
|
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
|
4469
4435
|
"dev": true,
|
|
4470
|
-
"license": "MIT"
|
|
4471
|
-
"peer": true
|
|
4436
|
+
"license": "MIT"
|
|
4472
4437
|
},
|
|
4473
4438
|
"node_modules/json-stable-stringify-without-jsonify": {
|
|
4474
4439
|
"version": "1.0.1",
|
|
4475
4440
|
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
|
|
4476
4441
|
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
|
|
4477
4442
|
"dev": true,
|
|
4478
|
-
"license": "MIT"
|
|
4479
|
-
"peer": true
|
|
4443
|
+
"license": "MIT"
|
|
4480
4444
|
},
|
|
4481
4445
|
"node_modules/json5": {
|
|
4482
4446
|
"version": "1.0.2",
|
|
@@ -4510,7 +4474,6 @@
|
|
|
4510
4474
|
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
|
|
4511
4475
|
"dev": true,
|
|
4512
4476
|
"license": "MIT",
|
|
4513
|
-
"peer": true,
|
|
4514
4477
|
"dependencies": {
|
|
4515
4478
|
"json-buffer": "3.0.1"
|
|
4516
4479
|
}
|
|
@@ -4626,7 +4589,6 @@
|
|
|
4626
4589
|
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
|
|
4627
4590
|
"dev": true,
|
|
4628
4591
|
"license": "MIT",
|
|
4629
|
-
"peer": true,
|
|
4630
4592
|
"dependencies": {
|
|
4631
4593
|
"prelude-ls": "^1.2.1",
|
|
4632
4594
|
"type-check": "~0.4.0"
|
|
@@ -4675,7 +4637,6 @@
|
|
|
4675
4637
|
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
|
|
4676
4638
|
"dev": true,
|
|
4677
4639
|
"license": "MIT",
|
|
4678
|
-
"peer": true,
|
|
4679
4640
|
"dependencies": {
|
|
4680
4641
|
"p-locate": "^5.0.0"
|
|
4681
4642
|
},
|
|
@@ -4698,8 +4659,7 @@
|
|
|
4698
4659
|
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
|
4699
4660
|
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
|
4700
4661
|
"dev": true,
|
|
4701
|
-
"license": "MIT"
|
|
4702
|
-
"peer": true
|
|
4662
|
+
"license": "MIT"
|
|
4703
4663
|
},
|
|
4704
4664
|
"node_modules/log-update": {
|
|
4705
4665
|
"version": "4.0.0",
|
|
@@ -4931,8 +4891,7 @@
|
|
|
4931
4891
|
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
|
4932
4892
|
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
|
4933
4893
|
"dev": true,
|
|
4934
|
-
"license": "MIT"
|
|
4935
|
-
"peer": true
|
|
4894
|
+
"license": "MIT"
|
|
4936
4895
|
},
|
|
4937
4896
|
"node_modules/negotiator": {
|
|
4938
4897
|
"version": "0.6.3",
|
|
@@ -5133,7 +5092,6 @@
|
|
|
5133
5092
|
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
|
|
5134
5093
|
"dev": true,
|
|
5135
5094
|
"license": "MIT",
|
|
5136
|
-
"peer": true,
|
|
5137
5095
|
"dependencies": {
|
|
5138
5096
|
"deep-is": "^0.1.3",
|
|
5139
5097
|
"fast-levenshtein": "^2.0.6",
|
|
@@ -5196,7 +5154,6 @@
|
|
|
5196
5154
|
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
|
|
5197
5155
|
"dev": true,
|
|
5198
5156
|
"license": "MIT",
|
|
5199
|
-
"peer": true,
|
|
5200
5157
|
"dependencies": {
|
|
5201
5158
|
"yocto-queue": "^0.1.0"
|
|
5202
5159
|
},
|
|
@@ -5213,7 +5170,6 @@
|
|
|
5213
5170
|
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
|
|
5214
5171
|
"dev": true,
|
|
5215
5172
|
"license": "MIT",
|
|
5216
|
-
"peer": true,
|
|
5217
5173
|
"dependencies": {
|
|
5218
5174
|
"p-limit": "^3.0.2"
|
|
5219
5175
|
},
|
|
@@ -5277,7 +5233,6 @@
|
|
|
5277
5233
|
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
|
5278
5234
|
"dev": true,
|
|
5279
5235
|
"license": "MIT",
|
|
5280
|
-
"peer": true,
|
|
5281
5236
|
"dependencies": {
|
|
5282
5237
|
"callsites": "^3.0.0"
|
|
5283
5238
|
},
|
|
@@ -5308,7 +5263,6 @@
|
|
|
5308
5263
|
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
|
5309
5264
|
"dev": true,
|
|
5310
5265
|
"license": "MIT",
|
|
5311
|
-
"peer": true,
|
|
5312
5266
|
"engines": {
|
|
5313
5267
|
"node": ">=8"
|
|
5314
5268
|
}
|
|
@@ -5439,7 +5393,6 @@
|
|
|
5439
5393
|
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
|
|
5440
5394
|
"dev": true,
|
|
5441
5395
|
"license": "MIT",
|
|
5442
|
-
"peer": true,
|
|
5443
5396
|
"engines": {
|
|
5444
5397
|
"node": ">= 0.8.0"
|
|
5445
5398
|
}
|
|
@@ -5728,7 +5681,6 @@
|
|
|
5728
5681
|
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
|
5729
5682
|
"dev": true,
|
|
5730
5683
|
"license": "MIT",
|
|
5731
|
-
"peer": true,
|
|
5732
5684
|
"engines": {
|
|
5733
5685
|
"node": ">=4"
|
|
5734
5686
|
}
|
|
@@ -5818,6 +5770,7 @@
|
|
|
5818
5770
|
"integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==",
|
|
5819
5771
|
"dev": true,
|
|
5820
5772
|
"license": "MIT",
|
|
5773
|
+
"peer": true,
|
|
5821
5774
|
"dependencies": {
|
|
5822
5775
|
"@types/estree": "1.0.8"
|
|
5823
5776
|
},
|
|
@@ -6361,7 +6314,6 @@
|
|
|
6361
6314
|
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
|
|
6362
6315
|
"dev": true,
|
|
6363
6316
|
"license": "MIT",
|
|
6364
|
-
"peer": true,
|
|
6365
6317
|
"engines": {
|
|
6366
6318
|
"node": ">=8"
|
|
6367
6319
|
},
|
|
@@ -6528,7 +6480,6 @@
|
|
|
6528
6480
|
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
|
|
6529
6481
|
"dev": true,
|
|
6530
6482
|
"license": "MIT",
|
|
6531
|
-
"peer": true,
|
|
6532
6483
|
"dependencies": {
|
|
6533
6484
|
"prelude-ls": "^1.2.1"
|
|
6534
6485
|
},
|
|
@@ -6700,7 +6651,6 @@
|
|
|
6700
6651
|
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
|
6701
6652
|
"dev": true,
|
|
6702
6653
|
"license": "BSD-2-Clause",
|
|
6703
|
-
"peer": true,
|
|
6704
6654
|
"dependencies": {
|
|
6705
6655
|
"punycode": "^2.1.0"
|
|
6706
6656
|
}
|
|
@@ -6872,7 +6822,6 @@
|
|
|
6872
6822
|
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
|
|
6873
6823
|
"dev": true,
|
|
6874
6824
|
"license": "MIT",
|
|
6875
|
-
"peer": true,
|
|
6876
6825
|
"engines": {
|
|
6877
6826
|
"node": ">=0.10.0"
|
|
6878
6827
|
}
|
|
@@ -7004,7 +6953,6 @@
|
|
|
7004
6953
|
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
|
|
7005
6954
|
"dev": true,
|
|
7006
6955
|
"license": "MIT",
|
|
7007
|
-
"peer": true,
|
|
7008
6956
|
"engines": {
|
|
7009
6957
|
"node": ">=10"
|
|
7010
6958
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@adobe/eslint-config-helix": "^3.0.9",
|
|
16
|
-
"@eslint/config-helpers": "0.4.
|
|
16
|
+
"@eslint/config-helpers": "0.4.1",
|
|
17
17
|
"@esm-bundle/chai": "4.3.4-fix.0",
|
|
18
18
|
"@web/test-runner": "0.20.2",
|
|
19
19
|
"@web/test-runner-playwright": "0.11.1",
|
|
@@ -154,6 +154,90 @@ export class HelixServer extends BaseServer {
|
|
|
154
154
|
.send('');
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Resolves which HTML file to serve from the HTML folder
|
|
159
|
+
* @param {string} relativePath path relative to HTML folder
|
|
160
|
+
* @returns {Promise<{file: string, isPlain: boolean}|null>} resolved file info or null
|
|
161
|
+
*/
|
|
162
|
+
async resolveHtmlFolderFile(relativePath) {
|
|
163
|
+
// Security check: prevent path traversal with /../ anywhere in the path
|
|
164
|
+
if (relativePath.includes('/../')) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Don't process if it already has an extension
|
|
169
|
+
if (relativePath.includes('.')) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Try .html first
|
|
174
|
+
const htmlFile = path.resolve(
|
|
175
|
+
this._project.directory,
|
|
176
|
+
this._htmlFolder,
|
|
177
|
+
`${relativePath}.html`,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
if (!utils.validatePathSecurity(htmlFile, this._project.directory)) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const stats = await lstat(htmlFile);
|
|
186
|
+
if (stats.isFile()) {
|
|
187
|
+
return { file: htmlFile, isPlain: false };
|
|
188
|
+
}
|
|
189
|
+
} catch (e) {
|
|
190
|
+
// .html not found, try .plain.html
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Try .plain.html
|
|
194
|
+
const plainHtmlFile = path.resolve(
|
|
195
|
+
this._project.directory,
|
|
196
|
+
this._htmlFolder,
|
|
197
|
+
`${relativePath}.plain.html`,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
if (!utils.validatePathSecurity(plainHtmlFile, this._project.directory)) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const stats = await lstat(plainHtmlFile);
|
|
206
|
+
if (stats.isFile()) {
|
|
207
|
+
return { file: plainHtmlFile, isPlain: true };
|
|
208
|
+
}
|
|
209
|
+
} catch (e) {
|
|
210
|
+
// Neither exists
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Transforms .plain.html content into complete HTML with head.html
|
|
218
|
+
* @param {string} plainHtmlFile path to .plain.html file
|
|
219
|
+
* @returns {Promise<string>} complete HTML document
|
|
220
|
+
*/
|
|
221
|
+
async transformPlainHtml(plainHtmlFile) {
|
|
222
|
+
const plainContent = await readFile(plainHtmlFile, 'utf-8');
|
|
223
|
+
|
|
224
|
+
// Extract metadata and clean content
|
|
225
|
+
const { content, metadata } = utils.extractMetadataBlock(plainContent);
|
|
226
|
+
|
|
227
|
+
// Extract default metadata values from content
|
|
228
|
+
const defaults = utils.extractDefaultMetadata(content);
|
|
229
|
+
|
|
230
|
+
// Get head HTML using existing HeadHtmlSupport
|
|
231
|
+
await this._project.headHtml.update();
|
|
232
|
+
const headHtml = this._project.headHtml.localHtml || '';
|
|
233
|
+
|
|
234
|
+
// Generate meta tags from metadata with defaults
|
|
235
|
+
const metaTags = utils.generateMetaTags(metadata, defaults);
|
|
236
|
+
|
|
237
|
+
// Wrap in complete HTML structure
|
|
238
|
+
return utils.wrapPlainHtml(content, headHtml, metaTags);
|
|
239
|
+
}
|
|
240
|
+
|
|
157
241
|
/**
|
|
158
242
|
* HTML Folder handler - serves HTML files without extensions
|
|
159
243
|
* @param {Express.Request} req request
|
|
@@ -177,41 +261,12 @@ export class HelixServer extends BaseServer {
|
|
|
177
261
|
// Extract the path within the HTML folder
|
|
178
262
|
const relativePath = pathname.slice(folderPrefix.length);
|
|
179
263
|
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Don't process if it already has an extension
|
|
186
|
-
if (relativePath.includes('.')) {
|
|
264
|
+
// Resolve which file to serve (.html or .plain.html)
|
|
265
|
+
const resolvedFile = await this.resolveHtmlFolderFile(relativePath);
|
|
266
|
+
if (!resolvedFile) {
|
|
187
267
|
return next();
|
|
188
268
|
}
|
|
189
269
|
|
|
190
|
-
// Build the HTML file path - only support .html extension
|
|
191
|
-
const htmlFile = path.resolve(this._project.directory, this._htmlFolder, `${relativePath}.html`);
|
|
192
|
-
|
|
193
|
-
// Security check: ensure the file is within the project directory
|
|
194
|
-
// Use resolve to normalize both paths before comparing
|
|
195
|
-
const resolvedProjectDir = path.resolve(this._project.directory);
|
|
196
|
-
const relPath = path.relative(resolvedProjectDir, htmlFile);
|
|
197
|
-
|
|
198
|
-
// Only check for path traversal - remove the incorrect isAbsolute check
|
|
199
|
-
if (relPath.startsWith('..')) {
|
|
200
|
-
return next();
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Check if the HTML file exists and is a file
|
|
204
|
-
try {
|
|
205
|
-
const stats = await lstat(htmlFile);
|
|
206
|
-
if (!stats.isFile()) {
|
|
207
|
-
return next();
|
|
208
|
-
}
|
|
209
|
-
} catch (e) {
|
|
210
|
-
// File doesn't exist, continue to next handler
|
|
211
|
-
return next();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const sendFile = promisify(res.sendFile).bind(res);
|
|
215
270
|
const { log } = this;
|
|
216
271
|
const liveReload = this._liveReload;
|
|
217
272
|
|
|
@@ -220,24 +275,33 @@ export class HelixServer extends BaseServer {
|
|
|
220
275
|
liveReload.startRequest(req.id, req.url);
|
|
221
276
|
}
|
|
222
277
|
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
278
|
+
// Load content (handle .plain.html transformation)
|
|
279
|
+
let htmlContent;
|
|
280
|
+
if (resolvedFile.isPlain) {
|
|
281
|
+
htmlContent = await this.transformPlainHtml(resolvedFile.file);
|
|
282
|
+
} else {
|
|
283
|
+
htmlContent = await readFile(resolvedFile.file, 'utf-8');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Inject live reload script
|
|
287
|
+
if (liveReload) {
|
|
288
|
+
htmlContent = utils.injectLiveReloadScript(htmlContent, this);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Send response
|
|
292
|
+
res.set({
|
|
293
|
+
'content-type': 'text/html; charset=utf-8',
|
|
294
|
+
'access-control-allow-origin': '*',
|
|
233
295
|
});
|
|
296
|
+
res.send(htmlContent);
|
|
234
297
|
|
|
298
|
+
// Register with live reload
|
|
235
299
|
if (liveReload) {
|
|
236
|
-
liveReload.registerFile(req.id,
|
|
300
|
+
liveReload.registerFile(req.id, resolvedFile.file);
|
|
237
301
|
liveReload.endRequest(req.id);
|
|
238
302
|
}
|
|
239
303
|
|
|
240
|
-
log.debug(`served HTML file ${
|
|
304
|
+
log.debug(`served HTML file ${resolvedFile.file} for ${req.url}`);
|
|
241
305
|
return undefined;
|
|
242
306
|
}
|
|
243
307
|
|
package/src/server/utils.js
CHANGED
|
@@ -17,6 +17,8 @@ import { PassThrough } from 'stream';
|
|
|
17
17
|
import { readFileSync } from 'fs';
|
|
18
18
|
import { fileURLToPath } from 'url';
|
|
19
19
|
import cookie from 'cookie';
|
|
20
|
+
import { unified } from 'unified';
|
|
21
|
+
import rehypeParse from 'rehype-parse';
|
|
20
22
|
import { getFetch } from '../fetch-utils.js';
|
|
21
23
|
|
|
22
24
|
// Load console interceptor script at startup
|
|
@@ -525,6 +527,254 @@ window.LiveReloadOptions = {
|
|
|
525
527
|
text = text.replaceAll(re, (match, arg, q1, value, q2) => (`${arg}=${q1}${value || '/'}${q2}`));
|
|
526
528
|
return Buffer.from(text, 'utf-8');
|
|
527
529
|
},
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Escapes HTML entities for use in attributes
|
|
533
|
+
* @param {string} text text to escape
|
|
534
|
+
* @returns {string} escaped text
|
|
535
|
+
*/
|
|
536
|
+
escapeHtml(text) {
|
|
537
|
+
return text
|
|
538
|
+
.replace(/&/g, '&')
|
|
539
|
+
.replace(/</g, '<')
|
|
540
|
+
.replace(/>/g, '>')
|
|
541
|
+
.replace(/"/g, '"')
|
|
542
|
+
.replace(/'/g, ''');
|
|
543
|
+
},
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Extracts text content from HTML using proper HTML parsing
|
|
547
|
+
* @param {string} html HTML content to extract text from
|
|
548
|
+
* @returns {string} plain text content
|
|
549
|
+
*/
|
|
550
|
+
extractTextFromHtml(html) {
|
|
551
|
+
// Helper to recursively extract text from AST nodes
|
|
552
|
+
function extractText(node) {
|
|
553
|
+
if (node.type === 'text') {
|
|
554
|
+
return node.value;
|
|
555
|
+
}
|
|
556
|
+
if (node.children) {
|
|
557
|
+
return node.children.map(extractText).join('');
|
|
558
|
+
}
|
|
559
|
+
return '';
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
try {
|
|
563
|
+
const ast = unified()
|
|
564
|
+
.use(rehypeParse, { fragment: true })
|
|
565
|
+
.parse(html);
|
|
566
|
+
return extractText(ast).trim();
|
|
567
|
+
} catch (e) {
|
|
568
|
+
// If parsing fails, return empty string to avoid XSS vulnerabilities
|
|
569
|
+
return '';
|
|
570
|
+
}
|
|
571
|
+
},
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Validates that a file path is within a base directory (security check)
|
|
575
|
+
* @param {string} filePath absolute file path to validate
|
|
576
|
+
* @param {string} baseDirectory base directory path
|
|
577
|
+
* @returns {boolean} true if path is safe, false if it tries to escape base directory
|
|
578
|
+
*/
|
|
579
|
+
validatePathSecurity(filePath, baseDirectory) {
|
|
580
|
+
const resolvedBaseDir = path.resolve(baseDirectory);
|
|
581
|
+
const relPath = path.relative(resolvedBaseDir, filePath);
|
|
582
|
+
return !relPath.startsWith('..');
|
|
583
|
+
},
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Extracts metadata block from plain HTML content
|
|
587
|
+
* @param {string} html HTML content with potential metadata block
|
|
588
|
+
* @returns {{content: string, metadata: object}} cleaned content and metadata object
|
|
589
|
+
*/
|
|
590
|
+
extractMetadataBlock(html) {
|
|
591
|
+
// The metadata block structure is:
|
|
592
|
+
// <div>
|
|
593
|
+
// <div class="metadata">
|
|
594
|
+
// <div><div>key</div><div>value</div></div>
|
|
595
|
+
// </div>
|
|
596
|
+
// </div>
|
|
597
|
+
|
|
598
|
+
// Find the outer div containing the metadata div
|
|
599
|
+
const outerDivRegex = /<div>\s*<div class="metadata">/;
|
|
600
|
+
const outerMatch = html.match(outerDivRegex);
|
|
601
|
+
|
|
602
|
+
if (!outerMatch) {
|
|
603
|
+
return { content: html, metadata: {} };
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Find the matching closing </div></div> by counting nested divs
|
|
607
|
+
// Start after <div><div class="metadata">
|
|
608
|
+
const startIndex = outerMatch.index + outerMatch[0].length;
|
|
609
|
+
let depth = 2; // Already inside two divs
|
|
610
|
+
let endIndex = startIndex;
|
|
611
|
+
let closingDivCount = 0;
|
|
612
|
+
|
|
613
|
+
for (let i = startIndex; i < html.length && closingDivCount < 2; i += 1) {
|
|
614
|
+
if (html.substr(i, 5) === '<div>') {
|
|
615
|
+
depth += 1;
|
|
616
|
+
} else if (html.substr(i, 6) === '</div>') {
|
|
617
|
+
depth -= 1;
|
|
618
|
+
if (depth < 2) {
|
|
619
|
+
closingDivCount += 1;
|
|
620
|
+
if (closingDivCount === 2) {
|
|
621
|
+
endIndex = i + 6; // Include the last </div>
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (closingDivCount !== 2) {
|
|
628
|
+
// Malformed HTML, return as-is
|
|
629
|
+
return { content: html, metadata: {} };
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Extract the metadata block content and remove entire outer div from HTML
|
|
633
|
+
const fullMetadataBlock = html.substring(outerMatch.index, endIndex);
|
|
634
|
+
const metadataBlock = html.substring(startIndex, endIndex - 12); // -12 for </div></div>
|
|
635
|
+
const cleanedContent = html.replace(fullMetadataBlock, '').trim();
|
|
636
|
+
|
|
637
|
+
// Parse metadata block to extract key-value pairs
|
|
638
|
+
const pairRegex = /<div>\s*<div>([^<]+)<\/div>\s*<div>([\s\S]*?)<\/div>\s*<\/div>/g;
|
|
639
|
+
const metadata = {};
|
|
640
|
+
let pairMatch;
|
|
641
|
+
|
|
642
|
+
// eslint-disable-next-line no-cond-assign
|
|
643
|
+
while ((pairMatch = pairRegex.exec(metadataBlock)) !== null) {
|
|
644
|
+
const key = pairMatch[1].trim();
|
|
645
|
+
let value = pairMatch[2].trim();
|
|
646
|
+
|
|
647
|
+
// Handle <img> tags - extract src
|
|
648
|
+
const imgMatch = value.match(/<img[^>]+src="([^"]+)"/);
|
|
649
|
+
if (imgMatch) {
|
|
650
|
+
[, value] = imgMatch;
|
|
651
|
+
} else {
|
|
652
|
+
// Use proper HTML parsing to extract text content
|
|
653
|
+
value = utils.extractTextFromHtml(value);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
metadata[key] = value;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
return { content: cleanedContent, metadata };
|
|
660
|
+
},
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Extracts default metadata values from HTML content
|
|
664
|
+
* @param {string} content HTML content to extract defaults from
|
|
665
|
+
* @returns {object} default metadata values
|
|
666
|
+
*/
|
|
667
|
+
extractDefaultMetadata(content) {
|
|
668
|
+
const defaults = {};
|
|
669
|
+
|
|
670
|
+
// Extract title from first H1
|
|
671
|
+
const h1Match = content.match(/<h1[^>]*>(.*?)<\/h1>/i);
|
|
672
|
+
if (h1Match) {
|
|
673
|
+
// Use proper HTML parsing to extract text
|
|
674
|
+
defaults.title = utils.extractTextFromHtml(h1Match[1]);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// Extract description from first paragraph with 10+ words
|
|
678
|
+
const pMatch = content.match(/<p[^>]*>(.*?)<\/p>/i);
|
|
679
|
+
if (pMatch) {
|
|
680
|
+
// Use proper HTML parsing to extract text
|
|
681
|
+
const text = utils.extractTextFromHtml(pMatch[1]);
|
|
682
|
+
const wordCount = text.split(/\s+/).length;
|
|
683
|
+
if (wordCount >= 10) {
|
|
684
|
+
defaults.description = text;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Extract image from first img tag
|
|
689
|
+
const imgMatch = content.match(/<img[^>]+src="([^"]+)"/i);
|
|
690
|
+
if (imgMatch) {
|
|
691
|
+
[, defaults.image] = imgMatch;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
return defaults;
|
|
695
|
+
},
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Generates meta tags from metadata object with AEM.live special property support
|
|
699
|
+
* @param {object} metadata key-value pairs of metadata
|
|
700
|
+
* @param {object} defaults default values extracted from content
|
|
701
|
+
* @returns {string} HTML string of meta tags
|
|
702
|
+
*/
|
|
703
|
+
generateMetaTags(metadata, defaults = {}) {
|
|
704
|
+
// Handle title:suffix special property
|
|
705
|
+
let title = metadata.title || defaults.title || '';
|
|
706
|
+
const titleSuffix = metadata['title:suffix'];
|
|
707
|
+
if (titleSuffix && title) {
|
|
708
|
+
title = `${title} ${titleSuffix}`;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// Apply defaults for missing values
|
|
712
|
+
const description = metadata.description || defaults.description || '';
|
|
713
|
+
const image = metadata.image || defaults.image || '/default-meta-image.png';
|
|
714
|
+
|
|
715
|
+
// Build final metadata object with computed values
|
|
716
|
+
const finalMetadata = { ...metadata };
|
|
717
|
+
if (title) finalMetadata.title = title;
|
|
718
|
+
if (description) finalMetadata.description = description;
|
|
719
|
+
if (image) finalMetadata.image = image;
|
|
720
|
+
|
|
721
|
+
// Remove special properties that shouldn't become meta tags
|
|
722
|
+
delete finalMetadata['title:suffix'];
|
|
723
|
+
|
|
724
|
+
// Generate meta tags (both standard and OG format)
|
|
725
|
+
const ogFields = ['title', 'description', 'image', 'url'];
|
|
726
|
+
let metaTags = '';
|
|
727
|
+
|
|
728
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
729
|
+
for (const [key, value] of Object.entries(finalMetadata)) {
|
|
730
|
+
// Skip empty values
|
|
731
|
+
if (!value) {
|
|
732
|
+
// eslint-disable-next-line no-continue
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// Handle 'tags' property specially - create article:tag for each
|
|
737
|
+
if (key === 'tags') {
|
|
738
|
+
// Split by comma or newline
|
|
739
|
+
const tagList = value.split(/[,\n]/).map((t) => t.trim()).filter((t) => t);
|
|
740
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
741
|
+
for (const tag of tagList) {
|
|
742
|
+
metaTags += `<meta property="article:tag" content="${utils.escapeHtml(tag)}">`;
|
|
743
|
+
}
|
|
744
|
+
// Don't create standard name= tag for 'tags'
|
|
745
|
+
} else if (key === 'canonical') {
|
|
746
|
+
// Handle canonical specially
|
|
747
|
+
metaTags += `<link rel="canonical" href="${utils.escapeHtml(value)}">`;
|
|
748
|
+
metaTags += `<meta property="og:url" content="${utils.escapeHtml(value)}">`;
|
|
749
|
+
metaTags += `<meta name="twitter:url" content="${utils.escapeHtml(value)}">`;
|
|
750
|
+
// Don't create standard name= tag
|
|
751
|
+
} else {
|
|
752
|
+
// Standard meta tag
|
|
753
|
+
metaTags += `<meta name="${key}" content="${utils.escapeHtml(value)}">`;
|
|
754
|
+
|
|
755
|
+
// Open Graph meta tag for common fields
|
|
756
|
+
if (ogFields.includes(key.toLowerCase())) {
|
|
757
|
+
metaTags += `<meta property="og:${key}" content="${utils.escapeHtml(value)}">`;
|
|
758
|
+
// Add twitter card tags for common fields
|
|
759
|
+
metaTags += `<meta name="twitter:${key}" content="${utils.escapeHtml(value)}">`;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return metaTags;
|
|
765
|
+
},
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Wraps plain HTML content in complete HTML structure
|
|
769
|
+
* @param {string} content plain HTML content
|
|
770
|
+
* @param {string} headHtml head.html content
|
|
771
|
+
* @param {string} metaTags generated meta tags
|
|
772
|
+
* @returns {string} complete HTML document
|
|
773
|
+
*/
|
|
774
|
+
wrapPlainHtml(content, headHtml, metaTags) {
|
|
775
|
+
const fullHead = headHtml + metaTags;
|
|
776
|
+
return `<html><head>${fullHead}</head><body><header></header><main>${content}</main><footer></footer></body></html>`;
|
|
777
|
+
},
|
|
528
778
|
};
|
|
529
779
|
|
|
530
780
|
export default Object.freeze(utils);
|
package/src/up.js
CHANGED
|
@@ -117,7 +117,7 @@ export default function up() {
|
|
|
117
117
|
})
|
|
118
118
|
.option('html-folder', {
|
|
119
119
|
alias: 'htmlFolder',
|
|
120
|
-
describe: 'Serve HTML files from this folder without extensions (e.g., /folder/file serves folder/file.html) use this to preview content changes if you do not have access to the authoring system',
|
|
120
|
+
describe: 'Serve HTML files from this folder without extensions (e.g., /folder/file serves folder/file.html or folder/file.plain.html) use this to preview content changes if you do not have access to the authoring system',
|
|
121
121
|
type: 'string',
|
|
122
122
|
})
|
|
123
123
|
|