Skip to content

Commit

Permalink
Collect packages are used and eliminated in getServerSideProps (#35404)
Browse files Browse the repository at this point in the history
Collect telemetry info about packages are used and eliminated in `getServerSideProps`

https://github.com/vercel/next-telemetry/pull/71


Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
  • Loading branch information
Brooooooklyn and ijjk committed Apr 1, 2022
1 parent 127bbc0 commit 8b9ae83
Show file tree
Hide file tree
Showing 19 changed files with 221 additions and 22 deletions.
7 changes: 6 additions & 1 deletion packages/next-swc/crates/core/src/lib.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -113,6 +114,7 @@ pub fn custom_before_pass<'a, C: Comments + 'a>(
file: Arc<SourceFile>,
opts: &'a TransformOptions,
comments: C,
eliminated_packages: Rc<RefCell<FxHashSet<String>>>,
) -> impl Fold + 'a {
#[cfg(target_arch = "wasm32")]
let relay_plugin = noop();
Expand Down Expand Up @@ -148,7 +150,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,
Expand Down
27 changes: 24 additions & 3 deletions 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::*;
Expand All @@ -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<RefCell<FxHashSet<String>>>) -> impl Fold {
Repeat::new(NextSsg {
state: Default::default(),
state: State {
eliminated_packages,
..Default::default()
},
in_lhs_of_var: false,
})
}
Expand All @@ -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<RefCell<FxHashSet<String>>>,
}

impl State {
Expand Down Expand Up @@ -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,
}

Expand Down Expand Up @@ -344,11 +353,23 @@ impl Fold for NextSsg {
return i;
}

let import_src = &i.src.value;

i.specifiers.retain(|s| match s {
ImportSpecifier::Named(ImportNamedSpecifier { local, .. })
| ImportSpecifier::Default(ImportDefaultSpecifier { local, .. })
| ImportSpecifier::Namespace(ImportStarAsSpecifier { local, .. }) => {
if self.should_remove(local.to_id()) {
if self.state.is_server_props
// filter out non-packages import
// third part packages must start with `a-z` or `@`
&& import_src.starts_with(|c: char| c.is_ascii_lowercase() || c == '@')
{
self.state
.eliminated_packages
.borrow_mut()
.insert(import_src.to_string());
}
tracing::trace!(
"Dropping import `{}{:?}` because it should be removed",
local.sym,
Expand Down
7 changes: 6 additions & 1 deletion packages/next-swc/crates/core/tests/errors.rs
Expand Up @@ -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,
);
}
2 changes: 1 addition & 1 deletion packages/next-swc/crates/core/tests/fixture.rs
Expand Up @@ -113,7 +113,7 @@ fn next_ssg_fixture(input: PathBuf) {
},
top_level_mark,
);
chain!(next_ssg(), jsx)
chain!(next_ssg(Default::default()), jsx)
},
&input,
&output,
Expand Down
8 changes: 7 additions & 1 deletion packages/next-swc/crates/core/tests/full.rs
Expand Up @@ -73,7 +73,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(),
) {
Expand Down
59 changes: 59 additions & 0 deletions packages/next-swc/crates/core/tests/telemetry.rs
@@ -0,0 +1,59 @@
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;

use fxhash::FxHashSet;
use next_swc::next_ssg::next_ssg;
use once_cell::sync::Lazy;
use swc::{try_with_handler, Compiler};
use swc_common::{FileName, FilePathMapping, SourceMap};
use swc_ecmascript::transforms::pass::noop;

static COMPILER: Lazy<Arc<Compiler>> = Lazy::new(|| {
let cm = Arc::new(SourceMap::new(FilePathMapping::empty()));

Arc::new(Compiler::new(cm))
});

#[test]
fn should_collect_estimated_third_part_packages() {
let eliminated_packages: Rc<RefCell<FxHashSet<String>>> = Default::default();
let fm = COMPILER.cm.new_source_file(
FileName::Real("fixture.js".into()),
r#"import http from 'http'
import { hash } from '@napi-rs/bcrypt'
import { omit } from '~/utils/omit'
import config from './data.json'
export default () => 'Hello World'
export function getServerSideProps() {
console.log(http)
console.log(config)
return { props: { digest: hash('hello') } }
}
"#
.to_owned(),
);
assert!(
try_with_handler(COMPILER.cm.clone(), Default::default(), |handler| {
COMPILER.process_js_with_custom_pass(
fm,
None,
handler,
&Default::default(),
|_, _| next_ssg(eliminated_packages.clone()),
|_, _| noop(),
)
})
.is_ok()
);
assert_eq!(
eliminated_packages
.borrow()
.iter()
.collect::<Vec<&String>>(),
vec!["@napi-rs/bcrypt", "http"]
);
}
2 changes: 1 addition & 1 deletion packages/next-swc/crates/napi/src/bundle/mod.rs
Expand Up @@ -132,7 +132,7 @@ impl Task for BundleTask {
}

fn resolve(self, env: napi::Env, output: Self::Output) -> napi::Result<Self::JsValue> {
complete_output(&env, output)
complete_output(&env, output, Default::default())
}
}

Expand Down
22 changes: 20 additions & 2 deletions packages/next-swc/crates/napi/src/lib.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -86,8 +87,25 @@ fn construct_compiler(ctx: CallContext) -> napi::Result<JsUndefined> {
ctx.env.get_undefined()
}

pub fn complete_output(env: &Env, output: TransformOutput) -> napi::Result<JsObject> {
env.to_js_value(&output)?.coerce_to_object()
pub fn complete_output(
env: &Env,
output: TransformOutput,
eliminated_packages: FxHashSet<String>,
) -> napi::Result<JsObject> {
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::<Vec<String>>(),
)?)?,
)?;
}
Ok(js_output)
}

pub type ArcCompiler = Arc<Compiler>;
4 changes: 2 additions & 2 deletions packages/next-swc/crates/napi/src/minify.rs
Expand Up @@ -92,7 +92,7 @@ impl Task for MinifyTask {
}

fn resolve(self, env: napi::Env, output: Self::Output) -> napi::Result<Self::JsValue> {
complete_output(&env, output)
complete_output(&env, output, Default::default())
}
}

Expand Down Expand Up @@ -127,5 +127,5 @@ pub fn minify_sync(cx: CallContext) -> napi::Result<JsObject> {
)
.convert_err()?;

complete_output(cx.env, output)
complete_output(cx.env, output, Default::default())
}
30 changes: 24 additions & 6 deletions packages/next-swc/crates/napi/src/transform.rs
Expand Up @@ -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};
Expand All @@ -60,10 +63,11 @@ pub struct TransformTask {
}

impl Task for TransformTask {
type Output = TransformOutput;
type Output = (TransformOutput, FxHashSet<String>);
type JsValue = JsObject;

fn compute(&mut self) -> napi::Result<Self::Output> {
let eliminated_packages: Rc<RefCell<fxhash::FxHashSet<String>>> = Default::default();
let res = catch_unwind(AssertUnwindSafe(|| {
try_with_handler(
self.c.cm.clone(),
Expand Down Expand Up @@ -108,7 +112,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(),
)
})
Expand All @@ -124,16 +136,22 @@ 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),
)),
}
}

fn resolve(self, env: Env, result: Self::Output) -> napi::Result<Self::JsValue> {
complete_output(&env, result)
fn resolve(
self,
env: Env,
(output, eliminated_packages): Self::Output,
) -> napi::Result<Self::JsValue> {
complete_output(&env, output, eliminated_packages)
}
}

Expand Down Expand Up @@ -203,7 +221,7 @@ where
)
.convert_err()?;

complete_output(cx.env, output)
complete_output(cx.env, output, Default::default())
}

#[js_function(4)]
Expand Down
4 changes: 3 additions & 1 deletion packages/next-swc/crates/wasm/src/lib.rs
Expand Up @@ -68,7 +68,9 @@ pub fn transform_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
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")?;
Expand Down
2 changes: 2 additions & 0 deletions packages/next/build/index.ts
Expand Up @@ -73,6 +73,7 @@ import {
eventTypeCheckCompleted,
EVENT_BUILD_FEATURE_USAGE,
EventBuildFeatureUsage,
eventPackageUsedInGetServerSideProps,
} from '../telemetry/events'
import { Telemetry } from '../telemetry/storage'
import { CompilerResult, runCompiler } from './compiler'
Expand Down Expand Up @@ -1961,6 +1962,7 @@ export default async function build(
if (telemetryPlugin) {
const events = eventBuildFeatureUsage(telemetryPlugin)
telemetry.record(events)
telemetry.record(eventPackageUsedInGetServerSideProps(telemetryPlugin))
}

if (ssgPages.size > 0) {
Expand Down
5 changes: 5 additions & 0 deletions packages/next/build/webpack/loaders/next-swc-loader.js
Expand Up @@ -88,6 +88,11 @@ async function loaderTransform(parentTrace, source, inputSourceMap) {
const swcSpan = parentTrace.traceChild('next-swc-transform')
return swcSpan.traceAsyncFn(() =>
transform(source, programmaticOptions).then((output) => {
if (output.eliminatedPackages && this.eliminatedPackages) {
for (const pkg of JSON.parse(output.eliminatedPackages)) {
this.eliminatedPackages.add(pkg)
}
}
return [output.code, output.map ? JSON.parse(output.map) : undefined]
})
)
Expand Down

0 comments on commit 8b9ae83

Please sign in to comment.