From bd0e593057f4e9693cc592c8da3728e79e8e1303 Mon Sep 17 00:00:00 2001 From: Janicklas Ralph James Date: Mon, 29 Nov 2021 10:17:40 -0800 Subject: [PATCH 01/24] Changes to the beforeInteractive strategy to make it work for streaming --- packages/next/client/script.tsx | 15 +++++++++++++-- packages/next/server/render.tsx | 11 ++++++++++- packages/next/shared/lib/router/router.ts | 10 ++++++++++ test/integration/script-loader/pages/page1.js | 15 ++++++++++----- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/packages/next/client/script.tsx b/packages/next/client/script.tsx index 302991bf3468158..b8b6c6139e9b1c2 100644 --- a/packages/next/client/script.tsx +++ b/packages/next/client/script.tsx @@ -104,9 +104,9 @@ const loadScript = (props: ScriptProps): void => { document.body.appendChild(el) } -function handleClientScriptLoad(props: ScriptProps) { +export function handleClientScriptLoad(props: ScriptProps) { const { strategy = 'afterInteractive' } = props - if (strategy === 'afterInteractive') { + if (strategy === 'afterInteractive' || strategy === 'beforeInteractive') { loadScript(props) } else if (strategy === 'lazyOnload') { window.addEventListener('load', () => { @@ -125,8 +125,19 @@ function loadLazyScript(props: ScriptProps) { } } +function addBeforeInteractiveToCache() { + const scripts = document.querySelectorAll( + '[data-nscript="beforeInteractive"]' + ) + scripts.forEach((script) => { + const cacheKey = script.id || script.src + LoadCache.add(cacheKey) + }) +} + export function initScriptLoader(scriptLoaderItems: ScriptProps[]) { scriptLoaderItems.forEach(handleClientScriptLoad) + addBeforeInteractiveToCache() } function Script(props: ScriptProps): JSX.Element | null { diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index 055650636c1dff7..8b8f0129d36d8fe 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -412,6 +412,7 @@ export async function renderToHTML( App.getInitialProps === (App as any).origGetInitialProps const hasPageGetInitialProps = !!(Component as any).getInitialProps + const hasPageScripts = (Component as any).scriptLoader const pageIsDynamic = isDynamicRoute(pathname) @@ -599,6 +600,14 @@ export async function renderToHTML( let head: JSX.Element[] = defaultHead(inAmpMode) + let initialScripts = {} + if (hasPageScripts) { + initialScripts.beforeInteractive = [] + .concat(hasPageScripts()) + .filter((script) => script.props.strategy === 'beforeInteractive') + .map((script) => script.props) + } + let scriptLoader: any = {} const nextExport = !isSSG && (renderOpts.nextExport || (dev && (isAutoExport || isFallback))) @@ -614,7 +623,7 @@ export async function renderToHTML( updateScripts: (scripts) => { scriptLoader = scripts }, - scripts: {}, + scripts: initialScripts, mountedInstances: new Set(), }} > diff --git a/packages/next/shared/lib/router/router.ts b/packages/next/shared/lib/router/router.ts index 2d4826e278fa83a..bea3980d7e661bc 100644 --- a/packages/next/shared/lib/router/router.ts +++ b/packages/next/shared/lib/router/router.ts @@ -16,6 +16,8 @@ import { isAssetError, markAssetError, } from '../../../client/route-loader' +import { RouterEvent } from '../../../client/router' +import { handleClientScriptLoad } from '../../../client/script' import isError from '../../../lib/is-error' import { denormalizePagePath } from '../../../server/denormalize-page-path' import { normalizeLocalePath } from '../i18n/normalize-locale-path' @@ -1235,6 +1237,14 @@ export default class Router implements BaseRouter { ) let { error, props, __N_SSG, __N_SSP } = routeInfo + if(routeInfo.Component && routeInfo.Component.scriptLoader) { + const scripts = [].concat(routeInfo.Component.scriptLoader()) + + scripts.forEach(script => { + handleClientScriptLoad(script.props) + }) + } + // handle redirect on client-transition if ((__N_SSG || __N_SSP) && props) { if (props.pageProps && props.pageProps.__N_REDIRECT) { diff --git a/test/integration/script-loader/pages/page1.js b/test/integration/script-loader/pages/page1.js index 8ef106350652e94..3b6a309f2b85b2e 100644 --- a/test/integration/script-loader/pages/page1.js +++ b/test/integration/script-loader/pages/page1.js @@ -3,14 +3,19 @@ import Script from 'next/script' const Page = () => { return (
-
page1
) } +Page.scriptLoader = () => { + return ( + + ) +} + export default Page From 5f4b871bc6e599ead6938e39aef823df9a72dd34 Mon Sep 17 00:00:00 2001 From: Janicklas Ralph James Date: Thu, 17 Feb 2022 13:44:42 +0530 Subject: [PATCH 02/24] Updating design and adding eslint rule --- packages/eslint-plugin-next/lib/index.js | 4 +- .../rules/no-script-bi-outside-document.js | 51 +++++++++ packages/next/client/script.tsx | 15 +-- packages/next/pages/_document.tsx | 2 +- packages/next/server/render.tsx | 10 +- packages/next/shared/lib/router/router.ts | 8 +- .../script-loader/pages/_document.js | 6 + test/integration/script-loader/pages/page1.js | 6 +- .../no-script-bi-outside-document.test.ts | 103 ++++++++++++++++++ 9 files changed, 186 insertions(+), 19 deletions(-) create mode 100644 packages/eslint-plugin-next/lib/rules/no-script-bi-outside-document.js create mode 100644 test/unit/eslint-plugin-next/no-script-bi-outside-document.test.ts diff --git a/packages/eslint-plugin-next/lib/index.js b/packages/eslint-plugin-next/lib/index.js index 295978b671b6fc4..c2dd54b6eac79a1 100644 --- a/packages/eslint-plugin-next/lib/index.js +++ b/packages/eslint-plugin-next/lib/index.js @@ -20,6 +20,7 @@ module.exports = { 'no-duplicate-head': require('./rules/no-duplicate-head'), 'inline-script-id': require('./rules/inline-script-id'), 'next-script-for-ga': require('./rules/next-script-for-ga'), + 'no-script-bi-outside-document': require('./rules/no-script-bi-outside-document'), }, configs: { recommended: { @@ -39,12 +40,13 @@ module.exports = { '@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, + '@next/next/no-script-in-document': 0, '@next/next/no-script-component-in-head': 2, '@next/next/no-server-import-in-page': 2, '@next/next/no-typos': 1, '@next/next/no-duplicate-head': 2, '@next/next/inline-script-id': 2, + '@next/next/no-script-bi-outside-document': 2, }, }, 'core-web-vitals': { diff --git a/packages/eslint-plugin-next/lib/rules/no-script-bi-outside-document.js b/packages/eslint-plugin-next/lib/rules/no-script-bi-outside-document.js new file mode 100644 index 000000000000000..74d9ef38a4de800 --- /dev/null +++ b/packages/eslint-plugin-next/lib/rules/no-script-bi-outside-document.js @@ -0,0 +1,51 @@ +const path = require('path') + +module.exports = { + meta: { + docs: { + description: + 'Disallow using next/script beforeInteractive strategy outside the next/_document component', + recommended: true, + url: 'https://nextjs.org/docs/messages/no-script-bi-outside-document', + }, + }, + create: function (context) { + let scriptImportName = null + + return { + 'ImportDeclaration[source.value="next/script"] > ImportDefaultSpecifier'( + node + ) { + scriptImportName = node.local.name + }, + JSXOpeningElement(node) { + if (!scriptImportName) { + return + } + + if (node.name && node.name.name !== scriptImportName) { + return + } + + const strategy = node.attributes.find( + (child) => child.name && child.name.name === 'strategy' + ) + + if (strategy.value && strategy.value.value !== 'beforeInteractive') { + return + } + + const document = context.getFilename().split('pages')[1] + if (document && path.parse(document).name.startsWith('_document')) { + return + } + + context.report({ + node, + message: + 'next/script beforeInteractive strategy should only be used inside next/_document. See: https://nextjs.org/docs/messages/no-script-bi-outside-document', + }) + }, + } + }, +} diff --git a/packages/next/client/script.tsx b/packages/next/client/script.tsx index b8b6c6139e9b1c2..5b4efafe1fbf720 100644 --- a/packages/next/client/script.tsx +++ b/packages/next/client/script.tsx @@ -106,12 +106,12 @@ const loadScript = (props: ScriptProps): void => { export function handleClientScriptLoad(props: ScriptProps) { const { strategy = 'afterInteractive' } = props - if (strategy === 'afterInteractive' || strategy === 'beforeInteractive') { - loadScript(props) - } else if (strategy === 'lazyOnload') { + if (strategy === 'lazyOnload') { window.addEventListener('load', () => { requestIdleCallback(() => loadScript(props)) }) + } else { + loadScript(props) } } @@ -126,11 +126,12 @@ function loadLazyScript(props: ScriptProps) { } function addBeforeInteractiveToCache() { - const scripts = document.querySelectorAll( - '[data-nscript="beforeInteractive"]' - ) + const scripts = [ + ...document.querySelectorAll('[data-nscript="beforeInteractive"]'), + ...document.querySelectorAll('[data-nscript="beforePageRender"]'), + ] scripts.forEach((script) => { - const cacheKey = script.id || script.src + const cacheKey = script.id || script.getAttribute('src') LoadCache.add(cacheKey) }) } diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index 91b0b526fea94e2..ab890e1fe6a62a9 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -82,7 +82,7 @@ function getPreNextScripts(context: HtmlProps, props: OriginProps) { key={scriptProps.src || index} defer={!disableOptimizedLoading} nonce={props.nonce} - data-nscript="beforeInteractive" + data-nscript={strategy} crossOrigin={props.crossOrigin || crossOrigin} /> ) diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index 739f2249927078d..5633729d71c1ff4 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -729,12 +729,16 @@ export async function renderToHTML( let head: JSX.Element[] = defaultHead(inAmpMode) - let initialScripts = {} + let initialScripts: any = {} if (hasPageScripts) { initialScripts.beforeInteractive = [] .concat(hasPageScripts()) - .filter((script) => script.props.strategy === 'beforeInteractive') - .map((script) => script.props) + .filter( + (script: any) => + script.props.strategy === 'beforeInteractive' || + script.props.strategy === 'beforePageRender' + ) + .map((script: any) => script.props) } let scriptLoader: any = {} diff --git a/packages/next/shared/lib/router/router.ts b/packages/next/shared/lib/router/router.ts index 83a0a1543703c12..bd7fc755422f60b 100644 --- a/packages/next/shared/lib/router/router.ts +++ b/packages/next/shared/lib/router/router.ts @@ -16,7 +16,6 @@ import { isAssetError, markAssetError, } from '../../../client/route-loader' -import { RouterEvent } from '../../../client/router' import { handleClientScriptLoad } from '../../../client/script' import isError, { getProperError } from '../../../lib/is-error' import { denormalizePagePath } from '../../../server/denormalize-page-path' @@ -1268,10 +1267,11 @@ export default class Router implements BaseRouter { ) let { error, props, __N_SSG, __N_SSP } = routeInfo - if(routeInfo.Component && routeInfo.Component.scriptLoader) { - const scripts = [].concat(routeInfo.Component.scriptLoader()) + const component: any = routeInfo.Component + if (component && component.scriptLoader) { + const scripts = [].concat(component.scriptLoader()) - scripts.forEach(script => { + scripts.forEach((script: any) => { handleClientScriptLoad(script.props) }) } diff --git a/test/integration/script-loader/pages/_document.js b/test/integration/script-loader/pages/_document.js index 172594542f776b7..3f8fb7381eea653 100644 --- a/test/integration/script-loader/pages/_document.js +++ b/test/integration/script-loader/pages/_document.js @@ -1,6 +1,7 @@ import * as React from 'react' /// @ts-ignore import Document, { Main, NextScript, Head, Html } from 'next/document' +import Script from 'next/script' export default class MyDocument extends Document { constructor(props) { @@ -23,6 +24,11 @@ export default class MyDocument extends Document {
+
diff --git a/test/integration/script-loader/pages/page1.js b/test/integration/script-loader/pages/page1.js index 3b6a309f2b85b2e..1bac56721f98dda 100644 --- a/test/integration/script-loader/pages/page1.js +++ b/test/integration/script-loader/pages/page1.js @@ -11,9 +11,9 @@ const Page = () => { Page.scriptLoader = () => { return ( ) } diff --git a/test/unit/eslint-plugin-next/no-script-bi-outside-document.test.ts b/test/unit/eslint-plugin-next/no-script-bi-outside-document.test.ts new file mode 100644 index 000000000000000..54db4e748bc88c9 --- /dev/null +++ b/test/unit/eslint-plugin-next/no-script-bi-outside-document.test.ts @@ -0,0 +1,103 @@ +import rule from '@next/eslint-plugin-next/lib/rules/no-script-bi-outside-document' +import { RuleTester } from 'eslint' +;(RuleTester as any).setDefaultConfig({ + parserOptions: { + ecmaVersion: 2018, + sourceType: 'module', + ecmaFeatures: { + modules: true, + jsx: true, + }, + }, +}) +const ruleTester = new RuleTester() + +ruleTester.run('no-script-bi-outside-document', rule, { + valid: [ + { + code: ` + import Document, { Html, Main, NextScript } from 'next/document' + import Script from 'next/script' + + class MyDocument extends Document { + render() { + return ( + + + + + +
+ + + + + ) + } + } + + export default MyDocument + `, + filename: 'pages/_document.js', + }, + { + code: ` + import Document, { Html, Main, NextScript } from 'next/document' + import ScriptComponent from 'next/script' + + class MyDocument extends Document { + render() { + return ( + + + + + +
+ + + + + ) + } + } + + export default MyDocument + `, + filename: 'pages/_document.tsx', + }, + ], + + invalid: [ + { + code: ` + import Head from "next/head"; + import Script from "next/script"; + + export default function Index() { + return ( + + ); + }`, + filename: 'pages/index.js', + errors: [ + { + message: + 'next/script beforeInteractive strategy should only be used inside next/_document. See: https://nextjs.org/docs/messages/no-script-bi-outside-document', + }, + ], + }, + ], +}) From 41837206cf23ad9d26e5a50938972f47d43d4cc4 Mon Sep 17 00:00:00 2001 From: Janicklas Ralph James Date: Tue, 22 Feb 2022 13:25:54 +0530 Subject: [PATCH 03/24] updating docs --- docs/basic-features/script.md | 59 ++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/docs/basic-features/script.md b/docs/basic-features/script.md index e8642346389e0d0..9c0776316e9fa82 100644 --- a/docs/basic-features/script.md +++ b/docs/basic-features/script.md @@ -64,21 +64,38 @@ With `next/script`, you decide when to load your third-party script by using the + + + ) + } +} ``` Examples of scripts that should be loaded as soon as possible with this strategy include: @@ -86,6 +103,34 @@ Examples of scripts that should be loaded as soon as possible with this strategy - Bot detectors - Cookie consent managers +#### beforePageRender + +Scripts that load with the `beforePageRender` strategy are injected into the initial HTML from the server and run before self-bundled JavaScript is executed. This strategy is similar to `beforeInteractive` but is designed for scripts that are needed by a page and not the entire site. Syntax for adding the script is as shown below. + +```jsx +import Script from 'next/script' + +const Page = () => { + return ( +
+
page1
+
+ ) +} + +Page.scriptLoader = () => { + return ( + + ) +} + +export default Page +``` + #### afterInteractive Scripts that use the `afterInteractive` strategy are injected client-side and will run after Next.js hydrates the page. This strategy should be used for scripts that do not need to load as soon as possible and can be fetched and executed immediately after the page is interactive. From 375a603e47def0fb62972302b04d951e31a830cb Mon Sep 17 00:00:00 2001 From: Janicklas Ralph James Date: Wed, 23 Feb 2022 07:52:13 +0530 Subject: [PATCH 04/24] fix review comments --- docs/basic-features/script.md | 20 +-- ...ore-interactive-script-outside-document.md | 34 ++++++ errors/no-script-in-document-page.md | 27 ---- packages/eslint-plugin-next/lib/index.js | 6 +- ...ore-interactive-script-outside-doument.js} | 4 +- .../lib/rules/no-script-in-document.js | 30 ----- ...ore-interactive-script-outside-doument.ts} | 6 +- .../no-script-in-document.test.ts | 115 ------------------ 8 files changed, 52 insertions(+), 190 deletions(-) create mode 100644 errors/no-before-interactive-script-outside-document.md delete mode 100644 errors/no-script-in-document-page.md rename packages/eslint-plugin-next/lib/rules/{no-script-bi-outside-document.js => no-before-interactive-script-outside-doument.js} (89%) delete mode 100644 packages/eslint-plugin-next/lib/rules/no-script-in-document.js rename test/unit/eslint-plugin-next/{no-script-bi-outside-document.test.ts => no-before-interactive-script-outside-doument.ts} (92%) delete mode 100644 test/unit/eslint-plugin-next/no-script-in-document.test.ts diff --git a/docs/basic-features/script.md b/docs/basic-features/script.md index 9c0776316e9fa82..e2e69a857bfa560 100644 --- a/docs/basic-features/script.md +++ b/docs/basic-features/script.md @@ -64,16 +64,18 @@ With `next/script`, you decide when to load your third-party script by using the @@ -103,9 +104,10 @@ Examples of scripts that should be loaded as soon as possible with this strategy - Bot detectors - Cookie consent managers + #### afterInteractive diff --git a/errors/no-before-interactive-script-outside-document.md b/errors/no-before-interactive-script-outside-document.md new file mode 100644 index 000000000000000..a6bc15a81869687 --- /dev/null +++ b/errors/no-before-interactive-script-outside-document.md @@ -0,0 +1,34 @@ +# beforeInteractive Script component outside \_document.js + +#### Why This Error Occurred + +You can't use the `next/script` component with the `beforeInteractive` strategy outside the `_document.js` page. That's because `beforeInteractive` strategy only works inside **\_document.js** and is designed to load scripts that is needed by the entire site (i.e. the script will load when any page in the application has been loaded server-side). +The strategy is designed this way to make it compatible with streaming modes. + +#### Possible Ways to Fix It + +If you want a global script, move the script inside `_document.js` page. + +```jsx +// In _document.js + +export default class MyDocument extends Document { + render() { + return ( + + + +
+ + + + + ) + } +} +``` + +- [next-script](https://nextjs.org/docs/basic-features/script#usage) diff --git a/errors/no-script-in-document-page.md b/errors/no-script-in-document-page.md deleted file mode 100644 index 201d01d32e24a74..000000000000000 --- a/errors/no-script-in-document-page.md +++ /dev/null @@ -1,27 +0,0 @@ -# Script component inside \_document.js - -#### Why This Error Occurred - -You can't use the `next/script` component inside the `_document.js` page. That's because the `_document.js` page only runs on the server and `next/script` has client-side functionality to ensure loading order. - -#### Possible Ways to Fix It - -If you want a global script, instead use the `_app.js` page. - -```jsx -import Script from 'next/script' - -function MyApp({ Component, pageProps }) { - return ( - <> - - - - ) - } +export default function Document() { + return ( + + + +
+ + + + + ) } ``` diff --git a/errors/no-before-interactive-script-outside-document.md b/errors/no-before-interactive-script-outside-document.md index e89bf648845aeb8..9499054fefbb4a9 100644 --- a/errors/no-before-interactive-script-outside-document.md +++ b/errors/no-before-interactive-script-outside-document.md @@ -11,22 +11,20 @@ If you want a global script, move the script inside `_document.js` page. ```jsx // In _document.js -export default class MyDocument extends Document { - render() { - return ( - - - -
- - - - - ) - } +export default function Document() { + return ( + + + +
+ + + + + ) } ``` diff --git a/packages/eslint-plugin-next/lib/index.js b/packages/eslint-plugin-next/lib/index.js index 37c2236dcdc668b..f8fc58be50f3831 100644 --- a/packages/eslint-plugin-next/lib/index.js +++ b/packages/eslint-plugin-next/lib/index.js @@ -44,7 +44,7 @@ module.exports = { '@next/next/no-typos': 1, '@next/next/no-duplicate-head': 2, '@next/next/inline-script-id': 2, - '@next/next/no-before-interactive-script-outside-document': 2, + '@next/next/no-before-interactive-script-outside-document': 1, }, }, 'core-web-vitals': { diff --git a/test/integration/script-loader/base/pages/_app.js b/test/integration/script-loader/base/pages/_app.js index becbc64e9beb31a..f811f147a872e11 100644 --- a/test/integration/script-loader/base/pages/_app.js +++ b/test/integration/script-loader/base/pages/_app.js @@ -15,11 +15,6 @@ function MyApp({ Component, pageProps }) { src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js?a=documentLazyOnload" strategy="lazyOnload" /> - -
- - - ) - } +export default function Document() { + return ( + + + + + +
+ + +
+ + + ) } diff --git a/test/integration/script-loader/base/pages/page6.js b/test/integration/script-loader/base/pages/page6.js new file mode 100644 index 000000000000000..4c0bd620f09a295 --- /dev/null +++ b/test/integration/script-loader/base/pages/page6.js @@ -0,0 +1,16 @@ +import Script from 'next/script' + +const Page = () => { + return ( +
+ +
page6
+
+ ) +} + +export default Page diff --git a/test/integration/script-loader/test/index.test.js b/test/integration/script-loader/test/index.test.js index a366fcfdd0a362e..cfa0d465bbee442 100644 --- a/test/integration/script-loader/test/index.test.js +++ b/test/integration/script-loader/test/index.test.js @@ -129,6 +129,27 @@ describe('Next.js Script - Primary Strategies', () => { test('documentBeforeInteractive') }) + // Warning - Will be removed in the next major release + it('priority beforeInteractive - older version', async () => { + const html = await renderViaHTTP(appPort, '/page6') + const $ = cheerio.load(html) + + function test(id) { + const script = $(`#${id}`) + + // Renders script tag + expect(script.length).toBe(1) + expect(script.attr('data-nscript')).toBeDefined() + + // Script is inserted before NextScripts + expect( + $(`#${id} ~ script[src^="/_next/static/chunks/main"]`).length + ).toBeGreaterThan(0) + } + + test('documentBeforeInteractive') + }) + it('priority beforeInteractive on navigate', async () => { let browser try { From 316f368ba568c37f0cef29be6abc4985fcf6a2b6 Mon Sep 17 00:00:00 2001 From: Janicklas Ralph James Date: Thu, 7 Apr 2022 15:42:13 -0700 Subject: [PATCH 19/24] Fix failing test --- test/integration/script-loader/base/pages/page6.js | 2 +- test/integration/script-loader/test/index.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/script-loader/base/pages/page6.js b/test/integration/script-loader/base/pages/page6.js index 4c0bd620f09a295..9042f62c12df452 100644 --- a/test/integration/script-loader/base/pages/page6.js +++ b/test/integration/script-loader/base/pages/page6.js @@ -4,7 +4,7 @@ const Page = () => { return (
diff --git a/test/integration/script-loader/test/index.test.js b/test/integration/script-loader/test/index.test.js index cfa0d465bbee442..188d4c1bd64293b 100644 --- a/test/integration/script-loader/test/index.test.js +++ b/test/integration/script-loader/test/index.test.js @@ -147,7 +147,7 @@ describe('Next.js Script - Primary Strategies', () => { ).toBeGreaterThan(0) } - test('documentBeforeInteractive') + test('scriptBeforePageRenderOld') }) it('priority beforeInteractive on navigate', async () => { From c6e2f2f8781b31a59135d96d1ee396d8e84822c9 Mon Sep 17 00:00:00 2001 From: Janicklas Ralph James Date: Mon, 11 Apr 2022 11:44:27 -0700 Subject: [PATCH 20/24] Fix failing test --- test/integration/script-loader/base/pages/_document.js | 8 ++++---- test/integration/script-loader/test/index.test.js | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/integration/script-loader/base/pages/_document.js b/test/integration/script-loader/base/pages/_document.js index 32ad15ac9385424..31a725a6e2778e6 100644 --- a/test/integration/script-loader/base/pages/_document.js +++ b/test/integration/script-loader/base/pages/_document.js @@ -11,15 +11,15 @@ export default function Document() { rel="stylesheet" href="https://fonts.googleapis.com/css?family=Voces" /> - - -
- + + +
+
diff --git a/test/integration/script-loader/test/index.test.js b/test/integration/script-loader/test/index.test.js index 188d4c1bd64293b..44355c135bb9373 100644 --- a/test/integration/script-loader/test/index.test.js +++ b/test/integration/script-loader/test/index.test.js @@ -126,7 +126,7 @@ describe('Next.js Script - Primary Strategies', () => { ).toBeGreaterThan(0) } - test('documentBeforeInteractive') + test('scriptBeforeInteractive') }) // Warning - Will be removed in the next major release @@ -157,7 +157,7 @@ describe('Next.js Script - Primary Strategies', () => { // beforeInteractive scripts should load once let documentBIScripts = await browser.elementsByCss( - '[src$="documentBeforeInteractive"]' + '[src$="scriptBeforeInteractive"]' ) expect(documentBIScripts.length).toBe(1) @@ -168,7 +168,7 @@ describe('Next.js Script - Primary Strategies', () => { // Ensure beforeInteractive script isn't duplicated on navigation documentBIScripts = await browser.elementsByCss( - '[src$="documentBeforeInteractive"]' + '[src$="scriptBeforeInteractive"]' ) expect(documentBIScripts.length).toBe(1) } finally { From 01346c11174ceaae88aaafc4c9271aeaca5c9013 Mon Sep 17 00:00:00 2001 From: Janicklas Ralph James Date: Mon, 11 Apr 2022 15:05:20 -0700 Subject: [PATCH 21/24] Fix failing test --- test/e2e/next-script-worker-strategy/index.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/e2e/next-script-worker-strategy/index.test.ts b/test/e2e/next-script-worker-strategy/index.test.ts index c281217ed9fc254..5c43622d61ac45d 100644 --- a/test/e2e/next-script-worker-strategy/index.test.ts +++ b/test/e2e/next-script-worker-strategy/index.test.ts @@ -12,7 +12,7 @@ describe('experimental.nextScriptWorkers: false with no Partytown dependency', ( files: { 'pages/index.js': ` import Script from 'next/script' - + export default function Page() { return ( <> @@ -68,7 +68,7 @@ describe('experimental.nextScriptWorkers: true with required Partytown dependenc files: { 'pages/index.js': ` import Script from 'next/script' - + export default function Page() { return ( <> @@ -132,7 +132,7 @@ describe('experimental.nextScriptWorkers: true with required Partytown dependenc `document.querySelectorAll('script[type="text/partytown-x"]').length` ) - expect(processedWorkerScripts).toEqual(1) + expect(processedWorkerScripts).toBeGreaterThan(0) } finally { if (browser) await browser.close() } @@ -183,7 +183,7 @@ describe('experimental.nextScriptWorkers: true with config override', () => { `, 'pages/index.js': ` import Script from 'next/script' - + export default function Page() { return ( <> From 243b0ef6c8d581a5d72332ee2a1a6d9bf6eb4936 Mon Sep 17 00:00:00 2001 From: Janicklas Ralph James Date: Tue, 12 Apr 2022 11:23:15 -0700 Subject: [PATCH 22/24] Fix failing test --- test/e2e/next-script-worker-strategy/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/next-script-worker-strategy/index.test.ts b/test/e2e/next-script-worker-strategy/index.test.ts index 5c43622d61ac45d..2283ea964273016 100644 --- a/test/e2e/next-script-worker-strategy/index.test.ts +++ b/test/e2e/next-script-worker-strategy/index.test.ts @@ -123,7 +123,7 @@ describe('experimental.nextScriptWorkers: true with required Partytown dependenc `document.querySelectorAll('script[type="text/partytown"]').length` ) - expect(predefinedWorkerScripts).toEqual(1) + expect(predefinedWorkerScripts).toBeGreaterThan(0) await waitFor(1000) From 2bc903b70fab0055f9ed370b213c2e45fd3664b9 Mon Sep 17 00:00:00 2001 From: Janicklas Ralph James Date: Tue, 12 Apr 2022 14:41:06 -0700 Subject: [PATCH 23/24] Add _document imports in examples --- docs/basic-features/script.md | 2 ++ errors/no-before-interactive-script-outside-document.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/basic-features/script.md b/docs/basic-features/script.md index adc196187699bbc..8e5312b180b2223 100644 --- a/docs/basic-features/script.md +++ b/docs/basic-features/script.md @@ -79,6 +79,8 @@ The reason `beforeInteractive` was designed to work only inside `\_document.js` ```jsx // In _document.js +import { Html, Head, Main, NextScript } from 'next/document' +import Script from 'next/script' export default function Document() { return ( diff --git a/errors/no-before-interactive-script-outside-document.md b/errors/no-before-interactive-script-outside-document.md index 9499054fefbb4a9..9ba97c59e80539f 100644 --- a/errors/no-before-interactive-script-outside-document.md +++ b/errors/no-before-interactive-script-outside-document.md @@ -10,6 +10,8 @@ If you want a global script, move the script inside `_document.js` page. ```jsx // In _document.js +import { Html, Head, Main, NextScript } from 'next/document' +import Script from 'next/script' export default function Document() { return ( From 265c654a0069169ca4307b63731dc6be93361c03 Mon Sep 17 00:00:00 2001 From: Janicklas Ralph James Date: Thu, 21 Apr 2022 13:40:28 -0700 Subject: [PATCH 24/24] Add unstable_ prefix to the page scriptLoader --- packages/next/server/render.tsx | 2 +- packages/next/shared/lib/router/router.ts | 4 ++-- test/integration/script-loader/base/pages/page1.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index 36c2ba77cbfa1b4..95399d2bb986c30 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -586,7 +586,7 @@ export async function renderToHTML( App.getInitialProps === (App as any).origGetInitialProps const hasPageGetInitialProps = !!(Component as any)?.getInitialProps - const hasPageScripts = (Component as any)?.scriptLoader + const hasPageScripts = (Component as any)?.unstable_scriptLoader const pageIsDynamic = isDynamicRoute(pathname) diff --git a/packages/next/shared/lib/router/router.ts b/packages/next/shared/lib/router/router.ts index e78df9052205f0c..a2bdea45efd973f 100644 --- a/packages/next/shared/lib/router/router.ts +++ b/packages/next/shared/lib/router/router.ts @@ -1277,8 +1277,8 @@ export default class Router implements BaseRouter { let { error, props, __N_SSG, __N_SSP } = routeInfo const component: any = routeInfo.Component - if (component && component.scriptLoader) { - const scripts = [].concat(component.scriptLoader()) + if (component && component.unstable_scriptLoader) { + const scripts = [].concat(component.unstable_scriptLoader()) scripts.forEach((script: any) => { handleClientScriptLoad(script.props) diff --git a/test/integration/script-loader/base/pages/page1.js b/test/integration/script-loader/base/pages/page1.js index 1bac56721f98dda..9ffa5f252e48ecf 100644 --- a/test/integration/script-loader/base/pages/page1.js +++ b/test/integration/script-loader/base/pages/page1.js @@ -8,7 +8,7 @@ const Page = () => { ) } -Page.scriptLoader = () => { +Page.unstable_scriptLoader = () => { return (