Skip to content

feat: scope Liquid objects to step URIs for Flow config field LSP support#1238

Draft
Gasser-Aly wants to merge 4 commits into
mainfrom
gasser/flow-app-support-prototype
Draft

feat: scope Liquid objects to step URIs for Flow config field LSP support#1238
Gasser-Aly wants to merge 4 commits into
mainfrom
gasser/flow-app-support-prototype

Conversation

@Gasser-Aly

@Gasser-Aly Gasser-Aly commented Jun 17, 2026

Copy link
Copy Markdown

Why

Flow's Workflow Editor is a highly dynamic environment. As users build workflows, each step has its own runtime context — a step triggered by "customer created" has completely different Liquid objects available than one triggered by "order created". When a user changes a trigger or reconfigures a step, the available objects change too.

Each step can have multiple config fields, and each config field is an independently-editable Liquid snippet (a separate file). All config fields within a step share the same set of Liquid objects — the objects provided by that step's environment, fetched from the backend.

Before this PR, the Liquid LSP had no concept of per-file or per-step object scoping. It could only use the global theme object list, which meant:

  • No completions for step-specific objects (event properties, step variables, etc.)
  • False "undefined object" errors on perfectly valid Liquid
  • No type-aware property traversal for Flow-specific types

This PR introduces the mechanism for clients to register custom Liquid objects scoped to a URI prefix, so the Flow UI can tell the LSP exactly which objects are valid for a given step's config fields.

What this PR adds

  • setObjectsForURI / getObjectsForURI on AugmentedThemeDocset — stores and looks up ObjectEntry[] by exact URI or prefix match, so all config field files under a step URI share the same registered objects.
  • shopify/setObjects LSP notification — a new SetObjectsNotification that clients send to register custom objects for a URI. The Flow UI sends this after fetching the step's environment from the backend.
  • shopify.supportsSetObjects initialization flag — clients opt in by setting this in initializationOptions. When true, TypeSystem bypasses memoization for object lookups (since results are now URI-dependent) and routes through per-URI objects. When false (the default), all existing memoization is preserved — no performance impact for theme developers.
  • URI threading through TypeSystemobjectEntries, _objectMap, and globalVariables now receive the current file URI and use per-URI objects when the flag is on, falling back to memo'd global results otherwise.
  • Suppressed core property completions in ObjectAttributeCompletionProvider when custom objects are registered for the current URI — avoids surfacing misleading array/string built-in suggestions in Flow's custom object context.

What's next

  • The Flow UI client (useLiquidLsp) already calls updateObjects(stepUri, lspObjects) — it needs to be wired to send the shopify/setObjects notification using this new mechanism, and pass shopify.supportsSetObjects: true in initializationOptions.
  • Consider adding cleanup logic to remove URI-scoped objects when files are closed or the step is removed.

@Gasser-Aly Gasser-Aly changed the title Gasser/flow app support prototype Add URI-scoped object entries support via setObjects notification Jun 17, 2026
@Gasser-Aly Gasser-Aly changed the title Add URI-scoped object entries support via setObjects notification feat: scope Liquid objects to step URIs for Flow config field LSP support Jun 17, 2026
…s init flag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment on lines +768 to +770
connection.onNotification(SetObjectsNotification.type, ({ uri, objects }) => {
themeDocset.setObjectsForURI(uri, objects);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why a notification and not a request? Does the client not care about if/when this resolves? Don't care to await to make sure the objects are set before proceeding?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

100% valid concern

e.g.

  • flow opens a config field doc
  • the doc contains some object
  • the language server receives textDocument/didOpen
  • this triggers runChecks
  • at this point Flow hasn't sent shopify/setObjects
  • unknown object occurs

Comment on lines +768 to +770
connection.onNotification(SetObjectsNotification.type, ({ uri, objects }) => {
themeDocset.setObjectsForURI(uri, objects);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

100% valid concern

e.g.

  • flow opens a config field doc
  • the doc contains some object
  • the language server receives textDocument/didOpen
  • this triggers runChecks
  • at this point Flow hasn't sent shopify/setObjects
  • unknown object occurs

if (exact) return exact;

// Prefix match: allows registering by step path and matching file URIs within it
for (const [prefix, objects] of this.objectsByPrefix) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A URI under step-10 can match a previously registered step-1 prefix, and overlapping prefixes will not pick the most specific match. This should use a normalized prefix boundary and/or longest-prefix match.

{
"name": "@shopify/theme-check-common",
"version": "3.26.1",
"version": "3.26.1-flow.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You need to also update theme-language-server-common's package.json so it uses it

Comment on lines +302 to +305
if (this.isUriScopedMode() && uri && this.themeDocset.getObjectsForURI) {
const perURI = this.themeDocset.getObjectsForURI(uri);
if (perURI) return perURI;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Are we okay if Flow sends only step-specific objects, built-in Shopify types and augmented objects disappear for completion/hover/diagnostics? More question than concern here.

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.

3 participants