Skip to content

Simplified version that doesn't break the deploy. - #215

Merged
killev merged 8 commits into
mainfrom
409-feature/integrate-gtm-bug-fix
Aug 14, 2025
Merged

Simplified version that doesn't break the deploy.#215
killev merged 8 commits into
mainfrom
409-feature/integrate-gtm-bug-fix

Conversation

@vasilyyaremchuk

Copy link
Copy Markdown
Collaborator

Fix the error during the Dev deploy


TypeError: self.prependNodes is not a function
at Object.init (/app/node_modules/@apostrophecms/seo/index.js:20:10)
at self.create (/app/node_modules/apostrophe/lib/moog.js:310:20)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
at async instantiateModules (/app/node_modules/apostrophe/index.js:669:32)
at async apostrophe (/app/node_modules/apostrophe/index.js:319:5)
at async /app/node_modules/apostrophe/index.js:160:17
at async module.exports (/app/node_modules/apostrophe/index.js:159:16)


Get rid of the dependency on the contributed SEO package, simplify the approach to insert the scripts in the template.

@coderabbitai

coderabbitai Bot commented Aug 14, 2025

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@killev has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 12 minutes and 34 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between c0b222d and afccd0a.

📒 Files selected for processing (1)
  • website/modules/@apostrophecms/seo/index.js (2 hunks)

Walkthrough

Refactors the local @apostrophecms/seo module to externalize GTM ID logic into a new lib/gtm-utils.js with sanitizeGtmId and resolveGtmId. Removes the improve export and in-module init injections, replacing them with explicit template component calls. tagManagerHead and tagManagerBody now use gtmUtils.resolveGtmId(req, self.options). metaHead no longer handles GTM. layout.html adds component invocations for tagManagerHead and tagManagerBody. app.js clears previous GTM options for @apostrophecms/seo. package.json removes the external @apostrophecms/seo dependency.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • GTM implementation. #214: Prior GTM/SEO changes to the same module and templates; this PR refactors that behavior and removes previous init/improve handling.

Suggested reviewers

  • VitalyyP
  • killev
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 409-feature/integrate-gtm-bug-fix

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions

github-actions Bot commented Aug 14, 2025

Copy link
Copy Markdown

🔍 Vulnerabilities of apostrophe-cms:test

📦 Image Reference apostrophe-cms:test
digestsha256:c700ca033c317d15777775bda5c72556ffbe9f2fc89a037123847b95fd25af96
vulnerabilitiescritical: 1 high: 4 medium: 0 low: 0
platformlinux/amd64
size291 MB
packages984
📦 Base Image node:23-alpine
also known as
  • 23-alpine3.22
  • 23.11-alpine
  • 23.11-alpine3.22
  • 23.11.1-alpine
  • 23.11.1-alpine3.22
digestsha256:b9d38d589853406ff0d4364f21969840c3e0397087643aef8eede40edbb6c7cd
vulnerabilitiescritical: 0 high: 0 medium: 1 low: 1
critical: 1 high: 0 medium: 0 low: 0 form-data 4.0.2 (npm)

pkg:npm/form-data@4.0.2

critical 9.4: CVE--2025--7783 Use of Insufficiently Random Values

Affected range>=4.0.0
<4.0.4
Fixed version4.0.4
CVSS Score9.4
CVSS VectorCVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:H/SI:H/SA:N
EPSS Score0.076%
EPSS Percentile23rd percentile
Description

Summary

form-data uses Math.random() to select a boundary value for multipart form-encoded data. This can lead to a security issue if an attacker:

  1. can observe other values produced by Math.random in the target application, and
  2. can control one field of a request made using form-data

Because the values of Math.random() are pseudo-random and predictable (see: https://blog.securityevaluators.com/hacking-the-javascript-lottery-80cc437e3b7f), an attacker who can observe a few sequential values can determine the state of the PRNG and predict future values, includes those used to generate form-data's boundary value. The allows the attacker to craft a value that contains a boundary value, allowing them to inject additional parameters into the request.

This is largely the same vulnerability as was recently found in undici by parrot409 -- I'm not affiliated with that researcher but want to give credit where credit is due! My PoC is largely based on their work.

Details

The culprit is this line here: https://github.com/form-data/form-data/blob/426ba9ac440f95d1998dac9a5cd8d738043b048f/lib/form_data.js#L347

An attacker who is able to predict the output of Math.random() can predict this boundary value, and craft a payload that contains the boundary value, followed by another, fully attacker-controlled field. This is roughly equivalent to any sort of improper escaping vulnerability, with the caveat that the attacker must find a way to observe other Math.random() values generated by the application to solve for the state of the PRNG. However, Math.random() is used in all sorts of places that might be visible to an attacker (including by form-data itself, if the attacker can arrange for the vulnerable application to make a request to an attacker-controlled server using form-data, such as a user-controlled webhook -- the attacker could observe the boundary values from those requests to observe the Math.random() outputs). A common example would be a x-request-id header added by the server. These sorts of headers are often used for distributed tracing, to correlate errors across the frontend and backend. Math.random() is a fine place to get these sorts of IDs (in fact, opentelemetry uses Math.random for this purpose)

PoC

PoC here: https://github.com/benweissmann/CVE-2025-7783-poc

Instructions are in that repo. It's based on the PoC from https://hackerone.com/reports/2913312 but simplified somewhat; the vulnerable application has a more direct side-channel from which to observe Math.random() values (a separate endpoint that happens to include a randomly-generated request ID).

Impact

For an application to be vulnerable, it must:

  • Use form-data to send data including user-controlled data to some other system. The attacker must be able to do something malicious by adding extra parameters (that were not intended to be user-controlled) to this request. Depending on the target system's handling of repeated parameters, the attacker might be able to overwrite values in addition to appending values (some multipart form handlers deal with repeats by overwriting values instead of representing them as an array)
  • Reveal values of Math.random(). It's easiest if the attacker can observe multiple sequential values, but more complex math could recover the PRNG state to some degree of confidence with non-sequential values.

If an application is vulnerable, this allows an attacker to make arbitrary requests to internal systems.

critical: 0 high: 1 medium: 0 low: 0 connect-multiparty 2.2.0 (npm)

pkg:npm/connect-multiparty@2.2.0

high 7.8: CVE--2022--29623 Unrestricted Upload of File with Dangerous Type

Affected range<=2.2.0
Fixed versionNot Fixed
CVSS Score7.8
CVSS VectorCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
EPSS Score0.320%
EPSS Percentile54th percentile
Description

An arbitrary file upload vulnerability in the file upload module of Express Connect-Multiparty 2.2.0 allows attackers to execute arbitrary code via a crafted PDF file. NOTE: the Supplier has not verified this vulnerability report.

critical: 0 high: 1 medium: 0 low: 0 async 1.5.2 (npm)

pkg:npm/async@1.5.2

high 7.8: CVE--2021--43138 OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities

Affected range<2.6.4
Fixed version2.6.4, 3.2.2
CVSS Score7.8
CVSS VectorCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
EPSS Score0.907%
EPSS Percentile75th percentile
Description

A vulnerability exists in Async through 3.2.1 (fixed in 3.2.2), which could let a malicious user obtain privileges via the mapValues() method.

critical: 0 high: 1 medium: 0 low: 0 async 0.9.2 (npm)

pkg:npm/async@0.9.2

high 7.8: CVE--2021--43138 OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities

Affected range<2.6.4
Fixed version2.6.4, 3.2.2
CVSS Score7.8
CVSS VectorCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
EPSS Score0.907%
EPSS Percentile75th percentile
Description

A vulnerability exists in Async through 3.2.1 (fixed in 3.2.2), which could let a malicious user obtain privileges via the mapValues() method.

critical: 0 high: 1 medium: 0 low: 0 linkifyjs 4.2.0 (npm)

pkg:npm/linkifyjs@4.2.0

high 8.8: CVE--2025--8101 Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')

Affected range<4.3.2
Fixed version4.3.2
CVSS Score8.8
CVSS VectorCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N
EPSS Score0.060%
EPSS Percentile19th percentile
Description

Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') vulnerability in Linkify (linkifyjs) allows XSS Targeting HTML Attributes and Manipulating User-Controlled Variables.This issue affects Linkify: from 4.3.1 before 4.3.2.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 4

🧹 Nitpick comments (3)
website/modules/@apostrophecms/seo/views/gtmHead.html (1)

2-6: Harden GTM snippet for CSP and optional custom dataLayer name

Two small but impactful tweaks:

  • Add a conditional nonce attribute to support strict Content Security Policy.
  • Allow overriding the data layer name via data.layerName while preserving the default.

Apply this diff:

-<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
+<script{% if data.cspNonce %} nonce="{{ data.cspNonce }}"{% endif %}>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
 new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
 j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
-})(window,document,'script','dataLayer','{{ data.gtmId }}');</script>
+})(window,document,'script','{{ data.layerName | default("dataLayer") }}','{{ data.gtmId }}');</script>
website/modules/@apostrophecms/seo/views/gtmBody.html (1)

2-3: A11y: hide the noscript iframe from assistive tech

Add aria-hidden="true" and tabindex="-1" (optional) so screen readers don't announce the hidden iframe.

Apply this diff:

-<noscript><iframe src="https://www.googletagmanager.com/ns.html?id={{ data.gtmId }}"
-height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
+<noscript><iframe src="https://www.googletagmanager.com/ns.html?id={{ data.gtmId }}"
+height="0" width="0" style="display:none;visibility:hidden" aria-hidden="true" tabindex="-1"></iframe></noscript>
website/modules/@apostrophecms/seo/index.js (1)

24-28: Minor: dedupe the GTM ID resolution pattern

tagManagerBody and tagManagerHead share identical resolution and conditional return logic. Consider extracting a tiny helper for maintainability.

Example refactor:

   components(self) {
-    return {
+    const resolveForPage = (req) => {
+      if (!req?.data?.page) return '';
+      return gtmUtils.resolveGtmId(req, self.options);
+    };
+    return {
       tagManagerBody(req, data) {
-        if (!req?.data?.page) {
-          return {};
-        }
-        const gtmId = gtmUtils.resolveGtmId(req, self.options);
+        const gtmId = resolveForPage(req);
         if (gtmId) {
           return { gtmId };
         }
         return {};
       },

Repeat similarly for tagManagerHead.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b60dca9 and 35f9c74.

📒 Files selected for processing (8)
  • website/modules/@apostrophecms/seo/index.js (3 hunks)
  • website/modules/@apostrophecms/seo/lib/gtm-utils.js (1 hunks)
  • website/modules/@apostrophecms/seo/views/gtmBody.html (1 hunks)
  • website/modules/@apostrophecms/seo/views/gtmHead.html (1 hunks)
  • website/modules/@apostrophecms/seo/views/tagManagerBody.html (1 hunks)
  • website/modules/@apostrophecms/seo/views/tagManagerHead.html (1 hunks)
  • website/package.json (0 hunks)
  • website/views/layout.html (1 hunks)
💤 Files with no reviewable changes (1)
  • website/package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: e2e-tests
  • GitHub Check: lint
  • GitHub Check: unit-tests
  • GitHub Check: security-scan
🔇 Additional comments (6)
website/modules/@apostrophecms/seo/views/tagManagerBody.html (1)

2-2: LGTM: delegating to a single gtmBody template

Good consolidation; the gating by data.gtmId remains intact and prevents unnecessary markup.

website/modules/@apostrophecms/seo/views/tagManagerHead.html (1)

2-2: LGTM: render the dedicated GTM head template

Clean delegation; keeps the gate and centralizes the snippet in one place.

website/modules/@apostrophecms/seo/views/gtmBody.html (1)

2-3: LGTM on GTM noscript block

The markup is standard, uses the sanitized data.gtmId, and is safely escaped by Nunjucks.

website/modules/@apostrophecms/seo/index.js (3)

1-1: Sanitized GTM ID resolution via utility looks good

Depending on gtmUtils.resolveGtmId(req, self.options) centralizes validation and keeps templates clean.


7-10: Verify template injection ordering for GTM

Google recommends placing the GTM head snippet as early as possible in <head>. You're currently appending it to <head> and prepending metaHead. If early execution is important to you, consider also prepend-ing tagManagerHead (and ordering the two prepends so GTM comes first).

Would you like me to propose an ordering that guarantees GTM precedes other head inserts?


3-10: No missing files — SEO template components present

Your script output confirmed "All expected files are present." Verified files:

  • website/modules/@apostrophecms/seo/lib/gtm-utils.js
  • website/modules/@apostrophecms/seo/views/tagManagerHead.html
  • website/modules/@apostrophecms/seo/views/tagManagerBody.html
  • website/modules/@apostrophecms/seo/views/metaHead.html
  • website/modules/@apostrophecms/seo/views/gtmHead.html
  • website/modules/@apostrophecms/seo/views/gtmBody.html

No action required.

Comment thread website/modules/@apostrophecms/seo/index.js Outdated
Comment thread website/modules/@apostrophecms/seo/lib/gtm-utils.js Outdated
Comment thread website/views/layout.html Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

♻️ Duplicate comments (1)
website/views/layout.html (1)

17-17: Good fix: replaced undefined globals with explicit SEO component render

Switching to {% render '@apostrophecms/seo:tagManagerHead', data %} removes the dependency on missing Nunjucks globals and aligns with Apostrophe’s component pattern.

🧹 Nitpick comments (1)
website/modules/@apostrophecms/seo/index.js (1)

23-26: GTM sanitization OK; templates & resolver present — DRY optional

Short summary: sanitizeGtmId enforces a strict GTM pattern and resolveGtmId uses it; tag-manager templates exist. Small DRY improvement is optional. package.json was not found so I could not confirm npm package removal; app.js registers '@apostrophecms/seo' locally.

Files/locations:

  • website/modules/@apostrophecms/seo/lib/gtm-utils.js
    • sanitizeGtmId: String(id||'').trim().toUpperCase(); validates with /^GTM-[\dA-Z]+$/u and returns the value only if it matches.
    • resolveGtmId: reads req?.data?.global?.seoGoogleTagManager or options?.googleTagManager?.id, trims and returns sanitizeGtmId(candidate).
  • website/modules/@apostrophecms/seo/index.js
    • Calls gtmUtils.resolveGtmId(req, self.options) at the spots you flagged (around lines 23–26 and 33–36).
  • Templates present: website/modules/@apostrophecms/seo/views/tagManagerHead.html, tagManagerBody.html, gtmHead.html, gtmBody.html
  • app.js: contains '@apostrophecms/seo' registration (line ~38) indicating the module is configured locally.
  • package.json: not present in repo root — cannot verify npm package removal from package.json.

Optional small DRY suggestion (unchanged from original):
const resolve = (req) => gtmUtils.resolveGtmId(req, self.options);

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f616620 and 755cd17.

📒 Files selected for processing (3)
  • website/modules/@apostrophecms/seo/index.js (3 hunks)
  • website/modules/@apostrophecms/seo/lib/gtm-utils.js (1 hunks)
  • website/views/layout.html (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • website/modules/@apostrophecms/seo/lib/gtm-utils.js
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-02T16:04:13.069Z
Learnt from: IhorMasechko
PR: speedandfunction/website#143
File: website/modules/default-page/index.js:24-24
Timestamp: 2025-06-02T16:04:13.069Z
Learning: In ApostropheCMS projects, even when a global page-type module removes the 'orphan' field, individual page modules (like default-page, home-page, case-studies-page) may still need explicit `remove: ['orphan']` for the removal to work correctly. The global improvement may not be sufficient in all cases.

Applied to files:

  • website/modules/@apostrophecms/seo/index.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: e2e-tests
  • GitHub Check: lint
  • GitHub Check: unit-tests
  • GitHub Check: security-scan
🔇 Additional comments (2)
website/views/layout.html (1)

21-21: Correct placement of GTM noscript block

Rendering tagManagerBody in beforeMain puts the noscript iframe near the top of the body as recommended by GTM. Looks good.

website/modules/@apostrophecms/seo/index.js (1)

1-1: Centralizing GTM ID logic in a utility is the right move

Importing gtm-utils improves cohesion and keeps the component lean.

Comment thread website/modules/@apostrophecms/seo/index.js Outdated
Comment thread website/views/layout.html Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
website/modules/@apostrophecms/seo/index.js (1)

10-14: Remove auto-injection to prevent duplicate GTM tags (layout already renders them)

Layout renders tagManagerHead/Body; keeping these insertions duplicates the tags and can cause GTM issues. Remove these injections.

Apply this diff:

   init(self) {
-    // Ensure SEO components are injected into the template
-    self.apos.template.prepend('body', '@apostrophecms/seo:tagManagerBody');
-    self.apos.template.append('head', '@apostrophecms/seo:tagManagerHead');
-    self.apos.template.prepend('head', '@apostrophecms/seo:metaHead');
+    // Layout handles rendering of SEO components explicitly.
   },
🧹 Nitpick comments (1)
website/modules/@apostrophecms/seo/index.js (1)

4-8: Sane default for GTM ID via env; consider consistency with app-level config (optional)

Using process.env for a default is fine. If you want uniform config handling across the codebase, you could optionally source this via the same getEnv mechanism used in app.js and pass it in the modules config. Not required for functionality, just consistency.

Apply in app.js (optional):

-      // GTM setup handled by local SEO module
+      // GTM setup handled by local SEO module
+      '@apostrophecms/seo': {
+        options: {
+          googleTagManager: {
+            id: getEnv('GOOGLE_TAG_MANAGER_ID'),
+          },
+        },
+      },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 755cd17 and 914f42d.

📒 Files selected for processing (2)
  • website/app.js (1 hunks)
  • website/modules/@apostrophecms/seo/index.js (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-02T16:04:13.069Z
Learnt from: IhorMasechko
PR: speedandfunction/website#143
File: website/modules/default-page/index.js:24-24
Timestamp: 2025-06-02T16:04:13.069Z
Learning: In ApostropheCMS projects, even when a global page-type module removes the 'orphan' field, individual page modules (like default-page, home-page, case-studies-page) may still need explicit `remove: ['orphan']` for the removal to work correctly. The global improvement may not be sufficient in all cases.

Applied to files:

  • website/modules/@apostrophecms/seo/index.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: e2e-tests
  • GitHub Check: unit-tests
  • GitHub Check: lint
  • GitHub Check: security-scan
🔇 Additional comments (3)
website/modules/@apostrophecms/seo/index.js (3)

1-1: Centralizing GTM resolution via a small utility is the right call

Importing gtm-utils keeps the template components lean and the validation logic reusable. LGTM.


28-31: GTM ID resolution is clean and guarded by req.data.page

Only resolves and passes gtmId on actual page requests; templates can rely on sanitized, uppercase IDs. LGTM.

Also applies to: 38-41


17-23: Don't remove metaHead — it's still registered by the template system

Short check shows metaHead is still referenced, so removing it would be incorrect:

  • website/modules/@apostrophecms/seo/index.js:13 — self.apos.template.prepend('head', '@apostrophecms/seo:metaHead');
  • website/modules/@apostrophecms/seo/index.js:17 — metaHead definition

If your intent is to stop auto-injection, remove the template.prepend call instead and verify there are no other callers; otherwise ignore the original diff.

Likely an incorrect or invalid review comment.

Comment thread website/app.js Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 0

♻️ Duplicate comments (1)
website/modules/@apostrophecms/seo/index.js (1)

15-23: If metaHead is unused, remove it to reduce surface area.

Given layout only calls tagManagerHead/Body, keeping metaHead is unnecessary. Removing it avoids confusion and dead code.

Apply this diff:

     return {
-      metaHead(req, data) {
-        // Only on front-end page requests
-        if (!req?.data?.page) {
-          return {};
-        }
-        return {};
-      },
🧹 Nitpick comments (2)
website/modules/@apostrophecms/seo/index.js (2)

9-14: Remove no-op init hook; it adds noise without behavior.

The init hook only contains a comment. Drop it to keep the module lean.

Apply this diff:

-  init(self) {
-    /*
-     * SEO components are called directly from layout.html template
-     * No automatic injection needed to avoid duplication
-     */
-  },
+  // No init hook required (layout renders components explicitly).

28-28: Minor DRY: factor out GTM ID resolution within components.

Both components resolve the GTM ID identically. A tiny in-scope helper keeps this consistent and easier to tweak.

Apply this diff:

   components(self) {
-    return {
+    const getGtmId = (req) => gtmUtils.resolveGtmId(req, self.options);
+    return {
       metaHead(req, data) {
         // Only on front-end page requests
         if (!req?.data?.page) {
           return {};
         }
         return {};
       },
       tagManagerBody(req, data) {
         if (!req?.data?.page) {
           return {};
         }
-        const gtmId = gtmUtils.resolveGtmId(req, self.options);
+        const gtmId = getGtmId(req);
         if (gtmId) {
           return { gtmId };
         }
         return {};
       },
       tagManagerHead(req, data) {
         if (!req?.data?.page) {
           return {};
         }
-        const gtmId = gtmUtils.resolveGtmId(req, self.options);
+        const gtmId = getGtmId(req);
         if (gtmId) {
           return { gtmId };
         }
         return {};
       },
     };
   },

Also applies to: 38-38

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 914f42d and c0b222d.

📒 Files selected for processing (3)
  • website/app.js (1 hunks)
  • website/modules/@apostrophecms/seo/index.js (3 hunks)
  • website/views/layout.html (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • website/views/layout.html
  • website/app.js
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-02T16:04:13.069Z
Learnt from: IhorMasechko
PR: speedandfunction/website#143
File: website/modules/default-page/index.js:24-24
Timestamp: 2025-06-02T16:04:13.069Z
Learning: In ApostropheCMS projects, even when a global page-type module removes the 'orphan' field, individual page modules (like default-page, home-page, case-studies-page) may still need explicit `remove: ['orphan']` for the removal to work correctly. The global improvement may not be sufficient in all cases.

Applied to files:

  • website/modules/@apostrophecms/seo/index.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: lint
  • GitHub Check: e2e-tests
  • GitHub Check: security-scan
  • GitHub Check: unit-tests
🔇 Additional comments (2)
website/modules/@apostrophecms/seo/index.js (2)

4-7: Sensible fallback for GTM ID via options (env).

Defaulting GTM ID from process.env is a pragmatic fallback and aligns with the simplified approach. Assuming gtm-utils sanitizes invalid values to empty, this is safe and non-breaking.


1-1: GTM helper + component templates verified — no action required.

Verified:

  • website/modules/@apostrophecms/seo/lib/gtm-utils.js — defines sanitizeGtmId and resolveGtmId and exports them (module.exports).
  • website/modules/@apostrophecms/seo/views/tagManagerHead.html — present.
  • website/modules/@apostrophecms/seo/views/tagManagerBody.html — present.
  • website/views/layout.html — invokes both components ({% component '@apostrophecms/seo:tagManagerHead' with data %} at line 17 and {% component '@apostrophecms/seo:tagManagerBody' with data %} at line 21).

No missing files or export issues found.

@killev
killev enabled auto-merge (squash) August 14, 2025 15:57
@sonarqubecloud

Copy link
Copy Markdown

@killev
killev merged commit e823b3a into main Aug 14, 2025
12 checks passed
@killev
killev deleted the 409-feature/integrate-gtm-bug-fix branch August 14, 2025 16:01
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.

2 participants