feat: support dynamicRef/dynamicAnchor (JSON Schema 2020-12) (#337)
- feat: resolve dynamicRefasastaticref and register $dynamicAnchor (2020-12)
Implement the first half of JSON Schema 2020-12 dynamicRef/dynamicAnchor support:
- Register dynamicAnchornamesasordinaryanchorssoareferencecanresolvetothem,exactlylikeanchor.
- Resolve dynamicReftoitsinitial(lexically−static)targetandvalidatetheinstanceagainstit,sodynamicRef behaves like a plain reftothelocationitinitiallypointsat.Thetargetiskeptashidden,non−enumerablemetadata(dynamicRefTarget) so sibling keywords on the reference are preserved.
- Remove the previous ‘unsupported feature’ warning for dynamicRef/dynamicAnchor and update the corresponding tests.
True dynamic-scope (bookended) resolution is added in a follow-up commit; the 7 official test-suite cases that require it are temporarily skipped.
- feat: add dynamic-scope resolution for $dynamicRef (2020-12)
Complete JSON Schema 2020-12 $dynamicRef support with true dynamic-scope (“bookended”) resolution:
- Add a pre-pass (collectDynamicAnchors) that, from the original schema tree before refmergingflattensresourceboundaries,buildsper−resourceanchormaps(anchorMaps: dynamic-only and all-anchor) and records each dynamicRef′slexicalresource(indynamicRefInfo.scope).
- Resolve an internal $dynamicRef’s initial target within its own lexical resource so a sibling resource’s identically-named anchor cannot leak in.
- At validation time, apply the bookending rule: when the initial target is a dynamicAnchorofthereferencedname,resolvetotheoutermostmatchingdynamicAnchor currently in the dynamic scope; otherwise behave like a plain $ref.
- Apply $dynamicRef alongside any sibling keywords on the same node, rather than treating it as a redirect that replaces them.
- Propagate the hidden dynamicRefmetadatathroughschemamergingsoadynamicRef living directly on a $ref’d resource still resolves.
- Ignore $dynamicRef as an unknown keyword in drafts before 2020-12.
- Store the internal, non-enumerable metadata in two consolidated records (DynamicRefInfo and AnchorMaps) rather than five separate hidden fields.
All 32 official dynamicRef test-suite cases now pass; the temporary skips added in the previous commit are removed.
- test: cover dynamicRef/dynamicAnchor edge cases (2020-12)
Add project-owned tests for the gaps found in the $dynamicRef coverage review, so the behavior no longer relies solely on the vendored official suite:
- a sub-schema shared by reference is anchor-collected once (shared-node guard) and raises no spurious duplicate-anchor error
- a duplicate $dynamicAnchor within one resource is reported as a schema resolution error
- genuine dynamic-scope resolution: an outer $dynamicAnchor overrides a base resource’s default (extendible pattern)
- a dynamicReftoaplainanchor (not bookended) behaves like a plain $ref
- a dynamicRefwithaJSON−pointerfragmentbehaveslikeaplainref
- a $dynamicRef resolved through an external schema
- $dynamicRef is ignored as an unknown keyword in draft 2019-09
- completion and hover resolve through a $dynamicRef node
- fix: extend $dynamicRef dynamic scope across external documents (2020-12)
The dynamic-scope pre-pass (collectDynamicAnchors) ran only over the root document’s resources, so a dynamicAnchorthatoverridesabasedefaultwashonoredonlywhenitlivedinthedocumentbeingvalidated.Whentheextendingresourcelivedina∗separate∗documentreachedviaref, its $dynamicAnchor never joined the dynamic scope and the override was lost.
- Collect each referenced document’s own anchor maps (once) during external link resolution, before refmergingflattensresourceboundaries,soitsdynamicAnchor declarations can participate in the dynamic scope and its $dynamicRef nodes get their lexical resource recorded.
- Fold a merged section’s anchor maps into the target during merge, with outermost-resource-wins precedence, so a dynamicAnchordefinedinanexternal(orref’d sub-) resource can override a base default while the base’s own entries keep priority.
- Derive the effective draft from each resource’s own schemasoareferencedpre−2020−12documentisnotgivendynamicAnchor semantics.
Tests cover the cross-document override for both an internal-fragment (dynamicRef "#name") and a non-fragment URI (dynamicRef “other#name”) starting point. Each case uses a fresh service and a fresh schema object: in-place $ref resolution mutates the schema it is given, so reusing one object would let a prior validation leak anchor state and mask the gap.
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com
- fix: correct reference-scope handling for 2019-09/2020-12 test suite
Bump the vendored JSON-Schema-Test-Suite from 69acf52 to 92acb61 and fix the spec-compliance gaps it surfaced. The full suite now passes with no skipped assertions.
- $recursiveRef no longer early-returns, so sibling unevaluatedItems / unevaluatedProperties still evaluate against the node and accumulate their processed-property annotations.
- Resolve defs/definitionsbeforeif/then/else/itemsduringtraversalsoareferencedresourcefinishesassemblingbeforeareferrermergesit.FixesadynamicRef chain reached through then: { $ref: … } capturing a half-resolved snapshot.
- Isolate into an allOf (instead of flatten-merging) when both schemas constrain array items, or when a referencing schema’s unevaluatedItems would otherwise capture a referenced schema’s positional items and its internal self-references.
- Keep recursiveAnchor/dynamicAnchor and unevaluated* on the allOf wrapper so bookending targets stay discoverable and unevaluated* observes the aggregated annotations of every in-place applicator.
- Treat a boolean subschema (true/false) as a real schema when following a $ref, not as a missing section.
- Register a referenced document’s embedded idresourcesasresolvablehandles,andjoinafragment−referencedresource′sdynamicAnchor names to the referencing dynamic scope.
- Fold only the dynamic anchor map across resources; the local map stays per-resource so a sibling’s identically-named anchor cannot leak into an internal $dynamicRef’s lexical resolution.
- update changelog
Co-authored-by: Keegan Caruso keegancaruso@microsoft.com Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com Co-authored-by: Martin Aeschlimann martinae@microsoft.com
版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9
京公网安备 11010802047560号
vscode-json-languageservice
JSON language service extracted from VSCode to be reused, e.g in the Monaco editor.
Why?
The vscode-json-languageservice contains the language smarts behind the JSON editing experience of Visual Studio Code and the Monaco editor.
For the complete API see jsonLanguageService.ts and jsonLanguageTypes.ts
Installation
Sample usage
See sample.ts for an example on how to use the JSON language service.
To run the sample use
yarn sampleDevelopment
Use
yarn testto compile and run testsHow can I run and debug the service?
jsonCompletion.tsHow can I run and debug the service inside an instance of VSCode?
yarn link vscode-json-languageserviceinvscode/extensions/json-language-features/serverto run VSCode with the latest changes fromvscode-json-languageservicevscode/scripts/code.sh|bat) and open a.jsonfilevscode-json-languageservicesources, run commandDebug: Attach to Node processand pick thecode-ossprocess with thejson-language-featurespathjsonCompletion.ts.jsonfile