@drzl/validation-core 3.15.1 → 3.16.1
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/index.cjs +89 -18
- package/dist/index.d.cts +15 -3
- package/dist/index.d.ts +15 -3
- package/dist/index.js +91 -18
- package/package.json +17 -4
package/dist/index.cjs
CHANGED
|
@@ -60,6 +60,11 @@ __export(index_exports, {
|
|
|
60
60
|
validateAffix: () => validateAffix
|
|
61
61
|
});
|
|
62
62
|
module.exports = __toCommonJS(index_exports);
|
|
63
|
+
var import_node_child_process = require("child_process");
|
|
64
|
+
var import_node_fs2 = require("fs");
|
|
65
|
+
var import_node_module = require("module");
|
|
66
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
67
|
+
var import_node_url = require("url");
|
|
63
68
|
|
|
64
69
|
// src/checks.ts
|
|
65
70
|
var COMPARISON = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*(>=|<=|<>|!=|>|<|=)\s*(.+?)\s*$/;
|
|
@@ -394,12 +399,12 @@ var SUFFIX_RE = /^[A-Za-z0-9_$]+$/;
|
|
|
394
399
|
function validateAffix(affix, schemaSuffix) {
|
|
395
400
|
const issues = [];
|
|
396
401
|
if (!affix) return issues;
|
|
397
|
-
const checkOne = (value,
|
|
402
|
+
const checkOne = (value, path2, kind) => {
|
|
398
403
|
if (value === "") return;
|
|
399
404
|
const ok = kind === "prefix" ? PREFIX_RE.test(value) : SUFFIX_RE.test(value);
|
|
400
405
|
if (ok) return;
|
|
401
406
|
issues.push({
|
|
402
|
-
path,
|
|
407
|
+
path: path2,
|
|
403
408
|
message: `${JSON.stringify(value)} cannot appear in a TypeScript identifier. Use only letters, digits, "_" and "$"` + (kind === "prefix" ? ", and do not start with a digit." : ".")
|
|
404
409
|
});
|
|
405
410
|
};
|
|
@@ -513,32 +518,98 @@ function updateColumns(table) {
|
|
|
513
518
|
function selectColumns(table) {
|
|
514
519
|
return table.columns;
|
|
515
520
|
}
|
|
521
|
+
var reportedEngines = /* @__PURE__ */ new Set();
|
|
522
|
+
var ENGINE_PACKAGE = { prettier: "prettier", biome: "@biomejs/biome" };
|
|
523
|
+
function reportUnusableFormatter(engine, cause) {
|
|
524
|
+
if (reportedEngines.has(engine)) return;
|
|
525
|
+
reportedEngines.add(engine);
|
|
526
|
+
const pkg = ENGINE_PACKAGE[engine];
|
|
527
|
+
const remedy = engine === "prettier" ? 'Install prettier, which is an optional peer of @drzl/validation-core, or set format.engine to "auto" to accept whatever formatter is present.' : 'Install @biomejs/biome in the project being generated into, or set format.engine to "auto" or "prettier".';
|
|
528
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
529
|
+
console.warn(
|
|
530
|
+
`[drzl] format.engine is "${engine}" but ${pkg} could not be used, so the generated files were left unformatted. ${remedy} Reason: ${reason}`
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
function nearestExistingDir(from) {
|
|
534
|
+
let dir = import_node_path2.default.dirname(import_node_path2.default.resolve(from));
|
|
535
|
+
for (; ; ) {
|
|
536
|
+
try {
|
|
537
|
+
if ((0, import_node_fs2.statSync)(dir).isDirectory()) return dir;
|
|
538
|
+
} catch {
|
|
539
|
+
}
|
|
540
|
+
const parent = import_node_path2.default.dirname(dir);
|
|
541
|
+
if (parent === dir) return dir;
|
|
542
|
+
dir = parent;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function biomeBinary(startDir) {
|
|
546
|
+
const require_ = (0, import_node_module.createRequire)((0, import_node_url.pathToFileURL)(import_node_path2.default.join(startDir, "noop.js")));
|
|
547
|
+
const manifestPath = require_.resolve("@biomejs/biome/package.json", {
|
|
548
|
+
paths: [startDir, process.cwd()]
|
|
549
|
+
});
|
|
550
|
+
const manifest = JSON.parse((0, import_node_fs2.readFileSync)(manifestPath, "utf8"));
|
|
551
|
+
const relative = typeof manifest.bin === "string" ? manifest.bin : manifest.bin?.biome;
|
|
552
|
+
if (typeof relative !== "string") {
|
|
553
|
+
throw new Error(`@biomejs/biome at ${manifestPath} declares no biome binary`);
|
|
554
|
+
}
|
|
555
|
+
const binary = import_node_path2.default.resolve(import_node_path2.default.dirname(manifestPath), relative);
|
|
556
|
+
if (!(0, import_node_fs2.existsSync)(binary)) {
|
|
557
|
+
throw new Error(`@biomejs/biome declares a binary at ${relative}, and there is nothing there`);
|
|
558
|
+
}
|
|
559
|
+
return binary;
|
|
560
|
+
}
|
|
561
|
+
function formatBiome(code, filePath, configAnchor) {
|
|
562
|
+
const cwd = nearestExistingDir(configAnchor);
|
|
563
|
+
const binary = biomeBinary(nearestExistingDir(filePath));
|
|
564
|
+
return new Promise((resolve, reject) => {
|
|
565
|
+
const child = (0, import_node_child_process.spawn)(
|
|
566
|
+
process.execPath,
|
|
567
|
+
[binary, "format", `--stdin-file-path=${import_node_path2.default.basename(filePath)}`],
|
|
568
|
+
{ cwd, stdio: ["pipe", "pipe", "pipe"] }
|
|
569
|
+
);
|
|
570
|
+
let out = "";
|
|
571
|
+
let err = "";
|
|
572
|
+
child.stdout.setEncoding("utf8");
|
|
573
|
+
child.stdout.on("data", (chunk) => out += chunk);
|
|
574
|
+
child.stderr.setEncoding("utf8");
|
|
575
|
+
child.stderr.on("data", (chunk) => err += chunk);
|
|
576
|
+
child.on("error", reject);
|
|
577
|
+
child.stdin.on("error", () => {
|
|
578
|
+
});
|
|
579
|
+
child.on("close", (status) => {
|
|
580
|
+
if (status !== 0) {
|
|
581
|
+
const detail = err.trim().split("\n")[0] || out.trim().split("\n")[0] || "no output";
|
|
582
|
+
reject(new Error(`biome format exited with ${status}: ${detail}`));
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (out === "" && code !== "") {
|
|
586
|
+
reject(new Error("biome format exited 0 but returned nothing"));
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
resolve(out);
|
|
590
|
+
});
|
|
591
|
+
child.stdin.end(code);
|
|
592
|
+
});
|
|
593
|
+
}
|
|
516
594
|
async function formatCode(code, filePath, fmt) {
|
|
517
595
|
if (fmt && fmt.enabled === false) return code;
|
|
518
596
|
const engine = fmt?.engine ?? "auto";
|
|
519
|
-
|
|
520
|
-
|
|
597
|
+
if (engine === "prettier" || engine === "auto") {
|
|
598
|
+
try {
|
|
521
599
|
const prettier = await import("prettier");
|
|
522
600
|
const cfgRef = fmt?.configPath ?? filePath;
|
|
523
601
|
const cfg = await prettier.resolveConfig(cfgRef).catch(() => null);
|
|
524
602
|
return prettier.format(code, { ...cfg ?? {}, parser: "typescript", filepath: filePath });
|
|
603
|
+
} catch (err) {
|
|
604
|
+
if (engine === "prettier") reportUnusableFormatter("prettier", err);
|
|
525
605
|
}
|
|
526
|
-
} catch {
|
|
527
606
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
if (biome
|
|
533
|
-
const res = await biome.formatContent(code, { filePath });
|
|
534
|
-
return (res && (res.content || res.formatted)) ?? code;
|
|
535
|
-
}
|
|
536
|
-
if (biome?.format) {
|
|
537
|
-
const res = await biome.format(code, { filePath });
|
|
538
|
-
return res ?? code;
|
|
539
|
-
}
|
|
607
|
+
if (engine === "biome" || engine === "auto") {
|
|
608
|
+
try {
|
|
609
|
+
return await formatBiome(code, filePath, fmt?.configPath ?? filePath);
|
|
610
|
+
} catch (err) {
|
|
611
|
+
if (engine === "biome") reportUnusableFormatter("biome", err);
|
|
540
612
|
}
|
|
541
|
-
} catch {
|
|
542
613
|
}
|
|
543
614
|
return code;
|
|
544
615
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -512,9 +512,21 @@ declare function selectColumns(table: Table): Column[];
|
|
|
512
512
|
* anyone installing @drzl/cli. It is `--external` in every build script that can reach it, and
|
|
513
513
|
* no-bundled-formatter.spec.ts builds those scripts and checks.
|
|
514
514
|
*
|
|
515
|
-
*
|
|
516
|
-
* valid TypeScript that merely looks worse, and losing generated files at the last step
|
|
517
|
-
* a far worse trade than losing their whitespace.
|
|
515
|
+
* An absent formatter is never fatal. A consumer with no formatter gets the code as rendered,
|
|
516
|
+
* which is valid TypeScript that merely looks worse, and losing generated files at the last step
|
|
517
|
+
* would be a far worse trade than losing their whitespace.
|
|
518
|
+
*
|
|
519
|
+
* Whether it is worth saying so depends on what was asked for, which is the difference between the
|
|
520
|
+
* two branches below. `engine: 'auto'` asked for whatever happens to be installed, so finding
|
|
521
|
+
* nothing is an outcome and the code comes back unchanged in silence. Naming an engine is a
|
|
522
|
+
* request, and an unmet request that produces neither formatted output nor a message reads as
|
|
523
|
+
* "this is fine" when it is not: the consumer configured something, it did not happen, and nothing
|
|
524
|
+
* in the run says which. So a named engine that cannot be loaded warns on stderr and still returns
|
|
525
|
+
* the code, rather than throwing. Throwing would lose a whole generation over whitespace, which is
|
|
526
|
+
* a bad trade even now that the CLI reports the reason faithfully: it used to answer any throw at
|
|
527
|
+
* all with "<name> generator missing. Install with: npm install @drzl/generator-<name>", naming a
|
|
528
|
+
* package the consumer already had, and it now separates an unresolvable package from a generator
|
|
529
|
+
* that ran and failed.
|
|
518
530
|
*/
|
|
519
531
|
declare function formatCode(code: string, filePath: string, fmt?: FormatOptions): Promise<any>;
|
|
520
532
|
|
package/dist/index.d.ts
CHANGED
|
@@ -512,9 +512,21 @@ declare function selectColumns(table: Table): Column[];
|
|
|
512
512
|
* anyone installing @drzl/cli. It is `--external` in every build script that can reach it, and
|
|
513
513
|
* no-bundled-formatter.spec.ts builds those scripts and checks.
|
|
514
514
|
*
|
|
515
|
-
*
|
|
516
|
-
* valid TypeScript that merely looks worse, and losing generated files at the last step
|
|
517
|
-
* a far worse trade than losing their whitespace.
|
|
515
|
+
* An absent formatter is never fatal. A consumer with no formatter gets the code as rendered,
|
|
516
|
+
* which is valid TypeScript that merely looks worse, and losing generated files at the last step
|
|
517
|
+
* would be a far worse trade than losing their whitespace.
|
|
518
|
+
*
|
|
519
|
+
* Whether it is worth saying so depends on what was asked for, which is the difference between the
|
|
520
|
+
* two branches below. `engine: 'auto'` asked for whatever happens to be installed, so finding
|
|
521
|
+
* nothing is an outcome and the code comes back unchanged in silence. Naming an engine is a
|
|
522
|
+
* request, and an unmet request that produces neither formatted output nor a message reads as
|
|
523
|
+
* "this is fine" when it is not: the consumer configured something, it did not happen, and nothing
|
|
524
|
+
* in the run says which. So a named engine that cannot be loaded warns on stderr and still returns
|
|
525
|
+
* the code, rather than throwing. Throwing would lose a whole generation over whitespace, which is
|
|
526
|
+
* a bad trade even now that the CLI reports the reason faithfully: it used to answer any throw at
|
|
527
|
+
* all with "<name> generator missing. Install with: npm install @drzl/generator-<name>", naming a
|
|
528
|
+
* package the consumer already had, and it now separates an unresolvable package from a generator
|
|
529
|
+
* that ran and failed.
|
|
518
530
|
*/
|
|
519
531
|
declare function formatCode(code: string, filePath: string, fmt?: FormatOptions): Promise<any>;
|
|
520
532
|
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { existsSync, readFileSync, statSync } from "fs";
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { pathToFileURL } from "url";
|
|
7
|
+
|
|
1
8
|
// src/checks.ts
|
|
2
9
|
var COMPARISON = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*(>=|<=|<>|!=|>|<|=)\s*(.+?)\s*$/;
|
|
3
10
|
var IN_LIST = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s+IN\s*\((.+)\)\s*$/i;
|
|
@@ -331,12 +338,12 @@ var SUFFIX_RE = /^[A-Za-z0-9_$]+$/;
|
|
|
331
338
|
function validateAffix(affix, schemaSuffix) {
|
|
332
339
|
const issues = [];
|
|
333
340
|
if (!affix) return issues;
|
|
334
|
-
const checkOne = (value,
|
|
341
|
+
const checkOne = (value, path2, kind) => {
|
|
335
342
|
if (value === "") return;
|
|
336
343
|
const ok = kind === "prefix" ? PREFIX_RE.test(value) : SUFFIX_RE.test(value);
|
|
337
344
|
if (ok) return;
|
|
338
345
|
issues.push({
|
|
339
|
-
path,
|
|
346
|
+
path: path2,
|
|
340
347
|
message: `${JSON.stringify(value)} cannot appear in a TypeScript identifier. Use only letters, digits, "_" and "$"` + (kind === "prefix" ? ", and do not start with a digit." : ".")
|
|
341
348
|
});
|
|
342
349
|
};
|
|
@@ -450,32 +457,98 @@ function updateColumns(table) {
|
|
|
450
457
|
function selectColumns(table) {
|
|
451
458
|
return table.columns;
|
|
452
459
|
}
|
|
460
|
+
var reportedEngines = /* @__PURE__ */ new Set();
|
|
461
|
+
var ENGINE_PACKAGE = { prettier: "prettier", biome: "@biomejs/biome" };
|
|
462
|
+
function reportUnusableFormatter(engine, cause) {
|
|
463
|
+
if (reportedEngines.has(engine)) return;
|
|
464
|
+
reportedEngines.add(engine);
|
|
465
|
+
const pkg = ENGINE_PACKAGE[engine];
|
|
466
|
+
const remedy = engine === "prettier" ? 'Install prettier, which is an optional peer of @drzl/validation-core, or set format.engine to "auto" to accept whatever formatter is present.' : 'Install @biomejs/biome in the project being generated into, or set format.engine to "auto" or "prettier".';
|
|
467
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
468
|
+
console.warn(
|
|
469
|
+
`[drzl] format.engine is "${engine}" but ${pkg} could not be used, so the generated files were left unformatted. ${remedy} Reason: ${reason}`
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
function nearestExistingDir(from) {
|
|
473
|
+
let dir = path.dirname(path.resolve(from));
|
|
474
|
+
for (; ; ) {
|
|
475
|
+
try {
|
|
476
|
+
if (statSync(dir).isDirectory()) return dir;
|
|
477
|
+
} catch {
|
|
478
|
+
}
|
|
479
|
+
const parent = path.dirname(dir);
|
|
480
|
+
if (parent === dir) return dir;
|
|
481
|
+
dir = parent;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
function biomeBinary(startDir) {
|
|
485
|
+
const require_ = createRequire(pathToFileURL(path.join(startDir, "noop.js")));
|
|
486
|
+
const manifestPath = require_.resolve("@biomejs/biome/package.json", {
|
|
487
|
+
paths: [startDir, process.cwd()]
|
|
488
|
+
});
|
|
489
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
490
|
+
const relative = typeof manifest.bin === "string" ? manifest.bin : manifest.bin?.biome;
|
|
491
|
+
if (typeof relative !== "string") {
|
|
492
|
+
throw new Error(`@biomejs/biome at ${manifestPath} declares no biome binary`);
|
|
493
|
+
}
|
|
494
|
+
const binary = path.resolve(path.dirname(manifestPath), relative);
|
|
495
|
+
if (!existsSync(binary)) {
|
|
496
|
+
throw new Error(`@biomejs/biome declares a binary at ${relative}, and there is nothing there`);
|
|
497
|
+
}
|
|
498
|
+
return binary;
|
|
499
|
+
}
|
|
500
|
+
function formatBiome(code, filePath, configAnchor) {
|
|
501
|
+
const cwd = nearestExistingDir(configAnchor);
|
|
502
|
+
const binary = biomeBinary(nearestExistingDir(filePath));
|
|
503
|
+
return new Promise((resolve, reject) => {
|
|
504
|
+
const child = spawn(
|
|
505
|
+
process.execPath,
|
|
506
|
+
[binary, "format", `--stdin-file-path=${path.basename(filePath)}`],
|
|
507
|
+
{ cwd, stdio: ["pipe", "pipe", "pipe"] }
|
|
508
|
+
);
|
|
509
|
+
let out = "";
|
|
510
|
+
let err = "";
|
|
511
|
+
child.stdout.setEncoding("utf8");
|
|
512
|
+
child.stdout.on("data", (chunk) => out += chunk);
|
|
513
|
+
child.stderr.setEncoding("utf8");
|
|
514
|
+
child.stderr.on("data", (chunk) => err += chunk);
|
|
515
|
+
child.on("error", reject);
|
|
516
|
+
child.stdin.on("error", () => {
|
|
517
|
+
});
|
|
518
|
+
child.on("close", (status) => {
|
|
519
|
+
if (status !== 0) {
|
|
520
|
+
const detail = err.trim().split("\n")[0] || out.trim().split("\n")[0] || "no output";
|
|
521
|
+
reject(new Error(`biome format exited with ${status}: ${detail}`));
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
if (out === "" && code !== "") {
|
|
525
|
+
reject(new Error("biome format exited 0 but returned nothing"));
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
resolve(out);
|
|
529
|
+
});
|
|
530
|
+
child.stdin.end(code);
|
|
531
|
+
});
|
|
532
|
+
}
|
|
453
533
|
async function formatCode(code, filePath, fmt) {
|
|
454
534
|
if (fmt && fmt.enabled === false) return code;
|
|
455
535
|
const engine = fmt?.engine ?? "auto";
|
|
456
|
-
|
|
457
|
-
|
|
536
|
+
if (engine === "prettier" || engine === "auto") {
|
|
537
|
+
try {
|
|
458
538
|
const prettier = await import("prettier");
|
|
459
539
|
const cfgRef = fmt?.configPath ?? filePath;
|
|
460
540
|
const cfg = await prettier.resolveConfig(cfgRef).catch(() => null);
|
|
461
541
|
return prettier.format(code, { ...cfg ?? {}, parser: "typescript", filepath: filePath });
|
|
542
|
+
} catch (err) {
|
|
543
|
+
if (engine === "prettier") reportUnusableFormatter("prettier", err);
|
|
462
544
|
}
|
|
463
|
-
} catch {
|
|
464
545
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
if (biome
|
|
470
|
-
const res = await biome.formatContent(code, { filePath });
|
|
471
|
-
return (res && (res.content || res.formatted)) ?? code;
|
|
472
|
-
}
|
|
473
|
-
if (biome?.format) {
|
|
474
|
-
const res = await biome.format(code, { filePath });
|
|
475
|
-
return res ?? code;
|
|
476
|
-
}
|
|
546
|
+
if (engine === "biome" || engine === "auto") {
|
|
547
|
+
try {
|
|
548
|
+
return await formatBiome(code, filePath, fmt?.configPath ?? filePath);
|
|
549
|
+
} catch (err) {
|
|
550
|
+
if (engine === "biome") reportUnusableFormatter("biome", err);
|
|
477
551
|
}
|
|
478
|
-
} catch {
|
|
479
552
|
}
|
|
480
553
|
return code;
|
|
481
554
|
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drzl/validation-core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.16.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"main": "dist/index.
|
|
8
|
-
"
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/index.d.cts",
|
|
18
|
+
"default": "./dist/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
9
22
|
"files": [
|
|
10
23
|
"dist"
|
|
11
24
|
],
|
|
12
25
|
"sideEffects": false,
|
|
13
26
|
"dependencies": {
|
|
14
|
-
"@drzl/analyzer": "^1.
|
|
27
|
+
"@drzl/analyzer": "^1.17.1"
|
|
15
28
|
},
|
|
16
29
|
"peerDependencies": {
|
|
17
30
|
"prettier": ">=3"
|