@jseeio/jsee 0.3.4 → 0.3.7

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/bin/jsee CHANGED
@@ -1,704 +1,3 @@
1
1
  #!/usr/bin/env node
2
-
3
- const fs = require('fs')
4
- const path = require('path')
5
- const os = require('os')
6
- const crypto = require('crypto')
7
-
8
- const minimist = require('minimist')
9
- const jsdoc2md = require('jsdoc-to-markdown')
10
- const showdown = require('showdown')
11
- const showdownKatex = require('showdown-katex')
12
- const converter = new showdown.Converter({
13
- extensions: [
14
- showdownKatex({
15
- throwOnError: true,
16
- displayMode: true,
17
- errorColor: '#1500ff',
18
- output: 'mathml'
19
- }),
20
- ],
21
- tables: true
22
- })
23
- showdown.setFlavor('github')
24
-
25
- // left padding of multiple lines
26
- function pad (str, len, start=0) {
27
- return str.split('\n').map((s, i) => i >= start ? ' '.repeat(len) + s : s).join('\n')
28
- }
29
-
30
- function depad (str, len) {
31
- return str.split('\n').map(s => s.slice(len)).join('\n')
32
- }
33
-
34
- const argv = minimist(process.argv.slice(2), {
35
- alias: {
36
- inputs: 'i',
37
- outputs: 'o',
38
- description: 'd',
39
- ga: 'g',
40
- port: 'p',
41
- version: 'v',
42
- fetch: 'f',
43
- },
44
- default: {
45
- inputs: '',
46
- fetch: false,
47
- port: 3000,
48
- version: 'latest'
49
- }
50
- })
51
- // Set argv.inputs to the first non-option argument if it exists
52
- if (argv._.length > 0) {
53
- argv.inputs = argv._[0]
54
- }
55
-
56
- gen(argv)
57
-
58
- // Adding async here breaks express. TODO: investigate
59
- async function gen (argv, returnHtml=false) {
60
- console.log('argv:', argv)
61
- let cwd = process.cwd()
62
- let inputs = argv.inputs
63
- let outputs = argv.outputs
64
- let description = argv.description
65
- let version = argv.version
66
- let ga = argv.ga
67
- let schema
68
- let descriptionTxt = ''
69
- let descriptionHtml = ''
70
- let jsdocMarkdown = ''
71
-
72
- // if inputs is a string with js file names, split it into an array
73
- if (typeof inputs === 'string') {
74
- if (inputs.includes('.js')) {
75
- inputs = inputs.split(',')
76
- }
77
- }
78
-
79
- // if outputs is a string with js file names, split it into an array
80
- if (typeof outputs === 'string') {
81
- outputs = outputs.split(',')
82
- }
83
-
84
- if (inputs.length === 0) {
85
- console.error('No inputs provided')
86
- process.exit(1)
87
- } else if ((inputs.length === 1) && (inputs[0].includes('.json'))) {
88
- // Input is json schema
89
- // Curren working directory if not provided
90
- schema = require(path.join(cwd, inputs[0]))
91
- } else {
92
- // Array of js files
93
- // Generate schema
94
- let jsdocData = jsdoc2md.getTemplateDataSync({ files: inputs.map(f => path.join(cwd, f)) })
95
- schema = genSchema(jsdocData)
96
- // jsdocMarkdown = jsdoc2md.renderSync({
97
- // data: jsdocData,
98
- // 'param-list-format': 'list',
99
- // })
100
- }
101
-
102
- // console.log('Schema:', schema)
103
-
104
- // Generate description block
105
- if (description) {
106
- const descriptionMd = fs.readFileSync(path.join(cwd, description), 'utf8')
107
- descriptionHtml = converter.makeHtml(descriptionMd)
108
-
109
- if (descriptionMd.includes('---')) {
110
- descriptionTxt = descriptionMd
111
- .split('---')[0]
112
- .replace(/\n/g, ' ')
113
- .replace(/\s+/g, ' ')
114
- .replace(/#/g, '')
115
- .replace(/\*/g, '')
116
- .trim()
117
- }
118
- }
119
-
120
- descriptionHtml += genHtmlFromSchema(schema)
121
-
122
-
123
-
124
- // Generate jsee code
125
- let jseeHtml = ''
126
- let hiddenElementHtml = ''
127
- if (argv.fetch) {
128
- // Fetch jsee code from the CDN or local server
129
- let jseeCode
130
- if (argv.version === 'dev') {
131
- jseeCode = fs.readFileSync(path.join(__dirname, '..', 'dist', 'jsee.js'), 'utf8')
132
- } else if (argv.version === 'latest') {
133
- jseeCode = fs.readFileSync(path.join(__dirname, '..', 'dist', 'jsee.runtime.js'), 'utf8')
134
- } else {
135
- // Pre-fetch the jsee runtime from the CDN https://cdn.jsdelivr.net/npm/@jseeio/jsee@${argv.version}/dist/jsee.runtime.js
136
- jseeCode = await fetch(`https://cdn.jsdelivr.net/npm/@jseeio/jsee@${argv.version}/dist/jsee.runtime.js`)
137
- jseeCode = await jseeCode.text()
138
- }
139
- jseeHtml = `<script>${jseeCode}</script>`
140
- // Fetch model files and store them in hidden elements
141
- hiddenElementHtml += '<div id="hidden-storage" style="display: none;">'
142
- if (!schema.model) {
143
- console.error('No model found in schema')
144
- process.exit(1)
145
- }
146
- if (!Array.isArray(schema.model)) {
147
- schema.model = [schema.model]
148
- }
149
- for (let m of schema.model) {
150
- if (m.url) {
151
- const modelCode = fs.readFileSync(path.join(cwd, m.url), 'utf8')
152
- hiddenElementHtml += `<script type="text/plain" style="display: none;" data-src="${m.url}">${modelCode}</script>`
153
- }
154
- if (m.imports) {
155
- for (let i of m.imports) {
156
- const importUrl = i.includes('.js') ? i : `https://cdn.jsdelivr.net/npm/${i}`
157
-
158
- // Create cache directory if it doesn't exist
159
- const cacheDir = path.join(os.homedir(), '.cache', 'jsee')
160
- fs.mkdirSync(cacheDir, { recursive: true })
161
-
162
- // Create a hash of the importUrl
163
- const hash = crypto.createHash('sha256').update(importUrl).digest('hex')
164
- const cacheFilePath = path.join(cacheDir, `${hash}.js`)
165
-
166
- let importCode
167
- let useCache = false
168
-
169
- // Check if cache file exists and is less than 1 day old
170
- if (fs.existsSync(cacheFilePath)) {
171
- const stats = fs.statSync(cacheFilePath)
172
- const mtime = new Date(stats.mtime)
173
- const now = new Date()
174
- const ageInDays = (now - mtime) / (1000 * 60 * 60 * 24)
175
-
176
- if (ageInDays < 1) {
177
- console.log('Using cached import:', importUrl)
178
- importCode = fs.readFileSync(cacheFilePath, 'utf8');
179
- useCache = true;
180
- }
181
- }
182
-
183
- if (!useCache) {
184
- const response = await fetch(importUrl);
185
- if (!response.ok) {
186
- console.error(`Failed to fetch ${importUrl}: ${response.statusText}`);
187
- process.exit(1);
188
- }
189
- importCode = await response.text()
190
- fs.writeFileSync(cacheFilePath, importCode, 'utf8')
191
- console.log('Fetched and stored to cache:', importUrl)
192
- }
193
- hiddenElementHtml += `<script type="text/plain" style="display: none;" data-src="${importUrl}">${importCode}</script>`
194
- }
195
- }
196
- }
197
- hiddenElementHtml += '</div>'
198
- } else {
199
- jseeHtml = outputs
200
- ? `<script src="https://cdn.jsdelivr.net/npm/@jseeio/jsee@${argv.version}/dist/jsee.runtime.js"></script>`
201
- : `<script src="http://localhost:${argv.port}/dist/jsee.runtime.js"></script>`
202
- }
203
-
204
- let socialHtml = ''
205
- let gaHtml = ''
206
- let orgHtml = ''
207
-
208
- if (schema.page) {
209
- if (schema.page.title) {
210
- title = schema.page.title
211
- }
212
- if (schema.page.ga) {
213
- gaHtml = `
214
- <script id="ga-src" async src="https://www.googletagmanager.com/gtag/js?id=${schema.page.ga}"></script>
215
- <script id="ga-body">
216
- window['ga-disable-${schema.page.ga}'] = window.doNotTrack === "1" || navigator.doNotTrack === "1" || navigator.doNotTrack === "yes" || navigator.msDoNotTrack === "1";
217
- window.dataLayer = window.dataLayer || [];
218
- function gtag(){dataLayer.push(arguments);}
219
- gtag('js', new Date());
220
- gtag('config', '${schema.page.ga}');
221
- </script>
222
- `
223
- }
224
-
225
- // Social media links
226
- if (schema.page.social) {
227
- // iterate over dict with k, v pairs
228
- for (let [name, url] of Object.entries(schema.page.social)) {
229
- switch (name) {
230
- case 'twitter':
231
- socialHtml += `<li><a rel="me" href="https://twitter.com/${url}">Twitter</a></li>`
232
- break
233
- case 'github':
234
- socialHtml += `<li><a rel="me" href="https://github.com/${url}">GitHub</a></li>`
235
- break
236
- case 'facebook':
237
- socialHtml += `<li><a rel="me" href="https://www.facebook.com/${url}">Facebook</a></li>`
238
- break
239
- case 'linkedin':
240
- socialHtml += `<li><a rel="me" href="https://www.linkedin.com/company/${url}">LinkedIn</a></li>`
241
- break
242
- case 'instagram':
243
- socialHtml += `<li><a rel="me" href="https://www.instagram.com/${url}">Instagram</a></li>`
244
- break
245
- case 'youtube':
246
- socialHtml += `<li><a rel="me" href="https://www.youtube.com/${url}">YouTube</a></li>`
247
- break
248
- default:
249
- socialHtml += `<li><a rel="me" href="${s.url}">${s.name}</a></li>`
250
- }
251
- }
252
- }
253
-
254
- if (schema.page.org) {
255
- orgHtml = `<div class="footer-org"><h4 class="footer-heading"><a href="${schema.page.org.url}">${schema.page.org.name}</a></h4>`
256
- if (schema.page.org.description) {
257
- orgHtml += `<p>${schema.page.org.description}</p>`
258
- }
259
- orgHtml += '</div>'
260
- }
261
-
262
- }
263
-
264
- const html = template(schema, {
265
- descriptionHtml: pad(descriptionHtml, 8, 1),
266
- descriptionTxt: descriptionTxt,
267
- gaHtml: pad(gaHtml, 2, 1),
268
- jseeHtml: jseeHtml,
269
- hiddenElementHtml: hiddenElementHtml,
270
- socialHtml: pad(socialHtml, 2, 1),
271
- orgHtml: pad(orgHtml, 2, 1),
272
- })
273
-
274
- if (returnHtml) {
275
- // Return the html as a string
276
- return html
277
- } else if (outputs) {
278
- // Store the html in the output file
279
- for (let o of outputs) {
280
- if (o === 'stdout') {
281
- console.log(html)
282
- } else if (o.includes('.html')) {
283
- fs.writeFileSync(path.join(cwd, o), html)
284
- } else if (o.includes('.json')) {
285
- fs.writeFileSync(path.join(cwd, o), JSON.stringify(schema, null, 2))
286
- } else if (o.includes('.md')) {
287
- fs.writeFileSync(path.join(cwd, o), genMarkdownFromSchema(schema))
288
- } else {
289
- console.error('Invalid output file:', o)
290
- }
291
- }
292
- fs.writeFileSync(path.join(cwd, outputs[0]), html)
293
- } else {
294
- // Serve the html
295
- const express = require('express')
296
- const app = express()
297
- app.get('/', async (req, res) => {
298
- console.log('Serving index.html')
299
- res.send(await gen(argv, true))
300
- })
301
- app.get('/dist/jsee.runtime.js', (req, res) => {
302
- console.log('Serving jsee.runtime.js')
303
- res.sendFile(path.join(__dirname, '..', 'dist', 'jsee.runtime.js'))
304
- })
305
- app.use(express.static(cwd))
306
- app.listen(argv.port, () => {
307
- console.log(`JSEE app is running: http://localhost:${argv.port}`)
308
- })
309
- }
310
- }
311
-
312
- function genSchema (jsdocData) {
313
- let schema = {
314
- model: [],
315
- inputs: [],
316
- outputs: [],
317
- }
318
- for (let d of jsdocData) {
319
- const model = {
320
- name: d.name ? d.name : d.meta.filename.split('.')[0],
321
- description: d.description ? d.description : '',
322
- type: d.kind,
323
- container: 'args',
324
- url: path.relative(process.cwd(), path.join(d.meta.path, d.meta.filename)),
325
- worker: false
326
- }
327
- if (d.requires) {
328
- model.imports = d.requires.map(r => r.replace('module:', ''))
329
- }
330
- if (d.params) {
331
- // Check if all params have the same name before '.'
332
- const names = new Set(d.params.map(p => p.name.split('.')[0]))
333
- if ((d.params.length > 1) && (names.size === 1)) {
334
- // Object
335
- model.container = 'object'
336
- d.params.slice(1).forEach(p => {
337
- const inp = {
338
- name: p.name.split('.')[1],
339
- type: p.type.names[0],
340
- description: p.description,
341
- }
342
- if (p.defaultvalue) {
343
- inp.default = p.defaultvalue
344
- }
345
- schema.inputs.push(inp)
346
- })
347
- } else {
348
- // Array
349
- model.container = 'args'
350
- d.params.forEach(p => {
351
- const inp = {
352
- name: p.name,
353
- type: p.type.names[0],
354
- description: p.description,
355
- }
356
- if (p.defaultvalue) {
357
- inp.default = p.defaultvalue
358
- }
359
- schema.inputs.push(inp)
360
- })
361
- }
362
- }
363
- if (d.returns) {
364
- d.returns.forEach(r => {
365
- r.name = r.name ? r.name : r.description.split('-')[0].trim()
366
- r.description = r.description.split('-').slice(1).join('-').trim()
367
- })
368
- const names = new Set(d.returns.map(r => r.name.split('.')[0]))
369
- if ((d.returns.length > 1) && (names.size === 1)) {
370
- // Object
371
- d.returns.slice(1).forEach(p => {
372
- const out = {
373
- name: p.name.split('.')[1],
374
- type: p.type.names[0],
375
- description: p.description,
376
- }
377
- schema.outputs.push(out)
378
- })
379
- } else {
380
- // Array
381
- d.returns.forEach(p => {
382
- const out = {
383
- name: p.name,
384
- type: p.type.names[0],
385
- description: p.description,
386
- }
387
- schema.outputs.push(out)
388
- })
389
- }
390
- }
391
- if (d.customTags) {
392
- d.customTags.forEach(t => {
393
- if (t.tag === 'worker') {
394
- model.worker = true
395
- }
396
- })
397
- }
398
- schema.model.push(model)
399
- }
400
- return schema
401
- }
402
-
403
- function genHtmlFromSchema(schema) {
404
- let htmlDescription = '<br><div class="schema-description">';
405
-
406
- // Process the model section
407
- if (schema.model && schema.model.length > 0) {
408
- schema.model.forEach(model => {
409
- htmlDescription += `<h3><strong>${model.name}</strong></h3>`
410
- if (model.description) {
411
- htmlDescription += `<p>${model.description}</p>`
412
- }
413
- })
414
- }
415
-
416
- // Process the inputs section
417
- if (schema.inputs && schema.inputs.length > 0) {
418
- htmlDescription += '<h4>Inputs</h4><ul>';
419
- schema.inputs.forEach(input => {
420
- htmlDescription += `<li><strong>${input.name}</strong> (${input.type})`
421
- if (input.description) {
422
- htmlDescription += ` - ${input.description}`
423
- }
424
- })
425
- htmlDescription += '</ul>';
426
- }
427
-
428
- // Process the outputs section
429
- if (schema.outputs && schema.outputs.length > 0) {
430
- htmlDescription += '<h4>Outputs</h4><ul>';
431
- schema.outputs.forEach(output => {
432
- htmlDescription += `<li><strong>${output.name}</strong> (${output.type})`
433
- if (output.description) {
434
- htmlDescription += ` - ${output.description}`
435
- }
436
- })
437
- htmlDescription += '</ul>';
438
- }
439
- htmlDescription += '</div>';
440
- return htmlDescription;
441
- }
442
-
443
- function genMarkdownFromSchema(schema) {
444
- let markdownDescription = '';
445
-
446
- // Process the model section
447
- if (schema.model && schema.model.length > 0) {
448
- schema.model.forEach(model => {
449
- markdownDescription += `### **${model.name}**\n`;
450
- if (model.description) {
451
- markdownDescription += `${model.description}\n\n`;
452
- }
453
- });
454
- }
455
-
456
- // Process the inputs section
457
- if (schema.inputs && schema.inputs.length > 0) {
458
- markdownDescription += '#### Inputs\n';
459
- schema.inputs.forEach(input => {
460
- markdownDescription += `- **${input.name}** (${input.type})`;
461
- if (input.description) {
462
- markdownDescription += ` - ${input.description}`;
463
- }
464
- markdownDescription += '\n';
465
- });
466
- }
467
-
468
- // Process the outputs section
469
- if (schema.outputs && schema.outputs.length > 0) {
470
- markdownDescription += '#### Outputs\n';
471
- schema.outputs.forEach(output => {
472
- markdownDescription += `- **${output.name}** (${output.type})`;
473
- if (output.description) {
474
- markdownDescription += ` - ${output.description}`;
475
- }
476
- markdownDescription += '\n';
477
- });
478
- }
479
-
480
- return markdownDescription;
481
- }
482
-
483
- function template(schema, blocks) {
484
- let title = 'jsee'
485
- let url = schema.page.url ? schema.page.url : ''
486
- if (schema.title) {
487
- title = schema.title
488
- } else if (schema.page && schema.page.title) {
489
- title = schema.page.title
490
- } else if (schema.model) {
491
- if (Array.isArray(schema.model)) {
492
- title = schema.model[0].name
493
- } else {
494
- title = schema.model.name
495
- }
496
- }
497
-
498
- return `<!DOCTYPE html>
499
-
500
- <!-- Generated by JSEE (https://jsee.org) -->
501
- <!-- Do not edit this file directly. Edit the source files and run jsee to generate this file. -->
502
- <!-- Source: ${argv.inputs} -->
503
- <!-- License: MIT (https://opensource.org/licenses/MIT) -->
504
-
505
- <html lang="en">
506
- <head>
507
- <meta charset="utf-8">
508
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
509
- <meta name="viewport" content="width=device-width, initial-scale=1">
510
- <title>${title}</title>
511
- <meta name="description" content="${blocks.descriptionTxt}">
512
-
513
- <!-- Open Graph -->
514
- <meta property="og:title" content="${title}" />
515
- <meta property="og:description" content="${blocks.descriptionTxt}" />
516
- <meta property="og:locale" content="en_US" />
517
- <meta property="og:url" content="${url}" />
518
- <meta property="og:site_name" content="${title}" />
519
- <meta property="og:type" content="website" />
520
-
521
- <!-- Twitter Card -->
522
- <meta name="twitter:card" content="summary" />
523
- <meta name="twitter:title" content="${title}" />
524
- <meta name="twitter:description" content="${blocks.descriptionTxt}" />
525
-
526
- <!-- Structured Data -->
527
- <script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","headline":"${title}","name":"${title}","url":"${url}", "description":"${blocks.descriptionTxt}"}</script>
528
-
529
- <!-- Canonical Link -->
530
- <link rel="canonical" href="${url}" />
531
-
532
- <!-- Favicon -->
533
- <link href="data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAEAAAAAAAAAD9/f0AAAAAAPj4+AAMDAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAABERAAAAAAAAEREAAAAAAAAREQABERESABERAAETMzAAEREAARAAAAAREQABEAAAABERAAEQARAAEREAARABEAAREQABEAAAABERAAEQAAAAEREAAREREAAREQABEREQABERAAAAAAAAEREAAAAAAAAREQAAAAAAABHAAwAAwAMAAMADAADAAwAAwAMAAMADAADAAwAAwAMAAMADAADAAwAAwAMAAMADAADAAwAAwAMAAMADAADAAwAA" rel="icon" type="image/x-icon" />
534
-
535
- <!-- Styles -->
536
- <style>
537
- /** Main */
538
- html { font-size: 16px; }
539
- body, h1, h2, h3, h4, h5, h6, p, blockquote, pre, hr, dl, dd, ol, ul, figure { margin: 0; padding: 0; }
540
- body { font: 400 16px/1.5 -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Segoe UI Symbol", "Segoe UI Emoji", "Apple Color Emoji", Roboto, Helvetica, Arial, sans-serif; color: #111111; background-color: #fdfdfd; -webkit-text-size-adjust: 100%; -webkit-font-feature-settings: "kern" 1; -moz-font-feature-settings: "kern" 1; -o-font-feature-settings: "kern" 1; font-feature-settings: "kern" 1; font-kerning: normal; display: flex; min-height: 100vh; flex-direction: column; overflow-wrap: break-word; }
541
- h1, h2, h3, h4, h5, h6, p, blockquote, pre, ul, ol, dl, figure, .highlight { margin-bottom: 15px; }
542
- hr { margin-top: 30px; margin-bottom: 30px; border: 0; border-top: 1px solid #ececec; }
543
- main { display: block; /* Default value of display of main element is 'inline' in IE 11. */ }
544
- img { max-width: 100%; vertical-align: middle; }
545
- figure > img { display: block; }
546
- figcaption { font-size: 14px; }
547
- ul, ol { margin-left: 30px; }
548
- li > ul, li > ol { margin-bottom: 0; }
549
- h1, h2, h3, h4, h5, h6 { font-weight: 400; }
550
- a { color: #2a7ae2; text-decoration: none; }
551
- a:visited { color: #1756a9; }
552
- a:hover { color: #111111; text-decoration: underline; }
553
- .social-media-list a:hover, .pagination a:hover { text-decoration: none; }
554
- .social-media-list a:hover .username, .pagination a:hover .username { text-decoration: underline; }
555
- blockquote { color: #828282; border-left: 4px solid #e8e8e8; padding-left: 15px; font-size: 1.125rem; font-style: italic; }
556
- blockquote > :last-child { margin-bottom: 0; }
557
- blockquote i, blockquote em { font-style: normal; }
558
- pre, code { font-family: "Menlo", "Inconsolata", "Consolas", "Roboto Mono", "Ubuntu Mono", "Liberation Mono", "Courier New", monospace; font-size: 0.9375em; border: 1px solid #e8e8e8; border-radius: 3px; background-color: #eeeeff; }
559
- code { padding: 1px 5px; }
560
- pre { padding: 8px 12px; overflow-x: auto; }
561
- pre > code { border: 0; padding-right: 0; padding-left: 0; }
562
- .wrapper { max-width: calc(800px - (30px)); margin-right: auto; margin-left: auto; padding-right: 15px; padding-left: 15px; }
563
- @media screen and (min-width: 800px) { .wrapper { max-width: calc(800px - (30px * 2)); padding-right: 30px; padding-left: 30px; } }
564
- .wrapper:after { content: ""; display: table; clear: both; }
565
- .orange { color: #f66a0a; }
566
- .grey { color: #828282; }
567
- .svg-icon { width: 16px; height: 16px; display: inline-block; fill: currentColor; padding: 5px 3px 2px 5px; vertical-align: text-bottom; }
568
- table { margin-bottom: 30px; width: 100%; text-align: left; color: #3f3f3f; border-collapse: collapse; border: 1px solid #e8e8e8; }
569
- table tr:nth-child(even) { background-color: #f7f7f7; }
570
- table th, table td { padding: 10px 15px; }
571
- table th { background-color: #f0f0f0; border: 1px solid #e0e0e0; }
572
- table td { border: 1px solid #e8e8e8; }
573
- @media screen and (max-width: 800px) { table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; -ms-overflow-style: -ms-autohiding-scrollbar; } }
574
- .site-header { border-bottom: 1px solid #e8e8e8; min-height: 55.95px; line-height: 54px; position: relative; }
575
- .site-title { font-size: 1.625rem; font-weight: 800; letter-spacing: -1px; margin-bottom: 0; float: left; }
576
- @media screen and (max-width: 600px) { .site-title { padding-right: 45px; } }
577
- .site-title, .site-title:visited { color: #424242; }
578
- .site-nav { position: absolute; top: 9px; right: 15px; background-color: #fdfdfd; border: 1px solid #e8e8e8; border-radius: 5px; text-align: right; }
579
- .site-nav .nav-trigger { display: none; }
580
- .site-nav .menu-icon { float: right; width: 36px; height: 26px; line-height: 0; padding-top: 10px; text-align: center; }
581
- .site-nav .menu-icon > svg path { fill: #424242; }
582
- .site-nav label[for="nav-trigger"] { display: block; float: right; width: 36px; height: 36px; z-index: 2; cursor: pointer; }
583
- .site-nav input ~ .trigger { clear: both; display: none; }
584
- .site-nav input:checked ~ .trigger { display: block; padding-bottom: 5px; }
585
- .site-nav .page-link { color: #111111; line-height: 1.5; display: block; padding: 5px 10px; margin-left: 20px; }
586
- .site-nav .page-link:not(:last-child) { margin-right: 0; }
587
- @media screen and (min-width: 600px) { .site-nav { position: static; float: right; border: none; background-color: inherit; } .site-nav label[for="nav-trigger"] { display: none; } .site-nav .menu-icon { display: none; } .site-nav input ~ .trigger { display: block; } .site-nav .page-link { display: inline; padding: 0; margin-left: auto; } .site-nav .page-link:not(:last-child) { margin-right: 20px; } }
588
- .site-footer { border-top: 1px solid #e8e8e8; padding: 30px 0; }
589
- .footer-heading { font-size: 1.7rem; line-height: 1.7rem; font-weight: 200; margin-bottom: 5px;}
590
- .footer-heading a { color: #a2a2a2; text-decoration: none; }
591
- .footer-heading a:hover { color: #828282; }
592
- .footer-org p { font-size: 0.65rem; color: #828282; }
593
- .feed-subscribe .svg-icon { padding: 5px 5px 2px 0; }
594
- .contact-list, .social-media-list, .pagination { list-style: none; margin-left: 0; }
595
- .footer-col-wrapper, .social-links { font-size: 0.9375rem; color: #828282; }
596
- .footer-col { margin-bottom: 15px; }
597
- .footer-col-1, .footer-col-2 { width: calc(50% - (30px / 2)); }
598
- .footer-col-3 { width: calc(100% - (30px / 2)); }
599
- @media screen and (min-width: 800px) { .footer-col-1 { width: calc(35% - (30px / 2)); } .footer-col-2 { width: calc(20% - (30px / 2)); } .footer-col-3 { width: calc(45% - (30px / 2)); } }
600
- @media screen and (min-width: 600px) { .footer-col-wrapper { display: flex; } .footer-col { width: calc(100% - (30px / 2)); padding: 0 15px; } .footer-col:first-child { padding-right: 15px; padding-left: 0; } .footer-col:last-child { padding-right: 0; padding-left: 15px; } }
601
- /** Page content */
602
- .page-content { padding: 30px 0; flex: 1 0 auto; }
603
- .page-heading { font-size: 2rem; }
604
- .post-list-heading { font-size: 1.75rem; }
605
- .post-list { margin-left: 0; list-style: none; }
606
- .post-list > li { margin-bottom: 30px; }
607
- .post-meta { font-size: 14px; color: #828282; }
608
- .post-link { display: block; font-size: 1.5rem; }
609
- /** Posts */
610
- .post-header { margin-bottom: 30px; }
611
- .post-title, .post-content h1 { font-size: 2.625rem; letter-spacing: -1px; line-height: 1.15; }
612
- @media screen and (min-width: 800px) { .post-title, .post-content h1 { font-size: 2.625rem; } }
613
- .post-content { margin-bottom: 30px; }
614
- .post-content h1, .post-content h2, .post-content h3 { margin-top: 60px; }
615
- .post-content h4, .post-content h5, .post-content h6 { margin-top: 30px; }
616
- .post-content h2 { font-size: 1.75rem; }
617
- @media screen and (min-width: 800px) { .post-content h2 { font-size: 2rem; } }
618
- .post-content h3 { font-size: 1.375rem; }
619
- @media screen and (min-width: 800px) { .post-content h3 { font-size: 1.625rem; } }
620
- .post-content h4 { font-size: 1.25rem; }
621
- .post-content h5 { font-size: 1.125rem; }
622
- .post-content h6 { font-size: 1.0625rem; }
623
- .social-media-list, .pagination { display: table; margin: 0 auto; }
624
- .social-media-list li, .pagination li { float: left; margin: 5px 10px 5px 0; }
625
- .social-media-list li:last-of-type, .pagination li:last-of-type { margin-right: 0; }
626
- .social-media-list li a, .pagination li a { display: block; padding: 7.5px; border: 1px solid #e8e8e8; }
627
- .social-media-list li a:hover, .pagination li a:hover { border-color: #dbdbdb; }
628
- /** Pagination navbar */
629
- .pagination { margin-bottom: 30px; }
630
- .pagination li a, .pagination li div { min-width: 41px; text-align: center; box-sizing: border-box; }
631
- .pagination li div { display: block; padding: 7.5px; border: 1px solid transparent; }
632
- .pagination li div.pager-edge { color: #e8e8e8; border: 1px dashed; }
633
- /** Grid helpers */
634
- @media screen and (min-width: 800px) { .one-half { width: calc(50% - (30px / 2)); } }
635
- /** Jsee elements */
636
- .app-container { background-color: #F0F1F4; border-bottom: 1px solid #e8e8e8; padding-bottom: 55px }
637
- #download-btn { float: right; margin-top: 10px; padding: 10px; background-color: white; border: none; cursor: pointer; }
638
- #download-btn:hover { background-color: #f0f0f0; }
639
- .schema-description { background-color: #f8f8fa; padding: 20px; margin-top: 20px; border-radius: 10px; border: 1px solid #e8e8e8; }
640
- .schema-description h2, .schema-description h3, .schema-description h4 { margin-top: 10px; }
641
- /** Logos */
642
- .logo_footer { margin-left: -3px; }
643
- .logo_footer svg { opacity: 0.35; }
644
- .logo_footer:hover svg { opacity: 1; }
645
- .social-links { display: flex; justify-content: right; }
646
- .social-links .social-media-list, .social-links .pagination { margin: 0; }
647
- </style>
648
- <link type="application/atom+xml" rel="alternate" href="/feed.xml" title="hashr" />
649
- ${blocks.gaHtml}
650
- </head>
651
- <body>
652
- ${blocks.hiddenElementHtml}
653
- <header class="site-header">
654
- <div class="wrapper">
655
- <span class="site-title">${title}</span>
656
- <button id="download-btn" title="Download bundled HTML file without external dependencies to use offline">Download bundle (html)</button>
657
- </div>
658
- </header>
659
- <div class="page-content app-container">
660
- <div class="wrapper">
661
- <div id="jsee-container"></div>
662
- </div>
663
- </div>
664
- <main class="page-content" aria-label="Content">
665
- <div class="wrapper">
666
- <article class="post">
667
- <div class="post-content">
668
- ${blocks.descriptionHtml}
669
- </div>
670
- </article>
671
- </div>
672
- </main>
673
- <footer class="site-footer h-card">
674
- <data class="u-url" href="/"></data>
675
- <div class="wrapper">
676
- <div class="footer-col-wrapper">
677
- <div class="footer-col">
678
- ${blocks.orgHtml}
679
- </div>
680
- <div class="footer-col">
681
- <div class="social-links">
682
- <ul class="social-media-list">
683
- ${blocks.socialHtml}
684
- </ul>
685
- </div>
686
- </div>
687
- </div>
688
- </div>
689
- </footer>
690
- ${blocks.jseeHtml}
691
- <script>
692
- const schema = ${JSON.stringify(schema, null, 2)}
693
- const title = "${title}"
694
- var env = new JSEE({
695
- container: document.getElementById('jsee-container'),
696
- schema
697
- })
698
- document.getElementById('download-btn').addEventListener('click', async () => {
699
- env.download(title)
700
- })
701
- </script>
702
- </body>
703
- </html>`
704
- }
2
+ const gen = require('../src/cli.js')
3
+ gen(process.argv)