@underpostnet/underpost 2.99.4 → 2.99.5

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.
@@ -28,11 +28,13 @@ const levels = {
28
28
  debug: 4,
29
29
  };
30
30
 
31
- // This method set the current severity based on
32
- // the current NODE_ENV: show all the log levels
33
- // if the server was run in development mode; otherwise,
34
- // if it was run in production, show only warn and error messages.
35
- const level = () => 'info'; // (process.env.NODE_ENV || 'development' ? 'debug' : 'warn');
31
+ /**
32
+ * The `level` function determines the logging level based on the provided `logLevel` parameter or defaults to 'info'.
33
+ * @param {string} logLevel - The logging level to be used. If not provided, it defaults to 'info'.
34
+ * @returns {string} The logging level to be used for the logger.
35
+ * @memberof Logger
36
+ */
37
+ const level = (logLevel = '') => logLevel || 'info';
36
38
 
37
39
  // Define different colors for each level.
38
40
  // Colors make the log message more visible,
@@ -98,13 +100,14 @@ const setUpInfo = async (logger = new winston.Logger()) => {
98
100
  * messages.
99
101
  * @param meta - The `meta` parameter in the `loggerFactory` function is used to extract the last part
100
102
  * of a URL and use it to create log files in a specific directory.
103
+ * @param logLevel - Specify the logging level for the logger instance. e.g., 'error', 'warn', 'info', 'debug'.
101
104
  * @returns {underpostLogger} The `loggerFactory` function returns a logger instance created using Winston logger
102
105
  * library. The logger instance is configured with various transports for printing out messages to
103
106
  * different destinations such as the terminal, error.log file, and all.log file. The logger instance
104
107
  * also has a method `setUpInfo` attached to it for setting up additional information.
105
108
  * @memberof Logger
106
109
  */
107
- const loggerFactory = (meta = { url: '' }) => {
110
+ const loggerFactory = (meta = { url: '' }, logLevel = '') => {
108
111
  meta = meta.url.split('/').pop();
109
112
  // Define which transports the logger must use to print out messages.
110
113
  // In this example, we are using three different transports
@@ -125,7 +128,7 @@ const loggerFactory = (meta = { url: '' }) => {
125
128
  // and used to log messages.
126
129
  const logger = winston.createLogger({
127
130
  defaultMeta: meta,
128
- level: level(),
131
+ level: level(logLevel),
129
132
  levels,
130
133
  format: format(meta),
131
134
  transports,
@@ -147,38 +150,30 @@ const loggerFactory = (meta = { url: '' }) => {
147
150
  };
148
151
 
149
152
  /**
150
- * The `loggerMiddleware` function creates a middleware for logging HTTP requests using Morgan with
151
- * custom message format and options.
152
- * @param meta - The `meta` parameter in the `loggerMiddleware` function is an object that contains
153
- * information about the request URL. It has a default value of an empty object `{ url: '' }`. This
154
- * object is used to provide additional metadata for logging purposes.
155
- * @returns {Handler<any, any>} The `loggerMiddleware` function returns a middleware function that uses the Morgan library
156
- * to log HTTP request information. The middleware function formats the log message using predefined
157
- * tokens provided by Morgan and custom tokens like `:host` to include specific request details. The
158
- * log message format includes information such as remote address, HTTP method, host, URL, status code,
159
- * content length, and response time in milliseconds. The middleware
153
+ * The `loggerMiddleware` function is an Express middleware that uses the Morgan library to log HTTP requests.
154
+ * @param {Object} meta - An object containing metadata, such as the URL, to be used in the logger.
155
+ * @param {string} logLevel - The logging level to be used for the logger (e.g., 'error', 'warn', 'info', 'debug').
156
+ * @param {Function} skip - A function to determine whether to skip logging for a particular request.
157
+ * @returns {Function} A middleware function that can be used in an Express application to log HTTP requests.
160
158
  * @memberof Logger
161
159
  */
162
- const loggerMiddleware = (meta = { url: '' }) => {
160
+ const loggerMiddleware = (
161
+ meta = { url: '' },
162
+ logLevel = 'info',
163
+ skip = (req, res) => process.env.NODE_ENV === 'production',
164
+ ) => {
163
165
  const stream = {
164
166
  // Use the http severity
165
- write: (message) => loggerFactory(meta).http(message),
167
+ write: (message) => loggerFactory(meta, logLevel).http(message),
166
168
  };
167
169
 
168
- const skip = (req, res) => process.env.NODE_ENV === 'production';
169
-
170
170
  morgan.token('host', function (req, res) {
171
171
  return req.headers['host'];
172
172
  });
173
173
 
174
174
  return morgan(
175
- // Define message format string (this is the default one).
176
- // The message format is made from tokens, and each token is
177
- // defined inside the Morgan library.
178
- // You can create your custom token to show what do you want from a request.
175
+ // Define message format string
179
176
  `:remote-addr :method :host:url :status :res[content-length] - :response-time ms`,
180
- // Options: in this case, I overwrote the stream and the skip logic.
181
- // See the methods above.
182
177
  { stream, skip },
183
178
  );
184
179
  };
package/src/server/tls.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * @class UnderpostTls
3
- * @description TLS/SSL utilities for Underpost.
4
- * @memberof UnderpostTls
2
+ * Transport Layer Security (TLS) / SSL utilities and HTTPS server creation and management.
3
+ * @module src/server/tls.js
4
+ * @namespace UnderpostTLS
5
5
  */
6
6
 
7
7
  import fs from 'fs-extra';
@@ -42,7 +42,7 @@ class TLS {
42
42
  * It attempts to be permissive: accepts cert-only, cert+ca, or fullchain.
43
43
  * @param {string} host
44
44
  * @returns {{key?:string, cert?:string, fullchain?:string, ca?:string, dir:string}}
45
- * @memberof UnderpostTls
45
+ * @memberof UnderpostTLS
46
46
  */
47
47
  static locateSslFiles(host = DEFAULT_HOST) {
48
48
  const dir = SSL_BASE(host);
@@ -102,7 +102,7 @@ class TLS {
102
102
  * Validate that a secure context can be built for host (key + cert or fullchain present)
103
103
  * @param {string} host
104
104
  * @returns {boolean}
105
- * @memberof UnderpostTls
105
+ * @memberof UnderpostTLS
106
106
  */
107
107
  static validateSecureContext(host = DEFAULT_HOST) {
108
108
  const files = TLS.locateSslFiles(host);
@@ -115,7 +115,7 @@ class TLS {
115
115
  * If separate cert + ca are found, they will be used accordingly.
116
116
  * @param {string} host
117
117
  * @returns {{key:string, cert:string, ca?:string}} options
118
- * @memberof UnderpostTls
118
+ * @memberof UnderpostTLS
119
119
  */
120
120
  static buildSecureContext(host = DEFAULT_HOST) {
121
121
  const files = TLS.locateSslFiles(host);
@@ -141,7 +141,7 @@ class TLS {
141
141
  * The function will copy existing discovered files to: key.key, crt.crt, ca_bundle.crt when possible.
142
142
  * @param {string} host
143
143
  * @returns {boolean} true if at least key+cert exist after operation
144
- * @memberof UnderpostTls
144
+ * @memberof UnderpostTLS
145
145
  */
146
146
  static async buildLocalSSL(host = DEFAULT_HOST) {
147
147
  const dir = SSL_BASE(host);
@@ -176,7 +176,7 @@ class TLS {
176
176
  * @param {import('express').Application} app
177
177
  * @param {Object<string, any>} hosts
178
178
  * @returns {{ServerSSL?: https.Server}}
179
- * @memberof UnderpostTls
179
+ * @memberof UnderpostTLS
180
180
  */
181
181
  static async createSslServer(app, hosts = { [DEFAULT_HOST]: {} }) {
182
182
  let server;
@@ -214,7 +214,7 @@ class TLS {
214
214
  * @param {number} port
215
215
  * @param {Object<string, any>} proxyRouter
216
216
  * @returns {import('express').RequestHandler}
217
- * @memberof UnderpostTls
217
+ * @memberof UnderpostTLS
218
218
  */
219
219
  static sslRedirectMiddleware(req, res, port = 80, proxyRouter = {}) {
220
220
  const sslRedirectUrl = `https://${req.headers.host}${req.url}`;
@@ -235,11 +235,11 @@ class TLS {
235
235
  }
236
236
 
237
237
  /**
238
- * @class UnderpostTls
238
+ * @class UnderpostTLS
239
239
  * @description TLS/SSL utilities for Underpost.
240
- * @memberof UnderpostTls
240
+ * @memberof UnderpostTLS
241
241
  */
242
- class UnderpostTls {
242
+ class UnderpostTLS {
243
243
  static API = {
244
244
  TLS,
245
245
  SSL_BASE,
@@ -252,5 +252,5 @@ class UnderpostTls {
252
252
  };
253
253
  }
254
254
 
255
- export { TLS, SSL_BASE, UnderpostTls };
256
- export default UnderpostTls;
255
+ export { TLS, SSL_BASE, UnderpostTLS };
256
+ export default UnderpostTLS;
@@ -1,481 +0,0 @@
1
- # Underpost Static Site Generator - Quick Reference
2
-
3
- ## Basic Commands
4
-
5
- ### Generate Config Template
6
- ```bash
7
- underpost static --generate-config ./static-config.json
8
- ```
9
-
10
- ### Build from Config File
11
- ```bash
12
- underpost static --config-file ./static-config.json
13
- ```
14
-
15
- ### Simple Page Generation
16
- ```bash
17
- underpost static \
18
- --page ./src/client/ssr/body/Page.js \
19
- --output-path ./dist/index.html \
20
- --title "My Page"
21
- ```
22
-
23
- ## Common CLI Patterns
24
-
25
- ### Landing Page with SEO
26
- ```bash
27
- underpost static \
28
- --page ./src/client/ssr/body/Landing.js \
29
- --output-path ./dist/landing.html \
30
- --title "Welcome" \
31
- --description "My awesome landing page" \
32
- --keywords "landing,web,app" \
33
- --theme-color "#007bff" \
34
- --canonical-url "https://example.com"
35
- ```
36
-
37
- ### Page with External Scripts
38
- ```bash
39
- underpost static \
40
- --page ./src/client/ssr/body/App.js \
41
- --output-path ./dist/app.html \
42
- --head-scripts "https://cdn.example.com/analytics.js" \
43
- --body-scripts "/app.js,/vendor.js"
44
- ```
45
-
46
- ### Page with Custom Icons
47
- ```bash
48
- underpost static \
49
- --page ./src/client/ssr/body/Home.js \
50
- --output-path ./dist/index.html \
51
- --favicon "/favicon.ico" \
52
- --apple-touch-icon "/apple-touch-icon.png" \
53
- --manifest "/manifest.json"
54
- ```
55
-
56
- ### Production Build with Deployment
57
- ```bash
58
- underpost static \
59
- --page ./src/client/ssr/body/App.js \
60
- --output-path ./public/index.html \
61
- --deploy-id "production-v1" \
62
- --build-host "example.com" \
63
- --build-path "/" \
64
- --build \
65
- --env production
66
- ```
67
-
68
- ## Configuration File Patterns
69
-
70
- ### Minimal Config
71
- ```json
72
- {
73
- "page": "./src/client/ssr/body/Page.js",
74
- "outputPath": "./dist/index.html",
75
- "metadata": {
76
- "title": "My Page",
77
- "description": "Page description"
78
- }
79
- }
80
- ```
81
-
82
- ### SEO-Optimized Config
83
- ```json
84
- {
85
- "page": "./src/client/ssr/body/Page.js",
86
- "outputPath": "./dist/index.html",
87
- "metadata": {
88
- "title": "My SEO Page",
89
- "description": "Comprehensive description for SEO",
90
- "keywords": ["seo", "optimization", "web"],
91
- "author": "Your Name",
92
- "canonicalURL": "https://example.com",
93
- "thumbnail": "https://example.com/og-image.png",
94
- "themeColor": "#4CAF50"
95
- }
96
- }
97
- ```
98
-
99
- ### With Scripts and Styles
100
- ```json
101
- {
102
- "page": "./src/client/ssr/body/Page.js",
103
- "outputPath": "./dist/index.html",
104
- "scripts": {
105
- "head": [
106
- { "src": "https://cdn.example.com/lib.js", "async": true }
107
- ],
108
- "body": [
109
- { "src": "/app.js", "type": "module", "defer": true }
110
- ]
111
- },
112
- "styles": [
113
- { "href": "/main.css" },
114
- { "content": "body { margin: 0; }" }
115
- ]
116
- }
117
- ```
118
-
119
- ### PWA Configuration
120
- ```json
121
- {
122
- "page": "./src/client/ssr/body/App.js",
123
- "outputPath": "./dist/index.html",
124
- "metadata": {
125
- "title": "My PWA",
126
- "themeColor": "#007bff"
127
- },
128
- "icons": {
129
- "favicon": "/favicon.ico",
130
- "appleTouchIcon": "/apple-touch-icon.png",
131
- "manifest": "/manifest.json"
132
- },
133
- "scripts": {
134
- "body": [
135
- {
136
- "content": "if('serviceWorker' in navigator){navigator.serviceWorker.register('/sw.js');}"
137
- }
138
- ]
139
- }
140
- }
141
- ```
142
-
143
- ### With Structured Data
144
- ```json
145
- {
146
- "page": "./src/client/ssr/body/Page.js",
147
- "outputPath": "./dist/index.html",
148
- "metadata": {
149
- "title": "My Product"
150
- },
151
- "microdata": [
152
- {
153
- "@context": "https://schema.org",
154
- "@type": "Product",
155
- "name": "My Product",
156
- "offers": {
157
- "@type": "Offer",
158
- "price": "29.99",
159
- "priceCurrency": "USD"
160
- }
161
- }
162
- ]
163
- }
164
- ```
165
-
166
- ## SSR Component Patterns
167
-
168
- ### Basic Component
169
- ```javascript
170
- SrrComponent = () => html`
171
- <div class="page">
172
- <h1>Hello World</h1>
173
- </div>
174
- `;
175
- ```
176
-
177
- ### Component with Styles
178
- ```javascript
179
- SrrComponent = () => html`
180
- <div class="custom-page">
181
- <h1>Styled Page</h1>
182
- </div>
183
- <style>
184
- .custom-page {
185
- padding: 20px;
186
- }
187
- </style>
188
- `;
189
- ```
190
-
191
- ### Component with JavaScript
192
- ```javascript
193
- SrrComponent = () => html`
194
- <div class="interactive-page">
195
- <button id="myButton">Click Me</button>
196
- </div>
197
- <script>
198
- document.addEventListener('DOMContentLoaded', function() {
199
- document.getElementById('myButton').addEventListener('click', function() {
200
- alert('Button clicked!');
201
- });
202
- });
203
- </script>
204
- `;
205
- ```
206
-
207
- ### Component with Dynamic Content
208
- ```javascript
209
- SrrComponent = () => html`
210
- <div class="dynamic-page">
211
- <h1>Current Year: ${new Date().getFullYear()}</h1>
212
- <p>Generated at: ${new Date().toISOString()}</p>
213
- </div>
214
- `;
215
- ```
216
-
217
- ## Environment-Specific Builds
218
-
219
- ### Development
220
- ```bash
221
- underpost static \
222
- --config-file ./config.json \
223
- --env development \
224
- --no-minify \
225
- --dev
226
- ```
227
-
228
- ### Production
229
- ```bash
230
- underpost static \
231
- --config-file ./config.json \
232
- --env production \
233
- --minify
234
- ```
235
-
236
- ## Multi-Page Generation
237
-
238
- ### Using Script (Bash)
239
- ```bash
240
- #!/bin/bash
241
- PAGES=("home" "about" "contact")
242
-
243
- for page in "${PAGES[@]}"; do
244
- underpost static \
245
- --page "./src/client/ssr/body/${page}.js" \
246
- --output-path "./dist/${page}.html" \
247
- --title "$(echo $page | sed 's/.*/\u&/')"
248
- done
249
- ```
250
-
251
- ### Using JavaScript
252
- ```javascript
253
- import UnderpostStatic from './src/cli/static.js';
254
-
255
- const pages = ['home', 'about', 'contact'];
256
-
257
- for (const page of pages) {
258
- await UnderpostStatic.API.callback({
259
- page: `./src/client/ssr/body/${page}.js`,
260
- outputPath: `./dist/${page}.html`,
261
- metadata: {
262
- title: page.charAt(0).toUpperCase() + page.slice(1)
263
- }
264
- });
265
- }
266
- ```
267
-
268
- ## Metadata Patterns
269
-
270
- ### Blog Post
271
- ```json
272
- {
273
- "metadata": {
274
- "title": "How to Build Static Sites",
275
- "description": "A comprehensive guide to building static sites",
276
- "keywords": ["static", "guide", "tutorial"],
277
- "author": "Jane Doe"
278
- },
279
- "microdata": [
280
- {
281
- "@context": "https://schema.org",
282
- "@type": "BlogPosting",
283
- "headline": "How to Build Static Sites",
284
- "author": { "@type": "Person", "name": "Jane Doe" },
285
- "datePublished": "2024-01-01"
286
- }
287
- ]
288
- }
289
- ```
290
-
291
- ### E-commerce Product
292
- ```json
293
- {
294
- "metadata": {
295
- "title": "Premium Widget - $29.99",
296
- "description": "High-quality widget with free shipping",
297
- "thumbnail": "https://shop.com/widget.jpg"
298
- },
299
- "microdata": [
300
- {
301
- "@context": "https://schema.org",
302
- "@type": "Product",
303
- "name": "Premium Widget",
304
- "image": "https://shop.com/widget.jpg",
305
- "offers": {
306
- "@type": "Offer",
307
- "price": "29.99",
308
- "priceCurrency": "USD",
309
- "availability": "https://schema.org/InStock"
310
- }
311
- }
312
- ]
313
- }
314
- ```
315
-
316
- ### Organization
317
- ```json
318
- {
319
- "microdata": [
320
- {
321
- "@context": "https://schema.org",
322
- "@type": "Organization",
323
- "name": "My Company",
324
- "url": "https://example.com",
325
- "logo": "https://example.com/logo.png",
326
- "sameAs": [
327
- "https://twitter.com/mycompany",
328
- "https://linkedin.com/company/mycompany"
329
- ]
330
- }
331
- ]
332
- }
333
- ```
334
-
335
- ## JavaScript API Usage
336
-
337
- ### Basic Generation
338
- ```javascript
339
- import UnderpostStatic from './src/cli/static.js';
340
-
341
- await UnderpostStatic.API.callback({
342
- page: './src/client/ssr/body/Page.js',
343
- outputPath: './dist/index.html',
344
- metadata: { title: 'My Page' }
345
- });
346
- ```
347
-
348
- ### Using Template Helpers
349
- ```javascript
350
- import { TemplateHelpers } from './src/cli/static.js';
351
-
352
- const scriptTag = TemplateHelpers.createScriptTag({
353
- src: '/app.js',
354
- defer: true,
355
- type: 'module'
356
- });
357
-
358
- const metaTags = TemplateHelpers.createMetaTags({
359
- title: 'My Page',
360
- description: 'Description',
361
- keywords: ['key1', 'key2']
362
- });
363
- ```
364
-
365
- ### Validation
366
- ```javascript
367
- import { ConfigValidator } from './src/cli/static.js';
368
-
369
- const result = ConfigValidator.validate(options);
370
- if (!result.isValid) {
371
- console.error('Errors:', result.errors);
372
- }
373
- ```
374
-
375
- ## Common Script Snippets
376
-
377
- ### Google Analytics
378
- ```json
379
- {
380
- "scripts": {
381
- "head": [
382
- {
383
- "src": "https://www.googletagmanager.com/gtag/js?id=GA_ID",
384
- "async": true
385
- },
386
- {
387
- "content": "window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','GA_ID');"
388
- }
389
- ]
390
- }
391
- }
392
- ```
393
-
394
- ### Service Worker Registration
395
- ```json
396
- {
397
- "scripts": {
398
- "body": [
399
- {
400
- "content": "if('serviceWorker' in navigator){navigator.serviceWorker.register('/sw.js').then(reg=>console.log('SW registered',reg)).catch(err=>console.log('SW error',err));}"
401
- }
402
- ]
403
- }
404
- }
405
- ```
406
-
407
- ### App Configuration
408
- ```json
409
- {
410
- "scripts": {
411
- "head": [
412
- {
413
- "content": "window.appConfig={apiUrl:'https://api.example.com',version:'1.0.0',features:{analytics:true,debugging:false}};"
414
- }
415
- ]
416
- }
417
- }
418
- ```
419
-
420
- ## Troubleshooting Quick Fixes
421
-
422
- ### Component Not Found
423
- ```bash
424
- # Check if file exists
425
- ls -la ./src/client/ssr/body/Page.js
426
-
427
- # Use absolute path from project root
428
- underpost static --page ./src/client/ssr/body/Page.js --output-path ./dist/index.html
429
- ```
430
-
431
- ### Invalid JSON Config
432
- ```bash
433
- # Validate JSON
434
- cat config.json | python -m json.tool
435
-
436
- # Or use jq
437
- jq . config.json
438
- ```
439
-
440
- ### Output Directory Missing
441
- ```bash
442
- # Create directory first
443
- mkdir -p ./dist/pages
444
-
445
- # Then run command
446
- underpost static --config-file ./config.json
447
- ```
448
-
449
- ## Performance Optimization
450
-
451
- ### Minification
452
- ```json
453
- {
454
- "env": "production",
455
- "minify": true
456
- }
457
- ```
458
-
459
- ### Async Loading
460
- ```json
461
- {
462
- "scripts": {
463
- "head": [
464
- { "src": "/analytics.js", "async": true }
465
- ],
466
- "body": [
467
- { "src": "/app.js", "defer": true }
468
- ]
469
- }
470
- }
471
- ```
472
-
473
- ### Critical CSS
474
- ```json
475
- {
476
- "styles": [
477
- { "content": "body{margin:0;font-family:sans-serif}" },
478
- { "href": "/main.css" }
479
- ]
480
- }
481
- ```