@eriknaslund/skiss 0.3.0 → 0.4.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/LICENSE +1 -1
- package/README.md +5 -2
- package/dist/cli.js +105 -35
- package/dist/index.d.ts +5 -5
- package/dist/index.js +91 -34
- package/package.json +4 -2
- package/dist/cli.js.map +0 -1
- package/dist/index.js.map +0 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -64,7 +64,7 @@ Or without installing: `npx @eriknaslund/skiss diagram model.skiss`.
|
|
|
64
64
|
|
|
65
65
|
`skiss compile model.skiss` writes a LinkML schema instead, as YAML on standard output. `--json` writes the same schema as JSON, `--name` sets the schema name (it defaults to the file name without its extension, and to `sketch` when reading standard input), and `-o`, `-`, `--strict` and the diagnostics work exactly as they do for `diagram`.
|
|
66
66
|
|
|
67
|
-
`skiss import schema.linkml.yaml` goes the other way: it reads a LinkML schema, YAML or JSON, and writes Skiss. What the projection could not carry — `is_a`, patterns, `required`, the names it had to convert — is one line on standard error before the diagnostics, because a sketch is a smaller language than LinkML and that line says by how much. `-o`, `-` and the diagnostics work as they do for `compile`; `--strict` exits 1 when the projected sketch has a diagnostic, and also when anything was dropped.
|
|
67
|
+
`skiss import schema.linkml.yaml` goes the other way: it reads a LinkML schema, YAML or JSON, and writes Skiss. What the projection could not carry — `is_a`, patterns, `required`, the names it had to convert — is one line on standard error before the diagnostics, because a sketch is a smaller language than LinkML and that line says by how much. `-o`, `-` and the diagnostics work as they do for `compile`; `--strict` exits 1 when the projected sketch has a diagnostic, and also when anything was dropped. An input that is not a schema at all — not YAML, or YAML no schema can be read out of — produces no sketch, says why on standard error, and exits 2, the code every command uses for unreadable input.
|
|
68
68
|
|
|
69
69
|
`npm install @eriknaslund/skiss` also gives you the library: `compile`, `formatDiagnostic`, `parse`, `resolve`, `toLinkML`, `serialize`, `toMermaid`, `toSkiss`, `fromLinkML`, `formatDropped`, `importLinkML` and `VERSION`, browser-safe.
|
|
70
70
|
|
|
@@ -79,11 +79,14 @@ Editors that render Skiss live are separate projects built on this package. This
|
|
|
79
79
|
| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | How the code is organised. |
|
|
80
80
|
| [docs/adr/](docs/adr/) | Architecture decision records. |
|
|
81
81
|
| [docs/ROADMAP.md](docs/ROADMAP.md) | What comes next. |
|
|
82
|
+
| [docs/RELEASING.md](docs/RELEASING.md) | How a version reaches npm. |
|
|
82
83
|
| [AGENTS.md](AGENTS.md) | How this repository is built. |
|
|
83
84
|
|
|
84
85
|
## Status
|
|
85
86
|
|
|
86
|
-
|
|
87
|
+
0.4.0, published on npm as [`@eriknaslund/skiss`](https://www.npmjs.com/package/@eriknaslund/skiss). The language parses, resolves and diagnoses; `skiss diagram` draws it, `skiss compile` writes LinkML that the real LinkML toolchain validates in CI, and `skiss import` reads a LinkML schema back into a sketch. The library exports the same in both directions and is browser-safe.
|
|
88
|
+
|
|
89
|
+
The [specification](docs/SPEC.md) is at 0.2 and moves when the language does, which is not with every release. Not built: inheritance, and editing a projected schema so that what Skiss cannot express survives the edit (SPEC §8.1). [CHANGELOG.md](CHANGELOG.md) is what changed when.
|
|
87
90
|
|
|
88
91
|
## License
|
|
89
92
|
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,8 @@ var isWs = (c) => c === " " || c === " ";
|
|
|
10
10
|
var trimWs = (s) => s.replace(/^[ \t]+/, "").replace(/[ \t]+$/, "");
|
|
11
11
|
var CLASS_NAME = /^[A-Z][A-Za-z0-9]*$/;
|
|
12
12
|
var FIELD_NAME = /^[a-z][A-Za-z0-9]*$/;
|
|
13
|
-
var
|
|
13
|
+
var SYSTEM_NAME = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
14
|
+
var ENUM_VALUE = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
14
15
|
var PRIMITIVES = {
|
|
15
16
|
string: "string",
|
|
16
17
|
int: "int",
|
|
@@ -315,7 +316,7 @@ function parseSystem(c, at) {
|
|
|
315
316
|
system ?? afterSpan(at)
|
|
316
317
|
);
|
|
317
318
|
}
|
|
318
|
-
if (!
|
|
319
|
+
if (!SYSTEM_NAME.test(system.text)) {
|
|
319
320
|
return fail(
|
|
320
321
|
"E_UNPARSABLE",
|
|
321
322
|
`A system name is letters, digits, \`-\` and \`_\`, starting with a letter: \`${system.text}\``,
|
|
@@ -373,10 +374,10 @@ function parseType(c, colon) {
|
|
|
373
374
|
}
|
|
374
375
|
if (words.length > 1) {
|
|
375
376
|
for (const w of words) {
|
|
376
|
-
if (!
|
|
377
|
+
if (!ENUM_VALUE.test(w.text)) {
|
|
377
378
|
return fail(
|
|
378
379
|
"E_UNPARSABLE",
|
|
379
|
-
`An enum value is letters, digits, \`-\` and \`_\`,
|
|
380
|
+
`An enum value is letters, digits, \`-\` and \`_\`, and does not start with \`-\` or \`_\`: \`${w.text}\``,
|
|
380
381
|
c.line,
|
|
381
382
|
w
|
|
382
383
|
);
|
|
@@ -513,6 +514,18 @@ function resolve(doc) {
|
|
|
513
514
|
);
|
|
514
515
|
} else if (field.type?.kind === "class") {
|
|
515
516
|
reference(field.type.name);
|
|
517
|
+
} else if (field.type?.kind === "enum") {
|
|
518
|
+
const seen = /* @__PURE__ */ new Set();
|
|
519
|
+
for (const value of field.type.values) {
|
|
520
|
+
if (seen.has(value.text)) {
|
|
521
|
+
warn(
|
|
522
|
+
"W_DUPLICATE_ENUM_VALUE",
|
|
523
|
+
`value \`${value.text}\` is already in this enum; it is carried once`,
|
|
524
|
+
value
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
seen.add(value.text);
|
|
528
|
+
}
|
|
516
529
|
}
|
|
517
530
|
if (field.joinsTo !== void 0) {
|
|
518
531
|
const { className, fieldName } = field.joinsTo;
|
|
@@ -626,7 +639,8 @@ function toLinkML(doc, opts) {
|
|
|
626
639
|
const classNames = new Set(classes.map((cls) => cls.node.name.text));
|
|
627
640
|
for (const name2 of resolved.undeclared) classNames.add(name2.text);
|
|
628
641
|
const enums = nameEnums(classes, classNames);
|
|
629
|
-
const prefixes =
|
|
642
|
+
const prefixes = nameKeyed();
|
|
643
|
+
prefixes.linkml = "https://w3id.org/linkml/";
|
|
630
644
|
if (!Object.hasOwn(prefixes, schemaName)) prefixes[schemaName] = `${BASE_URI}/${schemaName}/`;
|
|
631
645
|
const systemPrefix = /* @__PURE__ */ new Map();
|
|
632
646
|
const addSystem = (system) => {
|
|
@@ -657,12 +671,12 @@ function toLinkML(doc, opts) {
|
|
|
657
671
|
prefixes,
|
|
658
672
|
imports: ["linkml:types"]
|
|
659
673
|
};
|
|
660
|
-
const out =
|
|
674
|
+
const out = nameKeyed();
|
|
661
675
|
for (const cls of classes) {
|
|
662
676
|
out[cls.node.name.text] = buildClass(cls, mappingPrefix, enums);
|
|
663
677
|
}
|
|
664
678
|
for (const name2 of resolved.undeclared) {
|
|
665
|
-
if (!(name2.text
|
|
679
|
+
if (!Object.hasOwn(out, name2.text)) out[name2.text] = { annotations: { undeclared: true } };
|
|
666
680
|
}
|
|
667
681
|
if (Object.keys(out).length > 0) schema.classes = out;
|
|
668
682
|
const enumBlock = buildEnums(classes, enums);
|
|
@@ -720,13 +734,13 @@ function nameEnums(classes, classNames) {
|
|
|
720
734
|
return names;
|
|
721
735
|
}
|
|
722
736
|
function buildEnums(classes, enums) {
|
|
723
|
-
const out =
|
|
737
|
+
const out = nameKeyed();
|
|
724
738
|
for (const { node, fields } of classes) {
|
|
725
739
|
for (const field of fields) {
|
|
726
740
|
if (field.type?.kind !== "enum") continue;
|
|
727
741
|
const name2 = enums.get(useKey(node.name.text, field.name.text));
|
|
728
|
-
if (name2 === void 0 || name2
|
|
729
|
-
const values =
|
|
742
|
+
if (name2 === void 0 || Object.hasOwn(out, name2)) continue;
|
|
743
|
+
const values = nameKeyed();
|
|
730
744
|
for (const value of field.type.values) values[value.text] = null;
|
|
731
745
|
out[name2] = { permissible_values: values };
|
|
732
746
|
}
|
|
@@ -745,7 +759,7 @@ function buildClass(cls, mappingPrefix, enums) {
|
|
|
745
759
|
const target = node.similarTo.text;
|
|
746
760
|
out.close_mappings = [`${mappingPrefix(target)}:${target}`];
|
|
747
761
|
}
|
|
748
|
-
const attributes =
|
|
762
|
+
const attributes = nameKeyed();
|
|
749
763
|
for (const field of fields) {
|
|
750
764
|
attributes[field.name.text] = buildAttribute(field, node.name.text, enums);
|
|
751
765
|
}
|
|
@@ -783,10 +797,12 @@ function rangeOf(field, className, enums) {
|
|
|
783
797
|
}
|
|
784
798
|
}
|
|
785
799
|
var RESERVED_PREFIXES = /* @__PURE__ */ new Set(["linkml", "xsd", "shex", "schema"]);
|
|
800
|
+
var PROTOTYPE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
801
|
+
var nameKeyed = () => /* @__PURE__ */ Object.create(null);
|
|
786
802
|
var linkmlName = (text) => {
|
|
787
803
|
const name2 = text.toLowerCase().replace(/[^a-z0-9]/g, "_");
|
|
788
804
|
if (!/[a-z0-9]/.test(name2)) return "sketch";
|
|
789
|
-
if (RESERVED_PREFIXES.has(name2)) return `${name2}_`;
|
|
805
|
+
if (RESERVED_PREFIXES.has(name2) || PROTOTYPE_KEYS.has(name2)) return `${name2}_`;
|
|
790
806
|
return /^[0-9]/.test(name2) ? `_${name2}` : name2;
|
|
791
807
|
};
|
|
792
808
|
var capitalise = (text) => text.charAt(0).toUpperCase() + text.slice(1);
|
|
@@ -949,10 +965,15 @@ function typeText2(type) {
|
|
|
949
965
|
}
|
|
950
966
|
return type.many ? `${text}[]` : text;
|
|
951
967
|
}
|
|
968
|
+
function writeDescription(text) {
|
|
969
|
+
const attached = text.replace(/[ \t]+\?(?=[ \t]|$)/g, "?");
|
|
970
|
+
return /^\?([ \t]|$)/.test(attached) ? `#${attached}` : `# ${attached}`;
|
|
971
|
+
}
|
|
972
|
+
var rewordsDescription = (text) => writeDescription(text) !== `# ${text}`;
|
|
952
973
|
function withTrailer(head, node) {
|
|
953
974
|
const parts2 = [];
|
|
954
975
|
if (node.description !== void 0 && node.description !== "")
|
|
955
|
-
parts2.push(
|
|
976
|
+
parts2.push(writeDescription(node.description));
|
|
956
977
|
if (node.note !== void 0 && node.note !== "") parts2.push(`? ${node.note}`);
|
|
957
978
|
if (parts2.length === 0) return head;
|
|
958
979
|
const gap = head.length < TRAILER_COLUMN - 1 ? " ".repeat(TRAILER_COLUMN - head.length) : " ";
|
|
@@ -990,6 +1011,7 @@ var QUIET_SCHEMA_KEYS = /* @__PURE__ */ new Set([
|
|
|
990
1011
|
"slots",
|
|
991
1012
|
"enums"
|
|
992
1013
|
]);
|
|
1014
|
+
var REWORDED = "a standalone `?` is attached to the word before it";
|
|
993
1015
|
var UNKNOWN_TYPE = /^[a-z][a-z0-9_-]*$/;
|
|
994
1016
|
function fromLinkML(schema) {
|
|
995
1017
|
const root = asRecord(schema);
|
|
@@ -1033,7 +1055,7 @@ function fromLinkML(schema) {
|
|
|
1033
1055
|
const rawValues = get(asRecord(get(enumDefs, key2)) ?? {}, "permissible_values");
|
|
1034
1056
|
const values = asRecord(rawValues);
|
|
1035
1057
|
const keys = Object.keys(values ?? {});
|
|
1036
|
-
const bad = keys.find((value) => !
|
|
1058
|
+
const bad = keys.find((value) => !ENUM_VALUE.test(value));
|
|
1037
1059
|
if (values === void 0 && present(rawValues)) {
|
|
1038
1060
|
unwritable.set(key2, `${shown(rawValues)} is not a map of permissible values`);
|
|
1039
1061
|
} else if (keys.length < 2) {
|
|
@@ -1064,14 +1086,26 @@ function fromLinkML(schema) {
|
|
|
1064
1086
|
}
|
|
1065
1087
|
continue;
|
|
1066
1088
|
}
|
|
1089
|
+
const undeclared = annotations.get("undeclared");
|
|
1090
|
+
if (undeclared !== void 0 && typeof undeclared !== "boolean") {
|
|
1091
|
+
report("annotation", name2, notText("undeclared", undeclared));
|
|
1092
|
+
}
|
|
1067
1093
|
const node = { name: at0(name2), fields: [], line: 0 };
|
|
1068
1094
|
const description = oneLine(stringOf(get(def, "description")));
|
|
1069
|
-
if (description !== void 0)
|
|
1095
|
+
if (description !== void 0) {
|
|
1096
|
+
node.description = description;
|
|
1097
|
+
if (rewordsDescription(description)) report("reworded", name2, REWORDED);
|
|
1098
|
+
}
|
|
1070
1099
|
const system = annotations.get("system");
|
|
1071
|
-
if (typeof system === "string"
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
if (
|
|
1100
|
+
if (typeof system === "string") {
|
|
1101
|
+
if (SYSTEM_NAME.test(system)) node.system = at0(system);
|
|
1102
|
+
else report("annotation", name2, "`system` is not a system name");
|
|
1103
|
+
} else if (system !== void 0) report("annotation", name2, notText("system", system));
|
|
1104
|
+
const rawNote = annotations.get("note");
|
|
1105
|
+
if (typeof rawNote === "string") {
|
|
1106
|
+
const note = oneLine(rawNote);
|
|
1107
|
+
if (note !== void 0) node.note = note;
|
|
1108
|
+
} else if (rawNote !== void 0) report("annotation", name2, notText("note", rawNote));
|
|
1075
1109
|
const mappings = stringList(get(def, "close_mappings"));
|
|
1076
1110
|
const first = mappings[0];
|
|
1077
1111
|
const target = first === void 0 ? "" : localName(first);
|
|
@@ -1176,13 +1210,18 @@ function fromLinkML(schema) {
|
|
|
1176
1210
|
line: 0
|
|
1177
1211
|
};
|
|
1178
1212
|
const description = oneLine(stringOf(get(def, "description")));
|
|
1179
|
-
if (description !== void 0)
|
|
1213
|
+
if (description !== void 0) {
|
|
1214
|
+
field.description = description;
|
|
1215
|
+
if (rewordsDescription(description)) report("reworded", element, REWORDED);
|
|
1216
|
+
}
|
|
1180
1217
|
const type = typeFor(element, name2, def);
|
|
1181
1218
|
if (type !== void 0) field.type = type;
|
|
1182
1219
|
const annotations = annotationsOf(def);
|
|
1183
1220
|
const system = annotations.get("system");
|
|
1184
|
-
if (typeof system === "string"
|
|
1185
|
-
|
|
1221
|
+
if (typeof system === "string") {
|
|
1222
|
+
if (SYSTEM_NAME.test(system)) field.system = at0(system);
|
|
1223
|
+
else report("annotation", element, "`system` is not a system name");
|
|
1224
|
+
} else if (system !== void 0) report("annotation", element, notText("system", system));
|
|
1186
1225
|
const joins = annotations.get("joins_to");
|
|
1187
1226
|
if (typeof joins === "string") {
|
|
1188
1227
|
const dot = joins.indexOf(".");
|
|
@@ -1195,9 +1234,12 @@ function fromLinkML(schema) {
|
|
|
1195
1234
|
reference(element, target, targetClass);
|
|
1196
1235
|
reference(element, targetField, targetName);
|
|
1197
1236
|
} else report("annotation", element, "`joins_to` is not `Class.field`");
|
|
1198
|
-
} else if (joins !== void 0) report("annotation", element, "
|
|
1199
|
-
const
|
|
1200
|
-
if (
|
|
1237
|
+
} else if (joins !== void 0) report("annotation", element, notText("joins_to", joins));
|
|
1238
|
+
const rawNote = annotations.get("note");
|
|
1239
|
+
if (typeof rawNote === "string") {
|
|
1240
|
+
const note = oneLine(rawNote);
|
|
1241
|
+
if (note !== void 0) field.note = note;
|
|
1242
|
+
} else if (rawNote !== void 0) report("annotation", element, notText("note", rawNote));
|
|
1201
1243
|
for (const other of Object.keys(def)) {
|
|
1202
1244
|
if (!SLOT_KEYS.has(other)) {
|
|
1203
1245
|
report(other, element);
|
|
@@ -1272,13 +1314,17 @@ var COUNTED = {
|
|
|
1272
1314
|
class: ["class", "classes"],
|
|
1273
1315
|
slot: ["slot", "slots"],
|
|
1274
1316
|
schema: ["schema key", "schema keys"],
|
|
1317
|
+
narrowed: ["narrowed range", "narrowed ranges"],
|
|
1318
|
+
enum_detail: ["enum detail", "enum details"],
|
|
1319
|
+
annotation: ["unreadable annotation", "unreadable annotations"],
|
|
1320
|
+
reworded: ["description reworded", "descriptions reworded"],
|
|
1275
1321
|
// `mixin: true` marks the class; `mixins: [M]` points at another one.
|
|
1276
1322
|
mixin: ["mixin class", "mixin classes"],
|
|
1277
1323
|
mixins: ["mixin", "mixins"],
|
|
1278
1324
|
pattern: ["pattern", "patterns"],
|
|
1279
1325
|
unit: ["unit", "units"]
|
|
1280
1326
|
};
|
|
1281
|
-
var
|
|
1327
|
+
var UNREADABLE = "unreadable";
|
|
1282
1328
|
function formatDropped(dropped) {
|
|
1283
1329
|
const groups = /* @__PURE__ */ new Map();
|
|
1284
1330
|
for (const entry of dropped) {
|
|
@@ -1287,15 +1333,21 @@ function formatDropped(dropped) {
|
|
|
1287
1333
|
else group.push(entry);
|
|
1288
1334
|
}
|
|
1289
1335
|
const drops = [];
|
|
1336
|
+
const notRead = [];
|
|
1290
1337
|
let renamed = "";
|
|
1291
1338
|
let inlined = "";
|
|
1292
1339
|
for (const [kind, entries] of groups) {
|
|
1340
|
+
if (kind === UNREADABLE) {
|
|
1341
|
+
for (const entry of entries) notRead.push(entry.detail ?? "the schema could not be read");
|
|
1342
|
+
continue;
|
|
1343
|
+
}
|
|
1293
1344
|
const phrase = phraseFor(kind, entries);
|
|
1294
1345
|
if (kind === "renamed") renamed = phrase;
|
|
1295
1346
|
else if (kind === "inlined") inlined = phrase;
|
|
1296
1347
|
else drops.push(phrase);
|
|
1297
1348
|
}
|
|
1298
1349
|
const sentences = [];
|
|
1350
|
+
if (notRead.length > 0) sentences.push(`Not read: ${notRead.join("; ")}.`);
|
|
1299
1351
|
if (drops.length > 0) sentences.push(`Dropped: ${drops.join(", ")}.`);
|
|
1300
1352
|
if (renamed !== "") sentences.push(`Renamed: ${renamed}.`);
|
|
1301
1353
|
if (inlined !== "") sentences.push(`Inlined: ${inlined}.`);
|
|
@@ -1305,11 +1357,10 @@ function phraseFor(kind, entries) {
|
|
|
1305
1357
|
const n = entries.length;
|
|
1306
1358
|
const counted = Object.hasOwn(COUNTED, kind) ? COUNTED[kind] : void 0;
|
|
1307
1359
|
if (counted !== void 0) return `${n} ${n === 1 ? counted[0] : counted[1]}`;
|
|
1308
|
-
return `${kind} on ${n} ${nounFor(
|
|
1360
|
+
return `${kind} on ${n} ${nounFor(entries)}`;
|
|
1309
1361
|
}
|
|
1310
|
-
function nounFor(
|
|
1362
|
+
function nounFor(entries) {
|
|
1311
1363
|
const one = entries.length === 1;
|
|
1312
|
-
if (ENUM_KINDS.has(kind)) return one ? "enum" : "enums";
|
|
1313
1364
|
const slots = entries.filter((entry) => entry.element.includes(".")).length;
|
|
1314
1365
|
if (slots === entries.length) return one ? "slot" : "slots";
|
|
1315
1366
|
if (slots === 0) return one ? "class" : "classes";
|
|
@@ -1360,12 +1411,18 @@ function annotationsOf(def) {
|
|
|
1360
1411
|
if (annotations === void 0) return out;
|
|
1361
1412
|
for (const tag of Object.keys(annotations)) {
|
|
1362
1413
|
const raw = get(annotations, tag);
|
|
1363
|
-
const
|
|
1364
|
-
|
|
1365
|
-
|
|
1414
|
+
const long = asRecord(raw);
|
|
1415
|
+
const value = long === void 0 ? raw : get(long, "value");
|
|
1416
|
+
out.set(tag, value ?? null);
|
|
1366
1417
|
}
|
|
1367
1418
|
return out;
|
|
1368
1419
|
}
|
|
1420
|
+
function notText(tag, value) {
|
|
1421
|
+
if (value === null) return `\`${tag}\` was written with no value`;
|
|
1422
|
+
if (Array.isArray(value)) return `\`${tag}\` is a list, not text`;
|
|
1423
|
+
if (typeof value === "object") return `\`${tag}\` is a map, not text`;
|
|
1424
|
+
return `\`${tag}\` is a ${typeof value}, not text`;
|
|
1425
|
+
}
|
|
1369
1426
|
function slotKeysOf(def) {
|
|
1370
1427
|
const keys = Object.keys(asRecord(get(def, "attributes")) ?? {});
|
|
1371
1428
|
for (const slot of stringList(get(def, "slots"))) {
|
|
@@ -1390,7 +1447,7 @@ var schemaElement = (root) => stringOf(get(root, "name")) ?? "schema";
|
|
|
1390
1447
|
var nothing = (element, detail) => ({
|
|
1391
1448
|
document: parse(""),
|
|
1392
1449
|
source: "",
|
|
1393
|
-
dropped: [{ kind:
|
|
1450
|
+
dropped: [{ kind: UNREADABLE, element, detail }]
|
|
1394
1451
|
});
|
|
1395
1452
|
|
|
1396
1453
|
// src/import.ts
|
|
@@ -1417,7 +1474,7 @@ function notYAML(error) {
|
|
|
1417
1474
|
}
|
|
1418
1475
|
|
|
1419
1476
|
// src/index.ts
|
|
1420
|
-
var VERSION = "0.
|
|
1477
|
+
var VERSION = "0.4.0";
|
|
1421
1478
|
|
|
1422
1479
|
// src/cli.ts
|
|
1423
1480
|
var HELP = `Usage: skiss <command> [options]
|
|
@@ -1450,7 +1507,8 @@ wrote has, so their line numbers are lines of that sketch.
|
|
|
1450
1507
|
Exit codes:
|
|
1451
1508
|
0 the output was produced
|
|
1452
1509
|
1 --strict and the input has diagnostics (the output is still written)
|
|
1453
|
-
2 usage error, missing file or unreadable input
|
|
1510
|
+
2 usage error, missing file, or unreadable input: for import, a text no
|
|
1511
|
+
schema could be read out of, which produces no sketch at all
|
|
1454
1512
|
70 internal error, a bug in skiss
|
|
1455
1513
|
`;
|
|
1456
1514
|
var EXIT_OK = 0;
|
|
@@ -1520,9 +1578,15 @@ async function main(argv) {
|
|
|
1520
1578
|
`);
|
|
1521
1579
|
if (values.output === void 0) process.stdout.write(output);
|
|
1522
1580
|
else await writeOutput(values.output, output);
|
|
1581
|
+
if (unreadableInput(command, diagnostics, dropped)) return EXIT_USAGE;
|
|
1523
1582
|
const strict = values.strict === true && (diagnostics.length > 0 || dropped.length > 0);
|
|
1524
1583
|
return strict ? EXIT_STRICT : EXIT_OK;
|
|
1525
1584
|
}
|
|
1585
|
+
function unreadableInput(command, diagnostics, dropped) {
|
|
1586
|
+
if (command !== "import") return false;
|
|
1587
|
+
if (dropped.some((entry) => entry.kind === "unreadable")) return true;
|
|
1588
|
+
return diagnostics.some((d) => d.code === "E_NOT_YAML");
|
|
1589
|
+
}
|
|
1526
1590
|
function runCommand(command, source, file, values) {
|
|
1527
1591
|
if (command === "import") return importLinkML(source);
|
|
1528
1592
|
if (command === "compile") {
|
|
@@ -1568,6 +1632,12 @@ function reason(error) {
|
|
|
1568
1632
|
}
|
|
1569
1633
|
return String(error);
|
|
1570
1634
|
}
|
|
1635
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
1636
|
+
stream.on("error", (error) => {
|
|
1637
|
+
if (error.code === "EPIPE") process.exit(EXIT_OK);
|
|
1638
|
+
throw error;
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1571
1641
|
main(process.argv.slice(2)).then(
|
|
1572
1642
|
(code) => {
|
|
1573
1643
|
process.exitCode = code;
|
package/dist/index.d.ts
CHANGED
|
@@ -68,7 +68,7 @@ type Severity = 'error' | 'warning';
|
|
|
68
68
|
* `resolve` the `W_` codes, and `importLinkML` the one code of its own,
|
|
69
69
|
* `E_NOT_YAML`, for a text no schema can be read out of.
|
|
70
70
|
*/
|
|
71
|
-
type DiagnosticCode = 'E_UNPARSABLE' | 'E_FIELD_WITHOUT_CLASS' | 'E_MISSING_TYPE' | 'E_UNCLOSED_MANY' | 'E_BAD_NAME' | 'W_UNKNOWN_TYPE' | 'W_UNDECLARED_CLASS' | 'W_UNDECLARED_FIELD' | 'W_DUPLICATE_CLASS' | 'W_DUPLICATE_FIELD' | 'W_MULTIPLE_IDENTIFIERS' | 'E_NOT_YAML';
|
|
71
|
+
type DiagnosticCode = 'E_UNPARSABLE' | 'E_FIELD_WITHOUT_CLASS' | 'E_MISSING_TYPE' | 'E_UNCLOSED_MANY' | 'E_BAD_NAME' | 'W_UNKNOWN_TYPE' | 'W_UNDECLARED_CLASS' | 'W_UNDECLARED_FIELD' | 'W_DUPLICATE_CLASS' | 'W_DUPLICATE_FIELD' | 'W_DUPLICATE_ENUM_VALUE' | 'W_MULTIPLE_IDENTIFIERS' | 'E_NOT_YAML';
|
|
72
72
|
interface Diagnostic {
|
|
73
73
|
severity: Severity;
|
|
74
74
|
code: DiagnosticCode;
|
|
@@ -175,9 +175,9 @@ declare function formatDiagnostic(d: Diagnostic, file?: string): string;
|
|
|
175
175
|
/**
|
|
176
176
|
* One thing the projection could not carry. `kind` is the LinkML key, or one
|
|
177
177
|
* of the projection's own kinds: `renamed`, `narrowed`, `inlined`,
|
|
178
|
-
* `enum_detail`, `annotation`, `schema`. `element`
|
|
179
|
-
* the sketch names it — `Class`, `Class.field` — or,
|
|
180
|
-
* name, which the sketch does not keep.
|
|
178
|
+
* `enum_detail`, `annotation`, `reworded`, `schema`, `unreadable`. `element`
|
|
179
|
+
* names what carried it as the sketch names it — `Class`, `Class.field` — or,
|
|
180
|
+
* for an enum, its LinkML name, which the sketch does not keep.
|
|
181
181
|
*/
|
|
182
182
|
interface Dropped {
|
|
183
183
|
kind: string;
|
|
@@ -215,6 +215,6 @@ interface ResolvedDocument extends Document {
|
|
|
215
215
|
}
|
|
216
216
|
declare function resolve(doc: Document): ResolvedDocument;
|
|
217
217
|
|
|
218
|
-
declare const VERSION = "0.
|
|
218
|
+
declare const VERSION = "0.4.0";
|
|
219
219
|
|
|
220
220
|
export { type ClassNode, type CompileOptions, type CompileResult, type Diagnostic, type DiagnosticCode, type Document, type Dropped, type FieldNode, type FromLinkMLResult, type ImportResult, type JoinRef, type LinkMLAnnotations, type LinkMLAttribute, type LinkMLClass, type LinkMLCompileOptions, type LinkMLEnum, type LinkMLOptions, type LinkMLSchema, type MermaidCompileOptions, type MermaidOptions, type Name, type Primitive, type ResolvedDocument, type SerializeFormat, type Severity, type TypeRef, VERSION, compile, formatDiagnostic, formatDropped, fromLinkML, importLinkML, parse, resolve, serialize, toLinkML, toMermaid, toSkiss };
|