diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yml b/.github/ISSUE_TEMPLATE/1.bug_report.yml index d0aa0b7589ac..dbce6318ee4a 100644 --- a/.github/ISSUE_TEMPLATE/1.bug_report.yml +++ b/.github/ISSUE_TEMPLATE/1.bug_report.yml @@ -7,49 +7,28 @@ body: value: Thanks for taking the time to file a bug report! Please fill out this form as completely as possible. - type: markdown attributes: - value: If you leave out sections there is a high likelihood it will be moved to the GitHub Discussions "Help" section. - - type: markdown - attributes: - value: 'Please first verify if your issue exists in the Next.js canary release line: `npm install next@canary`.' - - type: markdown - attributes: - value: 'next@canary is the beta version of Next.js. It includes all features and fixes that are pending to land on the stable release line.' + value: If you leave out sections there is a high likelihood it will be moved to the GitHub Discussions ["Help" section](https://github.com/vercel/next.js/discussions/categories/help). + - type: checkboxes + attributes: + label: Verify canary release + description: '`next@canary` is the canary version of Next.js that ships daily. It includes all features and fixes that have not been released to the stable version yet. Think of canary as a public beta. Some issues may already be fixed in the canary version, so please verify that your issue reproduces before opening a new issue.' + options: + - label: I verified that the issue exists in Next.js canary release + required: true - type: textarea attributes: - label: Run `next info` (available from version 12.0.8 and up) + label: Provide environment information description: Please run `next info` in the root directory of your project and paste the results. You might need to use `npx --no-install next info` if next is not in the current PATH. - validations: - required: false - - type: input - attributes: - label: What version of Next.js are you using? - description: 'For example: 10.0.1' validations: required: true - type: input attributes: - label: What version of Node.js are you using? - description: 'For example: 12.0.0' - validations: - required: true + label: What browser are you using? (if relevant) + description: 'Please specify the exact version. For example: Chrome 100.0.4878.0' - type: input attributes: - label: What browser are you using? - description: 'For example: Chrome, Safari' - validations: - required: true - - type: input - attributes: - label: What operating system are you using? - description: 'For example: macOS, Windows' - validations: - required: true - - type: input - attributes: - label: How are you deploying your application? + label: How are you deploying your application? (if relevant) description: 'For example: next start, next export, Vercel, Other platform' - validations: - required: true - type: textarea attributes: label: Describe the Bug diff --git a/.github/ISSUE_TEMPLATE/2.example_bug_report.yml b/.github/ISSUE_TEMPLATE/2.example_bug_report.yml index 8535a441bf4a..04483f5a4518 100644 --- a/.github/ISSUE_TEMPLATE/2.example_bug_report.yml +++ b/.github/ISSUE_TEMPLATE/2.example_bug_report.yml @@ -1,49 +1,40 @@ name: Example Bug Report -description: Create a bug report for the examples +description: Create a bug report for one of the Next.js examples labels: 'type: example,template: bug' body: - type: markdown attributes: - value: Thanks for taking the time to file a examples bug report! Please fill out this form as completely as possible. + value: Thanks for taking the time to file a bug report for [one of the examples](https://github.com/vercel/next.js/tree/canary/examples)! Please fill out this form as completely as possible. - type: markdown attributes: - value: If you leave out sections there is a high likelihood it will be moved to the GitHub Discussions "Help" section. - - type: input + value: If you leave out sections there is a high likelihood it will be moved to the GitHub Discussions ["Help" section](https://github.com/vercel/next.js/discussions/categories/help). + - type: checkboxes attributes: - label: What example does this report relate to? - description: 'For example: with-styled-components' - validations: - required: true - - type: input - attributes: - label: What version of Next.js are you using? - description: 'For example: 10.0.1' - validations: - required: true - - type: input + label: Verify canary release + description: '`next@canary` is the canary version of Next.js that ships daily. It includes all features and fixes that have not been released to the stable version yet. Think of canary as a public beta. Some issues may already be fixed in the canary version, so please verify that your issue reproduces before opening a new issue.' + options: + - label: I verified that the issue exists in Next.js canary release + required: true + - type: textarea attributes: - label: What version of Node.js are you using? - description: 'For example: 12.0.0' + label: Provide environment information + description: Please run `next info` in the root directory of your project and paste the results. You might need to use `npx --no-install next info` if next is not in the current PATH. validations: required: true - type: input attributes: - label: What browser are you using? - description: 'For example: Chrome, Safari' + label: Which example does this report relate to? + description: "See a complete list in the [examples folder](https://github.com/vercel/next.js/tree/canary/examples). For example: with-styled-components. Note: Examples not in the examples folder might be maintained by the example's library author. Check out their projects before opening the issue on Next.js" validations: required: true - type: input attributes: - label: What operating system are you using? - description: 'For example: macOS, Windows' - validations: - required: true + label: What browser are you using? (if relevant) + description: 'Please specify the exact version. For example: Chrome 100.0.4878.0' - type: input attributes: - label: How are you deploying your application? + label: How are you deploying your application? (if relevant) description: 'For example: next start, next export, Vercel, Other platform' - validations: - required: true - type: textarea attributes: label: Describe the Bug diff --git a/docs/advanced-features/react-18/overview.md b/docs/advanced-features/react-18/overview.md index 2df4aa92b3b0..735431c54d17 100644 --- a/docs/advanced-features/react-18/overview.md +++ b/docs/advanced-features/react-18/overview.md @@ -19,10 +19,10 @@ You can now start using React 18's new APIs like `startTransition` and `Suspense Streaming server-rendering (SSR) is an experimental feature in Next.js 12. When enabled, SSR will use the same [Edge Runtime](/docs/api-reference/edge-runtime.md) as [Middleware](/docs/middleware.md). -[Learn how to enable streaming in Next.js.](/docs/react-18/streaming.md) +[Learn how to enable streaming in Next.js.](/docs/advanced-features/react-18/streaming.md) ## React Server Components (Alpha) Server Components are a new feature in React that let you reduce your JavaScript bundle size by separating server and client-side code. Server Components allow developers to build apps that span the server and client, combining the rich interactivity of client-side apps with the improved performance of traditional server rendering. -Server Components are still in research and development. [Learn how to try Server Components](/docs/react-18/server-components.md) as an experimental feature in Next.js. +Server Components are still in research and development. [Learn how to try Server Components](/docs/advanced-features/react-18/server-components.md) as an experimental feature in Next.js. diff --git a/docs/advanced-features/react-18/server-components.md b/docs/advanced-features/react-18/server-components.md index 940c0c4755fb..dea51f2014fd 100644 --- a/docs/advanced-features/react-18/server-components.md +++ b/docs/advanced-features/react-18/server-components.md @@ -59,7 +59,7 @@ The `` and `` components will always be server-side rendered and > Make sure you're using default imports and exports for server components (`.server.js`). The support of named exports are a work in progress! -To see a full example, check out the [hello world example](https://github.com/vercel/next.js/tree/canary/examples/react-server-components) or the larger [vercel/next-rsc-demo demo](https://github.com/vercel/next-rsc-demo). +To see a full example, check out the [vercel/next-react-server-components demo](https://github.com/vercel/next-react-server-components). ## Supported Next.js APIs diff --git a/docs/advanced-features/using-mdx.md b/docs/advanced-features/using-mdx.md index bdfb3b3e8f3d..8087473ca22d 100644 --- a/docs/advanced-features/using-mdx.md +++ b/docs/advanced-features/using-mdx.md @@ -51,7 +51,8 @@ The following steps outline how to setup `@next/mdx` in your Next.js project: }, }) module.exports = withMDX({ - pageExtensions: ['js', 'jsx', 'md', 'mdx'], + // Append the default value with md extensions + pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'], }) ``` diff --git a/docs/api-reference/data-fetching/get-static-paths.md b/docs/api-reference/data-fetching/get-static-paths.md index 9175bed28a72..5b86474d8890 100644 --- a/docs/api-reference/data-fetching/get-static-paths.md +++ b/docs/api-reference/data-fetching/get-static-paths.md @@ -7,10 +7,12 @@ description: API reference for `getStaticPaths`. Learn how to fetch data and gen
Version History -| Version | Changes | -| -------- | --------------------------------------------------------------------------------------------------------------- | +| Version | Changes | +| ------- | ------- | + +| `v12.1.0` | [On-demand Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md#on-demand-revalidation-beta) added (Beta). | | `v9.5.0` | Stable [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md) | -| `v9.3.0` | `getStaticPaths` introduced. | +| `v9.3.0` | `getStaticPaths` introduced. |
diff --git a/docs/api-reference/data-fetching/get-static-props.md b/docs/api-reference/data-fetching/get-static-props.md index 7e04d1d9252e..c5f34276ffa8 100644 --- a/docs/api-reference/data-fetching/get-static-props.md +++ b/docs/api-reference/data-fetching/get-static-props.md @@ -7,12 +7,14 @@ description: API reference for `getStaticProps`. Learn how to use `getStaticProp
Version History -| Version | Changes | -| --------- | ----------------------------------------------------------------------------------------------------------------- | -| `v10.0.0` | `locale`, `locales`, `defaultLocale`, and `notFound` options added. | -| `v9.5.0` | Stable [Incremental Static Regeneration](https://nextjs.org/blog/next-9-5#stable-incremental-static-regeneration) | -| `v9.3.0` | `getStaticProps` introduced. | -| `v10.0.0` | `fallback: 'blocking'` return option added. | +| Version | Changes | +| ------- | ------- | + +| `v12.1.0` | [On-demand Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md#on-demand-revalidation-beta) added (Beta). | +| `v10.0.0` | `locale`, `locales`, `defaultLocale`, and `notFound` options added. | +| `v10.0.0` | `fallback: 'blocking'` return option added. | +| `v9.5.0` | Stable [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md) | +| `v9.3.0` | `getStaticProps` introduced. |
diff --git a/docs/api-reference/next/image.md b/docs/api-reference/next/image.md index 0e3d2cb1617a..4905a0f1125a 100644 --- a/docs/api-reference/next/image.md +++ b/docs/api-reference/next/image.md @@ -16,7 +16,8 @@ description: Enable Image Optimization with the built-in Image component. | Version | Changes | | --------- | ------------------------------------------------------------------------------------------------- | -| `v12.0.9` | `lazyRoot` prop added | +| `v12.1.0` | `dangerouslyAllowSVG` and `contentSecurityPolicy` configuration added. | +| `v12.0.9` | `lazyRoot` prop added. | | `v12.0.0` | `formats` configuration added.
AVIF support added.
Wrapper `
` changed to ``. | | `v11.1.0` | `onLoadingComplete` and `lazyBoundary` props added. | | `v11.0.0` | `src` prop support for static import.
`placeholder` prop added.
`blurDataURL` prop added. | @@ -401,7 +402,7 @@ module.exports = { The following describes the caching algorithm for the default [loader](#loader). For all other loaders, please refer to your cloud provider's documentation. -Images are optimized dynamically upon request and stored in the `/cache/images` directory. The optimized image file will be served for subsequent requests until the expiration is reached. When a request is made that matches a cached but expired file, the cached file is deleted before generating a new optimized image and caching the new file. +Images are optimized dynamically upon request and stored in the `/cache/images` directory. The optimized image file will be served for subsequent requests until the expiration is reached. When a request is made that matches a cached but expired file, the expired image is served stale immediately. Then the image is optimized again in the background (also called revalidation) and saved to the cache with the new expiration date. The expiration (or rather Max Age) is defined by either the [`minimumCacheTTL`](#minimum-cache-ttl) configuration or the upstream server's `Cache-Control` header, whichever is larger. Specifically, the `max-age` value of the `Cache-Control` header is used. If both `s-maxage` and `max-age` are found, then `s-maxage` is preferred. @@ -439,6 +440,21 @@ module.exports = { } ``` +### Dangerously Allow SVG + +The default [loader](#loader) does not optimize SVG images for a few reasons. First, SVG is a vector format meaning it can be resized losslessly. Second, SVG has many of the same features as HTML/CSS, which can lead to vulnerabilities without proper [Content Security Policy (CSP) headers](/docs/advanced-features/security-headers.md). + +If you need to serve SVG images with the default Image Optimization API, you can set `dangerouslyAllowSVG` and `contentSecurityPolicy` inside your `next.config.js`: + +```js +module.exports = { + images: { + dangerouslyAllowSVG: true, + contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;", + }, +} +``` + ## Related For an overview of the Image component features and usage guidelines, see: diff --git a/docs/api-routes/response-helpers.md b/docs/api-routes/response-helpers.md index 41673745b259..0e3af609fbc6 100644 --- a/docs/api-routes/response-helpers.md +++ b/docs/api-routes/response-helpers.md @@ -12,6 +12,7 @@ The included helpers are: - `res.json(body)` - Sends a JSON response. `body` must be a [serializable object](https://developer.mozilla.org/en-US/docs/Glossary/Serialization) - `res.send(body)` - Sends the HTTP response. `body` can be a `string`, an `object` or a `Buffer` - `res.redirect([status,] path)` - Redirects to a specified path or URL. `status` must be a valid [HTTP status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes). If not specified, `status` defaults to "307" "Temporary redirect". +- `res.unstable_revalidate(urlPath)` - [Revalidate a page on demand](/docs/basic-features/data-fetching/incremental-static-regeneration.md#on-demand-revalidation-beta) using `getStaticProps`. `urlPath` must be a `string`. ## Setting the status code of a response diff --git a/docs/basic-features/data-fetching/client-side.md b/docs/basic-features/data-fetching/client-side.md index 97cad41a1a8a..6b1768a6e901 100644 --- a/docs/basic-features/data-fetching/client-side.md +++ b/docs/basic-features/data-fetching/client-side.md @@ -68,3 +68,14 @@ function Profile() { ) } ``` + +## Related + +For more information on what to do next, we recommend the following sections: + + diff --git a/docs/basic-features/data-fetching/get-server-side-props.md b/docs/basic-features/data-fetching/get-server-side-props.md index 53952a44942e..377a775aa5a2 100644 --- a/docs/basic-features/data-fetching/get-server-side-props.md +++ b/docs/basic-features/data-fetching/get-server-side-props.md @@ -74,6 +74,10 @@ export async function getServerSideProps() { export default Page ``` +## Related + +For more information on what to do next, we recommend the following sections: +
getServerSideProps API Reference diff --git a/docs/basic-features/data-fetching/get-static-paths.md b/docs/basic-features/data-fetching/get-static-paths.md index cb9b5b68b438..ecb2e7f57c8c 100644 --- a/docs/basic-features/data-fetching/get-static-paths.md +++ b/docs/basic-features/data-fetching/get-static-paths.md @@ -19,7 +19,7 @@ export async function getStaticPaths() { } ``` -Note that`getStaticProps` **must** be used with `getStaticPaths`, and that you **cannot** use it with [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md). +`getStaticPaths` **must** be used with `getStaticProps`. You **cannot** use it with [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md). The [`getStaticPaths` API reference](/docs/api-reference/data-fetching/get-static-paths.md) covers all parameters and props that can be used with `getStaticPaths`. @@ -35,7 +35,11 @@ You should use `getStaticPaths` if you’re statically pre-rendering pages that ## When does getStaticPaths run -`getStaticPaths` only runs at build time on server-side. If you're using [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md), `getStaticPaths` can also be run on-demand _in the background_, but still only on the server-side. +`getStaticPaths` always runs on the server and never on the client. You can validate code written inside `getStaticPaths` is removed from the client-side bundle [with this tool](https://next-code-elimination.vercel.app/). + +- `getStaticPaths` runs during `next build` for Pages included in `paths` +- `getStaticPaths` runs on-demand in the background when using `fallback: true` +- `getStaticPaths` runs on-demand blocking rendering when using `fallback: blocking` ## Where can I use getStaticPaths @@ -47,6 +51,10 @@ Note that you must use export `getStaticPaths` as a standalone function — it w In development (`next dev`), `getStaticPaths` will be called on every request. +## Related + +For more information on what to do next, we recommend the following sections: +
getStaticPaths API Reference diff --git a/docs/basic-features/data-fetching/get-static-props.md b/docs/basic-features/data-fetching/get-static-props.md index 0b483f5a6b99..a5a5190fda97 100644 --- a/docs/basic-features/data-fetching/get-static-props.md +++ b/docs/basic-features/data-fetching/get-static-props.md @@ -23,9 +23,17 @@ You should use `getStaticProps` if: - The data can be publicly cached (not user-specific) - The page must be pre-rendered (for SEO) and be very fast — `getStaticProps` generates `HTML` and `JSON` files, both of which can be cached by a CDN for performance +## When does getStaticProps run + +`getStaticProps` always runs on the server and never on the client. You can validate code written inside `getStaticProps` is removed from the client-side bundle [with this tool](https://next-code-elimination.vercel.app/). + +- `getStaticProps` always runs during `next build` +- `getStaticProps` runs in the background when using `revalidate` +- `getStaticProps` runs on-demand in the background when using [`unstable_revalidate`](/docs/basic-features/data-fetching/incremental-static-regeneration.md#on-demand-revalidation-beta) + When combined with [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md), `getStaticProps` will run in the background while the stale page is being revalidated, and the fresh page served to the browser. -Because `getStaticProps` runs at build time, it does **not** have access to the incoming request (such as query parameters or `HTTP` headers) as it generates static `HTML`. If you need access to the request for your page, consider using [Middleware](/docs/middleware.md) in addition to `getStaticProps`. +`getStaticProps` does not have access to the incoming request (such as query parameters or HTTP headers) as it generates static HTML. If you need access to the request for your page, consider using [Middleware](/docs/middleware.md) in addition to `getStaticProps`. ## Using getStaticProps to fetch data from a CMS @@ -128,9 +136,11 @@ In development (`next dev`), `getStaticProps` will be called on every request. ## Preview Mode -In some cases, you might want to temporarily bypass Static Generation and render the page at **request time** instead of build time. For example, you might be using a headless CMS and want to preview drafts before they're published. +You can temporarily bypass static generation and render the page at **request time** instead of build time using [**Preview Mode**](/docs/advanced-features/preview-mode.md). For example, you might be using a headless CMS and want to preview drafts before they're published. + +## Related -This use case is supported in Next.js by the [**Preview Mode**](/docs/advanced-features/preview-mode.md) feature. +For more information on what to do next, we recommend the following sections:
diff --git a/docs/basic-features/data-fetching/incremental-static-regeneration.md b/docs/basic-features/data-fetching/incremental-static-regeneration.md index abe7f13c8154..fa0a1b350e8d 100644 --- a/docs/basic-features/data-fetching/incremental-static-regeneration.md +++ b/docs/basic-features/data-fetching/incremental-static-regeneration.md @@ -16,9 +16,11 @@ description: 'Learn how to create or update static pages at runtime with Increme
Version History -| Version | Changes | -| -------- | ---------------- | -| `v9.5.0` | Base Path added. | +| Version | Changes | +| --------- | --------------------------------------------------------------------------------------- | +| `v12.1.0` | On-demand ISR added (Beta). | +| `v12.0.0` | [Bot-aware ISR fallback](https://nextjs.org/blog/next-12#bot-aware-isr-fallback) added. | +| `v9.5.0` | Base Path added. |
@@ -85,7 +87,61 @@ When a request is made to a page that was pre-rendered at build time, it will in When a request is made to a path that hasn’t been generated, Next.js will server-render the page on the first request. Future requests will serve the static file from the cache. ISR on Vercel [persists the cache globally and handles rollbacks](https://vercel.com/docs/concepts/next.js/incremental-static-regeneration). -## Error Handling and Revalidation +## On-demand Revalidation (Beta) + +If you set a `revalidate` time of `60`, all visitors will see the same generated version of your site for one minute. The only way to invalidate the cache is from someone visiting that page after the minute has passed. + +Starting with `v12.1.0`, Next.js supports on-demand Incremental Static Regeneration to manually purge the Next.js cache for a specific page. This makes it easier to update your site when: + +- Content from your headless CMS is created or updated +- Ecommerce metadata changes (price, description, category, reviews, etc.) + +Inside `getStaticProps`, you do not need to specify `revalidate` to use on-demand revalidation. If `revalidate` is omitted, Next.js will use the default value of `false` (no revalidation) and only revalidate the page on-demand when `unstable_revalidate` is called. + +### Using On-Demand Revalidation + +First, create a secret token only known by your Next.js app. This secret will be used to prevent unauthorized access to the revalidation API Route. You can access the route (either manually or with a webhook) with the following URL structure: + +```bash +https:///api/revalidate?secret= +``` + +Next, add the secret as an [Environment Variable](/docs/basic-features/environment-variables.md) to your application. Finally, create the revalidation API Route: + +```jsx +// pages/api/revalidate.js + +export default async function handler(req, res) { + // Check for secret to confirm this is a valid request + if (req.query.secret !== process.env.MY_SECRET_TOKEN) { + return res.status(401).json({ message: 'Invalid token' }) + } + + try { + await res.unstable_revalidate('/path-to-revalidate') + return res.json({ revalidated: true }) + } catch (err) { + // If there was an error, Next.js will continue + // to show the last successfully generated page + return res.status(500).send('Error revalidating') + } +} +``` + +[View our demo](https://on-demand-isr.vercel.app) to see on-demand revalidation in action and provide feedback. + +### Testing on-demand ISR during development + +When running locally with `next dev`, `getStaticProps` is invoked on every request. To verify your on-demand ISR configuration is correct, you will need to create a [production build](/docs/api-reference/cli.md#build) and start the [production server](/docs/api-reference/cli.md#production): + +```bash +$ next build +$ next start +``` + +Then, you are able to validate static pages are successfully revalidated. + +## Error handling and revalidation If there is an error inside `getStaticProps` when handling background regeneration, or you manually throw an error, the last successfully generated page will continue to show. On the next subsequent request, Next.js will retry calling `getStaticProps`. @@ -114,3 +170,14 @@ export async function getStaticProps() { } } ``` + +## Related + +For more information on what to do next, we recommend the following sections: + +
diff --git a/docs/middleware.md b/docs/middleware.md index c56eb4df4298..d204f99a6c4d 100644 --- a/docs/middleware.md +++ b/docs/middleware.md @@ -10,7 +10,7 @@ description: Learn how to use Middleware in Next.js to run code before a request | Version | Changes | | --------- | ------------------------------------------------------------------------------------------ | | `v12.0.9` | Enforce absolute URLs in Edge Runtime ([PR](https://github.com/vercel/next.js/pull/33410)) | -| `v12.0.0` | Middleware (beta) added. | +| `v12.0.0` | Middleware (Beta) added. | diff --git a/errors/invalid-images-config.md b/errors/invalid-images-config.md index 581724439222..b409f6056830 100644 --- a/errors/invalid-images-config.md +++ b/errors/invalid-images-config.md @@ -27,6 +27,10 @@ module.exports = { minimumCacheTTL: 60, // ordered list of acceptable optimized image formats (mime types) formats: ['image/webp'], + // enable dangerous use of SVG images + dangerouslyAllowSVG: false, + // set the Content-Security-Policy header + contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;", }, } ``` diff --git a/errors/manifest.json b/errors/manifest.json index bbe43951f5d5..d8afb228b260 100644 --- a/errors/manifest.json +++ b/errors/manifest.json @@ -620,6 +620,10 @@ "title": "ignored-compiler-options", "path": "/errors/ignored-compiler-options.md" }, + { + "title": "opening-an-issue", + "path": "/errors/opening-an-issue.md" + }, { "title": "multiple-flush-effects", "path": "/errors/multiple-flush-effects.md" diff --git a/errors/middleware-relative-urls.md b/errors/middleware-relative-urls.md index dabdf3306085..856bc94afaf3 100644 --- a/errors/middleware-relative-urls.md +++ b/errors/middleware-relative-urls.md @@ -2,11 +2,11 @@ #### Why This Error Occurred -You are using a Middleware function that uses `Response.redirect(url)`, `NextResponse.redirect(url)` or `NextResponse.rewrite(url)` where `url` is a relative or an invalid URL. Currently this will work, but building a request with `new Request(url)` or running `fetch(url)` when `url` is a relative URL will **not** work. For this reason and to bring consistency to Next.js Middleware, this behavior will be deprecated soon in favor of always using absolute URLs. +You are using a Middleware function that uses `Response.redirect(url)`, `NextResponse.redirect(url)` or `NextResponse.rewrite(url)` where `url` is a relative or an invalid URL. Prior to Next.js 12.1, we allowed passing relative URLs. However, constructing a request with `new Request(url)` or running `fetch(url)` when `url` is a relative URL **does not** work. For this reason and to bring consistency to Next.js Middleware, this behavior has been deprecated and now removed. #### Possible Ways to Fix It -To fix this warning you must always pass absolute URL for redirecting and rewriting. There are several ways to get the absolute URL but the recommended way is to clone `NextURL` and mutate it: +To fix this error you must always pass absolute URL for redirecting and rewriting. There are several ways to get the absolute URL but the recommended way is to clone `NextURL` and mutate it: ```typescript import type { NextRequest } from 'next/server' diff --git a/errors/opening-an-issue.md b/errors/opening-an-issue.md new file mode 100644 index 000000000000..34a08b391843 --- /dev/null +++ b/errors/opening-an-issue.md @@ -0,0 +1,28 @@ +# Opening a new Issue + +#### Why This Message Occurred + +When `next info` was run, Next.js detected that it's was not on the latest canary release. + +`next@canary` is the canary version of Next.js that ships daily. It includes all features and fixes that have not been released to the stable version yet. Think of canary as a public beta. + +Some issues may already be fixed in the canary version, so please verify that your issue reproduces before opening a new issue. + +Run the following in the codebase: + +```bash +npm install next@canary +``` + +or + +```bash +yarn add next@canary +``` + +And go through the prepared reproduction steps once again, and check if the issue still exists. + +### Useful Links + +- [Video: How to Contribute to Open Source (Next.js)](https://www.youtube.com/watch?v=cuoNzXFLitc) +- [Contributing to Next.js](https://github.com/vercel/next.js/blob/canary/contributing.md) diff --git a/examples/with-firebase/pages/index.js b/examples/with-firebase/pages/index.js index bfef773741ee..4993df640231 100644 --- a/examples/with-firebase/pages/index.js +++ b/examples/with-firebase/pages/index.js @@ -20,9 +20,7 @@ export default function Home() { const createUser = async () => { const db = getFirestore() - await setDoc(doc(db, 'profile', profile.username), { - profile, - }) + await setDoc(doc(db, 'profile', profile.username), profile) alert('User created!!') } diff --git a/lerna.json b/lerna.json index a517697c141b..3aa8531b8bb4 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "12.0.11-canary.18" + "version": "12.1.0" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index bfaf28f519db..4e57156340a9 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "12.0.11-canary.18", + "version": "12.1.0", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index 08d215f8ab7f..c141e9f35c9d 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "12.0.11-canary.18", + "version": "12.1.0", "description": "ESLint configuration used by NextJS.", "main": "index.js", "license": "MIT", @@ -9,7 +9,7 @@ "directory": "packages/eslint-config-next" }, "dependencies": { - "@next/eslint-plugin-next": "12.0.11-canary.18", + "@next/eslint-plugin-next": "12.1.0", "@rushstack/eslint-patch": "^1.0.8", "@typescript-eslint/parser": "^5.0.0", "eslint-import-resolver-node": "^0.3.4", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index f776c2485cef..16f5abeb55cb 100644 --- a/packages/eslint-plugin-next/package.json +++ b/packages/eslint-plugin-next/package.json @@ -1,6 +1,6 @@ { "name": "@next/eslint-plugin-next", - "version": "12.0.11-canary.18", + "version": "12.1.0", "description": "ESLint plugin for NextJS.", "main": "lib/index.js", "license": "MIT", diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index 25e64cab7382..90bd9a3bccf2 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "12.0.11-canary.18", + "version": "12.1.0", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 5f95a9835b6e..25b85d8f7df4 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "12.0.11-canary.18", + "version": "12.1.0", "license": "MIT", "dependencies": { "chalk": "4.1.0", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 3a9151ee3418..42a1b5c61c38 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "12.0.11-canary.18", + "version": "12.1.0", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 1600d8aedb0f..06c4c6b95eb7 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "12.0.11-canary.18", + "version": "12.1.0", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index 749b71487b03..2fedae0f986f 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "12.0.11-canary.18", + "version": "12.1.0", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index 6a204da8d44d..9f6788d25e1b 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "12.0.11-canary.18", + "version": "12.1.0", "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", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 549509485d95..3882f7dc5d81 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "12.0.11-canary.18", + "version": "12.1.0", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next-swc/Cargo.lock b/packages/next-swc/Cargo.lock index af269fdef7c0..603918b6e739 100644 --- a/packages/next-swc/Cargo.lock +++ b/packages/next-swc/Cargo.lock @@ -812,7 +812,7 @@ dependencies = [ "swc_css", "swc_ecma_loader", "swc_ecma_transforms_testing", - "swc_ecmascript 0.114.2", + "swc_ecmascript", "swc_node_base", "swc_stylis", "testing", @@ -839,7 +839,7 @@ dependencies = [ "swc_bundler", "swc_common", "swc_ecma_loader", - "swc_ecmascript 0.114.2", + "swc_ecmascript", "swc_node_base", ] @@ -915,9 +915,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.9.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" [[package]] name = "opaque-debug" @@ -1589,15 +1589,15 @@ dependencies = [ "serde", "swc_atoms", "swc_common", - "swc_ecmascript 0.112.6", + "swc_ecmascript", "tracing", ] [[package]] name = "swc" -version = "0.126.2" +version = "0.121.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c2373a6f6dddd42719a2d8fcd9989849915ae15691fd1a75f6b04d14a6cab91" +checksum = "e6d2753ff42ccbcf55b444c5b5c75bdf8d711b9c022c6117a1945a5b259721da" dependencies = [ "ahash", "anyhow", @@ -1628,7 +1628,7 @@ dependencies = [ "swc_ecma_transforms_optimization", "swc_ecma_utils", "swc_ecma_visit", - "swc_ecmascript 0.114.2", + "swc_ecmascript", "swc_node_comments", "swc_visit", "tracing", @@ -1646,9 +1646,9 @@ dependencies = [ [[package]] name = "swc_bundler" -version = "0.107.0" +version = "0.105.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b6761f9b57072658a42ff25304a7edc7f8444024726ed27d5861e4644c1ffe" +checksum = "6288db2c327430667c9ab30fb053a76f6dbb4868569ef9833e04d355ef1e1d96" dependencies = [ "ahash", "anyhow", @@ -1711,9 +1711,9 @@ dependencies = [ [[package]] name = "swc_css" -version = "0.85.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f657d1876aa951dabce2be53af58e73f3c6fd61d17f177b647966ad9d77168" +checksum = "75a4e8913ddbf92a39e087996c30a80dfcac5192adc3fb63cd3beabe8975b0c8" dependencies = [ "swc_css_ast", "swc_css_codegen", @@ -1724,9 +1724,9 @@ dependencies = [ [[package]] name = "swc_css_ast" -version = "0.77.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26127ec7150da493706c0a8153cf1ad73fe1f2dd4e010e51ca783a97d4881a44" +checksum = "b0861c74eba5c61ade5c1ef3a14e3e0fd0699f20e5619bbc292c2ac4f9463617" dependencies = [ "is-macro", "serde", @@ -1737,9 +1737,9 @@ dependencies = [ [[package]] name = "swc_css_codegen" -version = "0.82.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f7181cd4048b62f2e89e5a26b38c7d2d6bb5cd9080850b8d0278f8525062688" +checksum = "de309d82614f8d3613c27f4ccafbacc1ed524ca5bfd7792387f76314d5131df2" dependencies = [ "auto_impl", "bitflags", @@ -1764,9 +1764,9 @@ dependencies = [ [[package]] name = "swc_css_parser" -version = "0.83.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ce9cc25996b42c50a0d2312021faf9e2d646a8c5baea6ea17ab7e928ba4bda" +checksum = "ac061b34fb0a3afa4ac5777b705c149f91c2ab29fc5f8463acb2bf8a17e02938" dependencies = [ "bitflags", "lexical", @@ -1777,9 +1777,9 @@ dependencies = [ [[package]] name = "swc_css_utils" -version = "0.74.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee0b4a58cce4b02567f54c079cd7ce3a18bfb79638f81f2197079e7c35e4f749" +checksum = "94cdc01e5631f63f8d4c52545d9a61ed5a6616a2d8d4d4b6235ea2ff779ae80c" dependencies = [ "swc_atoms", "swc_common", @@ -1789,9 +1789,9 @@ dependencies = [ [[package]] name = "swc_css_visit" -version = "0.76.0" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d4bab1a12ffe359eca6413b09865e27c9e05c0e8d765bebd558d14df31f6e44" +checksum = "ce49ce82798c85e1a8f2a46552b6ef9530868302c333f8b135da3b5c9d5b1ca8" dependencies = [ "swc_atoms", "swc_common", @@ -1862,16 +1862,13 @@ dependencies = [ [[package]] name = "swc_ecma_lints" -version = "0.14.8" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dc7e7d669e28b2df325d9a232f1d74e7b77d2606c04f0f70cc37e38dcf49ad3" +checksum = "a4c9664c8261d32d1dff6df9978c4aa3b0ac686b8bbcab9d4cc8a2f56c9efdfc" dependencies = [ - "ahash", "auto_impl", - "dashmap", "parking_lot", "rayon", - "regex", "serde", "swc_atoms", "swc_common", @@ -1902,9 +1899,9 @@ dependencies = [ [[package]] name = "swc_ecma_minifier" -version = "0.74.1" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "184e0e6337f2a4cfff36e3456e7937846dadf0b47833b7f711654e4acd0a13aa" +checksum = "6541c47325e3b6b2c0a1ecdff32049bc3ae83765eb09e49f532ed4fcbe54a6e7" dependencies = [ "ahash", "indexmap", @@ -1931,9 +1928,9 @@ dependencies = [ [[package]] name = "swc_ecma_parser" -version = "0.88.3" +version = "0.88.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd30c93f08afdf29226b5695e45aadcc6ce452470cc63ea87a7eb53d29bb02b" +checksum = "016e15f5837f1fb954c7a693a0229fc25cba2982ab9c13773db44ce0e00dc275" dependencies = [ "either", "enum_kind", @@ -1951,9 +1948,9 @@ dependencies = [ [[package]] name = "swc_ecma_preset_env" -version = "0.90.0" +version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5335f575d5cd4fcefe8bb3c52fa8dc8d59d461f07da43fb2ac6c31689aa88e0f" +checksum = "7ebd2ffd8d26a5d52d86da8d643038da2952e86993998f120554a80e0a64e2b5" dependencies = [ "ahash", "anyhow", @@ -1977,9 +1974,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms" -version = "0.117.0" +version = "0.115.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1fa132c1a736c2c61736958f1102249348a4cc911c7f60e3a6255aa49c1c03e" +checksum = "142a3d2bd7a1d78fd2112a98cb295b7b6aa67c2c590d639650882cd658fc43c9" dependencies = [ "swc_atoms", "swc_common", @@ -2020,9 +2017,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_classes" -version = "0.46.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8983235c6902879b65dcb1003d4084adf094408c96d94d62d3f33f44c3fa8e" +checksum = "d8fec6a1780299ff1006539b71bed431bde5e48c9773235de4ccc414467ff0f2" dependencies = [ "swc_atoms", "swc_common", @@ -2034,9 +2031,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_compat" -version = "0.70.0" +version = "0.69.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9a64fc99b205b6104a6996832b0006fc059f5aa9fe61d5fb2c3b9f1885b14" +checksum = "c7e1ec45963836c97a27c1c43c316ddfdddeac274aa793223a0a82391ba2304f" dependencies = [ "ahash", "arrayvec 0.7.2", @@ -2072,9 +2069,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_module" -version = "0.78.0" +version = "0.76.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "441616e68981062a1650a923d2c63075021a8178045e2112007a90a157cdb1da" +checksum = "151f5f8fb7bf42d4bbc04e61af0b5fd84e31b1a3014e006f60246b1cac68274e" dependencies = [ "Inflector", "ahash", @@ -2094,9 +2091,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_optimization" -version = "0.87.0" +version = "0.85.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e074149dd5e969d35a790851b47b0f76700b14fcfda1c05e15751c1458e2dd38" +checksum = "57792864e708f7ba41c4fed8c8b8e2c01dba8da4a44aa43ebb70a60e07e45265" dependencies = [ "ahash", "dashmap", @@ -2117,9 +2114,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_proposal" -version = "0.77.0" +version = "0.76.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048ac8ea82e02fa9a54b9aa448dc5d15a8e994304364fcd8c4e2f650572c9141" +checksum = "4e0c8f8ad3e3960658e4d7a4a87609b893b2a023f570e45b91f4ebe025845b43" dependencies = [ "either", "serde", @@ -2137,9 +2134,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_react" -version = "0.80.0" +version = "0.78.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb5b7c98597bf41d1503ca4039be5445fd02e7aa381ae520d1c78a8a370f7f5" +checksum = "3d93afe0884dbcb5d4f931d6b2673ba65ad17d0de4d1c88e69f2730f13817739" dependencies = [ "ahash", "base64 0.13.0", @@ -2185,9 +2182,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_typescript" -version = "0.82.0" +version = "0.80.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7859a18a33f751d488fbc1b7a0073fb08c69d794ad1f8daa2da47bfda2d9242" +checksum = "4b136bcb8a598378542393b2e9b51231e23e34b858baa0582aa72e78a2d5fee5" dependencies = [ "serde", "swc_atoms", @@ -2202,9 +2199,9 @@ dependencies = [ [[package]] name = "swc_ecma_utils" -version = "0.65.3" +version = "0.65.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b462ac7dd5340544e7a12965bb7fbbbf9db8b26c1b32159b43c4b2430fed3fc8" +checksum = "a372b01214518c1dbfb9f84d7e08e8e46e2098e7e1f63eb9e9f6f06025ae2863" dependencies = [ "indexmap", "once_cell", @@ -2235,18 +2232,6 @@ name = "swc_ecmascript" version = "0.112.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce38cf2c41ed841d82dbfb1c328379a3ec75d4e4e43900ea8c8ef3c51a44e3cb" -dependencies = [ - "swc_ecma_ast", - "swc_ecma_parser", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecmascript" -version = "0.114.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e752c12b209c65e203a952186ab2fcefa8021a17a8a393b7d5f410e1fe1a0e87" dependencies = [ "swc_ecma_ast", "swc_ecma_codegen", @@ -2328,9 +2313,9 @@ dependencies = [ [[package]] name = "swc_stylis" -version = "0.81.0" +version = "0.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cf66bb3ab3e136fb0852c2577824cd19b76f3346d7c5558cfdb782b5f14f6a4" +checksum = "75fd0f4f37579b15bbcd762b0fd1ae3fc6ef29215777b96cbc2bcdfaacd439ab" dependencies = [ "swc_atoms", "swc_common", @@ -2664,7 +2649,7 @@ dependencies = [ "serde_json", "swc", "swc_common", - "swc_ecmascript 0.114.2", + "swc_ecmascript", "tracing", "wasm-bindgen", "wasm-bindgen-futures", diff --git a/packages/next-swc/crates/core/Cargo.toml b/packages/next-swc/crates/core/Cargo.toml index 6e82e60b7a98..1b43fb3ebc4c 100644 --- a/packages/next-swc/crates/core/Cargo.toml +++ b/packages/next-swc/crates/core/Cargo.toml @@ -16,14 +16,14 @@ pathdiff = "0.2.0" serde = "1" serde_json = "1" styled_components = "0.14.0" -swc = "0.126.2" +swc = "0.121.7" swc_atoms = "0.2.7" swc_common = { version = "0.17.0", features = ["concurrent", "sourcemap"] } -swc_css = "0.85.0" +swc_css = "0.46.0" swc_ecma_loader = { version = "0.28.0", features = ["node", "lru"] } -swc_ecmascript = { version = "0.114.2", features = ["codegen", "minifier", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"] } +swc_ecmascript = { version = "0.112.6", features = ["codegen", "minifier", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"] } swc_node_base = "0.5.1" -swc_stylis = "0.81.0" +swc_stylis = "0.43.0" tracing = {version = "0.1.28", features = ["release_max_level_off"]} regex = "1.5" diff --git a/packages/next-swc/crates/core/src/styled_jsx/transform_css.rs b/packages/next-swc/crates/core/src/styled_jsx/transform_css.rs index d25cd05e6349..dcf00d73bf56 100644 --- a/packages/next-swc/crates/core/src/styled_jsx/transform_css.rs +++ b/packages/next-swc/crates/core/src/styled_jsx/transform_css.rs @@ -21,7 +21,7 @@ use tracing::{debug, trace}; use super::{hash_string, string_literal_expr, LocalStyle}; pub fn transform_css( - _cm: Arc, + cm: Arc, style_info: &LocalStyle, is_global: bool, class_name: &Option, @@ -33,6 +33,7 @@ pub fn transform_css( style_info.css_span.lo, style_info.css_span.hi, ParserConfig { + parse_values: false, allow_wrong_line_comments: true, }, // We ignore errors because we inject placeholders for expressions which is @@ -61,6 +62,7 @@ pub fn transform_css( }; // ? Do we need to support optionally prefixing? ss.visit_mut_with(&mut prefixer()); + ss.visit_mut_with(&mut CssPlaceholderFixer { cm }); ss.visit_mut_with(&mut Namespacer { class_name: match class_name { Some(s) => s.clone(), @@ -129,6 +131,48 @@ fn read_number(s: &str) -> (usize, usize) { unreachable!("read_number(`{}`) is invalid because it is empty", s) } +/// This fixes invalid css which is created from interpolated expressions. +/// +/// `__styled-jsx-placeholder-` is handled at here. +struct CssPlaceholderFixer { + cm: Arc, +} + +impl VisitMut for CssPlaceholderFixer { + fn visit_mut_media_query(&mut self, q: &mut MediaQuery) { + q.visit_mut_children_with(self); + + match q { + MediaQuery::Ident(q) => { + if !q.raw.starts_with("__styled-jsx-placeholder-") { + return; + } + // We need to support both of @media ($breakPoint) {} and @media $queryString {} + // This is complex because @media (__styled-jsx-placeholder-0__) {} is valid + // while @media __styled-jsx-placeholder-0__ {} is not + // + // So we check original source code to determine if we should inject + // parenthesis. + + // TODO(kdy1): Avoid allocation. + // To remove allocation, we should patch swc_common to provide a way to get + // source code without allocation. + // + // + // We need + // + // fn with_source_code (self: &mut Self, f: impl FnOnce(&str) -> Ret) -> _ {} + if let Ok(source) = self.cm.span_to_snippet(q.span) { + if source.starts_with('(') { + q.raw = format!("({})", &q.value).into(); + } + } + } + _ => {} + } + } +} + struct Namespacer { class_name: String, is_global: bool, @@ -185,7 +229,9 @@ impl VisitMut for Namespacer { } ComplexSelectorChildren::Combinator(v) => match v.value { CombinatorValue::Descendant => {} - _ => { + CombinatorValue::NextSibling + | CombinatorValue::Child + | CombinatorValue::LaterSibling => { combinator = Some(v.clone()); new_selectors.push(sel); @@ -205,22 +251,23 @@ impl Namespacer { ) -> Result, Error> { let mut pseudo_index = None; - let empty_tokens = vec![]; + let empty_tokens = Tokens { + span: node.span, + tokens: vec![], + }; let mut arg_tokens; for (i, selector) in node.subclass_selectors.iter().enumerate() { let (name, args) = match selector { SubclassSelector::PseudoClass(PseudoClassSelector { name, children, .. }) => { - arg_tokens = children - .iter() - .flatten() - .flat_map(|v| match v { - PseudoSelectorChildren::Nth(v) => nth_to_tokens(v).tokens, - PseudoSelectorChildren::PreservedToken(v) => vec![v.clone()], - }) - .collect::>(); - - (name, &arg_tokens) + match children { + Some(PseudoSelectorChildren::Nth(v)) => { + arg_tokens = nth_to_tokens(&v); + (name, &arg_tokens) + } + Some(PseudoSelectorChildren::Tokens(v)) => (name, v), + None => (name, &empty_tokens), + } } SubclassSelector::PseudoElement(PseudoElementSelector { name, children, .. @@ -233,19 +280,9 @@ impl Namespacer { // One off global selector if &name.value == "global" { - let args = args.clone(); - let mut args = { - let lo = args.first().map(|v| v.span.lo).unwrap_or(BytePos(0)); - let hi = args.last().map(|v| v.span.hi).unwrap_or(BytePos(0)); - - Tokens { - span: Span::new(lo, hi, Default::default()), - tokens: args, - } - }; - let block_tokens = get_block_tokens(&args); let mut front_tokens = get_front_selector_tokens(&args); + let mut args = args.clone(); front_tokens.extend(args.tokens); front_tokens.extend(block_tokens); args.tokens = front_tokens; @@ -254,6 +291,7 @@ impl Namespacer { let x: ComplexSelector = parse_tokens( &args, ParserConfig { + parse_values: false, allow_wrong_line_comments: true, }, // TODO(kdy1): We might be able to report syntax errors. @@ -484,6 +522,7 @@ fn nth_to_tokens(nth: &Nth) -> Tokens { let mut lexer = swc_css::parser::lexer::Lexer::new( StringInput::new(&s, nth.span.lo, nth.span.hi), ParserConfig { + parse_values: false, allow_wrong_line_comments: true, ..Default::default() }, diff --git a/packages/next-swc/crates/core/tests/errors/styled-jsx/ts-with-css-resolve/output.js b/packages/next-swc/crates/core/tests/errors/styled-jsx/ts-with-css-resolve/output.js index 6fbd823e7814..135a868443be 100644 --- a/packages/next-swc/crates/core/tests/errors/styled-jsx/ts-with-css-resolve/output.js +++ b/packages/next-swc/crates/core/tests/errors/styled-jsx/ts-with-css-resolve/output.js @@ -1,5 +1,5 @@ import _JSXStyle from "styled-jsx/style"; export default { - styles: <_JSXStyle id={"71f03d42ea0ec6"}>{".container.jsx-71f03d42ea0ec6{background:#000;color:white;font-weight:700;height:100px}"}, + styles: <_JSXStyle id={"71f03d42ea0ec6"}>{".container.jsx-71f03d42ea0ec6{background:#000;\ncolor:white;\nfont-weight:700;\nheight:100px}"}, className: "jsx-71f03d42ea0ec6" -}; +}; \ No newline at end of file diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/css-selector-after-pseudo/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/css-selector-after-pseudo/output.js index adc37567bc9b..6cb37534c641 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/css-selector-after-pseudo/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/css-selector-after-pseudo/output.js @@ -4,7 +4,7 @@ function NavigationItem({ active , className }) { active }, className, "navigation-item") || "")}> - <_JSXStyle id={"2342aae4628612c6"}>{".navigation-item.jsx-2342aae4628612c6 a::after{content:attr(data-text);content:attr(data-text)/\"\"}"} + <_JSXStyle id={"2342aae4628612c6"}>{".navigation-item.jsx-2342aae4628612c6 a::after{content:attr(data-text);\ncontent: attr(data-text) / ''}"} ; } diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/expressions/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/expressions/output.js index 5822f4fe70aa..ea1779a5ca0e 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/expressions/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/expressions/output.js @@ -51,7 +51,8 @@ export default (({ display })=>
test

- <_JSXStyle id={"6116059e04f3bff7"}>{`p.${color}.jsx-1ada4ad4dab7822f{color:${otherColor};display:${obj.display}}`} + <_JSXStyle id={"6116059e04f3bff7"}>{`p.${color}.jsx-1ada4ad4dab7822f{color:${otherColor}; +display:${obj.display}}`} <_JSXStyle id={"94239b6d6b42c9b5"}>{"p.jsx-1ada4ad4dab7822f{color:red}"} @@ -73,11 +74,16 @@ export default (({ display })=>
{`p.__jsx-style-dynamic-selector{color:${darken(color) + 2}}`} - <_JSXStyle id={"4e4be2da62837c76"}>{`@media(min-width:${mediumScreen}){p.jsx-1ada4ad4dab7822f{color:green}p.jsx-1ada4ad4dab7822f{color:${`red`}}}p.jsx-1ada4ad4dab7822f{color:red}`} + <_JSXStyle id={"4e4be2da62837c76"}>{`@media (min-width:${mediumScreen}) {p.jsx-1ada4ad4dab7822f{color:green} +p.jsx-1ada4ad4dab7822f{color:${`red`}}} +p.jsx-1ada4ad4dab7822f{color:red}`} - <_JSXStyle id={"27040f0829fb73d4"}>{`p.jsx-1ada4ad4dab7822f{-webkit-animation-duration:${animationDuration};animation-duration:${animationDuration}}`} + <_JSXStyle id={"27040f0829fb73d4"}>{`p.jsx-1ada4ad4dab7822f{-webkit-animation-duration:${animationDuration}; +animation-duration:${animationDuration}}`} - <_JSXStyle id={"3e72d735e703a530"}>{`p.jsx-1ada4ad4dab7822f{-webkit-animation:${animationDuration} forwards ${animationName};animation:${animationDuration} forwards ${animationName}}div.jsx-1ada4ad4dab7822f{background:${color}}`} + <_JSXStyle id={"3e72d735e703a530"}>{`p.jsx-1ada4ad4dab7822f{-webkit-animation:${animationDuration} forwards ${animationName}; +animation:${animationDuration} forwards ${animationName}} +div.jsx-1ada4ad4dab7822f{background:${color}}`} diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/external-stylesheet/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/external-stylesheet/output.js index e0304b413fd0..7dc7df9b4c8d 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/external-stylesheet/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/external-stylesheet/output.js @@ -14,7 +14,7 @@ export default (()=>
woot
- <_JSXStyle id={"bee92b62eadf8a14"}>{"p.jsx-bee92b62eadf8a14{color:red}div.jsx-bee92b62eadf8a14{color:green}"} + <_JSXStyle id={"bee92b62eadf8a14"}>{"p.jsx-bee92b62eadf8a14{color:red}\ndiv.jsx-bee92b62eadf8a14{color:green}"} <_JSXStyle id={styles.__hash}>{styles} @@ -30,7 +30,7 @@ export const Test = ()=>
woot
- <_JSXStyle id={"bee92b62eadf8a14"}>{"p.jsx-bee92b62eadf8a14{color:red}div.jsx-bee92b62eadf8a14{color:green}"} + <_JSXStyle id={"bee92b62eadf8a14"}>{"p.jsx-bee92b62eadf8a14{color:red}\ndiv.jsx-bee92b62eadf8a14{color:green}"}
; diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/fragment/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/fragment/output.js index aefcddc43fcb..dda9c2b5fc7c 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/fragment/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/fragment/output.js @@ -28,7 +28,7 @@ export default (()=><> - <_JSXStyle id={"6dd5f97e085c0297"}>{"p.jsx-6dd5f97e085c0297{color:cyan}.foo.jsx-6dd5f97e085c0297{font-size:18px;color:hotpink}#head.jsx-6dd5f97e085c0297{text-decoration:underline}"} + <_JSXStyle id={"6dd5f97e085c0297"}>{"p.jsx-6dd5f97e085c0297{color:cyan}\n.foo.jsx-6dd5f97e085c0297{font-size:18px;\ncolor:hotpink}\n#head.jsx-6dd5f97e085c0297{text-decoration:underline}"} ); diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/global/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/global/output.js index 2ec88553ec8d..7eab66c441b5 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/global/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/global/output.js @@ -1,7 +1,7 @@ import _JSXStyle from "styled-jsx/style"; const Test = ()=>
- <_JSXStyle id={"d47d6adadf14e957"}>{"body{color:red}:hover{color:red;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-animation:foo 1s ease-out;animation:foo 1s ease-out}div a{display:none}[data-test]>div{color:red}"} + <_JSXStyle id={"d47d6adadf14e957"}>{"body{color:red}\n:hover{color:red;\ndisplay:-webkit-box;\ndisplay:-webkit-flex;\ndisplay:-ms-flexbox;\ndisplay:flex;\n-webkit-animation:foo 1s ease-out;\nanimation:foo 1s ease-out}\ndiv a{display:none}\n[data-test]>div{color:red}"}
; diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/issue-30480/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/issue-30480/output.js index f36b6e2e7383..d91b5759a137 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/issue-30480/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/issue-30480/output.js @@ -10,7 +10,7 @@ export default (({ breakPoint })=>
{`@media(${breakPoint}){}`} + ]}>{`@media (${breakPoint}) {}`}
); diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/issue-30570/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/issue-30570/output.js index ac9cfafc4245..fec5cf368064 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/issue-30570/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/issue-30570/output.js @@ -6,7 +6,7 @@ export default function IndexPage() { - <_JSXStyle id={"bbdada4ef17d18ef"}>{"@supports(display:flex){h1{color:hotpink}}"} + <_JSXStyle id={"bbdada4ef17d18ef"}>{"@supports (display:flex) {h1{color:hotpink}}"}
; }; diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/issue-31562-interpolation-in-mdea/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/issue-31562-interpolation-in-mdea/output.js index ac5394ea192a..fb59ef207106 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/issue-31562-interpolation-in-mdea/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/issue-31562-interpolation-in-mdea/output.js @@ -36,7 +36,10 @@ export default class { Typography.base.size.mediumPlus, Target.largePlus, Typography.base.size.largePlus - ]}>{`html{font-size:${Typography.base.size.default};line-height:${Typography.base.lineHeight}}@media ${Target.mediumPlus}{html{font-size:${Typography.base.size.mediumPlus}}}@media ${Target.largePlus}{html{font-size:${Typography.base.size.largePlus}}}`} + ]}>{`html{font-size:${Typography.base.size.default}; +line-height:${Typography.base.lineHeight}} +@media ${Target.mediumPlus} {html{font-size:${Typography.base.size.mediumPlus}}} +@media ${Target.largePlus} {html{font-size:${Typography.base.size.largePlus}}}`}
; } diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/styles/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/styles/output.js index 7b61c0b7ec13..415058e5691e 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/styles/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/styles/output.js @@ -11,18 +11,21 @@ export const uh = bar; export const foo = new String(`div.jsx-945eaa83250ed332{color:${color}}`); foo.__hash = "945eaa83250ed332"; ({ - styles: <_JSXStyle id={"c107a919a5b2943d"}>{`div.jsx-c107a919a5b2943d{color:${colors.green.light}}a.jsx-c107a919a5b2943d{color:red}`}, + styles: <_JSXStyle id={"c107a919a5b2943d"}>{`div.jsx-c107a919a5b2943d{color:${colors.green.light}} +a.jsx-c107a919a5b2943d{color:red}`}, className: "jsx-c107a919a5b2943d" }); const b = { - styles: <_JSXStyle id={"c107a919a5b2943d"}>{`div.jsx-c107a919a5b2943d{color:${colors.green.light}}a.jsx-c107a919a5b2943d{color:red}`}, + styles: <_JSXStyle id={"c107a919a5b2943d"}>{`div.jsx-c107a919a5b2943d{color:${colors.green.light}} +a.jsx-c107a919a5b2943d{color:red}`}, className: "jsx-c107a919a5b2943d" }; const dynamic = (colors1)=>{ const b = { styles: <_JSXStyle id={"60132422fc87f1d1"} dynamic={[ colors1.green.light - ]}>{`div.__jsx-style-dynamic-selector{color:${colors1.green.light}}a.__jsx-style-dynamic-selector{color:red}`}, + ]}>{`div.__jsx-style-dynamic-selector{color:${colors1.green.light}} +a.__jsx-style-dynamic-selector{color:red}`}, className: _JSXStyle.dynamic([ [ "60132422fc87f1d1", @@ -34,6 +37,7 @@ const dynamic = (colors1)=>{ }; }; export default { - styles: <_JSXStyle id={"e5da8dd7ff5c7f39"}>{`div.jsx-e5da8dd7ff5c7f39{font-size:3em}p.jsx-e5da8dd7ff5c7f39{color:${color}}`}, + styles: <_JSXStyle id={"e5da8dd7ff5c7f39"}>{`div.jsx-e5da8dd7ff5c7f39{font-size:3em} +p.jsx-e5da8dd7ff5c7f39{color:${color}}`}, className: "jsx-e5da8dd7ff5c7f39" }; diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/too-many/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/too-many/output.js index fd67a8dec05a..00ff7acd83a6 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/too-many/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/too-many/output.js @@ -43,7 +43,21 @@ export const Red = ({ Component ="button" })=>{ e13, e14, e15 - ]}>{`.button.__jsx-style-dynamic-selector{--button-1:${e1};--button-2:${e2};--button-3:${e3};--button-4:${e4};--button-5:${e5};--button-6:${e6};--button-7:${e7};--button-8:${e8};--button-9:${e9};--button-10:${e10};--button-11:${e11};--button-12:${e12};--button-13:${e13};--button-14:${e14};--button-15:${e15}}`} + ]}>{`.button.__jsx-style-dynamic-selector{--button-1: ${e1}; +--button-2: ${e2}; +--button-3: ${e3}; +--button-4: ${e4}; +--button-5: ${e5}; +--button-6: ${e6}; +--button-7: ${e7}; +--button-8: ${e8}; +--button-9: ${e9}; +--button-10: ${e10}; +--button-11: ${e11}; +--button-12: ${e12}; +--button-13: ${e13}; +--button-14: ${e14}; +--button-15: ${e15}}`} ; }; diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-escape-1/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-escape-1/output.js index b12af6f02d53..7b9b2f69a803 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-escape-1/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-escape-1/output.js @@ -5,7 +5,7 @@ export default class {

test

- <_JSXStyle id={"1f6cef12199c3a8f"}>{"p.jsx-1f6cef12199c3a8f{content:\"`\"}"} + <_JSXStyle id={"1f6cef12199c3a8f"}>{"p.jsx-1f6cef12199c3a8f{content:'`'}"}
; } diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-escape-2/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-escape-2/output.js index c94ca5493d06..3e3ed2489c67 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-escape-2/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-escape-2/output.js @@ -11,7 +11,8 @@ export default function Home({ fontFamily }) { <_JSXStyle id={"f804e2f486b6ac13"} dynamic={[ fontFamily - ]}>{`body{font-family:${fontFamily}}code:before,code:after{content:"\`"}`} + ]}>{`body{font-family:${fontFamily}} +code:before, code:after{content:'\`'}`}
; }; diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-1-as-property/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-1-as-property/output.js index 28d29b34b196..9d93281ced13 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-1-as-property/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-1-as-property/output.js @@ -21,7 +21,7 @@ export default class { <_JSXStyle id={"e359801ddd3b3cb6"} dynamic={[ inputSize ? "height: calc(2 * var(--a)) !important;" : "" - ]}>{`@media only screen{a.__jsx-style-dynamic-selector{${inputSize ? "height: calc(2 * var(--a)) !important;" : ""} + ]}>{`@media only screen {a.__jsx-style-dynamic-selector{${inputSize ? "height: calc(2 * var(--a)) !important;" : ""} }}`}
; diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-2-as-part-of-value/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-2-as-part-of-value/output.js index 7c0c59d4ac65..bc25a6c5235b 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-2-as-part-of-value/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-2-as-part-of-value/output.js @@ -27,7 +27,8 @@ export default class { a[b], -1 * (c || 0), d - ]}>{`.a:hover .b.__jsx-style-dynamic-selector{a:${a[b]}px!important;b:translate3d(0,${-1 * (c || 0)}px,-${d}px)scale(1)!important}`} + ]}>{`.a:hover .b.__jsx-style-dynamic-selector{a:${a[b]}px!important; +b:translate3d(0, ${-1 * (c || 0)}px, -${d}px) scale(1)!important}`} ; } diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-3-as-value/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-3-as-value/output.js index 754e53aa0330..f311c3526881 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-3-as-value/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-3-as-value/output.js @@ -21,7 +21,7 @@ export default class { <_JSXStyle id={"4ca4ef3595473f53"} dynamic={[ a - ]}>{`@media only screen{a.__jsx-style-dynamic-selector{color:${a}}}`} + ]}>{`@media only screen {a.__jsx-style-dynamic-selector{color:${a}}}`} ; } diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-4-as-part-of-value-in-multiple/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-4-as-part-of-value-in-multiple/output.js index 8f9e67c31304..4683d8c24306 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-4-as-part-of-value-in-multiple/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-4-as-part-of-value-in-multiple/output.js @@ -24,7 +24,9 @@ export default class { <_JSXStyle id={"97886c1e9511aafa"} dynamic={[ a || "var(--c)", b || "inherit" - ]}>{`.a:hover .b.__jsx-style-dynamic-selector{display:inline-block;padding:0 ${a || "var(--c)"};color:${b || "inherit"}}`} + ]}>{`.a:hover .b.__jsx-style-dynamic-selector{display:inline-block; +padding:0 ${a || "var(--c)"}; +color:${b || "inherit"}}`} ; } diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-5-values-of-multiple-properties/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-5-values-of-multiple-properties/output.js index 898bc9e3d60a..e009c13a7456 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-5-values-of-multiple-properties/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/tpl-placeholder-5-values-of-multiple-properties/output.js @@ -24,7 +24,8 @@ export default class { <_JSXStyle id={"bcc606c168bcd197"} dynamic={[ a ? "100%" : "200px", b ? "0" : "8px 20px" - ]}>{`.item.__jsx-style-dynamic-selector{max-width:${a ? "100%" : "200px"};padding:${b ? "0" : "8px 20px"}}`} + ]}>{`.item.__jsx-style-dynamic-selector{max-width:${a ? "100%" : "200px"}; +padding:${b ? "0" : "8px 20px"}}`} ; } diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-complex-selector/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-complex-selector/output.js index 5f2002467dea..a6480c2fb787 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-complex-selector/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-complex-selector/output.js @@ -3,7 +3,7 @@ export default (()=>

test

- <_JSXStyle id={"713499aa363d6373"}>{"p.jsx-713499aa363d6373 a.jsx-713499aa363d6373 span.jsx-713499aa363d6373{color:red}p.jsx-713499aa363d6373 span{background:blue}p.jsx-713499aa363d6373 a[title=\"'w ' ' t'\"].jsx-713499aa363d6373{margin:auto}p.jsx-713499aa363d6373 span:not(.test){color:green}p.jsx-713499aa363d6373,h1.jsx-713499aa363d6373{color:blue;-webkit-animation:hahaha 3s ease forwards infinite;animation:hahaha 3s ease forwards infinite;-webkit-animation-name:hahaha;animation-name:hahaha;animation-delay:100ms}p.jsx-713499aa363d6373{-webkit-animation:hahaha 1s,hehehe 2s;animation:hahaha 1s,hehehe 2s}p.jsx-713499aa363d6373:hover{color:red}p.jsx-713499aa363d6373::before{color:red}.jsx-713499aa363d6373:hover{color:red}.jsx-713499aa363d6373::before{color:red}.jsx-713499aa363d6373:hover p.jsx-713499aa363d6373{color:red}p.jsx-713499aa363d6373+a.jsx-713499aa363d6373{color:red}p.jsx-713499aa363d6373~a.jsx-713499aa363d6373{color:red}p.jsx-713499aa363d6373>a.jsx-713499aa363d6373{color:red}@keyframes hahaha{from{top:0}to{top:100}}@keyframes hehehe{from{left:0}to{left:100}}@media(min-width:500px){.test.jsx-713499aa363d6373{color:red}}.test.jsx-713499aa363d6373{display:block}.inline-flex.jsx-713499aa363d6373{display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex}.flex.jsx-713499aa363d6373{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.test.jsx-713499aa363d6373{box-shadow:0 0 10px black,inset 0 0 5px black}.test[title=\",\"].jsx-713499aa363d6373{display:inline-block}.test.is-status.jsx-713499aa363d6373 .test.jsx-713499aa363d6373{color:red}.a-selector.jsx-713499aa363d6373:hover,.a-selector.jsx-713499aa363d6373:focus{outline:none}"} + <_JSXStyle id={"713499aa363d6373"}>{"p.jsx-713499aa363d6373 a.jsx-713499aa363d6373 span.jsx-713499aa363d6373{color:red}\np.jsx-713499aa363d6373 span{background:blue}\np.jsx-713499aa363d6373 a[title=\"'w ' ' t'\"].jsx-713499aa363d6373{margin:auto}\np.jsx-713499aa363d6373 span:not(.test){color:green}\np.jsx-713499aa363d6373, h1.jsx-713499aa363d6373{color:blue;\n-webkit-animation:hahaha 3s ease forwards infinite;\nanimation:hahaha 3s ease forwards infinite;\n-webkit-animation-name:hahaha;\nanimation-name:hahaha;\nanimation-delay:100ms}\np.jsx-713499aa363d6373{-webkit-animation:hahaha 1s, hehehe 2s;\nanimation:hahaha 1s, hehehe 2s}\np.jsx-713499aa363d6373:hover{color:red}\np.jsx-713499aa363d6373::before{color:red}\n.jsx-713499aa363d6373:hover{color:red}\n.jsx-713499aa363d6373::before{color:red}\n.jsx-713499aa363d6373:hover p.jsx-713499aa363d6373{color:red}\np.jsx-713499aa363d6373+a.jsx-713499aa363d6373{color:red}\np.jsx-713499aa363d6373~a.jsx-713499aa363d6373{color:red}\np.jsx-713499aa363d6373>a.jsx-713499aa363d6373{color:red}\n@keyframes hahaha {from {top:0}to {top:100}}\n@keyframes hehehe {from {left:0}to {left:100}}\n@media (min-width:500px) {.test.jsx-713499aa363d6373{color:red}}\n.test.jsx-713499aa363d6373{display:block}\n.inline-flex.jsx-713499aa363d6373{display:-webkit-inline-box;\ndisplay:-webkit-inline-flex;\ndisplay:-ms-inline-flexbox;\ndisplay:inline-flex}\n.flex.jsx-713499aa363d6373{display:-webkit-box;\ndisplay:-webkit-flex;\ndisplay:-ms-flexbox;\ndisplay:flex}\n.test.jsx-713499aa363d6373{box-shadow:0 0 10px black, inset 0 0 5px black}\n.test[title=\",\"].jsx-713499aa363d6373{display:inline-block}\n.test.is-status.jsx-713499aa363d6373 .test.jsx-713499aa363d6373{color:red}\n.a-selector.jsx-713499aa363d6373:hover, .a-selector.jsx-713499aa363d6373:focus{outline:none}"}
); diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-global/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-global/output.js index b9405bfa1150..e8ab9d155548 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-global/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-global/output.js @@ -3,7 +3,7 @@ export default (()=>

test

- <_JSXStyle id={"53fd644ab080300c"}>{"html.jsx-53fd644ab080300c{background-image:linear-gradient(0deg,rgba(255,255,255,.8),rgba(255,255,255,.8)),url(/static/background.svg)}p{color:blue}p{color:blue}p,a.jsx-53fd644ab080300c{color:blue}.foo+a{color:red}body{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Helvetica,Arial,sans-serif}"} + <_JSXStyle id={"53fd644ab080300c"}>{"html.jsx-53fd644ab080300c{background-image:linear-gradient(0deg, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.8)), url(/static/background.svg)}\np{color:blue}\np{color:blue}\np, a.jsx-53fd644ab080300c{color:blue}\n.foo+a{color:red}\nbody{font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif}"}
); diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-media-query/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-media-query/output.js index c5a6b455fb26..abc84ac0f354 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-media-query/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-media-query/output.js @@ -3,7 +3,7 @@ export default (()=>

test

- <_JSXStyle id={"1f7963ae04c6466a"}>{"@media(min-width:1px)and (max-width:768px){[class*=\"grid__col--\"].jsx-1f7963ae04c6466a{margin-top:12px;margin-bottom:12px}}@media(max-width:64em){.test.jsx-1f7963ae04c6466a{margin-bottom:1em}@supports(-moz-appearance:none)and (display:contents){.test.jsx-1f7963ae04c6466a{margin-bottom:2rem}}}"} + <_JSXStyle id={"1f7963ae04c6466a"}>{"@media (min-width:1px) and (max-width:768px) {[class*='grid__col--'].jsx-1f7963ae04c6466a{margin-top:12px;\nmargin-bottom:12px}}\n@media (max-width:64em) {.test.jsx-1f7963ae04c6466a{margin-bottom:1em}\n@supports (-moz-appearance:none) and (display:contents) {.test.jsx-1f7963ae04c6466a{margin-bottom:2rem}}}"}
); diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-normal/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-normal/output.js index 98a03ea47b4c..9c81fa9facdf 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-normal/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css-normal/output.js @@ -3,7 +3,7 @@ export default (()=>

test

- <_JSXStyle id={"1a19bb4817c105dd"}>{"p.jsx-1a19bb4817c105dd{color:red}p.jsx-1a19bb4817c105dd{color:red}*.jsx-1a19bb4817c105dd{color:blue}[href=\"woot\"].jsx-1a19bb4817c105dd{color:red}"} + <_JSXStyle id={"1a19bb4817c105dd"}>{"p.jsx-1a19bb4817c105dd{color:red}\np.jsx-1a19bb4817c105dd{color:red}\n*.jsx-1a19bb4817c105dd{color:blue}\n[href=\"woot\"].jsx-1a19bb4817c105dd{color:red}"}
); diff --git a/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css/output.js b/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css/output.js index 7a279450b965..ddefde312281 100644 --- a/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css/output.js +++ b/packages/next-swc/crates/core/tests/fixture/styled-jsx/transform-css/output.js @@ -3,7 +3,7 @@ export default (()=>

test

- <_JSXStyle id={"768337a97aceabd1"}>{"html.jsx-768337a97aceabd1{background-image:linear-gradient(0deg,rgba(255,255,255,.8),rgba(255,255,255,.8)),url(/static/background.svg)}p{color:blue}p{color:blue}p,a.jsx-768337a97aceabd1{color:blue}.foo+a{color:red}body{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Helvetica,Arial,sans-serif}p.jsx-768337a97aceabd1{color:red}p.jsx-768337a97aceabd1{color:red}*.jsx-768337a97aceabd1{color:blue}[href=\"woot\"].jsx-768337a97aceabd1{color:red}p.jsx-768337a97aceabd1 a.jsx-768337a97aceabd1 span.jsx-768337a97aceabd1{color:red}p.jsx-768337a97aceabd1 span{background:blue}p.jsx-768337a97aceabd1 a[title=\"'w ' ' t'\"].jsx-768337a97aceabd1{margin:auto}p.jsx-768337a97aceabd1 span:not(.test){color:green}p.jsx-768337a97aceabd1,h1.jsx-768337a97aceabd1{color:blue;-webkit-animation:hahaha 3s ease forwards infinite;animation:hahaha 3s ease forwards infinite;-webkit-animation-name:hahaha;animation-name:hahaha;animation-delay:100ms}p.jsx-768337a97aceabd1{-webkit-animation:hahaha 1s,hehehe 2s;animation:hahaha 1s,hehehe 2s}p.jsx-768337a97aceabd1:hover{color:red}p.jsx-768337a97aceabd1::before{color:red}.jsx-768337a97aceabd1:hover{color:red}.jsx-768337a97aceabd1::before{color:red}.jsx-768337a97aceabd1:hover p.jsx-768337a97aceabd1{color:red}p.jsx-768337a97aceabd1+a.jsx-768337a97aceabd1{color:red}p.jsx-768337a97aceabd1~a.jsx-768337a97aceabd1{color:red}p.jsx-768337a97aceabd1>a.jsx-768337a97aceabd1{color:red}@keyframes hahaha{from{top:0}to{top:100}}@keyframes hehehe{from{left:0}to{left:100}}@media(min-width:500px){.test.jsx-768337a97aceabd1{color:red}}.test.jsx-768337a97aceabd1{display:block}.inline-flex.jsx-768337a97aceabd1{display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex}.flex.jsx-768337a97aceabd1{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.test.jsx-768337a97aceabd1{box-shadow:0 0 10px black,inset 0 0 5px black}.test[title=\",\"].jsx-768337a97aceabd1{display:inline-block}.test.is-status.jsx-768337a97aceabd1 .test.jsx-768337a97aceabd1{color:red}.a-selector.jsx-768337a97aceabd1:hover,.a-selector.jsx-768337a97aceabd1:focus{outline:none}@media(min-width:1px)and (max-width:768px){[class*=\"grid__col--\"].jsx-768337a97aceabd1{margin-top:12px;margin-bottom:12px}}@media(max-width:64em){.test.jsx-768337a97aceabd1{margin-bottom:1em}@supports(-moz-appearance:none)and (display:contents){.test.jsx-768337a97aceabd1{margin-bottom:2rem}}}"} + <_JSXStyle id={"768337a97aceabd1"}>{"html.jsx-768337a97aceabd1{background-image:linear-gradient(0deg, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.8)), url(/static/background.svg)}\np{color:blue}\np{color:blue}\np, a.jsx-768337a97aceabd1{color:blue}\n.foo+a{color:red}\nbody{font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif}\np.jsx-768337a97aceabd1{color:red}\np.jsx-768337a97aceabd1{color:red}\n*.jsx-768337a97aceabd1{color:blue}\n[href=\"woot\"].jsx-768337a97aceabd1{color:red}\np.jsx-768337a97aceabd1 a.jsx-768337a97aceabd1 span.jsx-768337a97aceabd1{color:red}\np.jsx-768337a97aceabd1 span{background:blue}\np.jsx-768337a97aceabd1 a[title=\"'w ' ' t'\"].jsx-768337a97aceabd1{margin:auto}\np.jsx-768337a97aceabd1 span:not(.test){color:green}\np.jsx-768337a97aceabd1, h1.jsx-768337a97aceabd1{color:blue;\n-webkit-animation:hahaha 3s ease forwards infinite;\nanimation:hahaha 3s ease forwards infinite;\n-webkit-animation-name:hahaha;\nanimation-name:hahaha;\nanimation-delay:100ms}\np.jsx-768337a97aceabd1{-webkit-animation:hahaha 1s, hehehe 2s;\nanimation:hahaha 1s, hehehe 2s}\np.jsx-768337a97aceabd1:hover{color:red}\np.jsx-768337a97aceabd1::before{color:red}\n.jsx-768337a97aceabd1:hover{color:red}\n.jsx-768337a97aceabd1::before{color:red}\n.jsx-768337a97aceabd1:hover p.jsx-768337a97aceabd1{color:red}\np.jsx-768337a97aceabd1+a.jsx-768337a97aceabd1{color:red}\np.jsx-768337a97aceabd1~a.jsx-768337a97aceabd1{color:red}\np.jsx-768337a97aceabd1>a.jsx-768337a97aceabd1{color:red}\n@keyframes hahaha {from {top:0}to {top:100}}\n@keyframes hehehe {from {left:0}to {left:100}}\n@media (min-width:500px) {.test.jsx-768337a97aceabd1{color:red}}\n.test.jsx-768337a97aceabd1{display:block}\n.inline-flex.jsx-768337a97aceabd1{display:-webkit-inline-box;\ndisplay:-webkit-inline-flex;\ndisplay:-ms-inline-flexbox;\ndisplay:inline-flex}\n.flex.jsx-768337a97aceabd1{display:-webkit-box;\ndisplay:-webkit-flex;\ndisplay:-ms-flexbox;\ndisplay:flex}\n.test.jsx-768337a97aceabd1{box-shadow:0 0 10px black, inset 0 0 5px black}\n.test[title=\",\"].jsx-768337a97aceabd1{display:inline-block}\n.test.is-status.jsx-768337a97aceabd1 .test.jsx-768337a97aceabd1{color:red}\n.a-selector.jsx-768337a97aceabd1:hover, .a-selector.jsx-768337a97aceabd1:focus{outline:none}\n@media (min-width:1px) and (max-width:768px) {[class*='grid__col--'].jsx-768337a97aceabd1{margin-top:12px;\nmargin-bottom:12px}}\n@media (max-width:64em) {.test.jsx-768337a97aceabd1{margin-bottom:1em}\n@supports (-moz-appearance:none) and (display:contents) {.test.jsx-768337a97aceabd1{margin-bottom:2rem}}}"}
); diff --git a/packages/next-swc/crates/core/tests/full/example/output.js b/packages/next-swc/crates/core/tests/full/example/output.js index e0a9c88dd80f..ebba00c28d0b 100644 --- a/packages/next-swc/crates/core/tests/full/example/output.js +++ b/packages/next-swc/crates/core/tests/full/example/output.js @@ -1,9 +1,9 @@ -function a(a, b) { +import a from "other"; +function b(a, b) { (null == b || b > a.length) && (b = a.length); for(var c = 0, d = new Array(b); c < b; c++)d[c] = a[c]; return d; } -import b from "other"; (function(a, c) { return (function(a) { if (Array.isArray(a)) return a; @@ -24,17 +24,17 @@ import b from "other"; } return g; } - })(a, c) || (function(b, c) { - if (b) { - if ("string" == typeof b) return a(b, c); - var d = Object.prototype.toString.call(b).slice(8, -1); - if ("Object" === d && b.constructor && (d = b.constructor.name), "Map" === d || "Set" === d) return Array.from(d); - if ("Arguments" === d || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(d)) return a(b, c); + })(a, c) || (function(a, c) { + if (a) { + if ("string" == typeof a) return b(a, c); + var d = Object.prototype.toString.call(a).slice(8, -1); + if ("Object" === d && a.constructor && (d = a.constructor.name), "Map" === d || "Set" === d) return Array.from(d); + if ("Arguments" === d || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(d)) return b(a, c); } })(a, c) || (function() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); })(); -})(b, 1)[0]; +})(a, 1)[0]; var c = function() { "use strict"; !function(a, b) { diff --git a/packages/next-swc/crates/core/tests/loader/css-hygiene-1/output.js b/packages/next-swc/crates/core/tests/loader/css-hygiene-1/output.js index cd1517cec174..ad93e37fb91f 100644 --- a/packages/next-swc/crates/core/tests/loader/css-hygiene-1/output.js +++ b/packages/next-swc/crates/core/tests/loader/css-hygiene-1/output.js @@ -1,3 +1,3 @@ -var _defaultExport = new String("@media(max-width:870px){th.expiration-date-cell,td.expiration-date-cell{display:none}}"); +var _defaultExport = new String("@media (max-width:870px) {th.expiration-date-cell, td.expiration-date-cell{display:none}}"); _defaultExport.__hash = "fd71bf06ba8860bb"; export default _defaultExport; diff --git a/packages/next-swc/crates/core/tests/loader/example/output.js b/packages/next-swc/crates/core/tests/loader/example/output.js index 1bb38a8e44b6..7bbcd606d5af 100644 --- a/packages/next-swc/crates/core/tests/loader/example/output.js +++ b/packages/next-swc/crates/core/tests/loader/example/output.js @@ -1,3 +1,4 @@ +import other from 'other'; function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i]; @@ -49,7 +50,6 @@ function _unsupportedIterableToArray(o, minLen) { if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } -import other from 'other'; var _other = _slicedToArray(other, 1), foo = _other[0]; var Foo = function Foo() { "use strict"; diff --git a/packages/next-swc/crates/core/tests/loader/issue-31627/output.js b/packages/next-swc/crates/core/tests/loader/issue-31627/output.js index 1bd450ee9faf..abdbe09ac240 100644 --- a/packages/next-swc/crates/core/tests/loader/issue-31627/output.js +++ b/packages/next-swc/crates/core/tests/loader/issue-31627/output.js @@ -1,10 +1,10 @@ +import { useEffect } from 'react'; +import { select, selectAll } from 'd3-selection'; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } -import { useEffect } from 'react'; -import { select, selectAll } from 'd3-selection'; export default function Home() { useEffect(function() { new MyClass(); diff --git a/packages/next-swc/crates/core/tests/loader/styled-components/1/output.js b/packages/next-swc/crates/core/tests/loader/styled-components/1/output.js index 8e100ad666c0..70e1bb4afc6b 100644 --- a/packages/next-swc/crates/core/tests/loader/styled-components/1/output.js +++ b/packages/next-swc/crates/core/tests/loader/styled-components/1/output.js @@ -1,3 +1,4 @@ +import styled from 'styled-components'; function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); @@ -17,7 +18,6 @@ function _templateObject() { }; return data; } -import styled from 'styled-components'; export var foo = styled.input.withConfig({ displayName: "input__foo", componentId: "sc-12c52e68-0" diff --git a/packages/next-swc/crates/napi/Cargo.toml b/packages/next-swc/crates/napi/Cargo.toml index 493424eb23bf..f7d20e9a6405 100644 --- a/packages/next-swc/crates/napi/Cargo.toml +++ b/packages/next-swc/crates/napi/Cargo.toml @@ -16,12 +16,12 @@ once_cell = "1.8.0" serde = "1" serde_json = "1" next-swc = { version = "0.0.0", path = "../core" } -swc = "0.126.2" +swc = "0.121.7" swc_atoms = "0.2.7" -swc_bundler = { version = "0.107.0", features = ["concurrent"] } +swc_bundler = { version = "0.105.0", features = ["concurrent"] } swc_common = { version = "0.17.0", features = ["concurrent", "sourcemap"] } swc_ecma_loader = { version = "0.28.0", features = ["node", "lru"] } -swc_ecmascript = { version = "0.114.2", features = ["codegen", "minifier", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"] } +swc_ecmascript = { version = "0.112.6", features = ["codegen", "minifier", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"] } swc_node_base = "0.5.1" [build-dependencies] diff --git a/packages/next-swc/crates/wasm/Cargo.toml b/packages/next-swc/crates/wasm/Cargo.toml index b25e21bcbe0e..c84005528842 100644 --- a/packages/next-swc/crates/wasm/Cargo.toml +++ b/packages/next-swc/crates/wasm/Cargo.toml @@ -16,9 +16,9 @@ path-clean = "0.1" serde = {version = "1", features = ["derive"]} serde_json = "1" next-swc = { version = "0.0.0", path = "../core" } -swc = "0.126.2" +swc = "0.121.7" swc_common = { version = "0.17.0", features = ["concurrent", "sourcemap"] } -swc_ecmascript = { version = "0.114.2", features = ["codegen", "minifier", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"] } +swc_ecmascript = { version = "0.112.6", features = ["codegen", "minifier", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"] } tracing = {version = "0.1.28", features = ["release_max_level_off"]} wasm-bindgen = {version = "0.2", features = ["serde-serialize"]} wasm-bindgen-futures = "0.4.8" diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index 98b823888fac..9f16469b8681 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "12.0.11-canary.18", + "version": "12.1.0", "private": true, "scripts": { "build-native": "napi build --platform --cargo-name next_swc_napi native", diff --git a/packages/next/cli/next-info.ts b/packages/next/cli/next-info.ts index b0e957bc8dcc..4fd15e44dfc5 100755 --- a/packages/next/cli/next-info.ts +++ b/packages/next/cli/next-info.ts @@ -4,6 +4,7 @@ import childProcess from 'child_process' import chalk from 'next/dist/compiled/chalk' import arg from 'next/dist/compiled/arg/index.js' +import fetch from 'next/dist/compiled/node-fetch' import { printAndExit } from '../server/lib/utils' import { cliCommand } from '../bin/next' import isError from '../lib/is-error' @@ -41,6 +42,8 @@ const nextInfo: cliCommand = async (argv) => { return } + const installedRelease = getPackageVersion('next') + console.log(` Operating System: Platform: ${os.platform()} @@ -52,9 +55,36 @@ const nextInfo: cliCommand = async (argv) => { Yarn: ${getBinaryVersion('yarn')} pnpm: ${getBinaryVersion('pnpm')} Relevant packages: - next: ${getPackageVersion('next')} + next: ${installedRelease} react: ${getPackageVersion('react')} - react-dom: ${getPackageVersion('react-dom')}`) + react-dom: ${getPackageVersion('react-dom')} +`) + + try { + const res = await fetch( + 'https://api.github.com/repos/vercel/next.js/releases' + ) + const releases = await res.json() + const newestRelease = releases[0].tag_name.replace(/^v/, '') + + if (installedRelease !== newestRelease) { + console.warn( + `${chalk.yellow( + chalk.bold('warn') + )} - Latest canary version not detected, detected: "${installedRelease}", newest: "${newestRelease}". + Please try the latest canary version (\`npm install next@canary\`) to confirm the issue still exists before creating a new issue. + Read more - https://nextjs.org/docs/messages/opening-an-issue` + ) + } + } catch { + console.warn( + `${chalk.yellow( + chalk.bold('warn') + )} - Failed to fetch latest canary version. Visit https://github.com/vercel/next.js/releases. Detected "${installedRelease}". + Make sure to try the latest canary version (\`npm install next@canary\`) to confirm the issue still exists before creating a new issue. + Read more - https://nextjs.org/docs/messages/opening-an-issue` + ) + } } export { nextInfo } diff --git a/packages/next/client/dev/on-demand-entries-client.js b/packages/next/client/dev/on-demand-entries-client.js index 349f95f5c22d..c68a9dd287e4 100644 --- a/packages/next/client/dev/on-demand-entries-client.js +++ b/packages/next/client/dev/on-demand-entries-client.js @@ -11,7 +11,17 @@ export default async (page) => { } else { Router.ready(() => { setInterval(() => { - sendMessage(JSON.stringify({ event: 'ping', page: Router.pathname })) + // when notFound: true is returned we should use the notFoundPage + // as the Router.pathname will point to the 404 page but we want + // to ping the source page that returned notFound: true instead + const notFoundSrcPage = self.__NEXT_DATA__.notFoundSrcPage + const pathname = + (Router.pathname === '/404' || Router.pathname === '/_error') && + notFoundSrcPage + ? notFoundSrcPage + : Router.pathname + + sendMessage(JSON.stringify({ event: 'ping', page: pathname })) }, 2500) }) } diff --git a/packages/next/client/image.tsx b/packages/next/client/image.tsx index a634db7a22dd..2bc5f9ed8cb1 100644 --- a/packages/next/client/image.tsx +++ b/packages/next/client/image.tsx @@ -462,7 +462,7 @@ export default function Image({ ) } - if (!unoptimized) { + if (!unoptimized && loader !== defaultImageLoader) { const urlStr = loader({ config, src, @@ -862,6 +862,12 @@ function defaultLoader({ } } + if (src.endsWith('.svg') && !config.dangerouslyAllowSVG) { + // Special case to make svg serve as-is to avoid proxying + // through the built-in Image Optimization API. + return src + } + return `${config.path}?url=${encodeURIComponent(src)}&w=${width}&q=${ quality || 75 }` diff --git a/packages/next/compiled/webpack/bundle5.js b/packages/next/compiled/webpack/bundle5.js index 093c9ba888ef..fd1d6d65a405 100644 --- a/packages/next/compiled/webpack/bundle5.js +++ b/packages/next/compiled/webpack/bundle5.js @@ -19047,6 +19047,7 @@ const compareModuleIterables = compareIterables(compareModulesByIdentifier); /** @typedef {(c: Chunk, chunkGraph: ChunkGraph) => boolean} ChunkFilterPredicate */ /** @typedef {(m: Module) => boolean} ModuleFilterPredicate */ +/** @typedef {[Module, Entrypoint | undefined]} EntryModuleWithChunkGroup */ /** * @typedef {Object} ChunkSizeOptions @@ -20181,8 +20182,6 @@ class ChunkGraph { return cgc.dependentHashModules; } - /** @typedef {[Module, Entrypoint | undefined]} EntryModuleWithChunkGroup */ - /** * @param {Chunk} chunk the chunk * @returns {Iterable} iterable of modules (do not modify) @@ -22479,6 +22478,7 @@ const { isSourceEqual } = __webpack_require__(41245); /** * @typedef {Object} ChunkHashContext + * @property {CodeGenerationResults} codeGenerationResults results of code generation * @property {RuntimeTemplate} runtimeTemplate the runtime template * @property {ModuleGraph} moduleGraph the module graph * @property {ChunkGraph} chunkGraph the chunk graph @@ -26434,6 +26434,7 @@ This prevents using hashes of each other and should be avoided.`); chunk.updateHash(chunkHash, chunkGraph); this.hooks.chunkHash.call(chunk, chunkHash, { chunkGraph, + codeGenerationResults: this.codeGenerationResults, moduleGraph: this.moduleGraph, runtimeTemplate: this.runtimeTemplate }); @@ -27267,7 +27268,7 @@ This prevents using hashes of each other and should be avoided.`); strictModuleErrorHandling, strictModuleExceptionHandling } = this.outputOptions; - const __nested_webpack_require_152290__ = id => { + const __nested_webpack_require_152432__ = id => { const cached = moduleCache[id]; if (cached !== undefined) { if (cached.error) throw cached.error; @@ -27276,20 +27277,20 @@ This prevents using hashes of each other and should be avoided.`); const moduleArgument = moduleArgumentsById.get(id); return __webpack_require_module__(moduleArgument, id); }; - const interceptModuleExecution = (__nested_webpack_require_152290__[ + const interceptModuleExecution = (__nested_webpack_require_152432__[ RuntimeGlobals.interceptModuleExecution.replace( "__webpack_require__.", "" ) ] = []); - const moduleCache = (__nested_webpack_require_152290__[ + const moduleCache = (__nested_webpack_require_152432__[ RuntimeGlobals.moduleCache.replace( "__webpack_require__.", "" ) ] = {}); - context.__webpack_require__ = __nested_webpack_require_152290__; + context.__webpack_require__ = __nested_webpack_require_152432__; /** * @param {ExecuteModuleArgument} moduleArgument the module argument @@ -27305,7 +27306,7 @@ This prevents using hashes of each other and should be avoided.`); loaded: false, error: undefined }, - require: __nested_webpack_require_152290__ + require: __nested_webpack_require_152432__ }; interceptModuleExecution.forEach(handler => handler(execOptions) @@ -27345,7 +27346,7 @@ This prevents using hashes of each other and should be avoided.`); moduleArgumentsMap.get(runtimeModule) ); } - exports = __nested_webpack_require_152290__(module.identifier()); + exports = __nested_webpack_require_152432__(module.identifier()); } catch (e) { const err = new WebpackError( `Execution of module code from module graph (${module.readableIdentifier( @@ -34588,15 +34589,15 @@ class ExportsInfo { } } for (const exportInfo of this._exports.values()) { + if (!canMangle && exportInfo.canMangleProvide !== false) { + exportInfo.canMangleProvide = false; + changed = true; + } if (excludeExports && excludeExports.has(exportInfo.name)) continue; if (exportInfo.provided !== true && exportInfo.provided !== null) { exportInfo.provided = null; changed = true; } - if (!canMangle && exportInfo.canMangleProvide !== false) { - exportInfo.canMangleProvide = false; - changed = true; - } if (targetKey) { exportInfo.setTarget(targetKey, targetModule, [exportInfo.name], -1); } @@ -49902,7 +49903,10 @@ const LazySet = __webpack_require__(38938); const { getScheme } = __webpack_require__(54500); const { cachedCleverMerge, cachedSetProperty } = __webpack_require__(60839); const { join } = __webpack_require__(17139); -const { parseResource } = __webpack_require__(82186); +const { + parseResource, + parseResourceWithoutFragment +} = __webpack_require__(82186); /** @typedef {import("../declarations/WebpackOptions").ModuleOptionsNormalized} ModuleOptions */ /** @typedef {import("./Generator")} Generator */ @@ -49940,6 +49944,11 @@ const { parseResource } = __webpack_require__(82186); /** @typedef {ResourceData & { data: Record }} ResourceDataWithData */ +/** @typedef {Object} ParsedLoaderRequest + * @property {string} loader loader + * @property {string|undefined} options options + */ + const EMPTY_RESOLVE_OPTIONS = {}; const EMPTY_PARSER_OPTIONS = {}; const EMPTY_GENERATOR_OPTIONS = {}; @@ -49971,27 +49980,6 @@ const stringifyLoadersAndResource = (loaders, resource) => { return str + resource; }; -/** - * @param {string} resultString resultString - * @returns {{loader: string, options: string|undefined}} parsed loader request - */ -const identToLoaderRequest = resultString => { - const idx = resultString.indexOf("?"); - if (idx >= 0) { - const loader = resultString.substr(0, idx); - const options = resultString.substr(idx + 1); - return { - loader, - options - }; - } else { - return { - loader: resultString, - options: undefined - }; - } -}; - const needCalls = (times, callback) => { return err => { if (--times === 0) { @@ -50138,6 +50126,9 @@ class NormalModuleFactory extends ModuleFactory { const cacheParseResource = parseResource.bindCache( associatedObjectForCache ); + const cachedParseResourceWithoutFragment = + parseResourceWithoutFragment.bindCache(associatedObjectForCache); + this._parseResourceWithoutFragment = cachedParseResourceWithoutFragment; this.hooks.factorize.tapAsync( { @@ -50225,7 +50216,7 @@ class NormalModuleFactory extends ModuleFactory { let matchResourceData = undefined; /** @type {string} */ let unresolvedResource; - /** @type {{loader: string, options: string|undefined}[]} */ + /** @type {ParsedLoaderRequest[]} */ let elements; let noPreAutoLoaders = false; let noAutoLoaders = false; @@ -50279,7 +50270,13 @@ class NormalModuleFactory extends ModuleFactory { ) .split(/!+/); unresolvedResource = rawElements.pop(); - elements = rawElements.map(identToLoaderRequest); + elements = rawElements.map(el => { + const { path, query } = cachedParseResourceWithoutFragment(el); + return { + loader: path, + options: query ? query.slice(1) : undefined + }; + }); scheme = getScheme(unresolvedResource); } else { unresolvedResource = requestWithoutMatchResource; @@ -50891,12 +50888,14 @@ If changing the source code is not an option there is also a resolve options cal } if (err) return callback(err); - const parsedResult = identToLoaderRequest(result); + const parsedResult = this._parseResourceWithoutFragment(result); const resolved = { - loader: parsedResult.loader, + loader: parsedResult.path, options: item.options === undefined - ? parsedResult.options + ? parsedResult.query + ? parsedResult.query.slice(1) + : undefined : item.options, ident: item.options === undefined ? undefined : item.ident }; @@ -51311,7 +51310,7 @@ const createDefaultHandler = (profile, logger) => { /** * @callback ReportProgress * @param {number} p - * @param {...string[]} [args] + * @param {...string} [args] * @returns {void} */ @@ -55963,6 +55962,7 @@ module.exports.NUMBER_OF_IDENTIFIER_CONTINUATION_CHARS = +const mime = __webpack_require__(78585); const { basename, extname } = __webpack_require__(71017); const util = __webpack_require__(73837); const Chunk = __webpack_require__(39385); @@ -56075,29 +56075,53 @@ const replacePathVariables = (path, data, assetInfo) => { // [name] - file // [ext] - .js if (typeof data.filename === "string") { - const { path: file, query, fragment } = parseResource(data.filename); - - const ext = extname(file); - const base = basename(file); - const name = base.slice(0, base.length - ext.length); - const path = file.slice(0, file.length - base.length); - - replacements.set("file", replacer(file)); - replacements.set("query", replacer(query, true)); - replacements.set("fragment", replacer(fragment, true)); - replacements.set("path", replacer(path, true)); - replacements.set("base", replacer(base)); - replacements.set("name", replacer(name)); - replacements.set("ext", replacer(ext, true)); - // Legacy - replacements.set( - "filebase", - deprecated( - replacer(base), - "[filebase] is now [base]", - "DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_FILENAME" - ) - ); + // check that filename is data uri + let match = data.filename.match(/^data:([^;,]+)/); + if (match) { + const ext = mime.extension(match[1]); + const emptyReplacer = replacer("", true); + + replacements.set("file", emptyReplacer); + replacements.set("query", emptyReplacer); + replacements.set("fragment", emptyReplacer); + replacements.set("path", emptyReplacer); + replacements.set("base", emptyReplacer); + replacements.set("name", emptyReplacer); + replacements.set("ext", replacer(ext ? `.${ext}` : "", true)); + // Legacy + replacements.set( + "filebase", + deprecated( + emptyReplacer, + "[filebase] is now [base]", + "DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_FILENAME" + ) + ); + } else { + const { path: file, query, fragment } = parseResource(data.filename); + + const ext = extname(file); + const base = basename(file); + const name = base.slice(0, base.length - ext.length); + const path = file.slice(0, file.length - base.length); + + replacements.set("file", replacer(file)); + replacements.set("query", replacer(query, true)); + replacements.set("fragment", replacer(fragment, true)); + replacements.set("path", replacer(path, true)); + replacements.set("base", replacer(base)); + replacements.set("name", replacer(name)); + replacements.set("ext", replacer(ext, true)); + // Legacy + replacements.set( + "filebase", + deprecated( + replacer(base), + "[filebase] is now [base]", + "DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_FILENAME" + ) + ); + } } // Compilation context @@ -58118,6 +58142,7 @@ const Generator = __webpack_require__(93401); const RuntimeGlobals = __webpack_require__(16475); const createHash = __webpack_require__(49835); const { makePathsRelative } = __webpack_require__(82186); +const nonNumericOnlyHash = __webpack_require__(55668); /** @typedef {import("webpack-sources").Source} Source */ /** @typedef {import("../../declarations/WebpackOptions").AssetGeneratorOptions} AssetGeneratorOptions */ @@ -58338,8 +58363,8 @@ class AssetGenerator extends Generator { const fullHash = /** @type {string} */ ( hash.digest(runtimeTemplate.outputOptions.hashDigest) ); - const contentHash = fullHash.slice( - 0, + const contentHash = nonNumericOnlyHash( + fullHash, runtimeTemplate.outputOptions.hashDigestLength ); module.buildInfo.fullContentHash = fullHash; @@ -60103,7 +60128,7 @@ const visitModules = ( const module = it.value; if ( availableModules.has(module) || - availableModules.plus.has(m) + availableModules.plus.has(module) ) { cachedMinAvailableModules.add(module); } @@ -64946,6 +64971,7 @@ const applyOutputDefaults = ( D(output, "strictModuleExceptionHandling", false); const optimistic = v => v || v === undefined; + const conditionallyOptimistic = (v, c) => (v === undefined && c) || v; F( output.environment, "arrowFunction", @@ -64959,8 +64985,12 @@ const applyOutputDefaults = ( ); F(output.environment, "forOf", () => tp && optimistic(tp.forOf)); F(output.environment, "bigIntLiteral", () => tp && tp.bigIntLiteral); - F(output.environment, "dynamicImport", () => tp && tp.dynamicImport); - F(output.environment, "module", () => tp && tp.module); + F(output.environment, "dynamicImport", () => + conditionallyOptimistic(tp && tp.dynamicImport, output.module) + ); + F(output.environment, "module", () => + conditionallyOptimistic(tp && tp.module, output.module) + ); const { trustedTypes } = output; if (trustedTypes) { @@ -68350,78 +68380,71 @@ class CssLoadingRuntimeModule extends RuntimeModule { "", withLoading ? Template.asString([ - `${fn}.css = ${runtimeTemplate.basicFunction( - "chunkId, promises", - hasCssMatcher !== false - ? [ - "// css chunk loading", - `var installedChunkData = ${RuntimeGlobals.hasOwnProperty}(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;`, - 'if(installedChunkData !== 0) { // 0 means "already installed".', - Template.indent([ - "", - '// a Promise means "currently loading".', - "if(installedChunkData) {", - Template.indent([ - "promises.push(installedChunkData[2]);" - ]), - "} else {", - Template.indent([ - hasCssMatcher === true - ? "if(true) { // all chunks have CSS" - : `if(${hasCssMatcher("chunkId")}) {`, + `${fn}.css = ${runtimeTemplate.basicFunction("chunkId, promises", [ + "// css chunk loading", + `var installedChunkData = ${RuntimeGlobals.hasOwnProperty}(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;`, + 'if(installedChunkData !== 0) { // 0 means "already installed".', + Template.indent([ + "", + '// a Promise means "currently loading".', + "if(installedChunkData) {", + Template.indent(["promises.push(installedChunkData[2]);"]), + "} else {", + Template.indent([ + hasCssMatcher === true + ? "if(true) { // all chunks have CSS" + : `if(${hasCssMatcher("chunkId")}) {`, + Template.indent([ + "// setup Promise in chunk cache", + `var promise = new Promise(${runtimeTemplate.expressionFunction( + `installedChunkData = installedChunks[chunkId] = [resolve, reject]`, + "resolve, reject" + )});`, + "promises.push(installedChunkData[2] = promise);", + "", + "// start chunk loading", + `var url = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkCssFilename}(chunkId);`, + "// create error before stack unwound to get useful stacktrace later", + "var error = new Error();", + `var loadingEnded = ${runtimeTemplate.basicFunction( + "event", + [ + `if(${RuntimeGlobals.hasOwnProperty}(installedChunks, chunkId)) {`, Template.indent([ - "// setup Promise in chunk cache", - `var promise = new Promise(${runtimeTemplate.expressionFunction( - `installedChunkData = installedChunks[chunkId] = [resolve, reject]`, - "resolve, reject" - )});`, - "promises.push(installedChunkData[2] = promise);", - "", - "// start chunk loading", - `var url = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkCssFilename}(chunkId);`, - "// create error before stack unwound to get useful stacktrace later", - "var error = new Error();", - `var loadingEnded = ${runtimeTemplate.basicFunction( - "event", - [ - `if(${RuntimeGlobals.hasOwnProperty}(installedChunks, chunkId)) {`, - Template.indent([ - "installedChunkData = installedChunks[chunkId];", - "if(installedChunkData !== 0) installedChunks[chunkId] = undefined;", - "if(installedChunkData) {", - Template.indent([ - 'if(event.type !== "load") {', - Template.indent([ - "var errorType = event && event.type;", - "var realSrc = event && event.target && event.target.src;", - "error.message = 'Loading css chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';", - "error.name = 'ChunkLoadError';", - "error.type = errorType;", - "error.request = realSrc;", - "installedChunkData[1](error);" - ]), - "} else {", - Template.indent([ - `loadCssChunkData(${RuntimeGlobals.moduleFactories}, link, chunkId);`, - "installedChunkData[0]();" - ]), - "}" - ]), - "}" - ]), - "}" - ] - )};`, - "var link = loadStylesheet(chunkId, url, loadingEnded);" + "installedChunkData = installedChunks[chunkId];", + "if(installedChunkData !== 0) installedChunks[chunkId] = undefined;", + "if(installedChunkData) {", + Template.indent([ + 'if(event.type !== "load") {', + Template.indent([ + "var errorType = event && event.type;", + "var realSrc = event && event.target && event.target.src;", + "error.message = 'Loading css chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';", + "error.name = 'ChunkLoadError';", + "error.type = errorType;", + "error.request = realSrc;", + "installedChunkData[1](error);" + ]), + "} else {", + Template.indent([ + `loadCssChunkData(${RuntimeGlobals.moduleFactories}, link, chunkId);`, + "installedChunkData[0]();" + ]), + "}" + ]), + "}" ]), - "} else installedChunks[chunkId] = 0;" - ]), - "}" - ]), - "}" - ] - : "installedChunks[chunkId] = 0;" - )};` + "}" + ] + )};`, + "var link = loadStylesheet(chunkId, url, loadingEnded);" + ]), + "} else installedChunks[chunkId] = 0;" + ]), + "}" + ]), + "}" + ])};` ]) : "// no chunk loading", "", @@ -68550,6 +68573,7 @@ const { compareModulesByIdentifier } = __webpack_require__(29579); const createSchemaValidation = __webpack_require__(32540); const createHash = __webpack_require__(49835); const memoize = __webpack_require__(78676); +const nonNumericOnlyHash = __webpack_require__(55668); const CssExportsGenerator = __webpack_require__(91254); const CssGenerator = __webpack_require__(46061); const CssParser = __webpack_require__(98305); @@ -68732,7 +68756,7 @@ class CssModulesPlugin { hash.update(chunkGraph.getModuleHash(module, chunk.runtime)); } const digest = /** @type {string} */ (hash.digest(hashDigest)); - chunk.contentHash.css = digest.substr(0, hashDigestLength); + chunk.contentHash.css = nonNumericOnlyHash(digest, hashDigestLength); }); compilation.hooks.renderManifest.tap(plugin, (result, options) => { const { chunkGraph } = compilation; @@ -70636,7 +70660,8 @@ const interceptAllJavascriptModulesPluginHooks = (compilation, tracer) => { }; const makeInterceptorFor = (instance, tracer) => hookName => ({ - register: ({ name, type, context, fn }) => { + register: tapInfo => { + const { name, type, fn } = tapInfo; const newFn = // Don't tap our own hooks to ensure stream can close cleanly name === pluginName @@ -70647,9 +70672,7 @@ const makeInterceptorFor = (instance, tracer) => hookName => ({ fn }); return { - name, - type, - context, + ...tapInfo, fn: newFn }; } @@ -75184,12 +75207,18 @@ class ContextElementDependency extends ModuleDependency { } serialize(context) { - context.write(this.referencedExports); + const { write } = context; + write(this._typePrefix); + write(this._category); + write(this.referencedExports); super.serialize(context); } deserialize(context) { - this.referencedExports = context.read(); + const { read } = context; + this._typePrefix = read(); + this._category = read(); + this.referencedExports = read(); super.deserialize(context); } } @@ -76123,6 +76152,12 @@ const getProperty = (moduleGraph, module, exportName, property, runtime) => { } } switch (property) { + case "canMangle": { + const exportsInfo = moduleGraph.getExportsInfo(module); + const exportInfo = exportsInfo.getExportInfo(exportName); + if (exportInfo) return exportInfo.canMangle; + return exportsInfo.otherExportsInfo.canMangle; + } case "used": return ( moduleGraph.getExportsInfo(module).getUsed(exportName, runtime) !== @@ -87945,6 +87980,7 @@ const memoize = __webpack_require__(78676); /** @typedef {import("../declarations/WebpackOptions").Entry} Entry */ /** @typedef {import("../declarations/WebpackOptions").EntryNormalized} EntryNormalized */ /** @typedef {import("../declarations/WebpackOptions").EntryObject} EntryObject */ +/** @typedef {import("../declarations/WebpackOptions").FileCacheOptions} FileCacheOptions */ /** @typedef {import("../declarations/WebpackOptions").LibraryOptions} LibraryOptions */ /** @typedef {import("../declarations/WebpackOptions").ModuleOptions} ModuleOptions */ /** @typedef {import("../declarations/WebpackOptions").ResolveOptions} ResolveOptions */ @@ -87960,11 +87996,15 @@ const memoize = __webpack_require__(78676); /** @typedef {import("../declarations/WebpackOptions").WebpackPluginInstance} WebpackPluginInstance */ /** @typedef {import("./Compilation").Asset} Asset */ /** @typedef {import("./Compilation").AssetInfo} AssetInfo */ +/** @typedef {import("./Compilation").EntryOptions} EntryOptions */ +/** @typedef {import("./Compiler").AssetEmittedInfo} AssetEmittedInfo */ /** @typedef {import("./MultiStats")} MultiStats */ /** @typedef {import("./Parser").ParserState} ParserState */ /** @typedef {import("./ResolverFactory").ResolvePluginInstance} ResolvePluginInstance */ /** @typedef {import("./ResolverFactory").Resolver} Resolver */ /** @typedef {import("./Watching")} Watching */ +/** @typedef {import("./cli").Argument} Argument */ +/** @typedef {import("./cli").Problem} Problem */ /** @typedef {import("./stats/DefaultStatsFactoryPlugin").StatsAsset} StatsAsset */ /** @typedef {import("./stats/DefaultStatsFactoryPlugin").StatsChunk} StatsChunk */ /** @typedef {import("./stats/DefaultStatsFactoryPlugin").StatsChunkGroup} StatsChunkGroup */ @@ -89770,6 +89810,7 @@ const { last, someInIterable } = __webpack_require__(39104); const StringXor = __webpack_require__(40293); const { compareModulesByIdentifier } = __webpack_require__(29579); const createHash = __webpack_require__(49835); +const nonNumericOnlyHash = __webpack_require__(55668); const { intersectRuntime } = __webpack_require__(17156); const JavascriptGenerator = __webpack_require__(77106); const JavascriptParser = __webpack_require__(29050); @@ -89848,6 +89889,7 @@ const printGeneratedCodeForStack = (module, code) => { /** * @typedef {Object} RenderBootstrapContext * @property {Chunk} chunk the chunk + * @property {CodeGenerationResults} codeGenerationResults results of code generation * @property {RuntimeTemplate} runtimeTemplate the runtime template * @property {ModuleGraph} moduleGraph the module graph * @property {ChunkGraph} chunkGraph the chunk graph @@ -90077,6 +90119,7 @@ class JavascriptModulesPlugin { { hash: "0000", chunk, + codeGenerationResults: context.codeGenerationResults, chunkGraph: context.chunkGraph, moduleGraph: context.moduleGraph, runtimeTemplate: context.runtimeTemplate @@ -90089,6 +90132,7 @@ class JavascriptModulesPlugin { compilation.hooks.contentHash.tap("JavascriptModulesPlugin", chunk => { const { chunkGraph, + codeGenerationResults, moduleGraph, runtimeTemplate, outputOptions: { @@ -90106,6 +90150,7 @@ class JavascriptModulesPlugin { { hash: "0000", chunk, + codeGenerationResults, chunkGraph: compilation.chunkGraph, moduleGraph: compilation.moduleGraph, runtimeTemplate: compilation.runtimeTemplate @@ -90118,6 +90163,7 @@ class JavascriptModulesPlugin { } hooks.chunkHash.call(chunk, hash, { chunkGraph, + codeGenerationResults, moduleGraph, runtimeTemplate }); @@ -90144,7 +90190,10 @@ class JavascriptModulesPlugin { xor.updateHash(hash); } const digest = /** @type {string} */ (hash.digest(hashDigest)); - chunk.contentHash.javascript = digest.substr(0, hashDigestLength); + chunk.contentHash.javascript = nonNumericOnlyHash( + digest, + hashDigestLength + ); }); compilation.hooks.additionalTreeRuntimeRequirements.tap( "JavascriptModulesPlugin", @@ -90720,7 +90769,13 @@ class JavascriptModulesPlugin { * @returns {{ header: string[], beforeStartup: string[], startup: string[], afterStartup: string[], allowInlineStartup: boolean }} the generated source of the bootstrap code */ renderBootstrap(renderContext, hooks) { - const { chunkGraph, moduleGraph, chunk, runtimeTemplate } = renderContext; + const { + chunkGraph, + codeGenerationResults, + moduleGraph, + chunk, + runtimeTemplate + } = renderContext; const runtimeRequirements = chunkGraph.getTreeRuntimeRequirements(chunk); @@ -90844,8 +90899,18 @@ class JavascriptModulesPlugin { ); result.allowInlineStartup = false; } + + let data; + if (codeGenerationResults.has(entryModule, chunk.runtime)) { + const result = codeGenerationResults.get( + entryModule, + chunk.runtime + ); + data = result.data; + } if ( result.allowInlineStartup && + (!data || !data.get("topLevelDeclarations")) && (!entryModule.buildInfo || !entryModule.buildInfo.topLevelDeclarations) ) { @@ -95012,6 +95077,7 @@ const { getAllChunks } = __webpack_require__(91145); /** @typedef {import("../Chunk")} Chunk */ /** @typedef {import("../Compilation")} Compilation */ /** @typedef {import("../ChunkGraph")} ChunkGraph */ +/** @typedef {import("../ChunkGraph").EntryModuleWithChunkGroup} EntryModuleWithChunkGroup */ /** @typedef {import("../ChunkGroup")} ChunkGroup */ /** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */ /** @typedef {(string|number)[]} EntryItem */ @@ -95021,7 +95087,7 @@ const EXPORT_PREFIX = "var __webpack_exports__ = "; /** * @param {ChunkGraph} chunkGraph chunkGraph * @param {RuntimeTemplate} runtimeTemplate runtimeTemplate - * @param {import("../ChunkGraph").EntryModuleWithChunkGroup[]} entries entries + * @param {EntryModuleWithChunkGroup[]} entries entries * @param {Chunk} chunk chunk * @param {boolean} passive true: passive startup with on chunks loaded * @returns {string} runtime code @@ -95099,7 +95165,7 @@ exports.generateEntryStartup = ( /** * @param {Hash} hash the hash to update * @param {ChunkGraph} chunkGraph chunkGraph - * @param {import("../ChunkGraph").EntryModuleWithChunkGroup[]} entries entries + * @param {EntryModuleWithChunkGroup[]} entries entries * @param {Chunk} chunk chunk * @returns {void} */ @@ -96205,9 +96271,15 @@ class AssignLibraryPlugin extends AbstractLibraryPlugin { * @param {LibraryContext} libraryContext context * @returns {string | undefined} bailout reason */ - embedInRuntimeBailout(module, { chunk }, { options, compilation }) { + embedInRuntimeBailout( + module, + { chunk, codeGenerationResults }, + { options, compilation } + ) { + const { data } = codeGenerationResults.get(module, chunk.runtime); const topLevelDeclarations = - module.buildInfo && module.buildInfo.topLevelDeclarations; + (data && data.get("topLevelDeclarations")) || + (module.buildInfo && module.buildInfo.topLevelDeclarations); if (!topLevelDeclarations) return "it doesn't tell about top level declarations."; const fullNameResolved = this._getResolvedFullName( @@ -98277,6 +98349,7 @@ const builtins = [ "tty", "url", "util", + "util/types", "v8", "vm", "wasi", @@ -100695,10 +100768,6 @@ class ConcatenatedModule extends Module { const topLevelDeclarations = this.buildInfo.topLevelDeclarations; if (topLevelDeclarations !== undefined) { for (const decl of m.buildInfo.topLevelDeclarations) { - // reserved names will always be renamed - if (RESERVED_NAMES.has(decl)) continue; - // TODO actually this is incorrect since with renaming there could be more - // We should do the renaming during build topLevelDeclarations.add(decl); } } @@ -100986,6 +101055,8 @@ class ConcatenatedModule extends Module { // List of all used names to avoid conflicts const allUsedNames = new Set(RESERVED_NAMES); + // Updated Top level declarations are created by renaming + const topLevelDeclarations = new Set(); // List of additional names in scope for module references /** @type {Map, alreadyCheckedScopes: Set }>} */ @@ -101130,6 +101201,7 @@ class ConcatenatedModule extends Module { ); allUsedNames.add(newName); info.internalNames.set(name, newName); + topLevelDeclarations.add(newName); const source = info.source; const allIdentifiers = new Set( references.map(r => r.identifier).concat(variable.identifiers) @@ -101156,6 +101228,7 @@ class ConcatenatedModule extends Module { } else { allUsedNames.add(name); info.internalNames.set(name, name); + topLevelDeclarations.add(name); } } let namespaceObjectName; @@ -101173,6 +101246,7 @@ class ConcatenatedModule extends Module { allUsedNames.add(namespaceObjectName); } info.namespaceObjectName = namespaceObjectName; + topLevelDeclarations.add(namespaceObjectName); break; } case "external": { @@ -101184,6 +101258,7 @@ class ConcatenatedModule extends Module { ); allUsedNames.add(externalName); info.name = externalName; + topLevelDeclarations.add(externalName); break; } } @@ -101196,6 +101271,7 @@ class ConcatenatedModule extends Module { ); allUsedNames.add(externalNameInterop); info.interopNamespaceObjectName = externalNameInterop; + topLevelDeclarations.add(externalNameInterop); } if ( info.module.buildMeta.exportsType === "default" && @@ -101209,6 +101285,7 @@ class ConcatenatedModule extends Module { ); allUsedNames.add(externalNameInterop); info.interopNamespaceObject2Name = externalNameInterop; + topLevelDeclarations.add(externalNameInterop); } if ( info.module.buildMeta.exportsType === "dynamic" || @@ -101222,6 +101299,7 @@ class ConcatenatedModule extends Module { ); allUsedNames.add(externalNameInterop); info.interopDefaultAccessName = externalNameInterop; + topLevelDeclarations.add(externalNameInterop); } } @@ -101491,6 +101569,7 @@ ${defineGetters}` const data = new Map(); if (chunkInitFragments.length > 0) data.set("chunkInitFragments", chunkInitFragments); + data.set("topLevelDeclarations", topLevelDeclarations); /** @type {CodeGenerationResult} */ const resultEntry = { @@ -110956,7 +111035,7 @@ class HttpUriPlugin { /** * @param {string} url URL - * @param {FetchResult} cachedResult result from cache + * @param {FetchResult | RedirectFetchResult} cachedResult result from cache * @param {function((Error | null)=, FetchResult=): void} callback callback * @returns {void} */ @@ -111050,9 +111129,30 @@ class HttpUriPlugin { res.statusCode >= 301 && res.statusCode <= 308 ) { - return finishWith({ + const result = { location: new URL(location, url).href - }); + }; + if ( + !cachedResult || + !("location" in cachedResult) || + cachedResult.location !== result.location || + cachedResult.validUntil < validUntil || + cachedResult.storeLock !== storeLock || + cachedResult.storeCache !== storeCache || + cachedResult.etag !== etag + ) { + return finishWith(result); + } else { + logger.debug(`GET ${url} [${res.statusCode}] (unchanged)`); + return callback(null, { + ...result, + fresh: true, + storeLock, + storeCache, + validUntil, + etag + }); + } } const contentType = res.headers["content-type"] || ""; const bufferArr = []; @@ -112598,6 +112698,8 @@ Section -> Buffer // "wpc" + 1 in little-endian const VERSION = 0x01637077; +const WRITE_LIMIT_TOTAL = 0x7fff0000; +const WRITE_LIMIT_CHUNK = 511 * 1024 * 1024; /** * @param {Buffer[]} buffers buffers @@ -112645,7 +112747,7 @@ const readUInt64LE = Buffer.prototype.readBigUInt64LE * @param {FileMiddleware} middleware this * @param {BufferSerializableType[] | Promise} data data to be serialized * @param {string | boolean} name file base name - * @param {function(string | false, Buffer[]): Promise} writeFile writes a file + * @param {function(string | false, Buffer[], number): Promise} writeFile writes a file * @param {string | Hash} hashFunction hash function to use * @returns {Promise} resulting file pointer and promise */ @@ -112770,9 +112872,9 @@ const serialize = async ( if (name === true) { name = hashForName(buf, hashFunction); } - backgroundJobs.push(writeFile(name, buf)); let size = 0; for (const b of buf) size += b.length; + backgroundJobs.push(writeFile(name, buf, size)); return { size, name, @@ -112980,7 +113082,7 @@ class FileMiddleware extends SerializerMiddleware { // It's important that we don't touch existing files during serialization // because serialize may read existing files (when deserializing) const allWrittenFiles = new Set(); - const writeFile = async (name, content) => { + const writeFile = async (name, content, size) => { const file = name ? join(this.fs, filename, `../${name}${extension}`) : filename; @@ -112999,10 +113101,7 @@ class FileMiddleware extends SerializerMiddleware { [zConstants.BROTLI_PARAM_MODE]: zConstants.BROTLI_MODE_TEXT, [zConstants.BROTLI_PARAM_QUALITY]: 2, [zConstants.BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING]: true, - [zConstants.BROTLI_PARAM_SIZE_HINT]: content.reduce( - (size, b) => size + b.length, - 0 - ) + [zConstants.BROTLI_PARAM_SIZE_HINT]: size } }); } @@ -113014,8 +113113,44 @@ class FileMiddleware extends SerializerMiddleware { stream.on("error", err => reject(err)); stream.on("finish", () => resolve()); } - for (const b of content) stream.write(b); - stream.end(); + // split into chunks for WRITE_LIMIT_CHUNK size + const chunks = []; + for (const b of content) { + if (b.length < WRITE_LIMIT_CHUNK) { + chunks.push(b); + } else { + for (let i = 0; i < b.length; i += WRITE_LIMIT_CHUNK) { + chunks.push(b.slice(i, i + WRITE_LIMIT_CHUNK)); + } + } + } + + const len = chunks.length; + let i = 0; + const batchWrite = err => { + // will be handled in "on" error handler + if (err) return; + + if (i === len) { + stream.end(); + return; + } + + // queue up a batch of chunks up to the write limit + // end is exclusive + let end = i; + let sum = chunks[end++].length; + while (end < len) { + sum += chunks[end].length; + if (sum > WRITE_LIMIT_TOTAL) break; + end++; + } + while (i < end - 1) { + stream.write(chunks[i++]); + } + stream.write(chunks[i++], batchWrite); + }; + batchWrite(); }); if (name) allWrittenFiles.add(file); }; @@ -124682,7 +124817,7 @@ const toSimpleString = str => { /** * @param {Record} map value map - * @returns {true|false|function(string): string} true/false, when unconditionally true/false, or a template function to determine the value at runtime + * @returns {boolean|(function(string): string)} true/false, when unconditionally true/false, or a template function to determine the value at runtime */ const compileBooleanMatcher = map => { const positiveItems = Object.keys(map).filter(i => map[i]); @@ -125863,7 +125998,7 @@ module.exports = ({ maxSize, minSize, items, getSize, getKey }) => { // return the results return result.map(group => { - /** @type {GroupedItems} */ + /** @type {GroupedItems} */ return { key: group.key, items: group.nodes.map(node => node.item), @@ -126873,7 +127008,49 @@ const requestToAbsolute = (context, relativePath) => { return relativePath; }; -const makeCacheable = fn => { +const makeCacheable = realFn => { + /** @type {WeakMap>} */ + const cache = new WeakMap(); + + const getCache = associatedObjectForCache => { + const entry = cache.get(associatedObjectForCache); + if (entry !== undefined) return entry; + /** @type {Map} */ + const map = new Map(); + cache.set(associatedObjectForCache, map); + return map; + }; + + /** + * @param {string} str the path with query and fragment + * @param {Object=} associatedObjectForCache an object to which the cache will be attached + * @returns {ParsedResource} parsed parts + */ + const fn = (str, associatedObjectForCache) => { + if (!associatedObjectForCache) return realFn(str); + const cache = getCache(associatedObjectForCache); + const entry = cache.get(str); + if (entry !== undefined) return entry; + const result = realFn(str); + cache.set(str, result); + return result; + }; + + fn.bindCache = associatedObjectForCache => { + const cache = getCache(associatedObjectForCache); + return str => { + const entry = cache.get(str); + if (entry !== undefined) return entry; + const result = realFn(str); + cache.set(str, result); + return result; + }; + }; + + return fn; +}; + +const makeCacheableWithContext = fn => { /** @type {WeakMap>>} */ const cache = new WeakMap(); @@ -127007,7 +127184,7 @@ const _makePathsRelative = (context, identifier) => { .join(""); }; -exports.makePathsRelative = makeCacheable(_makePathsRelative); +exports.makePathsRelative = makeCacheableWithContext(_makePathsRelative); /** * @@ -127022,7 +127199,7 @@ const _makePathsAbsolute = (context, identifier) => { .join(""); }; -exports.makePathsAbsolute = makeCacheable(_makePathsAbsolute); +exports.makePathsAbsolute = makeCacheableWithContext(_makePathsAbsolute); /** * @param {string} context absolute context path @@ -127036,7 +127213,7 @@ const _contextify = (context, request) => { .join("!"); }; -const contextify = makeCacheable(_contextify); +const contextify = makeCacheableWithContext(_contextify); exports.contextify = contextify; /** @@ -127051,13 +127228,15 @@ const _absolutify = (context, request) => { .join("!"); }; -const absolutify = makeCacheable(_absolutify); +const absolutify = makeCacheableWithContext(_absolutify); exports.absolutify = absolutify; const PATH_QUERY_FRAGMENT_REGEXP = /^((?:\0.|[^?#\0])*)(\?(?:\0.|[^#\0])*)?(#.*)?$/; +const PATH_QUERY_REGEXP = /^((?:\0.|[^?\0])*)(\?.*)?$/; /** @typedef {{ resource: string, path: string, query: string, fragment: string }} ParsedResource */ +/** @typedef {{ resource: string, path: string, query: string }} ParsedResourceWithoutFragment */ /** * @param {string} str the path with query and fragment @@ -127072,47 +127251,24 @@ const _parseResource = str => { fragment: match[3] || "" }; }; -exports.parseResource = (realFn => { - /** @type {WeakMap>} */ - const cache = new WeakMap(); - - const getCache = associatedObjectForCache => { - const entry = cache.get(associatedObjectForCache); - if (entry !== undefined) return entry; - /** @type {Map} */ - const map = new Map(); - cache.set(associatedObjectForCache, map); - return map; - }; - - /** - * @param {string} str the path with query and fragment - * @param {Object=} associatedObjectForCache an object to which the cache will be attached - * @returns {ParsedResource} parsed parts - */ - const fn = (str, associatedObjectForCache) => { - if (!associatedObjectForCache) return realFn(str); - const cache = getCache(associatedObjectForCache); - const entry = cache.get(str); - if (entry !== undefined) return entry; - const result = realFn(str); - cache.set(str, result); - return result; - }; +exports.parseResource = makeCacheable(_parseResource); - fn.bindCache = associatedObjectForCache => { - const cache = getCache(associatedObjectForCache); - return str => { - const entry = cache.get(str); - if (entry !== undefined) return entry; - const result = realFn(str); - cache.set(str, result); - return result; - }; +/** + * Parse resource, skips fragment part + * @param {string} str the path with query and fragment + * @returns {ParsedResourceWithoutFragment} parsed parts + */ +const _parseResourceWithoutFragment = str => { + const match = PATH_QUERY_REGEXP.exec(str); + return { + resource: str, + path: match[1].replace(/\0(.)/g, "$1"), + query: match[2] ? match[2].replace(/\0(.)/g, "$1") : "" }; - - return fn; -})(_parseResource); +}; +exports.parseResourceWithoutFragment = makeCacheable( + _parseResourceWithoutFragment +); /** * @param {string} filename the filename which should be undone @@ -127443,6 +127599,36 @@ const memoize = fn => { module.exports = memoize; +/***/ }), + +/***/ 55668: +/***/ (function(module) { + +"use strict"; +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Ivan Kopeykin @vankop +*/ + + + +const A_CODE = "a".charCodeAt(0); + +/** + * @param {string} hash hash + * @param {number} hashLength hash length + * @returns {string} returns hash that has at least one non numeric char + */ +module.exports = (hash, hashLength) => { + if (hashLength < 1) return ""; + const slice = hash.slice(0, hashLength); + if (slice.match(/[^\d]/)) return slice; + return `${String.fromCharCode( + A_CODE + (parseInt(hash[0], 10) % 6) + )}${slice.slice(1)}`; +}; + + /***/ }), /***/ 70002: @@ -128725,7 +128911,9 @@ exports.versionLt = versionLt; */ exports.parseRange = str => { const splitAndConvert = str => { - return str.split(".").map(item => (`${+item}` === item ? +item : item)); + return str + .split(".") + .map(item => (item !== "NaN" && `${+item}` === item ? +item : item)); }; // see https://docs.npmjs.com/misc/semver#range-grammar for grammar const parsePartial = str => { @@ -128767,13 +128955,15 @@ exports.parseRange = str => { return [-range[0] - 1, ...range.slice(1)]; }; const parseSimple = str => { - // simple ::= primitive | partial | tilde | caret - // primitive ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial - // tilde ::= '~' partial - // caret ::= '^' partial + // simple ::= primitive | partial | tilde | caret + // primitive ::= ( '<' | '>' | '>=' | '<=' | '=' | '!' ) ( ' ' ) * partial + // tilde ::= '~' ( ' ' ) * partial + // caret ::= '^' ( ' ' ) * partial const match = /^(\^|~|<=|<|>=|>|=|v|!)/.exec(str); const start = match ? match[0] : ""; - const remainder = parsePartial(str.slice(start.length)); + const remainder = parsePartial( + start.length ? str.slice(start.length).trim() : str.trim() + ); switch (start) { case "^": if (remainder.length > 1 && remainder[1] === 0) { @@ -128827,11 +129017,14 @@ exports.parseRange = str => { return [, ...arr, ...items.slice(1).map(() => fn)]; }; const parseRange = str => { - // range ::= hyphen | simple ( ' ' simple ) * | '' - // hyphen ::= partial ' - ' partial - const items = str.split(" - "); + // range ::= hyphen | simple ( ' ' ( ' ' ) * simple ) * | '' + // hyphen ::= partial ( ' ' ) * ' - ' ( ' ' ) * partial + const items = str.split(/\s+-\s+/); if (items.length === 1) { - const items = str.trim().split(/\s+/g).map(parseSimple); + const items = str + .trim() + .split(/(?<=[-0-9A-Za-z])\s+/g) + .map(parseSimple); return combine(items, 2); } const a = parsePartial(items[0]); @@ -135437,7 +135630,7 @@ module.exports = class MainFieldPlugin { /***/ }), -/***/ 48506: +/***/ 79024: /***/ (function(module, __unused_webpack_exports, __webpack_require__) { "use strict"; @@ -135454,7 +135647,7 @@ const getPaths = __webpack_require__(82918); /** @typedef {import("./Resolver")} Resolver */ /** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */ -module.exports = class ModulesInHierachicDirectoriesPlugin { +module.exports = class ModulesInHierarchicalDirectoriesPlugin { /** * @param {string | ResolveStepHook} source source * @param {string | Array} directories directories @@ -135475,7 +135668,7 @@ module.exports = class ModulesInHierachicDirectoriesPlugin { resolver .getHook(this.source) .tapAsync( - "ModulesInHierachicDirectoriesPlugin", + "ModulesInHierarchicalDirectoriesPlugin", (request, resolveContext, callback) => { const fs = resolver.fileSystem; const addrs = getPaths(request.path) @@ -135924,6 +136117,7 @@ const { * @property {WriteOnlySet=} missingDependencies dependencies that was not found on file system * @property {Set=} stack set of hooks' calls. For instance, `resolve → parsedResolve → describedResolve`, * @property {(function(string): void)=} log log function + * @property {(function (ResolveRequest): void)=} yield yield result, if provided plugins can return several results */ /** @typedef {AsyncSeriesBailHook<[ResolveRequest, ResolveContext], ResolveRequest | null>} ResolveStepHook */ @@ -136092,6 +136286,16 @@ class Resolver { request: request }; + let yield_; + let yieldCalled = false; + if (typeof resolveContext.yield === "function") { + const old = resolveContext.yield; + yield_ = obj => { + yieldCalled = true; + old(obj); + }; + } + const message = `resolve '${request}' in '${path}'`; const finishResolved = result => { @@ -136129,6 +136333,7 @@ class Resolver { parentLog(msg); log.push(msg); }, + yield: yield_, fileDependencies: resolveContext.fileDependencies, contextDependencies: resolveContext.contextDependencies, missingDependencies: resolveContext.missingDependencies, @@ -136138,6 +136343,7 @@ class Resolver { if (err) return callback(err); if (result) return finishResolved(result); + if (yieldCalled) return callback(null); return finishWithoutResolve(log); } @@ -136151,6 +136357,7 @@ class Resolver { message, { log: undefined, + yield: yield_, fileDependencies: resolveContext.fileDependencies, contextDependencies: resolveContext.contextDependencies, missingDependencies: resolveContext.missingDependencies, @@ -136160,6 +136367,7 @@ class Resolver { if (err) return callback(err); if (result) return finishResolved(result); + if (yieldCalled) return callback(null); // log is missing for the error details // so we redo the resolving for the log info @@ -136174,11 +136382,15 @@ class Resolver { message, { log: msg => log.push(msg), + yield: yield_, stack: resolveContext.stack }, - (err, result) => { + err => { if (err) return callback(err); + // In a case that there is a race condition and yield will be called + if (yieldCalled) return callback(null); + return finishWithoutResolve(log); } ); @@ -136217,6 +136429,7 @@ class Resolver { const innerContext = createInnerContext( { log: resolveContext.log, + yield: resolveContext.yield, fileDependencies: resolveContext.fileDependencies, contextDependencies: resolveContext.contextDependencies, missingDependencies: resolveContext.missingDependencies, @@ -136326,7 +136539,7 @@ const ImportsFieldPlugin = __webpack_require__(7317); const JoinRequestPartPlugin = __webpack_require__(35949); const JoinRequestPlugin = __webpack_require__(5190); const MainFieldPlugin = __webpack_require__(47450); -const ModulesInHierachicDirectoriesPlugin = __webpack_require__(48506); +const ModulesInHierarchicalDirectoriesPlugin = __webpack_require__(79024); const ModulesInRootPlugin = __webpack_require__(88138); const NextPlugin = __webpack_require__(40777); const ParsePlugin = __webpack_require__(97849); @@ -136376,7 +136589,7 @@ const UseFilePlugin = __webpack_require__(96972); * @property {boolean=} fullySpecified The request is already fully specified and no extensions or directories are resolved for it * @property {boolean=} resolveToContext Resolve to a context instead of a file * @property {(string|RegExp)[]=} restrictions A list of resolve restrictions - * @property {boolean=} useSyncFileSystemCalls Use only the sync constiants of the file system calls + * @property {boolean=} useSyncFileSystemCalls Use only the sync constraints of the file system calls * @property {boolean=} preferRelative Prefer to resolve module requests as relative requests before falling back to modules * @property {boolean=} preferAbsolute Prefer to resolve server-relative urls as absolute paths before falling back to resolve in roots */ @@ -136583,7 +136796,7 @@ exports.createResolver = function (options) { resolver.ensureHook("resolve"); resolver.ensureHook("internalResolve"); - resolver.ensureHook("newInteralResolve"); + resolver.ensureHook("newInternalResolve"); resolver.ensureHook("parsedResolve"); resolver.ensureHook("describedResolve"); resolver.ensureHook("internal"); @@ -136605,6 +136818,11 @@ exports.createResolver = function (options) { resolver.ensureHook("existingFile"); resolver.ensureHook("resolved"); + // TODO remove in next major + // cspell:word Interal + // Backward-compat + resolver.hooks.newInteralResolve = resolver.hooks.newInternalResolve; + // resolve for (const { source, resolveOptions } of [ { source: "resolve", resolveOptions: { fullySpecified } }, @@ -136709,7 +136927,7 @@ exports.createResolver = function (options) { if (Array.isArray(item)) { if (item.includes("node_modules") && pnpApi) { plugins.push( - new ModulesInHierachicDirectoriesPlugin( + new ModulesInHierarchicalDirectoriesPlugin( "raw-module", item.filter(i => i !== "node_modules"), "module" @@ -136720,7 +136938,11 @@ exports.createResolver = function (options) { ); } else { plugins.push( - new ModulesInHierachicDirectoriesPlugin("raw-module", item, "module") + new ModulesInHierarchicalDirectoriesPlugin( + "raw-module", + item, + "module" + ) ); } } else { @@ -137065,7 +137287,12 @@ module.exports = class ResultPlugin { resolverContext.log("reporting result " + obj.path); resolver.hooks.result.callAsync(obj, resolverContext, err => { if (err) return callback(err); - callback(null, obj); + if (typeof resolverContext.yield === "function") { + resolverContext.yield(obj); + callback(null, null); + } else { + callback(null, obj); + } }); } ); @@ -137273,7 +137500,7 @@ module.exports = class SymlinkPlugin { .tapAsync("SymlinkPlugin", (request, resolveContext, callback) => { if (request.ignoreSymlinks) return callback(); const pathsResult = getPaths(request.path); - const pathSeqments = pathsResult.seqments; + const pathSegments = pathsResult.segments; const paths = pathsResult.paths; let containsSymlink = false; @@ -137286,7 +137513,7 @@ module.exports = class SymlinkPlugin { resolveContext.fileDependencies.add(path); fs.readlink(path, (err, result) => { if (!err && result) { - pathSeqments[idx] = result; + pathSegments[idx] = result; containsSymlink = true; // Shortcut when absolute symlink found const resultType = getType(result.toString()); @@ -137302,11 +137529,11 @@ module.exports = class SymlinkPlugin { }, (err, idx) => { if (!containsSymlink) return callback(); - const resultSeqments = + const resultSegments = typeof idx === "number" - ? pathSeqments.slice(0, idx + 1) - : pathSeqments.slice(); - const result = resultSeqments.reduceRight((a, b) => { + ? pathSegments.slice(0, idx + 1) + : pathSegments.slice(); + const result = resultSegments.reduceRight((a, b) => { return resolver.join(a, b); }); const obj = { @@ -137646,6 +137873,7 @@ module.exports = function createInnerContext( } const childContext = { log: innerLog, + yield: options.yield, fileDependencies: options.fileDependencies, contextDependencies: options.contextDependencies, missingDependencies: options.missingDependencies, @@ -137737,23 +137965,24 @@ module.exports = function getInnerRequest(resolver, request) { module.exports = function getPaths(path) { + if (path === "/") return { paths: ["/"], segments: [""] }; const parts = path.split(/(.*?[\\/]+)/); const paths = [path]; - const seqments = [parts[parts.length - 1]]; + const segments = [parts[parts.length - 1]]; let part = parts[parts.length - 1]; path = path.substr(0, path.length - part.length - 1); for (let i = parts.length - 2; i > 2; i -= 2) { paths.push(path); part = parts[i]; path = path.substr(0, path.length - part.length) || "/"; - seqments.push(part.substr(0, part.length - 1)); + segments.push(part.substr(0, part.length - 1)); } part = parts[1]; - seqments.push(part); + segments.push(part); paths.push(part); return { paths: paths, - seqments: seqments + segments: segments }; }; @@ -141590,7 +141819,7 @@ module.exports = JSON.parse('{"application/1d-interleaved-parityfec":{"source":" /***/ (function(module) { "use strict"; -module.exports = {"i8":"5.68.0"}; +module.exports = {"i8":"5.69.1"}; /***/ }), diff --git a/packages/next/package.json b/packages/next/package.json index f36aff156375..316aee06f97a 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "12.0.11-canary.18", + "version": "12.1.0", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -69,7 +69,7 @@ ] }, "dependencies": { - "@next/env": "12.0.11-canary.18", + "@next/env": "12.1.0", "caniuse-lite": "^1.0.30001283", "postcss": "8.4.5", "styled-jsx": "5.0.0", @@ -117,11 +117,11 @@ "@hapi/accept": "5.0.2", "@napi-rs/cli": "1.2.1", "@napi-rs/triples": "1.0.3", - "@next/polyfill-module": "12.0.11-canary.18", - "@next/polyfill-nomodule": "12.0.11-canary.18", - "@next/react-dev-overlay": "12.0.11-canary.18", - "@next/react-refresh-utils": "12.0.11-canary.18", - "@next/swc": "12.0.11-canary.18", + "@next/polyfill-module": "12.1.0", + "@next/polyfill-nomodule": "12.1.0", + "@next/react-dev-overlay": "12.1.0", + "@next/react-refresh-utils": "12.1.0", + "@next/swc": "12.1.0", "@peculiar/webcrypto": "1.1.7", "@taskr/clear": "1.1.0", "@taskr/esnext": "1.1.0", @@ -267,7 +267,7 @@ "webpack-sources1": "npm:webpack-sources@1.4.3", "webpack-sources3": "npm:webpack-sources@3.2.3", "webpack4": "npm:webpack@4.44.1", - "webpack5": "npm:webpack@5.68.0", + "webpack5": "npm:webpack@5.69.1", "ws": "8.2.3" }, "resolutions": { diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index 91b0b526fea9..6af1f81fc86a 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -1,11 +1,9 @@ import React, { Component, ReactElement, ReactNode, useContext } from 'react' import { OPTIMIZED_FONT_PROVIDERS } from '../shared/lib/constants' -import { +import type { DocumentContext, DocumentInitialProps, DocumentProps, - HtmlContext, - HtmlProps, } from '../shared/lib/utils' import { BuildManifest, getPageFiles } from '../server/get-page-files' import { cleanAmpPath } from '../server/utils' @@ -13,6 +11,9 @@ import { htmlEscapeJsonString } from '../server/htmlescape' import Script, { ScriptProps } from '../client/script' import isError from '../lib/is-error' +import { HtmlContext } from '../shared/lib/html-context' +import type { HtmlProps } from '../shared/lib/html-context' + export { DocumentContext, DocumentInitialProps, DocumentProps } export type OriginProps = { diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts index 6b4a6ace6885..77160619770f 100644 --- a/packages/next/server/base-server.ts +++ b/packages/next/server/base-server.ts @@ -1550,6 +1550,9 @@ export default abstract class Server { res.body('{"notFound":true}').send() return null } else { + if (this.renderOpts.dev) { + query.__nextNotFoundSrcPage = pathname + } await this.render404( req, res, diff --git a/packages/next/server/config.ts b/packages/next/server/config.ts index 556992760980..f51deaa8254a 100644 --- a/packages/next/server/config.ts +++ b/packages/next/server/config.ts @@ -351,6 +351,28 @@ function assignDefaults(userConfig: { [key: string]: any }) { ) } } + + if ( + typeof images.dangerouslyAllowSVG !== 'undefined' && + typeof images.dangerouslyAllowSVG !== 'boolean' + ) { + throw new Error( + `Specified images.dangerouslyAllowSVG should be a boolean + ', ' + )}), received (${images.dangerouslyAllowSVG}).\nSee more info here: https://nextjs.org/docs/messages/invalid-images-config` + ) + } + + if ( + typeof images.contentSecurityPolicy !== 'undefined' && + typeof images.contentSecurityPolicy !== 'string' + ) { + throw new Error( + `Specified images.contentSecurityPolicy should be a string + ', ' + )}), received (${images.contentSecurityPolicy}).\nSee more info here: https://nextjs.org/docs/messages/invalid-images-config` + ) + } } if (result.webpack5 === false) { diff --git a/packages/next/server/dev/hot-reloader.ts b/packages/next/server/dev/hot-reloader.ts index a5b9391be164..92847d189629 100644 --- a/packages/next/server/dev/hot-reloader.ts +++ b/packages/next/server/dev/hot-reloader.ts @@ -3,7 +3,7 @@ import { IncomingMessage, ServerResponse } from 'http' import { WebpackHotMiddleware } from './hot-middleware' import { join, relative, isAbsolute } from 'path' import { UrlObject } from 'url' -import { webpack } from 'next/dist/compiled/webpack/webpack' +import { webpack, StringXor } from 'next/dist/compiled/webpack/webpack' import type { webpack5 } from 'next/dist/compiled/webpack/webpack' import { createEntrypoints, @@ -594,21 +594,56 @@ export default class HotReloader { const trackPageChanges = (pageHashMap: Map, changedItems: Set) => (stats: webpack5.Compilation) => { - stats.entrypoints.forEach((entry, key) => { - if (key.startsWith('pages/')) { - // TODO this doesn't handle on demand loaded chunks - entry.chunks.forEach((chunk: any) => { - if (chunk.id === key) { - const prevHash = pageHashMap.get(key) - - if (prevHash && prevHash !== chunk.hash) { - changedItems.add(key) + try { + stats.entrypoints.forEach((entry, key) => { + if (key.startsWith('pages/')) { + // TODO this doesn't handle on demand loaded chunks + entry.chunks.forEach((chunk) => { + if (chunk.id === key) { + const modsIterable: any = + stats.chunkGraph.getChunkModulesIterable(chunk) + + let chunksHash = new StringXor() + + modsIterable.forEach((mod: any) => { + if ( + mod.resource && + mod.resource.replace(/\\/g, '/').includes(key) + ) { + // use original source to calculate hash since mod.hash + // includes the source map in development which changes + // every time for both server and client so we calculate + // the hash without the source map for the page module + const hash = require('crypto') + .createHash('sha256') + .update(mod.originalSource().buffer()) + .digest() + .toString('hex') + + chunksHash.add(hash) + } else { + // for non-pages we can use the module hash directly + const hash = stats.chunkGraph.getModuleHash( + mod, + chunk.runtime + ) + chunksHash.add(hash) + } + }) + const prevHash = pageHashMap.get(key) + const curHash = chunksHash.toString() + + if (prevHash && prevHash !== curHash) { + changedItems.add(key) + } + pageHashMap.set(key, curHash) } - pageHashMap.set(key, chunk.hash) - } - }) - } - }) + }) + } + }) + } catch (err) { + console.error(err) + } } multiCompiler.compilers[0].hooks.emit.tap( diff --git a/packages/next/server/image-config.ts b/packages/next/server/image-config.ts index 41dd013b9f2d..001745447c1a 100644 --- a/packages/next/server/image-config.ts +++ b/packages/next/server/image-config.ts @@ -29,16 +29,22 @@ export type ImageConfigComplete = { path: string /** @see [Image domains configuration](https://nextjs.org/docs/basic-features/image-optimization#domains) */ - domains?: string[] + domains: string[] /** @see [Cache behavior](https://nextjs.org/docs/api-reference/next/image#caching-behavior) */ - disableStaticImages?: boolean + disableStaticImages: boolean /** @see [Cache behavior](https://nextjs.org/docs/api-reference/next/image#caching-behavior) */ - minimumCacheTTL?: number + minimumCacheTTL: number /** @see [Acceptable formats](https://nextjs.org/docs/api-reference/next/image#acceptable-formats) */ - formats?: ImageFormat[] + formats: ImageFormat[] + + /** @see [Dangerously Allow SVG](https://nextjs.org/docs/api-reference/next/image#dangerously-allow-svg) */ + dangerouslyAllowSVG: boolean + + /** @see [Dangerously Allow SVG](https://nextjs.org/docs/api-reference/next/image#dangerously-allow-svg) */ + contentSecurityPolicy: string } export type ImageConfig = Partial @@ -52,4 +58,6 @@ export const imageConfigDefault: ImageConfigComplete = { disableStaticImages: false, minimumCacheTTL: 60, formats: ['image/webp'], + dangerouslyAllowSVG: false, + contentSecurityPolicy: `script-src 'none'; frame-src 'none'; sandbox;`, } diff --git a/packages/next/server/image-optimizer.ts b/packages/next/server/image-optimizer.ts index 56feff6010b3..35c10f24ed43 100644 --- a/packages/next/server/image-optimizer.ts +++ b/packages/next/server/image-optimizer.ts @@ -380,6 +380,16 @@ export async function imageOptimizer( } } + if (upstreamType === SVG && !nextConfig.images.dangerouslyAllowSVG) { + console.error( + `The requested resource "${href}" has type "${upstreamType}" but dangerouslyAllowSVG is disabled` + ) + throw new ImageError( + 400, + '"url" parameter is valid but image type is not allowed' + ) + } + if (upstreamType) { const vector = VECTOR_TYPES.includes(upstreamType) const animate = @@ -576,14 +586,15 @@ function getFileNameWithExtension( return `${fileName}.${extension}` } -export function setResponseHeaders( +function setResponseHeaders( req: IncomingMessage, res: ServerResponse, url: string, etag: string, contentType: string | null, isStatic: boolean, - xCache: XCacheHeader + xCache: XCacheHeader, + contentSecurityPolicy: string ) { res.setHeader('Vary', 'Accept') res.setHeader( @@ -608,7 +619,9 @@ export function setResponseHeaders( ) } - res.setHeader('Content-Security-Policy', `script-src 'none'; sandbox;`) + if (contentSecurityPolicy) { + res.setHeader('Content-Security-Policy', contentSecurityPolicy) + } res.setHeader('X-Nextjs-Cache', xCache) return { finished: false } @@ -621,7 +634,8 @@ export function sendResponse( extension: string, buffer: Buffer, isStatic: boolean, - xCache: XCacheHeader + xCache: XCacheHeader, + contentSecurityPolicy: string ) { const contentType = getContentType(extension) const etag = getHash([buffer]) @@ -632,7 +646,8 @@ export function sendResponse( etag, contentType, isStatic, - xCache + xCache, + contentSecurityPolicy ) if (!result.finished) { res.end(buffer) diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index c259936ebc2a..4c7779481822 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -255,7 +255,8 @@ export default class NextNodeServer extends BaseServer { cacheEntry.value.extension, cacheEntry.value.buffer, paramsResult.isStatic, - cacheEntry.isMiss ? 'MISS' : cacheEntry.isStale ? 'STALE' : 'HIT' + cacheEntry.isMiss ? 'MISS' : cacheEntry.isStale ? 'STALE' : 'HIT', + imagesConfig.contentSecurityPolicy ) } catch (err) { if (err instanceof ImageError) { diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index 780041e806cc..965f9b60b6dd 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -37,8 +37,6 @@ import { DocumentInitialProps, DocumentProps, DocumentContext, - HtmlContext, - HtmlProps, getDisplayName, isResSent, loadGetInitialProps, @@ -46,6 +44,10 @@ import { RenderPage, RenderPageResult, } from '../shared/lib/utils' + +import { HtmlContext } from '../shared/lib/html-context' +import type { HtmlProps } from '../shared/lib/html-context' + import type { NextApiRequestCookies, __ApiPreviewProps } from './api-utils' import { denormalizePagePath } from './denormalize-page-path' import type { FontManifest } from './font-utils' @@ -532,9 +534,11 @@ export async function renderToHTML( const headTags = (...args: any) => callMiddleware('headTags', args) const isFallback = !!query.__nextFallback + const notFoundSrcPage = query.__nextNotFoundSrcPage delete query.__nextFallback delete query.__nextLocale delete query.__nextDefaultLocale + delete query.__nextIsNotFound const isSSG = !!getStaticProps const isBuildTimeSSG = isSSG && renderOpts.nextExport @@ -1450,6 +1454,7 @@ export async function renderToHTML( defaultLocale, domainLocales, isPreview: isPreview === true ? true : undefined, + notFoundSrcPage: notFoundSrcPage && dev ? notFoundSrcPage : undefined, }, buildManifest: filteredBuildManifest, docComponentsRendered, @@ -1878,19 +1883,28 @@ function createInlineDataStream( if (!dataStreamFinished) { const dataStreamReader = dataStream.getReader() - dataStreamFinished = (async () => { - try { - while (true) { - const { done, value } = await dataStreamReader.read() - if (done) { - return + + // We are buffering here for the inlined data stream because the + // "shell" stream might be chunkenized again by the underlying stream + // implementation, e.g. with a specific high-water mark. To ensure it's + // the safe timing to pipe the data stream, this extra tick is + // necessary. + dataStreamFinished = new Promise((res) => + setTimeout(async () => { + try { + while (true) { + const { done, value } = await dataStreamReader.read() + if (done) { + return res() + } + controller.enqueue(value) } - controller.enqueue(value) + } catch (err) { + controller.error(err) } - } catch (err) { - controller.error(err) - } - })() + res() + }, 0) + ) } }, flush() { diff --git a/packages/next/server/request-meta.ts b/packages/next/server/request-meta.ts index e2fed315088d..f906e5f5f1f0 100644 --- a/packages/next/server/request-meta.ts +++ b/packages/next/server/request-meta.ts @@ -53,6 +53,7 @@ export function addRequestMeta( } type NextQueryMetadata = { + __nextNotFoundSrcPage?: string __nextDefaultLocale?: string __nextFallback?: 'true' __nextLocale?: string diff --git a/packages/next/server/web/utils.ts b/packages/next/server/web/utils.ts index 06e257457f09..a795ec35e144 100644 --- a/packages/next/server/web/utils.ts +++ b/packages/next/server/web/utils.ts @@ -149,18 +149,16 @@ export function splitCookiesString(cookiesString: string) { } /** - * We will be soon deprecating the usage of relative URLs in Middleware introducing - * URL validation. This helper puts the future code in place and prints a warning - * for cases where it will break. Meanwhile we preserve the previous behavior. + * Validate the correctness of a user-provided URL. */ export function validateURL(url: string | URL): string { try { return String(new URL(String(url))) } catch (error: any) { - console.log( - `warn -`, - 'using relative URLs for Middleware will be deprecated soon - https://nextjs.org/docs/messages/middleware-relative-urls' + throw new Error( + `URLs is malformed. Please use only absolute URLs - https://nextjs.org/docs/messages/middleware-relative-urls`, + // @ts-expect-error This will work for people who enable the error causes polyfill + { cause: error } ) - return String(url) } } diff --git a/packages/next/shared/lib/html-context.ts b/packages/next/shared/lib/html-context.ts new file mode 100644 index 000000000000..25baecbd7bd0 --- /dev/null +++ b/packages/next/shared/lib/html-context.ts @@ -0,0 +1,42 @@ +import type { BuildManifest } from '../../server/get-page-files' +import type { NEXT_DATA, MaybeDeferContentHook } from './utils' + +import { createContext } from 'react' + +export type HtmlProps = { + __NEXT_DATA__: NEXT_DATA + dangerousAsPath: string + docComponentsRendered: { + Html?: boolean + Main?: boolean + Head?: boolean + NextScript?: boolean + } + buildManifest: BuildManifest + ampPath: string + inAmpMode: boolean + hybridAmp: boolean + isDevelopment: boolean + dynamicImports: string[] + assetPrefix?: string + canonicalBase: string + headTags: any[] + unstable_runtimeJS?: false + unstable_JsPreload?: false + devOnlyCacheBusterQueryString: string + scriptLoader: { afterInteractive?: string[]; beforeInteractive?: any[] } + locale?: string + disableOptimizedLoading?: boolean + styles?: React.ReactElement[] | React.ReactFragment + head?: Array + useMaybeDeferContent: MaybeDeferContentHook + crossOrigin?: string + optimizeCss?: boolean + optimizeFonts?: boolean + runtime?: 'edge' | 'nodejs' +} + +export const HtmlContext = createContext(null as any) +if (process.env.NODE_ENV !== 'production') { + HtmlContext.displayName = 'HtmlContext' +} diff --git a/packages/next/shared/lib/router/router.ts b/packages/next/shared/lib/router/router.ts index d6ca2f98fba8..d3e172431880 100644 --- a/packages/next/shared/lib/router/router.ts +++ b/packages/next/shared/lib/router/router.ts @@ -22,7 +22,6 @@ import { normalizeLocalePath } from '../i18n/normalize-locale-path' import mitt from '../mitt' import { AppContextType, - formatWithValidation, getLocationOrigin, getURL, loadGetInitialProps, @@ -38,6 +37,7 @@ import resolveRewrites from './utils/resolve-rewrites' import { getRouteMatcher } from './utils/route-matcher' import { getRouteRegex } from './utils/route-regex' import { getMiddlewareRegex } from './utils/get-middleware-regex' +import { formatWithValidation } from './utils/format-url' declare global { interface Window { diff --git a/packages/next/shared/lib/router/utils/format-url.ts b/packages/next/shared/lib/router/utils/format-url.ts index bef24b4e3e7d..205ab30a7142 100644 --- a/packages/next/shared/lib/router/utils/format-url.ts +++ b/packages/next/shared/lib/router/utils/format-url.ts @@ -20,8 +20,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -import { UrlObject } from 'url' -import { ParsedUrlQuery } from 'querystring' +import type { UrlObject } from 'url' +import type { ParsedUrlQuery } from 'querystring' import * as querystring from './querystring' const slashedProtocols = /https?|ftp|gopher|file/ @@ -71,3 +71,34 @@ export function formatUrl(urlObj: UrlObject) { return `${protocol}${host}${pathname}${search}${hash}` } + +export const urlObjectKeys = [ + 'auth', + 'hash', + 'host', + 'hostname', + 'href', + 'path', + 'pathname', + 'port', + 'protocol', + 'query', + 'search', + 'slashes', +] + +export function formatWithValidation(url: UrlObject): string { + if (process.env.NODE_ENV === 'development') { + if (url !== null && typeof url === 'object') { + Object.keys(url).forEach((key) => { + if (urlObjectKeys.indexOf(key) === -1) { + console.warn( + `Unknown key passed via urlObject into url.format: ${key}` + ) + } + }) + } + } + + return formatUrl(url) +} diff --git a/packages/next/shared/lib/utils.ts b/packages/next/shared/lib/utils.ts index 6c22ce7ff852..f18e19f6cf45 100644 --- a/packages/next/shared/lib/utils.ts +++ b/packages/next/shared/lib/utils.ts @@ -1,4 +1,4 @@ -import type { BuildManifest } from '../../server/get-page-files' +import type { HtmlProps } from './html-context' import type { ComponentType } from 'react' import type { DomainLocale } from '../../server/config' import type { Env } from '@next/env' @@ -6,9 +6,6 @@ import type { IncomingMessage, ServerResponse } from 'http' import type { NextRouter } from './router/router' import type { ParsedUrlQuery } from 'querystring' import type { PreviewData } from 'next/types' -import type { UrlObject } from 'url' -import { createContext } from 'react' -import { formatUrl } from './router/utils/format-url' export type NextComponentType< C extends BaseContext = NextPageContext, @@ -107,6 +104,7 @@ export type NEXT_DATA = { domainLocales?: DomainLocale[] scriptLoader?: any[] isPreview?: boolean + notFoundSrcPage?: string rsc?: boolean } @@ -194,39 +192,6 @@ export type MaybeDeferContentHook = ( contentFn: () => JSX.Element ) => [boolean, JSX.Element] -export type HtmlProps = { - __NEXT_DATA__: NEXT_DATA - dangerousAsPath: string - docComponentsRendered: { - Html?: boolean - Main?: boolean - Head?: boolean - NextScript?: boolean - } - buildManifest: BuildManifest - ampPath: string - inAmpMode: boolean - hybridAmp: boolean - isDevelopment: boolean - dynamicImports: string[] - assetPrefix?: string - canonicalBase: string - headTags: any[] - unstable_runtimeJS?: false - unstable_JsPreload?: false - devOnlyCacheBusterQueryString: string - scriptLoader: { afterInteractive?: string[]; beforeInteractive?: any[] } - locale?: string - disableOptimizedLoading?: boolean - styles?: React.ReactElement[] | React.ReactFragment - head?: Array - useMaybeDeferContent: MaybeDeferContentHook - crossOrigin?: string - optimizeCss?: boolean - optimizeFonts?: boolean - runtime?: 'edge' | 'nodejs' -} - /** * Next `API` route request */ @@ -409,37 +374,6 @@ export async function loadGetInitialProps< return props } -export const urlObjectKeys = [ - 'auth', - 'hash', - 'host', - 'hostname', - 'href', - 'path', - 'pathname', - 'port', - 'protocol', - 'query', - 'search', - 'slashes', -] - -export function formatWithValidation(url: UrlObject): string { - if (process.env.NODE_ENV === 'development') { - if (url !== null && typeof url === 'object') { - Object.keys(url).forEach((key) => { - if (urlObjectKeys.indexOf(key) === -1) { - console.warn( - `Unknown key passed via urlObject into url.format: ${key}` - ) - } - }) - } - } - - return formatUrl(url) -} - export const SP = typeof performance !== 'undefined' export const ST = SP && @@ -448,11 +382,6 @@ export const ST = export class DecodeError extends Error {} -export const HtmlContext = createContext(null as any) -if (process.env.NODE_ENV !== 'production') { - HtmlContext.displayName = 'HtmlContext' -} - export interface CacheFs { readFile(f: string): Promise readFileSync(f: string): string diff --git a/packages/next/types/webpack.d.ts b/packages/next/types/webpack.d.ts index cd3f07453c22..5cbda79547b0 100644 --- a/packages/next/types/webpack.d.ts +++ b/packages/next/types/webpack.d.ts @@ -35,6 +35,7 @@ declare module 'next/dist/compiled/webpack/webpack' { export let BasicEvaluatedExpression: any export let GraphHelpers: any export let sources: typeof webpackSources + export let StringXor: any // TODO change this to webpack5 export { webpack4 as webpack, loader, webpack4, webpack5 } } diff --git a/packages/react-dev-overlay/package.json b/packages/react-dev-overlay/package.json index a81dcc722a69..a1d512c8a6f7 100644 --- a/packages/react-dev-overlay/package.json +++ b/packages/react-dev-overlay/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-dev-overlay", - "version": "12.0.11-canary.18", + "version": "12.1.0", "description": "A development-only overlay for developing React applications.", "repository": { "url": "vercel/next.js", diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index 12cb97223853..f797514e1ac9 100644 --- a/packages/react-refresh-utils/package.json +++ b/packages/react-refresh-utils/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-refresh-utils", - "version": "12.0.11-canary.18", + "version": "12.1.0", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", diff --git a/test/development/basic/gssp-ssr-change-reloading/lib/data.json b/test/development/basic/gssp-ssr-change-reloading/lib/data.json new file mode 100644 index 000000000000..f2a886f39de7 --- /dev/null +++ b/test/development/basic/gssp-ssr-change-reloading/lib/data.json @@ -0,0 +1,3 @@ +{ + "hello": "world" +} diff --git a/test/integration/gssp-ssr-change-reloading/pages/another/index.js b/test/development/basic/gssp-ssr-change-reloading/pages/another/index.js similarity index 100% rename from test/integration/gssp-ssr-change-reloading/pages/another/index.js rename to test/development/basic/gssp-ssr-change-reloading/pages/another/index.js diff --git a/test/integration/gssp-ssr-change-reloading/pages/gsp-blog/[post].js b/test/development/basic/gssp-ssr-change-reloading/pages/gsp-blog/[post].js similarity index 100% rename from test/integration/gssp-ssr-change-reloading/pages/gsp-blog/[post].js rename to test/development/basic/gssp-ssr-change-reloading/pages/gsp-blog/[post].js diff --git a/test/integration/gssp-ssr-change-reloading/pages/gssp-blog/[post].js b/test/development/basic/gssp-ssr-change-reloading/pages/gssp-blog/[post].js similarity index 100% rename from test/integration/gssp-ssr-change-reloading/pages/gssp-blog/[post].js rename to test/development/basic/gssp-ssr-change-reloading/pages/gssp-blog/[post].js diff --git a/test/integration/gssp-ssr-change-reloading/pages/index.js b/test/development/basic/gssp-ssr-change-reloading/pages/index.js similarity index 89% rename from test/integration/gssp-ssr-change-reloading/pages/index.js rename to test/development/basic/gssp-ssr-change-reloading/pages/index.js index 679405ddf80a..3f2fe4523faa 100644 --- a/test/integration/gssp-ssr-change-reloading/pages/index.js +++ b/test/development/basic/gssp-ssr-change-reloading/pages/index.js @@ -1,4 +1,5 @@ import { useRouter } from 'next/router' +import data from '../lib/data.json' export default function Gsp(props) { if (useRouter().isFallback) { @@ -19,6 +20,7 @@ export const getStaticProps = async () => { return { props: { count, + data, random: Math.random(), }, } diff --git a/test/development/basic/gssp-ssr-change-reloading/test/index.test.ts b/test/development/basic/gssp-ssr-change-reloading/test/index.test.ts new file mode 100644 index 000000000000..828dc2adb2fe --- /dev/null +++ b/test/development/basic/gssp-ssr-change-reloading/test/index.test.ts @@ -0,0 +1,309 @@ +/* eslint-env jest */ + +import { join } from 'path' +import webdriver from 'next-webdriver' +import { createNext, FileRef } from 'e2e-utils' +import { check, getRedboxHeader, hasRedbox } from 'next-test-utils' +import { NextInstance } from 'test/lib/next-modes/base' + +const installCheckVisible = (browser) => { + return browser.eval(`(function() { + window.checkInterval = setInterval(function() { + let watcherDiv = document.querySelector('#__next-build-watcher') + watcherDiv = watcherDiv.shadowRoot || watcherDiv + window.showedBuilder = window.showedBuilder || ( + watcherDiv.querySelector('div').className.indexOf('visible') > -1 + ) + if (window.showedBuilder) clearInterval(window.checkInterval) + }, 50) + })()`) +} + +describe('GS(S)P Server-Side Change Reloading', () => { + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: { + pages: new FileRef(join(__dirname, '../pages')), + lib: new FileRef(join(__dirname, '../lib')), + }, + }) + }) + afterAll(() => next.destroy()) + + it('should not reload page when client-side is changed too GSP', async () => { + const browser = await webdriver(next.url, '/gsp-blog/first') + await check(() => browser.elementByCss('#change').text(), 'change me') + await browser.eval(`window.beforeChange = 'hi'`) + + const props = JSON.parse(await browser.elementByCss('#props').text()) + + const page = 'pages/gsp-blog/[post].js' + const originalContent = await next.readFile(page) + await next.patchFile(page, originalContent.replace('change me', 'changed')) + + await check(() => browser.elementByCss('#change').text(), 'changed') + expect(await browser.eval(`window.beforeChange`)).toBe('hi') + + const props2 = JSON.parse(await browser.elementByCss('#props').text()) + expect(props).toEqual(props2) + + await next.patchFile(page, originalContent) + await check(() => browser.elementByCss('#change').text(), 'change me') + }) + + it('should update page when getStaticProps is changed only', async () => { + const browser = await webdriver(next.url, '/gsp-blog/first') + await browser.eval(`window.beforeChange = 'hi'`) + + const props = JSON.parse(await browser.elementByCss('#props').text()) + expect(props.count).toBe(1) + + const page = 'pages/gsp-blog/[post].js' + const originalContent = await next.readFile(page) + await next.patchFile( + page, + originalContent.replace('count = 1', 'count = 2') + ) + + await check( + async () => + JSON.parse(await browser.elementByCss('#props').text()).count + '', + '2' + ) + expect(await browser.eval(`window.beforeChange`)).toBe('hi') + await next.patchFile(page, originalContent) + + await check( + async () => + JSON.parse(await browser.elementByCss('#props').text()).count + '', + '1' + ) + }) + + it('should show indicator when re-fetching data', async () => { + const browser = await webdriver(next.url, '/gsp-blog/second') + await installCheckVisible(browser) + await browser.eval(`window.beforeChange = 'hi'`) + + const props = JSON.parse(await browser.elementByCss('#props').text()) + expect(props.count).toBe(1) + + const page = 'pages/gsp-blog/[post].js' + const originalContent = await next.readFile(page) + await next.patchFile( + page, + originalContent.replace('count = 1', 'count = 2') + ) + + await check( + async () => + JSON.parse(await browser.elementByCss('#props').text()).count + '', + '2' + ) + expect(await browser.eval(`window.beforeChange`)).toBe('hi') + expect(await browser.eval(`window.showedBuilder`)).toBe(true) + + await next.patchFile(page, originalContent) + await check( + async () => + JSON.parse(await browser.elementByCss('#props').text()).count + '', + '1' + ) + }) + + it('should update page when getStaticPaths is changed only', async () => { + const browser = await webdriver(next.url, '/gsp-blog/first') + await browser.eval(`window.beforeChange = 'hi'`) + + const props = JSON.parse(await browser.elementByCss('#props').text()) + expect(props.count).toBe(1) + + const page = 'pages/gsp-blog/[post].js' + const originalContent = await next.readFile(page) + await next.patchFile( + page, + originalContent.replace('paths = 1', 'paths = 2') + ) + + expect(await browser.eval('window.beforeChange')).toBe('hi') + await next.patchFile(page, originalContent) + }) + + it('should update page when getStaticProps is changed only for /index', async () => { + const browser = await webdriver(next.url, '/') + await browser.eval(`window.beforeChange = 'hi'`) + + const props = JSON.parse(await browser.elementByCss('#props').text()) + expect(props.count).toBe(1) + + const page = 'pages/index.js' + const originalContent = await next.readFile(page) + await next.patchFile( + page, + originalContent.replace('count = 1', 'count = 2') + ) + + expect(await browser.eval('window.beforeChange')).toBe('hi') + await next.patchFile(page, originalContent) + }) + + it('should update page when getStaticProps is changed only for /another/index', async () => { + const browser = await webdriver(next.url, '/another') + await browser.eval(`window.beforeChange = 'hi'`) + + const props = JSON.parse(await browser.elementByCss('#props').text()) + expect(props.count).toBe(1) + + const page = 'pages/another/index.js' + const originalContent = await next.readFile(page) + await next.patchFile( + page, + originalContent.replace('count = 1', 'count = 2') + ) + + expect(await browser.eval('window.beforeChange')).toBe('hi') + await next.patchFile(page, originalContent) + }) + + it('should not reload page when client-side is changed too GSSP', async () => { + const browser = await webdriver(next.url, '/gssp-blog/first') + await check(() => browser.elementByCss('#change').text(), 'change me') + await browser.eval(`window.beforeChange = 'hi'`) + + const props = JSON.parse(await browser.elementByCss('#props').text()) + + const page = 'pages/gssp-blog/[post].js' + const originalContent = await next.readFile(page) + await next.patchFile(page, originalContent.replace('change me', 'changed')) + + await check(() => browser.elementByCss('#change').text(), 'changed') + expect(await browser.eval(`window.beforeChange`)).toBe('hi') + + const props2 = JSON.parse(await browser.elementByCss('#props').text()) + expect(props).toEqual(props2) + + await next.patchFile(page, originalContent) + await check(() => browser.elementByCss('#change').text(), 'change me') + }) + + it('should update page when getServerSideProps is changed only', async () => { + const browser = await webdriver(next.url, '/gssp-blog/first') + await check( + async () => + JSON.parse(await browser.elementByCss('#props').text()).count + '', + '1' + ) + await browser.eval(`window.beforeChange = 'hi'`) + + const props = JSON.parse(await browser.elementByCss('#props').text()) + expect(props.count).toBe(1) + + const page = 'pages/gssp-blog/[post].js' + const originalContent = await next.readFile(page) + await next.patchFile( + page, + originalContent.replace('count = 1', 'count = 2') + ) + + await check( + async () => + JSON.parse(await browser.elementByCss('#props').text()).count + '', + '2' + ) + expect(await browser.eval(`window.beforeChange`)).toBe('hi') + await next.patchFile(page, originalContent) + + await check( + async () => + JSON.parse(await browser.elementByCss('#props').text()).count + '', + '1' + ) + }) + + it('should update on props error in getStaticProps', async () => { + const browser = await webdriver(next.url, '/') + await browser.eval(`window.beforeChange = 'hi'`) + + const props = JSON.parse(await browser.elementByCss('#props').text()) + expect(props.count).toBe(1) + + const page = 'pages/index.js' + const originalContent = await next.readFile(page) + + try { + await next.patchFile(page, originalContent.replace('props:', 'propss:')) + expect(await hasRedbox(browser, true)).toBe(true) + expect(await getRedboxHeader(browser)).toContain( + 'Additional keys were returned from' + ) + + await next.patchFile(page, originalContent) + expect(await hasRedbox(browser, false)).toBe(false) + } finally { + await next.patchFile(page, originalContent) + } + }) + + it('should update on thrown error in getStaticProps', async () => { + const browser = await webdriver(next.url, '/') + await browser.eval(`window.beforeChange = 'hi'`) + + const props = JSON.parse(await browser.elementByCss('#props').text()) + expect(props.count).toBe(1) + + const page = 'pages/index.js' + const originalContent = await next.readFile(page) + + try { + await next.patchFile( + page, + originalContent.replace( + 'const count', + 'throw new Error("custom oops"); const count' + ) + ) + expect(await hasRedbox(browser, true)).toBe(true) + expect(await getRedboxHeader(browser)).toContain('custom oops') + + await next.patchFile(page, originalContent) + expect(await hasRedbox(browser, false)).toBe(false) + } finally { + await next.patchFile(page, originalContent) + } + }) + + it('should refresh data when server import is updated', async () => { + const browser = await webdriver(next.url, '/') + await browser.eval(`window.beforeChange = 'hi'`) + + const props = JSON.parse(await browser.elementByCss('#props').text()) + expect(props.count).toBe(1) + expect(props.data).toEqual({ hello: 'world' }) + + const page = 'lib/data.json' + const originalContent = await next.readFile(page) + + try { + await next.patchFile(page, JSON.stringify({ hello: 'replaced!!' })) + await check(async () => { + const props = JSON.parse(await browser.elementByCss('#props').text()) + return props.count === 1 && props.data.hello === 'replaced!!' + ? 'success' + : JSON.stringify(props) + }, 'success') + expect(await browser.eval('window.beforeChange')).toBe('hi') + + await next.patchFile(page, originalContent) + await check(async () => { + const props = JSON.parse(await browser.elementByCss('#props').text()) + return props.count === 1 && props.data.hello === 'world' + ? 'success' + : JSON.stringify(props) + }, 'success') + } finally { + await next.patchFile(page, originalContent) + } + }) +}) diff --git a/test/development/gssp-notfound/index.test.ts b/test/development/gssp-notfound/index.test.ts new file mode 100644 index 000000000000..9ad292b97405 --- /dev/null +++ b/test/development/gssp-notfound/index.test.ts @@ -0,0 +1,34 @@ +import { createNext } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' +import { waitFor } from 'next-test-utils' +import webdriver from 'next-webdriver' + +describe('getServerSideProps returns notFound: true', () => { + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: { + 'pages/index.js': ` + const Home = () => null + export default Home + + export function getServerSideProps() { + console.log("gssp called") + return { notFound: true } + } + `, + }, + dependencies: {}, + }) + }) + afterAll(() => next.destroy()) + + it('should not poll indefinitely', async () => { + const browser = await webdriver(next.appPort, '/') + await waitFor(3000) + await browser.close() + const logOccurrences = next.cliOutput.split('gssp called').length - 1 + expect(logOccurrences).toBe(1) + }) +}) diff --git a/test/integration/font-optimization/fixtures/with-typekit/manifest-snapshot.json b/test/integration/font-optimization/fixtures/with-typekit/manifest-snapshot.json index cb517f819778..b7d9fb5eb6d5 100644 --- a/test/integration/font-optimization/fixtures/with-typekit/manifest-snapshot.json +++ b/test/integration/font-optimization/fixtures/with-typekit/manifest-snapshot.json @@ -1,14 +1,14 @@ [ { "url": "https://use.typekit.net/plm1izr.css", - "content": "@import url(\"https://p.typekit.net/p.css?s=1&k=plm1izr&ht=tk&f=32266&a=23152309&app=typekit&e=css\");@font-face{font-family:\"birra-2\";src:url(\"https://use.typekit.net/af/23e0ad/00000000000000003b9b410c/27/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3\") format(\"woff2\"),url(\"https://use.typekit.net/af/23e0ad/00000000000000003b9b410c/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3\") format(\"woff\"),url(\"https://use.typekit.net/af/23e0ad/00000000000000003b9b410c/27/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3\") format(\"opentype\");font-display:auto;font-style:normal;font-weight:700}.tk-birra-2{font-family:\"birra-2\",serif}" + "content": "@import url(\"https://p.typekit.net/p.css?s=1&k=plm1izr&ht=tk&f=32266&a=23152309&app=typekit&e=css\");@font-face{font-family:\"birra-2\";src:url(\"https://use.typekit.net/af/23e0ad/00000000000000003b9b410c/27/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3\") format(\"woff2\"),url(\"https://use.typekit.net/af/23e0ad/00000000000000003b9b410c/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3\") format(\"woff\"),url(\"https://use.typekit.net/af/23e0ad/00000000000000003b9b410c/27/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3\") format(\"opentype\");font-display:auto;font-style:normal;font-weight:700;font-stretch:normal}.tk-birra-2{font-family:\"birra-2\",serif}" }, { "url": "https://use.typekit.net/ucs7mcf.css", - "content": "@import url(\"https://p.typekit.net/p.css?s=1&k=ucs7mcf&ht=tk&f=43886&a=23152309&app=typekit&e=css\");@font-face{font-family:\"flegrei\";src:url(\"https://use.typekit.net/af/74a5d1/00000000000000003b9b3d6e/27/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3\") format(\"woff2\"),url(\"https://use.typekit.net/af/74a5d1/00000000000000003b9b3d6e/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3\") format(\"woff\"),url(\"https://use.typekit.net/af/74a5d1/00000000000000003b9b3d6e/27/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3\") format(\"opentype\");font-display:auto;font-style:normal;font-weight:400}.tk-flegrei{font-family:\"flegrei\",sans-serif}" + "content": "@import url(\"https://p.typekit.net/p.css?s=1&k=ucs7mcf&ht=tk&f=43886&a=23152309&app=typekit&e=css\");@font-face{font-family:\"flegrei\";src:url(\"https://use.typekit.net/af/74a5d1/00000000000000003b9b3d6e/27/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3\") format(\"woff2\"),url(\"https://use.typekit.net/af/74a5d1/00000000000000003b9b3d6e/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3\") format(\"woff\"),url(\"https://use.typekit.net/af/74a5d1/00000000000000003b9b3d6e/27/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3\") format(\"opentype\");font-display:auto;font-style:normal;font-weight:400;font-stretch:normal}.tk-flegrei{font-family:\"flegrei\",sans-serif}" }, { "url": "https://use.typekit.net/erd0sed.css", - "content": "@import url(\"https://p.typekit.net/p.css?s=1&k=erd0sed&ht=tk&f=43885&a=23152309&app=typekit&e=css\");@font-face{font-family:\"pantelleria\";src:url(\"https://use.typekit.net/af/1f141c/00000000000000003b9b3d6f/27/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3\") format(\"woff2\"),url(\"https://use.typekit.net/af/1f141c/00000000000000003b9b3d6f/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3\") format(\"woff\"),url(\"https://use.typekit.net/af/1f141c/00000000000000003b9b3d6f/27/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3\") format(\"opentype\");font-display:auto;font-style:normal;font-weight:400}.tk-pantelleria{font-family:\"pantelleria\",sans-serif}" + "content": "@import url(\"https://p.typekit.net/p.css?s=1&k=erd0sed&ht=tk&f=43885&a=23152309&app=typekit&e=css\");@font-face{font-family:\"pantelleria\";src:url(\"https://use.typekit.net/af/1f141c/00000000000000003b9b3d6f/27/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3\") format(\"woff2\"),url(\"https://use.typekit.net/af/1f141c/00000000000000003b9b3d6f/27/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3\") format(\"woff\"),url(\"https://use.typekit.net/af/1f141c/00000000000000003b9b3d6f/27/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3\") format(\"opentype\");font-display:auto;font-style:normal;font-weight:400;font-stretch:normal}.tk-pantelleria{font-family:\"pantelleria\",sans-serif}" } ] diff --git a/test/integration/gssp-ssr-change-reloading/test/index.test.js b/test/integration/gssp-ssr-change-reloading/test/index.test.js deleted file mode 100644 index 3fcd32e4a23d..000000000000 --- a/test/integration/gssp-ssr-change-reloading/test/index.test.js +++ /dev/null @@ -1,197 +0,0 @@ -/* eslint-env jest */ - -import { join } from 'path' -import webdriver from 'next-webdriver' -import { killApp, findPort, launchApp, File, check } from 'next-test-utils' - -const appDir = join(__dirname, '..') - -let appPort -let app - -const installCheckVisible = (browser) => { - return browser.eval(`(function() { - window.checkInterval = setInterval(function() { - let watcherDiv = document.querySelector('#__next-build-watcher') - watcherDiv = watcherDiv.shadowRoot || watcherDiv - window.showedBuilder = window.showedBuilder || ( - watcherDiv.querySelector('div').className.indexOf('visible') > -1 - ) - if (window.showedBuilder) clearInterval(window.checkInterval) - }, 50) - })()`) -} - -describe('GS(S)P Server-Side Change Reloading', () => { - beforeAll(async () => { - appPort = await findPort() - app = await launchApp(appDir, appPort) - }) - afterAll(() => killApp(app)) - - it('should not reload page when client-side is changed too GSP', async () => { - const browser = await webdriver(appPort, '/gsp-blog/first') - await check(() => browser.elementByCss('#change').text(), 'change me') - await browser.eval(() => (window.beforeChange = 'hi')) - - const props = JSON.parse(await browser.elementByCss('#props').text()) - - const page = new File(join(appDir, 'pages/gsp-blog/[post].js')) - page.replace('change me', 'changed') - - await check(() => browser.elementByCss('#change').text(), 'changed') - expect(await browser.eval(() => window.beforeChange)).toBe('hi') - - const props2 = JSON.parse(await browser.elementByCss('#props').text()) - expect(props).toEqual(props2) - - page.restore() - - await check(() => browser.elementByCss('#change').text(), 'change me') - }) - - it('should update page when getStaticProps is changed only', async () => { - const browser = await webdriver(appPort, '/gsp-blog/first') - await browser.eval(() => (window.beforeChange = 'hi')) - - const props = JSON.parse(await browser.elementByCss('#props').text()) - expect(props.count).toBe(1) - - const page = new File(join(appDir, 'pages/gsp-blog/[post].js')) - page.replace('count = 1', 'count = 2') - - await check( - async () => - JSON.parse(await browser.elementByCss('#props').text()).count + '', - '2' - ) - expect(await browser.eval(() => window.beforeChange)).toBe('hi') - page.restore() - - await check( - async () => - JSON.parse(await browser.elementByCss('#props').text()).count + '', - '1' - ) - }) - - it('should show indicator when re-fetching data', async () => { - const browser = await webdriver(appPort, '/gsp-blog/second') - await installCheckVisible(browser) - await browser.eval(() => (window.beforeChange = 'hi')) - - const props = JSON.parse(await browser.elementByCss('#props').text()) - expect(props.count).toBe(1) - - const page = new File(join(appDir, 'pages/gsp-blog/[post].js')) - page.replace('count = 1', 'count = 2') - - await check( - async () => - JSON.parse(await browser.elementByCss('#props').text()).count + '', - '2' - ) - expect(await browser.eval(() => window.beforeChange)).toBe('hi') - expect(await browser.eval(() => window.showedBuilder)).toBe(true) - page.restore() - - await check( - async () => - JSON.parse(await browser.elementByCss('#props').text()).count + '', - '1' - ) - }) - - it('should update page when getStaticPaths is changed only', async () => { - const browser = await webdriver(appPort, '/gsp-blog/first') - await browser.eval(() => (window.beforeChange = 'hi')) - - const props = JSON.parse(await browser.elementByCss('#props').text()) - expect(props.count).toBe(1) - - const page = new File(join(appDir, 'pages/gsp-blog/[post].js')) - page.replace('paths = 1', 'paths = 2') - - expect(await browser.eval('window.beforeChange')).toBe('hi') - page.restore() - }) - - it('should update page when getStaticProps is changed only for /index', async () => { - const browser = await webdriver(appPort, '/') - await browser.eval(() => (window.beforeChange = 'hi')) - - const props = JSON.parse(await browser.elementByCss('#props').text()) - expect(props.count).toBe(1) - - const page = new File(join(appDir, 'pages/index.js')) - page.replace('count = 1', 'count = 2') - - expect(await browser.eval('window.beforeChange')).toBe('hi') - page.restore() - }) - - it('should update page when getStaticProps is changed only for /another/index', async () => { - const browser = await webdriver(appPort, '/another') - await browser.eval(() => (window.beforeChange = 'hi')) - - const props = JSON.parse(await browser.elementByCss('#props').text()) - expect(props.count).toBe(1) - - const page = new File(join(appDir, 'pages/another/index.js')) - page.replace('count = 1', 'count = 2') - - expect(await browser.eval('window.beforeChange')).toBe('hi') - page.restore() - }) - - it('should not reload page when client-side is changed too GSSP', async () => { - const browser = await webdriver(appPort, '/gssp-blog/first') - await check(() => browser.elementByCss('#change').text(), 'change me') - await browser.eval(() => (window.beforeChange = 'hi')) - - const props = JSON.parse(await browser.elementByCss('#props').text()) - - const page = new File(join(appDir, 'pages/gssp-blog/[post].js')) - page.replace('change me', 'changed') - - await check(() => browser.elementByCss('#change').text(), 'changed') - expect(await browser.eval(() => window.beforeChange)).toBe('hi') - - const props2 = JSON.parse(await browser.elementByCss('#props').text()) - expect(props).toEqual(props2) - - page.restore() - - await check(() => browser.elementByCss('#change').text(), 'change me') - }) - - it('should update page when getServerSideProps is changed only', async () => { - const browser = await webdriver(appPort, '/gssp-blog/first') - await check( - async () => - JSON.parse(await browser.elementByCss('#props').text()).count + '', - '1' - ) - await browser.eval(() => (window.beforeChange = 'hi')) - - const props = JSON.parse(await browser.elementByCss('#props').text()) - expect(props.count).toBe(1) - - const page = new File(join(appDir, 'pages/gssp-blog/[post].js')) - page.replace('count = 1', 'count = 2') - - await check( - async () => - JSON.parse(await browser.elementByCss('#props').text()).count + '', - '2' - ) - expect(await browser.eval(() => window.beforeChange)).toBe('hi') - page.restore() - - await check( - async () => - JSON.parse(await browser.elementByCss('#props').text()).count + '', - '1' - ) - }) -}) diff --git a/test/integration/image-component/default/pages/loader-svg.js b/test/integration/image-component/default/pages/loader-svg.js new file mode 100644 index 000000000000..2390b5936ce0 --- /dev/null +++ b/test/integration/image-component/default/pages/loader-svg.js @@ -0,0 +1,22 @@ +import React from 'react' +import Image from 'next/image' + +const Page = () => { + return ( +
+

Should work with SVG

+ `${src}?size=${width}`} + /> +
+ + +
+ ) +} + +export default Page diff --git a/test/integration/image-component/default/test/index.test.js b/test/integration/image-component/default/test/index.test.js index 8267389e1c94..7d406b9e6335 100644 --- a/test/integration/image-component/default/test/index.test.js +++ b/test/integration/image-component/default/test/index.test.js @@ -208,7 +208,7 @@ function runTests(mode) { ) await check( () => browser.eval(`document.getElementById("img3").currentSrc`), - /test(.*)svg/ + /test\.svg/ ) await check( () => browser.eval(`document.getElementById("img4").currentSrc`), @@ -785,6 +785,30 @@ function runTests(mode) { /Image with src (.*)gif(.*) has "sizes" property but it will be ignored/gm ) }) + + it('should not warn when svg, even if with loader prop or without', async () => { + const browser = await webdriver(appPort, '/loader-svg') + await browser.eval(`document.querySelector("footer").scrollIntoView()`) + const warnings = (await browser.log('browser')) + .map((log) => log.message) + .join('\n') + expect(await hasRedbox(browser)).toBe(false) + expect(warnings).not.toMatch( + /Image with src (.*) has a "loader" property that does not implement width/gm + ) + expect(await browser.elementById('with-loader').getAttribute('src')).toBe( + '/test.svg?size=256' + ) + expect( + await browser.elementById('with-loader').getAttribute('srcset') + ).toBe('/test.svg?size=128 1x, /test.svg?size=256 2x') + expect( + await browser.elementById('without-loader').getAttribute('src') + ).toBe('/test.svg') + expect( + await browser.elementById('without-loader').getAttribute('srcset') + ).toBe('/test.svg 1x, /test.svg 2x') + }) } else { //server-only tests it('should not create an image folder in server/chunks', async () => { @@ -1077,7 +1101,7 @@ function runTests(mode) { expect( await hasImageMatchingUrl( browser, - `http://localhost:${appPort}/_next/image?url=%2Ftest.svg&w=828&q=75` + `http://localhost:${appPort}/test.svg` ) ).toBe(true) expect( diff --git a/test/integration/image-optimizer/test/index.test.js b/test/integration/image-optimizer/test/index.test.js index 52797c710166..44ae3d8b8b2b 100644 --- a/test/integration/image-optimizer/test/index.test.js +++ b/test/integration/image-optimizer/test/index.test.js @@ -224,6 +224,56 @@ describe('Image Optimizer', () => { `Specified images.loader property (imgix) also requires images.path property to be assigned to a URL prefix.` ) }) + + it('should error when images.dangerouslyAllowSVG is not a boolean', async () => { + await nextConfig.replace( + '{ /* replaceme */ }', + JSON.stringify({ + images: { + dangerouslyAllowSVG: 'foo', + }, + }) + ) + let stderr = '' + + app = await launchApp(appDir, await findPort(), { + onStderr(msg) { + stderr += msg || '' + }, + }) + await waitFor(1000) + await killApp(app).catch(() => {}) + await nextConfig.restore() + + expect(stderr).toContain( + `Specified images.dangerouslyAllowSVG should be a boolean` + ) + }) + + it('should error when images.contentSecurityPolicy is not a string', async () => { + await nextConfig.replace( + '{ /* replaceme */ }', + JSON.stringify({ + images: { + contentSecurityPolicy: 1, + }, + }) + ) + let stderr = '' + + app = await launchApp(appDir, await findPort(), { + onStderr(msg) { + stderr += msg || '' + }, + }) + await waitFor(1000) + await killApp(app).catch(() => {}) + await nextConfig.restore() + + expect(stderr).toContain( + `Specified images.contentSecurityPolicy should be a string` + ) + }) }) // domains for testing @@ -240,11 +290,13 @@ describe('Image Optimizer', () => { describe('Server support for minimumCacheTTL in next.config.js', () => { const size = 96 // defaults defined in server/config.ts + const dangerouslyAllowSVG = true const ctx = { w: size, isDev: false, domains, minimumCacheTTL, + dangerouslyAllowSVG, imagesDir, appDir, } @@ -253,6 +305,7 @@ describe('Image Optimizer', () => { images: { domains, minimumCacheTTL, + dangerouslyAllowSVG, }, }) ctx.nextOutput = '' diff --git a/test/integration/image-optimizer/test/util.js b/test/integration/image-optimizer/test/util.js index 47867ab3b254..5c83a4fc74c5 100644 --- a/test/integration/image-optimizer/test/util.js +++ b/test/integration/image-optimizer/test/util.js @@ -186,29 +186,39 @@ export function runTests(ctx) { expect(isAnimated(await res.buffer())).toBe(true) }) - it('should maintain vector svg', async () => { - const query = { w: ctx.w, q: 90, url: '/test.svg' } - const opts = { headers: { accept: 'image/webp' } } - const res = await fetchViaHTTP(ctx.appPort, '/_next/image', query, opts) - expect(res.status).toBe(200) - expect(res.headers.get('Content-Type')).toContain('image/svg+xml') - expect(res.headers.get('Cache-Control')).toBe( - `public, max-age=0, must-revalidate` - ) - // SVG is compressible so will have accept-encoding set from - // compression - expect(res.headers.get('Vary')).toMatch(/^Accept(,|$)/) - expect(res.headers.get('etag')).toBeTruthy() - expect(res.headers.get('Content-Disposition')).toBe( - `inline; filename="test.svg"` - ) - const actual = await res.text() - const expected = await fs.readFile( - join(ctx.appDir, 'public', 'test.svg'), - 'utf8' - ) - expect(actual).toMatch(expected) - }) + if (ctx.dangerouslyAllowSVG) { + it('should maintain vector svg', async () => { + const query = { w: ctx.w, q: 90, url: '/test.svg' } + const opts = { headers: { accept: 'image/webp' } } + const res = await fetchViaHTTP(ctx.appPort, '/_next/image', query, opts) + expect(res.status).toBe(200) + expect(res.headers.get('Content-Type')).toContain('image/svg+xml') + expect(res.headers.get('Cache-Control')).toBe( + `public, max-age=0, must-revalidate` + ) + // SVG is compressible so will have accept-encoding set from + // compression + expect(res.headers.get('Vary')).toMatch(/^Accept(,|$)/) + expect(res.headers.get('etag')).toBeTruthy() + expect(res.headers.get('Content-Disposition')).toBe( + `inline; filename="test.svg"` + ) + const actual = await res.text() + const expected = await fs.readFile( + join(ctx.appDir, 'public', 'test.svg'), + 'utf8' + ) + expect(actual).toMatch(expected) + }) + } else { + it('should not allow vector svg', async () => { + const query = { w: ctx.w, q: 35, url: '/test.svg' } + const opts = { headers: { accept: 'image/webp' } } + const res = await fetchViaHTTP(ctx.appPort, '/_next/image', query, opts) + expect(res.status).toBe(400) + expect(await res.text()).toContain('valid but image type is not allowed') + }) + } it('should maintain ico format', async () => { const query = { w: ctx.w, q: 90, url: `/test.ico` } @@ -778,41 +788,43 @@ export function runTests(ctx) { } }) - it('should use cached image file when parameters are the same for svg', async () => { - await cleanImagesDir(ctx) + if (ctx.dangerouslyAllowSVG) { + it('should use cached image file when parameters are the same for svg', async () => { + await cleanImagesDir(ctx) - const query = { url: '/test.svg', w: ctx.w, q: 80 } - const opts = { headers: { accept: 'image/webp' } } + const query = { url: '/test.svg', w: ctx.w, q: 80 } + const opts = { headers: { accept: 'image/webp' } } - const res1 = await fetchViaHTTP(ctx.appPort, '/_next/image', query, opts) - expect(res1.status).toBe(200) - expect(res1.headers.get('X-Nextjs-Cache')).toBe('MISS') - expect(res1.headers.get('Content-Type')).toBe('image/svg+xml') - expect(res1.headers.get('Content-Disposition')).toBe( - `inline; filename="test.svg"` - ) - const etagOne = res1.headers.get('etag') + const res1 = await fetchViaHTTP(ctx.appPort, '/_next/image', query, opts) + expect(res1.status).toBe(200) + expect(res1.headers.get('X-Nextjs-Cache')).toBe('MISS') + expect(res1.headers.get('Content-Type')).toBe('image/svg+xml') + expect(res1.headers.get('Content-Disposition')).toBe( + `inline; filename="test.svg"` + ) + const etagOne = res1.headers.get('etag') - let json1 - await check(async () => { - json1 = await fsToJson(ctx.imagesDir) - return Object.keys(json1).some((dir) => { - return Object.keys(json1[dir]).some((file) => file.includes(etagOne)) - }) - ? 'success' - : 'fail' - }, 'success') + let json1 + await check(async () => { + json1 = await fsToJson(ctx.imagesDir) + return Object.keys(json1).some((dir) => { + return Object.keys(json1[dir]).some((file) => file.includes(etagOne)) + }) + ? 'success' + : 'fail' + }, 'success') - const res2 = await fetchViaHTTP(ctx.appPort, '/_next/image', query, opts) - expect(res2.status).toBe(200) - expect(res2.headers.get('X-Nextjs-Cache')).toBe('HIT') - expect(res2.headers.get('Content-Type')).toBe('image/svg+xml') - expect(res2.headers.get('Content-Disposition')).toBe( - `inline; filename="test.svg"` - ) - const json2 = await fsToJson(ctx.imagesDir) - expect(json2).toStrictEqual(json1) - }) + const res2 = await fetchViaHTTP(ctx.appPort, '/_next/image', query, opts) + expect(res2.status).toBe(200) + expect(res2.headers.get('X-Nextjs-Cache')).toBe('HIT') + expect(res2.headers.get('Content-Type')).toBe('image/svg+xml') + expect(res2.headers.get('Content-Disposition')).toBe( + `inline; filename="test.svg"` + ) + const json2 = await fsToJson(ctx.imagesDir) + expect(json2).toStrictEqual(json1) + }) + } it('should use cached image file when parameters are the same for animated gif', async () => { await cleanImagesDir(ctx) diff --git a/test/integration/middleware/core/pages/rewrites/_middleware.js b/test/integration/middleware/core/pages/rewrites/_middleware.js index 27137642a1c1..d9b83488318c 100644 --- a/test/integration/middleware/core/pages/rewrites/_middleware.js +++ b/test/integration/middleware/core/pages/rewrites/_middleware.js @@ -12,7 +12,7 @@ export async function middleware(request) { ) { const isExternal = url.searchParams.get('override') === 'external' return NextResponse.rewrite( - isExternal ? 'https://vercel.com' : '/rewrites/a' + isExternal ? 'https://vercel.com' : new URL('/rewrites/a', request.url) ) } diff --git a/test/integration/middleware/core/test/index.test.js b/test/integration/middleware/core/test/index.test.js index c6d33990720f..805cf48fd395 100644 --- a/test/integration/middleware/core/test/index.test.js +++ b/test/integration/middleware/core/test/index.test.js @@ -19,7 +19,7 @@ const context = {} context.appDir = join(__dirname, '../') const middlewareWarning = 'using beta Middleware (not covered by semver)' -const urlsWarning = 'using relative URLs for Middleware will be deprecated soon' +const urlsError = 'Please use only absolute URLs' describe('Middleware base tests', () => { describe('dev mode', () => { @@ -110,7 +110,7 @@ describe('Middleware base tests', () => { }) }) -function urlTests(log, locale = '') { +function urlTests(_log, locale = '') { it('rewrites by default to a target location', async () => { const res = await fetchViaHTTP(context.appPort, `${locale}/urls`) const html = await res.text() @@ -146,18 +146,39 @@ function urlTests(log, locale = '') { }) it('warns when using Response.redirect with a relative URL', async () => { - await fetchViaHTTP(context.appPort, `${locale}/urls/relative-redirect`) - expect(log.output).toContain(urlsWarning) + const response = await fetchViaHTTP( + context.appPort, + `${locale}/urls/relative-redirect` + ) + expect(await response.json()).toEqual({ + error: { + message: expect.stringContaining(urlsError), + }, + }) }) it('warns when using NextResponse.redirect with a relative URL', async () => { - await fetchViaHTTP(context.appPort, `${locale}/urls/relative-next-redirect`) - expect(log.output).toContain(urlsWarning) + const response = await fetchViaHTTP( + context.appPort, + `${locale}/urls/relative-next-redirect` + ) + expect(await response.json()).toEqual({ + error: { + message: expect.stringContaining(urlsError), + }, + }) }) - it('warns when using NextResponse.rewrite with a relative URL', async () => { - await fetchViaHTTP(context.appPort, `${locale}/urls/relative-next-rewrite`) - expect(log.output).toContain(urlsWarning) + it('throws when using NextResponse.rewrite with a relative URL', async () => { + const response = await fetchViaHTTP( + context.appPort, + `${locale}/urls/relative-next-rewrite` + ) + expect(await response.json()).toEqual({ + error: { + message: expect.stringContaining(urlsError), + }, + }) }) } diff --git a/test/integration/production/next.config.js b/test/integration/production/next.config.js index cc65ddf36517..e661257a8b2d 100644 --- a/test/integration/production/next.config.js +++ b/test/integration/production/next.config.js @@ -39,4 +39,8 @@ module.exports = { }, ] }, + images: { + // Make sure we have sane default CSP, even when SVG is enabled + dangerouslyAllowSVG: true, + }, } diff --git a/test/integration/production/test/security.js b/test/integration/production/test/security.js index 6ce693ef84b7..8a4778411b7a 100644 --- a/test/integration/production/test/security.js +++ b/test/integration/production/test/security.js @@ -319,7 +319,7 @@ module.exports = (context) => { }) if (browserName !== 'internet explorer') { - it('should not execute script embedded inside svg image', async () => { + it('should not execute script embedded inside svg image, even if dangerouslyAllowSVG=true', async () => { let browser try { browser = await webdriver(context.appPort, '/svg-image') diff --git a/yarn.lock b/yarn.lock index f557f48cfb93..1ee4e9b20c7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4501,10 +4501,10 @@ version "4.1.5" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" -"@types/eslint-scope@^3.7.0": - version "3.7.0" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86" - integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw== +"@types/eslint-scope@^3.7.3": + version "3.7.3" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224" + integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g== dependencies: "@types/eslint" "*" "@types/estree" "*" @@ -4526,10 +4526,10 @@ version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" -"@types/estree@^0.0.50": - version "0.0.50" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" - integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== +"@types/estree@^0.0.51": + version "0.0.51" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== "@types/etag@1.8.0": version "1.8.0" @@ -8879,9 +8879,9 @@ enhanced-resolve@^4.3.0: tapable "^1.0.0" enhanced-resolve@^5.8.3: - version "5.8.3" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz#6d552d465cce0423f5b3d718511ea53826a7b2f0" - integrity sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA== + version "5.9.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz#49ac24953ac8452ed8fed2ef1340fc8e043667ee" + integrity sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -20847,13 +20847,13 @@ webpack-bundle-analyzer@4.3.0: watchpack "^1.7.4" webpack-sources "^1.4.1" -"webpack5@npm:webpack@5.68.0": - version "5.68.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.68.0.tgz#a653a58ed44280062e47257f260117e4be90d560" - integrity sha512-zUcqaUO0772UuuW2bzaES2Zjlm/y3kRBQDVFVCge+s2Y8mwuUTdperGaAv65/NtRL/1zanpSJOq/MD8u61vo6g== +"webpack5@npm:webpack@5.69.1": + version "5.69.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.69.1.tgz#8cfd92c192c6a52c99ab00529b5a0d33aa848dc5" + integrity sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A== dependencies: - "@types/eslint-scope" "^3.7.0" - "@types/estree" "^0.0.50" + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^0.0.51" "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/wasm-edit" "1.11.1" "@webassemblyjs/wasm-parser" "1.11.1"