chore: Fix the testing of the output.ts

This commit is contained in:
BluJ
2023-03-28 11:37:17 -06:00
parent 5943d961ed
commit 5f9a9ea4ff
10 changed files with 172 additions and 182 deletions

View File

@@ -1,5 +1,5 @@
export class IBuilder<A> { export class IBuilder<A> {
protected constructor(readonly a: A) { } protected constructor(readonly a: A) {}
public build(): A { public build(): A {
return this.a; return this.a;

View File

@@ -1,10 +1,6 @@
import { BuilderExtract, IBuilder } from "./builder"; import { BuilderExtract, IBuilder } from "./builder";
import { Config } from "./config"; import { Config } from "./config";
import { import { InputSpec, UniqueBy, ValueSpecList } from "../config-types";
InputSpec,
UniqueBy,
ValueSpecList,
} from "../config-types";
import { guardAll } from "../../util"; import { guardAll } from "../../util";
/** /**

View File

@@ -14,9 +14,9 @@ import { guardAll } from "../../util";
export type DefaultString = export type DefaultString =
| string | string
| { | {
charset: string | null | undefined; charset: string | null | undefined;
len: number; len: number;
}; };
/** /**
* A value is going to be part of the form in the FE of the OS. * A value is going to be part of the form in the FE of the OS.

View File

@@ -67,7 +67,7 @@ export interface ValueSpecBoolean extends WithStandalone {
export interface ValueSpecUnion extends WithStandalone { export interface ValueSpecUnion extends WithStandalone {
type: "union"; type: "union";
nullable: boolean; nullable: boolean;
default: null | string default: null | string;
variants: Record<string, { name: string; spec: InputSpec }>; variants: Record<string, { name: string; spec: InputSpec }>;
} }
@@ -105,8 +105,7 @@ export type ListValueSpecOf<T extends ListValueSpecType> = T extends "string"
/** represents a spec for a list */ /** represents a spec for a list */
export type ValueSpecList = ValueSpecListOf<ListValueSpecType>; export type ValueSpecList = ValueSpecListOf<ListValueSpecType>;
export interface ValueSpecListOf<T extends ListValueSpecType> export interface ValueSpecListOf<T extends ListValueSpecType> extends WithStandalone {
extends WithStandalone {
type: "list"; type: "list";
subtype: T; subtype: T;
spec: ListValueSpecOf<T>; spec: ListValueSpecOf<T>;
@@ -123,10 +122,7 @@ export interface ValueSpecListOf<T extends ListValueSpecType>
} }
// sometimes the type checker needs just a little bit of help // sometimes the type checker needs just a little bit of help
export function isValueSpecListOf<S extends ListValueSpecType>( export function isValueSpecListOf<S extends ListValueSpecType>(t: ValueSpecList, s: S): t is ValueSpecListOf<S> {
t: ValueSpecList,
s: S
): t is ValueSpecListOf<S> {
return t.subtype === s; return t.subtype === s;
} }
@@ -162,4 +158,5 @@ export type UniqueBy =
export type DefaultString = string | { charset: string; len: number }; export type DefaultString = string | { charset: string; len: number };
export const unionSelectKey = 'unionSelectKey' as const export const unionSelectKey = "unionSelectKey" as const;
export type UnionSelectKey = typeof unionSelectKey;

View File

@@ -40,8 +40,8 @@ async function timeoutHealth(
const defaultParams = { const defaultParams = {
defaultIntervalS: 60, defaultIntervalS: 60,
defaultTimeoutS: 10, defaultTimeoutS: 10,
defaultDelayS: 10 defaultDelayS: 10,
} };
export default function healthRunner( export default function healthRunner(
name: string, name: string,
@@ -52,15 +52,14 @@ export default function healthRunner(
/** \ /** \
* All values in seconds * All values in seconds
* defaults: * defaults:
* interval: 60s * interval: 60s
* timeout: 10s * timeout: 10s
* delay: 10s * delay: 10s
*/ */
create(effects: Types.Effects, { create(
interval = 60, effects: Types.Effects,
timeout = 10, { interval = 60, timeout = 10, delay = 10 } = {}
delay = 10, ) {
} = {}) {
let running: any; let running: any;
function startFn() { function startFn() {
clearInterval(running); clearInterval(running);

View File

@@ -1,19 +1,108 @@
import { InputSpec, matchInputSpec } from "./output"; import { unionSelectKey } from "../config/config-types";
import { InputSpec, matchInputSpec, threads } from "./output";
type TEqual<A, B> = A extends B ? (B extends A ? null : never) : never; type IfEquals<T, U, Y = unknown, N = never> = (<G>() => G extends T ? 1 : 2) extends <G>() => G extends U ? 1 : 2
function testOutput<A, B>(): (c: TEqual<A, B>) => null { ? Y
: N;
function testOutput<A, B>(): (c: IfEquals<A, B>) => null {
return () => null; return () => null;
} }
const testValue = null as unknown;
function isObject(item: unknown): item is object {
return !!(item && typeof item === "object" && !Array.isArray(item));
}
type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never;
export function mergeDeep<A extends unknown[]>(...sources: A) {
return _mergeDeep({}, ...sources);
}
function _mergeDeep<A extends unknown[]>(
target: UnionToIntersection<A[number]> | {},
...sources: A
): UnionToIntersection<A[number]> | {} {
if (!sources.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject((source as any)[key])) {
if (!(target as any)[key]) Object.assign(target, { [key]: {} });
_mergeDeep((target as any)[key], (source as any)[key]);
} else {
Object.assign(target, { [key]: (source as any)[key] });
}
}
}
return _mergeDeep(target, ...sources);
}
// @ts-expect-error Because enable should be a boolean // @ts-expect-error Because enable should be a boolean
testOutput<InputSpec["rpc"]["enable"], string>()(null); testOutput<InputSpec["rpc"]["enable"], string>()(null);
testOutput<InputSpec["rpc"]["enable"], boolean>()(null); testOutput<InputSpec["rpc"]["enable"], boolean>()(null);
testOutput<InputSpec["rpc"]["username"], string>()(null); testOutput<InputSpec["rpc"]["username"], string>()(null);
testOutput<InputSpec["rpc"]["advanced"]["auth"], readonly string[]>()(null); testOutput<InputSpec["rpc"]["advanced"]["auth"], string[]>()(null);
testOutput<InputSpec["rpc"]["advanced"]["serialversion"], readonly string[]>()(null); testOutput<InputSpec["rpc"]["advanced"]["serialversion"], "segwit" | "non-segwit">()(null);
testOutput<InputSpec["rpc"]["advanced"]["servertimeout"], number>()(null);
testOutput<InputSpec["advanced"]["peers"]["addnode"][0]["hostname"], string>()(null);
describe("Inputs", () => { describe("Inputs", () => {
test("test", () => { const validInput: InputSpec = {
expect(true).toEqual(true); rpc: {
enable: true,
username: "test",
password: "test",
advanced: {
auth: ["test"],
serialversion: "segwit",
servertimeout: 6,
threads: 3,
workqueue: 9,
},
},
"zmq-enabled": false,
txindex: false,
wallet: { enable: false, avoidpartialspends: false, discardfee: 0.0001 },
advanced: {
mempool: {
maxmempool: 1,
persistmempool: true,
mempoolexpiry: 23,
mempoolfullrbf: true,
},
peers: {
listen: true,
onlyconnect: true,
onlyonion: true,
addnode: [
{
hostname: "test",
port: 1,
},
],
},
dbcache: 5,
pruning: {
[unionSelectKey]: "disabled",
},
blockfilters: {
blockfilterindex: false,
peerblockfilters: false,
},
bloomfilters: { peerbloomfilters: false },
},
};
test("test valid input", () => {
const output = matchInputSpec.unsafeCast(validInput);
expect(output).toEqual(validInput);
});
test("test errors", () => {
expect(() =>
matchInputSpec.unsafeCast(mergeDeep(validInput, { rpc: { advanced: { threads: 0 } } }))
).toThrowError();
expect(() => matchInputSpec.unsafeCast(mergeDeep(validInput, { rpc: { enable: 2 } }))).toThrowError();
expect(() =>
matchInputSpec.unsafeCast(mergeDeep(validInput, { rpc: { advanced: { serialversion: "testing" } } }))
).toThrowError();
}); });
}); });

View File

@@ -1,9 +1,8 @@
import * as matches from "ts-matches"; import * as matches from "ts-matches";
import { Parser } from "ts-matches"; import { Parser } from "ts-matches";
import { InputSpec, ValueSpec as ValueSpecAny } from "../config/config-types"; import { InputSpec, unionSelectKey, ValueSpec as ValueSpecAny } from "../config/config-types";
const { string, some, object, dictionary, unknown, number, literals, boolean } = const { string, some, object, dictionary, unknown, number, literals, boolean } = matches;
matches;
type TypeBoolean = "boolean"; type TypeBoolean = "boolean";
type TypeString = "string"; type TypeString = "string";
@@ -16,69 +15,57 @@ type TypeUnion = "union";
// prettier-ignore // prettier-ignore
type GuardDefaultNullable<A, Type> = type GuardDefaultNullable<A, Type> =
A extends { readonly default: unknown } ? Type : A extends { default: unknown } ? Type :
A extends { readonly nullable: true } ? Type : A extends { nullable: true } ? Type :
A extends { readonly nullable: false } ? Type | null | undefined : A extends { nullable: false } ? Type | null | undefined :
Type Type
// prettier-ignore // prettier-ignore
type GuardNumber<A> = type GuardNumber<A> =
A extends { readonly type: TypeNumber } ? GuardDefaultNullable<A, number> : A extends { type: TypeNumber } ? GuardDefaultNullable<A, number> :
unknown unknown
// prettier-ignore // prettier-ignore
type GuardString<A> = type GuardString<A> =
A extends { readonly type: TypeString } ? GuardDefaultNullable<A, string> : A extends { type: TypeString } ? GuardDefaultNullable<A, string> :
unknown unknown
// prettier-ignore // prettier-ignore
type GuardBoolean<A> = type GuardBoolean<A> =
A extends { readonly type: TypeBoolean } ? GuardDefaultNullable<A, boolean> : A extends { type: TypeBoolean } ? GuardDefaultNullable<A, boolean> :
unknown unknown
// prettier-ignore // prettier-ignore
type GuardObject<A> = type GuardObject<A> =
A extends { readonly type: TypeObject, readonly spec: infer B } ? ( A extends { type: TypeObject, spec: infer B } ? (
B extends Record<string, unknown> ? { readonly [K in keyof B & string]: _<GuardAll<B[K]>> } : B extends Record<string, unknown> ? { [K in keyof B & string]: _<GuardAll<B[K]>> } :
{ _error: "Invalid Spec" } { _error: "Invalid Spec" }
) : ) :
unknown unknown
// prettier-ignore // prettier-ignore
export type GuardList<A> = export type GuardList<A> =
A extends { readonly type: TypeList, readonly subtype: infer B, spec?: { spec?: infer C } } ? ReadonlyArray<GuardAll<Omit<A, "type" | "subtype" | "spec"> & ({ type: B, spec: C })>> : A extends { type: TypeList, subtype: infer B, spec?: { spec?: infer C } } ? Array<GuardAll<Omit<A, "type" | "subtype" | "spec"> & ({ type: B, spec: C })>> :
A extends { readonly type: TypeList, readonly subtype: infer B, spec?: {} } ? ReadonlyArray<GuardAll<Omit<A, "type"> & ({ type: B })>> : A extends { type: TypeList, subtype: infer B, spec?: {} } ? Array<GuardAll<Omit<A, "type"> & ({ type: B })>> :
unknown unknown
// prettier-ignore // prettier-ignore
type GuardSelect<A> = type GuardSelect<A> =
A extends { readonly type: TypeSelect, variants: infer B } ? ( A extends { type: TypeSelect, values: infer B } ? (
B extends Record<string, string> ? keyof B : never B extends Record<string, string> ? keyof B : never
) : ) :
unknown unknown
const bluj: GuardSelect<{ // prettier-ignore
type: "select"; type GuardMultiselect<A> =
} & { A extends { type: TypeMultiselect, variants: { [key in infer B & string]: string } } ?B[] :
readonly name: "Serialization Version";
readonly description: "Return raw transaction or block hex with Segwit or non-SegWit serialization.";
readonly warning: null;
readonly default: "segwit";
readonly nullable: false;
readonly values: {
...;
};
}>
// prettier-ignore
type GuardMultiselect<A> =
A extends { readonly type: TypeMultiselect, variants: { [key in infer B & string]: string } } ?B[] :
unknown unknown
// prettier-ignore // prettier-ignore
type VariantValue<A> = type VariantValue<A> =
A extends { name: string, spec: infer B } ? { name: A['name'], spec: TypeFromProps<B> } : A extends { name: string, spec: infer B } ? TypeFromProps<B> :
never never
// prettier-ignore // prettier-ignore
type GuardUnion<A> = type GuardUnion<A> =
A extends { readonly type: TypeUnion, variants: infer Variants & Record<string, unknown> } ? A extends { type: TypeUnion, variants: infer Variants & Record<string, unknown> } ?
{ [K in keyof Variants]: { [key in K]: VariantValue<Variants[K]>['spec'] } }[keyof Variants] : { [key in keyof Variants]: _<{[unionSelectKey]: key} & VariantValue<Variants[key]>> }[keyof Variants] :
unknown unknown
type _<T> = T; type _<T> = T;
@@ -92,7 +79,7 @@ export type GuardAll<A> = GuardNumber<A> &
GuardMultiselect<A>; GuardMultiselect<A>;
// prettier-ignore // prettier-ignore
export type TypeFromProps<A> = export type TypeFromProps<A> =
A extends Record<string, unknown> ? { readonly [K in keyof A & string]: _<GuardAll<A[K]>> } : A extends Record<string, unknown> ? { [K in keyof A & string]: _<GuardAll<A[K]>> } :
unknown; unknown;
const isType = object({ type: string }); const isType = object({ type: string });
@@ -131,28 +118,18 @@ function charRange(value = "") {
* @param param1 * @param param1
* @returns * @returns
*/ */
export function generateDefault( export function generateDefault(generate: { charset: string; len: number }, { random = () => Math.random() } = {}) {
generate: { charset: string; len: number }, const validCharSets: number[][] = generate.charset.split(",").map(charRange).filter(Array.isArray);
{ random = () => Math.random() } = {}
) {
const validCharSets: number[][] = generate.charset
.split(",")
.map(charRange)
.filter(Array.isArray);
if (validCharSets.length === 0) { if (validCharSets.length === 0) {
throw new Error("Expecing that we have a valid charset"); throw new Error("Expecing that we have a valid charset");
} }
const max = validCharSets.reduce( const max = validCharSets.reduce((acc, x) => x.reduce((x, y) => Math.max(x, y), acc), 0);
(acc, x) => x.reduce((x, y) => Math.max(x, y), acc),
0
);
let i = 0; let i = 0;
const answer: string[] = Array(generate.len); const answer: string[] = Array(generate.len);
while (i < generate.len) { while (i < generate.len) {
const nextValue = Math.round(random() * max); const nextValue = Math.round(random() * max);
const inRange = validCharSets.reduce( const inRange = validCharSets.reduce(
(acc, [lower, upper]) => (acc, [lower, upper]) => acc || (nextValue >= lower && nextValue <= upper),
acc || (nextValue >= lower && nextValue <= upper),
false false
); );
if (!inRange) continue; if (!inRange) continue;
@@ -168,16 +145,8 @@ export function matchNumberWithRange(range: string) {
const [, left, leftValue, , rightValue, , right] = matched; const [, left, leftValue, , rightValue, , right] = matched;
return number return number
.validate( .validate(
leftValue === "*" leftValue === "*" ? (_) => true : left === "[" ? (x) => x >= Number(leftValue) : (x) => x > Number(leftValue),
? (_) => true leftValue === "*" ? "any" : left === "[" ? `greaterThanOrEqualTo${leftValue}` : `greaterThan${leftValue}`
: left === "["
? (x) => x >= Number(leftValue)
: (x) => x > Number(leftValue),
leftValue === "*"
? "any"
: left === "["
? `greaterThanOrEqualTo${leftValue}`
: `greaterThan${leftValue}`
) )
.validate( .validate(
// prettier-ignore // prettier-ignore
@@ -209,9 +178,7 @@ const isGenerator = object({
function defaultNullable<A>(parser: Parser<unknown, A>, value: unknown) { function defaultNullable<A>(parser: Parser<unknown, A>, value: unknown) {
if (matchDefault.test(value)) { if (matchDefault.test(value)) {
if (isGenerator(value.default)) { if (isGenerator(value.default)) {
return parser.defaultTo( return parser.defaultTo(parser.unsafeCast(generateDefault(value.default)));
parser.unsafeCast(generateDefault(value.default))
);
} }
return parser.defaultTo(value.default); return parser.defaultTo(value.default);
} }
@@ -227,9 +194,7 @@ function defaultNullable<A>(parser: Parser<unknown, A>, value: unknown) {
* @param value * @param value
* @returns * @returns
*/ */
export function guardAll<A extends ValueSpecAny>( export function guardAll<A extends ValueSpecAny>(value: A): Parser<unknown, GuardAll<A>> {
value: A
): Parser<unknown, GuardAll<A>> {
if (!isType.test(value)) { if (!isType.test(value)) {
return unknown as any; return unknown as any;
} }
@@ -241,10 +206,7 @@ export function guardAll<A extends ValueSpecAny>(
return defaultNullable(string, value) as any; return defaultNullable(string, value) as any;
case "number": case "number":
return defaultNullable( return defaultNullable(withIntegral(withRange(value), value), value) as any;
withIntegral(withRange(value), value),
value
) as any;
case "object": case "object":
if (matchSpec.test(value)) { if (matchSpec.test(value)) {
@@ -254,9 +216,7 @@ export function guardAll<A extends ValueSpecAny>(
case "list": { case "list": {
const spec = (matchSpec.test(value) && value.spec) || {}; const spec = (matchSpec.test(value) && value.spec) || {};
const rangeValidate = const rangeValidate = (matchRange.test(value) && matchNumberWithRange(value.range).test) || (() => true);
(matchRange.test(value) && matchNumberWithRange(value.range).test) ||
(() => true);
const subtype = matchSubType.unsafeCast(value).subtype; const subtype = matchSubType.unsafeCast(value).subtype;
return defaultNullable( return defaultNullable(
@@ -269,34 +229,23 @@ export function guardAll<A extends ValueSpecAny>(
case "select": case "select":
if (matchValues.test(value)) { if (matchValues.test(value)) {
const valueKeys = Object.keys(value.values); const valueKeys = Object.keys(value.values);
return defaultNullable( return defaultNullable(literals(valueKeys[0], ...valueKeys), value) as any;
literals(valueKeys[0], ...valueKeys),
value
) as any;
} }
return unknown as any; return unknown as any;
case "multiselect": case "multiselect":
if (matchValues.test(value)) { if (matchValues.test(value)) {
const rangeValidate = const rangeValidate = (matchRange.test(value) && matchNumberWithRange(value.range).test) || (() => true);
(matchRange.test(value) && matchNumberWithRange(value.range).test) ||
(() => true);
const valueKeys = Object.keys(value.values); const valueKeys = Object.keys(value.values);
return defaultNullable( return defaultNullable(
matches matches.literals(valueKeys[0], ...valueKeys).validate((x) => rangeValidate(x.length), "valid length"),
.literals(valueKeys[0], ...valueKeys)
.validate((x) => rangeValidate(x.length), "valid length"),
value value
) as any; ) as any;
} }
return unknown as any; return unknown as any;
case "union": case "union":
if (matchUnion.test(value)) { if (matchUnion.test(value)) {
return some( return some(...Object.entries(value.variants).map(([_, { spec }]) => typeFromProps(spec))) as any;
...Object.entries(value.variants).map(([_, { spec }]) =>
typeFromProps(spec)
)
) as any;
} }
return unknown as any; return unknown as any;
} }
@@ -311,16 +260,9 @@ export function guardAll<A extends ValueSpecAny>(
* @param valueDictionary * @param valueDictionary
* @returns * @returns
*/ */
export function typeFromProps<A extends InputSpec>( export function typeFromProps<A extends InputSpec>(valueDictionary: A): Parser<unknown, TypeFromProps<A>> {
valueDictionary: A
): Parser<unknown, TypeFromProps<A>> {
if (!recordString.test(valueDictionary)) return unknown as any; if (!recordString.test(valueDictionary)) return unknown as any;
return object( return object(
Object.fromEntries( Object.fromEntries(Object.entries(valueDictionary).map(([key, value]) => [key, guardAll(value)]))
Object.entries(valueDictionary).map(([key, value]) => [
key,
guardAll(value),
])
)
) as any; ) as any;
} }

6
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "start-sdk", "name": "start-sdk",
"version": "0.4.0-lib0.beta1", "version": "0.4.0-lib0.beta3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "start-sdk", "name": "start-sdk",
"version": "0.4.0-lib0.beta1", "version": "0.4.0-lib0.beta3",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@iarna/toml": "^2.2.5", "@iarna/toml": "^2.2.5",
@@ -22,7 +22,6 @@
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"tsc-multi": "^0.6.1", "tsc-multi": "^0.6.1",
"tsconfig-paths": "^3.14.2", "tsconfig-paths": "^3.14.2",
"typescript": "^4.9.5",
"vitest": "^0.29.2" "vitest": "^0.29.2"
} }
}, },
@@ -4896,6 +4895,7 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dev": true, "dev": true,
"peer": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"

View File

@@ -1,6 +1,6 @@
{ {
"name": "start-sdk", "name": "start-sdk",
"version": "0.4.0-lib0.beta1", "version": "0.4.0-lib0.beta3",
"description": "For making the patterns that are wanted in making services for the startOS.", "description": "For making the patterns that are wanted in making services for the startOS.",
"main": "./lib/index.js", "main": "./lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
@@ -33,8 +33,7 @@
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"tsc-multi": "^0.6.1", "tsc-multi": "^0.6.1",
"tsconfig-paths": "^3.14.2", "tsconfig-paths": "^3.14.2",
"typescript": "^4.9.5",
"vitest": "^0.29.2" "vitest": "^0.29.2"
}, },
"declaration": true "declaration": true
} }

View File

@@ -8,15 +8,10 @@ export async function writeConvertedFile(
inputData: Promise<any> | any, inputData: Promise<any> | any,
options: Parameters<typeof makeFileContent>[1] options: Parameters<typeof makeFileContent>[1]
) { ) {
await fs.writeFile(file, await makeFileContent(inputData, options), (err) => await fs.writeFile(file, await makeFileContent(inputData, options), (err) => console.error(err));
console.error(err)
);
} }
export default async function makeFileContent( export default async function makeFileContent(inputData: Promise<any> | any, { startSdk = "start-sdk" } = {}) {
inputData: Promise<any> | any,
{ startSdk = "start-sdk" } = {}
) {
const outputLines: string[] = []; const outputLines: string[] = [];
outputLines.push(` outputLines.push(`
import {Config, Value, List, Variants} from '${startSdk}/config/builder'; import {Config, Value, List, Variants} from '${startSdk}/config/builder';
@@ -25,13 +20,8 @@ export default async function makeFileContent(
const namedConsts = new Set(["Config", "Value", "List"]); const namedConsts = new Set(["Config", "Value", "List"]);
const configName = newConst("InputSpec", convertInputSpec(data)); const configName = newConst("InputSpec", convertInputSpec(data));
const configMatcherName = newConst( const configMatcherName = newConst("matchInputSpec", `${configName}.validator()`);
"matchInputSpec", outputLines.push(`export type InputSpec = typeof ${configMatcherName}._TYPE;`);
`${configName}.validator()`
);
outputLines.push(
`export type InputSpec = typeof ${configMatcherName}._TYPE;`
);
return outputLines.join("\n"); return outputLines.join("\n");
@@ -99,10 +89,7 @@ export default async function makeFileContent(
)})`; )})`;
} }
case "enum": { case "enum": {
const allValueNames = new Set([ const allValueNames = new Set([...(value?.["values"] || []), ...Object.keys(value?.["value-names"] || {})]);
...(value?.["values"] || []),
...Object.keys(value?.["value-names"] || {})]
);
const values = Object.fromEntries( const values = Object.fromEntries(
Array.from(allValueNames) Array.from(allValueNames)
.filter(string.test) .filter(string.test)
@@ -122,10 +109,7 @@ export default async function makeFileContent(
)} as const)`; )} as const)`;
} }
case "object": { case "object": {
const specName = newConst( const specName = newConst(value.name + "_spec", convertInputSpec(value.spec));
value.name + "_spec",
convertInputSpec(value.spec)
);
return `Value.object({ return `Value.object({
name: ${JSON.stringify(value.name || null)}, name: ${JSON.stringify(value.name || null)},
description: ${JSON.stringify(value.description || null)}, description: ${JSON.stringify(value.description || null)},
@@ -168,7 +152,7 @@ export default async function makeFileContent(
name: value.name || null, name: value.name || null,
range: value.range || null, range: value.range || null,
spec: { spec: {
masked: value?.spec?.masked || null, masked: value?.spec?.masked || false,
placeholder: value?.spec?.placeholder || null, placeholder: value?.spec?.placeholder || null,
pattern: value?.spec?.pattern || null, pattern: value?.spec?.pattern || null,
patternDescription: value?.spec?.["pattern-description"] || null, patternDescription: value?.spec?.["pattern-description"] || null,
@@ -225,10 +209,7 @@ export default async function makeFileContent(
)})`; )})`;
} }
case "object": { case "object": {
const specName = newConst( const specName = newConst(value.name + "_spec", convertInputSpec(value.spec.spec));
value.name + "_spec",
convertInputSpec(value.spec.spec)
);
return `List.obj({ return `List.obj({
name: ${JSON.stringify(value.name || null)}, name: ${JSON.stringify(value.name || null)},
range: ${JSON.stringify(value.range || null)}, range: ${JSON.stringify(value.range || null)},
@@ -245,10 +226,7 @@ export default async function makeFileContent(
case "union": { case "union": {
const variants = newConst( const variants = newConst(
value.name + "_variants", value.name + "_variants",
convertVariants( convertVariants(value.spec.variants, value.spec["variant-names"] || {})
value.spec.variants,
value.spec["variant-names"] || {}
)
); );
// @TODO BluJ // @TODO BluJ
return `List.obj({ return `List.obj({
@@ -258,15 +236,9 @@ export default async function makeFileContent(
spec: { spec: {
${unionSelectKey}: { ${unionSelectKey}: {
type: "union", type: "union",
name: ${JSON.stringify( name: ${JSON.stringify(value?.spec?.tag?.name || null)},
value?.spec?.tag?.name || null description: ${JSON.stringify(value?.spec?.tag?.description || null)},
)}, warning: ${JSON.stringify(value?.spec?.tag?.warning || null)},
description: ${JSON.stringify(
value?.spec?.tag?.description || null
)},
warning: ${JSON.stringify(
value?.spec?.tag?.warning || null
)},
variants: ${variants}, variants: ${variants},
nullable: false, nullable: false,
} }
@@ -283,15 +255,11 @@ export default async function makeFileContent(
throw new Error(`Unknown subtype "${value.subtype}"`); throw new Error(`Unknown subtype "${value.subtype}"`);
} }
function convertVariants( function convertVariants(variants: Record<string, unknown>, variantNames: Record<string, string>): string {
variants: Record<string, unknown>,
variantNames: Record<string, string>
): string {
let answer = "Variants.of({"; let answer = "Variants.of({";
for (const [key, value] of Object.entries(variants)) { for (const [key, value] of Object.entries(variants)) {
const variantSpec = newConst(key, convertInputSpec(value)); const variantSpec = newConst(key, convertInputSpec(value));
answer += `"${key}": {name: "${variantNames[key] || key answer += `"${key}": {name: "${variantNames[key] || key}", spec: ${variantSpec}},`;
}", spec: ${variantSpec}},`;
} }
return `${answer}})`; return `${answer}})`;
} }