@liascript/exporter 3.0.0--1.0.3 → 3.0.1--1.0.3
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/dist/assets/capacitor/{index.bfe7363b.js → index.a7f021f7.js} +1 -1
- package/dist/assets/capacitor/index.html +1 -1
- package/dist/assets/capacitor/{jszip.min.f6eda75b.js → jszip.min.43389eb1.js} +1 -1
- package/dist/assets/capacitor/{trystero-ipfs.min.b27a61d7.js → trystero-ipfs.min.f25fe3e7.js} +1 -1
- package/dist/assets/indexeddb/{index.599a57d6.js → index.4aceca2f.js} +1 -1
- package/dist/assets/indexeddb/index.html +1 -1
- package/dist/assets/indexeddb/{jszip.min.63142cc8.js → jszip.min.4fbcc13f.js} +1 -1
- package/dist/assets/scorm2004/{index.7a5820ab.js → index.33bec53a.js} +1 -1
- package/dist/assets/scorm2004/index.html +1 -1
- package/dist/assets/scorm2004/{jszip.min.63142cc8.js → jszip.min.4fbcc13f.js} +1 -1
- package/dist/assets/xapi/{index.018a032a.js → index.f2e89e49.js} +1 -1
- package/dist/assets/xapi/index.html +1 -1
- package/dist/assets/xapi/{jszip.min.eaecf580.js → jszip.min.19c66d77.js} +1 -1
- package/dist/index.js +47 -47
- package/dist/server/presets.json +94 -0
- package/dist/server/presets.yaml +120 -0
- package/dist/server/public/app.js +1 -0
- package/dist/server/public/assets/android.svg +38 -0
- package/dist/server/public/assets/cmi.svg +154 -0
- package/dist/server/public/assets/docx.svg +20 -0
- package/dist/server/public/assets/edX.svg +75 -0
- package/dist/server/public/assets/edx.svg +75 -0
- package/dist/server/public/assets/epub.svg +18 -0
- package/dist/server/public/assets/icon.svg +82 -0
- package/dist/server/public/assets/ilias.png +0 -0
- package/dist/server/public/assets/json.svg +4 -0
- package/dist/server/public/assets/learnworlds.png +0 -0
- package/dist/server/public/assets/moodle.svg +190 -0
- package/dist/server/public/assets/opal.png +0 -0
- package/dist/server/public/assets/openolat.png +0 -0
- package/dist/server/public/assets/pdf.svg +4 -0
- package/dist/server/public/assets/rdf.svg +4 -0
- package/dist/server/public/assets/scorm.png +0 -0
- package/dist/server/public/assets/web.png +0 -0
- package/dist/server/public/assets/xapi.png +0 -0
- package/dist/server/public/i18n.js +1 -0
- package/dist/server/public/index.html +1587 -0
- package/dist/server/public/locales/de.json +247 -0
- package/dist/server/public/locales/en.json +247 -0
- package/dist/server/public/status.html +251 -0
- package/dist/server/public/styles.css +712 -0
- package/package.json +5 -1
- package/.parcelrc +0 -3
- package/DESKTOP_APP_README.md +0 -58
- package/DOCKERHUB_DESCRIPTION.md +0 -52
- package/Dockerfile +0 -129
- package/PLAYSTORE_GUIDE.md +0 -172
- package/action.yml +0 -157
- package/custom.css +0 -10
- package/electron-builder.json +0 -149
- package/src/cli.ts +0 -69
- package/src/colorize.ts +0 -115
- package/src/export/android.ts +0 -419
- package/src/export/docx.ts +0 -1025
- package/src/export/epub.ts +0 -1306
- package/src/export/h5p.ts +0 -390
- package/src/export/helper.ts +0 -360
- package/src/export/ims.ts +0 -191
- package/src/export/pdf.ts +0 -406
- package/src/export/presets.ts +0 -220
- package/src/export/project.ts +0 -829
- package/src/export/rdf.ts +0 -551
- package/src/export/scorm12.ts +0 -167
- package/src/export/scorm2004.ts +0 -140
- package/src/export/web.ts +0 -306
- package/src/export/xapi.ts +0 -424
- package/src/exporter.ts +0 -296
- package/src/index.ts +0 -96
- package/src/parser.ts +0 -373
- package/src/presets.yaml +0 -219
- package/src/types.ts +0 -82
- package/tsconfig.json +0 -24
package/src/export/rdf.ts
DELETED
|
@@ -1,551 +0,0 @@
|
|
|
1
|
-
import * as helper from './helper'
|
|
2
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
3
|
-
const jsonld = require('jsonld')
|
|
4
|
-
import * as fs from 'fs-extra'
|
|
5
|
-
import fetch from 'node-fetch'
|
|
6
|
-
import * as COLOR from '../colorize'
|
|
7
|
-
|
|
8
|
-
// Type definitions
|
|
9
|
-
export interface RDFArguments {
|
|
10
|
-
input: string
|
|
11
|
-
readme: string
|
|
12
|
-
output: string
|
|
13
|
-
format: string
|
|
14
|
-
path: string
|
|
15
|
-
key?: string
|
|
16
|
-
style?: string
|
|
17
|
-
'rdf-format'?: string
|
|
18
|
-
'rdf-preview'?: string
|
|
19
|
-
'rdf-url'?: string
|
|
20
|
-
'rdf-type'?: string
|
|
21
|
-
'rdf-template'?: string
|
|
22
|
-
'rdf-license'?: string
|
|
23
|
-
'rdf-educationalLevel'?: string
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface LiaDefinition {
|
|
27
|
-
author?: string
|
|
28
|
-
email?: string
|
|
29
|
-
language?: string
|
|
30
|
-
logo?: string
|
|
31
|
-
version?: string
|
|
32
|
-
macro?: {
|
|
33
|
-
comment?: string
|
|
34
|
-
tags?: string
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface SchemaDoc {
|
|
39
|
-
[key: string]: any
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Schema.org constants
|
|
43
|
-
const SCHEMA_ORG_CONTEXT = 'http://schema.org/'
|
|
44
|
-
const SCHEMA_ORG_PROPS = {
|
|
45
|
-
TYPE: 'http://schema.org/@type',
|
|
46
|
-
ID: 'http://schema.org/@id',
|
|
47
|
-
NAME: 'http://schema.org/name',
|
|
48
|
-
URL: 'http://schema.org/url',
|
|
49
|
-
AUTHOR: 'http://schema.org/author',
|
|
50
|
-
EMAIL: 'http://schema.org/email',
|
|
51
|
-
DESCRIPTION: 'http://schema.org/description',
|
|
52
|
-
KEYWORDS: 'http://schema.org/keywords',
|
|
53
|
-
VERSION: 'http://schema.org/version',
|
|
54
|
-
IN_LANGUAGE: 'http://schema.org/inLanguage',
|
|
55
|
-
IMAGE: 'http://schema.org/image',
|
|
56
|
-
LICENSE: 'http://schema.org/license',
|
|
57
|
-
EDUCATIONAL_LEVEL: 'http://schema.org/educationalLevel',
|
|
58
|
-
} as const
|
|
59
|
-
|
|
60
|
-
const SCHEMA_TYPES = {
|
|
61
|
-
PERSON: 'Person',
|
|
62
|
-
IMAGE_OBJECT: 'ImageObject',
|
|
63
|
-
COURSE: 'Course',
|
|
64
|
-
} as const
|
|
65
|
-
|
|
66
|
-
const LIASCRIPT_COURSE_URL = 'https://LiaScript.github.io/course/?'
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Displays help information for RDF export options.
|
|
70
|
-
* Shows available command-line flags and their descriptions for configuring
|
|
71
|
-
* RDF/JSON-LD metadata export from LiaScript courses.
|
|
72
|
-
*/
|
|
73
|
-
export function help() {
|
|
74
|
-
console.log('')
|
|
75
|
-
console.log(COLOR.heading('RDF settings:'), '\n')
|
|
76
|
-
|
|
77
|
-
COLOR.info(
|
|
78
|
-
'RDF (Resource Description Framework) export generates structured metadata for your LiaScript course or your project-yaml in standard linked data formats. This helps with course discovery and enables semantic web applications to understand your content. Available output formats are n-quads and JSON-LD.',
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
console.log('\nLearn more:')
|
|
82
|
-
console.log('- RDF: https://www.w3.org/RDF/')
|
|
83
|
-
console.log('- N-Quads: https://www.w3.org/TR/n-quads/')
|
|
84
|
-
console.log('- JSON-LD: https://json-ld.org/')
|
|
85
|
-
console.log('')
|
|
86
|
-
|
|
87
|
-
COLOR.command(
|
|
88
|
-
null,
|
|
89
|
-
'--rdf-format',
|
|
90
|
-
' Output format n-quads, json-ld (defaults to json-ld).',
|
|
91
|
-
)
|
|
92
|
-
COLOR.command(
|
|
93
|
-
null,
|
|
94
|
-
'--rdf-preview',
|
|
95
|
-
' Output the result to the console.',
|
|
96
|
-
)
|
|
97
|
-
COLOR.command(
|
|
98
|
-
null,
|
|
99
|
-
'--rdf-url',
|
|
100
|
-
' Refer to an external URL when parsing a local project.',
|
|
101
|
-
)
|
|
102
|
-
COLOR.command(
|
|
103
|
-
null,
|
|
104
|
-
'--rdf-type',
|
|
105
|
-
' Course from schema.org is applied as default, overwrite this with EducationalResource, etc.',
|
|
106
|
-
)
|
|
107
|
-
COLOR.command(
|
|
108
|
-
null,
|
|
109
|
-
'--rdf-license',
|
|
110
|
-
' Add a license-URL, otherwise if url was provided as input, this will check for an existing LICENSE file.',
|
|
111
|
-
)
|
|
112
|
-
COLOR.command(
|
|
113
|
-
null,
|
|
114
|
-
'--rdf-educationalLevel',
|
|
115
|
-
' Typically beginner, intermediate or advanced, and formal sets of level indicators.',
|
|
116
|
-
)
|
|
117
|
-
COLOR.command(
|
|
118
|
-
null,
|
|
119
|
-
'--rdf-template',
|
|
120
|
-
' Use a URL or json-file as a template.',
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export const format = 'rdf'
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Exports LiaScript course metadata as RDF in either n-quads or JSON-LD format.
|
|
128
|
-
*
|
|
129
|
-
* @param argument Configuration options including output format, preview mode, and RDF settings
|
|
130
|
-
* @param json Parsed LiaScript course data containing metadata and content
|
|
131
|
-
* @throws Error if export fails (template loading, file writing, or JSON-LD processing)
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* // Export as JSON-LD
|
|
135
|
-
* await exporter({ input: 'course.md', output: 'course', 'rdf-format': 'json-ld' }, liaJson)
|
|
136
|
-
*
|
|
137
|
-
* @example
|
|
138
|
-
* // Preview n-quads output
|
|
139
|
-
* await exporter({ input: 'course.md', output: 'course', 'rdf-format': 'n-quads', 'rdf-preview': 'true' }, liaJson)
|
|
140
|
-
*/
|
|
141
|
-
export async function exporter(
|
|
142
|
-
argument: RDFArguments,
|
|
143
|
-
json: any,
|
|
144
|
-
): Promise<void> {
|
|
145
|
-
try {
|
|
146
|
-
let doc = await parse(argument, json)
|
|
147
|
-
|
|
148
|
-
if (argument['rdf-format'] === 'n-quads') {
|
|
149
|
-
const nquads = await jsonld.toRDF(doc, { format: 'application/n-quads' })
|
|
150
|
-
if (argument['rdf-preview']) {
|
|
151
|
-
console.log(nquads)
|
|
152
|
-
} else {
|
|
153
|
-
await fs.writeFile(argument.output + '.nq', nquads)
|
|
154
|
-
}
|
|
155
|
-
} else {
|
|
156
|
-
doc = clean(doc)
|
|
157
|
-
if (argument['rdf-preview']) {
|
|
158
|
-
console.log(JSON.stringify(doc, null, 2))
|
|
159
|
-
} else {
|
|
160
|
-
await fs.writeFile(
|
|
161
|
-
argument.output + '.jsonld',
|
|
162
|
-
JSON.stringify(doc, null, 2),
|
|
163
|
-
)
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
} catch (err) {
|
|
167
|
-
console.error('Error exporting RDF:', err)
|
|
168
|
-
throw err
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Generates an HTML script tag containing JSON-LD structured data.
|
|
174
|
-
* Useful for embedding schema.org metadata directly in HTML pages for SEO.
|
|
175
|
-
*
|
|
176
|
-
* @param argument RDF configuration options
|
|
177
|
-
* @param json Parsed LiaScript course data
|
|
178
|
-
* @returns HTML script tag with embedded JSON-LD
|
|
179
|
-
* @throws Error if parsing or compaction fails
|
|
180
|
-
*
|
|
181
|
-
* @example
|
|
182
|
-
* const scriptTag = await script(argument, liaJson)
|
|
183
|
-
* // Returns: <script type="application/ld+json">{ ... }</script>
|
|
184
|
-
*/
|
|
185
|
-
export async function script(
|
|
186
|
-
argument: RDFArguments,
|
|
187
|
-
json: LiaJSON,
|
|
188
|
-
): Promise<string> {
|
|
189
|
-
try {
|
|
190
|
-
let doc = await parse(argument, json)
|
|
191
|
-
|
|
192
|
-
doc = await jsonld.compact(doc, SCHEMA_ORG_CONTEXT)
|
|
193
|
-
doc = clean(doc)
|
|
194
|
-
|
|
195
|
-
return `<script type="application/ld+json">
|
|
196
|
-
${JSON.stringify(doc, null, 2)}
|
|
197
|
-
</script>`
|
|
198
|
-
} catch (error) {
|
|
199
|
-
console.error('Error generating RDF script tag:', error)
|
|
200
|
-
throw error
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Compacts a JSON-LD document using the schema.org context.
|
|
206
|
-
* Reduces verbosity by using the schema.org context to shorten property names.
|
|
207
|
-
*
|
|
208
|
-
* @param doc The expanded JSON-LD document to compact
|
|
209
|
-
* @returns The compacted JSON-LD document
|
|
210
|
-
* @throws Error if compaction fails
|
|
211
|
-
*/
|
|
212
|
-
export async function compact(doc: SchemaDoc): Promise<SchemaDoc> {
|
|
213
|
-
try {
|
|
214
|
-
return await jsonld.compact(doc, SCHEMA_ORG_CONTEXT)
|
|
215
|
-
} catch (error) {
|
|
216
|
-
console.error('Error compacting JSON-LD document:', error)
|
|
217
|
-
throw error
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Loads and expands a template from a URL or local file.
|
|
223
|
-
* @param templatePath URL or file path to the template
|
|
224
|
-
* @returns Expanded JSON-LD document or empty object if template is not provided
|
|
225
|
-
*/
|
|
226
|
-
async function loadTemplate(templatePath?: string): Promise<SchemaDoc> {
|
|
227
|
-
if (!templatePath) {
|
|
228
|
-
return {}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
try {
|
|
232
|
-
let data: any
|
|
233
|
-
|
|
234
|
-
if (helper.isURL(templatePath)) {
|
|
235
|
-
const resp = await fetch(templatePath, {})
|
|
236
|
-
if (!resp.ok) {
|
|
237
|
-
throw new Error(`Failed to fetch template: ${resp.statusText}`)
|
|
238
|
-
}
|
|
239
|
-
data = await resp.json()
|
|
240
|
-
} else {
|
|
241
|
-
const fileContent = await fs.readFile(templatePath, 'utf8')
|
|
242
|
-
data = JSON.parse(fileContent)
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (data) {
|
|
246
|
-
return await jsonld.expand(data)
|
|
247
|
-
} else {
|
|
248
|
-
console.warn('could not load template from:', templatePath)
|
|
249
|
-
return {}
|
|
250
|
-
}
|
|
251
|
-
} catch (error) {
|
|
252
|
-
console.error('Error loading template:', error)
|
|
253
|
-
throw error
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Sets the core schema properties (name, type) on the document.
|
|
259
|
-
* @param doc The document to populate
|
|
260
|
-
* @param json LiaScript JSON data
|
|
261
|
-
* @param rdfType Optional type override from arguments
|
|
262
|
-
*/
|
|
263
|
-
function setCoreProperties(doc: SchemaDoc, json: any, rdfType?: string): void {
|
|
264
|
-
doc[SCHEMA_ORG_PROPS.NAME] = json.lia.str_title
|
|
265
|
-
doc[SCHEMA_ORG_PROPS.TYPE] =
|
|
266
|
-
doc[SCHEMA_ORG_PROPS.TYPE] || rdfType || SCHEMA_TYPES.COURSE
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Resolves the base URL from input or rdf-url argument.
|
|
271
|
-
* @param input The input URL/path
|
|
272
|
-
* @param rdfUrl Optional explicit RDF URL
|
|
273
|
-
* @returns Base URL or null if not a URL
|
|
274
|
-
*/
|
|
275
|
-
function resolveBaseURL(input: string, rdfUrl?: string): string | null {
|
|
276
|
-
if (helper.isURL(input) || rdfUrl) {
|
|
277
|
-
return helper.baseURL(rdfUrl || input)
|
|
278
|
-
}
|
|
279
|
-
return null
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Sets URL-related properties on the document if a base URL is available.
|
|
284
|
-
* @param doc The document to populate
|
|
285
|
-
* @param input The input URL/path
|
|
286
|
-
* @param rdfUrl Optional explicit RDF URL
|
|
287
|
-
* @returns The resolved base URL or null
|
|
288
|
-
*/
|
|
289
|
-
function setURLProperties(
|
|
290
|
-
doc: SchemaDoc,
|
|
291
|
-
input: string,
|
|
292
|
-
rdfUrl?: string,
|
|
293
|
-
): string | null {
|
|
294
|
-
if (helper.isURL(input) || rdfUrl) {
|
|
295
|
-
const urlValue = rdfUrl || input
|
|
296
|
-
doc[SCHEMA_ORG_PROPS.ID] = doc[SCHEMA_ORG_PROPS.ID] || urlValue
|
|
297
|
-
doc[SCHEMA_ORG_PROPS.URL] =
|
|
298
|
-
doc[SCHEMA_ORG_PROPS.URL] || LIASCRIPT_COURSE_URL + urlValue
|
|
299
|
-
|
|
300
|
-
return resolveBaseURL(input, rdfUrl)
|
|
301
|
-
}
|
|
302
|
-
return null
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Enriches the document with all metadata from LiaScript definition.
|
|
307
|
-
* @param doc The document to enrich
|
|
308
|
-
* @param definition LiaScript definition with metadata
|
|
309
|
-
* @param baseURL Base URL for resolving relative URLs
|
|
310
|
-
* @param argument RDF arguments for license and educational level
|
|
311
|
-
*/
|
|
312
|
-
async function enrichMetadata(
|
|
313
|
-
doc: SchemaDoc,
|
|
314
|
-
definition: LiaDefinition,
|
|
315
|
-
baseURL: string | null,
|
|
316
|
-
argument: RDFArguments,
|
|
317
|
-
): Promise<SchemaDoc> {
|
|
318
|
-
doc = baseInformation(doc, definition)
|
|
319
|
-
doc = langInformation(doc, definition)
|
|
320
|
-
doc = logoInformation(doc, definition, baseURL)
|
|
321
|
-
doc = await licenseInformation(doc, argument, baseURL)
|
|
322
|
-
|
|
323
|
-
if (argument['rdf-educationalLevel']) {
|
|
324
|
-
doc[SCHEMA_ORG_PROPS.EDUCATIONAL_LEVEL] = argument['rdf-educationalLevel']
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return doc
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Parses LiaScript JSON and generates a schema.org JSON-LD document.
|
|
332
|
-
* This is the main orchestration function that coordinates template loading,
|
|
333
|
-
* property setting, metadata enrichment, and document compaction.
|
|
334
|
-
*
|
|
335
|
-
* @param argument RDF export arguments including URLs, type, and template options
|
|
336
|
-
* @param json LiaScript JSON data containing course title and metadata
|
|
337
|
-
* @returns A compacted and cleaned JSON-LD document ready for export
|
|
338
|
-
* @throws Error if template loading, JSON-LD expansion, or compaction fails
|
|
339
|
-
*
|
|
340
|
-
* @example
|
|
341
|
-
* const rdfDoc = await parse({
|
|
342
|
-
* input: 'https://example.com/course.md',
|
|
343
|
-
* 'rdf-type': 'Course',
|
|
344
|
-
* 'rdf-educationalLevel': 'beginner'
|
|
345
|
-
* }, liaJson)
|
|
346
|
-
*/
|
|
347
|
-
export async function parse(
|
|
348
|
-
argument: RDFArguments,
|
|
349
|
-
json: string,
|
|
350
|
-
): Promise<SchemaDoc> {
|
|
351
|
-
try {
|
|
352
|
-
// Load template if provided
|
|
353
|
-
let doc = await loadTemplate(argument['rdf-template'])
|
|
354
|
-
|
|
355
|
-
// Set core properties
|
|
356
|
-
setCoreProperties(doc, json, argument['rdf-type'])
|
|
357
|
-
|
|
358
|
-
// Set URL properties and resolve base URL
|
|
359
|
-
const baseURL = setURLProperties(doc, argument.input, argument['rdf-url'])
|
|
360
|
-
|
|
361
|
-
// Enrich with all metadata
|
|
362
|
-
doc = await enrichMetadata(doc, json.lia.definition, baseURL, argument)
|
|
363
|
-
|
|
364
|
-
// Compact and clean the document
|
|
365
|
-
doc = await jsonld.compact(doc, SCHEMA_ORG_CONTEXT)
|
|
366
|
-
return clean(doc)
|
|
367
|
-
} catch (error) {
|
|
368
|
-
console.error('Error parsing LiaScript to RDF:', error)
|
|
369
|
-
throw error
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* Adds base metadata information to the schema.org document.
|
|
375
|
-
* Extracts and adds the following properties from the LiaScript definition:
|
|
376
|
-
* - author (name and email as schema:Person)
|
|
377
|
-
* - description (from macro comment)
|
|
378
|
-
* - keywords (from macro tags, comma-separated)
|
|
379
|
-
* - version
|
|
380
|
-
*
|
|
381
|
-
* @param doc The schema.org document to populate
|
|
382
|
-
* @param definition LiaScript definition containing metadata
|
|
383
|
-
* @returns The enriched document with base information
|
|
384
|
-
*/
|
|
385
|
-
function baseInformation(doc: SchemaDoc, definition: LiaDefinition): SchemaDoc {
|
|
386
|
-
if (definition?.author || definition?.email) {
|
|
387
|
-
const author: SchemaDoc = { [SCHEMA_ORG_PROPS.TYPE]: SCHEMA_TYPES.PERSON }
|
|
388
|
-
|
|
389
|
-
if (definition?.author) {
|
|
390
|
-
author[SCHEMA_ORG_PROPS.NAME] = definition?.author
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if (definition?.email) {
|
|
394
|
-
author[SCHEMA_ORG_PROPS.EMAIL] = definition?.email
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
doc[SCHEMA_ORG_PROPS.AUTHOR] = doc[SCHEMA_ORG_PROPS.AUTHOR] || author
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
if (definition.macro?.comment) {
|
|
401
|
-
doc[SCHEMA_ORG_PROPS.DESCRIPTION] =
|
|
402
|
-
doc[SCHEMA_ORG_PROPS.DESCRIPTION] || definition.macro?.comment
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
if (definition.macro?.tags) {
|
|
406
|
-
if (typeof doc[SCHEMA_ORG_PROPS.KEYWORDS] === 'string') {
|
|
407
|
-
doc[SCHEMA_ORG_PROPS.KEYWORDS] += ', ' + definition.macro.tags
|
|
408
|
-
} else {
|
|
409
|
-
const tags = definition.macro.tags.split(',').map((e: string) => e.trim())
|
|
410
|
-
|
|
411
|
-
if (typeof doc[SCHEMA_ORG_PROPS.KEYWORDS] === 'undefined') {
|
|
412
|
-
doc[SCHEMA_ORG_PROPS.KEYWORDS] = tags
|
|
413
|
-
} else {
|
|
414
|
-
doc[SCHEMA_ORG_PROPS.KEYWORDS] =
|
|
415
|
-
doc[SCHEMA_ORG_PROPS.KEYWORDS].concat(tags)
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
if (definition?.version) {
|
|
421
|
-
doc[SCHEMA_ORG_PROPS.VERSION] =
|
|
422
|
-
doc[SCHEMA_ORG_PROPS.VERSION] || definition?.version
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
return doc
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Adds language information to the schema.org document.
|
|
430
|
-
* Sets the inLanguage property from the LiaScript definition.
|
|
431
|
-
*
|
|
432
|
-
* @param doc The schema.org document to populate
|
|
433
|
-
* @param definition LiaScript definition containing language metadata
|
|
434
|
-
* @returns The document with language information added
|
|
435
|
-
* @todo Add support for course translations
|
|
436
|
-
*/
|
|
437
|
-
function langInformation(doc: SchemaDoc, definition: LiaDefinition): SchemaDoc {
|
|
438
|
-
if (definition?.language) {
|
|
439
|
-
doc[SCHEMA_ORG_PROPS.IN_LANGUAGE] =
|
|
440
|
-
doc[SCHEMA_ORG_PROPS.IN_LANGUAGE] || definition.language
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
return doc
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* Adds image/logo information to the schema.org document.
|
|
448
|
-
* Converts the LiaScript logo to a schema:ImageObject with proper URL resolution.
|
|
449
|
-
* Handles both absolute URLs and relative paths (resolved against baseURL).
|
|
450
|
-
*
|
|
451
|
-
* @param doc The schema.org document to populate
|
|
452
|
-
* @param definition LiaScript definition containing logo path
|
|
453
|
-
* @param baseURL Base URL for resolving relative logo paths, or null if not available
|
|
454
|
-
* @returns The document with image information added
|
|
455
|
-
* @todo Add support for thumbnailUrl from LiaScript icon
|
|
456
|
-
*/
|
|
457
|
-
function logoInformation(
|
|
458
|
-
doc: SchemaDoc,
|
|
459
|
-
definition: LiaDefinition,
|
|
460
|
-
baseURL: null | string,
|
|
461
|
-
): SchemaDoc {
|
|
462
|
-
if (definition?.logo) {
|
|
463
|
-
let imageUrl: string | null = null
|
|
464
|
-
|
|
465
|
-
if (helper.isURL(definition?.logo)) {
|
|
466
|
-
imageUrl = definition?.logo
|
|
467
|
-
} else if (baseURL) {
|
|
468
|
-
imageUrl = new URL(definition?.logo, baseURL).href
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
if (imageUrl) {
|
|
472
|
-
doc[SCHEMA_ORG_PROPS.IMAGE] = doc[SCHEMA_ORG_PROPS.IMAGE] || {
|
|
473
|
-
[SCHEMA_ORG_PROPS.TYPE]: SCHEMA_TYPES.IMAGE_OBJECT,
|
|
474
|
-
[SCHEMA_ORG_PROPS.URL]: imageUrl,
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
return doc
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
/**
|
|
483
|
-
* Adds license information to the schema.org document.
|
|
484
|
-
* Attempts to determine license URL from:
|
|
485
|
-
* 1. Explicit rdf-license argument
|
|
486
|
-
* 2. LICENSE file at the base URL (if it exists)
|
|
487
|
-
*
|
|
488
|
-
* @param doc The schema.org document to populate
|
|
489
|
-
* @param argument RDF arguments containing explicit license URL
|
|
490
|
-
* @param baseURL Base URL for checking LICENSE file existence
|
|
491
|
-
* @returns Promise resolving to the document with license information
|
|
492
|
-
*/
|
|
493
|
-
async function licenseInformation(
|
|
494
|
-
doc: SchemaDoc,
|
|
495
|
-
argument: RDFArguments,
|
|
496
|
-
baseURL: null | string,
|
|
497
|
-
): Promise<SchemaDoc> {
|
|
498
|
-
let licenseUrl: string | null = null
|
|
499
|
-
|
|
500
|
-
if (argument['rdf-license']) {
|
|
501
|
-
licenseUrl = argument['rdf-license']
|
|
502
|
-
} else if (baseURL && (await helper.checkLicense(baseURL))) {
|
|
503
|
-
try {
|
|
504
|
-
licenseUrl = new URL('LICENSE', baseURL).href
|
|
505
|
-
} catch (error) {
|
|
506
|
-
console.warn(
|
|
507
|
-
'Could not create a URL for the LICENSE file, using base URL as license URL.',
|
|
508
|
-
baseURL,
|
|
509
|
-
)
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
if (licenseUrl) {
|
|
514
|
-
doc[SCHEMA_ORG_PROPS.LICENSE] = doc[SCHEMA_ORG_PROPS.LICENSE] || licenseUrl
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
return doc
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* Recursively cleans a JSON-LD document by removing 'schema:' prefixes from keys.
|
|
522
|
-
* The jsonld library sometimes adds 'schema:' prefixes to keys ending in 'Url',
|
|
523
|
-
* this function normalizes them by removing the prefix.
|
|
524
|
-
*
|
|
525
|
-
* @param obj The object to clean (can be nested objects or arrays)
|
|
526
|
-
* @returns The cleaned object with 'schema:' prefixes removed from all keys
|
|
527
|
-
*
|
|
528
|
-
* @example
|
|
529
|
-
* // Input: { 'schema:imageUrl': 'http://...', name: 'Course' }
|
|
530
|
-
* // Output: { imageUrl: 'http://...', name: 'Course' }
|
|
531
|
-
*/
|
|
532
|
-
function clean(obj: any): any {
|
|
533
|
-
if (!obj || typeof obj !== 'object') {
|
|
534
|
-
return obj
|
|
535
|
-
}
|
|
536
|
-
if (Array.isArray(obj)) {
|
|
537
|
-
return obj.map((item: any) => clean(item))
|
|
538
|
-
}
|
|
539
|
-
return Object.keys(obj).reduce(
|
|
540
|
-
(acc: Record<string, any>, key: string) => {
|
|
541
|
-
const value = obj[key]
|
|
542
|
-
if (key.startsWith('schema:')) {
|
|
543
|
-
acc[key.split(':').pop() || key] = value
|
|
544
|
-
} else {
|
|
545
|
-
acc[key] = clean(value)
|
|
546
|
-
}
|
|
547
|
-
return acc
|
|
548
|
-
},
|
|
549
|
-
{} as Record<string, any>,
|
|
550
|
-
)
|
|
551
|
-
}
|
package/src/export/scorm12.ts
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import * as helper from './helper'
|
|
2
|
-
import * as RDF from './rdf'
|
|
3
|
-
import * as COLOR from '../colorize'
|
|
4
|
-
|
|
5
|
-
const scormPackager = require('@liascript/simple-scorm-packager')
|
|
6
|
-
const path = require('path')
|
|
7
|
-
const fs = require('fs-extra')
|
|
8
|
-
|
|
9
|
-
export function help() {
|
|
10
|
-
console.log('')
|
|
11
|
-
console.log(COLOR.heading('SCORM settings:'), '\n')
|
|
12
|
-
|
|
13
|
-
COLOR.info(
|
|
14
|
-
'SCORM (Sharable Content Object Reference Model) 1.2 & 2004 are standards for e-learning content that can be imported into LMS platforms like Moodle, Blackboard, and others.',
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
console.log('\nLearn more: https://scorm.com/scorm-explained/\n')
|
|
18
|
-
|
|
19
|
-
console.log(
|
|
20
|
-
'Known SCORM configurations per LMS:\n https://www.npmjs.com/package/@liascript/exporter#scorm-examples\n',
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
COLOR.command(
|
|
24
|
-
null,
|
|
25
|
-
'--scorm-organization',
|
|
26
|
-
' set the organization title',
|
|
27
|
-
)
|
|
28
|
-
COLOR.command(
|
|
29
|
-
null,
|
|
30
|
-
'--scorm-masteryScore',
|
|
31
|
-
' set the scorm masteryScore (a value between 0 -- 100), default is 0',
|
|
32
|
-
)
|
|
33
|
-
COLOR.command(
|
|
34
|
-
null,
|
|
35
|
-
'--scorm-typicalDuration',
|
|
36
|
-
' set the scorm duration, default is PT0H5M0S',
|
|
37
|
-
)
|
|
38
|
-
COLOR.command(
|
|
39
|
-
null,
|
|
40
|
-
'--scorm-iframe',
|
|
41
|
-
' use an iframe, when a SCORM starting parameter is not working',
|
|
42
|
-
)
|
|
43
|
-
COLOR.command(
|
|
44
|
-
null,
|
|
45
|
-
'--scorm-embed',
|
|
46
|
-
' embed the Markdown into the JS code, use in Moodle 4 to handle restrictions with dynamic loading',
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface Scorm12ExportArguments {
|
|
51
|
-
input: string
|
|
52
|
-
readme: string
|
|
53
|
-
output: string
|
|
54
|
-
format: string
|
|
55
|
-
path: string
|
|
56
|
-
key?: string
|
|
57
|
-
style?: string
|
|
58
|
-
'scorm-organization'?: string
|
|
59
|
-
'scorm-masteryScore'?: string
|
|
60
|
-
'scorm-typicalDuration'?: string
|
|
61
|
-
'scorm-iframe'?: boolean
|
|
62
|
-
'scorm-embed'?: string | boolean
|
|
63
|
-
'scorm-alwaysActive'?: boolean
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export const format = 'scorm1.2'
|
|
67
|
-
|
|
68
|
-
export async function exporter(argument: Scorm12ExportArguments, json: any) {
|
|
69
|
-
// make temp folder
|
|
70
|
-
let tmp = await helper.tmpDir()
|
|
71
|
-
const dirname = helper.dirname()
|
|
72
|
-
|
|
73
|
-
let tmpPath = path.join(tmp, 'pro')
|
|
74
|
-
|
|
75
|
-
// copy assets to temp
|
|
76
|
-
await fs.copy(path.join(dirname, './assets/scorm1.2'), tmpPath)
|
|
77
|
-
await fs.copy(path.join(dirname, './assets/common'), tmpPath)
|
|
78
|
-
|
|
79
|
-
let index = fs.readFileSync(path.join(tmpPath, 'index.html'), 'utf8')
|
|
80
|
-
|
|
81
|
-
// change responsive key
|
|
82
|
-
if (argument.key) {
|
|
83
|
-
index = helper.injectResponsivevoice(argument.key, index)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
index = helper.inject('<script src="config.js"></script>', index)
|
|
87
|
-
await helper.writeFile(
|
|
88
|
-
path.join(tmpPath, 'config.js'),
|
|
89
|
-
'window.config_ = ' +
|
|
90
|
-
JSON.stringify({
|
|
91
|
-
task: json.task,
|
|
92
|
-
quiz: json.quiz,
|
|
93
|
-
survey: json.survey,
|
|
94
|
-
}) +
|
|
95
|
-
';',
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
const jsonLD = await RDF.script(argument, json)
|
|
99
|
-
|
|
100
|
-
if (argument['scorm-iframe']) {
|
|
101
|
-
await helper.iframe(
|
|
102
|
-
tmpPath,
|
|
103
|
-
'start.html',
|
|
104
|
-
argument.readme,
|
|
105
|
-
jsonLD,
|
|
106
|
-
argument.style,
|
|
107
|
-
)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (argument['scorm-embed']) {
|
|
111
|
-
index = helper.inject('<script src="course.js"></script>', index, true)
|
|
112
|
-
await helper.writeFile(
|
|
113
|
-
path.join(tmpPath, 'course.js'),
|
|
114
|
-
'window["liascript_course"] = ' + JSON.stringify(argument['scorm-embed']),
|
|
115
|
-
)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
try {
|
|
119
|
-
index = helper.inject(jsonLD, index)
|
|
120
|
-
await helper.writeFile(path.join(tmpPath, 'index.html'), index)
|
|
121
|
-
} catch (e) {
|
|
122
|
-
console.warn(e)
|
|
123
|
-
return
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// copy base path or readme-directory into temp
|
|
127
|
-
await fs.copy(argument.path, tmpPath, {
|
|
128
|
-
filter: helper.filterHidden(argument.path),
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
let config = {
|
|
132
|
-
version: '1.2',
|
|
133
|
-
organization: argument['scorm-organization'] || 'LiaScript',
|
|
134
|
-
title: json.lia.str_title,
|
|
135
|
-
language: json.lia.definition.language,
|
|
136
|
-
masteryScore: argument['scorm-masteryScore'] || 0,
|
|
137
|
-
startingPage: argument['scorm-iframe'] ? 'start.html' : 'index.html',
|
|
138
|
-
startingParameters:
|
|
139
|
-
argument['scorm-iframe'] || argument['scorm-embed']
|
|
140
|
-
? undefined
|
|
141
|
-
: argument.readme,
|
|
142
|
-
source: path.join(tmp, 'pro'),
|
|
143
|
-
package: {
|
|
144
|
-
version: json.lia.definition.version,
|
|
145
|
-
zip: true,
|
|
146
|
-
appendTimeToOutput: false,
|
|
147
|
-
name: path.basename(argument.output),
|
|
148
|
-
author: json.lia.definition.author,
|
|
149
|
-
outputFolder: path.dirname(argument.output),
|
|
150
|
-
filename: path.basename(argument.output + '.zip'),
|
|
151
|
-
description: json.lia.comment,
|
|
152
|
-
//keywords: ['scorm', 'test', 'course'],
|
|
153
|
-
typicalDuration: argument['scorm-typicalDuration'] || 'PT0H5M0S',
|
|
154
|
-
//rights: `©${new Date().getFullYear()} My Amazing Company. All right reserved.`,
|
|
155
|
-
vcard: {
|
|
156
|
-
author: json.lia.definition.author,
|
|
157
|
-
org: argument['scorm-organization'] || 'LiaScript',
|
|
158
|
-
//tel: '(000) 000-0000',
|
|
159
|
-
//address: 'my address',
|
|
160
|
-
mail: json.lia.definition.email,
|
|
161
|
-
//url: 'https://mydomain.com'
|
|
162
|
-
},
|
|
163
|
-
},
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
await scormPackager(config)
|
|
167
|
-
}
|