From b701de60cd696c7a50666ab8b6ed6470d61574da Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Sat, 19 Feb 2022 22:13:48 +0100 Subject: [PATCH 01/10] Improve type imports in server/render and optimize the Edge SSR loader (#34552) * reorganize imports * code refactoring * remove unused variables * fix test Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../next-middleware-ssr-loader/render.ts | 38 ++----------- packages/next/server/dev/hot-reloader.ts | 4 +- packages/next/server/render.tsx | 56 +++++++++++-------- .../test/index.test.js | 5 -- 4 files changed, 41 insertions(+), 62 deletions(-) diff --git a/packages/next/build/webpack/loaders/next-middleware-ssr-loader/render.ts b/packages/next/build/webpack/loaders/next-middleware-ssr-loader/render.ts index beefd6dcb861411..8fa95529ea3a2ec 100644 --- a/packages/next/build/webpack/loaders/next-middleware-ssr-loader/render.ts +++ b/packages/next/build/webpack/loaders/next-middleware-ssr-loader/render.ts @@ -4,7 +4,6 @@ import type { BuildManifest } from '../../../../server/get-page-files' import type { ReactLoadableManifest } from '../../../../server/load-components' import { NextRequest } from '../../../../server/web/spec-extension/request' -import { toNodeHeaders } from '../../../../server/web/utils' import WebServer from '../../../../server/web-server' import { @@ -12,20 +11,6 @@ import { WebNextResponse, } from '../../../../server/base-http/web' -const createHeaders = (args?: any) => ({ - ...args, - 'x-middleware-ssr': '1', - 'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate', -}) - -function sendError(req: any, error: Error) { - const defaultMessage = 'An error occurred while rendering ' + req.url + '.' - return new Response((error && error.message) || defaultMessage, { - status: 500, - headers: createHeaders(), - }) -} - // Polyfilled for `path-browserify` inside the Web Server. process.cwd = () => '' @@ -123,31 +108,20 @@ export function getRender({ const requestHandler = server.getRequestHandler() return async function render(request: NextRequest) { - const { nextUrl: url, cookies, headers } = request - const { pathname, searchParams } = url - + const { nextUrl: url } = request + const { searchParams } = url const query = Object.fromEntries(searchParams) - const req = { - url: pathname, - cookies, - headers: toNodeHeaders(headers), - } // Preflight request if (request.method === 'HEAD') { + // Hint the client that the matched route is a SSR page. return new Response(null, { - headers: createHeaders(), + headers: { + 'x-middleware-ssr': '1', + }, }) } - // @TODO: We should move this into server/render. - if (Document.getInitialProps) { - const err = new Error( - '`getInitialProps` in Document component is not supported with the Edge Runtime.' - ) - return sendError(req, err) - } - const renderServerComponentData = isServerComponent ? query.__flight__ !== undefined : false diff --git a/packages/next/server/dev/hot-reloader.ts b/packages/next/server/dev/hot-reloader.ts index 92847d189629c92..0c6454f8072bc64 100644 --- a/packages/next/server/dev/hot-reloader.ts +++ b/packages/next/server/dev/hot-reloader.ts @@ -336,6 +336,8 @@ export default class HotReloader { ) ) + const hasEdgeRuntimePages = this.runtime === 'edge' + return webpackConfigSpan .traceChild('generate-webpack-config') .traceAsyncFn(() => @@ -363,7 +365,7 @@ export default class HotReloader { }), // For the edge runtime, we need an extra compiler to generate the // web-targeted server bundle for now. - this.runtime === 'edge' + hasEdgeRuntimePages ? getBaseWebpackConfig(this.dir, { dev: true, isServer: true, diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index c5be24e7da9c543..e4f121fdcd6148c 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -1,10 +1,29 @@ -import { IncomingMessage, ServerResponse } from 'http' -import { ParsedUrlQuery, stringify as stringifyQuery } from 'querystring' +import type { IncomingMessage, ServerResponse } from 'http' +import type { NextRouter } from '../shared/lib/router/router' +import type { HtmlProps } from '../shared/lib/html-context' +import type { DomainLocale } from './config' +import type { + AppType, + DocumentInitialProps, + DocumentProps, + DocumentContext, + NextComponentType, + RenderPage, + RenderPageResult, +} from '../shared/lib/utils' +import type { ImageConfigComplete } from './image-config' +import type { Redirect } from '../lib/load-custom-routes' +import type { NextApiRequestCookies, __ApiPreviewProps } from './api-utils' +import type { FontManifest } from './font-utils' +import type { LoadComponentsReturnType, ManifestItem } from './load-components' +import type { GetServerSideProps, GetStaticProps, PreviewData } from '../types' +import type { UnwrapPromise } from '../lib/coalesced-function' + import React from 'react' +import { ParsedUrlQuery, stringify as stringifyQuery } from 'querystring' import { createFromReadableStream } from 'next/dist/compiled/react-server-dom-webpack' import { renderToReadableStream } from 'next/dist/compiled/react-server-dom-webpack/writer.browser.server' import { StyleRegistry, createStyleRegistry } from 'styled-jsx' -import { UnwrapPromise } from '../lib/coalesced-function' import { GSP_NO_RETURNED_VALUE, GSSP_COMPONENT_MEMBER_ERROR, @@ -15,56 +34,38 @@ import { SSG_GET_INITIAL_PROPS_CONFLICT, UNSTABLE_REVALIDATE_RENAME_ERROR, } from '../lib/constants' -import { isSerializableProps } from '../lib/is-serializable-props' -import { GetServerSideProps, GetStaticProps, PreviewData } from '../types' -import { isInAmpMode } from '../shared/lib/amp' -import { AmpStateContext } from '../shared/lib/amp-context' import { SERVER_PROPS_ID, STATIC_PROPS_ID, STATIC_STATUS_PAGES, } from '../shared/lib/constants' +import { isSerializableProps } from '../lib/is-serializable-props' +import { isInAmpMode } from '../shared/lib/amp' +import { AmpStateContext } from '../shared/lib/amp-context' import { defaultHead } from '../shared/lib/head' import { HeadManagerContext } from '../shared/lib/head-manager-context' import Loadable from '../shared/lib/loadable' import { LoadableContext } from '../shared/lib/loadable-context' import { RouterContext } from '../shared/lib/router-context' -import { NextRouter } from '../shared/lib/router/router' import { isDynamicRoute } from '../shared/lib/router/utils/is-dynamic' import { - AppType, ComponentsEnhancer, - DocumentInitialProps, - DocumentProps, - DocumentContext, getDisplayName, isResSent, loadGetInitialProps, - NextComponentType, - 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' -import type { LoadComponentsReturnType, ManifestItem } from './load-components' import { normalizePagePath } from './normalize-page-path' import { getRequestMeta, NextParsedUrlQuery } from './request-meta' import { allowedStatusCodes, getRedirectStatus, - Redirect, } from '../lib/load-custom-routes' -import { DomainLocale } from './config' import RenderResult from './render-result' import isError from '../lib/is-error' import { readableStreamTee } from './web/utils' import { ImageConfigContext } from '../shared/lib/image-config-context' -import { ImageConfigComplete } from './image-config' import { FlushEffectsContext } from '../shared/lib/flush-effects' let optimizeAmp: typeof import('./optimize-amp').default @@ -1248,6 +1249,13 @@ export async function renderToHTML( */ const generateStaticHTML = supportsDynamicHTML !== true const renderDocument = async () => { + if (runtime === 'edge' && Document.getInitialProps) { + // In the Edge runtime, Document.getInitialProps isn't supported. + throw new Error( + '`getInitialProps` in Document component is not supported with the Edge Runtime.' + ) + } + if (!runtime && Document.getInitialProps) { const renderPage: RenderPage = ( options: ComponentsEnhancer = {} diff --git a/test/integration/react-streaming-and-server-components/test/index.test.js b/test/integration/react-streaming-and-server-components/test/index.test.js index 8f8e7e0fb805ffc..826ca4d5d1428a5 100644 --- a/test/integration/react-streaming-and-server-components/test/index.test.js +++ b/test/integration/react-streaming-and-server-components/test/index.test.js @@ -247,12 +247,7 @@ const documentSuite = { runTests: (context) => { it('should error when custom _document has getInitialProps method', async () => { const res = await fetchViaHTTP(context.appPort, '/') - const html = await res.text() - expect(res.status).toBe(500) - expect(html).toContain( - '`getInitialProps` in Document component is not supported with the Edge Runtime.' - ) }) }, beforeAll: () => documentPage.write(documentWithGip), From 94ad03385b3ff55fa8e9c6434bde1d2bcb90ef73 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Sat, 19 Feb 2022 23:26:54 +0100 Subject: [PATCH 02/10] remove commons chunk config (#34445) This removes the config for the `commons` chunk. I think the idea was that modules are that in all pages are put into a `commons` chunk, but that breaks when next/dynamic comes into play, which also creates chunks. So the `totalPages` condition is broken and could lead to too many modules placed into the commons chunk. Example: 2 pages, each has one next/dynamic. Both on demand chunks include module A. page 1 includes module B and next/dynamic on page 2 includes module B. A and B are placed into commons. commonjs chunk is loaded in page 1 and both next/dynamic. Page 1 would load module A even while it doesn't need it. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint` --- packages/next/build/webpack-config.ts | 5 ----- test/.stats-app/stats-config.js | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index d54d3c9f8c2bd23..f030cf3139e4337 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -791,11 +791,6 @@ export default async function getBaseWebpackConfig( minChunks: 1, reuseExistingChunk: true, }, - commons: { - name: 'commons', - minChunks: totalPages, - priority: 20, - }, middleware: { chunks: (chunk: webpack.compilation.Chunk) => chunk.name?.match(MIDDLEWARE_ROUTE), diff --git a/test/.stats-app/stats-config.js b/test/.stats-app/stats-config.js index 82741d746daad4b..7996360ebeb5040 100644 --- a/test/.stats-app/stats-config.js +++ b/test/.stats-app/stats-config.js @@ -9,7 +9,7 @@ const imagePageData = fs.readFileSync( const clientGlobs = [ { - name: 'Client Bundles (main, webpack, commons)', + name: 'Client Bundles (main, webpack)', globs: [ '.next/static/runtime/+(main|webpack)-*', '.next/static/chunks/!(polyfills*)', From bf8b835e2e1fd4b8364d7411d630d139478e8ea5 Mon Sep 17 00:00:00 2001 From: Amirhossein Beigi Date: Sat, 19 Feb 2022 14:33:32 -0800 Subject: [PATCH 03/10] fix: change showcase url (#34590) ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint` --- docs/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index cd97832dfbcf5b1..fe9ca683803402b 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -7,7 +7,7 @@ description: Get to know more about Next.js with the frequently asked questions.
Is Next.js production ready?

Yes! Next.js is used by many of the top websites in the world. See the - Showcase for more info.

+ Showcase for more info.

From 5a16b1a26f6c213776deed9ac465e2bc81cdf5f3 Mon Sep 17 00:00:00 2001 From: Abhishek Garg <40450554+marshalcode@users.noreply.github.com> Date: Mon, 21 Feb 2022 05:54:38 +0530 Subject: [PATCH 04/10] removing redundant/unnecessary lines as these are defaults (#34587) ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint` --- examples/with-tailwindcss/prettier.config.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/with-tailwindcss/prettier.config.js b/examples/with-tailwindcss/prettier.config.js index f18d82f4928bd2a..3f714ac456ec4e6 100644 --- a/examples/with-tailwindcss/prettier.config.js +++ b/examples/with-tailwindcss/prettier.config.js @@ -1,6 +1,4 @@ module.exports = { - arrowParens: 'always', singleQuote: true, - tabWidth: 2, semi: false, } From 0243e6e95d9d5bed451eb70cd6b6b3d70d204c7f Mon Sep 17 00:00:00 2001 From: LongYinan Date: Mon, 21 Feb 2022 17:24:49 +0800 Subject: [PATCH 05/10] Add `cargo clippy` and `cargo fmt` to CI (#34625) Run `cargo fmt` and `cargo clippy` and add them to CI. --- .github/workflows/build_test_deploy.yml | 42 +++++- .../crates/core/src/amp_attributes.rs | 90 ++++++------- .../next-swc/crates/core/src/auto_cjs/mod.rs | 16 +-- .../src/disallow_re_export_all_in_page.rs | 5 +- .../crates/core/src/hook_optimizer.rs | 25 ++-- packages/next-swc/crates/core/src/lib.rs | 12 +- .../next-swc/crates/core/src/next_dynamic.rs | 4 +- packages/next-swc/crates/core/src/next_ssg.rs | 124 +++++++----------- .../next-swc/crates/core/src/page_config.rs | 97 +++++++------- .../core/src/react_remove_properties.rs | 14 +- packages/next-swc/crates/core/src/relay.rs | 16 +-- .../crates/core/src/remove_console.rs | 28 ++-- .../next-swc/crates/core/src/shake_exports.rs | 4 +- .../crates/core/src/styled_jsx/mod.rs | 103 +++++++-------- .../core/src/styled_jsx/transform_css.rs | 56 ++++---- .../crates/core/src/styled_jsx/utils.rs | 10 +- packages/next-swc/crates/napi/build.rs | 2 +- .../next-swc/crates/napi/src/bundle/mod.rs | 10 +- packages/next-swc/crates/napi/src/lib.rs | 8 +- packages/next-swc/crates/napi/src/minify.rs | 6 +- packages/next-swc/crates/napi/src/parse.rs | 4 +- .../next-swc/crates/napi/src/transform.rs | 8 +- packages/next-swc/crates/napi/src/util.rs | 4 +- packages/next-swc/crates/wasm/src/lib.rs | 10 +- 24 files changed, 334 insertions(+), 364 deletions(-) diff --git a/.github/workflows/build_test_deploy.yml b/.github/workflows/build_test_deploy.yml index e2b02075f956a6d..ef3e5b93f3ccfd5 100644 --- a/.github/workflows/build_test_deploy.yml +++ b/.github/workflows/build_test_deploy.yml @@ -88,6 +88,44 @@ jobs: - run: ./scripts/check-manifests.js - run: yarn lint + rust-check: + runs-on: ubuntu-latest + needs: build + steps: + - name: Install + uses: actions-rs/toolchain@v1 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + profile: minimal + toolchain: nightly-2021-11-15 + components: rustfmt, clippy + + - name: Cache cargo registry + uses: actions/cache@v2 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + path: ~/.cargo/registry + key: stable-ubuntu-clippy-cargo-registry + + - name: Cache cargo index + uses: actions/cache@v2 + if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} + with: + path: ~/.cargo/git + key: stable-ubuntu-clippy-cargo-index + + - uses: actions/cache@v2 + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }} + + - name: Check + run: | + cargo fmt -- --check + cargo clippy --all -- -D warnings + working-directory: packages/next-swc + checkPrecompiled: name: Check Pre-compiled runs-on: ubuntu-latest @@ -610,14 +648,14 @@ jobs: toolchain: nightly-2021-11-15 - name: Cache cargo registry - uses: actions/cache@v1 + uses: actions/cache@v2 if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} with: path: ~/.cargo/registry key: stable-ubuntu-18.04-cargo-registry - name: Cache cargo index - uses: actions/cache@v1 + uses: actions/cache@v2 if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} with: path: ~/.cargo/git diff --git a/packages/next-swc/crates/core/src/amp_attributes.rs b/packages/next-swc/crates/core/src/amp_attributes.rs index b222d019d4a4e55..a5935641edd92eb 100644 --- a/packages/next-swc/crates/core/src/amp_attributes.rs +++ b/packages/next-swc/crates/core/src/amp_attributes.rs @@ -1,63 +1,63 @@ use swc_atoms::JsWord; use swc_ecmascript::ast::{ - Ident, JSXAttr, JSXAttrName, JSXAttrOrSpread, JSXElementName, JSXOpeningElement, + Ident, JSXAttr, JSXAttrName, JSXAttrOrSpread, JSXElementName, JSXOpeningElement, }; use swc_ecmascript::visit::Fold; pub fn amp_attributes() -> impl Fold { - AmpAttributePatcher::default() + AmpAttributePatcher::default() } #[derive(Debug, Default)] struct AmpAttributePatcher {} impl Fold for AmpAttributePatcher { - fn fold_jsx_opening_element(&mut self, node: JSXOpeningElement) -> JSXOpeningElement { - let JSXOpeningElement { - name, - mut attrs, - span, - self_closing, - type_args, - } = node; - let n = name.clone(); - - if let JSXElementName::Ident(Ident { sym, .. }) = name { - if sym.starts_with("amp-") { - for i in 0..attrs.len() { - if let JSXAttrOrSpread::JSXAttr(JSXAttr { - name: - JSXAttrName::Ident(Ident { - sym, - span: s, - optional: o, - }), + fn fold_jsx_opening_element(&mut self, node: JSXOpeningElement) -> JSXOpeningElement { + let JSXOpeningElement { + name, + mut attrs, span, - value, - }) = &attrs[i] - { - if sym as &str == "className" { - attrs[i] = JSXAttrOrSpread::JSXAttr(JSXAttr { - name: JSXAttrName::Ident(Ident { - sym: JsWord::from("class"), - span: s.clone(), - optional: o.clone(), - }), - span: span.clone(), - value: value.clone(), - }) + self_closing, + type_args, + } = node; + let n = name.clone(); + + if let JSXElementName::Ident(Ident { sym, .. }) = name { + if sym.starts_with("amp-") { + for i in &mut attrs { + if let JSXAttrOrSpread::JSXAttr(JSXAttr { + name: + JSXAttrName::Ident(Ident { + sym, + span: s, + optional: o, + }), + span, + value, + }) = &i + { + if sym as &str == "className" { + *i = JSXAttrOrSpread::JSXAttr(JSXAttr { + name: JSXAttrName::Ident(Ident { + sym: JsWord::from("class"), + span: *s, + optional: *o, + }), + span: *span, + value: value.clone(), + }) + } + } + } } - } } - } - } - JSXOpeningElement { - name: n, - attrs, - span, - self_closing, - type_args, + JSXOpeningElement { + name: n, + attrs, + span, + self_closing, + type_args, + } } - } } diff --git a/packages/next-swc/crates/core/src/auto_cjs/mod.rs b/packages/next-swc/crates/core/src/auto_cjs/mod.rs index 3c885940fd778e6..5f35aa8cc1b7914 100644 --- a/packages/next-swc/crates/core/src/auto_cjs/mod.rs +++ b/packages/next-swc/crates/core/src/auto_cjs/mod.rs @@ -18,17 +18,13 @@ struct CjsFinder { /// does not support changing configuration based on content of the file. impl Visit for CjsFinder { fn visit_member_expr(&mut self, e: &MemberExpr) { - match &*e.obj { - Expr::Ident(obj) => match &e.prop { - MemberProp::Ident(prop) => { - if &*obj.sym == "module" && &*prop.sym == "exports" { - self.found = true; - return; - } + if let Expr::Ident(obj) = &*e.obj { + if let MemberProp::Ident(prop) = &e.prop { + if &*obj.sym == "module" && &*prop.sym == "exports" { + self.found = true; + return; } - _ => {} - }, - _ => {} + } } e.obj.visit_with(self); diff --git a/packages/next-swc/crates/core/src/disallow_re_export_all_in_page.rs b/packages/next-swc/crates/core/src/disallow_re_export_all_in_page.rs index 34f1220232f3708..78983a8b32f7248 100644 --- a/packages/next-swc/crates/core/src/disallow_re_export_all_in_page.rs +++ b/packages/next-swc/crates/core/src/disallow_re_export_all_in_page.rs @@ -4,10 +4,7 @@ use swc_ecmascript::utils::HANDLER; use swc_ecmascript::visit::{noop_fold_type, Fold}; pub fn disallow_re_export_all_in_page(is_page_file: bool) -> impl Fold { - Optional::new( - DisallowReExportAllInPage, - is_page_file - ) + Optional::new(DisallowReExportAllInPage, is_page_file) } struct DisallowReExportAllInPage; diff --git a/packages/next-swc/crates/core/src/hook_optimizer.rs b/packages/next-swc/crates/core/src/hook_optimizer.rs index 0516fc8e1b48e9c..7b0726ca2256f13 100644 --- a/packages/next-swc/crates/core/src/hook_optimizer.rs +++ b/packages/next-swc/crates/core/src/hook_optimizer.rs @@ -75,8 +75,8 @@ impl HookOptimizer { if let Expr::Call(c) = &*init.as_deref().unwrap() { if let Callee::Expr(i) = &c.callee { if let Expr::Ident(Ident { sym, .. }) = &**i { - if self.hooks.contains(&sym) { - let name = get_object_pattern(&a); + if self.hooks.contains(sym) { + let name = get_object_pattern(a); return VarDeclarator { name, init: init_clone, @@ -89,7 +89,7 @@ impl HookOptimizer { } } - return decl; + decl } } @@ -98,15 +98,16 @@ fn get_object_pattern(array_pattern: &ArrayPat) -> Pat { .elems .iter() .enumerate() - .filter_map(|(i, elem)| match elem { - Some(elem) => Some(ObjectPatProp::KeyValue(KeyValuePatProp { - key: PropName::Num(Number { - value: i as f64, - span: DUMMY_SP, - }), - value: Box::new(elem.clone()), - })), - None => None, + .filter_map(|(i, elem)| { + elem.as_ref().map(|elem| { + ObjectPatProp::KeyValue(KeyValuePatProp { + key: PropName::Num(Number { + value: i as f64, + span: DUMMY_SP, + }), + value: Box::new(elem.clone()), + }) + }) }) .collect(); diff --git a/packages/next-swc/crates/core/src/lib.rs b/packages/next-swc/crates/core/src/lib.rs index 7d4847998dbb87d..6e788b3cd000a27 100644 --- a/packages/next-swc/crates/core/src/lib.rs +++ b/packages/next-swc/crates/core/src/lib.rs @@ -27,7 +27,7 @@ DEALINGS IN THE SOFTWARE. */ #![recursion_limit = "2048"] -//#![deny(clippy::all)] +#![deny(clippy::all)] use auto_cjs::contains_cjs; use either::Either; @@ -112,7 +112,11 @@ pub fn custom_before_pass( #[cfg(not(target_arch = "wasm32"))] let relay_plugin = { if let Some(config) = &opts.relay { - Either::Left(relay::relay(config, file.name.clone(), opts.pages_dir.clone())) + Either::Left(relay::relay( + config, + file.name.clone(), + opts.pages_dir.clone(), + )) } else { Either::Right(noop()) } @@ -120,7 +124,7 @@ pub fn custom_before_pass( chain!( disallow_re_export_all_in_page::disallow_re_export_all_in_page(opts.is_page_file), - styled_jsx::styled_jsx(cm.clone(), file.name.clone()), + styled_jsx::styled_jsx(cm, file.name.clone()), hook_optimizer::hook_optimizer(), match &opts.styled_components { Some(config) => { @@ -173,7 +177,7 @@ impl TransformOptions { let should_enable_commonjs = self.swc.config.module.is_none() && fm.src.contains("module.exports") && { let syntax = self.swc.config.jsc.syntax.unwrap_or_default(); - let target = self.swc.config.jsc.target.unwrap_or(EsVersion::latest()); + let target = self.swc.config.jsc.target.unwrap_or_else(EsVersion::latest); let lexer = Lexer::new(syntax, target, StringInput::from(&*fm), None); let mut p = Parser::new_from(lexer); p.parse_module() diff --git a/packages/next-swc/crates/core/src/next_dynamic.rs b/packages/next-swc/crates/core/src/next_dynamic.rs index 01bff29ad322b53..87b30f968e5dbca 100644 --- a/packages/next-swc/crates/core/src/next_dynamic.rs +++ b/packages/next-swc/crates/core/src/next_dynamic.rs @@ -74,7 +74,7 @@ impl Fold for NextDynamicPatcher { if let Callee::Expr(i) = &expr.callee { if let Expr::Ident(identifier) = &**i { if self.dynamic_bindings.contains(&identifier.to_id()) { - if expr.args.len() == 0 { + if expr.args.is_empty() { HANDLER.with(|handler| { handler .struct_span_err( @@ -116,7 +116,7 @@ impl Fold for NextDynamicPatcher { expr.args[0].expr = expr.args[0].expr.clone().fold_with(self); self.is_next_dynamic_first_arg = false; - if let None = self.dynamically_imported_specifier { + if self.dynamically_imported_specifier.is_none() { return expr; } diff --git a/packages/next-swc/crates/core/src/next_ssg.rs b/packages/next-swc/crates/core/src/next_ssg.rs index f744bc7e4e78f53..a4f389ce967a318 100644 --- a/packages/next-swc/crates/core/src/next_ssg.rs +++ b/packages/next-swc/crates/core/src/next_ssg.rs @@ -131,11 +131,8 @@ impl Fold for Analyzer<'_> { fn fold_expr(&mut self, e: Expr) -> Expr { let e = e.fold_children_with(self); - match &e { - Expr::Ident(i) => { - self.add_ref(i.to_id()); - } - _ => {} + if let Expr::Ident(i) = &e { + self.add_ref(i.to_id()); } e @@ -216,8 +213,8 @@ impl Fold for Analyzer<'_> { // Visit children to ensure that all references is added to the scope. let s = s.fold_children_with(self); - match &s { - ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(e)) => match &e.decl { + if let ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(e)) = &s { + match &e.decl { Decl::Fn(f) => { // Drop getStaticProps. if let Ok(is_data_identifier) = self.state.is_data_identifier(&f.ident) { @@ -235,9 +232,7 @@ impl Fold for Analyzer<'_> { } } _ => {} - }, - - _ => {} + } } s @@ -254,11 +249,8 @@ impl Fold for Analyzer<'_> { fn fold_prop(&mut self, p: Prop) -> Prop { let p = p.fold_children_with(self); - match &p { - Prop::Shorthand(i) => { - self.add_ref(i.to_id()); - } - _ => {} + if let Prop::Shorthand(i) = &p { + self.add_ref(i.to_id()); } p @@ -267,17 +259,14 @@ impl Fold for Analyzer<'_> { fn fold_var_declarator(&mut self, mut v: VarDeclarator) -> VarDeclarator { let old_in_data = self.in_data_fn; - match &v.name { - Pat::Ident(name) => { - if let Ok(is_data_identifier) = self.state.is_data_identifier(&name.id) { - if is_data_identifier { - self.in_data_fn = true; - } - } else { - return v; + if let Pat::Ident(name) = &v.name { + if let Ok(is_data_identifier) = self.state.is_data_identifier(&name.id) { + if is_data_identifier { + self.in_data_fn = true; } + } else { + return v; } - _ => {} } let old_in_lhs_of_var = self.in_lhs_of_var; @@ -397,18 +386,15 @@ impl Fold for NextSsg { } fn fold_module_item(&mut self, i: ModuleItem) -> ModuleItem { - match i { - ModuleItem::ModuleDecl(ModuleDecl::Import(i)) => { - let is_for_side_effect = i.specifiers.is_empty(); - let i = i.fold_with(self); - - if !is_for_side_effect && i.specifiers.is_empty() { - return ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP })); - } + if let ModuleItem::ModuleDecl(ModuleDecl::Import(i)) = i { + let is_for_side_effect = i.specifiers.is_empty(); + let i = i.fold_with(self); - return ModuleItem::ModuleDecl(ModuleDecl::Import(i)); + if !is_for_side_effect && i.specifiers.is_empty() { + return ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP })); } - _ => {} + + return ModuleItem::ModuleDecl(ModuleDecl::Import(i)); } let i = i.fold_children_with(self); @@ -427,10 +413,7 @@ impl Fold for NextSsg { items = items.fold_children_with(self); // Drop nodes. - items.retain(|s| match s { - ModuleItem::Stmt(Stmt::Empty(..)) => false, - _ => true, - }); + items.retain(|s| !matches!(s, ModuleItem::Stmt(Stmt::Empty(..)))); if !self.state.done && !self.state.should_run_again @@ -461,29 +444,24 @@ impl Fold for NextSsg { let mut new = Vec::with_capacity(items.len() + 1); for item in take(&mut items) { - match &item { - ModuleItem::ModuleDecl( - ModuleDecl::ExportNamed(..) - | ModuleDecl::ExportDecl(..) - | ModuleDecl::ExportDefaultDecl(..) - | ModuleDecl::ExportDefaultExpr(..), - ) => { - if let Some(var) = var.take() { - new.push(ModuleItem::ModuleDecl(ModuleDecl::ExportDecl( - ExportDecl { - span: DUMMY_SP, - decl: Decl::Var(VarDecl { - span: DUMMY_SP, - kind: VarDeclKind::Var, - declare: Default::default(), - decls: vec![var], - }), - }, - ))) - } + if let ModuleItem::ModuleDecl( + ModuleDecl::ExportNamed(..) + | ModuleDecl::ExportDecl(..) + | ModuleDecl::ExportDefaultDecl(..) + | ModuleDecl::ExportDefaultExpr(..), + ) = &item + { + if let Some(var) = var.take() { + new.push(ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { + span: DUMMY_SP, + decl: Decl::Var(VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Var, + declare: Default::default(), + decls: vec![var], + }), + }))) } - - _ => {} } new.push(item); @@ -511,14 +489,14 @@ impl Fold for NextSsg { .. }) => self .state - .is_data_identifier(&exported) + .is_data_identifier(exported) .map(|is_data_identifier| !is_data_identifier), ExportSpecifier::Named(ExportNamedSpecifier { orig: ModuleExportName::Ident(orig), .. }) => self .state - .is_data_identifier(&orig) + .is_data_identifier(orig) .map(|is_data_identifier| !is_data_identifier), _ => Ok(true), @@ -528,15 +506,13 @@ impl Fold for NextSsg { Ok(false) => { tracing::trace!("Dropping a export specifier because it's a data identifier"); - match s { - ExportSpecifier::Named(ExportNamedSpecifier { - orig: ModuleExportName::Ident(orig), - .. - }) => { - self.state.should_run_again = true; - self.state.refs_from_data_fn.insert(orig.to_id()); - } - _ => {} + if let ExportSpecifier::Named(ExportNamedSpecifier { + orig: ModuleExportName::Ident(orig), + .. + }) = s + { + self.state.should_run_again = true; + self.state.refs_from_data_fn.insert(orig.to_id()); } false @@ -569,10 +545,7 @@ impl Fold for NextSsg { } Pat::Array(arr) => { if !arr.elems.is_empty() { - arr.elems.retain(|e| match e { - Some(Pat::Invalid(..)) => return false, - _ => true, - }); + arr.elems.retain(|e| !matches!(e, Some(Pat::Invalid(..)))); if arr.elems.is_empty() { return Pat::Invalid(Invalid { span: DUMMY_SP }); @@ -627,6 +600,7 @@ impl Fold for NextSsg { p } + #[allow(clippy::single_match)] fn fold_stmt(&mut self, mut s: Stmt) -> Stmt { match s { Stmt::Decl(Decl::Fn(f)) => { diff --git a/packages/next-swc/crates/core/src/page_config.rs b/packages/next-swc/crates/core/src/page_config.rs index 0b214038a15f1ae..9175f4730ca83f1 100644 --- a/packages/next-swc/crates/core/src/page_config.rs +++ b/packages/next-swc/crates/core/src/page_config.rs @@ -71,76 +71,73 @@ impl Fold for PageConfig { } fn fold_export_decl(&mut self, export: ExportDecl) -> ExportDecl { - match &export.decl { - Decl::Var(var_decl) => { - for decl in &var_decl.decls { - let mut is_config = false; - if let Pat::Ident(ident) = &decl.name { - if &ident.id.sym == CONFIG_KEY { - is_config = true; - } + if let Decl::Var(var_decl) = &export.decl { + for decl in &var_decl.decls { + let mut is_config = false; + if let Pat::Ident(ident) = &decl.name { + if &ident.id.sym == CONFIG_KEY { + is_config = true; } + } - if is_config { - if let Some(expr) = &decl.init { - if let Expr::Object(obj) = &**expr { - for prop in &obj.props { - if let PropOrSpread::Prop(prop) = prop { - if let Prop::KeyValue(kv) = &**prop { - match &kv.key { - PropName::Ident(ident) => { - if &ident.sym == "amp" { - if let Expr::Lit(Lit::Bool(Bool { - value, - .. - })) = &*kv.value - { - if *value && self.is_page_file { - self.drop_bundle = true; - } - } else if let Expr::Lit(Lit::Str(_)) = - &*kv.value - { - // Do not replace - // bundle - } else { - self.handle_error( - "Invalid value found.", - export.span, - ); + if is_config { + if let Some(expr) = &decl.init { + if let Expr::Object(obj) = &**expr { + for prop in &obj.props { + if let PropOrSpread::Prop(prop) = prop { + if let Prop::KeyValue(kv) = &**prop { + match &kv.key { + PropName::Ident(ident) => { + if &ident.sym == "amp" { + if let Expr::Lit(Lit::Bool(Bool { + value, + .. + })) = &*kv.value + { + if *value && self.is_page_file { + self.drop_bundle = true; } + } else if let Expr::Lit(Lit::Str(_)) = + &*kv.value + { + // Do not replace + // bundle + } else { + self.handle_error( + "Invalid value found.", + export.span, + ); } } - _ => { - self.handle_error( - "Invalid property found.", - export.span, - ); - } } - } else { - self.handle_error( - "Invalid property or value.", - export.span, - ); + _ => { + self.handle_error( + "Invalid property found.", + export.span, + ); + } } } else { self.handle_error( - "Property spread is not allowed.", + "Invalid property or value.", export.span, ); } + } else { + self.handle_error( + "Property spread is not allowed.", + export.span, + ); } - } else { - self.handle_error("Expected config to be an object.", export.span); } } else { self.handle_error("Expected config to be an object.", export.span); } + } else { + self.handle_error("Expected config to be an object.", export.span); } } } - _ => {} } export } diff --git a/packages/next-swc/crates/core/src/react_remove_properties.rs b/packages/next-swc/crates/core/src/react_remove_properties.rs index 6f51f0b9a816ab6..f2944795537d980 100644 --- a/packages/next-swc/crates/core/src/react_remove_properties.rs +++ b/packages/next-swc/crates/core/src/react_remove_properties.rs @@ -39,12 +39,11 @@ impl Fold for RemoveProperties { noop_fold_type!(); fn fold_jsx_opening_element(&mut self, mut el: JSXOpeningElement) -> JSXOpeningElement { - el.attrs.retain(|attr| match attr { - JSXAttrOrSpread::JSXAttr(JSXAttr { - name: JSXAttrName::Ident(ident), - .. - }) if self.should_remove_property(ident.sym.as_ref()) => false, - _ => true, + el.attrs.retain(|attr| { + !matches!(attr, JSXAttrOrSpread::JSXAttr(JSXAttr { + name: JSXAttrName::Ident(ident), + .. + }) if self.should_remove_property(ident.sym.as_ref())) }); el.fold_children_with(self) } @@ -67,6 +66,5 @@ pub fn remove_properties(config: Config) -> impl Fold { // Keep the default regex identical to `babel-plugin-react-remove-properties`. properties.push(Regex::new(r"^data-test").unwrap()); } - let remover = RemoveProperties { properties }; - remover + RemoveProperties { properties } } diff --git a/packages/next-swc/crates/core/src/relay.rs b/packages/next-swc/crates/core/src/relay.rs index b9035197bbb2b6d..680eceb4de2dbf7 100644 --- a/packages/next-swc/crates/core/src/relay.rs +++ b/packages/next-swc/crates/core/src/relay.rs @@ -53,15 +53,13 @@ fn build_require_expr_from_path(path: &str) -> Expr { Expr::Call(CallExpr { span: Default::default(), callee: quote_ident!("require").as_callee(), - args: vec![ - Lit::Str(Str { - span: Default::default(), - value: JsWord::from(path), - has_escape: false, - kind: Default::default(), - }) - .as_arg(), - ], + args: vec![Lit::Str(Str { + span: Default::default(), + value: JsWord::from(path), + has_escape: false, + kind: Default::default(), + }) + .as_arg()], type_args: None, }) } diff --git a/packages/next-swc/crates/core/src/remove_console.rs b/packages/next-swc/crates/core/src/remove_console.rs index 11238eb037e4e58..f2b3cb18683a312 100644 --- a/packages/next-swc/crates/core/src/remove_console.rs +++ b/packages/next-swc/crates/core/src/remove_console.rs @@ -39,12 +39,7 @@ struct RemoveConsole { impl RemoveConsole { fn is_global_console(&self, ident: &Ident) -> bool { - &ident.sym == "console" - && self - .bindings - .iter() - .find(|x| x.contains(&ident.to_id())) - .is_none() + &ident.sym == "console" && !self.bindings.iter().any(|x| x.contains(&ident.to_id())) } fn should_remove_call(&mut self, n: &CallExpr) -> bool { @@ -73,7 +68,7 @@ impl RemoveConsole { // Here we do an O(n) search on the list of excluded properties because the size // should be small. match &member_expr.prop { - MemberProp::Ident(i) if self.exclude.iter().find(|x| **x == i.sym).is_none() => {} + MemberProp::Ident(i) if !self.exclude.iter().any(|x| *x == i.sym) => {} _ => return false, } @@ -85,16 +80,12 @@ impl Fold for RemoveConsole { noop_fold_type!(); fn fold_stmt(&mut self, stmt: Stmt) -> Stmt { - match &stmt { - Stmt::Expr(e) => match &*e.expr { - Expr::Call(c) => { - if self.should_remove_call(c) { - return Stmt::Empty(EmptyStmt { span: DUMMY_SP }); - } + if let Stmt::Expr(e) = &stmt { + if let Expr::Call(c) = &*e.expr { + if self.should_remove_call(c) { + return Stmt::Empty(EmptyStmt { span: DUMMY_SP }); } - _ => {} - }, - _ => {} + } } stmt.fold_children_with(self) } @@ -135,10 +126,9 @@ pub fn remove_console(config: Config) -> impl Fold { Config::WithOptions(x) => x.exclude, _ => vec![], }; - let remover = RemoveConsole { + RemoveConsole { exclude, bindings: Default::default(), in_function_params: false, - }; - remover + } } diff --git a/packages/next-swc/crates/core/src/shake_exports.rs b/packages/next-swc/crates/core/src/shake_exports.rs index ba0e58ce89815bc..083e490967c2bec 100644 --- a/packages/next-swc/crates/core/src/shake_exports.rs +++ b/packages/next-swc/crates/core/src/shake_exports.rs @@ -26,9 +26,7 @@ struct ExportShaker { impl Fold for ExportShaker { fn fold_module(&mut self, module: Module) -> Module { let module = module.fold_children_with(self); - let module = module.fold_with(&mut dce(DCEConfig::default())); - - module + module.fold_with(&mut dce(DCEConfig::default())) } fn fold_module_items(&mut self, items: Vec) -> Vec { diff --git a/packages/next-swc/crates/core/src/styled_jsx/mod.rs b/packages/next-swc/crates/core/src/styled_jsx/mod.rs index a70666f40e321cd..21062867cd30463 100644 --- a/packages/next-swc/crates/core/src/styled_jsx/mod.rs +++ b/packages/next-swc/crates/core/src/styled_jsx/mod.rs @@ -26,10 +26,9 @@ mod utils; pub fn styled_jsx(cm: Arc, file_name: FileName) -> impl Fold { let file_name = match file_name { - FileName::Real(real_file_name) => match real_file_name.to_str() { - Some(real_file_name) => Some(real_file_name.to_string()), - None => None, - }, + FileName::Real(real_file_name) => real_file_name + .to_str() + .map(|real_file_name| real_file_name.to_string()), _ => None, }; @@ -81,6 +80,7 @@ pub struct LocalStyle { css: String, css_span: Span, is_dynamic: bool, + #[allow(clippy::vec_box)] expressions: Vec>, } @@ -105,10 +105,9 @@ impl Fold for StyledJSXTransformer { fn fold_jsx_element(&mut self, el: JSXElement) -> JSXElement { if is_styled_jsx(&el) { let parent_has_styled_jsx = self.has_styled_jsx; - if !parent_has_styled_jsx { - if self.check_for_jsx_styles(Some(&el), &el.children).is_err() { - return el; - } + if !parent_has_styled_jsx && self.check_for_jsx_styles(Some(&el), &el.children).is_err() + { + return el; } let el = match self.replace_jsx_style(&el) { Ok(el) => el, @@ -260,14 +259,12 @@ impl Fold for StyledJSXTransformer { fn fold_var_declarator(&mut self, declarator: VarDeclarator) -> VarDeclarator { let declarator = declarator.fold_children_with(self); if let Some(external_hash) = &self.external_hash.take() { - match &declarator.name { - Pat::Ident(BindingIdent { - id: Ident { span, sym, .. }, - .. - }) => { - self.add_hash = Some(((sym.clone(), span.ctxt), external_hash.clone())); - } - _ => {} + if let Pat::Ident(BindingIdent { + id: Ident { span, sym, .. }, + .. + }) = &declarator.name + { + self.add_hash = Some(((sym.clone(), span.ctxt), external_hash.clone())); } } declarator @@ -339,7 +336,7 @@ impl Fold for StyledJSXTransformer { if self.file_has_styled_jsx || self.file_has_css_resolve { prepend( &mut new_items, - styled_jsx_import_decl(&self.style_import_name.as_ref().unwrap()), + styled_jsx_import_decl(self.style_import_name.as_ref().unwrap()), ); } @@ -361,7 +358,7 @@ impl Fold for StyledJSXTransformer { } fn fold_function(&mut self, mut func: Function) -> Function { - self.func_scope_level = self.func_scope_level + 1; + self.func_scope_level += 1; let surrounding_scope_bindings = take(&mut self.nearest_scope_bindings); self.in_function_params = true; let mut new_params = vec![]; @@ -373,12 +370,12 @@ impl Fold for StyledJSXTransformer { self.nearest_scope_bindings.extend(collect_decls(&func)); func.body = func.body.fold_with(self); self.nearest_scope_bindings = surrounding_scope_bindings; - self.func_scope_level = self.func_scope_level - 1; + self.func_scope_level -= 1; func } fn fold_arrow_expr(&mut self, mut func: ArrowExpr) -> ArrowExpr { - self.func_scope_level = self.func_scope_level + 1; + self.func_scope_level += 1; let surrounding_scope_bindings = take(&mut self.nearest_scope_bindings); self.in_function_params = true; let mut new_params = vec![]; @@ -390,7 +387,7 @@ impl Fold for StyledJSXTransformer { self.nearest_scope_bindings.extend(collect_decls(&func)); func.body = func.body.fold_with(self); self.nearest_scope_bindings = surrounding_scope_bindings; - self.func_scope_level = self.func_scope_level - 1; + self.func_scope_level -= 1; func } @@ -436,10 +433,10 @@ impl StyledJSXTransformer { if el.is_some() && is_styled_jsx(el.unwrap()) { process_style(el.unwrap())?; } else { - for i in 0..children.len() { - if let JSXElementChild::JSXElement(child_el) = &children[i] { - if is_styled_jsx(&child_el) { - process_style(&child_el)?; + for i in children { + if let JSXElementChild::JSXElement(child_el) = &i { + if is_styled_jsx(child_el) { + process_style(child_el)?; } } } @@ -465,8 +462,8 @@ impl StyledJSXTransformer { match style_expr { StyleExpr::Str(Str { value, span, .. }) => { hasher.write(value.as_ref().as_bytes()); - css = value.to_string().clone(); - css_span = span.clone(); + css = value.to_string(); + css_span = *span; is_dynamic = false; } StyleExpr::Tpl( @@ -480,7 +477,7 @@ impl StyledJSXTransformer { if exprs.is_empty() { hasher.write(quasis[0].raw.value.as_bytes()); css = quasis[0].raw.value.to_string(); - css_span = span.clone(); + css_span = *span; is_dynamic = false; } else { drop_span(expr.clone()).hash(&mut hasher); @@ -493,15 +490,11 @@ impl StyledJSXTransformer { }; s = format!("{}{}{}", s, quasis[i].raw.value, placeholder) } - css = String::from(s); + css = s; css_span = *span; is_dynamic = if self.func_scope_level > 0 { - let res = self.evaluator.as_mut().unwrap().eval(&expr); - if let Some(EvalResult::Lit(_)) = res { - false - } else { - true - } + let res = self.evaluator.as_mut().unwrap().eval(expr); + !matches!(res, Some(EvalResult::Lit(_))) } else { false }; @@ -554,12 +547,12 @@ impl StyledJSXTransformer { JSXStyle::Local(style_info) => { let css = transform_css( self.cm.clone(), - &style_info, + style_info, is_global, &self.static_class_name, )?; Ok(make_local_styled_jsx_el( - &style_info, + style_info, css, self.style_import_name.as_ref().unwrap(), self.static_class_name.as_ref(), @@ -589,7 +582,7 @@ impl StyledJSXTransformer { ); let styles = vec![style]; let (static_class_name, class_name) = - compute_class_names(&styles, &self.style_import_name.as_ref().unwrap()); + compute_class_names(&styles, self.style_import_name.as_ref().unwrap()); let tag = match &*tagged_tpl.tag { Expr::Ident(Ident { sym, .. }) => sym.to_string(), Expr::Member(MemberExpr { @@ -606,7 +599,7 @@ impl StyledJSXTransformer { } else { bail!("This shouldn't happen, we already know that this is a template literal"); }; - let css = transform_css(self.cm.clone(), &style, tag == "global", &static_class_name)?; + let css = transform_css(self.cm.clone(), style, tag == "global", &static_class_name)?; if tag == "resolve" { self.file_has_css_resolve = true; return Ok(Expr::Object(ObjectLit { @@ -618,9 +611,9 @@ impl StyledJSXTransformer { optional: false, }), value: Box::new(Expr::JSXElement(Box::new(make_local_styled_jsx_el( - &style, + style, css, - &self.style_import_name.as_ref().unwrap(), + self.style_import_name.as_ref().unwrap(), self.static_class_name.as_ref(), )))), }))), @@ -796,14 +789,16 @@ fn get_existing_class_name(el: &JSXOpeningElement) -> (Option, Option (Option, Option true, - Expr::Ident(_) => true, - _ => false, - }; + let valid_spread = matches!(&**expr, Expr::Member(_) | Expr::Ident(_)); if valid_spread { let member_dot_name = Expr::Member(MemberExpr { @@ -875,11 +866,11 @@ fn get_existing_class_name(el: &JSXOpeningElement) -> (Option, Option) -> Expr { let mut new_expr = spreads[0].clone(); - for i in 1..spreads.len() { + for i in spreads.iter().skip(1) { new_expr = Expr::Bin(BinExpr { op: op!("||"), left: Box::new(new_expr.clone()), - right: Box::new(spreads[i].clone()), + right: Box::new(i.clone()), span: DUMMY_SP, }) } 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 d25cd05e634967a..32e9850857e7710 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 @@ -44,7 +44,7 @@ pub fn transform_css( Err(err) => { HANDLER.with(|handler| { // Print css parsing errors - err.to_diagnostics(&handler).emit(); + err.to_diagnostics(handler).emit(); // TODO(kdy1): We may print css so the user can see the error, and report it. @@ -78,17 +78,17 @@ pub fn transform_css( gen.emit(&ss).unwrap(); } - if style_info.expressions.len() == 0 { + if style_info.expressions.is_empty() { return Ok(string_literal_expr(&s)); } let mut parts: Vec<&str> = s.split("__styled-jsx-placeholder-").collect(); let mut final_expressions = vec![]; - for i in 1..parts.len() { - let (num_len, expression_index) = read_number(&parts[i]); + for i in parts.iter_mut().skip(1) { + let (num_len, expression_index) = read_number(i); final_expressions.push(style_info.expressions[expression_index].clone()); - let substr = &parts[i][(num_len + 2)..]; - parts[i] = substr; + let substr = &i[(num_len + 2)..]; + *i = substr; } Ok(Expr::Tpl(Tpl { @@ -260,24 +260,19 @@ impl Namespacer { &mut vec![], ) .unwrap(); - return x; + x }); return match complex_selectors { Ok(complex_selectors) => { - let mut v = complex_selectors.children[1..] - .iter() - .cloned() - .collect::>(); - - match v[0] { - ComplexSelectorChildren::Combinator(Combinator { - value: CombinatorValue::Descendant, - .. - }) => { - v.remove(0); - } - _ => {} + let mut v = complex_selectors.children[1..].to_vec(); + + if let ComplexSelectorChildren::Combinator(Combinator { + value: CombinatorValue::Descendant, + .. + }) = v[0] + { + v.remove(0); } if v.is_empty() { @@ -287,27 +282,21 @@ impl Namespacer { trace!("Combinator: {:?}", combinator); trace!("v[0]: {:?}", v[0]); - if combinator.is_some() { + if let Some(combinator) = combinator { match v.get(0) { Some(ComplexSelectorChildren::Combinator(..)) => {} Some(..) => {} _ => { - v.push(ComplexSelectorChildren::Combinator( - combinator.unwrap(), - )); + v.push(ComplexSelectorChildren::Combinator(combinator)); } } } v.iter_mut().for_each(|sel| { if i < node.subclass_selectors.len() { - match sel { - ComplexSelectorChildren::CompoundSelector(sel) => { - sel.subclass_selectors.extend( - node.subclass_selectors[i + 1..].iter().cloned(), - ); - } - _ => {} + if let ComplexSelectorChildren::CompoundSelector(sel) = sel { + sel.subclass_selectors + .extend(node.subclass_selectors[i + 1..].iter().cloned()); } } }); @@ -352,7 +341,7 @@ fn get_front_selector_tokens(selector_tokens: &Tokens) -> Vec { vec![ TokenAndSpan { span: Span { - lo: BytePos(start_pos + 0), + lo: BytePos(start_pos), hi: BytePos(start_pos + 1), ctxt: SyntaxContext::empty(), }, @@ -377,7 +366,7 @@ fn get_block_tokens(selector_tokens: &Tokens) -> Vec { vec![ TokenAndSpan { span: Span { - lo: BytePos(start_pos + 0), + lo: BytePos(start_pos), hi: BytePos(start_pos + 1), ctxt: SyntaxContext::empty(), }, @@ -485,7 +474,6 @@ fn nth_to_tokens(nth: &Nth) -> Tokens { StringInput::new(&s, nth.span.lo, nth.span.hi), ParserConfig { allow_wrong_line_comments: true, - ..Default::default() }, ); diff --git a/packages/next-swc/crates/core/src/styled_jsx/utils.rs b/packages/next-swc/crates/core/src/styled_jsx/utils.rs index f2acbf5f22bb1be..c4369ca77cf64ed 100644 --- a/packages/next-swc/crates/core/src/styled_jsx/utils.rs +++ b/packages/next-swc/crates/core/src/styled_jsx/utils.rs @@ -20,7 +20,7 @@ fn tpl_element(value: &str) -> TplElement { } pub fn compute_class_names( - styles: &Vec, + styles: &[JSXStyle], style_import_name: &str, ) -> (Option, Option) { let mut static_class_name = None; @@ -45,7 +45,7 @@ pub fn compute_class_names( } } - if external_styles.len() > 0 { + if !external_styles.is_empty() { let mut quasis = vec![tpl_element("jsx-")]; for _i in 1..external_styles.len() { quasis.push(tpl_element(" jsx-")) @@ -61,7 +61,7 @@ pub fn compute_class_names( })); } - if static_hashes.len() > 0 { + if !static_hashes.is_empty() { static_class_name = Some(format!("jsx-{}", hash_string(&static_hashes.join(",")))); } @@ -295,7 +295,7 @@ pub fn make_local_styled_jsx_el( } } -pub fn get_usable_import_specifier(_items: &Vec) -> String { +pub fn get_usable_import_specifier(_items: &[ModuleItem]) -> String { // TODO String::from("_JSXStyle") } @@ -323,7 +323,7 @@ pub fn styled_jsx_import_decl(style_import_name: &str) -> ModuleItem { } // TODO: maybe use DJBHasher (need to implement) -pub fn hash_string(str: &String) -> String { +pub fn hash_string(str: &str) -> String { let mut hasher = DefaultHasher::new(); hasher.write(str.as_bytes()); let hash_result = hasher.finish(); diff --git a/packages/next-swc/crates/napi/build.rs b/packages/next-swc/crates/napi/build.rs index 1f866b6a3c3acf7..9fc236788932b31 100644 --- a/packages/next-swc/crates/napi/build.rs +++ b/packages/next-swc/crates/napi/build.rs @@ -1,5 +1,5 @@ extern crate napi_build; fn main() { - napi_build::setup(); + napi_build::setup(); } diff --git a/packages/next-swc/crates/napi/src/bundle/mod.rs b/packages/next-swc/crates/napi/src/bundle/mod.rs index 3ac3de7f8275b23..1e90517d383146e 100644 --- a/packages/next-swc/crates/napi/src/bundle/mod.rs +++ b/packages/next-swc/crates/napi/src/bundle/mod.rs @@ -64,11 +64,11 @@ impl Task for BundleTask { // let mut bundler = Bundler::new( - &self.c.globals(), + self.c.globals(), self.c.cm.clone(), CustomLoader { cm: self.c.cm.clone(), - handler: &handler, + handler, }, make_resolver(), swc_bundler::Config { @@ -82,7 +82,7 @@ impl Task for BundleTask { ); let mut entries = HashMap::default(); - let path: PathBuf = option.entry.into(); + let path: PathBuf = option.entry; let path = path .canonicalize() .context("failed to canonicalize entry file")?; @@ -148,7 +148,7 @@ struct CustomLoader<'a> { impl swc_bundler::Load for CustomLoader<'_> { fn load(&self, f: &FileName) -> Result { let fm = match f { - FileName::Real(path) => self.cm.load_file(&path)?, + FileName::Real(path) => self.cm.load_file(path)?, _ => unreachable!(), }; @@ -163,7 +163,7 @@ impl swc_bundler::Load for CustomLoader<'_> { let mut parser = Parser::new_from(lexer); let module = parser.parse_module().map_err(|err| { - err.into_diagnostic(&self.handler).emit(); + err.into_diagnostic(self.handler).emit(); anyhow!("failed to parse") })?; diff --git a/packages/next-swc/crates/napi/src/lib.rs b/packages/next-swc/crates/napi/src/lib.rs index dcc6441fbff6a0c..de6f93fe222cb66 100644 --- a/packages/next-swc/crates/napi/src/lib.rs +++ b/packages/next-swc/crates/napi/src/lib.rs @@ -37,19 +37,19 @@ extern crate swc_node_base; use backtrace::Backtrace; use napi::{CallContext, Env, JsObject, JsUndefined}; use std::{env, panic::set_hook, sync::Arc}; -use swc::{ Compiler, TransformOutput}; +use swc::{Compiler, TransformOutput}; use swc_common::{self, sync::Lazy, FilePathMapping, SourceMap}; mod bundle; mod minify; +mod parse; mod transform; mod util; -mod parse; static COMPILER: Lazy> = Lazy::new(|| { let cm = Arc::new(SourceMap::new(FilePathMapping::empty())); - Arc::new(Compiler::new(cm.clone())) + Arc::new(Compiler::new(cm)) }); #[module_exports] @@ -68,7 +68,7 @@ fn init(mut exports: JsObject) -> napi::Result<()> { exports.create_named_method("minify", minify::minify)?; exports.create_named_method("minifySync", minify::minify_sync)?; - + exports.create_named_method("parse", parse::parse)?; Ok(()) diff --git a/packages/next-swc/crates/napi/src/minify.rs b/packages/next-swc/crates/napi/src/minify.rs index 29d947de0c3935a..32098fbb7352db3 100644 --- a/packages/next-swc/crates/napi/src/minify.rs +++ b/packages/next-swc/crates/napi/src/minify.rs @@ -79,7 +79,7 @@ impl Task for MinifyTask { try_with_handler(self.c.cm.clone(), true, |handler| { let fm = self.code.to_file(self.c.cm.clone()); - self.c.minify(fm, &handler, &self.opts) + self.c.minify(fm, handler, &self.opts) }) .convert_err() } @@ -110,8 +110,8 @@ pub fn minify_sync(cx: CallContext) -> napi::Result { let fm = code.to_file(c.cm.clone()); - let output = try_with_handler(c.cm.clone(), true, |handler| c.minify(fm, &handler, &opts)) + let output = try_with_handler(c.cm.clone(), true, |handler| c.minify(fm, handler, &opts)) .convert_err()?; - complete_output(&cx.env, output) + complete_output(cx.env, output) } diff --git a/packages/next-swc/crates/napi/src/parse.rs b/packages/next-swc/crates/napi/src/parse.rs index a6cb68a0291bf47..9d00b3e7ca7d086 100644 --- a/packages/next-swc/crates/napi/src/parse.rs +++ b/packages/next-swc/crates/napi/src/parse.rs @@ -11,7 +11,7 @@ pub struct ParseTask { pub options: String, } -pub fn complete_parse<'a>(env: &Env, ast_json: String) -> napi::Result { +pub fn complete_parse(env: &Env, ast_json: String) -> napi::Result { env.create_string_from_std(ast_json) } @@ -28,7 +28,7 @@ impl Task for ParseTask { let program = try_with_handler(c.cm.clone(), false, |handler| { c.parse_js( fm, - &handler, + handler, options.target, options.syntax, options.is_module, diff --git a/packages/next-swc/crates/napi/src/transform.rs b/packages/next-swc/crates/napi/src/transform.rs index 4992542638aa013..adc32cc25feb7e9 100644 --- a/packages/next-swc/crates/napi/src/transform.rs +++ b/packages/next-swc/crates/napi/src/transform.rs @@ -96,9 +96,9 @@ impl Task for TransformTask { let before_pass = custom_before_pass(self.c.cm.clone(), fm.clone(), &options); self.c.process_js_with_custom_pass( - fm.clone(), + fm, None, - &handler, + handler, &options.swc, |_| before_pass, |_| noop(), @@ -177,11 +177,11 @@ where if is_module.get_value()? { let program: Program = serde_json::from_str(s.as_str()?).context("failed to deserialize Program")?; - c.process_js(&handler, program, &options.swc) + c.process_js(handler, program, &options.swc) } else { let fm = op(&c, s.as_str()?.to_string(), &options).context("failed to load file")?; - c.process_js_file(fm, &handler, &options.swc) + c.process_js_file(fm, handler, &options.swc) } }) }) diff --git a/packages/next-swc/crates/napi/src/util.rs b/packages/next-swc/crates/napi/src/util.rs index ab98fea801083f1..775916055b1c4ff 100644 --- a/packages/next-swc/crates/napi/src/util.rs +++ b/packages/next-swc/crates/napi/src/util.rs @@ -54,7 +54,7 @@ impl CtxtExt for CallContext<'_> { Ok(String::from_utf8_lossy(buffer.as_ref()).to_string()) } - + fn get_deserialized(&self, index: usize) -> napi::Result where T: DeserializeOwned, @@ -79,6 +79,6 @@ pub(crate) fn deserialize_json(s: &str) -> Result where T: DeserializeOwned, { - serde_json::from_str(&s) + serde_json::from_str(s) .with_context(|| format!("failed to deserialize as {}\nJSON: {}", type_name::(), s)) } diff --git a/packages/next-swc/crates/wasm/src/lib.rs b/packages/next-swc/crates/wasm/src/lib.rs index 1273e36577f1d70..9816f32fa9c000b 100644 --- a/packages/next-swc/crates/wasm/src/lib.rs +++ b/packages/next-swc/crates/wasm/src/lib.rs @@ -22,10 +22,10 @@ pub fn minify_sync(s: &str, opts: JsValue) -> Result { let fm = c.cm.new_source_file(FileName::Anon, s.into()); let program = c - .minify(fm, &handler, &opts) + .minify(fm, handler, &opts) .context("failed to minify file")?; - Ok(JsValue::from_serde(&program).context("failed to serialize json")?) + JsValue::from_serde(&program).context("failed to serialize json") }) .map_err(convert_err) } @@ -40,7 +40,7 @@ pub fn transform_sync(s: &str, opts: JsValue) -> Result { let opts: TransformOptions = opts.into_serde().context("failed to parse options")?; let fm = c.cm.new_source_file( - if opts.swc.filename == "" { + if opts.swc.filename.is_empty() { FileName::Anon } else { FileName::Real(opts.swc.filename.clone().into()) @@ -49,10 +49,10 @@ pub fn transform_sync(s: &str, opts: JsValue) -> Result { ); let before_pass = custom_before_pass(c.cm.clone(), fm.clone(), &opts); let out = c - .process_js_with_custom_pass(fm, None, &handler, &opts.swc, |_| before_pass, |_| noop()) + .process_js_with_custom_pass(fm, None, handler, &opts.swc, |_| before_pass, |_| noop()) .context("failed to process js file")?; - Ok(JsValue::from_serde(&out).context("failed to serialize json")?) + JsValue::from_serde(&out).context("failed to serialize json") }) .map_err(convert_err) } From f110a3735b9c4cbdc23b302592f5c24e6a666f02 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Mon, 21 Feb 2022 13:40:59 +0100 Subject: [PATCH 06/10] Fix rsc bootstrap buffer missing in the future renders (#34631) ## Bug If there's upcoming streaming data from server components, should safely skip the bootstrap process. Previously we deleted the buffer then it will cause the buffer is missing in the later re-renders. Now we mark it as empty array, so it can safely skip the boostrap phase x-ref: #34475 - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [x] Errors have helpful link attached, see `contributing.md` --- packages/next/client/index.tsx | 4 +++- .../app/next.config.js | 1 + .../app/pages/streaming-rsc.server.js | 23 +++++++++++++++++++ .../test/rsc.js | 6 +++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 test/integration/react-streaming-and-server-components/app/pages/streaming-rsc.server.js diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index 08fdab14c86a070..ac8b5aa38195f46 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -702,7 +702,9 @@ if (process.env.__NEXT_RSC) { writer.write(encoder.encode(val)) }) buffer.length = 0 - serverDataBuffer.delete(key) + // Clean buffer but not deleting the key to mark bootstrap as complete. + // Then `nextServerDataCallback` will be safely skipped in the future renders. + serverDataBuffer.set(key, []) } serverDataWriter.set(key, writer) } diff --git a/test/integration/react-streaming-and-server-components/app/next.config.js b/test/integration/react-streaming-and-server-components/app/next.config.js index add280d239bb309..a17299bc8c9eaf2 100644 --- a/test/integration/react-streaming-and-server-components/app/next.config.js +++ b/test/integration/react-streaming-and-server-components/app/next.config.js @@ -1,6 +1,7 @@ const withReact18 = require('../../react-18/test/with-react-18') module.exports = withReact18({ + trailingSlash: true, reactStrictMode: true, onDemandEntries: { maxInactiveAge: 1000 * 60 * 60, diff --git a/test/integration/react-streaming-and-server-components/app/pages/streaming-rsc.server.js b/test/integration/react-streaming-and-server-components/app/pages/streaming-rsc.server.js new file mode 100644 index 000000000000000..ac58f5e9e2c382c --- /dev/null +++ b/test/integration/react-streaming-and-server-components/app/pages/streaming-rsc.server.js @@ -0,0 +1,23 @@ +import { Suspense } from 'react' + +let result +let promise +function Data() { + if (result) return result + if (!promise) + promise = new Promise((res) => { + setTimeout(() => { + result = 'next_streaming_data' + res() + }, 500) + }) + throw promise +} + +export default function Page() { + return ( + + + + ) +} diff --git a/test/integration/react-streaming-and-server-components/test/rsc.js b/test/integration/react-streaming-and-server-components/test/rsc.js index 604073c3930fd8e..a01fefe5eb1d605 100644 --- a/test/integration/react-streaming-and-server-components/test/rsc.js +++ b/test/integration/react-streaming-and-server-components/test/rsc.js @@ -82,6 +82,12 @@ export default function (context, { runtime, env }) { expect(await browser.eval('window.beforeNav')).toBe(1) }) + it('should handle streaming server components correctly', async () => { + const browser = await webdriver(context.appPort, '/streaming-rsc') + const content = await browser.eval(`window.document.body.innerText`) + expect(content).toMatchInlineSnapshot('"next_streaming_data"') + }) + // Disable next/image for nodejs runtime temporarily if (runtime === 'edge') { it('should suspense next/image in server components', async () => { From 5c8e787803af7337f8b7665bceb8b90be78dff5a Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Mon, 21 Feb 2022 19:09:32 +0100 Subject: [PATCH 07/10] Clarify test types during scaffolding (#34638) --- plopfile.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/plopfile.js b/plopfile.js index 2dc8b7e619e01cd..44cbe6eec120d2b 100644 --- a/plopfile.js +++ b/plopfile.js @@ -15,7 +15,18 @@ module.exports = function (plop) { type: 'list', name: 'type', message: 'Test type', - choices: ['e2e', 'unit', 'production', 'development'], + choices: [ + { + name: 'e2e - Test "next dev" and "next build && next start"', + value: 'e2e', + }, + { + name: 'production - Test "next build && next start"', + value: 'production', + }, + { name: 'development - Test "next dev"', value: 'development' }, + { name: 'unit - Test individual files', value: 'unit' }, + ], }, ], actions: function (data) { From 411a9b84d8f7dec29ddb0d249c161400bda5f02d Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Mon, 21 Feb 2022 12:14:11 -0600 Subject: [PATCH 08/10] Add note about public folder with standalone mode (#34646) --- docs/advanced-features/output-file-tracing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced-features/output-file-tracing.md b/docs/advanced-features/output-file-tracing.md index dce5f2cc628606d..7ca97b5b19daa10 100644 --- a/docs/advanced-features/output-file-tracing.md +++ b/docs/advanced-features/output-file-tracing.md @@ -34,7 +34,7 @@ module.exports = { This will create a folder at `.next/standalone` which can then be deployed on it's own without installing `node_modules`. -Additionally, a minimal `server.js` file is also output which can be used instead of `next start`. This minimal server does not copy the `.next/static` directory by default as this should ideally be handled by a CDN instead, although it can be copied to the `standalone` folder manually and the `server.js` file will serve it automatically. +Additionally, a minimal `server.js` file is also output which can be used instead of `next start`. This minimal server does not copy the `public` or `.next/static` folders by default as these should ideally be handled by a CDN instead, although these folders can be copied to the `standalone` folder manually and the `server.js` file will serve it automatically. ## Caveats From 30b434b53381afa4893b484856116ed21f4e9b82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 21 Feb 2022 19:21:54 +0100 Subject: [PATCH 09/10] fix: match `redirect` and `rewrite` destination types (#34617) --- packages/next/server/web/spec-extension/response.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/server/web/spec-extension/response.ts b/packages/next/server/web/spec-extension/response.ts index e9993f74720a941..c34ba8a1df566ed 100644 --- a/packages/next/server/web/spec-extension/response.ts +++ b/packages/next/server/web/spec-extension/response.ts @@ -87,7 +87,7 @@ export class NextResponse extends Response { }) } - static rewrite(destination: string | NextURL) { + static rewrite(destination: string | NextURL | URL) { return new NextResponse(null, { headers: { 'x-middleware-rewrite': validateURL(destination), From 7d98537f95f1db1a13e3ba85623b9462fa95036a Mon Sep 17 00:00:00 2001 From: mxgnus <84542021+mxgnus-de@users.noreply.github.com> Date: Mon, 21 Feb 2022 19:28:38 +0100 Subject: [PATCH 10/10] added styled-components & replay support in swc (#34647) styled-components & relay is supported in swc since nextjs 12.1 --- errors/swc-disabled.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/errors/swc-disabled.md b/errors/swc-disabled.md index 701f260c785b4c6..ce026dd0e882e25 100644 --- a/errors/swc-disabled.md +++ b/errors/swc-disabled.md @@ -10,8 +10,6 @@ When an application has custom Babel configuration Next.js will automatically op Many of the integrations with external libraries that currently require custom Babel transformations will be ported to Rust-based SWC transforms in the near future. These include but are not limited to: -- Styled Components - Emotion -- Relay In order to prioritize transforms that will help you adopt SWC please provide your `.babelrc` on [the feedback thread](https://github.com/vercel/next.js/discussions/30174).