@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 +2 -703
- package/dist/jsee.js +1 -1
- package/dist/jsee.runtime.js +1 -1
- package/package.json +1 -1
- package/src/cli.js +786 -0
- package/src/main.js +9 -5
package/bin/jsee
CHANGED
|
@@ -1,704 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
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)
|