Skip to content

Support map properties#22

Merged
skonves merged 2 commits into
mainfrom
additional-properties
Mar 22, 2026
Merged

Support map properties#22
skonves merged 2 commits into
mainfrom
additional-properties

Conversation

@skonves

@skonves skonves commented Mar 22, 2026

Copy link
Copy Markdown
Member

Resolves #9

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds parsing support for JSON Schema additionalProperties as Basketry mapProperties and extends union parsing to support anyOf (plus explicit disjunction metadata), addressing Issue #9.

Changes:

  • Implement mapProperties parsing from additionalProperties (boolean and schema forms), including handling inline enum/union naming for map values.
  • Implement anyOf unions as SimpleUnion with inclusive disjunction and add exclusive disjunction to oneOf unions.
  • Add a comprehensive spec test file for map properties and update the snapshot + ESLint glob quoting.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
src/spec/4.1-structure/4.1.11-map-properties.spec.ts Adds spec-level tests covering additionalProperties map parsing across boolean/ref/enum/oneOf/anyOf cases.
src/json-schema-parser.ts Introduces parseMapProperties / parseMapValue, implements anyOf union parsing, and adds union disjunction metadata.
src/snapshot/snapshot.json Updates snapshot output to include the new disjunction field for a SimpleUnion.
package.json Quotes ESLint globs to avoid shell expansion issues.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/json-schema-parser.ts
Comment on lines +662 to +703
private parseMapProperties(
schema: AST.AbstractSchemaNode,
typeName: StringLiteral,
): MapProperties | undefined {
const ap = schema.additionalProperties;
if (!ap) return undefined;

// Handle boolean literal additionalProperties (true/false)
if (isLiteralNode(ap.node)) {
// false is handled by objectAdditionalPropertiesRule; true means untyped map
if (ap.node.value !== true) return undefined;
return {
kind: 'MapProperties',
key: {
kind: 'MapKey',
value: {
kind: 'PrimitiveValue',
typeName: { kind: 'PrimitiveLiteral', value: 'string' },
rules: [],
},
},
requiredKeys: [],
value: {
kind: 'MapValue',
value: untyped(),
},
};
}

return {
kind: 'MapProperties',
key: {
kind: 'MapKey',
value: {
kind: 'PrimitiveValue',
typeName: { kind: 'PrimitiveLiteral', value: 'string' },
rules: [],
},
},
requiredKeys: [],
value: this.parseMapValue(ap, typeName.value),
};

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parseMapProperties/parseMapValue add several behaviors (e.g., additionalProperties: { type: 'string' } without enum, and inline object schemas under additionalProperties) that aren’t exercised by the new spec tests. Adding at least one test for a primitive-typed map and one for an inline object map value would help prevent regressions in these branches.

Copilot uses AI. Check for mistakes.
Comment on lines +569 to +577
const exactSet = new Set<unknown>();
function exact<T>(input: T): T {
exactSet.add(input);
return input;
}

function partial<T = any>(input: DeepPartial<T>): any {
if (exactSet.has(input)) return input;

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exactSet/exact() helper is declared but never used in this spec file, which adds noise and makes the assertions harder to scan. Consider removing it, or using it in at least one assertion so future readers know why it exists.

Suggested change
const exactSet = new Set<unknown>();
function exact<T>(input: T): T {
exactSet.add(input);
return input;
}
function partial<T = any>(input: DeepPartial<T>): any {
if (exactSet.has(input)) return input;
function partial<T = any>(input: DeepPartial<T>): any {

Copilot uses AI. Check for mistakes.
Comment thread src/json-schema-parser.ts

// Handle boolean literal additionalProperties (true/false)
if (isLiteralNode(ap.node)) {
// false is handled by objectAdditionalPropertiesRule; true means untyped map

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says additionalProperties: false is handled by objectAdditionalPropertiesRule, but objectAdditionalPropertiesFactory currently returns undefined (TODO), so the parser won’t emit any rule/violation for additionalProperties: false. Either update this comment to reflect current behavior, or implement the missing additionalProperties rule so false is enforced.

Suggested change
// false is handled by objectAdditionalPropertiesRule; true means untyped map
// currently, only `true` produces an untyped map; `false` results in no MapProperties/rule

Copilot uses AI. Check for mistakes.
Comment thread src/json-schema-parser.ts
Comment on lines +716 to +721
const desiredName =
schema.oneOf || schema.anyOf
? `${parentName}MapValues`
: schema.enum
? `${parentName}MapValue`
: `${parentName}MapValues`;

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

desiredName’s fallback branch duplicates the union case (... : ${parentName}MapValues``), which makes the ternary harder to reason about and may be unintended (e.g., inline non-union complex schemas would also be renamed to *MapValues). Consider simplifying the conditional and/or choosing distinct singular/plural names for the non-union case to make the intent explicit.

Suggested change
const desiredName =
schema.oneOf || schema.anyOf
? `${parentName}MapValues`
: schema.enum
? `${parentName}MapValue`
: `${parentName}MapValues`;
const hasUnion = !!(schema.oneOf || schema.anyOf);
const desiredName =
schema.enum && !hasUnion
? `${parentName}MapValue`
: `${parentName}MapValues`;

Copilot uses AI. Check for mistakes.
@skonves skonves force-pushed the additional-properties branch from 922bc7a to 83a5a96 Compare March 22, 2026 23:47
@skonves skonves merged commit dd39ba6 into main Mar 22, 2026
6 checks passed
@skonves skonves deleted the additional-properties branch March 22, 2026 23:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parse Map Properties

2 participants