From 49fb399e1ec7ddefff703c37a963782bf546cd0f Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 20 Oct 2026 16:32:49 +0200 Subject: [PATCH] chore(bundler): more tests Signed-off-by: Jan Kowalleck --- tools/src/main/js/bundler/test/README.md | 43 +++++-- .../js/bundler/test/fixtures/main.schema.json | 18 +++ .../modules/used-array-items.schema.json | 92 +++++++++++++++ .../snapshot/main-bundled.min.schema.json | 2 +- .../test/snapshot/main-bundled.schema.json | 107 ++++++++++++++++++ .../main/js/bundler/test/snapshot/report.json | 24 +++- 6 files changed, 273 insertions(+), 13 deletions(-) create mode 100644 tools/src/main/js/bundler/test/fixtures/modules/used-array-items.schema.json diff --git a/tools/src/main/js/bundler/test/README.md b/tools/src/main/js/bundler/test/README.md index 408a258c0..03d7af090 100644 --- a/tools/src/main/js/bundler/test/README.md +++ b/tools/src/main/js/bundler/test/README.md @@ -14,29 +14,51 @@ Minimal snapshot test for `../cli.js`. No test framework required — plain Node └── snapshot/ # expected bundler output, committed ├── main-bundled.schema.json # pretty bundle, all `$comment` kept ├── main-bundled.min.schema.json # minified bundle, only the root `$comment` kept - └── report.json # bundler report (embedded / external / rewired / warnings) + └── report.json # bundler report (embedded / external / rewired / tree-shake / warnings) ``` The runner copies `fixtures/` to a temporary directory, runs the CLI's `main()` there with `main.schema.json` as the entry and `modules/` as the modules directory, writes the returned report to `report.json`, and compares all three generated files byte-for-byte against `snapshot/`. -Each module file name states the edge case it covers (`used-whole`, `used-subdefs-only`, -`used-properties-only`, `unused`, `dynamic-anchor`); `externals/not-bundled` covers a file outside +Each module file name states the edge case it covers; `externals/not-bundled` covers a file outside the modules directory, the `remote` property in `used-whole` covers an absolute `https://` ref, and `inArray` in `used-whole` covers refs nested inside an array (`allOf`). +The modules exercise the tree-shaker as follows: + +- `used-whole` — referenced as a whole from the entry; the module body survives untouched, but + its unreferenced `$defs/unusedButKept` is dropped nevertheless. +- `used-subdefs-only` — only individual `$defs` are referenced; the module body (`type`, `properties`) + is dropped, the module is hollowed, and `$defs/dropped` is removed. A ref from the dropped body + to `unused` must *not* make `unused` reachable. +- `used-properties-only` — only nested *properties* are referenced, via JSON pointers like + `#/properties/address/properties/geo/properties/lat`. Only the referenced property subtrees + (and what they transitively reach, i.e. `$defs/degrees`) are kept; the containers along the path + (`address`, `geo`, the module itself) are hollowed; unreferenced siblings (`city`, `lon`, + `unusedTopLevel`) and `$defs/unusedDef` are dropped. +- `used-array-items` — refs point *into* schema arrays (`#/properties/combined/allOf/1`, + `#/properties/choice/oneOf/1`) and to a *non-schema* value (`#/properties/status/enum`). + Non-reached array siblings (`allOf/0`, `allOf/2`, `oneOf/0`) must be replaced by a void + `{"not": …}` placeholder — never removed, so indices and pointers stay stable, and never + serialised as `null`. The non-schema target is kept as a whole and its parent `status` is + hollowed. `$defs/keptViaArray` is kept transitively via `allOf/1`; `$defs/droppedDef` is removed. + A ref from the void `allOf/2` to `unused` must *not* make `unused` reachable. +- `unused` — nothing reachable refers to it; the whole module is removed. +- `dynamic-anchor` — declares a `$dynamicAnchor`; the tree-shaker cannot follow anchors and keeps it as is. + The entry schema additionally covers: - a property *named* `$comment` (`properties.$comment`) — it must survive minification, only its own `$comment` keyword is stripped; - local refs into the entry's own `$defs` — they are rewired to the hollow container - `#/$defs/main/$defs/…`, which is marked with `"not": {"$comment": …}`. + `#/$defs/main/$defs/…`, which is marked with `"not": {"$comment": …}`; the unreferenced + `$defs/mainUnused` is dropped from that container. - an inline object property (`properties.composed`) whose own properties reference - *properties* of a module rather than its `$defs` — via JSON pointers like - `#/properties/address/properties/geo/properties/lat`. Only the referenced property subtrees - (and what they transitively reach) of `used-properties-only` are kept; unreferenced siblings - (`city`, `lon`, `unusedTopLevel`), unreachable `$defs` and `unused.schema.json` are dropped. + *properties* of a module rather than its `$defs` (see `used-properties-only` above). +- refs into *array items* and to a *non-schema value* of a module + (`properties.middle`, `properties.lastChoice`, `properties.statusValues`; + see `used-array-items` above). Inside the fixtures, the annotation keywords have distinct roles: @@ -49,8 +71,9 @@ Inside the fixtures, the annotation keywords have distinct roles: `report.json` pins the bundler's bookkeeping: which files were embedded and under which `$defs` key, which refs were left external, every rewired ref (source → target), the -tree-shake result, and the validation warnings. Paths in the report are relative to the -entry schema's directory, so the snapshot is independent of the temporary work directory. +tree-shake result (every `reached`, `hollowed` and `removed` JSON pointer), and the +validation warnings. Paths in the report are relative to the entry schema's directory, +so the snapshot is independent of the temporary work directory. ## Run diff --git a/tools/src/main/js/bundler/test/fixtures/main.schema.json b/tools/src/main/js/bundler/test/fixtures/main.schema.json index 6b9e69bf0..b318859dc 100644 --- a/tools/src/main/js/bundler/test/fixtures/main.schema.json +++ b/tools/src/main/js/bundler/test/fixtures/main.schema.json @@ -54,6 +54,24 @@ "$ref": "modules/used-properties-only.schema.json#/properties/contact" } } + }, + "middle": { + "$comment": "Ref INTO a schema array (allOf/1) of a module -> siblings allOf/0 and allOf/2 must become void placeholders, not null; the index must stay 1.", + "title": "Middle", + "description": "The middle part of the module's combined schema.", + "$ref": "modules/used-array-items.schema.json#/properties/combined/allOf/1" + }, + "lastChoice": { + "$comment": "Ref INTO the last item of a oneOf -> the preceding item must become a void placeholder.", + "title": "Last choice", + "description": "The last alternative of the module's choice.", + "$ref": "modules/used-array-items.schema.json#/properties/choice/oneOf/1" + }, + "statusValues": { + "$comment": "Ref to a NON-schema value (the module's enum array). Semantically dubious, but a legal pointer -> the tree-shaker must record it as reached (no crash) and keep the enum intact.", + "title": "Status values", + "description": "Pointer to the module's status enum.", + "$ref": "modules/used-array-items.schema.json#/properties/status/enum" } }, "$defs": { diff --git a/tools/src/main/js/bundler/test/fixtures/modules/used-array-items.schema.json b/tools/src/main/js/bundler/test/fixtures/modules/used-array-items.schema.json new file mode 100644 index 000000000..76e65be21 --- /dev/null +++ b/tools/src/main/js/bundler/test/fixtures/modules/used-array-items.schema.json @@ -0,0 +1,92 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://example.com/test/modules/used-array-items.schema.json", + "$comment": "FIXTURE: refs point INTO schema arrays and to NON-schema values of this module. The module itself is never referenced as a whole -> hollowed.", + "title": "Used array items", + "description": "A module of which only individual array items and a non-schema value are referenced.", + "type": "object", + "properties": { + "combined": { + "$comment": "Only allOf/1 is referenced from main. allOf/0 and allOf/2 are not reached -> they must become void placeholders, never JSON null, so that the index of allOf/1 stays stable. 'combined' itself is not reached -> hollowed.", + "title": "Combined", + "description": "A composition of which only the middle part is referenced.", + "allOf": [ + { + "$comment": "allOf/0: NOT referenced -> void placeholder.", + "title": "Combined part 0", + "description": "The first part.", + "type": "object", + "properties": { + "a": { + "title": "A", + "description": "An unreferenced value.", + "type": "string" + } + } + }, + { + "$comment": "allOf/1: referenced from main via #/properties/combined/allOf/1 -> kept as a whole, including its internal ref to $defs/keptViaArray.", + "title": "Combined part 1", + "description": "The middle part.", + "type": "object", + "properties": { + "b": { + "title": "B", + "description": "A value defined in this module.", + "$ref": "#/$defs/keptViaArray" + } + } + }, + { + "$comment": "allOf/2: NOT referenced -> void placeholder. Its ref must NOT make unused.schema.json reachable.", + "title": "Combined part 2", + "description": "The last part.", + "$ref": "unused.schema.json" + } + ] + }, + "choice": { + "$comment": "Only the LAST item of oneOf is referenced -> oneOf/0 becomes a void placeholder, oneOf/1 is kept.", + "title": "Choice", + "description": "A choice of which only the last alternative is referenced.", + "oneOf": [ + { + "$comment": "oneOf/0: NOT referenced -> void placeholder.", + "title": "Choice 0", + "description": "The first alternative.", + "type": "null" + }, + { + "$comment": "oneOf/1: referenced from main via #/properties/choice/oneOf/1.", + "title": "Choice 1", + "description": "The second alternative.", + "type": "integer" + } + ] + }, + "status": { + "$comment": "A NON-schema value of this schema (the enum array) is referenced from main via #/properties/status/enum. The enum must be kept as a whole; 'status' itself is not reached -> hollowed; its 'type' is dropped.", + "title": "Status", + "description": "A status value.", + "type": "string", + "enum": [ + "on", + "off" + ] + } + }, + "$defs": { + "keptViaArray": { + "$comment": "Only reachable via combined.allOf/1.", + "title": "Kept via array", + "description": "A boolean value.", + "type": "boolean" + }, + "droppedDef": { + "$comment": "Not reachable -> shaken away.", + "title": "Dropped def", + "description": "A definition nothing reachable refers to.", + "type": "null" + } + } +} diff --git a/tools/src/main/js/bundler/test/snapshot/main-bundled.min.schema.json b/tools/src/main/js/bundler/test/snapshot/main-bundled.min.schema.json index 8cf1760c6..6a7dc4acc 100644 --- a/tools/src/main/js/bundler/test/snapshot/main-bundled.min.schema.json +++ b/tools/src/main/js/bundler/test/snapshot/main-bundled.min.schema.json @@ -1 +1 @@ -{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"http://example.com/test/main-bundled.min.schema.json","$comment":"FIXTURE: root schema; entry point of the bundle. This $comment is the only one that survives minification. The root title/description must survive in both outputs.","title":"Main","description":"Root document of the bundler test fixtures.","type":"object","properties":{"$comment":{"type":"string","title":"Comment"},"whole":{"title":"Whole","description":"A module referenced in its entirety.","$ref":"#/$defs/used-whole"},"sub":{"title":"Sub","description":"A single definition of another module.","$ref":"#/$defs/used-subdefs-only/$defs/kept"},"local":{"title":"Local","description":"A definition of this very schema.","$ref":"#/$defs/main/$defs/mainLocal"},"composed":{"title":"Composed","description":"An object assembled from parts of another module.","type":"object","properties":{"street":{"title":"Street","description":"The street, taken from the module's address.","$ref":"#/$defs/used-properties-only/properties/address/properties/street"},"latitude":{"title":"Latitude","description":"The latitude, taken from the module's address.geo.","$ref":"#/$defs/used-properties-only/properties/address/properties/geo/properties/lat"},"contact":{"title":"Contact","description":"The contact block of the module, as is.","$ref":"#/$defs/used-properties-only/properties/contact"}}}},"$defs":{"dynamic-anchor":{"$dynamicAnchor":"node","title":"Dynamic anchor","description":"A recursive tree node, addressable via a dynamic anchor.","type":"object","properties":{"children":{"title":"Children","description":"The child nodes of this node.","type":"array","items":{"title":"Child","description":"A nested node.","$ref":"#/$defs/dynamic-anchor"}}}},"main":{"$defs":{"mainLocal":{"title":"Main local","description":"A local string definition of the root schema.","type":"string"},"mainUnused":{"title":"Main unused","description":"A definition nothing refers to.","type":"null"}},"not":{}},"unused":{"title":"Unused","description":"A module that nothing reachable refers to.","type":"object","properties":{"pullsNothingIn":{"title":"Pulls nothing in","description":"A flag defined by another module.","$ref":"#/$defs/used-whole/$defs/unusedButKept"}}},"used-properties-only":{"title":"Used properties only","description":"A module of which only nested properties are referenced.","type":"object","properties":{"address":{"title":"Address","description":"A postal address.","type":"object","properties":{"street":{"title":"Street","description":"Street name and number.","type":"string"},"city":{"title":"City","description":"Name of the city.","type":"string"},"geo":{"title":"Geo","description":"Geographic coordinates.","type":"object","properties":{"lat":{"title":"Latitude","description":"Latitude in degrees.","$ref":"#/$defs/used-properties-only/$defs/degrees"},"lon":{"title":"Longitude","description":"Longitude in degrees.","$ref":"#/$defs/used-properties-only/$defs/degrees"}}}}},"contact":{"title":"Contact","description":"Contact details.","type":"object","properties":{"email":{"title":"E-mail","description":"An e-mail address.","type":"string","format":"email"},"phone":{"title":"Phone","description":"A phone number.","type":"string"}}},"unusedTopLevel":{"title":"Unused top level","description":"A property nothing refers to.","$ref":"#/$defs/unused"}},"$defs":{"degrees":{"title":"Degrees","description":"An angle in degrees.","type":"number","minimum":-180,"maximum":180},"unusedDef":{"title":"Unused def","description":"A definition nothing reachable refers to.","type":"null"}}},"used-subdefs-only":{"title":"Used sub-defs only","description":"A module of which only individual definitions are referenced.","type":"object","properties":{"body":{"title":"Body","description":"The body of this module.","$ref":"#/$defs/unused"}},"$defs":{"kept":{"title":"Kept","description":"A definition that is reached directly from the root schema.","type":"object","properties":{"shared":{"title":"Shared","description":"A value defined by another module.","$ref":"#/$defs/used-whole/$defs/sharedByOther"},"fromMain":{"title":"From main","description":"A value defined by the root schema.","$ref":"#/$defs/main/$defs/mainLocal"},"chain":{"title":"Chain","description":"A value defined further down in this module.","$ref":"#/$defs/used-subdefs-only/$defs/keptTransitively"}}},"keptTransitively":{"title":"Kept transitively","description":"A string value.","type":"string"},"dropped":{"title":"Dropped","description":"A definition nothing reachable refers to.","type":"null"}}},"used-whole":{"title":"Used whole","description":"A module that is referenced in its entirety.","type":"object","properties":{"name":{"title":"Name","description":"A display name.","type":"string"},"inner":{"title":"Inner","description":"Sibling annotations next to a $ref: title/description are kept, while the sibling $comment is removed in the minified output.","$ref":"#/$defs/used-whole/$defs/inner"},"license":{"title":"License","description":"The license of this item.","$ref":"externals/not-bundled.schema.json#/$defs/licenseId"},"remote":{"title":"Remote","description":"A thing defined by a remote schema.","$ref":"https://example.com/remote.schema.json#/$defs/thing"},"inArray":{"title":"In array","description":"A composition of several referenced schemas.","allOf":[{"$ref":"#/$defs/used-whole/$defs/inner"},{"$ref":"#/$defs/used-subdefs-only/$defs/keptTransitively"},{"$ref":"externals/not-bundled.schema.json#/$defs/licenseId"}]}},"$defs":{"inner":{"title":"Inner","description":"An integer value.","type":"integer"},"unusedButKept":{"title":"Unused but kept","description":"A flag.","type":"boolean"},"sharedByOther":{"title":"Shared by other","description":"A numeric value shared across modules.","type":"number"}}}}} \ No newline at end of file +{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"http://example.com/test/main-bundled.min.schema.json","$comment":"FIXTURE: root schema; entry point of the bundle. This $comment is the only one that survives minification. The root title/description must survive in both outputs.","title":"Main","description":"Root document of the bundler test fixtures.","type":"object","properties":{"$comment":{"type":"string","title":"Comment"},"whole":{"title":"Whole","description":"A module referenced in its entirety.","$ref":"#/$defs/used-whole"},"sub":{"title":"Sub","description":"A single definition of another module.","$ref":"#/$defs/used-subdefs-only/$defs/kept"},"local":{"title":"Local","description":"A definition of this very schema.","$ref":"#/$defs/main/$defs/mainLocal"},"composed":{"title":"Composed","description":"An object assembled from parts of another module.","type":"object","properties":{"street":{"title":"Street","description":"The street, taken from the module's address.","$ref":"#/$defs/used-properties-only/properties/address/properties/street"},"latitude":{"title":"Latitude","description":"The latitude, taken from the module's address.geo.","$ref":"#/$defs/used-properties-only/properties/address/properties/geo/properties/lat"},"contact":{"title":"Contact","description":"The contact block of the module, as is.","$ref":"#/$defs/used-properties-only/properties/contact"}}},"middle":{"title":"Middle","description":"The middle part of the module's combined schema.","$ref":"#/$defs/used-array-items/properties/combined/allOf/1"},"lastChoice":{"title":"Last choice","description":"The last alternative of the module's choice.","$ref":"#/$defs/used-array-items/properties/choice/oneOf/1"},"statusValues":{"title":"Status values","description":"Pointer to the module's status enum.","$ref":"#/$defs/used-array-items/properties/status/enum"}},"$defs":{"dynamic-anchor":{"$dynamicAnchor":"node","title":"Dynamic anchor","description":"A recursive tree node, addressable via a dynamic anchor.","type":"object","properties":{"children":{"title":"Children","description":"The child nodes of this node.","type":"array","items":{"title":"Child","description":"A nested node.","$ref":"#/$defs/dynamic-anchor"}}}},"main":{"$defs":{"mainLocal":{"title":"Main local","description":"A local string definition of the root schema.","type":"string"},"mainUnused":{"title":"Main unused","description":"A definition nothing refers to.","type":"null"}},"not":{}},"unused":{"title":"Unused","description":"A module that nothing reachable refers to.","type":"object","properties":{"pullsNothingIn":{"title":"Pulls nothing in","description":"A flag defined by another module.","$ref":"#/$defs/used-whole/$defs/unusedButKept"}}},"used-array-items":{"title":"Used array items","description":"A module of which only individual array items and a non-schema value are referenced.","type":"object","properties":{"combined":{"title":"Combined","description":"A composition of which only the middle part is referenced.","allOf":[{"title":"Combined part 0","description":"The first part.","type":"object","properties":{"a":{"title":"A","description":"An unreferenced value.","type":"string"}}},{"title":"Combined part 1","description":"The middle part.","type":"object","properties":{"b":{"title":"B","description":"A value defined in this module.","$ref":"#/$defs/used-array-items/$defs/keptViaArray"}}},{"title":"Combined part 2","description":"The last part.","$ref":"#/$defs/unused"}]},"choice":{"title":"Choice","description":"A choice of which only the last alternative is referenced.","oneOf":[{"title":"Choice 0","description":"The first alternative.","type":"null"},{"title":"Choice 1","description":"The second alternative.","type":"integer"}]},"status":{"title":"Status","description":"A status value.","type":"string","enum":["on","off"]}},"$defs":{"keptViaArray":{"title":"Kept via array","description":"A boolean value.","type":"boolean"},"droppedDef":{"title":"Dropped def","description":"A definition nothing reachable refers to.","type":"null"}}},"used-properties-only":{"title":"Used properties only","description":"A module of which only nested properties are referenced.","type":"object","properties":{"address":{"title":"Address","description":"A postal address.","type":"object","properties":{"street":{"title":"Street","description":"Street name and number.","type":"string"},"city":{"title":"City","description":"Name of the city.","type":"string"},"geo":{"title":"Geo","description":"Geographic coordinates.","type":"object","properties":{"lat":{"title":"Latitude","description":"Latitude in degrees.","$ref":"#/$defs/used-properties-only/$defs/degrees"},"lon":{"title":"Longitude","description":"Longitude in degrees.","$ref":"#/$defs/used-properties-only/$defs/degrees"}}}}},"contact":{"title":"Contact","description":"Contact details.","type":"object","properties":{"email":{"title":"E-mail","description":"An e-mail address.","type":"string","format":"email"},"phone":{"title":"Phone","description":"A phone number.","type":"string"}}},"unusedTopLevel":{"title":"Unused top level","description":"A property nothing refers to.","$ref":"#/$defs/unused"}},"$defs":{"degrees":{"title":"Degrees","description":"An angle in degrees.","type":"number","minimum":-180,"maximum":180},"unusedDef":{"title":"Unused def","description":"A definition nothing reachable refers to.","type":"null"}}},"used-subdefs-only":{"title":"Used sub-defs only","description":"A module of which only individual definitions are referenced.","type":"object","properties":{"body":{"title":"Body","description":"The body of this module.","$ref":"#/$defs/unused"}},"$defs":{"kept":{"title":"Kept","description":"A definition that is reached directly from the root schema.","type":"object","properties":{"shared":{"title":"Shared","description":"A value defined by another module.","$ref":"#/$defs/used-whole/$defs/sharedByOther"},"fromMain":{"title":"From main","description":"A value defined by the root schema.","$ref":"#/$defs/main/$defs/mainLocal"},"chain":{"title":"Chain","description":"A value defined further down in this module.","$ref":"#/$defs/used-subdefs-only/$defs/keptTransitively"}}},"keptTransitively":{"title":"Kept transitively","description":"A string value.","type":"string"},"dropped":{"title":"Dropped","description":"A definition nothing reachable refers to.","type":"null"}}},"used-whole":{"title":"Used whole","description":"A module that is referenced in its entirety.","type":"object","properties":{"name":{"title":"Name","description":"A display name.","type":"string"},"inner":{"title":"Inner","description":"Sibling annotations next to a $ref: title/description are kept, while the sibling $comment is removed in the minified output.","$ref":"#/$defs/used-whole/$defs/inner"},"license":{"title":"License","description":"The license of this item.","$ref":"externals/not-bundled.schema.json#/$defs/licenseId"},"remote":{"title":"Remote","description":"A thing defined by a remote schema.","$ref":"https://example.com/remote.schema.json#/$defs/thing"},"inArray":{"title":"In array","description":"A composition of several referenced schemas.","allOf":[{"$ref":"#/$defs/used-whole/$defs/inner"},{"$ref":"#/$defs/used-subdefs-only/$defs/keptTransitively"},{"$ref":"externals/not-bundled.schema.json#/$defs/licenseId"}]}},"$defs":{"inner":{"title":"Inner","description":"An integer value.","type":"integer"},"unusedButKept":{"title":"Unused but kept","description":"A flag.","type":"boolean"},"sharedByOther":{"title":"Shared by other","description":"A numeric value shared across modules.","type":"number"}}}}} \ No newline at end of file diff --git a/tools/src/main/js/bundler/test/snapshot/main-bundled.schema.json b/tools/src/main/js/bundler/test/snapshot/main-bundled.schema.json index bc5596cd3..d6b4d2662 100644 --- a/tools/src/main/js/bundler/test/snapshot/main-bundled.schema.json +++ b/tools/src/main/js/bundler/test/snapshot/main-bundled.schema.json @@ -54,6 +54,24 @@ "$ref": "#/$defs/used-properties-only/properties/contact" } } + }, + "middle": { + "$comment": "Ref INTO a schema array (allOf/1) of a module -> siblings allOf/0 and allOf/2 must become void placeholders, not null; the index must stay 1.", + "title": "Middle", + "description": "The middle part of the module's combined schema.", + "$ref": "#/$defs/used-array-items/properties/combined/allOf/1" + }, + "lastChoice": { + "$comment": "Ref INTO the last item of a oneOf -> the preceding item must become a void placeholder.", + "title": "Last choice", + "description": "The last alternative of the module's choice.", + "$ref": "#/$defs/used-array-items/properties/choice/oneOf/1" + }, + "statusValues": { + "$comment": "Ref to a NON-schema value (the module's enum array). Semantically dubious, but a legal pointer -> the tree-shaker must record it as reached (no crash) and keep the enum intact.", + "title": "Status values", + "description": "Pointer to the module's status enum.", + "$ref": "#/$defs/used-array-items/properties/status/enum" } }, "$defs": { @@ -108,6 +126,95 @@ } } }, + "used-array-items": { + "title": "Used array items", + "description": "A module of which only individual array items and a non-schema value are referenced.", + "type": "object", + "properties": { + "combined": { + "$comment": "Only allOf/1 is referenced from main. allOf/0 and allOf/2 are not reached -> they must become void placeholders, never JSON null, so that the index of allOf/1 stays stable. 'combined' itself is not reached -> hollowed.", + "title": "Combined", + "description": "A composition of which only the middle part is referenced.", + "allOf": [ + { + "$comment": "allOf/0: NOT referenced -> void placeholder.", + "title": "Combined part 0", + "description": "The first part.", + "type": "object", + "properties": { + "a": { + "title": "A", + "description": "An unreferenced value.", + "type": "string" + } + } + }, + { + "$comment": "allOf/1: referenced from main via #/properties/combined/allOf/1 -> kept as a whole, including its internal ref to $defs/keptViaArray.", + "title": "Combined part 1", + "description": "The middle part.", + "type": "object", + "properties": { + "b": { + "title": "B", + "description": "A value defined in this module.", + "$ref": "#/$defs/used-array-items/$defs/keptViaArray" + } + } + }, + { + "$comment": "allOf/2: NOT referenced -> void placeholder. Its ref must NOT make unused.schema.json reachable.", + "title": "Combined part 2", + "description": "The last part.", + "$ref": "#/$defs/unused" + } + ] + }, + "choice": { + "$comment": "Only the LAST item of oneOf is referenced -> oneOf/0 becomes a void placeholder, oneOf/1 is kept.", + "title": "Choice", + "description": "A choice of which only the last alternative is referenced.", + "oneOf": [ + { + "$comment": "oneOf/0: NOT referenced -> void placeholder.", + "title": "Choice 0", + "description": "The first alternative.", + "type": "null" + }, + { + "$comment": "oneOf/1: referenced from main via #/properties/choice/oneOf/1.", + "title": "Choice 1", + "description": "The second alternative.", + "type": "integer" + } + ] + }, + "status": { + "$comment": "A NON-schema value of this schema (the enum array) is referenced from main via #/properties/status/enum. The enum must be kept as a whole; 'status' itself is not reached -> hollowed; its 'type' is dropped.", + "title": "Status", + "description": "A status value.", + "type": "string", + "enum": [ + "on", + "off" + ] + } + }, + "$defs": { + "keptViaArray": { + "$comment": "Only reachable via combined.allOf/1.", + "title": "Kept via array", + "description": "A boolean value.", + "type": "boolean" + }, + "droppedDef": { + "$comment": "Not reachable -> shaken away.", + "title": "Dropped def", + "description": "A definition nothing reachable refers to.", + "type": "null" + } + } + }, "used-properties-only": { "title": "Used properties only", "description": "A module of which only nested properties are referenced.", diff --git a/tools/src/main/js/bundler/test/snapshot/report.json b/tools/src/main/js/bundler/test/snapshot/report.json index c865740d2..2de004668 100644 --- a/tools/src/main/js/bundler/test/snapshot/report.json +++ b/tools/src/main/js/bundler/test/snapshot/report.json @@ -17,6 +17,10 @@ "modules/unused.schema.json", "#/$defs/unused" ], + [ + "modules/used-array-items.schema.json", + "#/$defs/used-array-items" + ], [ "modules/used-properties-only.schema.json", "#/$defs/used-properties-only" @@ -55,6 +59,18 @@ "modules/used-properties-only.schema.json#/properties/contact", "#/$defs/used-properties-only/properties/contact" ], + [ + "modules/used-array-items.schema.json#/properties/combined/allOf/1", + "#/$defs/used-array-items/properties/combined/allOf/1" + ], + [ + "modules/used-array-items.schema.json#/properties/choice/oneOf/1", + "#/$defs/used-array-items/properties/choice/oneOf/1" + ], + [ + "modules/used-array-items.schema.json#/properties/status/enum", + "#/$defs/used-array-items/properties/status/enum" + ], [ "modules/dynamic-anchor.schema.json", "#/$defs/dynamic-anchor" @@ -64,13 +80,17 @@ "#/$defs/used-whole/$defs/unusedButKept" ], [ - "modules/used-properties-only.schema.json#/$defs/degrees", - "#/$defs/used-properties-only/$defs/degrees" + "modules/used-array-items.schema.json#/$defs/keptViaArray", + "#/$defs/used-array-items/$defs/keptViaArray" ], [ "modules/unused.schema.json", "#/$defs/unused" ], + [ + "modules/used-properties-only.schema.json#/$defs/degrees", + "#/$defs/used-properties-only/$defs/degrees" + ], [ "modules/used-whole.schema.json#/$defs/sharedByOther", "#/$defs/used-whole/$defs/sharedByOther"