💥 feat(core): declare component props under "props" - #179
Merged
Conversation
A component declared caller-supplied values under `inputs`, but callers passed them as props and bodies read them through `props`. The same concept had two public names. Rename the declaration to match invocation and consumption: - Markdown frontmatter `inputs:` → `props:` - function component `export const inputs` → `export const props` - `ComponentDefinition.inputs` / `FunctionComponentDefinition.inputs` → `.props` - `inspectDocument().inputs` → `.props` - `InputSchema` → `PropsSchema`, `InputSchemaError` → `PropsSchemaError`, `compileInputSchema` → `compilePropsSchema` - CLI schema variables → `propsSchema`, keeping `props` for resolved values This also settles the vocabulary #176 builds on: `meta` is component-owned configuration, `props` are caller-supplied, `content` is rendered Markdown, and `returns` will declare a component's return value. `returns` is not implemented here. `inputs` is not retained as a compatibility alias. A full `inputs:` schema now falls through to the ordinary meta rules, leaving the component with the default closed empty-object schema; a concise `inputs:` map with a top-level `required:` fails parsing, because `required` stays reserved. Both paths are covered by tests, as is the equivalent `export const inputs` removal. The generated `--props-*` options, `XMD_PROPS_*`, aggregate props, and programmatic `props` are unchanged, as is the concise/full schema behavior from #175. Closes #177
Test comments described the rename that produced the contract rather than the contract itself. Replace the #177 narration with present-tense statements of the surprising behavior, and rename the validation test to name its outcome instead of what a component "used to accept". Also correct `DocumentInfo.meta`, which excludes both reserved keys (`props` and `required`), fix the "an props map" test title, and describe the root-document sources as command-line options and environment variables rather than "environment props".
6 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
A component declared caller-supplied values under
inputs, but callers passed them as props and component bodies read them throughprops. The same concept had two public names, so the declaration never matched invocation or consumption.This also settles the vocabulary #176 builds on:
metais component-owned configuration,propsare caller-supplied,contentis rendered Markdown, andreturnswill declare a component's return value.Closes #177.
What changes
Before:
Hello, {props.name}After:
Hello, {props.name}The same term now spans declaration, invocation, and consumption:
inputs:→props:export const inputs→export const propsComponentDefinition.inputs/FunctionComponentDefinition.inputs→.propsinspectDocument().inputs→.propsInputSchema→PropsSchema,InputSchemaError→PropsSchemaError,compileInputSchema→compilePropsSchemaHow it works
packages/core/src/frontmatter.tsreservespropsandrequired; everything else is meta.definition.tsandexecute.tscompile the resulting schema for Markdown and function components respectively, andexpand.tsvalidates caller props against it.CLI internals take
propsSchemarather thanprops, becauseresolvePropsalready bindspropsto the resolved values — reusing the name would shadow it.Review guide
Start with:
packages/core/src/frontmatter.tsThen review:
packages/core/src/validate.ts—compilePropsSchema,PropsSchemaError, and the reworded diagnosticspackages/core/src/{definition,execute,expand,inspect}.ts— the two compile sites and the two validation sitespackages/cli/src/{props,cli}.ts—propsSchemathreadingspecs/executable-mdx-spec.md— §5.1 and the acceptance/invariant tablesLook carefully at:
What must stay true
inputscompatibility alias. Enforced byPROPS_KEYS = ["props", "required"]infrontmatter.tsand by"props" in modinexecute.ts; checked by the alias tests infrontmatter.test.ts,validation-integration.test.ts, andfunction-components.test.ts.--props-*,XMD_PROPS_*,--props, andXMD_PROPSkeep their spelling; checked bypackages/cli/tests/props-{cli,sources,schema}.test.ts.How to verify it
frontmatter.test.ts"treats a fullinputsschema as ordinary metadata" proves a full old-form declaration degrades tometa.inputsplus the default closed empty-object schema, and fails if an alias were reintroduced.frontmatter.test.ts"rejects a conciseinputsmap declared with a top-levelrequired" provesrequiredstays reserved, and fails if the concise old form were silently accepted.validation-integration.test.ts"a component declaringinputsrejects the prop it used to accept" proves the end-to-end consequence: the document parses, then fails at prop validation.function-components.test.tsFC-alias(two cases) provesexport const inputsdeclares nothing whileexport const propsvalidates normally — catching an undocumented alias on the function-component path.Local results with the CI-pinned Deno 2.9.1:
Note:
packages/cli/tests/agent-cli.test.ts(Tier CA) fails under Deno 2.9.4 on this branch and onmain— it is unmodified here and references neitherpropsnorinputs. It passes on the CI-pinned 2.9.1.Scope
Included
specs/root-document-inputs-spec.md→specs/root-document-props-spec.mdandsmoke-test/Guide/TypedInputs.md→TypedProps.md.Intentionally unchanged
returns(Support schema-validated component returns #176) is not implemented — this PR only reserves the vocabulary for it.packages/acp/,packages/durable-streams/, interpolation inputs, the Standard Schemainputparameter,JsonSchemaInput(the parameter type ofz.fromJSONSchema), and GitHub Actionsworkflow_callinputs..github/workflows/review.ymlandrepo-analysis.ymlstill install the released binary — see Risks.Generated or mechanical changes
inputs:→props:frontmatter change across.reviews/**(32 files),packages/core/components/**(5), andsmoke-test/**(13) is mechanical with no intended behavior change.specs/code-review-agent-spec.mdandspecs/oxlint-sensor-spec.mdare frontmatter examples only.Risks and limitations
reviewcheck on this PR is expected to be red, andrepo-analysiswill be broken onmainafter merge. Both workflowscurl | shthe latest published xmd and run it over.reviews/**andpackages/core/components/**. The released 0.5.2 binary readsprops:as ordinary meta, so every component gets a closed empty-object schema and rejects every prop. This is the accepted trade-off rather than adding a compatibility alias.deno task build) until the release ships.Scope confirmation