mirror of
https://github.com/swc-project/swc.git
synced 2024-11-26 09:54:22 +03:00
feat(plugin): Proxy swc_common
apis (#2646)
swc_common: - Add `Runtime` trait for plugin. - Implement `serde` for diagnostics. - Proxy diagnostics using `Runtime`. - Proxy `HygieneData::with` with `Runtime`. - Add implementation of `Runtime` with cargo feature `plugin-rt`. - Make `Runtime` implement `StableAbi`. swc_plugin: - Move api code to `swc_plugin_api`. - Depend on `swc_common/plugin-mode`. - Configure `Runtime` before invoking custom transforms. - Use `bincode` for serde. swc_plugin_runner: - Depend on `swc_common/plugin-rt`. - Pass `Runtime` implementation to the plugins. - Use `bincode` for serde.
This commit is contained in:
parent
0b76d29ae4
commit
380722976a
2
.github/workflows/cargo.yml
vendored
2
.github/workflows/cargo.yml
vendored
@ -116,6 +116,7 @@ jobs:
|
||||
- swc_css
|
||||
- swc_css_ast
|
||||
- swc_css_codegen
|
||||
- swc_css_codegen_macros
|
||||
- swc_css_parser
|
||||
- swc_css_utils
|
||||
- swc_css_visit
|
||||
@ -147,6 +148,7 @@ jobs:
|
||||
- swc_node_base
|
||||
- swc_node_bundler
|
||||
- swc_plugin
|
||||
- swc_plugin_api
|
||||
- swc_plugin_runner
|
||||
- swc_plugin_testing
|
||||
- swc_stylis
|
||||
|
37
Cargo.lock
generated
37
Cargo.lock
generated
@ -220,6 +220,15 @@ version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@ -2490,12 +2499,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_common"
|
||||
version = "0.14.4"
|
||||
version = "0.14.5"
|
||||
dependencies = [
|
||||
"abi_stable",
|
||||
"ahash",
|
||||
"anyhow",
|
||||
"arbitrary",
|
||||
"ast_node",
|
||||
"atty",
|
||||
"bincode",
|
||||
"cfg-if 0.1.10",
|
||||
"either",
|
||||
"from_variant",
|
||||
@ -3123,7 +3135,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_plugin"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"abi_stable",
|
||||
"anyhow",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"swc_atoms 0.2.9",
|
||||
"swc_common",
|
||||
"swc_ecma_ast",
|
||||
"swc_ecma_visit",
|
||||
"swc_plugin_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swc_plugin_api"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"abi_stable",
|
||||
"anyhow",
|
||||
@ -3137,7 +3164,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_plugin_runner"
|
||||
version = "0.13.1"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"abi_stable",
|
||||
"anyhow",
|
||||
@ -3149,13 +3176,13 @@ dependencies = [
|
||||
"swc_ecma_ast",
|
||||
"swc_ecma_codegen",
|
||||
"swc_ecma_parser",
|
||||
"swc_plugin",
|
||||
"swc_plugin_api",
|
||||
"testing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swc_plugin_testing"
|
||||
version = "0.15.1"
|
||||
version = "0.16.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"swc_atoms 0.2.9",
|
||||
|
@ -6,7 +6,7 @@ edition = "2018"
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc_common"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.14.4"
|
||||
version = "0.14.5"
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "dylib"]
|
||||
@ -15,13 +15,20 @@ crate-type = ["lib", "dylib"]
|
||||
concurrent = ["parking_lot"]
|
||||
debug = []
|
||||
default = []
|
||||
diagnostic-serde = []
|
||||
plugin-base = ["abi_stable", "anyhow", "bincode", "diagnostic-serde"]
|
||||
plugin-mode = ["plugin-base"]
|
||||
plugin-rt = ["plugin-base"]
|
||||
tty-emitter = ["atty", "termcolor"]
|
||||
|
||||
[dependencies]
|
||||
abi_stable = {version = "0.10.3", optional = true}
|
||||
ahash = "0.7.4"
|
||||
anyhow = {version = "1.0.45", optional = true}
|
||||
arbitrary = {version = "1", optional = true, features = ["derive"]}
|
||||
ast_node = {version = "0.7.3", path = "../macros/ast_node"}
|
||||
atty = {version = "0.2", optional = true}
|
||||
bincode = {version = "1.3.3", optional = true}
|
||||
cfg-if = "0.1.2"
|
||||
either = "1.5"
|
||||
from_variant = {version = "0.1.3", path = "../macros/from_variant"}
|
||||
|
@ -14,6 +14,10 @@ use std::fmt;
|
||||
|
||||
#[must_use]
|
||||
#[derive(Clone, Debug, PartialEq, Hash)]
|
||||
#[cfg_attr(
|
||||
feature = "diagnostic-serde",
|
||||
derive(serde::Serialize, serde::Deserialize)
|
||||
)]
|
||||
pub struct Diagnostic {
|
||||
pub level: Level,
|
||||
pub message: Vec<(String, Style)>,
|
||||
@ -24,6 +28,10 @@ pub struct Diagnostic {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(
|
||||
feature = "diagnostic-serde",
|
||||
derive(serde::Serialize, serde::Deserialize)
|
||||
)]
|
||||
pub enum DiagnosticId {
|
||||
Error(String),
|
||||
Lint(String),
|
||||
@ -31,6 +39,10 @@ pub enum DiagnosticId {
|
||||
|
||||
/// For example a note attached to an error.
|
||||
#[derive(Clone, Debug, PartialEq, Hash)]
|
||||
#[cfg_attr(
|
||||
feature = "diagnostic-serde",
|
||||
derive(serde::Serialize, serde::Deserialize)
|
||||
)]
|
||||
pub struct SubDiagnostic {
|
||||
pub level: Level,
|
||||
pub message: Vec<(String, Style)>,
|
||||
|
@ -27,7 +27,7 @@ use tracing::debug;
|
||||
#[derive(Clone)]
|
||||
pub struct DiagnosticBuilder<'a> {
|
||||
pub handler: &'a Handler,
|
||||
diagnostic: Box<Diagnostic>,
|
||||
pub(crate) diagnostic: Box<Diagnostic>,
|
||||
allow_suggestions: bool,
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,10 @@ mod snippet;
|
||||
mod styled_buffer;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Hash)]
|
||||
#[cfg_attr(
|
||||
feature = "diagnostic-serde",
|
||||
derive(serde::Serialize, serde::Deserialize)
|
||||
)]
|
||||
pub enum Applicability {
|
||||
MachineApplicable,
|
||||
HasPlaceholders,
|
||||
@ -48,6 +52,10 @@ pub enum Applicability {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Hash)]
|
||||
#[cfg_attr(
|
||||
feature = "diagnostic-serde",
|
||||
derive(serde::Serialize, serde::Deserialize)
|
||||
)]
|
||||
pub struct CodeSuggestion {
|
||||
/// Each substitute can have multiple variants due to multiple
|
||||
/// applicable suggestions
|
||||
@ -91,11 +99,19 @@ pub struct CodeSuggestion {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Hash)]
|
||||
/// See the docs on `CodeSuggestion::substitutions`
|
||||
#[cfg_attr(
|
||||
feature = "diagnostic-serde",
|
||||
derive(serde::Serialize, serde::Deserialize)
|
||||
)]
|
||||
pub struct Substitution {
|
||||
pub parts: Vec<SubstitutionPart>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Hash)]
|
||||
#[cfg_attr(
|
||||
feature = "diagnostic-serde",
|
||||
derive(serde::Serialize, serde::Deserialize)
|
||||
)]
|
||||
pub struct SubstitutionPart {
|
||||
pub span: Span,
|
||||
pub snippet: String,
|
||||
@ -764,6 +780,10 @@ impl Handler {
|
||||
}
|
||||
|
||||
#[derive(Copy, PartialEq, Clone, Hash, Debug)]
|
||||
#[cfg_attr(
|
||||
feature = "diagnostic-serde",
|
||||
derive(serde::Serialize, serde::Deserialize)
|
||||
)]
|
||||
pub enum Level {
|
||||
Bug,
|
||||
Fatal,
|
@ -179,6 +179,10 @@ pub struct StyledString {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Hash)]
|
||||
#[cfg_attr(
|
||||
feature = "diagnostic-serde",
|
||||
derive(serde::Serialize, serde::Deserialize)
|
||||
)]
|
||||
pub enum Style {
|
||||
MainHeaderMsg,
|
||||
HeaderMsg,
|
||||
|
@ -11,6 +11,21 @@
|
||||
//! ## `sourcemap`
|
||||
//!
|
||||
//! Adds methods to generate web sourcemap.
|
||||
//!
|
||||
//! ## `plugin-base`
|
||||
//!
|
||||
//! Base mode for plugins, which can be enabled by `plugin-mode` or `plugin-rt`.
|
||||
//!
|
||||
//! This mode creates a trait which can be used to override `swc_common` itself.
|
||||
//!
|
||||
//! ## `plugin-rt`
|
||||
//!
|
||||
//! Creates an implementation for the plugin trait. This implements simply
|
||||
//! invokes thread-locals declared in `swc_common`.
|
||||
//!
|
||||
//! ## `plugin-mode`
|
||||
//!
|
||||
//! Allows replacing operations related to thread-local variables with a trait.
|
||||
#![deny(unused)]
|
||||
|
||||
pub use self::{
|
||||
@ -46,6 +61,8 @@ pub mod input;
|
||||
pub mod iter;
|
||||
pub mod macros;
|
||||
pub mod pass;
|
||||
#[cfg(feature = "plugin-base")]
|
||||
pub mod plugin;
|
||||
mod pos;
|
||||
mod rustc_data_structures;
|
||||
pub mod serializer;
|
||||
@ -53,3 +70,6 @@ pub mod source_map;
|
||||
pub mod sync;
|
||||
mod syntax_pos;
|
||||
pub mod util;
|
||||
|
||||
#[cfg(all(not(debug_assertions), feature = "plugin-rt", feature = "plugin-mode"))]
|
||||
compile_error!("You can't enable `plugin-rt` and `plugin-mode` at the same time");
|
||||
|
180
common/src/plugin.rs
Normal file
180
common/src/plugin.rs
Normal file
@ -0,0 +1,180 @@
|
||||
//! Plugin support.
|
||||
//!
|
||||
//! We need to replace operations related to thread-local variables in
|
||||
//! `swc_common`.
|
||||
#![allow(unused)]
|
||||
|
||||
use crate::{syntax_pos::Mark, SyntaxContext};
|
||||
use abi_stable::{
|
||||
sabi_trait,
|
||||
std_types::{RBox, RVec},
|
||||
StableAbi,
|
||||
};
|
||||
use anyhow::{Context, Error};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use std::any::type_name;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(StableAbi)]
|
||||
pub struct Runtime {
|
||||
inner: RuntimeImpl_TO<'static, RBox<()>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "plugin-mode")]
|
||||
scoped_tls::scoped_thread_local!(
|
||||
/// If this variable is configured, many methods of
|
||||
/// `swc_common` will be proxied to this variable.
|
||||
pub(crate) static RT: RuntimeImpl_TO<'static, RBox<()>>
|
||||
);
|
||||
|
||||
/// **INTERNAL API**
|
||||
///
|
||||
///
|
||||
/// Don't use this. This is for internal use only.
|
||||
/// This can be changed without breaking semver version bump.
|
||||
#[sabi_trait]
|
||||
pub trait RuntimeImpl {
|
||||
/// Emit a structured diagnostic.
|
||||
///
|
||||
/// - `db`: Serialized version of Diagnostic which is serialized using
|
||||
/// bincode.
|
||||
fn emit_diagnostic(&self, db: RVec<u8>);
|
||||
|
||||
fn fresh_mark(&self, parent: Mark) -> Mark;
|
||||
|
||||
fn parent_mark(&self, mar: Mark) -> Mark;
|
||||
|
||||
fn is_mark_builtin(&self, mark: Mark) -> bool;
|
||||
|
||||
fn set_mark_is_builtin(&self, mark: Mark, is_builtin: bool);
|
||||
|
||||
fn is_mark_descendant_of(&self, mark: Mark, ancestor: Mark) -> bool;
|
||||
|
||||
fn least_ancestor_of_marks(&self, a: Mark, b: Mark) -> Mark;
|
||||
|
||||
fn apply_mark_to_syntax_context_internal(
|
||||
&self,
|
||||
ctxt: SyntaxContext,
|
||||
mark: Mark,
|
||||
) -> SyntaxContext;
|
||||
|
||||
fn remove_mark_of_syntax_context(&self, ctxt: &mut SyntaxContext) -> Mark;
|
||||
|
||||
fn outer_mark_of_syntax_context(&self, ctxt: SyntaxContext) -> Mark;
|
||||
}
|
||||
#[cfg(feature = "plugin-mode")]
|
||||
struct PluginEmitter;
|
||||
|
||||
#[cfg(feature = "plugin-mode")]
|
||||
impl crate::errors::Emitter for PluginEmitter {
|
||||
fn emit(&mut self, db: &crate::errors::DiagnosticBuilder<'_>) {
|
||||
let bytes: RVec<_> = serialize_for_plugin(&db.diagnostic).unwrap().into();
|
||||
|
||||
RT.with(|rt| rt.emit_diagnostic(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "plugin-mode")]
|
||||
pub fn with_runtime<F, Ret>(rt: &Runtime, op: F) -> Ret
|
||||
where
|
||||
F: FnOnce() -> Ret,
|
||||
{
|
||||
use crate::errors::{Handler, HANDLER};
|
||||
|
||||
let handler = Handler::with_emitter(true, false, Box::new(PluginEmitter));
|
||||
RT.set(&rt.inner, || {
|
||||
// We proxy error reporting to the core runtime.
|
||||
HANDLER.set(&handler, || op())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn serialize_for_plugin<T>(t: &T) -> Result<Vec<u8>, Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
bincode::serialize(&t)
|
||||
.with_context(|| format!("failed to serialize `{}` using bincode", type_name::<T>()))
|
||||
}
|
||||
|
||||
pub fn deserialize_for_plugin<T>(bytes: &[u8]) -> Result<T, Error>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
bincode::deserialize(bytes)
|
||||
.with_context(|| format!("failed to deserialize `{}` using bincode", type_name::<T>()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "plugin-rt")]
|
||||
struct PluginRt {
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[cfg(feature = "plugin-rt")]
|
||||
impl RuntimeImpl for PluginRt {
|
||||
fn emit_diagnostic(&self, db: RVec<u8>) {
|
||||
use crate::errors::{Diagnostic, DiagnosticBuilder, HANDLER};
|
||||
|
||||
let diagnostic: Diagnostic =
|
||||
deserialize_for_plugin(db.as_slice()).expect("plugin send invalid diagnostic");
|
||||
|
||||
HANDLER.with(|handler| {
|
||||
DiagnosticBuilder::new_diagnostic(&handler, diagnostic)
|
||||
.note(&format!(
|
||||
"this message is generated by plugin `{}`",
|
||||
&self.name
|
||||
))
|
||||
.emit();
|
||||
});
|
||||
}
|
||||
|
||||
fn fresh_mark(&self, parent: Mark) -> Mark {
|
||||
Mark::fresh(parent)
|
||||
}
|
||||
|
||||
fn parent_mark(&self, mark: Mark) -> Mark {
|
||||
mark.parent()
|
||||
}
|
||||
|
||||
fn is_mark_builtin(&self, mark: Mark) -> bool {
|
||||
mark.is_builtin()
|
||||
}
|
||||
|
||||
fn set_mark_is_builtin(&self, mark: Mark, is_builtin: bool) {
|
||||
mark.set_is_builtin(is_builtin)
|
||||
}
|
||||
|
||||
fn is_mark_descendant_of(&self, mark: Mark, ancestor: Mark) -> bool {
|
||||
mark.is_descendant_of(ancestor)
|
||||
}
|
||||
|
||||
fn least_ancestor_of_marks(&self, a: Mark, b: Mark) -> Mark {
|
||||
Mark::least_ancestor(a, b)
|
||||
}
|
||||
|
||||
fn apply_mark_to_syntax_context_internal(
|
||||
&self,
|
||||
ctxt: SyntaxContext,
|
||||
mark: Mark,
|
||||
) -> SyntaxContext {
|
||||
ctxt.apply_mark(mark)
|
||||
}
|
||||
|
||||
fn remove_mark_of_syntax_context(&self, ctxt: &mut SyntaxContext) -> Mark {
|
||||
ctxt.remove_mark()
|
||||
}
|
||||
|
||||
fn outer_mark_of_syntax_context(&self, ctxt: SyntaxContext) -> Mark {
|
||||
ctxt.outer()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "plugin-rt")]
|
||||
pub fn get_runtime_for_plugin(plugin_name: String) -> Runtime {
|
||||
use abi_stable::erased_types::TD_Opaque;
|
||||
|
||||
let rt = PluginRt { name: plugin_name };
|
||||
|
||||
let rt: RuntimeImpl_TO<'_, RBox<_>> = RuntimeImpl_TO::from_value(rt, TD_Opaque);
|
||||
|
||||
Runtime { inner: rt }
|
||||
}
|
@ -176,6 +176,10 @@ impl FileName {
|
||||
/// - they can have a *label*. In this case, the label is written next to the
|
||||
/// mark in the snippet when we render.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
#[cfg_attr(
|
||||
feature = "diagnostic-serde",
|
||||
derive(serde::Serialize, serde::Deserialize)
|
||||
)]
|
||||
pub struct MultiSpan {
|
||||
primary_spans: Vec<Span>,
|
||||
span_labels: Vec<(Span, String)>,
|
||||
|
@ -27,6 +27,8 @@ use std::{
|
||||
/// marks).
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
#[cfg_attr(feature = "abi_stable", repr(transparent))]
|
||||
#[cfg_attr(feature = "abi_stable", derive(abi_stable::StableAbi))]
|
||||
pub struct SyntaxContext(u32);
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
@ -48,6 +50,8 @@ struct SyntaxContextData {
|
||||
|
||||
/// A mark is a unique id associated with a macro expansion.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[cfg_attr(feature = "abi_stable", repr(transparent))]
|
||||
#[cfg_attr(feature = "abi_stable", derive(abi_stable::StableAbi))]
|
||||
pub struct Mark(u32);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -58,6 +62,11 @@ struct MarkData {
|
||||
|
||||
impl Mark {
|
||||
pub fn fresh(parent: Mark) -> Self {
|
||||
#[cfg(feature = "plugin-mode")]
|
||||
if crate::plugin::RT.is_set() {
|
||||
return crate::plugin::RT.with(|rt| rt.fresh_mark(parent));
|
||||
}
|
||||
|
||||
HygieneData::with(|data| {
|
||||
data.marks.push(MarkData {
|
||||
parent,
|
||||
@ -86,22 +95,44 @@ impl Mark {
|
||||
|
||||
#[inline]
|
||||
pub fn parent(self) -> Mark {
|
||||
#[cfg(feature = "plugin-mode")]
|
||||
if crate::plugin::RT.is_set() {
|
||||
return crate::plugin::RT.with(|rt| rt.parent_mark(self));
|
||||
}
|
||||
|
||||
HygieneData::with(|data| data.marks[self.0 as usize].parent)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_builtin(self) -> bool {
|
||||
assert_ne!(self, Mark::root());
|
||||
|
||||
#[cfg(feature = "plugin-mode")]
|
||||
if crate::plugin::RT.is_set() {
|
||||
return crate::plugin::RT.with(|rt| rt.is_mark_builtin(self));
|
||||
}
|
||||
|
||||
HygieneData::with(|data| data.marks[self.0 as usize].is_builtin)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_is_builtin(self, is_builtin: bool) {
|
||||
assert_ne!(self, Mark::root());
|
||||
|
||||
#[cfg(feature = "plugin-mode")]
|
||||
if crate::plugin::RT.is_set() {
|
||||
return crate::plugin::RT.with(|rt| rt.set_mark_is_builtin(self, is_builtin));
|
||||
}
|
||||
|
||||
HygieneData::with(|data| data.marks[self.0 as usize].is_builtin = is_builtin)
|
||||
}
|
||||
|
||||
pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
|
||||
#[cfg(feature = "plugin-mode")]
|
||||
if crate::plugin::RT.is_set() {
|
||||
return crate::plugin::RT.with(|rt| rt.is_mark_descendant_of(self, ancestor));
|
||||
}
|
||||
|
||||
HygieneData::with(|data| {
|
||||
while self != ancestor {
|
||||
if self == Mark::root() {
|
||||
@ -121,7 +152,13 @@ impl Mark {
|
||||
/// assert!(a.is_descendant_of(la))
|
||||
/// assert!(b.is_descendant_of(la))
|
||||
/// ```
|
||||
#[allow(unused_mut)]
|
||||
pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
|
||||
#[cfg(feature = "plugin-mode")]
|
||||
if crate::plugin::RT.is_set() {
|
||||
return crate::plugin::RT.with(|rt| rt.least_ancestor_of_marks(a, b));
|
||||
}
|
||||
|
||||
HygieneData::with(|data| {
|
||||
// Compute the path from a to the root
|
||||
let mut a_path = HashSet::<Mark>::default();
|
||||
@ -208,6 +245,12 @@ impl SyntaxContext {
|
||||
}
|
||||
|
||||
fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
|
||||
#[cfg(feature = "plugin-mode")]
|
||||
if crate::plugin::RT.is_set() {
|
||||
return crate::plugin::RT
|
||||
.with(|rt| rt.apply_mark_to_syntax_context_internal(self, mark));
|
||||
}
|
||||
|
||||
HygieneData::with(|data| {
|
||||
let syntax_contexts = &mut data.syntax_contexts;
|
||||
let mut opaque = syntax_contexts[self.0 as usize].opaque;
|
||||
@ -258,6 +301,11 @@ impl SyntaxContext {
|
||||
/// the SyntaxContext for the invocation of f that created g1.
|
||||
/// Returns the mark that was removed.
|
||||
pub fn remove_mark(&mut self) -> Mark {
|
||||
#[cfg(feature = "plugin-mode")]
|
||||
if crate::plugin::RT.is_set() {
|
||||
return crate::plugin::RT.with(|rt| rt.remove_mark_of_syntax_context(self));
|
||||
}
|
||||
|
||||
HygieneData::with(|data| {
|
||||
let outer_mark = data.syntax_contexts[self.0 as usize].outer_mark;
|
||||
*self = data.syntax_contexts[self.0 as usize].prev_ctxt;
|
||||
@ -375,6 +423,11 @@ impl SyntaxContext {
|
||||
|
||||
#[inline]
|
||||
pub fn outer(self) -> Mark {
|
||||
#[cfg(feature = "plugin-mode")]
|
||||
if crate::plugin::RT.is_set() {
|
||||
return crate::plugin::RT.with(|rt| rt.outer_mark_of_syntax_context(self));
|
||||
}
|
||||
|
||||
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
|
||||
}
|
||||
}
|
||||
|
@ -93,6 +93,7 @@
|
||||
"prec",
|
||||
"PREC",
|
||||
"proto",
|
||||
"proxied",
|
||||
"punct",
|
||||
"putc",
|
||||
"qself",
|
||||
|
@ -78,6 +78,7 @@ allow = [
|
||||
"BSD-3-Clause",
|
||||
"CC0-1.0",
|
||||
"ISC",
|
||||
"Zlib",
|
||||
]
|
||||
# List of explictly disallowed licenses
|
||||
# See https://spdx.org/licenses/ for list of possible licenses
|
||||
|
@ -6,7 +6,7 @@ edition = "2018"
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc_plugin"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -16,6 +16,7 @@ anyhow = "1.0.41"
|
||||
serde = "1.0.126"
|
||||
serde_json = "1.0.64"
|
||||
swc_atoms = {version = "0.2.7", path = "../atoms"}
|
||||
swc_common = {version = "0.14.0", path = "../common"}
|
||||
swc_common = {version = "0.14.0", path = "../common", features = ["plugin-mode"]}
|
||||
swc_ecma_ast = {version = "0.56.0", path = "../ecmascript/ast"}
|
||||
swc_ecma_visit = {version = "0.42.0", path = "../ecmascript/visit"}
|
||||
swc_plugin_api = {version = "0.1.0", path = "./api"}
|
||||
|
21
plugin/api/Cargo.toml
Normal file
21
plugin/api/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
description = "API for the swc plugins"
|
||||
documentation = "https://rustdoc.swc.rs/swc_plugin/"
|
||||
edition = "2018"
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc_plugin_api"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.1.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
abi_stable = "0.10.2"
|
||||
anyhow = "1.0.41"
|
||||
serde = "1.0.126"
|
||||
serde_json = "1.0.64"
|
||||
swc_atoms = {version = "0.2.7", path = "../../atoms"}
|
||||
swc_common = {version = "0.14.5", path = "../../common", features = ["plugin-base"]}
|
||||
swc_ecma_ast = {version = "0.56.0", path = "../../ecmascript/ast"}
|
||||
swc_ecma_visit = {version = "0.42.0", path = "../../ecmascript/visit"}
|
31
plugin/api/src/lib.rs
Normal file
31
plugin/api/src/lib.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use abi_stable::{
|
||||
library::RootModule,
|
||||
package_version_strings,
|
||||
sabi_types::VersionStrings,
|
||||
std_types::{RResult, RStr, RString, RVec},
|
||||
StableAbi,
|
||||
};
|
||||
|
||||
/// Don't use this directly.
|
||||
#[repr(C)]
|
||||
#[derive(StableAbi)]
|
||||
#[sabi(kind(Prefix(prefix_ref = "SwcPluginRef")))]
|
||||
#[sabi(missing_field(panic))]
|
||||
pub struct SwcPlugin {
|
||||
#[sabi(last_prefix_field)]
|
||||
pub process_js: Option<
|
||||
extern "C" fn(
|
||||
rt: swc_common::plugin::Runtime,
|
||||
config_json: RStr,
|
||||
ast: RVec<u8>,
|
||||
) -> RResult<RVec<u8>, RString>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl RootModule for SwcPluginRef {
|
||||
abi_stable::declare_root_module_statics! {SwcPluginRef}
|
||||
|
||||
const BASE_NAME: &'static str = "swc_plugin";
|
||||
const NAME: &'static str = "swc_plugin";
|
||||
const VERSION_STRINGS: VersionStrings = package_version_strings!();
|
||||
}
|
@ -6,7 +6,7 @@ edition = "2018"
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc_plugin_runner"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.13.1"
|
||||
version = "0.14.0"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
@ -16,10 +16,10 @@ libloading = "0.7.0"
|
||||
serde = {version = "1.0.126", features = ["derive"]}
|
||||
serde_json = "1.0.64"
|
||||
swc_atoms = "0.2.7"
|
||||
swc_common = {version = "0.14.0", path = "../../common"}
|
||||
swc_common = {version = "0.14.0", path = "../../common", features = ["plugin-rt"]}
|
||||
swc_ecma_ast = {version = "0.56.0", path = "../../ecmascript/ast"}
|
||||
swc_ecma_parser = {version = "0.76.3", path = "../../ecmascript/parser"}
|
||||
swc_plugin = {version = "0.9.0", path = "../"}
|
||||
swc_plugin_api = {version = "0.1.0", path = "../api"}
|
||||
|
||||
[dev-dependencies]
|
||||
swc_ecma_codegen = {version = "0.78.1", path = "../../ecmascript/codegen"}
|
||||
|
@ -1,34 +1,38 @@
|
||||
use abi_stable::{
|
||||
library::RootModule,
|
||||
std_types::{RResult, RStr, RString},
|
||||
std_types::{RResult, RStr},
|
||||
};
|
||||
use anyhow::{anyhow, Context, Error};
|
||||
use std::path::Path;
|
||||
use swc_common::plugin::{deserialize_for_plugin, serialize_for_plugin};
|
||||
use swc_ecma_ast::Program;
|
||||
use swc_plugin::SwcPluginRef;
|
||||
use swc_plugin_api::SwcPluginRef;
|
||||
|
||||
pub fn apply_js_plugin(program: &Program, path: &Path) -> Result<Program, Error> {
|
||||
pub fn apply_js_plugin(
|
||||
plugin_name: &str,
|
||||
path: &Path,
|
||||
config_json: &str,
|
||||
program: &Program,
|
||||
) -> Result<Program, Error> {
|
||||
(|| -> Result<_, Error> {
|
||||
let plugin_rt = swc_common::plugin::get_runtime_for_plugin(plugin_name.to_string());
|
||||
|
||||
let plugin = SwcPluginRef::load_from_file(path).context("failed to load plugin")?;
|
||||
|
||||
let config_json = "{}";
|
||||
let ast_json =
|
||||
serde_json::to_string(&program).context("failed to serialize program as json")?;
|
||||
let ast_serde = serialize_for_plugin(&program).context("failed to serialize ast")?;
|
||||
|
||||
let plugin_fn = plugin
|
||||
.process_js()
|
||||
.ok_or_else(|| anyhow!("the plugin does not support transforming js"))?;
|
||||
|
||||
let new_ast = plugin_fn(RStr::from(config_json), RString::from(ast_json));
|
||||
let new_ast = plugin_fn(plugin_rt, RStr::from(config_json), ast_serde.into());
|
||||
|
||||
let new = match new_ast {
|
||||
RResult::ROk(v) => v,
|
||||
RResult::RErr(err) => return Err(anyhow!("plugin returned an error\n{}", err)),
|
||||
};
|
||||
let new = new.into_string();
|
||||
|
||||
let new = serde_json::from_str(&new)
|
||||
.with_context(|| format!("plugin generated invalid ast: `{}`", new))?;
|
||||
let new: Program = deserialize_for_plugin(new.as_slice())
|
||||
.with_context(|| format!("plugin generated invalid ast`"))?;
|
||||
|
||||
Ok(new)
|
||||
})()
|
||||
|
@ -1,39 +1,18 @@
|
||||
/// Reexported for convenience.
|
||||
use abi_stable::{
|
||||
library::RootModule,
|
||||
package_version_strings,
|
||||
sabi_types::VersionStrings,
|
||||
std_types::{RResult, RStr, RString},
|
||||
StableAbi,
|
||||
};
|
||||
use abi_stable::std_types::{RResult, RStr, RString, RVec};
|
||||
use anyhow::Context;
|
||||
use serde::de::DeserializeOwned;
|
||||
use swc_common::plugin::{deserialize_for_plugin, serialize_for_plugin};
|
||||
use swc_ecma_ast::Program;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(StableAbi)]
|
||||
#[sabi(kind(Prefix(prefix_ref = "SwcPluginRef")))]
|
||||
#[sabi(missing_field(panic))]
|
||||
pub struct SwcPlugin {
|
||||
#[sabi(last_prefix_field)]
|
||||
pub process_js:
|
||||
Option<extern "C" fn(config_str: RStr, ast_json: RString) -> RResult<RString, RString>>,
|
||||
}
|
||||
|
||||
impl RootModule for SwcPluginRef {
|
||||
abi_stable::declare_root_module_statics! {SwcPluginRef}
|
||||
|
||||
const BASE_NAME: &'static str = "swc_plugin";
|
||||
const NAME: &'static str = "swc_plugin";
|
||||
const VERSION_STRINGS: VersionStrings = package_version_strings!();
|
||||
}
|
||||
pub use swc_plugin_api::*;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn invoke_js_plugin<C, F>(
|
||||
rt: swc_common::plugin::Runtime,
|
||||
op: fn(C) -> F,
|
||||
config_json: RStr,
|
||||
ast_json: RString,
|
||||
) -> RResult<RString, RString>
|
||||
ast: RVec<u8>,
|
||||
) -> RResult<RVec<u8>, RString>
|
||||
where
|
||||
C: DeserializeOwned,
|
||||
F: swc_ecma_visit::Fold,
|
||||
@ -47,31 +26,32 @@ where
|
||||
Err(err) => return RResult::RErr(format!("{:?}", err).into()),
|
||||
};
|
||||
|
||||
let ast =
|
||||
serde_json::from_str(ast_json.as_str()).context("failed to deserialize ast string as json");
|
||||
let ast = deserialize_for_plugin(ast.as_slice());
|
||||
let ast: Program = match ast {
|
||||
Ok(v) => v,
|
||||
Err(err) => return RResult::RErr(format!("{:?}", err).into()),
|
||||
};
|
||||
|
||||
let mut tr = op(config);
|
||||
swc_common::plugin::with_runtime(&rt, || {
|
||||
let mut tr = op(config);
|
||||
|
||||
let ast = ast.fold_with(&mut tr);
|
||||
let ast = ast.fold_with(&mut tr);
|
||||
|
||||
let res = match serde_json::to_string(&ast) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
return RResult::RErr(
|
||||
format!(
|
||||
"failed to serialize swc_ecma_ast::Program as json: {:?}",
|
||||
err
|
||||
let res = match serialize_for_plugin(&ast) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
return RResult::RErr(
|
||||
format!(
|
||||
"failed to serialize swc_ecma_ast::Program as json: {:?}",
|
||||
err
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
RResult::ROk(res.into())
|
||||
RResult::ROk(res.into())
|
||||
})
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
@ -80,13 +60,14 @@ macro_rules! define_js_plugin {
|
||||
#[abi_stable::export_root_module]
|
||||
pub fn swc_library() -> $crate::SwcPluginRef {
|
||||
extern "C" fn swc_js_plugin(
|
||||
rt: swc_common::plugin::Runtime,
|
||||
config_json: abi_stable::std_types::RStr,
|
||||
ast_json: abi_stable::std_types::RString,
|
||||
ast: abi_stable::std_types::RVec<u8>,
|
||||
) -> abi_stable::std_types::RResult<
|
||||
abi_stable::std_types::RString,
|
||||
abi_stable::std_types::RVec<u8>,
|
||||
abi_stable::std_types::RString,
|
||||
> {
|
||||
$crate::invoke_js_plugin($fn_name, config_json, ast_json)
|
||||
$crate::invoke_js_plugin(rt, $fn_name, config_json, ast)
|
||||
}
|
||||
use abi_stable::prefix_type::PrefixTypeTrait;
|
||||
|
||||
|
@ -6,7 +6,7 @@ edition = "2018"
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc_plugin_testing"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.15.1"
|
||||
version = "0.16.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -18,4 +18,4 @@ swc_ecma_ast = {version = "0.56.0", path = "../../ecmascript/ast"}
|
||||
swc_ecma_codegen = {version = "0.78.1", path = "../../ecmascript/codegen"}
|
||||
swc_ecma_utils = {version = "0.50.0", path = "../../ecmascript/utils"}
|
||||
swc_ecma_visit = {version = "0.42.0", path = "../../ecmascript/visit"}
|
||||
swc_plugin = {version = "0.9.0", path = "../"}
|
||||
swc_plugin = {version = "0.10.0", path = "../"}
|
||||
|
Loading…
Reference in New Issue
Block a user