From 47a4b8e6a5242e8a27490e346c431729222a0046 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Thu, 17 Mar 2022 22:57:26 +0800 Subject: [PATCH] Collect packages are used and eliminated in getServerSideProps --- examples/ssr-caching/pages/index.js | 3 ++ packages/next-swc/crates/core/src/lib.rs | 7 ++++- packages/next-swc/crates/core/src/next_ssg.rs | 21 +++++++++++-- packages/next-swc/crates/core/tests/errors.rs | 7 ++++- .../next-swc/crates/core/tests/fixture.rs | 2 +- packages/next-swc/crates/core/tests/full.rs | 8 ++++- .../next-swc/crates/napi/src/bundle/mod.rs | 2 +- packages/next-swc/crates/napi/src/lib.rs | 22 ++++++++++++-- packages/next-swc/crates/napi/src/minify.rs | 4 +-- .../next-swc/crates/napi/src/transform.rs | 30 +++++++++++++++---- packages/next-swc/crates/wasm/src/lib.rs | 4 ++- .../build/webpack/loaders/next-swc-loader.js | 14 +++++++-- .../build/webpack/plugins/telemetry-plugin.ts | 22 +++++++++++++- 13 files changed, 124 insertions(+), 22 deletions(-) diff --git a/examples/ssr-caching/pages/index.js b/examples/ssr-caching/pages/index.js index 46c8ff60f41ec04..6c0043179ab276e 100644 --- a/examples/ssr-caching/pages/index.js +++ b/examples/ssr-caching/pages/index.js @@ -1,3 +1,5 @@ +import React from 'react' + export default function Index({ time }) { return (
@@ -12,6 +14,7 @@ export async function getServerSideProps({ req, res }) { 'Cache-Control', 'public, s-maxage=10, stale-while-revalidate=59' ) + res.setHeader('x-react-version', React.version) return { props: { diff --git a/packages/next-swc/crates/core/src/lib.rs b/packages/next-swc/crates/core/src/lib.rs index dd13ea2af816850..10f32bba1e941fd 100644 --- a/packages/next-swc/crates/core/src/lib.rs +++ b/packages/next-swc/crates/core/src/lib.rs @@ -31,6 +31,7 @@ DEALINGS IN THE SOFTWARE. use auto_cjs::contains_cjs; use either::Either; +use fxhash::FxHashSet; use serde::Deserialize; use std::cell::RefCell; use std::rc::Rc; @@ -109,6 +110,7 @@ pub fn custom_before_pass<'a, C: Comments + 'a>( file: Arc, opts: &'a TransformOptions, comments: C, + eliminated_packages: Rc>>, ) -> impl Fold + 'a { #[cfg(target_arch = "wasm32")] let relay_plugin = noop(); @@ -144,7 +146,10 @@ pub fn custom_before_pass<'a, C: Comments + 'a>( Either::Right(noop()) } }, - Optional::new(next_ssg::next_ssg(), !opts.disable_next_ssg), + Optional::new( + next_ssg::next_ssg(eliminated_packages), + !opts.disable_next_ssg + ), amp_attributes::amp_attributes(), next_dynamic::next_dynamic( opts.is_development, diff --git a/packages/next-swc/crates/core/src/next_ssg.rs b/packages/next-swc/crates/core/src/next_ssg.rs index a25e885abf6576a..841553528522fd6 100644 --- a/packages/next-swc/crates/core/src/next_ssg.rs +++ b/packages/next-swc/crates/core/src/next_ssg.rs @@ -1,6 +1,8 @@ use easy_error::{bail, Error}; use fxhash::FxHashSet; +use std::cell::RefCell; use std::mem::take; +use std::rc::Rc; use swc_common::pass::{Repeat, Repeated}; use swc_common::DUMMY_SP; use swc_ecmascript::ast::*; @@ -12,9 +14,12 @@ use swc_ecmascript::{ }; /// Note: This paths requires running `resolver` **before** running this. -pub fn next_ssg() -> impl Fold { +pub fn next_ssg(eliminated_packages: Rc>>) -> impl Fold { Repeat::new(NextSsg { - state: Default::default(), + state: State { + eliminated_packages, + ..Default::default() + }, in_lhs_of_var: false, }) } @@ -41,6 +46,10 @@ struct State { done: bool, should_run_again: bool, + + /// Track the import packages which are eliminated in the + /// `getServerSideProps` + pub eliminated_packages: Rc>>, } impl State { @@ -288,7 +297,7 @@ impl Fold for Analyzer<'_> { /// Actual implementation of the transform. struct NextSsg { - state: State, + pub state: State, in_lhs_of_var: bool, } @@ -349,6 +358,12 @@ impl Fold for NextSsg { | ImportSpecifier::Default(ImportDefaultSpecifier { local, .. }) | ImportSpecifier::Namespace(ImportStarAsSpecifier { local, .. }) => { if self.should_remove(local.to_id()) { + if self.state.is_server_props { + self.state + .eliminated_packages + .borrow_mut() + .insert(local.sym.to_string()); + } tracing::trace!( "Dropping import `{}{:?}` because it should be removed", local.sym, diff --git a/packages/next-swc/crates/core/tests/errors.rs b/packages/next-swc/crates/core/tests/errors.rs index 35ce78e252de2ca..3176182ef27f2e8 100644 --- a/packages/next-swc/crates/core/tests/errors.rs +++ b/packages/next-swc/crates/core/tests/errors.rs @@ -63,5 +63,10 @@ fn styled_jsx_errors(input: PathBuf) { #[fixture("tests/errors/next-ssg/**/input.js")] fn next_ssg_errors(input: PathBuf) { let output = input.parent().unwrap().join("output.js"); - test_fixture_allowing_error(syntax(), &|_tr| next_ssg(), &input, &output); + test_fixture_allowing_error( + syntax(), + &|_tr| next_ssg(Default::default()), + &input, + &output, + ); } diff --git a/packages/next-swc/crates/core/tests/fixture.rs b/packages/next-swc/crates/core/tests/fixture.rs index ed9cf5b247b703f..9c0b38b286318c5 100644 --- a/packages/next-swc/crates/core/tests/fixture.rs +++ b/packages/next-swc/crates/core/tests/fixture.rs @@ -112,7 +112,7 @@ fn next_ssg_fixture(input: PathBuf) { }, top_level_mark, ); - chain!(next_ssg(), jsx) + chain!(next_ssg(Default::default()), jsx) }, &input, &output, diff --git a/packages/next-swc/crates/core/tests/full.rs b/packages/next-swc/crates/core/tests/full.rs index bd7c4a6f65d72c4..ab34b47570179c5 100644 --- a/packages/next-swc/crates/core/tests/full.rs +++ b/packages/next-swc/crates/core/tests/full.rs @@ -72,7 +72,13 @@ fn test(input: &Path, minify: bool) { &handler, &options.swc, |_, comments| { - custom_before_pass(cm.clone(), fm.clone(), &options, comments.clone()) + custom_before_pass( + cm.clone(), + fm.clone(), + &options, + comments.clone(), + Default::default(), + ) }, |_, _| noop(), ) { diff --git a/packages/next-swc/crates/napi/src/bundle/mod.rs b/packages/next-swc/crates/napi/src/bundle/mod.rs index 9599693d94a1629..e4d8f09d36d73aa 100644 --- a/packages/next-swc/crates/napi/src/bundle/mod.rs +++ b/packages/next-swc/crates/napi/src/bundle/mod.rs @@ -121,7 +121,7 @@ impl Task for BundleTask { } fn resolve(self, env: napi::Env, output: Self::Output) -> napi::Result { - complete_output(&env, output) + complete_output(&env, output, Default::default()) } } diff --git a/packages/next-swc/crates/napi/src/lib.rs b/packages/next-swc/crates/napi/src/lib.rs index de6f93fe222cb66..b649ab7df6d95b7 100644 --- a/packages/next-swc/crates/napi/src/lib.rs +++ b/packages/next-swc/crates/napi/src/lib.rs @@ -35,6 +35,7 @@ extern crate napi_derive; extern crate swc_node_base; use backtrace::Backtrace; +use fxhash::FxHashSet; use napi::{CallContext, Env, JsObject, JsUndefined}; use std::{env, panic::set_hook, sync::Arc}; use swc::{Compiler, TransformOutput}; @@ -84,8 +85,25 @@ fn construct_compiler(ctx: CallContext) -> napi::Result { ctx.env.get_undefined() } -pub fn complete_output(env: &Env, output: TransformOutput) -> napi::Result { - env.to_js_value(&output)?.coerce_to_object() +pub fn complete_output( + env: &Env, + output: TransformOutput, + eliminated_packages: FxHashSet, +) -> napi::Result { + let mut js_output = env.create_object()?; + js_output.set_named_property("code", env.create_string_from_std(output.code)?)?; + if let Some(map) = output.map { + js_output.set_named_property("map", env.create_string_from_std(map)?)?; + } + if !eliminated_packages.is_empty() { + js_output.set_named_property( + "eliminatedPackages", + env.create_string_from_std(serde_json::to_string( + &eliminated_packages.into_iter().collect::>(), + )?)?, + )?; + } + Ok(js_output) } pub type ArcCompiler = Arc; diff --git a/packages/next-swc/crates/napi/src/minify.rs b/packages/next-swc/crates/napi/src/minify.rs index 32098fbb7352db3..0c7e637e80565b7 100644 --- a/packages/next-swc/crates/napi/src/minify.rs +++ b/packages/next-swc/crates/napi/src/minify.rs @@ -85,7 +85,7 @@ impl Task for MinifyTask { } fn resolve(self, env: napi::Env, output: Self::Output) -> napi::Result { - complete_output(&env, output) + complete_output(&env, output, Default::default()) } } @@ -113,5 +113,5 @@ pub fn minify_sync(cx: CallContext) -> napi::Result { 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, Default::default()) } diff --git a/packages/next-swc/crates/napi/src/transform.rs b/packages/next-swc/crates/napi/src/transform.rs index 835392ae91d9a1c..1e19a33811aec3f 100644 --- a/packages/next-swc/crates/napi/src/transform.rs +++ b/packages/next-swc/crates/napi/src/transform.rs @@ -31,12 +31,15 @@ use crate::{ util::{deserialize_json, CtxtExt, MapErr}, }; use anyhow::{anyhow, bail, Context as _, Error}; +use fxhash::FxHashSet; use napi::{CallContext, Env, JsBoolean, JsBuffer, JsObject, JsString, JsUnknown, Status, Task}; use next_swc::{custom_before_pass, TransformOptions}; use std::fs::read_to_string; use std::{ + cell::RefCell, convert::TryFrom, panic::{catch_unwind, AssertUnwindSafe}, + rc::Rc, sync::Arc, }; use swc::{try_with_handler, Compiler, TransformOutput}; @@ -60,10 +63,11 @@ pub struct TransformTask { } impl Task for TransformTask { - type Output = TransformOutput; + type Output = (TransformOutput, FxHashSet); type JsValue = JsObject; fn compute(&mut self) -> napi::Result { + let eliminated_packages: Rc>> = Default::default(); let res = catch_unwind(AssertUnwindSafe(|| { try_with_handler(self.c.cm.clone(), true, |handler| { self.c.run(|| { @@ -102,7 +106,15 @@ impl Task for TransformTask { None, handler, &options.swc, - |_, comments| custom_before_pass(cm, file, &options, comments.clone()), + |_, comments| { + custom_before_pass( + cm, + file, + &options, + comments.clone(), + eliminated_packages.clone(), + ) + }, |_, _| noop(), ) }) @@ -117,7 +129,9 @@ impl Task for TransformTask { }); match res { - Ok(res) => res.convert_err(), + Ok(res) => res + .map(|o| (o, eliminated_packages.replace(Default::default()))) + .convert_err(), Err(err) => Err(napi::Error::new( Status::GenericFailure, format!("{:?}", err), @@ -125,8 +139,12 @@ impl Task for TransformTask { } } - fn resolve(self, env: Env, result: Self::Output) -> napi::Result { - complete_output(&env, result) + fn resolve( + self, + env: Env, + (output, eliminated_packages): Self::Output, + ) -> napi::Result { + complete_output(&env, output, eliminated_packages) } } @@ -189,7 +207,7 @@ where }) .convert_err()?; - complete_output(cx.env, output) + complete_output(cx.env, output, Default::default()) } #[js_function(4)] diff --git a/packages/next-swc/crates/wasm/src/lib.rs b/packages/next-swc/crates/wasm/src/lib.rs index 0383106c82eee1a..02048778947ca2f 100644 --- a/packages/next-swc/crates/wasm/src/lib.rs +++ b/packages/next-swc/crates/wasm/src/lib.rs @@ -55,7 +55,9 @@ pub fn transform_sync(s: &str, opts: JsValue) -> Result { None, handler, &opts.swc, - |_, comments| custom_before_pass(cm, file, &opts, comments.clone()), + |_, comments| { + custom_before_pass(cm, file, &opts, comments.clone(), Default::default()) + }, |_, _| noop(), ) .context("failed to process js file")?; diff --git a/packages/next/build/webpack/loaders/next-swc-loader.js b/packages/next/build/webpack/loaders/next-swc-loader.js index 70e596102ed7e30..eb4c594cf96b749 100644 --- a/packages/next/build/webpack/loaders/next-swc-loader.js +++ b/packages/next/build/webpack/loaders/next-swc-loader.js @@ -88,7 +88,13 @@ async function loaderTransform(parentTrace, source, inputSourceMap) { const swcSpan = parentTrace.traceChild('next-swc-transform') return swcSpan.traceAsyncFn(() => transform(source, programmaticOptions).then((output) => { - return [output.code, output.map ? JSON.parse(output.map) : undefined] + return [ + output.code, + output.map ? JSON.parse(output.map) : undefined, + output.eliminatedPackages + ? JSON.parse(output.eliminatedPackages) + : undefined, + ] }) ) } @@ -123,13 +129,17 @@ export function pitch() { export default function swcLoader(inputSource, inputSourceMap) { const loaderSpan = this.currentTraceSpan.traceChild('next-swc-loader') + const globalEliminatedPackages = this.eliminatedPackages const callback = this.async() loaderSpan .traceAsyncFn(() => loaderTransform.call(this, loaderSpan, inputSource, inputSourceMap) ) .then( - ([transformedSource, outputSourceMap]) => { + ([transformedSource, outputSourceMap, eliminatedPackages]) => { + if (eliminatedPackages) { + globalEliminatedPackages.push(...eliminatedPackages) + } callback(null, transformedSource, outputSourceMap || inputSourceMap) }, (err) => { diff --git a/packages/next/build/webpack/plugins/telemetry-plugin.ts b/packages/next/build/webpack/plugins/telemetry-plugin.ts index aeb73cad89db8f9..51337cb4dc7cd59 100644 --- a/packages/next/build/webpack/plugins/telemetry-plugin.ts +++ b/packages/next/build/webpack/plugins/telemetry-plugin.ts @@ -1,4 +1,7 @@ -import type { webpack5 as webpack } from 'next/dist/compiled/webpack/webpack' +import { + NormalModule, + webpack5 as webpack, +} from 'next/dist/compiled/webpack/webpack' type Feature = | 'next/image' @@ -54,6 +57,8 @@ const BUILD_FEATURES: Array = [ 'swcEmotion', ] +const ELIMINATED_PACKAGES: string[] = [] + /** * Plugin that queries the ModuleGraph to look for modules that correspond to * certain features (e.g. next/image and next/script) and record how many times @@ -83,6 +88,9 @@ export class TelemetryPlugin implements webpack.WebpackPluginInstance { compiler.hooks.make.tapAsync( TelemetryPlugin.name, async (compilation: webpack.Compilation, callback: () => void) => { + compilation.hooks.afterCodeGeneration.tap(TelemetryPlugin.name, () => { + console.log(compilation.assets) + }) compilation.hooks.finishModules.tapAsync( TelemetryPlugin.name, async (modules: Iterable, modulesFinish: () => void) => { @@ -105,11 +113,23 @@ export class TelemetryPlugin implements webpack.WebpackPluginInstance { callback() } ) + compiler.hooks.compilation.tap(TelemetryPlugin.name, (compilation) => { + const moduleHooks = NormalModule.getCompilationHooks(compilation) + moduleHooks.loader.tap(TelemetryPlugin.name, (loaderContext: any) => { + loaderContext.eliminatedPackages = ELIMINATED_PACKAGES + }) + }) } usages(): FeatureUsage[] { return [...this.usageTracker.values()] } + + packagesUsedInServerSideProps(): { package: string }[] { + return Array.from(new Set(ELIMINATED_PACKAGES)).map((pkg) => ({ + package: pkg, + })) + } } /**