Skip to content

Commit

Permalink
fix: repeated morph applications, required default values (#1161)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssalbdivad authored Oct 3, 2024
1 parent 4e687f8 commit d72778b
Show file tree
Hide file tree
Showing 37 changed files with 401 additions and 179 deletions.
2 changes: 1 addition & 1 deletion ark/attest/__tests__/externalSnapshots.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { attest, contextualize } from "@ark/attest"
import { attestInternal } from "@ark/attest/internal/assert/attest.ts"
import { attestInternal } from "@ark/attest/internal/assert/attest.js"
import { dirName, readJson, writeJson } from "@ark/fs"
import * as assert from "node:assert/strict"
import { rmSync } from "node:fs"
Expand Down
2 changes: 1 addition & 1 deletion ark/attest/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ark/attest",
"version": "0.20.0",
"version": "0.21.0",
"author": {
"name": "David Blass",
"email": "[email protected]",
Expand Down
2 changes: 1 addition & 1 deletion ark/docs/src/components/Code.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
arkHighlight,
type HighlightArgs,
type BuiltinLang
} from "./highlight.js"
} from "./highlight.ts"
// ideally we could just import { Code } from "astro:components" instead of
// using this custom component, but as of now, the `Code` component imported from
Expand Down
4 changes: 2 additions & 2 deletions ark/docs/src/components/HeroBackdrop.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react"
import { FloatYourBoat } from "./FloatYourBoat.js"
import { PlatformCloud } from "./PlatformCloud.js"
import { FloatYourBoat } from "./FloatYourBoat.tsx"
import { PlatformCloud } from "./PlatformCloud.tsx"

// workaround for compatibility issue between MDX and Astro
declare module "react" {
Expand Down
7 changes: 6 additions & 1 deletion ark/docs/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
"module": "ESNext",
"moduleResolution": "Bundler",
"allowJs": true,
"jsx": "preserve"
"jsx": "preserve",
// leaving this in for now as it results in some nonsense in 5.7 alpha like:
// "This relative import path is unsafe to rewrite because it looks like a file name,
// but actually resolves to ./src/components/shiki.config.js"
// can remove this line if it doesn't break the build in the future
"rewriteRelativeImportExtensions": false
},
"mdx": {
"checkMdx": true
Expand Down
2 changes: 1 addition & 1 deletion ark/fs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ark/fs",
"version": "0.15.0",
"version": "0.16.0",
"author": {
"name": "David Blass",
"email": "[email protected]",
Expand Down
18 changes: 1 addition & 17 deletions ark/repo/build.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import { symlinkSync } from "fs"
import { join } from "path"
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import {
fromCwd,
fromHere,
rewriteFile,
rmRf,
shell,
walkPaths,
writeJson
} from "../fs/index.ts"
import { fromCwd, fromHere, rmRf, shell, writeJson } from "../fs/index.ts"

const buildKind =
process.argv.includes("--cjs") || process.env.ARKTYPE_CJS ? "cjs" : "esm"
Expand All @@ -25,14 +17,6 @@ try {
rmRf("tsconfig.build.json")
symlinkSync(`../repo/tsconfig.${buildKind}.json`, "tsconfig.build.json")
buildCurrentProject()
walkPaths(outDir, { excludeDirs: true }).forEach(jsPath =>
rewriteFile(jsPath, src =>
src.replaceAll(
/(import|export\s+.*?from\s+["'])(.*?\.ts)(["'])/g,
(match, p1, p2, p3) => `${p1}${p2.replace(".ts", ".js")}${p3}`
)
)
)
rmRf("tsconfig.build.json")
symlinkSync(`../repo/tsconfig.dts.json`, "tsconfig.build.json")
buildCurrentProject()
Expand Down
2 changes: 1 addition & 1 deletion ark/repo/scratch.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type } from "arktype"
import { type, type Type } from "arktype"

const out = type("string.lower").to("string.trim").to("'success'")("Success") //?

Expand Down
4 changes: 0 additions & 4 deletions ark/repo/tsconfig.cjs.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@
"outDir": "out",
"noEmit": false,
"declaration": false,
"noCheck": true,
// the errors that would come from this setting don't trigger because of noCheck.
// required to force TS to emit despite .ts endings, which we rewrite
"allowImportingTsExtensions": false,
"customConditions": []
},
"exclude": ["out", "__tests__"]
Expand Down
3 changes: 0 additions & 3 deletions ark/repo/tsconfig.esm.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
"noEmit": false,
"declaration": false,
"noCheck": true,
// the errors that would come from this setting don't trigger because of noCheck.
// required to force TS to emit despite .ts endings, which we rewrite
"allowImportingTsExtensions": false,
"customConditions": []
},
"exclude": ["out", "__tests__"]
Expand Down
13 changes: 13 additions & 0 deletions ark/schema/__tests__/proto.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { attest, contextualize } from "@ark/attest"
import { node, rootSchema } from "@ark/schema"

contextualize(() => {
it("normalizes node schema", () => {
const d = node("proto", Date)
attest(d.json).snap({ proto: "Date" })

const reparsed = rootSchema(d)
attest(reparsed.json).equals(d.json)
attest(reparsed.id).equals(d.id)
})
})
9 changes: 6 additions & 3 deletions ark/schema/constraint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ import {
type UnknownAttachments,
type kindLeftOf
} from "./shared/implement.ts"
import { intersectNodes, intersectNodesRoot } from "./shared/intersections.ts"
import {
intersectNodesRoot,
intersectOrPipeNodes
} from "./shared/intersections.ts"
import type { JsonSchema } from "./shared/jsonSchema.ts"
import { $ark } from "./shared/registry.ts"
import type { TraverseAllows, TraverseApply } from "./shared/traversal.ts"
Expand Down Expand Up @@ -167,14 +170,14 @@ export const intersectConstraints = <kind extends ConstraintGroupKind>(
for (const root of s.roots) {
if (result instanceof Disjoint) return result

result = intersectNodes(root, result, s.ctx)!
result = intersectOrPipeNodes(root, result, s.ctx)!
}

return result as never
}
let matched = false
for (let i = 0; i < s.l.length; i++) {
const result = intersectNodes(s.l[i], head, s.ctx)
const result = intersectOrPipeNodes(s.l[i], head, s.ctx)
if (result === null) continue
if (result instanceof Disjoint) return result

Expand Down
6 changes: 1 addition & 5 deletions ark/schema/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,9 @@ export abstract class BaseNode<
withMeta(
meta: ArkEnv.meta | ((currentMeta: ArkEnv.meta) => ArkEnv.meta)
): this {
const newMeta =
typeof meta === "function" ?
meta({ ...this.meta })
: { ...this.meta, ...meta }
return this.$.node(this.kind, {
...this.inner,
meta: newMeta
meta: typeof meta === "function" ? meta({ ...this.meta }) : meta
}) as never
}

Expand Down
2 changes: 1 addition & 1 deletion ark/schema/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ark/schema",
"version": "0.15.0",
"version": "0.16.0",
"license": "MIT",
"author": {
"name": "David Blass",
Expand Down
8 changes: 5 additions & 3 deletions ark/schema/roots/alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
implementNode,
type nodeImplementationOf
} from "../shared/implement.ts"
import { intersectNodes } from "../shared/intersections.ts"
import { intersectOrPipeNodes } from "../shared/intersections.ts"
import {
writeCyclicJsonSchemaMessage,
type JsonSchema
Expand Down Expand Up @@ -76,14 +76,16 @@ const implementation: nodeImplementationOf<Alias.Declaration> =
alias: (l, r, ctx) =>
ctx.$.lazilyResolve(
() =>
neverIfDisjoint(intersectNodes(l.resolution, r.resolution, ctx)),
neverIfDisjoint(
intersectOrPipeNodes(l.resolution, r.resolution, ctx)
),
`${l.reference}${ctx.pipe ? "=>" : "&"}${r.reference}`
),
...defineRightwardIntersections("alias", (l, r, ctx) => {
if (r.isUnknown()) return l
if (r.isNever()) return r
return ctx.$.lazilyResolve(
() => neverIfDisjoint(intersectNodes(l.resolution, r, ctx)),
() => neverIfDisjoint(intersectOrPipeNodes(l.resolution, r, ctx)),
`${l.reference}${ctx.pipe ? "=>" : "&"}${r.id}`
)
})
Expand Down
6 changes: 3 additions & 3 deletions ark/schema/roots/intersection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import {
type RefinementKind,
type StructuralKind
} from "../shared/implement.ts"
import { intersectNodes } from "../shared/intersections.ts"
import { intersectOrPipeNodes } from "../shared/intersections.ts"
import type { JsonSchema } from "../shared/jsonSchema.ts"
import type { TraverseAllows, TraverseApply } from "../shared/traversal.ts"
import {
Expand Down Expand Up @@ -238,7 +238,7 @@ const implementation: nodeImplementationOf<Intersection.Declaration> =
// if l is unknown, return r
if (l.children.length === 0) return r

const basis = l.basis ? intersectNodes(l.basis, r, ctx) : r
const basis = l.basis ? intersectOrPipeNodes(l.basis, r, ctx) : r

return (
basis instanceof Disjoint ? basis
Expand Down Expand Up @@ -380,7 +380,7 @@ const intersectIntersections = (
const basisResult =
lBasis ?
rBasis ?
(intersectNodes(
(intersectOrPipeNodes(
lBasis,
rBasis,
ctx
Expand Down
10 changes: 5 additions & 5 deletions ark/schema/roots/morph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
type nodeImplementationOf,
type RootKind
} from "../shared/implement.ts"
import { intersectNodes } from "../shared/intersections.ts"
import { intersectOrPipeNodes } from "../shared/intersections.ts"
import {
writeJsonSchemaMorphMessage,
type JsonSchema
Expand Down Expand Up @@ -96,23 +96,23 @@ const implementation: nodeImplementationOf<Morph.Declaration> =
writeMorphIntersectionMessage(l.expression, r.expression)
)
}
const inTersection = intersectNodes(l.in, r.in, ctx)
const inTersection = intersectOrPipeNodes(l.in, r.in, ctx)
if (inTersection instanceof Disjoint) return inTersection

const baseInner: Omit<mutable<Morph.Inner>, "in"> = {
morphs: l.morphs
}

if (l.declaredIn || r.declaredIn) {
const declaredIn = intersectNodes(l.in, r.in, ctx)
const declaredIn = intersectOrPipeNodes(l.in, r.in, ctx)
// we can't treat this as a normal Disjoint since it's just declared
// it should only happen if someone's essentially trying to create a broken type
if (declaredIn instanceof Disjoint) return declaredIn.throw()
else baseInner.declaredIn = declaredIn as never
}

if (l.declaredOut || r.declaredOut) {
const declaredOut = intersectNodes(l.out, r.out, ctx)
const declaredOut = intersectOrPipeNodes(l.out, r.out, ctx)
if (declaredOut instanceof Disjoint) return declaredOut.throw()
else baseInner.declaredOut = declaredOut
}
Expand All @@ -129,7 +129,7 @@ const implementation: nodeImplementationOf<Morph.Declaration> =
)
},
...defineRightwardIntersections("morph", (l, r, ctx) => {
const inTersection = intersectNodes(l.in, r, ctx)
const inTersection = intersectOrPipeNodes(l.in, r, ctx)
return inTersection instanceof Disjoint ? inTersection : (
inTersection.distribute(
branch => ({
Expand Down
5 changes: 4 additions & 1 deletion ark/schema/roots/proto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from "../shared/jsonSchema.ts"
import { $ark } from "../shared/registry.ts"
import type { TraverseAllows } from "../shared/traversal.ts"
import { isNode } from "../shared/utils.ts"
import { InternalBasis } from "./basis.ts"
import type { Domain } from "./domain.ts"

Expand Down Expand Up @@ -75,7 +76,9 @@ const implementation: nodeImplementationOf<Proto.Declaration> =
},
normalize: schema =>
typeof schema === "string" ? { proto: builtinConstructors[schema] }
: typeof schema === "function" ? { proto: schema }
: typeof schema === "function" ?
isNode(schema) ? (schema as {} as ProtoNode)
: { proto: schema }
: typeof schema.proto === "string" ?
{ ...schema, proto: builtinConstructors[schema.proto] }
: (schema as Proto.ExpandedSchema<Constructor>),
Expand Down
19 changes: 19 additions & 0 deletions ark/schema/roots/root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,25 @@ export abstract class BaseRoot<
)
}

withoutOptionalOrDefaultMeta(): this {
if (!this.optionalMeta && this.defaultMeta === unset) return this
const meta = { ...this.meta }
delete meta.default
delete meta.optional

if (
!this.hasKind("morph") ||
(!this.in.optionalMeta && this.in.defaultMeta === unset)
)
return this.withMeta(meta)

return this.$.node("morph", {
...this.inner,
in: this.in.withoutOptionalOrDefaultMeta(),
meta
}) as never
}

as(): this {
return this
}
Expand Down
7 changes: 5 additions & 2 deletions ark/schema/roots/union.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ import {
type UnionChildKind,
type nodeImplementationOf
} from "../shared/implement.ts"
import { intersectNodes, intersectNodesRoot } from "../shared/intersections.ts"
import {
intersectNodesRoot,
intersectOrPipeNodes
} from "../shared/intersections.ts"
import type { JsonSchema } from "../shared/jsonSchema.ts"
import {
$ark,
Expand Down Expand Up @@ -591,7 +594,7 @@ export const intersectBranches = (
candidatesByR = {}
break
}
const branchIntersection = intersectNodes(l[lIndex], r[rIndex], ctx)
const branchIntersection = intersectOrPipeNodes(l[lIndex], r[rIndex], ctx)
if (branchIntersection instanceof Disjoint) {
// Doesn't tell us anything useful about their relationships
// with other branches
Expand Down
12 changes: 9 additions & 3 deletions ark/schema/shared/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ export class ArkErrors extends ReadonlyArray<ArkError> {
private mutable: ArkError[] = this as never

add(error: ArkError): void {
if (this.includes(error)) return
this._add(error)
}

private _add(error: ArkError): void {
const existing = this.byPath[error.propString]
if (existing) {
const errorIntersection = new ArkError(
Expand Down Expand Up @@ -114,14 +119,15 @@ export class ArkErrors extends ReadonlyArray<ArkError> {
}

merge(errors: ArkErrors): void {
errors.forEach(e =>
this.add(
errors.forEach(e => {
if (this.includes(e)) return
this._add(
new ArkError(
{ ...e, path: [...e.path, ...this.ctx.path] } as never,
this.ctx
)
)
)
})
}

get summary(): string {
Expand Down
Loading

0 comments on commit d72778b

Please sign in to comment.