Skip to content

feat: adjust url logic and fallbacks#998

Merged
asanehisa merged 21 commits intomainfrom
pathInfo
Jan 26, 2026
Merged

feat: adjust url logic and fallbacks#998
asanehisa merged 21 commits intomainfrom
pathInfo

Conversation

@asanehisa
Copy link
Contributor

@asanehisa asanehisa commented Jan 22, 2026

Resolve urls via:

  1. path info (if it exists)
  2. existing _pageset config's url templates
  3. hard coded paths based on slug, address, or id (in that order).

Example PathInfo json:

{
  primaryLocale: 'en',
  includeLocalePrefixForPrimaryLocale: 'false',
  template: '[[address.region]]/[[address.city]]/[[address.line1]]'
}

live site: https://www.yext.com/s/4412098/yextsites/162218/pagesets

live site with changes to the template's getPath as well: https://www.yext.com/s/4412098/yextsites/162230/pagesets

@asanehisa asanehisa added the create-dev-release Triggers dev release workflow label Jan 22, 2026
@github-actions
Copy link
Contributor

Warning: Component files have been updated but no migrations have been added. See https://github.com/yext/visual-editor/blob/main/packages/visual-editor/src/components/migrations/README.md for more information.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 22, 2026

commit: 48ea523

@asanehisa asanehisa marked this pull request as ready for review January 22, 2026 21:23
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 22, 2026

Walkthrough

Centralizes the StreamDocument type into packages/visual-editor/src/utils/types/StreamDocument.ts and updates imports. Adds new URL resolution modules and APIs (resolveUrlFromPathInfo, resolveUrlTemplate, resolveUrlTemplateOfChild) and a legacy resolver (legacyResolveUrlTemplate). Removes the old mergeMeta utility and updates call sites to the new URL resolvers (DirectoryCard, Locator, NearbyLocationCard, etc.). Expands schema resolution with resolveSchemaString and resolveSchemaJson. Removes the puck prop from LocatorResultCard and updates several TypeScript pragma comments and import paths.

Sequence Diagram(s)

sequenceDiagram
  participant Component as ClientComponent
  participant Resolver as resolveUrlTemplate
  participant PathInfo as resolveUrlFromPathInfo
  participant Legacy as legacyResolveUrlTemplate
  participant Location as getLocationPath
  participant Doc as StreamDocument

  Component->>Resolver: resolveUrlTemplate(Doc, relativePrefixToRoot)
  Resolver->>PathInfo: try resolveUrlFromPathInfo(Doc)
  alt PathInfo returns URL
    PathInfo-->>Resolver: URL
  else
    Resolver->>Legacy: try legacyResolveUrlTemplate(Doc, relativePrefixToRoot)
    alt Legacy returns URL
      Legacy-->>Resolver: URL
    else
      Resolver->>Location: try getLocationPath(Doc, relativePrefixToRoot)
      Location-->>Resolver: URL | undefined
    end
  end
  Resolver-->>Component: URL or throw if unresolved
Loading

Possibly related PRs

Suggested reviewers

  • colton-demetriou
  • benlife5
  • mkilpatrick
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: adjust url logic and fallbacks' clearly and concisely describes the main change: refactoring URL resolution logic with a new fallback strategy.
Description check ✅ Passed The description is directly related to the changeset, explaining the new URL resolution order (pathInfo first, then _pageset templates, then hardcoded paths), with concrete examples and live site references.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/visual-editor/src/utils/api/fetchLocalesToPathsForEntity.ts (1)

41-49: Avoid recording empty URLs when resolution fails.
resolvePageSetUrl returns "" on failure (and logs), so the current code will still write an empty path for that locale. This can propagate broken links. Consider skipping the entry (or using a fallback) when the resolved URL is falsy.

💡 Suggested guard
-          const resolvedUrl = resolvePageSetUrl(mergedDocument, "");
-          localeToPath[normalizeLocale(profile.meta.locale)] = resolvedUrl;
+          const resolvedUrl = resolvePageSetUrl(mergedDocument, "");
+          if (resolvedUrl) {
+            localeToPath[normalizeLocale(profile.meta.locale)] = resolvedUrl;
+          } else {
+            console.warn(
+              `Failed to resolve URL template for locale ${profile.meta.locale}`
+            );
+          }
🤖 Fix all issues with AI agents
In `@packages/visual-editor/src/utils/urls/resolveUrlFromPathInfo.ts`:
- Around line 56-71: The isPrimaryLocale function can throw on malformed JSON
when parsing streamDocument?.__?.pathInfo; wrap the JSON.parse call in a
try/catch (inside isPrimaryLocale) and fall back to an empty object
(PathInfoShape default) on parse errors so the function returns safely; keep the
remaining logic unchanged (check pathInfoJson.primaryLocale, then
__.isPrimaryLocale, then default to locale === "en"). Optionally capture/log the
parse error if a logger is available, but ensure the function never throws from
JSON.parse.
- Around line 14-21: The code in resolveUrlFromPathInfo.ts calls JSON.parse on
streamDocument?.__?.pathInfo without guarding against malformed JSON; wrap the
parse in a try-catch inside the resolveUrlFromPathInfo function (or the block
that defines pathInfoJson) and on parse failure return undefined (or otherwise
degrade consistently with the existing missing-template behavior), optionally
logging the parse error for diagnostics; if you intentionally want parse errors
to throw, update the test description to state that throwing on malformed
pathInfo is deliberate instead of changing runtime behavior.
♻️ Duplicate comments (3)
packages/visual-editor/src/components/atoms/cta.tsx (1)

318-321: Same feedback as body.tsx: prefer @ts-expect-error over @ts-ignore.

See the comment on body.tsx for the rationale.

packages/visual-editor/src/components/atoms/toggle.tsx (1)

13-14: Same feedback: prefer @ts-expect-error over @ts-ignore.

See the comment on body.tsx for the rationale.

packages/visual-editor/src/components/atoms/heading.tsx (1)

116-117: Same feedback: prefer @ts-expect-error over @ts-ignore.

See the comment on body.tsx for the rationale.

🧹 Nitpick comments (3)
packages/visual-editor/src/components/atoms/body.tsx (1)

40-41: Consider keeping @ts-expect-error instead of @ts-ignore.

@ts-expect-error is generally preferred over @ts-ignore because it will alert you if the underlying TypeScript issue is ever resolved (e.g., if TypeScript starts accepting CSS variables for textTransform). With @ts-ignore, the suppression remains silently even when no longer needed.

Suggested change
-        // `@ts-ignore`: the css variable here resolves to a valid enum value
+        // `@ts-expect-error` the css variable here resolves to a valid enum value
packages/visual-editor/src/utils/schema/getSchema.ts (1)

1-1: Inconsistent use of .ts extension in import.

This import includes the explicit .ts extension, while other files in this PR (e.g., schemaBlocks.ts line 1) omit it. Consider removing the extension for consistency:

-import { StreamDocument } from "../types/StreamDocument.ts";
+import { StreamDocument } from "../types/StreamDocument";

While both may work depending on your TypeScript/bundler configuration, consistent import style across the codebase is preferred.

packages/visual-editor/src/utils/urls/getLocationPath.ts (1)

1-4: Use a type-only import with direct path to avoid unnecessary barrel dependency.

StreamDocument is used only as a type in the interface declaration (line 6). Import it directly from ./types/StreamDocument.ts rather than the barrel to keep the dependency graph cleaner and avoid potential circular references. The direct path already exists in the codebase and aligns with how resolvePageSetUrl.ts imports the same type.

♻️ Suggested change
-import { StreamDocument } from "../index.ts";
+import type { StreamDocument } from "../types/StreamDocument.ts";

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/visual-editor/src/utils/urls/resolveUrlTemplate.ts`:
- Around line 60-65: The early return that skips DIRECTORY and LOCATOR page sets
prevents extraction of their config.urlTemplate; update the guard in
resolvePageSetUrlTemplate (in resolveUrlTemplate.ts) so it only returns early
when pagesetJson.type is DIRECTORY or LOCATOR AND
pagesetJson.config?.urlTemplate is missing/empty, or simply remove the type
check entirely so the function proceeds to read pagesetJson.config.urlTemplate;
ensure callers' expectations remain valid by leaving resolution intact when
config.urlTemplate exists.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/visual-editor/src/utils/urls/resolveUrlTemplate.test.ts (1)

119-137: Inconsistent type field placement in test data.

The type: "DIRECTORY" field is nested inside config (line 124), but in other mock documents (mockDirectoryMergedDocument at line 53, mockLocatorMergedDocument at line 76), type is at the root level of the _pageset JSON. The implementation checks pagesetJson?.type, which expects type at the root level.

This test passes because resolveUrlTemplateOfChild uses entityPageSetUrlTemplates regardless of the type field, but the mock data structure is misleading.

Suggested fix
     const directoryDocWithBothTemplates = {
       ...mockDirectoryMergedDocument,
       _pageset: JSON.stringify({
+        type: "DIRECTORY",
         config: {
-          type: "DIRECTORY",
           urlTemplate: {
             primary: "directory/[[address.city]]/[[id]]",
             alternate: "[[locale]]/directory/[[address.city]]/[[id]]",
           },
         },
       }),
     };

Copy link
Contributor

@benlife5 benlife5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't been following the url template conversations super closely but this LGTM in general

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/visual-editor/src/internal/hooks/layout/useMessageReceivers.ts (1)

81-88: Pass the streamDocument as the child profile.

resolveUrlTemplateOfChild merges profile into the stream document. Passing {} drops fields like id, address, and slug, which can make child resolvers fail and throw for locator/dm_ entities.

🛠️ Proposed fix
-      path = resolveUrlTemplateOfChild({}, streamDocument, "");
+      path = resolveUrlTemplateOfChild(streamDocument, streamDocument, "");
packages/visual-editor/src/utils/schema/resolveSchema.ts (1)

19-42: Avoid double‑escaping embedded values in schema strings.

stringifyResolvedField currently escapes non‑string values and then the schema is serialized again later, which can produce backslashes in output (double‑escaped JSON). Return the raw stringified value and let the final serialization handle escaping.

🛠️ Proposed fix
-  const jsonStringLiteral = JSON.stringify(stringToEmbed);
-
-  // We return the content *inside* the quotes, which is the properly escaped string.
-  return jsonStringLiteral.slice(1, -1);
+  return stringToEmbed;

Based on learnings, pre‑escaping causes double‑escaping when the resolved object is later JSON.stringify’d.

🤖 Fix all issues with AI agents
In `@packages/visual-editor/src/utils/urls/legacyResolveUrlTemplate.ts`:
- Around line 22-24: Wrap the JSON.parse calls in try-catch to safely handle
malformed JSON: when parsing streamDocument?.__?.entityPageSetUrlTemplates for
urlTemplates and the other parse inside legacyResolveUrlTemplate, catch any
SyntaxError and fall back to an empty object ({}), and log or silently ignore
the error as appropriate; update the parsing logic around the urlTemplates
variable and the parse at line 47 in legacyResolveUrlTemplate to use this
try-catch pattern so invalid JSON does not throw.

In `@packages/visual-editor/src/utils/urls/resolveUrlTemplate.ts`:
- Around line 95-109: The mergeMeta function currently ignores the child
(streamDocument) locale and profile.locale, causing child URLs to inherit the
parent locale; update locale resolution to prefer the child locale first and
fall back to profile meta or profile.locale (e.g., set locale =
streamDocument?.locale || profile?.meta?.locale || profile?.locale), and adjust
isPrimaryLocale to consult streamDocument?.meta?.isPrimaryLocale before falling
back to profile?.meta?.isPrimaryLocale and finally to the locale === "en"
default; make these changes inside mergeMeta to preserve child locale and
correct primary-locale logic.

@asanehisa asanehisa requested a review from mkilpatrick January 26, 2026 17:17
mkilpatrick
mkilpatrick previously approved these changes Jan 26, 2026
Copy link
Collaborator

@mkilpatrick mkilpatrick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is cleaner!

@asanehisa asanehisa merged commit b9cbdc6 into main Jan 26, 2026
18 checks passed
@asanehisa asanehisa deleted the pathInfo branch January 26, 2026 19:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

create-dev-release Triggers dev release workflow

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants