Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(schema-org): allow null to opt out of default values#680

Merged
harlan-zw merged 3 commits into
mainfrom
fix/548-empty-schema-org-values
Mar 10, 2026
Merged

fix(schema-org): allow null to opt out of default values#680
harlan-zw merged 3 commits into
mainfrom
fix/548-empty-schema-org-values

Conversation

@harlan-zw

@harlan-zw harlan-zw commented Mar 10, 2026

Copy link
Copy Markdown
Collaborator

πŸ”— Linked issue

Resolves #548

❓ Type of change

  • πŸ“– Documentation
  • 🐞 Bug fix
  • πŸ‘Œ Enhancement
  • ✨ New feature
  • 🧹 Chore
  • ⚠️ Breaking change

πŸ“š Description

Schema-org's setIfEmpty always injected defaults for properties like sku, priceValidUntil, and url, with no way to opt out. Now, setting a property to null explicitly removes it from the output. The setIfEmpty check was changed from falsy to === undefined, and null stripping was split into a final pass after all resolvers run so the sentinel survives intermediate resolution.

Summary by CodeRabbit

  • Bug Fixes

    • Null values now act as explicit opt-outs: setting fields (brand, description, offer details) to null prevents automatic defaults from being applied; default-assignment now only occurs for undefined values.
  • Tests

    • Added coverage for null-as-opt-out behavior to ensure properties set to null are not auto-populated.
  • Chores

    • Added post-resolution removal of null properties so null sentinels are cleared before final output.

When a user sets a schema-org property to null, it is now removed from
the final output instead of being overwritten by setIfEmpty defaults or
inheritMeta. null acts as an explicit opt-out sentinel that is preserved
through the full resolution pipeline and stripped only after all
resolvers (including resolveRootNode) have run.

Closes #548
@coderabbitai

coderabbitai Bot commented Mar 10, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. πŸŽ‰

ℹ️ Recent review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6c4a25fa-ac58-4d17-81dd-ee6e6e4a5c7d

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between e577133 and 0b9a46d.

πŸ“’ Files selected for processing (1)
  • packages/schema-org/src/core/graph.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/schema-org/src/core/graph.ts

πŸ“ Walkthrough

Walkthrough

This change preserves explicit nulls during resolution so resolvers can observe them to opt out of defaults, then removes _resolver and recursively strips null properties (via stripNullProperties) from nodes before final normalization and output.

Changes

Cohort / File(s) Summary
Core Graph Processing
packages/schema-org/src/core/graph.ts
Delay deletion of _resolver until after all resolveRootNode calls; after that delete _resolver on nodes and invoke stripNullProperties to remove null sentinels before normalization.
Utility Functions
packages/schema-org/src/utils/index.ts
Add stripNullProperties(obj: any) to recursively remove null values; update stripEmptyProperties to only remove empty strings and undefined (preserve null); change setIfEmpty to assign only when target is undefined and provided value is not null.
Product Tests
packages/schema-org/src/nodes/Product/index.test.ts
Add three tests asserting that null values opt out of defaults: brand (setIfEmpty), description (inheritMeta), and offer defaults (priceValidUntil, priceCurrency) are removed when set to null.

Sequence Diagram(s)

sequenceDiagram
  participant Injector as Graph Injector
  participant Resolver as resolveRootNode (resolvers)
  participant Nodes as Nodes (ctx.nodes)
  participant Utils as stripNullProperties
  participant Normalizer as Final Normalization

  Injector->>Resolver: Call resolveRootNode for each root node
  Resolver->>Nodes: Read node properties (nulls preserved)
  Resolver-->>Injector: Return resolved node (may rely on null sentinels)
  Injector->>Nodes: Delete `_resolver` from all nodes (after all resolves)
  Injector->>Utils: Call stripNullProperties on nodes
  Utils->>Nodes: Recursively remove `null` properties
  Injector->>Normalizer: Pass cleaned nodes for final normalization
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

πŸ‡ I nibble a null, a quiet little tap,
Defaults step back β€” I close the gap.
I hop through nodes, I clear the trace,
stripNullProperties leaves a cleaner place. πŸ₯•βœ¨

πŸš₯ Pre-merge checks | βœ… 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
βœ… Passed checks (4 passed)
Check name Status Explanation
Title check βœ… Passed The title clearly summarizes the main change: allowing null values to opt out of default value injection in schema-org.
Description check βœ… Passed The description follows the template structure, provides linked issue reference, marks appropriate change types, and explains the feature clearly.
Linked Issues check βœ… Passed The code changes directly implement the feature requested in #548: treating null as opt-out sentinel, changing emptiness check to === undefined, and preserving null through resolution.
Out of Scope Changes check βœ… Passed All changes are scoped to implementing #548: utils modifications for null handling, graph processing order adjustment, and product test cases validating the feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • πŸ“ Generate docstrings (stacked PR)
  • πŸ“ Generate docstrings (commit on current branch)
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/548-empty-schema-org-values

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Bundle Size Gzipped
Client (Minimal) 10.5 kB 4.3 kB
Server (Minimal) 10.2 kB 4.2 kB
Vue Client (Minimal) 11.4 kB 4.7 kB
Vue Server (Minimal) 11.1 kB 4.6 kB

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/schema-org/src/utils/index.ts (1)

54-56: setIfEmpty still drops valid falsy defaults.

The function now respects null on the destination side, but Line 55 still skips injecting 0, false, and '' because the RHS guard relies on truthiness. To distinguish only "undefined" from "explicitly provided", consider:

♻️ Proposed refactor
 export function setIfEmpty<T extends Thing>(node: T, field: keyof T, value: any) {
-  if (node?.[field] === undefined && value)
+  if (node?.[field] === undefined && value != null)
     node[field] = value
 }
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/schema-org/src/utils/index.ts` around lines 54 - 56, The setIfEmpty
function currently avoids setting defaults when the provided value is falsy (0,
false, ''), because the RHS uses truthiness; update the conditional in
setIfEmpty so it only treats undefined as "no value" (i.e., check value !==
undefined) while still requiring the destination to be undefined (node?.[field]
=== undefined). Modify the logic in the setIfEmpty function to use the explicit
undefined check for the incoming value so explicit falsy defaults are assigned.
πŸ€– Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/schema-org/src/core/graph.ts`:
- Around line 125-131: The null-sentinel stripping (stripNullProperties) and
removal of node._resolver are happening per-node immediately after calling
node._resolver.resolveRootNode, which makes pass-2 resolvers see earlier nodes
with sentinels already erased; instead, defer calling stripNullProperties(node)
and delete node._resolver until after the entire second-pass resolution loop
completes (i.e., run resolveRootNode for every node first using
node._resolver.resolveRootNode(node, ctx) / ctx.find(), then in a subsequent
loop call stripNullProperties(node) and delete node._resolver for each node), so
all resolveRootNode handlers can observe null opt-out sentinels consistently.

In `@packages/schema-org/src/utils/index.ts`:
- Around line 164-177: The stripNullProperties function currently uses delete on
numeric keys which creates sparse arrays (holes) that JSON.stringify turns into
nulls; update stripNullProperties to detect arrays (Array.isArray(v)) and for
arrays remove null entries by constructing a new filtered array (e.g., v =
v.filter(el => el !== null)) or splice out nulls, then recurse into each
remaining element (calling stripNullProperties on object elements) instead of
deleting numeric indices; keep the existing __v_isReadonly and __v_isRef checks
and apply them to array elements as well, and assign the filtered array back to
obj[k] so callers of stripNullProperties (the function name) see a compact array
rather than a sparse one.

---

Nitpick comments:
In `@packages/schema-org/src/utils/index.ts`:
- Around line 54-56: The setIfEmpty function currently avoids setting defaults
when the provided value is falsy (0, false, ''), because the RHS uses
truthiness; update the conditional in setIfEmpty so it only treats undefined as
"no value" (i.e., check value !== undefined) while still requiring the
destination to be undefined (node?.[field] === undefined). Modify the logic in
the setIfEmpty function to use the explicit undefined check for the incoming
value so explicit falsy defaults are assigned.

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5da2e0eb-70a9-4087-9090-66f332491192

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 4838b84 and 4f5a7c1.

πŸ“’ Files selected for processing (3)
  • packages/schema-org/src/core/graph.ts
  • packages/schema-org/src/nodes/Product/index.test.ts
  • packages/schema-org/src/utils/index.ts

Comment thread packages/schema-org/src/core/graph.ts Outdated
Comment thread packages/schema-org/src/utils/index.ts
@harlan-zw

Copy link
Copy Markdown
Collaborator Author

@copilot fix code rabbits issues

Copilot AI commented Mar 10, 2026

Copy link
Copy Markdown
Contributor

@harlan-zw I've opened a new pull request, #687, to work on those changes. Once the pull request is ready, I'll request review from you.

* Initial plan

* fix(schema-org): address CodeRabbit issues with null opt-out implementation

Co-authored-by: harlan-zw <[email protected]>

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: harlan-zw <[email protected]>

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

πŸ€– Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/schema-org/src/core/graph.ts`:
- Around line 131-133: The loop currently calls
stripNullProperties(ctx.nodes[i]) before deleting ctx.nodes[i]._resolver, which
lets stripNullProperties traverse and mutate node._resolver; to fix, remove the
_resolver property before stripping by moving the delete into a prior pass or by
changing stripNullProperties to ignore the "_resolver" keyβ€”update the code
touching ctx.nodes (reference: ctx.nodes array, stripNullProperties function,
and the "_resolver" property) so that deletion of _resolver occurs before any
stripping operation (or add logic in stripNullProperties to skip "_resolver").

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 21e742f6-c14a-430e-9e3d-99ab73e4ec7d

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 4f5a7c1 and e577133.

πŸ“’ Files selected for processing (2)
  • packages/schema-org/src/core/graph.ts
  • packages/schema-org/src/utils/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/schema-org/src/utils/index.ts

Comment thread packages/schema-org/src/core/graph.ts Outdated
@harlan-zw

Copy link
Copy Markdown
Collaborator Author

more issues @copilot

Copilot AI commented Mar 10, 2026

Copy link
Copy Markdown
Contributor

@harlan-zw I've opened a new pull request, #688, to work on those changes. Once the pull request is ready, I'll request review from you.

* Initial plan

* fix(schema-org): delete _resolver before stripping null properties

Co-authored-by: harlan-zw <[email protected]>

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: harlan-zw <[email protected]>
@harlan-zw harlan-zw merged commit 01a7aaf into main Mar 10, 2026
6 checks passed
@harlan-zw harlan-zw mentioned this pull request Apr 4, 2026
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.

Allow empty schema.org values

2 participants