mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
Note: new PR description written by @timneutkens ## What? Improves the detection of Google Tag Manager vs Google Analytics. As indicated by the author of this PR it was confusing that when you have Google Tag Manager set up it gives you an error message saying you're adding Google Analytics even though it's Tag Manager. I've updated the PR to have a separate message for Google Tag Manager and expanded the error message docs too. It also mentioned `next/script` in a bunch of places even though the error doc recommends `@next/third-parties`. I've updated all cases to refer to `@next/third-parties` instead. <details> <summary>Previous PR description</summary> Google has multiple products with similar names: the "Google Tag" and "Google Tag Manager". Google Tag Manager uses `gtm.js` whereas the "Google Tag" uses `gtag`. Google Analytics previously used `analytics.js` (<2017) and is now delivered via the "Google Tag". Google Tag: `www.googletagmanager.com/gtag/js` Google Tag Manager: `www.googletagmanager.com/gtm.js` The `next-script-for-ga.ts` rule's matching list (`SUPPORTED_HTML_CONTENT_URLS`) includes `gtm.js` which means when a developer adds Google Tag Manager to their NextJS website, they'll receive the warning and be encouraged to swap from `gtm.js` to `gtag`. A developer asleep at the wheel (...me) may think... > I'm using Google Analytics via Google Tag Manager, and NextJS is suggesting I swap `gtm.js` for `gtag` because it will improve performance. `gtag` sounds like it is part of "Google Tag Manager" so I am going to follow the instructions so that my website is faster. However, `gtag` is **not** compatible with `gtm.js` (they're different products with similar names and similar purposes) and so making this change will cause very confusing Google Tag Manager behaviour. I am suggesting that `gtm.js` is removed completely from the rule, because anyone using `gtm.js` should not be following these instructions. A separate rule could be added specifically for Google Tag Manager, that does not refer to `gtag`. </details> --------- Co-authored-by: Tim Neutkens <[email protected]>
219 lines
6.6 KiB
TypeScript
219 lines
6.6 KiB
TypeScript
import { RuleTester } from 'eslint'
|
|
import { rules } from '@next/eslint-plugin-next'
|
|
|
|
const NextESLintRule = rules['next-script-for-ga']
|
|
|
|
const url = 'https://nextjs.org/docs/messages/next-script-for-ga'
|
|
const ERROR_MSG_GOOGLE_ANALYTICS = `Prefer \`GoogleAnalytics\` component from \`@next/third-parties/google\` when using the inline script for Google Analytics. See: ${url}`
|
|
const ERROR_MSG_GOOGLE_TAG_MANAGER = `Prefer \`GoogleTagManager\` component from \`@next/third-parties/google\` when using the inline script for Google Tag Manager. See: ${url}`
|
|
|
|
const tests = {
|
|
valid: [
|
|
`import Script from 'next/script'
|
|
|
|
export class Blah extends Head {
|
|
render() {
|
|
return (
|
|
<div>
|
|
<h1>Hello title</h1>
|
|
<Script
|
|
src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
|
|
strategy="lazyOnload"
|
|
/>
|
|
<Script id="google-analytics">
|
|
{\`
|
|
window.dataLayer = window.dataLayer || [];
|
|
function gtag(){window.dataLayer.push(arguments);}
|
|
gtag('js', new Date());
|
|
|
|
gtag('config', 'GA_MEASUREMENT_ID');
|
|
\`}
|
|
</Script>
|
|
</div>
|
|
);
|
|
}
|
|
}`,
|
|
`import Script from 'next/script'
|
|
|
|
export class Blah extends Head {
|
|
render() {
|
|
return (
|
|
<div>
|
|
<h1>Hello title</h1>
|
|
<Script id="google-analytics">
|
|
{\`(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
|
|
|
ga('create', 'UA-XXXXX-Y', 'auto');
|
|
ga('send', 'pageview');
|
|
})\`}
|
|
</Script>
|
|
</div>
|
|
);
|
|
}
|
|
}`,
|
|
`import Script from 'next/script'
|
|
|
|
export class Blah extends Head {
|
|
render() {
|
|
return (
|
|
<div>
|
|
<h1>Hello title</h1>
|
|
<Script id="google-analytics">
|
|
{\`window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
|
ga('create', 'UA-XXXXX-Y', 'auto');
|
|
ga('send', 'pageview');
|
|
})\`}
|
|
</Script>
|
|
</div>
|
|
);
|
|
}
|
|
}`,
|
|
`export class Blah extends Head {
|
|
render() {
|
|
return (
|
|
<div>
|
|
<h1>Hello title</h1>
|
|
<script dangerouslySetInnerHTML={{}} />
|
|
</div>
|
|
);
|
|
}
|
|
}`,
|
|
],
|
|
|
|
invalid: [
|
|
{
|
|
code: `
|
|
export class Blah extends Head {
|
|
render() {
|
|
return (
|
|
<div>
|
|
<h1>Hello title</h1>
|
|
<script async src='https://www.googletagmanager.com/gtag/js?id=$\{GA_TRACKING_ID}' />
|
|
<script
|
|
dangerouslySetInnerHTML={{
|
|
__html: \`
|
|
window.dataLayer = window.dataLayer || [];
|
|
function gtag(){dataLayer.push(arguments);}
|
|
gtag('js', new Date());
|
|
gtag('config', '\${GA_TRACKING_ID}', {
|
|
page_path: window.location.pathname,
|
|
});
|
|
\`,
|
|
}}/>
|
|
</div>
|
|
);
|
|
}
|
|
}`,
|
|
errors: [
|
|
{
|
|
message: ERROR_MSG_GOOGLE_TAG_MANAGER,
|
|
type: 'JSXOpeningElement',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: `
|
|
export class Blah extends Head {
|
|
render() {
|
|
return (
|
|
<div>
|
|
<h1>Hello title</h1>
|
|
<script dangerouslySetInnerHTML={{
|
|
__html: \`
|
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
|
|
|
ga('create', 'UA-XXXXX-Y', 'auto');
|
|
ga('send', 'pageview');
|
|
\`,
|
|
}}/>
|
|
</div>
|
|
);
|
|
}
|
|
}`,
|
|
errors: [
|
|
{
|
|
message: ERROR_MSG_GOOGLE_ANALYTICS,
|
|
type: 'JSXOpeningElement',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: `
|
|
export class Blah extends Head {
|
|
render() {
|
|
return (
|
|
<div>
|
|
<h1>Hello title</h1>
|
|
<script dangerouslySetInnerHTML={{
|
|
__html: \`
|
|
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
|
ga('create', 'UA-XXXXX-Y', 'auto');
|
|
ga('send', 'pageview');
|
|
\`,
|
|
}}/>
|
|
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
|
</div>
|
|
);
|
|
}
|
|
}`,
|
|
errors: [
|
|
{
|
|
message: ERROR_MSG_GOOGLE_ANALYTICS,
|
|
type: 'JSXOpeningElement',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
code: `
|
|
export class Blah extends Head {
|
|
createGoogleAnalyticsMarkup() {
|
|
return {
|
|
__html: \`
|
|
window.dataLayer = window.dataLayer || [];
|
|
function gtag(){dataLayer.push(arguments);}
|
|
gtag('js', new Date());
|
|
gtag('config', 'UA-148481588-2');\`,
|
|
};
|
|
}
|
|
|
|
render() {
|
|
return (
|
|
<div>
|
|
<h1>Hello title</h1>
|
|
<script dangerouslySetInnerHTML={this.createGoogleAnalyticsMarkup()} />
|
|
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
|
</div>
|
|
);
|
|
}
|
|
}`,
|
|
errors: [
|
|
{
|
|
message: ERROR_MSG_GOOGLE_ANALYTICS,
|
|
type: 'JSXOpeningElement',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
|
|
describe('next-script-for-ga', () => {
|
|
new RuleTester({
|
|
languageOptions: {
|
|
ecmaVersion: 2018,
|
|
sourceType: 'module',
|
|
parserOptions: {
|
|
ecmaFeatures: {
|
|
modules: true,
|
|
jsx: true,
|
|
},
|
|
},
|
|
},
|
|
}).run('eslint', NextESLintRule, tests)
|
|
})
|