proschi 0.7.0 → 0.8.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/dist/cli.cjs +72 -9
- package/dist/render.cjs +5 -5
- package/dist/server.cjs +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# proschi
|
|
2
2
|
|
|
3
3
|
Command-line checker and language server for
|
|
4
|
-
[Proschi](https://
|
|
4
|
+
[Proschi](https://proschi.app/), a small text language for
|
|
5
5
|
microservice architectures and the request flows that run through them.
|
|
6
6
|
|
|
7
7
|
```sh
|
package/dist/cli.cjs
CHANGED
|
@@ -2237,8 +2237,8 @@ var require_resolve = __commonJS({
|
|
|
2237
2237
|
}
|
|
2238
2238
|
return count;
|
|
2239
2239
|
}
|
|
2240
|
-
function getFullPath(resolver, id = "",
|
|
2241
|
-
if (
|
|
2240
|
+
function getFullPath(resolver, id = "", normalize2) {
|
|
2241
|
+
if (normalize2 !== false)
|
|
2242
2242
|
id = normalizeId(id);
|
|
2243
2243
|
const p = resolver.parse(id);
|
|
2244
2244
|
return _getFullPath(resolver, p);
|
|
@@ -3833,7 +3833,7 @@ var require_fast_uri = __commonJS({
|
|
|
3833
3833
|
}
|
|
3834
3834
|
return decodedScheme;
|
|
3835
3835
|
}
|
|
3836
|
-
function
|
|
3836
|
+
function normalize2(uri, options) {
|
|
3837
3837
|
if (typeof uri === "string") {
|
|
3838
3838
|
uri = /** @type {T} */
|
|
3839
3839
|
normalizeString(uri, options);
|
|
@@ -4211,7 +4211,7 @@ var require_fast_uri = __commonJS({
|
|
|
4211
4211
|
}
|
|
4212
4212
|
var fastUri = {
|
|
4213
4213
|
SCHEMES,
|
|
4214
|
-
normalize,
|
|
4214
|
+
normalize: normalize2,
|
|
4215
4215
|
resolve: resolve4,
|
|
4216
4216
|
resolveComponent,
|
|
4217
4217
|
equal,
|
|
@@ -19285,9 +19285,12 @@ var PROBLEM_MD = "problem.md";
|
|
|
19285
19285
|
var GIVEN = "given.proschi";
|
|
19286
19286
|
var STARTER = "starter.proschi";
|
|
19287
19287
|
var SOLUTION = "solution.proschi";
|
|
19288
|
+
var LESSON_MD = "lesson.md";
|
|
19288
19289
|
var WRONG_DIR = "wrong";
|
|
19289
19290
|
var REQUIRED = [PROBLEM_MD, GIVEN, STARTER, SOLUTION];
|
|
19291
|
+
var OPTIONAL = [LESSON_MD];
|
|
19290
19292
|
var ID = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
19293
|
+
var RESERVED_IDS = ["problem", "roadmap", "approach"];
|
|
19291
19294
|
var EXPECT_FAIL = /^#\s*expect-fail:\s*(.*?)\s*$/;
|
|
19292
19295
|
var ProblemFolderError = class extends Error {
|
|
19293
19296
|
folder;
|
|
@@ -19313,7 +19316,7 @@ function expectFailLines(source2) {
|
|
|
19313
19316
|
}
|
|
19314
19317
|
return names;
|
|
19315
19318
|
}
|
|
19316
|
-
var FIELDS = ["title", "summary", "difficulty", "tags", "order", "hints"];
|
|
19319
|
+
var FIELDS = ["title", "summary", "difficulty", "tags", "company", "order", "hints", "version"];
|
|
19317
19320
|
function readProblemMd(id, text) {
|
|
19318
19321
|
const fail2 = (message, line) => {
|
|
19319
19322
|
throw new ProblemFolderError(id, message, PROBLEM_MD, line);
|
|
@@ -19339,17 +19342,22 @@ function readProblemMd(id, text) {
|
|
|
19339
19342
|
};
|
|
19340
19343
|
const difficulty = str("difficulty");
|
|
19341
19344
|
if (!DIFFICULTIES.includes(difficulty)) fail2(`'difficulty' must be one of ${DIFFICULTIES.join(", ")}`);
|
|
19345
|
+
const company = data.company === void 0 ? void 0 : str("company");
|
|
19342
19346
|
const order = data.order;
|
|
19343
19347
|
if (order !== void 0 && typeof order !== "number") fail2("'order' must be a number");
|
|
19348
|
+
const version = data.version;
|
|
19349
|
+
if (version !== void 0 && !(typeof version === "number" && Number.isInteger(version) && version >= 1)) fail2("'version' must be a whole number from 1");
|
|
19344
19350
|
if (body === "") fail2("The statement (the Markdown after the front matter) is empty");
|
|
19345
19351
|
return {
|
|
19346
19352
|
title: str("title"),
|
|
19347
19353
|
summary: str("summary"),
|
|
19348
19354
|
difficulty,
|
|
19349
19355
|
tags: list("tags"),
|
|
19356
|
+
...company !== void 0 ? { company } : {},
|
|
19350
19357
|
...typeof order === "number" ? { order } : {},
|
|
19351
19358
|
statement: body,
|
|
19352
|
-
hints: list("hints")
|
|
19359
|
+
hints: list("hints"),
|
|
19360
|
+
...typeof version === "number" ? { version } : {}
|
|
19353
19361
|
};
|
|
19354
19362
|
}
|
|
19355
19363
|
function problemFromFiles(id, files) {
|
|
@@ -19357,11 +19365,12 @@ function problemFromFiles(id, files) {
|
|
|
19357
19365
|
throw new ProblemFolderError(id, message);
|
|
19358
19366
|
};
|
|
19359
19367
|
if (!ID.test(id)) fail2('The folder name is the problem id and must be lowercase words joined by "-", e.g. url-shortener');
|
|
19368
|
+
if (RESERVED_IDS.includes(id)) fail2(`"${id}" is taken by a practice page; reserved ids: ${RESERVED_IDS.join(", ")}`);
|
|
19360
19369
|
for (const name of REQUIRED) if (files[name] === void 0) fail2(`Missing ${name}`);
|
|
19361
19370
|
for (const name of Object.keys(files)) {
|
|
19362
19371
|
const wrong2 = name.startsWith(`${WRONG_DIR}/`) ? name.slice(WRONG_DIR.length + 1) : void 0;
|
|
19363
|
-
if (!REQUIRED.includes(name) && !(wrong2 && /^[a-z0-9][a-z0-9-]*\.proschi$/.test(wrong2))) {
|
|
19364
|
-
fail2(`Unexpected file ${name}: a problem folder holds ${REQUIRED.join(", ")} and ${WRONG_DIR}/<name>.proschi (lowercase, digits and "-")`);
|
|
19372
|
+
if (!REQUIRED.includes(name) && !OPTIONAL.includes(name) && !(wrong2 && /^[a-z0-9][a-z0-9-]*\.proschi$/.test(wrong2))) {
|
|
19373
|
+
fail2(`Unexpected file ${name}: a problem folder holds ${REQUIRED.join(", ")}, optionally ${OPTIONAL.join(", ")}, and ${WRONG_DIR}/<name>.proschi (lowercase, digits and "-")`);
|
|
19365
19374
|
}
|
|
19366
19375
|
}
|
|
19367
19376
|
const meta = readProblemMd(id, files[PROBLEM_MD]);
|
|
@@ -19372,10 +19381,63 @@ function problemFromFiles(id, files) {
|
|
|
19372
19381
|
given: files[GIVEN],
|
|
19373
19382
|
starter: files[STARTER],
|
|
19374
19383
|
solution: files[SOLUTION],
|
|
19384
|
+
...files[LESSON_MD] !== void 0 ? { lesson: files[LESSON_MD] } : {},
|
|
19375
19385
|
...wrong.length ? { wrong } : {}
|
|
19376
19386
|
};
|
|
19377
19387
|
}
|
|
19378
19388
|
|
|
19389
|
+
// ../frontend/src/practice/lesson.ts
|
|
19390
|
+
var LESSON_HEADINGS = [
|
|
19391
|
+
"What you'll learn",
|
|
19392
|
+
"The problem, explained",
|
|
19393
|
+
"Back-of-the-envelope",
|
|
19394
|
+
"Concepts",
|
|
19395
|
+
"Designing it step by step",
|
|
19396
|
+
"Common mistakes",
|
|
19397
|
+
"In the interview",
|
|
19398
|
+
"Further reading"
|
|
19399
|
+
];
|
|
19400
|
+
var normalize = (heading) => heading.replace(/[‘’]/g, "'").trim();
|
|
19401
|
+
function proseLines(source2) {
|
|
19402
|
+
const out = [];
|
|
19403
|
+
let fence = false;
|
|
19404
|
+
source2.replace(/\r\n?/g, "\n").split("\n").forEach((text, i) => {
|
|
19405
|
+
if (/^\s*```/.test(text)) fence = !fence;
|
|
19406
|
+
else if (!fence) out.push({ text, line: i + 1 });
|
|
19407
|
+
});
|
|
19408
|
+
return out;
|
|
19409
|
+
}
|
|
19410
|
+
function h2Headings(source2) {
|
|
19411
|
+
return proseLines(source2).map(({ text, line }) => ({ m: /^##\s+(.*?)\s*#*\s*$/.exec(text), line })).filter((h) => h.m !== null).map(({ m, line }) => ({ text: m[1], line }));
|
|
19412
|
+
}
|
|
19413
|
+
function lessonIssues(source2) {
|
|
19414
|
+
const issues = [];
|
|
19415
|
+
if (!source2.trim()) return [{ message: "The lesson is empty" }];
|
|
19416
|
+
const headings = h2Headings(source2);
|
|
19417
|
+
let last = -1;
|
|
19418
|
+
let lastName = "";
|
|
19419
|
+
for (const required of LESSON_HEADINGS) {
|
|
19420
|
+
const found = headings.find((h) => normalize(h.text) === required);
|
|
19421
|
+
if (!found) {
|
|
19422
|
+
issues.push({ message: `The lesson needs a "## ${required}" section (the sections are: ${LESSON_HEADINGS.join(", ")})` });
|
|
19423
|
+
continue;
|
|
19424
|
+
}
|
|
19425
|
+
const index = headings.indexOf(found);
|
|
19426
|
+
if (index < last) issues.push({ message: `"## ${required}" must come after "## ${lastName}"`, line: found.line });
|
|
19427
|
+
else {
|
|
19428
|
+
last = index;
|
|
19429
|
+
lastName = required;
|
|
19430
|
+
}
|
|
19431
|
+
}
|
|
19432
|
+
for (const { text, line } of proseLines(source2)) {
|
|
19433
|
+
const prose = text.replace(/`[^`]*`/g, "");
|
|
19434
|
+
for (const m of prose.matchAll(/\]\(([^)\s]*)[^)]*\)/g)) {
|
|
19435
|
+
if (!/^https?:\/\/\S+$/i.test(m[1])) issues.push({ message: `Links in a lesson must go to an http(s) address, not "${m[1]}"`, line });
|
|
19436
|
+
}
|
|
19437
|
+
}
|
|
19438
|
+
return issues;
|
|
19439
|
+
}
|
|
19440
|
+
|
|
19379
19441
|
// ../frontend/src/dsl/imports.ts
|
|
19380
19442
|
function joinImportPath(fromPath, importPath) {
|
|
19381
19443
|
const parts = importPath.startsWith("/") || !fromPath ? [] : fromPath.split("/").slice(0, -1);
|
|
@@ -19470,6 +19532,7 @@ function validateProblem(problem, engine = defaultEngine) {
|
|
|
19470
19532
|
if (!problem.statement.includes(`**${name}**`)) add(PROBLEM_MD, `The statement must name the use case **${name}** in bold`);
|
|
19471
19533
|
if (!problem.solution.includes(`usecase "${name}"`)) add(SOLUTION, `The solution must define usecase "${name}" from the traffic`);
|
|
19472
19534
|
}
|
|
19535
|
+
if (problem.lesson !== void 0) for (const issue of lessonIssues(problem.lesson)) add(LESSON_MD, issue.message, issue.line);
|
|
19473
19536
|
const importLine = problem.given.split("\n").findIndex((l) => /^\s*import /.test(l));
|
|
19474
19537
|
if (importLine >= 0) add(GIVEN, "The given must not import anything", importLine + 1);
|
|
19475
19538
|
report(GIVEN, parse(problem.given).diagnostics.filter((d) => d.severity === "error"));
|
|
@@ -19845,7 +19908,7 @@ ${PROBLEM_USAGE}`);
|
|
|
19845
19908
|
}
|
|
19846
19909
|
|
|
19847
19910
|
// src/cli.ts
|
|
19848
|
-
var VERSION2 = true ? "0.
|
|
19911
|
+
var VERSION2 = true ? "0.8.0" : "dev";
|
|
19849
19912
|
var USAGE = `Usage:
|
|
19850
19913
|
proschi check [--strict] [--format text|github|json] [--openapi <node>=<spec>]... <file|dir>...
|
|
19851
19914
|
proschi parse <file>
|