@examplary/cli 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +3 -2
- package/src/commands/preview.js +12 -8
- package/src/commands/upload.js +4 -16
- package/src/lib/read-meta.js +40 -0
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ npx @examplary/cli preview
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
To upload your custom question type, run the following command in a directory
|
|
18
|
-
that contains a `question-type.json` file:
|
|
18
|
+
that contains a `question-type.json` or `question-type.yml` file:
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
npx @examplary/cli upload
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@examplary/cli",
|
|
3
|
-
"description": "
|
|
3
|
+
"description": "Command-line tools for building and publishing Examplary question types.",
|
|
4
4
|
"packageManager": "yarn@4.8.1",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.6.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"exp": "./bin/index.js"
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"use-local-storage": "^3.0.0",
|
|
45
45
|
"vite": "^8.0.0",
|
|
46
46
|
"ws": "^8.18.3",
|
|
47
|
+
"yaml": "^2.8.2",
|
|
47
48
|
"yargs": "^18.0.0"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
package/src/commands/preview.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
2
|
import { watch } from "fs";
|
|
3
|
-
import {
|
|
3
|
+
import { readFile } from "fs/promises";
|
|
4
4
|
import { createServer as createHttpServer } from "http";
|
|
5
|
-
import path, {
|
|
5
|
+
import path, { resolve } from "path";
|
|
6
6
|
import { cwd, exit } from "process";
|
|
7
7
|
|
|
8
8
|
import { serve } from "@hono/node-server";
|
|
@@ -16,19 +16,20 @@ import { WebSocketServer } from "ws";
|
|
|
16
16
|
import { setApiHost, setApiKey } from "../lib/api.js";
|
|
17
17
|
import { buildComponent } from "../lib/bundle.js";
|
|
18
18
|
import { adaptExpressMiddleware } from "../lib/middleware.js";
|
|
19
|
+
import { readMetaFile } from "../lib/read-meta.js";
|
|
19
20
|
import { buildStyles } from "../lib/styles.js";
|
|
20
21
|
|
|
21
22
|
export const previewCommand = async (argv) => {
|
|
22
23
|
setApiHost(argv.host);
|
|
23
24
|
setApiKey(argv.key);
|
|
24
25
|
|
|
25
|
-
// Let's check a question-type.json file exists
|
|
26
26
|
const dir = cwd();
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
// Verify a question-type file exists (json or yml)
|
|
28
29
|
try {
|
|
29
|
-
await
|
|
30
|
+
await readMetaFile();
|
|
30
31
|
} catch (e) {
|
|
31
|
-
console.error(`🚫
|
|
32
|
+
console.error(`🚫 ${e.message}`);
|
|
32
33
|
exit(1);
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -121,7 +122,7 @@ export const previewCommand = async (argv) => {
|
|
|
121
122
|
app.get("/", async (c) => {
|
|
122
123
|
let questionType;
|
|
123
124
|
try {
|
|
124
|
-
questionType =
|
|
125
|
+
({ definition: questionType } = await readMetaFile());
|
|
125
126
|
questionType.components = questionType.components
|
|
126
127
|
? Object.entries(questionType.components).reduce(
|
|
127
128
|
(acc, [componentName, componentPath]) => ({
|
|
@@ -132,7 +133,10 @@ export const previewCommand = async (argv) => {
|
|
|
132
133
|
)
|
|
133
134
|
: questionType.components;
|
|
134
135
|
} catch (e) {
|
|
135
|
-
return c.text(
|
|
136
|
+
return c.text(
|
|
137
|
+
`Error reading question type definition: ${e.message}`,
|
|
138
|
+
500,
|
|
139
|
+
);
|
|
136
140
|
}
|
|
137
141
|
|
|
138
142
|
let template = await readFile(resolve(root, "index.html"), "utf-8");
|
package/src/commands/upload.js
CHANGED
|
@@ -1,36 +1,24 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
2
|
import { readFile } from "fs/promises";
|
|
3
|
-
import {
|
|
3
|
+
import { resolve } from "path";
|
|
4
4
|
import { cwd, exit } from "process";
|
|
5
5
|
|
|
6
6
|
import { setApiHost, setApiKey, uploadFile } from "../lib/api.js";
|
|
7
7
|
import { buildComponent } from "../lib/bundle.js";
|
|
8
8
|
import { deepCompileJsonata } from "../lib/jsonata.js";
|
|
9
|
+
import { readMetaFile } from "../lib/read-meta.js";
|
|
9
10
|
import { buildStyles } from "../lib/styles.js";
|
|
10
11
|
|
|
11
12
|
export const uploadCommand = async (argv) => {
|
|
12
13
|
setApiHost(argv.host);
|
|
13
14
|
setApiKey(argv.key);
|
|
14
15
|
|
|
15
|
-
// Try to read the question-type.json files in the current directory
|
|
16
16
|
const dir = cwd();
|
|
17
|
-
const metaFilePath = join(dir, "question-type.json");
|
|
18
|
-
let metaFileContents;
|
|
19
17
|
let definition = {};
|
|
20
18
|
try {
|
|
21
|
-
|
|
19
|
+
({ definition } = await readMetaFile());
|
|
22
20
|
} catch (e) {
|
|
23
|
-
console.error(`🚫
|
|
24
|
-
exit(1);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Read JSON
|
|
28
|
-
try {
|
|
29
|
-
const json = JSON.parse(metaFileContents);
|
|
30
|
-
definition = json;
|
|
31
|
-
if (definition.$schema) delete definition.$schema;
|
|
32
|
-
} catch (error) {
|
|
33
|
-
console.error(`🚫 Invalid JSON file: ${error.message}`);
|
|
21
|
+
console.error(`🚫 ${e.message}`);
|
|
34
22
|
exit(1);
|
|
35
23
|
}
|
|
36
24
|
console.log(`Uploading question type: ${definition.id}...`);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { cwd } from "process";
|
|
4
|
+
|
|
5
|
+
import yaml from "yaml";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Reads and parses the question-type definition file.
|
|
9
|
+
* Prefers question-type.json over question-type.yml.
|
|
10
|
+
*/
|
|
11
|
+
export const readMetaFile = async () => {
|
|
12
|
+
const dir = cwd();
|
|
13
|
+
const jsonPath = join(dir, "question-type.json");
|
|
14
|
+
const ymlPath = join(dir, "question-type.yml");
|
|
15
|
+
|
|
16
|
+
// Try JSON first, then YAML
|
|
17
|
+
for (const { path, parse, format } of [
|
|
18
|
+
{ path: jsonPath, parse: JSON.parse, format: "JSON" },
|
|
19
|
+
{ path: ymlPath, parse: yaml.parse, format: "YAML" },
|
|
20
|
+
]) {
|
|
21
|
+
let contents;
|
|
22
|
+
try {
|
|
23
|
+
contents = await readFile(path, "utf-8");
|
|
24
|
+
} catch {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const definition = parse(contents);
|
|
30
|
+
if (definition.$schema) delete definition.$schema;
|
|
31
|
+
return { definition, path };
|
|
32
|
+
} catch (error) {
|
|
33
|
+
throw new Error(`Invalid ${format} in ${path}: ${error.message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
throw new Error(
|
|
38
|
+
"No question-type.json or question-type.yml found in the current directory",
|
|
39
|
+
);
|
|
40
|
+
};
|