Skip to content

Commit

Permalink
fix: Decorator metadata (#1248)
Browse files Browse the repository at this point in the history
swc_ecma_transforms:
 - Emit proper typename for `design:type` used with enum. (#1160)
  • Loading branch information
kdy1 authored Dec 3, 2020
1 parent 5478463 commit 2e29d78
Show file tree
Hide file tree
Showing 20 changed files with 426 additions and 25 deletions.
10 changes: 8 additions & 2 deletions benches/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,14 @@ fn bench_codegen(b: &mut Bencher, _target: JscTarget) {

b.iter(|| {
black_box(
c.print(&module, SourceMapsConfig::Bool(false), None, false)
.unwrap(),
c.print(
&module,
JscTarget::Es2020,
SourceMapsConfig::Bool(false),
None,
false,
)
.unwrap(),
);
})
}
Expand Down
2 changes: 1 addition & 1 deletion bundler/tests/fixture/deno-8574/output/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ function onceStrict(fn) {
f.called = false;
return f;
}
const VERSION1 = "5.4.11";
const VERSION1 = "5.4.12";
function getBufferResponse(response) {
return response.arrayBuffer();
}
Expand Down
2 changes: 1 addition & 1 deletion ecmascript/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_ecmascript"
repository = "https://github.com/swc-project/swc.git"
version = "0.14.4"
version = "0.14.5"

[features]
codegen = ["swc_ecma_codegen"]
Expand Down
4 changes: 2 additions & 2 deletions ecmascript/codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs"]
license = "Apache-2.0/MIT"
name = "swc_ecma_codegen"
repository = "https://github.com/swc-project/swc.git"
version = "0.41.3"
version = "0.41.4"

[dependencies]
bitflags = "1"
Expand All @@ -17,8 +17,8 @@ swc_atoms = {version = "0.2", path = "../../atoms"}
swc_common = {version = "0.10.0", path = "../../common"}
swc_ecma_ast = {version = "0.35.0", path = "../ast"}
swc_ecma_codegen_macros = {version = "0.5", path = "./macros"}
swc_ecma_parser = {version = "0.43.0", path = "../parser"}

[dev-dependencies]
swc_common = {version = "0.10.0", path = "../../common", features = ["sourcemap"]}
swc_ecma_parser = {version = "0.43.0", path = "../parser"}
testing = {version = "0.10.0", path = "../../testing"}
17 changes: 15 additions & 2 deletions ecmascript/codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use swc_common::{
};
use swc_ecma_ast::*;
use swc_ecma_codegen_macros::emitter;
use swc_ecma_parser::JscTarget;

#[macro_use]
pub mod macros;
Expand Down Expand Up @@ -388,7 +389,13 @@ impl<'a> Emitter<'a> {
// self.wr.write_str_lit(node.span, &s)?;
// return Ok(());
// }
let value = escape(&self.cm, node.span, &node.value, single_quote);
let value = escape(
&self.cm,
self.wr.target(),
node.span,
&node.value,
single_quote,
);
// let value = node.value.replace("\n", "\\n");

let single_quote = single_quote.unwrap_or(false);
Expand Down Expand Up @@ -2412,7 +2419,13 @@ fn unescape(s: &str) -> String {
result
}

fn escape<'s>(cm: &SourceMap, span: Span, s: &'s str, single_quote: Option<bool>) -> Cow<'s, str> {
fn escape<'s>(
cm: &SourceMap,
target: JscTarget,
span: Span,
s: &'s str,
single_quote: Option<bool>,
) -> Cow<'s, str> {
if span.is_dummy() {
return Cow::Owned(s.escape_default().to_string());
}
Expand Down
12 changes: 12 additions & 0 deletions ecmascript/codegen/src/text_writer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub use self::{basic_impl::JsWriter, semicolon::omit_trailing_semi};
use super::*;
use swc_common::Span;
use swc_ecma_parser::JscTarget;

mod basic_impl;
mod semicolon;
Expand All @@ -12,6 +13,17 @@ pub type Symbol = Str;
///
/// Ported from `EmitWriteJs`.
pub trait WriteJs {
/// Returns javascript target which should be used while generating code.
///
/// This defaults to [JscTarget::Es2020] because it preserves input as much
/// as possible.
///
/// Implementor **should return same value** regardless how much time it is
/// called.
fn target(&self) -> JscTarget {
JscTarget::Es2020
}

fn increase_indent(&mut self) -> Result;
fn decrease_indent(&mut self) -> Result;

Expand Down
59 changes: 58 additions & 1 deletion ecmascript/transforms/src/proposals/decorators/legacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,30 @@ use crate::util::{
alias_if_required, default_constructor, prepend, prop_name_to_expr_value, undefined,
ExprFactory, ModuleItemLike, StmtLike,
};
use fxhash::FxHashMap;
use smallvec::SmallVec;
use std::mem::replace;
use swc_common::{util::move_map::MoveMap, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith, VisitWith};
use swc_ecma_utils::{ident::IdentLike, Id};
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith, Node, Visit, VisitWith};

mod metadata;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum EnumKind {
Mixed,
Str,
Num,
}

#[derive(Debug)]
pub(super) struct Legacy {
metadata: bool,
uninitialized_vars: Vec<VarDeclarator>,
initialized_vars: Vec<VarDeclarator>,
exports: Vec<ExportSpecifier>,
enums: FxHashMap<Id, EnumKind>,
}

pub(super) fn new(metadata: bool) -> Legacy {
Expand All @@ -26,6 +36,48 @@ pub(super) fn new(metadata: bool) -> Legacy {
uninitialized_vars: Default::default(),
initialized_vars: Default::default(),
exports: Default::default(),
enums: Default::default(),
}
}

impl Visit for Legacy {
fn visit_ts_enum_decl(&mut self, e: &TsEnumDecl, _: &dyn Node) {
let enum_kind = e
.members
.iter()
.map(|member| member.init.as_ref())
.map(|init| match init {
Some(e) => match &**e {
Expr::Lit(lit) => match lit {
Lit::Str(_) => EnumKind::Str,
Lit::Num(_) => EnumKind::Num,
_ => EnumKind::Mixed,
},
_ => EnumKind::Mixed,
},
None => EnumKind::Num,
})
.fold(None, |opt: Option<EnumKind>, item| {
//
let a = match item {
EnumKind::Mixed => return Some(EnumKind::Mixed),
_ => item,
};

let b = match opt {
Some(EnumKind::Mixed) => return Some(EnumKind::Mixed),
Some(v) => v,
None => return Some(item),
};
if a == b {
return Some(a);
} else {
return Some(EnumKind::Mixed);
}
});
if let Some(kind) = enum_kind {
self.enums.insert(e.id.to_id(), kind);
}
}
}

Expand Down Expand Up @@ -78,6 +130,10 @@ impl Fold for Legacy {
}

fn fold_module(&mut self, m: Module) -> Module {
// Collect required information.
// For example, value type of enum affects codegen
m.visit_with(&Invalid { span: DUMMY_SP }, self);

let mut m = m.fold_children_with(self);

if !self.uninitialized_vars.is_empty() {
Expand Down Expand Up @@ -214,6 +270,7 @@ impl Legacy {
let i = c.ident.clone();

c = c.fold_with(&mut ParamMetadata).fold_with(&mut Metadata {
enums: &self.enums,
class_name: i.as_ref(),
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use fxhash::FxHashMap;
use swc_common::{util::move_map::MoveMap, Spanned, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_utils::{undefined, ExprFactory};
use swc_ecma_utils::{ident::IdentLike, undefined, ExprFactory, Id};
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith};

use super::EnumKind;

/// https://github.com/leonardfactory/babel-plugin-transform-typescript-metadata/blob/master/src/parameter/parameterVisitor.ts
pub(super) struct ParamMetadata;

Expand Down Expand Up @@ -118,6 +121,8 @@ impl ParamMetadata {

/// https://github.com/leonardfactory/babel-plugin-transform-typescript-metadata/blob/master/src/metadata/metadataVisitor.ts
pub(super) struct Metadata<'a> {
pub(super) enums: &'a FxHashMap<Id, EnumKind>,

pub(super) class_name: Option<&'a Ident>,
}

Expand Down Expand Up @@ -219,6 +224,35 @@ impl Fold for Metadata<'_> {
return p;
}

if let Some(name) = p
.type_ann
.as_ref()
.map(|ty| &ty.type_ann)
.map(|type_ann| match &**type_ann {
TsType::TsTypeRef(r) => Some(r),
_ => None,
})
.flatten()
.map(|r| match &r.type_name {
TsEntityName::TsQualifiedName(_) => None,
TsEntityName::Ident(i) => Some(i),
})
.flatten()
{
if let Some(kind) = self.enums.get(&name.to_id()) {
let dec = self.create_metadata_design_decorator(
"design:type",
match kind {
EnumKind::Mixed => quote_ident!("Object").as_arg(),
EnumKind::Str => quote_ident!("String").as_arg(),
EnumKind::Num => quote_ident!("Number").as_arg(),
},
);
p.decorators.push(dec);
return p;
}
}

let dec = self.create_metadata_design_decorator(
"design:type",
serialize_type(self.class_name, p.type_ann.as_ref()).as_arg(),
Expand Down
51 changes: 51 additions & 0 deletions ecmascript/transforms/tests/proposal_decorators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5129,3 +5129,54 @@ let Sample = _class = _dec11(_class = _dec10(_class = _dec9(((_class = class Sam
], Object.getOwnPropertyDescriptor(_class.prototype, "assignments"), _class.prototype), _class)) || _class) || _class) || _class;"##,
ok_if_code_eq
);

test!(
ts(),
|_| decorators(Config {
legacy: true,
emit_metadata: true,
}),
issue_1160_1,
"
enum MyEnum {
x = \"xxx\",
y = \"yyy\"
}
class Xpto {
@Decorator()
value!: MyEnum;
}
function Decorator() {
return function (...args) {};
}
",
"
var _class, _descriptor;
enum MyEnum {
x = \"xxx\",
y = \"yyy\"
}
var _dec = Decorator(), _dec1 = typeof Reflect !== \"undefined\" && typeof Reflect.metadata === \
\"function\" && Reflect.metadata(\"design:type\", String);
let Xpto = ((_class = class Xpto {
constructor(){
_initializerDefineProperty(this, \"value\", _descriptor, this);
}
}) || _class, _descriptor = _applyDecoratedDescriptor(_class.prototype, \"value\", [
_dec,
_dec1
], {
configurable: true,
enumerable: true,
writable: true,
initializer: void 0
}), _class);
function Decorator() {
return function(...args) {
};
}
",
ok_if_code_eq
);
18 changes: 15 additions & 3 deletions native/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ impl Task for BundleTask {
type JsValue = JsObject;

fn compute(&mut self) -> napi::Result<Self::Output> {
// Defaults to es3
let codegen_target = self
.config
.static_items
.config
.codegen_target()
.unwrap_or_default();

let res = catch_unwind(AssertUnwindSafe(|| {
let bundler = Bundler::new(
self.swc.globals(),
Expand Down Expand Up @@ -125,9 +133,13 @@ impl Task for BundleTask {
})
.unwrap_or(false);

let output =
self.swc
.print(&m, SourceMapsConfig::Bool(true), None, minify)?;
let output = self.swc.print(
&m,
codegen_target,
SourceMapsConfig::Bool(true),
None,
minify,
)?;

Ok((k, output))
})
Expand Down
11 changes: 11 additions & 0 deletions native/src/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use swc::{
Compiler, TransformOutput,
};
use swc_ecma_ast::Program;
use swc_ecma_parser::JscTarget;

// ----- Printing -----

Expand All @@ -26,6 +27,12 @@ impl Task for PrintTask {
self.c
.print(
&self.program,
self.options
.config
.as_ref()
.map(|config| &config.jsc)
.map(|jsc| jsc.target)
.unwrap_or(JscTarget::Es2020),
self.options
.source_maps
.clone()
Expand Down Expand Up @@ -72,9 +79,13 @@ pub fn print_sync(cx: CallContext) -> napi::Result<JsObject> {

let options: Options = cx.get_deserialized(1)?;

// Defaults to es3
let codegen_target = options.codegen_target().unwrap_or_default();

let result = {
c.print(
&program,
codegen_target,
options
.source_maps
.clone()
Expand Down
Loading

0 comments on commit 2e29d78

Please sign in to comment.