diff --git a/.changeset/silver-lions-sort.md b/.changeset/silver-lions-sort.md new file mode 100644 index 0000000000..a810d49090 --- /dev/null +++ b/.changeset/silver-lions-sort.md @@ -0,0 +1,5 @@ +--- +'@tanstack/start-plugin-core': patch +--- + +Sort server function manifest entries by ID before emitting the resolver module. The `serverFnsById` map is populated in source-file scan order, which varies across machines and incremental builds, producing non-deterministic key ordering in the emitted `__tanstack-start-server-fn-resolver-*.mjs` artifact. Stable alphabetic sorting ensures reproducible builds, consistent content hashes, and clean `git diff` on committed artifacts. diff --git a/packages/start-plugin-core/src/start-compiler/server-fn-resolver-module.ts b/packages/start-plugin-core/src/start-compiler/server-fn-resolver-module.ts index 2635c38e07..8f872a48c1 100644 --- a/packages/start-plugin-core/src/start-compiler/server-fn-resolver-module.ts +++ b/packages/start-plugin-core/src/start-compiler/server-fn-resolver-module.ts @@ -16,12 +16,19 @@ interface GenerateServerFnResolverModuleOptions { function getResolverManifestEntries( serverFnsById: Record, ): Array { - return Object.entries(serverFnsById).map(([id, fn]) => ({ - id, - functionName: fn.functionName, - extractedFilename: fn.extractedFilename, - isClientReferenced: fn.isClientReferenced ?? true, - })) + return ( + Object.entries(serverFnsById) + // Sort entries by ID so that the generated manifest has a stable, deterministic order. + // Non-deterministic ordering causes the compiled hash of the same source file to change + // between builds, breaking content-addressed caching and reproducible deployments. + .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)) + .map(([id, fn]) => ({ + id, + functionName: fn.functionName, + extractedFilename: fn.extractedFilename, + isClientReferenced: fn.isClientReferenced ?? true, + })) + ) } function getClientReferencedCheck(