Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/canary' into update/babel
Browse files Browse the repository at this point in the history
  • Loading branch information
ijjk committed Aug 16, 2021
2 parents 030e494 + 4dad4fb commit 418f657
Show file tree
Hide file tree
Showing 48 changed files with 64,982 additions and 63,870 deletions.
1 change: 1 addition & 0 deletions docs/basic-features/eslint.md
Expand Up @@ -95,6 +95,7 @@ Next.js provides an ESLint plugin, [`eslint-plugin-next`](https://www.npmjs.com/
| ✔️ | [next/no-title-in-document-head](https://nextjs.org/docs/messages/no-title-in-document-head) | Disallow using <title> with Head from next/document |
| ✔️ | [next/no-unwanted-polyfillio](https://nextjs.org/docs/messages/no-unwanted-polyfillio) | Prevent duplicate polyfills from Polyfill.io |
| ✔️ | next/no-typos | Ensure no typos were made declaring [Next.js's data fetching function](https://nextjs.org/docs/basic-features/data-fetching) |
| ✔️ | [next/next-script-for-ga](https://nextjs.org/docs/messages/next-script-for-ga) | Use the Script component to defer loading of the script until necessary. |

- ✔: Enabled in the recommended configuration

Expand Down
2 changes: 1 addition & 1 deletion docs/testing.md
Expand Up @@ -16,7 +16,7 @@ Cypress is a test runner used for **End-to-End (E2E)** and **Integration Testing

### Quickstart

You can use `create-next-app` with the [with-cypress example](https://github.com/vercel/next.js/tree/canary/examples/with-jest) to quickly get started.
You can use `create-next-app` with the [with-cypress example](https://github.com/vercel/next.js/tree/canary/examples/with-cypress) to quickly get started.

```bash
npx create-next-app --example with-cypress with-cypress-app
Expand Down
98 changes: 98 additions & 0 deletions errors/next-script-for-ga.md
@@ -0,0 +1,98 @@
# Next Script for Google Analytics

### Why This Error Occurred

An inline script was used for Google analytics which might impact your webpage's performance.

### Possible Ways to Fix It

#### Using gtag.js

If you are using the [gtag.js](https://developers.google.com/analytics/devguides/collection/gtagjs) script to add analytics, use the `next/script` component with the right loading strategy to defer loading of the script until necessary.

```jsx
import Script from 'next/script'

const Home = () => {
return (
<div class="container">
<!-- Global site tag (gtag.js) - Google Analytics -->
<Script
src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
strategy="lazyOnload"
></Script>
<Script>
{`
window.dataLayer = window.dataLayer || [];
function gtag(){window.dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'GA_MEASUREMENT_ID');
`}
</Script>
</div>
)
}

export default Home
```

#### Using analytics.js

If you are using the [analytics.js](https://developers.google.com/analytics/devguides/collection/analyticsjs) script to add analytics:

```jsx
import Script from 'next/script'

const Home = () => {
return (
<div class="container">
<Script>
{`
(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>
)
}

export default Home
```

If you are using the [alternative async variant](https://developers.google.com/analytics/devguides/collection/analyticsjs#alternative_async_tag):

```jsx
import Script from 'next/script'

const Home = () => {
return (
<div class="container">
<Script>
{`
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'GOOGLE_ANALYTICS_ID', 'auto');
ga('send', 'pageview');
`}
</Script>
<Script
src="https://www.google-analytics.com/analytics.js"
strategy="lazyOnload"
></Script>
</div>
)
}

export default Home
```

### Useful Links

- [Add analytics.js to Your Site](https://developers.google.com/analytics/devguides/collection/analyticsjs)
- [Efficiently load third-party JavaScript](https://web.dev/efficiently-load-third-party-javascript/)
- [next/script Documentation](https://nextjs.org/docs/basic-features/script)
2 changes: 1 addition & 1 deletion lerna.json
Expand Up @@ -17,5 +17,5 @@
"registry": "https://registry.npmjs.org/"
}
},
"version": "11.1.1-canary.8"
"version": "11.1.1-canary.9"
}
2 changes: 1 addition & 1 deletion packages/create-next-app/package.json
@@ -1,6 +1,6 @@
{
"name": "create-next-app",
"version": "11.1.1-canary.8",
"version": "11.1.1-canary.9",
"keywords": [
"react",
"next",
Expand Down
4 changes: 2 additions & 2 deletions packages/eslint-config-next/package.json
@@ -1,6 +1,6 @@
{
"name": "eslint-config-next",
"version": "11.1.1-canary.8",
"version": "11.1.1-canary.9",
"description": "ESLint configuration used by NextJS.",
"main": "index.js",
"license": "MIT",
Expand All @@ -9,7 +9,7 @@
"directory": "packages/eslint-config-next"
},
"dependencies": {
"@next/eslint-plugin-next": "11.1.1-canary.8",
"@next/eslint-plugin-next": "11.1.1-canary.9",
"@rushstack/eslint-patch": "^1.0.6",
"@typescript-eslint/parser": "^4.20.0",
"eslint-import-resolver-node": "^0.3.4",
Expand Down
2 changes: 2 additions & 0 deletions packages/eslint-plugin-next/lib/index.js
Expand Up @@ -16,6 +16,7 @@ module.exports = {
'no-script-in-head': require('./rules/no-script-in-head'),
'no-typos': require('./rules/no-typos'),
'no-duplicate-head': require('./rules/no-duplicate-head'),
'next-script-for-ga': require('./rules/next-script-for-ga'),
},
configs: {
recommended: {
Expand All @@ -31,6 +32,7 @@ module.exports = {
'@next/next/google-font-display': 1,
'@next/next/google-font-preconnect': 1,
'@next/next/link-passhref': 1,
'@next/next/next-script-for-ga': 1,
'@next/next/no-document-import-in-page': 2,
'@next/next/no-head-import-in-document': 2,
'@next/next/no-script-in-document': 2,
Expand Down
77 changes: 77 additions & 0 deletions packages/eslint-plugin-next/lib/rules/next-script-for-ga.js
@@ -0,0 +1,77 @@
const NodeAttributes = require('../utils/node-attributes.js')

const SUPPORTED_SRCS = [
'www.google-analytics.com/analytics.js',
'www.googletagmanager.com/gtag/js',
]
const SUPPORTED_HTML_CONTENT_URLS = [
'www.google-analytics.com/analytics.js',
'www.googletagmanager.com/gtm.js',
]
const ERROR_MSG =
'Use the `next/script` component for loading third party scripts. See: https://nextjs.org/docs/messages/next-script-for-ga.'

// Check if one of the items in the list is a substring of the passed string
const containsStr = (str, strList) => {
return strList.some((s) => str.includes(s))
}

module.exports = {
meta: {
docs: {
description:
'Prefer next script component when using the inline script for Google Analytics',
recommended: true,
},
},
create: function (context) {
return {
JSXOpeningElement(node) {
if (node.name.name !== 'script') {
return
}
if (node.attributes.length === 0) {
return
}
const attributes = new NodeAttributes(node)

// Check if the Alternative async tag is being used to add GA.
// https://developers.google.com/analytics/devguides/collection/analyticsjs#alternative_async_tag
// https://developers.google.com/analytics/devguides/collection/gtagjs
if (
typeof attributes.value('src') === 'string' &&
containsStr(attributes.value('src'), SUPPORTED_SRCS)
) {
return context.report({
node,
message: ERROR_MSG,
})
}

// Check if inline script is being used to add GA.
// https://developers.google.com/analytics/devguides/collection/analyticsjs#the_google_analytics_tag
// https://developers.google.com/tag-manager/quickstart
if (
attributes.has('dangerouslySetInnerHTML') &&
attributes.value('dangerouslySetInnerHTML')[0]
) {
const htmlContent =
attributes.value('dangerouslySetInnerHTML')[0].value.quasis &&
attributes.value('dangerouslySetInnerHTML')[0].value.quasis[0].value
.raw
if (
htmlContent &&
containsStr(htmlContent, SUPPORTED_HTML_CONTENT_URLS)
) {
context.report({
node,
message: ERROR_MSG,
})
}
}
},
}
},
}

module.exports.schema = []
4 changes: 3 additions & 1 deletion packages/eslint-plugin-next/lib/utils/node-attributes.js
Expand Up @@ -26,7 +26,9 @@ class NodeAttributes {
this.attributes[attribute.name.name].value = attribute.value.value
} else if (attribute.value.expression) {
this.attributes[attribute.name.name].value =
attribute.value.expression.value
typeof attribute.value.expression.value !== 'undefined'
? attribute.value.expression.value
: attribute.value.expression.properties
}
}
})
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin-next/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/eslint-plugin-next",
"version": "11.1.1-canary.8",
"version": "11.1.1-canary.9",
"description": "ESLint plugin for NextJS.",
"main": "lib/index.js",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-bundle-analyzer/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/bundle-analyzer",
"version": "11.1.1-canary.8",
"version": "11.1.1-canary.9",
"main": "index.js",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/next-codemod/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/codemod",
"version": "11.1.1-canary.8",
"version": "11.1.1-canary.9",
"license": "MIT",
"dependencies": {
"chalk": "4.1.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-env/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/env",
"version": "11.1.1-canary.8",
"version": "11.1.1-canary.9",
"keywords": [
"react",
"next",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-mdx/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/mdx",
"version": "11.1.1-canary.8",
"version": "11.1.1-canary.9",
"main": "index.js",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/next-plugin-storybook/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/plugin-storybook",
"version": "11.1.1-canary.8",
"version": "11.1.1-canary.9",
"repository": {
"url": "vercel/next.js",
"directory": "packages/next-plugin-storybook"
Expand Down
2 changes: 1 addition & 1 deletion packages/next-polyfill-module/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/polyfill-module",
"version": "11.1.1-canary.8",
"version": "11.1.1-canary.9",
"description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)",
"main": "dist/polyfill-module.js",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-polyfill-nomodule/package.json
@@ -1,6 +1,6 @@
{
"name": "@next/polyfill-nomodule",
"version": "11.1.1-canary.8",
"version": "11.1.1-canary.9",
"description": "A polyfill for non-dead, nomodule browsers.",
"main": "dist/polyfill-nomodule.js",
"license": "MIT",
Expand Down

0 comments on commit 418f657

Please sign in to comment.