Skip to content

Skip non-theme directories when preloading files (for the LSP)#1152

Open
elia wants to merge 1 commit intoShopify:mainfrom
elia:elia/known-folders
Open

Skip non-theme directories when preloading files (for the LSP)#1152
elia wants to merge 1 commit intoShopify:mainfrom
elia:elia/known-folders

Conversation

@elia
Copy link

@elia elia commented Mar 16, 2026

What are you adding in this PR?

Fixes #1151.

When preloading files at startup, the language server was recursively loading all .json files under the theme root — including unrelated data files (e.g. data/orders.json). A single large JSON file parsed into an AST can consume 1–2 GB of heap, causing the server to OOM on repos that happen to have a data/ directory alongside the theme.

This change restricts preloading to Shopify theme directories only: assets, blocks, config, layout, locales, sections, snippets, templates.

What's next? Any followup issues?

None.

What did you learn?

Nothing surprising in retrospect, but worth noting: recursiveReadDirectory starts from the workspace root, not the theme root — so any large files anywhere in the repo are fair game without an explicit list.

Before you deploy

  • I included a patch bump changeset

Restrict preload() to Shopify theme directories (assets, blocks,
config, layout, locales, sections, snippets, templates). Previously
all .json files under the root were loaded, including unrelated data
files. A single 175 MB JSON file parsed into an AST can consume
1-2 GB of heap, easily exhausting even an 8 GB limit on large repos.

Co-Authored-By: Claude <noreply@anthropic.com>
@elia elia requested a review from a team as a code owner March 16, 2026 16:36
Copy link
Contributor

@graygilmore graygilmore left a comment

Choose a reason for hiding this comment

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

This makes sense. I'm going to just check with the team that we don't want to make any exceptions for files in the root directory.

Copy link
Contributor

@aswamy aswamy left a comment

Choose a reason for hiding this comment

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

Thank you for your contribution @elia !

I would suggest one thing here. The current approach does work, but can still be costly if there are deeply nested files (like modules in your theme directory).

If we move this change to the recursiveReadDirectory function itself, we could prevent unnecessary reads of directories at the top-level of the recursive call.

e.g.

// context-utils.js

export async function recursiveReadDirectory(
  fs: AbstractFileSystem,
  uri: string,
  filter: (fileTuple: FileTuple) => boolean,
  directoryFilter?: (fileTuple: FileTuple) => boolean,
): Promise<UriString[]> {
  const allFiles = await fs.readDirectory(uri);
  const files = allFiles.filter(
    (ft) =>
      !isIgnored(ft) &&
      (isDirectory(ft) ? (directoryFilter ? directoryFilter(ft) : true) : filter(ft)),
  );

  const results = await Promise.all(
    files.map((ft) => {
      if (isDirectory(ft)) {
        // Only apply directoryFilter at the root level — once inside a theme
        // directory, recurse freely.
        return recursiveReadDirectory(fs, ft[0], filter);
      } else {
        return Promise.resolve([ft[0]]);
      }
    }),
  );

  return results.flat();
}
// DocumentManager.ts

const THEME_DIRS =
  /\/(assets|blocks|config|layout|locales|sections|snippets|templates)$/;
const filesToLoad = await recursiveReadDirectory(
  this.fs,
  rootUri,
  ([uri]) => /\.(liquid|json)$/.test(uri) && !this.sourceCodes.has(uri),
  ([uri]) => THEME_DIRS.test(uri),
);

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.

theme-language-server OOM crash on large themes (~600 files) — Electron's 4GB heap limit

3 participants