💥 feat(core): concise input declarations - #175
Merged
Merged
Conversation
A component that needs a few named inputs had to restate the enclosing
object schema, which is an implementation invariant rather than
something the author chooses.
Frontmatter now accepts a map of input names to draft-07 subschemas,
with a top-level `required` array naming the props a caller must supply:
required: [name]
inputs:
name: { type: string }
loud: { type: boolean, default: false }
Normalization wraps that in the closed object it implies, so everything
downstream keeps seeing one canonical draft-07 schema. It happens during
frontmatter parsing, which execution and inspection already share, so
imported components, root documents, `inspectDocument`, and the
generated `--props-*` options all follow with no further change.
A `type` or `$schema` key selects the full schema even when malformed,
so a broken schema is still diagnosed as one. Consequently the map form
cannot declare props named `type` or `$schema`; the full form declares
them under `properties` like any other name.
Top-level `required` is now reserved, so a document that used it as
metadata must move it under `meta.required`. `inputs: {}` changes from
invalid to the empty declaration.
|
|
||
| // `type` and `$schema` mark a full schema even when their values are | ||
| // malformed, so a broken full schema is diagnosed as one rather than | ||
| // read as a map of properties named `type` or `$schema`. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
Suggested change
| // read as a map of properties named `type` or `$schema`. |
| throw new Error('frontmatter "required" must list input names as strings'); | ||
| } | ||
| // An inputs map is closed, so a name it does not declare could never be | ||
| // supplied and the schema would be impossible to satisfy. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
Suggested change
| // supplied and the schema would be impossible to satisfy. |
| // `inputs` is the component's JSON Schema. Absent → the closed | ||
| // empty-object schema. A fresh object per component keeps the | ||
| // `inputs` is the component's JSON Schema, in either spelling. Absent → | ||
| // the closed empty-object schema. A fresh object per component keeps the |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
Suggested change
| // the closed empty-object schema. A fresh object per component keeps the |
PR #175: 💥 feat(core): concise input declarations6 files, +526 / -59 Scope🟡 585 lines changed. PRs under 400 receive more thorough review. Structural✅ No structural bloat detected. Slop✅ Slop indicators look low. Static Analysis✅ Oxlint found no issues. CorrectnessNo extraneous code patterns detected. |
The frontmatter pseudocode showed execution paths the implementation rejects: it normalized without validating `required`, accepted any property value, and merged a top-level `required` into a full schema. It now expresses every rule the parser enforces. Tier B ids match the tests that carry them: B16 normalization, B17 `required` entering the schema rather than the metadata, B18 the mixed declaration. Concise/full parity is descriptive and claims no id. RS6 asserts the diagnostic an omitted required property produces. A component's prop failure is collected into the output rather than aborting the run, so the error segment is the observable, not the execution status — the same for both spellings.
|
|
||
| // `type` and `$schema` mark a full schema even when their values are | ||
| // malformed, so a broken full schema is diagnosed as one rather than | ||
| // read as a map of properties named `type` or `$schema`. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
Suggested change
| // read as a map of properties named `type` or `$schema`. |
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Closes #172.
A Markdown component that needs a few named inputs has to restate the enclosing object schema:
The object shape is an implementation invariant, not information the author is choosing.
What changes
Before — the only spelling was the full schema above.
After — frontmatter also accepts a map of input names to draft-07 subschemas, with a top-level
requiredarray:Both spellings declare the same component. Property definitions remain draft-07 JSON Schema; only the enclosing closed object becomes implicit.
How it works
Normalization happens during frontmatter parsing, which execution and inspection already share through
parseMarkdownDefinition. Imported components, root documents,inspectDocument, and the generated--props-*options therefore all follow with no further change —packages/cliis untouched.A
typeor$schemakey selects the full schema, even when its value is malformed, so a broken schema is still diagnosed as one rather than read as a map of props namedtype. Two consequences: the map form cannot declare props namedtypeor$schema(the full form declares them underpropertieslike any other name), and the map form never carries$schema, because it is draft-07 by construction.Review guide
Start with:
packages/core/src/frontmatter.ts—parseInputSchemaandnormalizeInputs.Then review:
packages/core/tests/frontmatter.test.ts— classification and normalizationpackages/core/tests/root-props.test.tsTier RS — behavioral parityspecs/executable-mdx-spec.md— the "Input definitions" rewriteLook carefully at:
frontmatter.ts, notcompileInputSchema. A function component'sinputsexport never passes through frontmatter, so putting it in the compiler would silently extend the map form to TypeScript components.compileInputSchemacaches validators in aWeakMapkeyed by schema identity, so a shared object would leak compiled state across definitions.What must stay true
additionalProperties: false, checked byRS4.slot/asare still rejected — enforced byenforceRootContractrunning after normalization, checked invalidation-integration.test.ts..tspath not callingparseFrontmatter.How to verify it
B16/B17prove a map normalizes to the exact full form and that both spellings produce equal schemas; they fail if normalization dropsrequiredoradditionalProperties.RS2runs both spellings end to end and compares output, proving required properties and recursive defaults behave identically.RS6proves an imported map-form component validates like the root.RS7provesinspectDocumentreturns the normalized schema, which is why the CLI needs no change.parseFrontmatter().inputsintocompileInputSchema, asserting errors at the boundary that actually produces them.Manual:
Scope
Included
parseFrontmatter, reserving top-levelrequiredexecutable-mdx-spec.mdandroot-document-inputs-spec.mdIntentionally unchanged
inputsexport; the map form is a frontmatter spelling.component-schema-conformance.test.tswalks them as the regression net.Risks and limitations
This is a backward-incompatible frontmatter-language change. Every top-level key except
inputspreviously became metadata:requiredis now reserved for concise input declarations;meta.required;inputs: {}changes from invalid to the empty concise declaration.No document in this repository uses top-level
required, but that only means this repository needs no migration — documents elsewhere may.Scope confirmation