Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collect packages are used and eliminated in getServerSideProps #35404

Merged
merged 4 commits into from Apr 1, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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());
Brooooooklyn marked this conversation as resolved.
Show resolved Hide resolved
}
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