Skip to content

Commit

Permalink
feat: narrowed mapping with defaults, narrowed .props (#1133)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssalbdivad authored Sep 17, 2024
1 parent 24df831 commit 098409b
Show file tree
Hide file tree
Showing 139 changed files with 1,067 additions and 726 deletions.
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
// should just import as * as ts when we need it in attest
"typescript"
],
"typescript.preferences.autoImportSpecifierExcludeRegexes": [
// has a "type" export
"^(node:)?os$"
],
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
"typescript.tsdk": "./node_modules/typescript/lib",
// IF YOU UPDATE THE MOCHA CONFIG HERE, PLEASE ALSO UPDATE package.json/mocha AND ark/repo/mocha.jsonc
Expand Down
24 changes: 16 additions & 8 deletions ark/attest/assert/assertions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { printable, throwInternalError } from "@ark/util"
import type { type } from "arktype"
import { AssertionError } from "node:assert"
import * as assert from "node:assert/strict"
import type { TypeRelationshipAssertionData } from "../cache/writeAssertionCache.ts"
import type { AssertionContext } from "./attest.ts"
Expand All @@ -9,15 +8,15 @@ export type ThrowAssertionErrorContext = {
message: string
expected?: unknown
actual?: unknown
ctx: AssertionContext
stack: string
}

export const throwAssertionError = ({
ctx,
stack,
...errorArgs
}: ThrowAssertionErrorContext): never => {
const e = new assert.AssertionError(errorArgs)
e.stack = ctx.assertionStack
e.stack = stack
throw e
}

Expand Down Expand Up @@ -68,7 +67,12 @@ export const versionableAssertion =
} catch (e) {
errorMessage += `❌TypeScript@${version}:${e}\n`
}
if (errorMessage) throw new AssertionError({ message: errorMessage })
if (errorMessage) {
throwAssertionError({
stack: ctx.assertionStack,
message: errorMessage
})
}
}
} else fn(expected, actual, ctx)
}
Expand Down Expand Up @@ -136,7 +140,7 @@ export const typeEqualityMapping: TypeAssertionMapping =

export const assertEqualOrMatching: AssertFn = versionableAssertion(
(expected, actual, ctx) => {
const assertionArgs = { actual, expected, ctx }
const assertionArgs = { actual, expected, stack: ctx.assertionStack }
if (typeof actual !== "string") {
throwAssertionError({
message: `Value was of type ${typeof actual} (expected a string).`,
Expand Down Expand Up @@ -172,8 +176,12 @@ export const getThrownMessage = (
result: AssertedFnCallResult,
ctx: AssertionContext
): string | undefined => {
if (!("threw" in result))
throwAssertionError({ message: "Function didn't throw.", ctx })
if (!("threw" in result)) {
throwAssertionError({
message: "Function didn't throw",
stack: ctx.assertionStack
})
}

return result.threw
}
Expand Down
6 changes: 5 additions & 1 deletion ark/attest/assert/attest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ export const attest: AttestFn = Object.assign(attestInternal, {
const ctx = getBenchCtx([calledFrom.file])
ctx.benchCallPosition = calledFrom
ctx.lastSnapCallPosition = calledFrom
instantiationDataHandler({ ...ctx, kind: "instantiations" }, args, false)
instantiationDataHandler(
{ ...ctx, lastSnapFunctionName: "instantiations" },
args,
false
)
}
}) as never
13 changes: 7 additions & 6 deletions ark/attest/assert/chainableAssertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export class ChainableAssertions implements AssertionRecord {
instanceOf(expected: Constructor): this {
if (!(this.ctx.actual instanceof expected)) {
throwAssertionError({
ctx: this.ctx,
stack: this.ctx.assertionStack,
message: `Expected an instance of ${expected.name} (was ${
typeof this.ctx.actual === "object" && this.ctx.actual !== null ?
this.ctx.actual.constructor.name
Expand Down Expand Up @@ -199,7 +199,12 @@ export class ChainableAssertions implements AssertionRecord {
if (this.ctx.cfg.skipTypes) return chainableNoOpProxy

this.ctx.actual = new TypeAssertionMapping(data => {
checkCompletionsForErrors(data.completions)
if (typeof data.completions === "string") {
// if the completions were ambiguously defined, e.g. two string
// literals with the same value, they are writen as an error
// message to the JSON. Throw it immediately.
throw new Error(data.completions)
}
return { actual: data.completions }
})
this.ctx.lastSnapName = "completions"
Expand Down Expand Up @@ -233,10 +238,6 @@ export class ChainableAssertions implements AssertionRecord {
}
}

const checkCompletionsForErrors = (completions?: Completions) => {
if (typeof completions === "string") throw new Error(completions)
}

const declarationPrefix = "type T = "

const formatTypeString = (typeString: string) =>
Expand Down
21 changes: 13 additions & 8 deletions ark/attest/bench/baseline.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { snapshot } from "@ark/util"
import { AssertionError } from "node:assert"
import { snapshot, throwInternalError } from "@ark/util"
import process from "node:process"
import { throwAssertionError } from "../assert/assertions.ts"
import {
queueSnapshotUpdate,
writeSnapshotUpdatesOnExit
Expand All @@ -23,18 +23,23 @@ export const queueBaselineUpdateIfNeeded = (

const serializedValue = snapshot(updated)
if (!ctx.lastSnapCallPosition) {
throw new Error(
`Unable to update baseline for ${ctx.qualifiedName} ('lastSnapCallPosition' was unset).`
throwInternalError(
`Unable to update baseline for ${ctx.qualifiedName} ('lastSnapCallPosition' was unset)`
)
}
if (!ctx.lastSnapFunctionName) {
throwInternalError(
`Unable to update baseline for ${ctx.qualifiedName} ('lastSnapFunctionName' was unset)`
)
}
queueSnapshotUpdate({
position: ctx.lastSnapCallPosition,
serializedValue,
snapFunctionName: ctx.kind,
snapFunctionName: ctx.lastSnapFunctionName,
baselinePath: ctx.qualifiedPath
})

if (ctx.kind === "types") writeSnapshotUpdatesOnExit()
if (ctx.lastSnapFunctionName === "types") writeSnapshotUpdatesOnExit()
}

/** Pretty print comparison and set the process.exitCode to 1 if delta threshold is exceeded */
Expand Down Expand Up @@ -63,8 +68,8 @@ const handlePositiveDelta = (formattedDelta: string, ctx: BenchContext) => {
console.error(`📈 ${message}`)
if (ctx.cfg.benchErrorOnThresholdExceeded) {
const errorSummary = `❌ ${message}`
if (ctx.kind === "instantiations")
throw new AssertionError({ message: errorSummary })
if (ctx.lastSnapFunctionName === "instantiations")
throwAssertionError({ stack: ctx.assertionStack, message: errorSummary })
else {
process.exitCode = 1
// Summarize failures at the end of output
Expand Down
30 changes: 16 additions & 14 deletions ark/attest/bench/bench.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { caller, rmRf, type SourcePosition } from "@ark/fs"
import { caller, getCallStack, rmRf, type SourcePosition } from "@ark/fs"
import { performance } from "node:perf_hooks"
import {
ensureCacheDirs,
Expand Down Expand Up @@ -204,7 +204,7 @@ export class BenchAssertions<
console.groupEnd()
queueBaselineUpdateIfNeeded(createTimeMeasure(ms), baseline, {
...this.ctx,
kind: name
lastSnapFunctionName: name
})
return this.getNextAssertions()
}
Expand Down Expand Up @@ -232,7 +232,7 @@ export class BenchAssertions<
console.groupEnd()
queueBaselineUpdateIfNeeded(markResults, baseline, {
...this.ctx,
kind: "mark"
lastSnapFunctionName: "mark"
})
return this.getNextAssertions()
}
Expand Down Expand Up @@ -321,10 +321,11 @@ export type BenchContext = {
qualifiedName: string
options: InternalBenchOptions
cfg: ParsedAttestConfig
assertionStack: string
benchCallPosition: SourcePosition
lastSnapCallPosition: SourcePosition | undefined
lastSnapFunctionName: string | undefined
isAsync: boolean
kind: TimeAssertionName | "types" | "instantiations"
}

export type BenchableFunction = () => unknown | Promise<unknown>
Expand All @@ -347,13 +348,14 @@ export const getBenchCtx = (
qualifiedPath: string[],
isAsync: boolean = false,
options: BenchOptions = {}
): BenchContext =>
({
qualifiedPath,
qualifiedName: qualifiedPath.join("/"),
options,
cfg: getConfig(),
benchCallPosition: caller(),
lastSnapCallPosition: undefined,
isAsync
}) as BenchContext
): BenchContext => ({
qualifiedPath,
qualifiedName: qualifiedPath.join("/"),
options,
cfg: getConfig(),
benchCallPosition: caller(),
lastSnapCallPosition: undefined,
lastSnapFunctionName: undefined,
isAsync,
assertionStack: getCallStack({ offset: 1 }).join("\n")
})
5 changes: 3 additions & 2 deletions ark/attest/bench/type.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { caller } from "@ark/fs"
import { throwInternalError } from "@ark/util"
import ts from "typescript"
import { getBenchAssertionsAtPosition } from "../cache/getCachedAssertions.ts"
import {
Expand Down Expand Up @@ -31,7 +32,7 @@ export const createBenchTypeAssertion = (
): BenchTypeAssertions => ({
types: (...args: [instantiations?: Measure<TypeUnit> | undefined]) => {
ctx.lastSnapCallPosition = caller()
instantiationDataHandler({ ...ctx, kind: "types" }, args[0])
instantiationDataHandler({ ...ctx, lastSnapFunctionName: "types" }, args[0])
}
})

Expand Down Expand Up @@ -60,7 +61,7 @@ export const getContributedInstantiations = (ctx: BenchContext): number => {
) as ts.ArrowFunction | ts.FunctionExpression | undefined

if (!body)
throw new Error("Unable to retrieve contents of the call expression")
throwInternalError("Unable to retrieve contents of the call expression")

return getInstantiationsContributedByNode(file, body)
}
Expand Down
3 changes: 2 additions & 1 deletion ark/attest/cache/snapshots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
writeJson,
type SourcePosition
} from "@ark/fs"
import { throwInternalError } from "@ark/util"
import { existsSync } from "node:fs"
import { basename, dirname, isAbsolute, join } from "node:path"
import type ts from "typescript"
Expand Down Expand Up @@ -86,7 +87,7 @@ const findCallExpressionAncestor = (
const calls = getCallExpressionsByName(startNode, [functionName], true)
if (calls.length) return startNode

throw new Error(
throwInternalError(
`Unable to locate expected inline ${functionName} call from assertion at ${positionToString(
position
)}.`
Expand Down
12 changes: 6 additions & 6 deletions ark/attest/cache/ts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { fromCwd, type SourcePosition } from "@ark/fs"
import { throwInternalError, type dict } from "@ark/util"
import { printable, throwInternalError, type dict } from "@ark/util"
import * as tsvfs from "@typescript/vfs"
import { readFileSync } from "node:fs"
import { dirname, join } from "node:path"
Expand Down Expand Up @@ -53,7 +53,7 @@ export class TsServer {
getSourceFileOrThrow(path: string): ts.SourceFile {
const tsPath = path.replaceAll(/\\/g, "/")
const file = this.virtualEnv.getSourceFile(tsPath)
if (!file) throw new Error(`Could not find ${path}.`)
if (!file) throwInternalError(`Could not find TS path ${path}`)

return file
}
Expand All @@ -65,7 +65,7 @@ export const nearestCallExpressionChild = (
): ts.CallExpression => {
const result = nearestBoundingCallExpression(node, position)
if (!result) {
throw new Error(
throwInternalError(
`Unable to find bounding call expression at position ${position} in ${
node.getSourceFile().fileName
}`
Expand Down Expand Up @@ -97,8 +97,8 @@ export const getAbsolutePosition = (
position.char - 1
)
if (!pos) {
throw new Error(
`Absolute position was not able to be found in ${file.fileName}`
throwInternalError(
`Absolute position ${printable(position)} does not exist in ${file.fileName}`
)
}
return pos
Expand All @@ -116,7 +116,7 @@ export const getTsConfigInfoOrThrow = (): TsconfigInfo => {
tsconfig ?? ts.findConfigFile(fromCwd(), ts.sys.fileExists, "tsconfig.json")
if (!configFilePath) {
throw new Error(
`File ${tsconfig ?? join(fromCwd(), "tsconfig.json")} must exist.`
`File ${tsconfig ?? join(fromCwd(), "tsconfig.json")} must exist`
)
}

Expand Down
8 changes: 7 additions & 1 deletion ark/attest/cache/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { filePath } from "@ark/fs"
import { throwInternalError } from "@ark/util"
import * as tsvfs from "@typescript/vfs"
import ts from "typescript"
import { getConfig } from "../config.ts"
Expand Down Expand Up @@ -68,7 +69,12 @@ export const gatherInlineInstantiationData = (
const body = getDescendants(enclosingFunction.ancestor).find(
node => ts.isArrowFunction(node) || ts.isFunctionExpression(node)
) as ts.ArrowFunction | ts.FunctionExpression | undefined
if (!body) throw new Error("Unable to find file contents")
if (!body) {
throwInternalError(
`Unable to resolve source associated with TS Node:
${enclosingFunction.ancestor.getText()}`
)
}

return {
location: enclosingFunction.position,
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.18.1",
"version": "0.18.2",
"author": {
"name": "David Blass",
"email": "[email protected]",
Expand Down
2 changes: 1 addition & 1 deletion ark/dark/injected.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
},
"arkChained": {
"contentName": "meta.embedded.arktype.definition",
"begin": "([^\\)\\(\\s]+)?(\\.)\\b(and|or|when|equals|ifEquals|extends|ifExtends|intersect|exclude|extract|overlaps|subsumes|to)(\\()",
"begin": "([^\\)\\(\\s]+)?(\\.)\\b(and|or|when|extends|ifExtends|intersect|exclude|extract|overlaps|subsumes|to)(\\()",
"beginCaptures": {
"2": {
"name": "punctuation.accessor.ts"
Expand Down
2 changes: 1 addition & 1 deletion ark/dark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "arkdark",
"displayName": "ArkDark",
"description": "Syntax highlighting, inline errors and theme for ArkType⛵",
"version": "5.12.1",
"version": "5.12.2",
"publisher": "arktypeio",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion ark/dark/tsWithArkType.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
},
"arkChained": {
"contentName": "meta.embedded.arktype.definition",
"begin": "([^\\)\\(\\s]+)?(\\.)\\b(and|or|when|equals|ifEquals|extends|ifExtends|intersect|exclude|extract|overlaps|subsumes|to)(\\()",
"begin": "([^\\)\\(\\s]+)?(\\.)\\b(and|or|when|extends|ifExtends|intersect|exclude|extract|overlaps|subsumes|to)(\\()",
"beginCaptures": {
"2": {
"name": "punctuation.accessor.ts"
Expand Down
8 changes: 4 additions & 4 deletions ark/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
"@astrojs/react": "3.6.2",
"@astrojs/starlight": "0.27.1",
"@astrojs/ts-plugin": "1.10.2",
"@shikijs/transformers": "1.17.6",
"@shikijs/twoslash": "1.17.6",
"@shikijs/transformers": "1.17.7",
"@shikijs/twoslash": "1.17.7",
"arkdark": "workspace:*",
"arktype": "workspace:*",
"astro": "4.15.6",
Expand All @@ -28,11 +28,11 @@
"react": "18.3.1",
"react-dom": "18.3.1",
"sharp": "0.33.5",
"shiki": "1.17.6",
"shiki": "1.17.7",
"twoslash": "0.2.11"
},
"devDependencies": {
"@types/react": "18.3.5",
"@types/react": "18.3.7",
"@types/react-dom": "18.3.0",
"typescript": "catalog:"
}
Expand Down
Loading

0 comments on commit 098409b

Please sign in to comment.