diff --git a/lib/modules/manager/bazel/__snapshots__/extract.spec.ts.snap b/lib/modules/manager/bazel/__snapshots__/extract.spec.ts.snap
index 096eb05ff041a03ab1b859dbb4f6a8a369023121..068fe046658f1a018068660c7cb198688a7e7cb6 100644
--- a/lib/modules/manager/bazel/__snapshots__/extract.spec.ts.snap
+++ b/lib/modules/manager/bazel/__snapshots__/extract.spec.ts.snap
@@ -8,11 +8,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "com_github_bitly_go-nsq",
     "depType": "go_repository",
     "managerData": {
-      "def": "go_repository(
-    name = "com_github_bitly_go-nsq",
-    importpath = "github.com/bitly/go-nsq",
-    tag = "v1.0.5"
-)",
+      "idx": 0,
     },
     "packageName": "github.com/bitly/go-nsq",
   },
@@ -25,11 +21,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depType": "go_repository",
     "digestOneAndOnly": true,
     "managerData": {
-      "def": "go_repository(
-   name = "com_github_google_uuid",
-   importpath = "github.com/google/uuid",
-   commit = "dec09d789f3dba190787f8b4454c7d3c936fed9e"
-)",
+      "idx": 1,
     },
     "packageName": "github.com/google/uuid",
   },
@@ -39,11 +31,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "com_gopkgin_mgo_v2",
     "depType": "go_repository",
     "managerData": {
-      "def": "go_repository(
-    name = "com_gopkgin_mgo_v2",
-    importpath = "gopkg.in/mgo.v2",
-    tag = "v2"
-)",
+      "idx": 2,
     },
     "packageName": "gopkg.in/mgo.v2",
   },
@@ -53,11 +41,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "build_bazel_rules_nodejs",
     "depType": "git_repository",
     "managerData": {
-      "def": "git_repository(
-    name = "build_bazel_rules_nodejs",
-    remote = "https://github.com/bazelbuild/rules_nodejs.git",
-    tag = "0.3.1",
-)",
+      "idx": 3,
     },
     "packageName": "bazelbuild/rules_nodejs",
   },
@@ -67,11 +51,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "build_bazel_rules_typescript",
     "depType": "git_repository",
     "managerData": {
-      "def": "git_repository(
-    name = "build_bazel_rules_typescript",
-    remote = "https://github.com/bazelbuild/rules_typescript.git",
-    tag = "0.6.1",
-)",
+      "idx": 4,
     },
     "packageName": "bazelbuild/rules_typescript",
   },
@@ -81,12 +61,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "distroless",
     "depType": "http_archive",
     "managerData": {
-      "def": "http_archive(
-    name="distroless",
-    sha256="f7a6ecfb8174a1dd4713ea3b21621072996ada7e8f1a69e6ae7581be137c6dd6",
-    strip_prefix="distroless-446923c3756ceeaa75888f52fcbdd48bb314fbf8",
-    urls=["https://github.com/GoogleContainerTools/distroless/archive/446923c3756ceeaa75888f52fcbdd48bb314fbf8.tar.gz"]
-)",
+      "idx": 5,
     },
     "packageName": "GoogleContainerTools/distroless",
   },
@@ -96,15 +71,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "bazel_toolchains",
     "depType": "http_archive",
     "managerData": {
-      "def": "http_archive(
-  name = "bazel_toolchains",
-    sha256 = "4b1468b254a572dbe134cc1fd7c6eab1618a72acd339749ea343bd8f55c3b7eb",
-    strip_prefix = "bazel-toolchains-d665ccfa3e9c90fa789671bf4ef5f7c19c5715c4",
-    urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/d665ccfa3e9c90fa789671bf4ef5f7c19c5715c4.tar.gz",
-        "https://github.com/bazelbuild/bazel-toolchains/archive/d665ccfa3e9c90fa789671bf4ef5f7c19c5715c4.tar.gz",
-    ],
-)",
+      "idx": 6,
     },
     "packageName": "bazelbuild/bazel-toolchains",
   },
@@ -114,13 +81,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "rules_nodejs",
     "depType": "http_archive",
     "managerData": {
-      "def": "http_archive(
-    name = "rules_nodejs",
-    sha256 = "5aef09ed3279aa01d5c928e3beb248f9ad32dde6aafe6373a8c994c3ce643064",
-    urls = [
-      "https://github.com/bazelbuild/rules_nodejs/releases/download/5.5.3/rules_nodejs-core-5.5.3.tar.gz"
-    ],
-)",
+      "idx": 7,
     },
     "packageName": "bazelbuild/rules_nodejs",
   },
@@ -130,11 +91,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "io_bazel_rules_sass",
     "depType": "git_repository",
     "managerData": {
-      "def": "git_repository(
-    name = "io_bazel_rules_sass",
-    remote = "https://github.com/bazelbuild/rules_sass.git",
-    tag = "0.0.3",
-)",
+      "idx": 8,
     },
     "packageName": "bazelbuild/rules_sass",
   },
@@ -144,12 +101,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "com_github_bazelbuild_buildtools",
     "depType": "git_repository",
     "managerData": {
-      "def": "git_repository(
-    name = "com_github_bazelbuild_buildtools",
-    remote = "https://github.com/bazelbuild/buildtools.git",
-    # Note, this commit matches the version of buildifier in angular/ngcontainer
-    commit = "b3b620e8bcff18ed3378cd3f35ebeb7016d71f71",
-)",
+      "idx": 9,
     },
     "packageName": "bazelbuild/buildtools",
   },
@@ -159,11 +111,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "io_bazel_rules_go",
     "depType": "http_archive",
     "managerData": {
-      "def": "http_archive(
-    name = "io_bazel_rules_go",
-    url = "https://github.com/bazelbuild/rules_go/releases/download/0.7.1/rules_go-0.7.1.tar.gz",
-    sha256 = "341d5eacef704415386974bc82a1783a8b7ffbff2ab6ba02375e1ca20d9b031c",
-)",
+      "idx": 10,
     },
     "packageName": "bazelbuild/rules_go",
   },
@@ -173,15 +121,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "bazel_skylib",
     "depType": "http_archive",
     "managerData": {
-      "def": "http_archive(
-    name = "bazel_skylib",
-    sha256 = "b5f6abe419da897b7901f90cbab08af958b97a8f3575b0d3dd062ac7ce78541f",
-    strip_prefix = "bazel-skylib-0.5.0",
-    urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/archive/0.5.0.tar.gz",
-        "https://github.com/bazelbuild/bazel-skylib/archive/0.5.0.tar.gz",
-    ],
-)",
+      "idx": 11,
     },
     "packageName": "bazelbuild/bazel-skylib",
   },
@@ -191,12 +131,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "distroless",
     "depType": "http_archive",
     "managerData": {
-      "def": "http_archive(
-    name="distroless",
-    sha256="f7a6ecfb8174a1dd4713ea3b21621072996ada7e8f1a69e6ae7581be137c6dd6",
-    strip_prefix="distroless-446923c3756ceeaa75888f52fcbdd48bb314fbf8",
-    urls=["https://github.com/GoogleContainerTools/distroless/archive/446923c3756ceeaa75888f52fcbdd48bb314fbf8.tar.gz"]
-)",
+      "idx": 12,
     },
     "packageName": "GoogleContainerTools/distroless",
   },
@@ -206,12 +141,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "io_bazel_rules_go",
     "depType": "http_archive",
     "managerData": {
-      "def": "maybe(
-  http_archive,
-  name = "io_bazel_rules_go",
-  sha256 = "2b1641428dff9018f9e85c0384f03ec6c10660d935b750e3fa1492a281a53b0f",
-  url = "https://github.com/bazelbuild/rules_go/releases/download/v0.29.0/rules_go-v0.29.0.zip",
-)",
+      "idx": 13,
     },
     "packageName": "bazelbuild/rules_go",
   },
@@ -221,15 +151,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "bazel_gazelle",
     "depType": "http_archive",
     "managerData": {
-      "def": "maybe(
-    http_archive,
-    name = "bazel_gazelle",
-    sha256 = "de69a09dc70417580aabf20a28619bb3ef60d038470c7cf8442fafcf627c21cb",
-    urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.24.0/bazel-gazelle-v0.24.0.tar.gz",
-        "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.24.0/bazel-gazelle-v0.24.0.tar.gz",
-    ],
-)",
+      "idx": 14,
     },
     "packageName": "bazelbuild/bazel-gazelle",
   },
@@ -242,12 +164,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depType": "go_repository",
     "digestOneAndOnly": true,
     "managerData": {
-      "def": "maybe(
-    go_repository,
-    name = "com_github_pkg_errors",
-    commit = "816c9085562cd7ee03e7f8188a1cfd942858cded",
-    importpath = "github.com/pkg/errors",
-)",
+      "idx": 15,
     },
     "packageName": "github.com/pkg/errors",
   },
@@ -258,13 +175,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "py3_image_base",
     "depType": "container_pull",
     "managerData": {
-      "def": "container_pull(
-    name = "py3_image_base",
-    digest = "sha256:d5a717649fd93ea5b9c430d7f84e4c37ba219eb53bd73ed1d4a5a98e9edd84a7",
-    registry = "gcr.io",
-    repository = "distroless/python3-debian10",
-    tag = "latest",
-)",
+      "idx": 16,
     },
     "packageName": "distroless/python3-debian10",
     "registryUrls": [
@@ -278,12 +189,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() extracts multiple ty
     "depName": "distroless",
     "depType": "http_file",
     "managerData": {
-      "def": "http_file(
-    name="distroless",
-    sha256="f7a6ecfb8174a1dd4713ea3b21621072996ada7e8f1a69e6ae7581be137c6dd6",
-    strip_prefix="distroless-446923c3756ceeaa75888f52fcbdd48bb314fbf8",
-    urls=["https://github.com/GoogleContainerTools/distroless/archive/446923c3756ceeaa75888f52fcbdd48bb314fbf8.tar.gz"]
-)",
+      "idx": 17,
     },
     "packageName": "GoogleContainerTools/distroless",
   },
@@ -298,12 +204,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() sequential http_arch
     "depName": "aspect_rules_js",
     "depType": "http_archive",
     "managerData": {
-      "def": "http_archive(
-            name = "aspect_rules_js",
-            sha256 = "db9f446752fe4100320cf8487e8fd476b9af0adf6b99b601bcfd70b289bb0598",
-            strip_prefix = "rules_js-1.1.2",
-            url = "https://github.com/aspect-build/rules_js/archive/refs/tags/v1.1.2.tar.gz",
-          )",
+      "idx": 0,
     },
     "packageName": "aspect-build/rules_js",
   },
@@ -313,11 +214,7 @@ exports[`modules/manager/bazel/extract extractPackageFile() sequential http_arch
     "depName": "rules_nodejs",
     "depType": "http_archive",
     "managerData": {
-      "def": "http_archive(
-              name = "rules_nodejs",
-              sha256 = "5aef09ed3279aa01d5c928e3beb248f9ad32dde6aafe6373a8c994c3ce643064",
-              urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/5.5.3/rules_nodejs-core-5.5.3.tar.gz"],
-          )",
+      "idx": 1,
     },
     "packageName": "bazelbuild/rules_nodejs",
   },
diff --git a/lib/modules/manager/bazel/common.spec.ts b/lib/modules/manager/bazel/common.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e4946216e58282c583f41871f2fd24ac62b90a6c
--- /dev/null
+++ b/lib/modules/manager/bazel/common.spec.ts
@@ -0,0 +1,49 @@
+import { updateCode } from './common';
+
+describe('modules/manager/bazel/common', () => {
+  describe('updateCode', () => {
+    it('returns input for invalid', () => {
+      const input = `!@#`;
+      const output = updateCode(input, [0], 'foobar');
+      expect(output).toBe(input);
+    });
+
+    it('replaces whole rule', () => {
+      const input = `git_repository(name = "foo")`;
+      const output = updateCode(input, [0], 'abcde');
+      expect(output).toBe(`abcde`);
+    });
+
+    it('replaces rule key', () => {
+      const input = `git_repository(name = "foo")`;
+      const output = updateCode(input, [0, 'name'], 'bar');
+      expect(output).toBe(`git_repository(name = "bar")`);
+    });
+
+    it('returns input on wrong index', () => {
+      const input = `git_repository(name = "foo")`;
+      const output = updateCode(input, [1, 'name'], 'bar');
+      expect(output).toBe(input);
+    });
+
+    it('returns input on wrong key', () => {
+      const input = `git_repository(name = "foo")`;
+      const output = updateCode(input, [0, 'foobar'], 'bar');
+      expect(output).toBe(input);
+    });
+
+    it('replaces array values', () => {
+      const input = `git_repository(name = "foo", deps = ["bar", "baz", "qux"])`;
+      const output = updateCode(input, [0, 'deps', 1], 'BAZ');
+      expect(output).toBe(
+        `git_repository(name = "foo", deps = ["bar", "BAZ", "qux"])`
+      );
+    });
+
+    it('updates using function', () => {
+      const input = `git_repository(name = "foo")`;
+      const output = updateCode(input, [0, 'name'], (x) => x.toUpperCase());
+      expect(output).toBe(`git_repository(name = "FOO")`);
+    });
+  });
+});
diff --git a/lib/modules/manager/bazel/common.ts b/lib/modules/manager/bazel/common.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b13a23121c394d35bfba4f69c1cdb1cb84ce369b
--- /dev/null
+++ b/lib/modules/manager/bazel/common.ts
@@ -0,0 +1,74 @@
+import is from '@sindresorhus/is';
+import { parse } from './parser';
+import type { Fragment, FragmentPath, FragmentUpdater } from './types';
+
+export function findCodeFragment(
+  input: string,
+  path: FragmentPath
+): Fragment | null {
+  const parsed = parse(input);
+  if (!parsed) {
+    return null;
+  }
+
+  const [ruleIndex, ...restPath] = path;
+  let fragment: Fragment | undefined = parsed[ruleIndex];
+  for (let pathIndex = 0; pathIndex < restPath.length; pathIndex += 1) {
+    if (!fragment) {
+      break;
+    }
+
+    const key = restPath[pathIndex];
+
+    if (fragment.type === 'array' && is.number(key)) {
+      fragment = fragment.children[key];
+    }
+
+    if (fragment.type === 'record' && is.string(key)) {
+      fragment = fragment.children[key];
+    }
+  }
+
+  return fragment ?? null;
+}
+
+export function patchCodeAtFragment(
+  input: string,
+  fragment: Fragment,
+  updater: FragmentUpdater
+): string {
+  const { value, offset } = fragment;
+  const left = input.slice(0, offset);
+  const right = input.slice(offset + value.length);
+  return is.string(updater)
+    ? `${left}${updater}${right}`
+    : `${left}${updater(value)}${right}`;
+}
+
+export function patchCodeAtFragments(
+  input: string,
+  fragments: Fragment[],
+  updater: FragmentUpdater
+): string {
+  const sortedFragments = fragments.sort(
+    ({ offset: a }, { offset: b }) => b - a
+  );
+  let result = input;
+  for (const fragment of sortedFragments) {
+    result = patchCodeAtFragment(result, fragment, updater);
+  }
+  return result;
+}
+
+export function updateCode(
+  input: string,
+  path: FragmentPath,
+  updater: FragmentUpdater
+): string {
+  const fragment = findCodeFragment(input, path);
+  if (!fragment) {
+    return input;
+  }
+
+  return patchCodeAtFragment(input, fragment, updater);
+}
diff --git a/lib/modules/manager/bazel/extract.ts b/lib/modules/manager/bazel/extract.ts
index a273c6cf5b21856abff7cfb44a4ce05a482f08f3..072c632a181298d786904c409adc3d02fed3c2ee 100644
--- a/lib/modules/manager/bazel/extract.ts
+++ b/lib/modules/manager/bazel/extract.ts
@@ -22,7 +22,7 @@ export function extractPackageFile(
       continue;
     }
 
-    dep.managerData = { def: fragment.value };
+    dep.managerData = { idx };
     deps.push(dep);
   }
 
diff --git a/lib/modules/manager/bazel/types.ts b/lib/modules/manager/bazel/types.ts
index 951abee9c4ba6aa5e430b281619492538ef86ec2..56f16a17b53e56aa417d309e4109f925338e4bbc 100644
--- a/lib/modules/manager/bazel/types.ts
+++ b/lib/modules/manager/bazel/types.ts
@@ -5,7 +5,7 @@ export interface UrlParsedResult {
 }
 
 export interface BazelManagerData {
-  def: string;
+  idx: number;
 }
 
 export type TargetAttribute = string | string[];
@@ -41,3 +41,10 @@ export type FragmentData =
   | string
   | FragmentData[]
   | { [k: string]: FragmentData };
+
+export type FragmentPath =
+  | [number]
+  | [number, string]
+  | [number, string, number];
+
+export type FragmentUpdater = string | ((_: string) => string);
diff --git a/lib/modules/manager/bazel/update.spec.ts b/lib/modules/manager/bazel/update.spec.ts
index 8b343510e74c2685f49dbdf54b58814c5a0baa65..f336e00445006d2e491b0f667ca139fa85192fd9 100644
--- a/lib/modules/manager/bazel/update.spec.ts
+++ b/lib/modules/manager/bazel/update.spec.ts
@@ -21,7 +21,7 @@ describe('modules/manager/bazel/update', () => {
       const upgrade = {
         depName: 'build_bazel_rules_nodejs',
         depType: 'git_repository',
-        managerData: { def: input },
+        managerData: { idx: 0 },
         currentValue: '0.1.8',
         newValue: '0.2.0',
       };
@@ -49,7 +49,7 @@ describe('modules/manager/bazel/update', () => {
       const upgrade = {
         depName: 'build_bazel_rules_nodejs',
         depType: 'git_repository',
-        managerData: { def: input },
+        managerData: { idx: 0 },
         currentValue: '0.1.8',
         newValue: '0.2.0',
       };
@@ -86,7 +86,7 @@ describe('modules/manager/bazel/update', () => {
       const upgrade = {
         depName: 'hasura',
         depType: 'container_pull',
-        managerData: { def: input },
+        managerData: { idx: 0 },
         currentValue,
         newValue,
         currentDigest,
@@ -119,7 +119,7 @@ describe('modules/manager/bazel/update', () => {
       const upgrade = {
         depName: 'com_github_google_uuid',
         depType: 'go_repository',
-        managerData: { def: input },
+        managerData: { idx: 0 },
         currentValue: 'v0.0.0',
         currentDigest,
         newDigest,
@@ -127,17 +127,12 @@ describe('modules/manager/bazel/update', () => {
         updateType: 'major' as UpdateType,
       };
 
-      const output = input.replace(
-        `"${currentDigest}"`,
-        `"${newDigest}",  # ${newValue}`
-      );
+      const output = input.replace(`"${currentDigest}"`, `"${newDigest}"`);
 
       const res = await updateDependency({ fileContent: input, upgrade });
 
       expect(res).toEqual(output);
-      expect(res).toContain(
-        '"aaa09d789f3dba190787f8b4454c7d3c936fe123",  # v1.0.3'
-      );
+      expect(res).toContain('"aaa09d789f3dba190787f8b4454c7d3c936fe123"');
     });
 
     it('updates commit-based http archive', async () => {
@@ -158,7 +153,7 @@ describe('modules/manager/bazel/update', () => {
         depName: 'distroless',
         depType: 'http_archive',
         repo: 'GoogleContainerTools/distroless',
-        managerData: { def: input },
+        managerData: { idx: 0 },
         currentDigest,
         newDigest,
       };
@@ -204,7 +199,7 @@ describe('modules/manager/bazel/update', () => {
         depName: 'bazel_skylib',
         depType: 'http_archive',
         repo: 'bazelbuild/bazel-skylib',
-        managerData: { def: input },
+        managerData: { idx: 0 },
         currentValue,
         newValue,
       };
@@ -248,7 +243,7 @@ describe('modules/manager/bazel/update', () => {
         depName: 'bazel_skylib',
         depType: 'http_archive',
         repo: 'bazelbuild/bazel-skylib',
-        managerData: { def: input },
+        managerData: { idx: 0 },
         currentValue,
         newValue,
       };
@@ -293,7 +288,7 @@ describe('modules/manager/bazel/update', () => {
         depName: 'bazel_skylib',
         depType: 'http_archive',
         repo: 'bazelbuild/bazel-skyfoo',
-        managerData: { def: input },
+        managerData: { idx: 0 },
         currentValue,
         newValue,
       };
@@ -321,7 +316,7 @@ describe('modules/manager/bazel/update', () => {
         depName: 'bazel_skylib',
         depType: 'http_archive',
         repo: 'bazelbuild/bazel-skylib',
-        managerData: { def: input },
+        managerData: { idx: 0 },
         currentValue: '0.5.0',
         newValue: '0.6.2',
       };
@@ -343,7 +338,7 @@ describe('modules/manager/bazel/update', () => {
         depName: 'bazel_skylib',
         depType: 'http_archive',
         repo: 'bazelbuild/bazel-skylib',
-        managerData: { def: input },
+        managerData: { idx: 0 },
         currentValue: '0.5.0',
         newValue: '0.6.2',
       };
@@ -372,7 +367,7 @@ describe('modules/manager/bazel/update', () => {
         depName: 'bazel_skylib',
         depType: 'http_archive',
         repo: 'bazelbuild/bazel-skylib',
-        managerData: { def: input },
+        managerData: { idx: 0 },
         currentValue,
         newValue,
       };
@@ -426,7 +421,7 @@ describe('modules/manager/bazel/update', () => {
         depName: 'bazel_skylib',
         depType: 'http_archive',
         repo: 'bazelbuild/bazel-skylib',
-        managerData: { def: input },
+        managerData: { idx: 0 },
         currentValue,
         newValue,
       };
@@ -484,7 +479,7 @@ describe('modules/manager/bazel/update', () => {
       depName: 'rules_nodejs',
       depType: 'http_archive',
       repo: 'bazelbuild/rules_nodejs',
-      managerData: { def: upgraded_http_archive },
+      managerData: { idx: 1 },
       currentValue: currentValue1,
       newValue: newValue1,
     };
@@ -543,7 +538,7 @@ describe('modules/manager/bazel/update', () => {
       depName: 'rules_nodejs',
       depType: 'http_archive',
       repo: 'bazelbuild/rules_nodejs',
-      managerData: { def: upgraded_http_archive },
+      managerData: { idx: 1 },
       currentValue: currentValue1,
       newValue: newValue1,
     };
diff --git a/lib/modules/manager/bazel/update.ts b/lib/modules/manager/bazel/update.ts
index 9c4b0904809b063a9ac19ca3d9347c5d296fb393..873733a4515e80bb81c74be44075300a76363143 100644
--- a/lib/modules/manager/bazel/update.ts
+++ b/lib/modules/manager/bazel/update.ts
@@ -1,54 +1,68 @@
-// TODO: types (#7154)
-/* eslint-disable @typescript-eslint/restrict-template-expressions */
+import is from '@sindresorhus/is';
 import hasha from 'hasha';
 import { logger } from '../../../logger';
 import * as packageCache from '../../../util/cache/package';
 import { Http } from '../../../util/http';
+import { map as pMap } from '../../../util/promises';
 import { regEx } from '../../../util/regex';
 import type { UpdateDependencyConfig } from '../types';
-import type { BazelManagerData } from './types';
+import { findCodeFragment, patchCodeAtFragments, updateCode } from './common';
+import type { BazelManagerData, RecordFragment, StringFragment } from './types';
 
 const http = new Http('bazel');
 
-function updateWithNewVersion(
-  content: string,
-  currentValue: string,
-  newValue: string
-): string {
-  // istanbul ignore if
-  if (currentValue === newValue) {
-    return content;
+function getUrlFragments(rule: RecordFragment): StringFragment[] {
+  const urls: StringFragment[] = [];
+
+  const urlRecord = rule.children['url'];
+  if (urlRecord?.type === 'string') {
+    urls.push(urlRecord);
+  }
+
+  const urlsRecord = rule.children['urls'];
+  if (urlsRecord?.type === 'array') {
+    for (const urlRecord of urlsRecord.children) {
+      if (urlRecord.type === 'string') {
+        urls.push(urlRecord);
+      }
+    }
   }
-  const replaceFrom = currentValue.replace(regEx(/^v/), '');
-  const replaceTo = newValue.replace(regEx(/^v/), '');
-  let newContent = content;
-  do {
-    newContent = newContent.replace(replaceFrom, replaceTo);
-  } while (newContent.includes(replaceFrom));
-  return newContent;
+
+  return urls;
 }
 
-function extractUrl(flattened: string): string[] | null {
-  const urlMatch = regEx(/url="(.*?)"/).exec(flattened);
-  if (!urlMatch) {
-    logger.debug('Cannot locate urls in new definition');
-    return null;
+const urlMassages = {
+  'bazel-skylib.': 'bazel_skylib-',
+  '/bazel-gazelle/releases/download/0': '/bazel-gazelle/releases/download/v0',
+  '/bazel-gazelle-0': '/bazel-gazelle-v0',
+  '/rules_go/releases/download/0': '/rules_go/releases/download/v0',
+  '/rules_go-0': '/rules_go-v0',
+};
+
+function massageUrl(url: string): string {
+  let result = url;
+  for (const [from, to] of Object.entries(urlMassages)) {
+    result = result.replace(from, to);
   }
-  return [urlMatch[1]];
+  return result;
+}
+
+function replaceAll(input: string, from: string, to: string): string {
+  return input.split(from).join(to);
 }
 
-function extractUrls(content: string): string[] | null {
-  const flattened = content.replace(regEx(/\n/g), '').replace(regEx(/\s/g), '');
-  const urlsMatch = regEx(/urls?=\[.*?\]/).exec(flattened);
-  if (!urlsMatch) {
-    return extractUrl(flattened);
+function replaceValues(
+  content: string,
+  from: string | null | undefined,
+  to: string | null | undefined
+): string {
+  // istanbul ignore if
+  if (!from || !to || from === to) {
+    return content;
   }
-  const urls = urlsMatch[0]
-    .replace(regEx(/urls?=\[/), '')
-    .replace(regEx(/,?\]$/), '')
-    .split(',')
-    .map((url) => url.replace(regEx(/"/g), ''));
-  return urls;
+  const massagedFrom = from.replace(regEx(/^v/), '');
+  const massagedTo = to.replace(regEx(/^v/), '');
+  return replaceAll(content, massagedFrom, massagedTo);
 }
 
 async function getHashFromUrl(url: string): Promise<string | null> {
@@ -75,22 +89,20 @@ async function getHashFromUrl(url: string): Promise<string | null> {
 
 async function getHashFromUrls(urls: string[]): Promise<string | null> {
   const hashes = (
-    await Promise.all(urls.map((url) => getHashFromUrl(url)))
-  ).filter(Boolean);
-  const distinctHashes = [...new Set(hashes)];
-  if (!distinctHashes.length) {
-    logger.debug({ hashes, urls }, 'Could not calculate hash for URLs');
+    await pMap(urls, (url) => getHashFromUrl(massageUrl(url)))
+  ).filter(is.truthy);
+  if (!hashes.length) {
+    logger.debug({ urls }, 'Could not calculate hash for URLs');
     return null;
   }
+
+  const distinctHashes = new Set(hashes);
   // istanbul ignore if
-  if (distinctHashes.length > 1) {
+  if (distinctHashes.size > 1) {
     logger.warn({ urls }, 'Found multiple hashes for single def');
   }
-  return distinctHashes[0];
-}
 
-function setNewHash(content: string, hash: string): string {
-  return content.replace(regEx(/(sha256\s*=\s*)"[^"]+"/), `$1"${hash}"`);
+  return hashes[0];
 }
 
 export async function updateDependency({
@@ -98,83 +110,77 @@ export async function updateDependency({
   upgrade,
 }: UpdateDependencyConfig<BazelManagerData>): Promise<string | null> {
   try {
-    logger.debug(
-      `bazel.updateDependency(): ${upgrade.newValue ?? upgrade.newDigest}`
-    );
-    let newDef: string | undefined;
-    if (upgrade.depType === 'container_pull' && upgrade.managerData?.def) {
-      newDef = upgrade.managerData.def
-        .replace(regEx(/(tag\s*=\s*)"[^"]+"/), `$1"${upgrade.newValue}"`)
-        .replace(regEx(/(digest\s*=\s*)"[^"]+"/), `$1"${upgrade.newDigest}"`);
+    const { newValue, newDigest } = upgrade;
+    logger.debug({ newValue, newDigest }, `bazel.updateDependency()`);
+    const idx = upgrade.managerData!.idx;
+
+    if (upgrade.depType === 'container_pull') {
+      let result = fileContent;
+
+      if (newValue) {
+        result = updateCode(result, [idx, 'tag'], newValue);
+      }
+
+      if (newDigest) {
+        result = updateCode(result, [idx, 'digest'], newDigest);
+      }
+
+      return result;
     }
+
     if (
-      (upgrade.depType === 'git_repository' ||
-        upgrade.depType === 'go_repository') &&
-      upgrade.managerData?.def
+      upgrade.depType === 'git_repository' ||
+      upgrade.depType === 'go_repository'
     ) {
-      newDef = upgrade.managerData.def
-        .replace(regEx(/(tag\s*=\s*)"[^"]+"/), `$1"${upgrade.newValue}"`)
-        .replace(regEx(/(commit\s*=\s*)"[^"]+"/), `$1"${upgrade.newDigest}"`);
-      if (upgrade.currentDigest && upgrade.updateType !== 'digest') {
-        newDef = newDef.replace(
-          regEx(/(commit\s*=\s*)"[^"]+".*?\n/),
-          `$1"${upgrade.newDigest}",  # ${upgrade.newValue}\n`
-        );
+      let result = fileContent;
+
+      if (newValue) {
+        result = updateCode(result, [idx, 'tag'], newValue);
       }
-    } else if (
-      (upgrade.depType === 'http_archive' || upgrade.depType === 'http_file') &&
-      upgrade.managerData?.def &&
-      (upgrade.currentValue || upgrade.currentDigest) &&
-      (upgrade.newValue ?? upgrade.newDigest)
-    ) {
-      newDef = updateWithNewVersion(
-        upgrade.managerData.def,
-        (upgrade.currentValue ?? upgrade.currentDigest)!,
-        (upgrade.newValue ?? upgrade.newDigest)!
-      );
-      const massages = {
-        'bazel-skylib.': 'bazel_skylib-',
-        '/bazel-gazelle/releases/download/0':
-          '/bazel-gazelle/releases/download/v0',
-        '/bazel-gazelle-0': '/bazel-gazelle-v0',
-        '/rules_go/releases/download/0': '/rules_go/releases/download/v0',
-        '/rules_go-0': '/rules_go-v0',
-      };
-      for (const [from, to] of Object.entries(massages)) {
-        newDef = newDef.replace(from, to);
+
+      if (newDigest) {
+        result = updateCode(result, [idx, 'commit'], newDigest);
+      }
+
+      return result;
+    }
+
+    if (upgrade.depType === 'http_file' || upgrade.depType === 'http_archive') {
+      const rule = findCodeFragment(fileContent, [idx]);
+      // istanbul ignore if
+      if (rule?.type !== 'record') {
+        return null;
       }
-      const urls = extractUrls(newDef);
-      if (!urls?.length) {
-        logger.debug({ newDef }, 'urls is empty');
+
+      const urlFragments = getUrlFragments(rule);
+      if (!urlFragments?.length) {
+        logger.debug({ def: rule.value }, 'urls is empty');
         return null;
       }
+
+      const updateValues = (oldUrl: string): string => {
+        let url = oldUrl;
+        url = replaceValues(url, upgrade.currentValue, upgrade.newValue);
+        url = replaceValues(url, upgrade.currentDigest, upgrade.newDigest);
+        return url;
+      };
+
+      const urls = urlFragments.map(({ value }) => updateValues(value));
       const hash = await getHashFromUrls(urls);
       if (!hash) {
         return null;
       }
-      logger.debug({ hash }, 'Calculated hash');
-      newDef = setNewHash(newDef, hash);
-    }
-    logger.debug({ oldDef: upgrade.managerData?.def, newDef });
-
-    // istanbul ignore if: needs test
-    if (!newDef) {
-      return null;
-    }
 
-    let existingRegExStr = `(?:maybe\\s*\\(\\s*)?${upgrade.depType}(?:\\(|,)[^\\)]+name\\s*=\\s*"${upgrade.depName}"(.*\\n)+?\\s*\\)`;
-    if (newDef.endsWith('\n')) {
-      existingRegExStr += '\n';
+      let result = fileContent;
+      result = patchCodeAtFragments(result, urlFragments, updateValues);
+      result = updateCode(result, [idx, 'strip_prefix'], updateValues);
+      result = updateCode(result, [idx, 'sha256'], hash);
+      return result;
     }
-    const existingDef = regEx(existingRegExStr);
-    // istanbul ignore if
-    if (!existingDef.test(fileContent)) {
-      logger.debug('Cannot match existing string');
-      return null;
-    }
-    return fileContent.replace(existingDef, newDef);
   } catch (err) /* istanbul ignore next */ {
     logger.debug({ err }, 'Error setting new bazel WORKSPACE version');
-    return null;
   }
+
+  // istanbul ignore next
+  return null;
 }