Skip to content

Commit

Permalink
feat(plugin): Make metadata api lazy (swc-project#5310)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwonoj authored Jul 27, 2022
1 parent 3aa4471 commit 5a164bd
Show file tree
Hide file tree
Showing 33 changed files with 716 additions and 395 deletions.
33 changes: 17 additions & 16 deletions crates/swc/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use swc_common::{
collections::{AHashMap, AHashSet},
comments::SingleThreadedComments,
errors::Handler,
plugin::metadata::TransformPluginMetadataContext,
FileName, Mark, SourceMap, SyntaxContext,
};
use swc_config::{
Expand Down Expand Up @@ -64,10 +65,8 @@ use swc_ecma_transforms_optimization::{inline_globals2, GlobalExprMap};
use swc_ecma_visit::{Fold, VisitMutWith};

use crate::{
builder::PassBuilder,
dropped_comments_preserver::dropped_comments_preserver,
plugin::{PluginConfig, PluginContext},
SwcImportResolver,
builder::PassBuilder, dropped_comments_preserver::dropped_comments_preserver,
plugin::PluginConfig, SwcImportResolver,
};

#[cfg(test)]
Expand Down Expand Up @@ -525,10 +524,11 @@ impl Options {
_ => None,
};

let plugin_context = PluginContext {
filename: transform_filename,
env_name: self.env_name.to_owned(),
};
let transform_metadata_context = Arc::new(TransformPluginMetadataContext::new(
transform_filename,
self.env_name.to_owned(),
None,
));

if experimental.plugins.is_some() {
swc_plugin_runner::cache::init_plugin_module_cache_once(&experimental.cache_root);
Expand All @@ -537,11 +537,11 @@ impl Options {
let comments = comments.cloned();
let source_map = cm.clone();
crate::plugin::plugins(
experimental.plugins,
transform_metadata_context,
Some(plugin_resolver),
comments,
source_map,
experimental,
plugin_context,
unresolved_mark,
)
};
Expand All @@ -558,20 +558,21 @@ impl Options {
_ => None,
};

let plugin_context = PluginContext {
filename: transform_filename,
env_name: self.env_name.to_owned(),
};
let transform_metadata_context = Arc::new(TransformPluginMetadataContext::new(
transform_filename,
self.env_name.to_owned(),
None,
));

swc_plugin_runner::cache::init_plugin_module_cache_once();
let comments = comments.cloned();
let source_map = cm.clone();
crate::plugin::plugins(
experimental.plugins,
transform_metadata_context,
None,
comments,
source_map,
experimental,
plugin_context,
unresolved_mark,
)
};
Expand Down
113 changes: 17 additions & 96 deletions crates/swc/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,42 +21,22 @@ use swc_ecma_visit::{noop_fold_type, Fold};
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct PluginConfig(String, serde_json::Value);

/// Struct represents arbitrary `context` or `state` to be passed to plugin's
/// entrypoint.
/// While internally this is strongly typed, it is not exposed as public
/// interface to plugin's entrypoint but instead will be passed as JSON string.
/// First, not all of plugins will need to deserialize this - plugin may opt in
/// to access when it's needed. Secondly, we do not have way to avoid breaking
/// changes when adding a new property. We may change this to typed
/// deserialization in the future if we have add-only schema support with
/// serialization / deserialization.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct PluginContext {
/// The path of the file being processed. This includes all of the path as
/// much as possible.
pub filename: Option<String>,
/// The current environment resolved as process.env.SWC_ENV ||
/// process.env.NODE_ENV || "development"
pub env_name: String,
}

#[cfg(feature = "plugin")]
pub fn plugins(
configured_plugins: Option<Vec<PluginConfig>>,
metadata_context: std::sync::Arc<swc_common::plugin::metadata::TransformPluginMetadataContext>,
resolver: Option<CachingResolver<NodeModulesResolver>>,
comments: Option<swc_common::comments::SingleThreadedComments>,
source_map: std::sync::Arc<swc_common::SourceMap>,
config: crate::config::JscExperimental,
plugin_context: PluginContext,
unresolved_mark: swc_common::Mark,
) -> impl Fold {
{
RustPlugins {
plugins: configured_plugins,
metadata_context,
resolver,
comments,
source_map,
plugins: config.plugins,
plugin_context,
unresolved_mark,
}
}
Expand All @@ -68,11 +48,11 @@ pub fn plugins() -> impl Fold {
}

struct RustPlugins {
plugins: Option<Vec<PluginConfig>>,
metadata_context: std::sync::Arc<swc_common::plugin::metadata::TransformPluginMetadataContext>,
resolver: Option<CachingResolver<NodeModulesResolver>>,
comments: Option<swc_common::comments::SingleThreadedComments>,
plugins: Option<Vec<PluginConfig>>,
source_map: std::sync::Arc<swc_common::SourceMap>,
plugin_context: PluginContext,
unresolved_mark: swc_common::Mark,
}

Expand All @@ -84,7 +64,7 @@ impl RustPlugins {
self.apply_inner(n).with_context(|| {
format!(
"failed to invoke plugin on '{:?}'",
self.plugin_context.filename
self.metadata_context.filename
)
})
}
Expand All @@ -96,8 +76,7 @@ impl RustPlugins {

use anyhow::Context;
use swc_common::{
collections::AHashMap,
plugin::{PluginSerializedBytes, VersionedSerializable},
plugin::serialized::{PluginSerializedBytes, VersionedSerializable},
FileName,
};
use swc_ecma_loader::resolve::Resolve;
Expand All @@ -122,8 +101,8 @@ impl RustPlugins {
// Note: This doesn't mean plugin won't perform any se/deserialization: it
// still have to construct from raw bytes internally to perform actual
// transform.
if let Some(plugins) = &self.plugins {
for p in plugins {
if let Some(plugins) = &mut self.plugins {
for p in plugins.drain(..) {
let resolved_path = self
.resolver
.as_ref()
Expand All @@ -141,57 +120,24 @@ impl RustPlugins {
&path,
&swc_plugin_runner::cache::PLUGIN_MODULE_CACHE,
&self.source_map,
&self.metadata_context,
Some(p.1),
)?;

if !transform_plugin_executor.is_transform_schema_compatible()? {
anyhow::bail!("Cannot execute incompatible plugin {}", &p.0);
}

let span = tracing::span!(
tracing::Level::INFO,
"serialize_context",
plugin_module = p.0.as_str()
);
let context_span_guard = span.enter();

let serialized_config_json = serde_json::to_string(&p.1)
.context("Failed to serialize plugin config as json")
.and_then(|value| {
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
value,
))
})?;

let serialized_context_json = serde_json::to_string(&self.plugin_context)
.context("Failed to serialize plugin context as json")
.and_then(|value| {
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
value,
))
})?;
drop(context_span_guard);

let span = tracing::span!(
tracing::Level::INFO,
"execute_plugin_runner",
plugin_module = p.0.as_str()
)
.entered();

// Forward host side experimental metadata into plugin.
// This is currently not being used, reserved to enable proper serialization
// transform.
let experimental_metadata: AHashMap<String, String> = AHashMap::default();
let experimental_metadata_reserved = PluginSerializedBytes::try_serialize(
&VersionedSerializable::new(experimental_metadata),
)?;

serialized_program = transform_plugin_executor
.transform(
&serialized_program,
&serialized_config_json,
&serialized_context_json,
&experimental_metadata_reserved,
self.unresolved_mark,
should_enable_comments_proxy,
)
Expand Down Expand Up @@ -220,7 +166,7 @@ impl RustPlugins {
use anyhow::Context;
use swc_common::{
collections::AHashMap,
plugin::{PluginSerializedBytes, VersionedSerializable},
plugin::serialized::{PluginSerializedBytes, VersionedSerializable},
FileName,
};
use swc_ecma_loader::resolve::Resolve;
Expand All @@ -235,45 +181,20 @@ impl RustPlugins {
let program = VersionedSerializable::new(n);
let mut serialized_program = PluginSerializedBytes::try_serialize(&program)?;

if let Some(plugins) = &self.plugins {
for p in plugins {
let serialized_config_json = serde_json::to_string(&p.1)
.context("Failed to serialize plugin config as json")
.and_then(|value| {
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
value,
))
})?;

let serialized_context_json = serde_json::to_string(&self.plugin_context)
.context("Failed to serialize plugin context as json")
.and_then(|value| {
PluginSerializedBytes::try_serialize(&VersionedSerializable::new(
value,
))
})?;

if let Some(plugins) = &mut self.plugins {
for p in plugins.drain(..) {
let mut transform_plugin_executor =
swc_plugin_runner::create_plugin_transform_executor(
&PathBuf::from(&p.0),
&swc_plugin_runner::cache::PLUGIN_MODULE_CACHE,
&self.source_map,
&self.metadata_context,
Some(p.1),
)?;

// Forward host side experimental metadata into plugin.
// This is currently not being used, reserved to enable proper serialization
// transform.
let experimental_metadata: AHashMap<String, String> = AHashMap::default();
let experimental_metadata_reserved = PluginSerializedBytes::try_serialize(
&VersionedSerializable::new(experimental_metadata),
)?;

serialized_program = transform_plugin_executor
.transform(
&serialized_program,
&serialized_config_json,
&serialized_context_json,
&experimental_metadata_reserved,
self.unresolved_mark,
should_enable_comments_proxy,
)
Expand Down
2 changes: 0 additions & 2 deletions crates/swc_common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ pub mod input;
pub mod iter;
pub mod macros;
pub mod pass;
#[cfg(feature = "plugin-base")]
#[cfg_attr(docsrs, doc(cfg(feature = "plugin-base")))]
pub mod plugin;
mod pos;
mod rustc_data_structures;
Expand Down
89 changes: 89 additions & 0 deletions crates/swc_common/src/plugin/metadata.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use std::env;

use crate::collections::AHashMap;

/// Indexable key to the metadata context for a transform plugin, avoiding
/// serialization & allocation to the host by using incremental number.
/// TransformPluginMetadataContext does not implement Index trait, instead
/// host does manual matching to corresponding value.
#[derive(Copy, Clone)]
pub enum TransformPluginMetadataContextKind {
// This value always should increase, even if some keys are removed in the
// future.
Filename = 1,
Env = 2,
Cwd = 3,
}

impl From<u32> for TransformPluginMetadataContextKind {
fn from(key: u32) -> TransformPluginMetadataContextKind {
match key {
1 => TransformPluginMetadataContextKind::Filename,
2 => TransformPluginMetadataContextKind::Env,
3 => TransformPluginMetadataContextKind::Cwd,
_ => panic!("Invalid TransformPluginMetadataContextKind key"),
}
}
}

/*
impl TransformPluginMetadataContextKind {
pub fn from(key: u32) -> TransformPluginMetadataContextKind {
match key {
1 => TransformPluginMetadataContextKind::Filename,
2 => TransformPluginMetadataContextKind::Env,
3 => TransformPluginMetadataContextKind::Cwd,
_ => panic!("Invalid TransformPluginMetadataContextKind key"),
}
}
}
impl Into<u32> for TransformPluginMetadataContextKind {
fn into(self) -> u32 {
match self {
TransformPluginMetadataContextKind::Filename => 1,
TransformPluginMetadataContextKind::Env => 2,
TransformPluginMetadataContextKind::Cwd => 3,
}
}
}*/

/// Host side metadata context plugin may need to access.
/// This is a global context - any plugin in single transform will have same
/// values.
pub struct TransformPluginMetadataContext {
/// The path of the file being processed. This includes all of the path as
/// much as possible.
pub filename: Option<String>,
/// The current environment resolved as process.env.SWC_ENV ||
/// process.env.NODE_ENV || "development"
pub env: String,
/// The current working directory.
pub cwd: Option<String>,
pub experimental: AHashMap<String, String>,
}

impl TransformPluginMetadataContext {
pub fn new(
filename: Option<String>,
env: String,
experimental: Option<AHashMap<String, String>>,
) -> Self {
TransformPluginMetadataContext {
filename,
env,
cwd: env::current_dir()
.map(|cwd| cwd.as_path().to_string_lossy().to_string())
.ok(),
experimental: experimental.unwrap_or_default(),
}
}

pub fn get(&self, key: &TransformPluginMetadataContextKind) -> Option<String> {
match key {
TransformPluginMetadataContextKind::Filename => self.filename.clone(),
TransformPluginMetadataContextKind::Env => Some(self.env.clone()),
TransformPluginMetadataContextKind::Cwd => self.cwd.clone(),
}
}
}
4 changes: 4 additions & 0 deletions crates/swc_common/src/plugin/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod metadata;
#[cfg(feature = "plugin-base")]
#[cfg_attr(docsrs, doc(cfg(feature = "plugin-base")))]
pub mod serialized;
Loading

0 comments on commit 5a164bd

Please sign in to comment.