fix(es/compat): Preserve constructor parameters (#2975)

swc_ecma_visit:
 - Add cargo feature `debug`.
 - Add more context for `tracing` when `debug` is enabled.

swc_ecma_utils:
 - Add some logging.

swc_ecma_transforms_compat:
 - Add some logging.
 - `destructuring`: Don't drop patterns. (Closes #2139)

testing:
 - Enable logging by default.
 - Configure logging for `Tester`.
This commit is contained in:
Donny/강동윤 2021-12-06 20:08:50 +09:00 committed by GitHub
parent 694d3c5072
commit f052a65bf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 410 additions and 68 deletions

View File

@ -50,11 +50,9 @@ jobs:
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2021-09-30
override: true
- name: Run benchmark
run: cargo +nightly-2021-09-30 bench --all --exclude swc_plugin | tee output.txt
run: cargo bench --all --exclude swc_plugin | tee output.txt
- name: Download previous benchmark results
run: mkdir raw-data && curl -o raw-data/benchmark-data.json https://raw.githubusercontent.com/swc-project/raw-data/gh-pages/benchmark-data.json

15
Cargo.lock generated
View File

@ -1718,9 +1718,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
version = "1.0.32"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a"
dependencies = [
"unicode-xid",
]
@ -2932,7 +2932,7 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_compat"
version = "0.55.0"
version = "0.55.1"
dependencies = [
"ahash",
"arrayvec",
@ -2955,6 +2955,7 @@ dependencies = [
"swc_ecma_utils",
"swc_ecma_visit",
"testing",
"tracing",
]
[[package]]
@ -3117,7 +3118,7 @@ dependencies = [
[[package]]
name = "swc_ecma_utils"
version = "0.54.0"
version = "0.54.1"
dependencies = [
"once_cell",
"rayon",
@ -3127,18 +3128,20 @@ dependencies = [
"swc_ecma_parser",
"swc_ecma_visit",
"testing",
"tracing",
"unicode-xid",
]
[[package]]
name = "swc_ecma_visit"
version = "0.44.1"
version = "0.44.2"
dependencies = [
"num-bigint",
"swc_atoms 0.2.9",
"swc_common",
"swc_ecma_ast",
"swc_visit",
"tracing",
]
[[package]]
@ -3451,7 +3454,7 @@ dependencies = [
[[package]]
name = "testing"
version = "0.15.2"
version = "0.15.3"
dependencies = [
"ansi_term 0.12.1",
"difference",

View File

@ -32,6 +32,7 @@ concurrent = [
"swc_ecma_transforms_compat/concurrent",
"swc_ecma_transforms_optimization/concurrent",
]
debug = ["swc_ecma_visit/debug"]
plugin = [
"swc_plugin_runner",
]

View File

@ -0,0 +1,13 @@
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true,
"dynamicImport": true
},
"externalHelpers": true
},
"env": {
"targets": "Chrome >= 48"
}
}

View File

@ -0,0 +1,208 @@
export default class ReusablePayments extends PureComponent<Props> {
public componentDidMount() {
this.setDefaultReusablePayment();
}
public componentDidUpdate(prevProps: Props) {
if (
prevProps.reusablePaymentSources !==
this.props.reusablePaymentSources
) {
this.setDefaultReusablePayment();
}
}
private setDefaultReusablePayment(
skipPaymentSource?: StripePaymentSourceFragment
) {
const { reusablePaymentSources, selectedReusablePayment, onChange } =
this.props;
const validReusablePaymentSources = reusablePaymentSources.filter(
(ps) =>
ps.__typename === "StripePaymentSource" &&
ps !== skipPaymentSource
) as Array<StripePaymentSourceFragment>;
if (selectedReusablePayment === null) {
return;
}
if (
selectedReusablePayment &&
validReusablePaymentSources.find(
(ps) => ps === selectedReusablePayment
)
) {
// selectedReusablePayment still valid
return;
}
if (!validReusablePaymentSources.length) {
onChange(null);
return;
}
const ps = validReusablePaymentSources.find((ps) => ps.isDefault);
onChange(ps || validReusablePaymentSources[0]);
}
private handleSelectPayment = (selected: StripePaymentSourceFragment) => {
return this.props.onChange(selected);
};
private handleDeletePaymentSource = (
id: string,
deletePaymentSource: DeletePaymentSourceMutationFn
) => {
const { selectedReusablePayment, onChange } = this.props;
if (
onChange &&
selectedReusablePayment &&
selectedReusablePayment.id === id
) {
this.setDefaultReusablePayment(selectedReusablePayment);
}
deletePaymentSource({
variables: {
id,
},
});
};
public render() {
const { selectedReusablePayment, reusablePaymentSources } = this.props;
const stripePaymentSources = reusablePaymentSources.filter(
(ps) => ps.__typename === "StripePaymentSource"
) as Array<StripePaymentSourceFragment>;
if (!stripePaymentSources.length) {
return null;
}
return (
<DeletePaymentSourceComponent
onCompleted={({ deletePaymentSource }) => {
if (deletePaymentSource.success) {
toast.success("Successfully removed Card");
return;
}
toast.error(deletePaymentSource.error);
}}
onError={(error) => {
toast.error(error.message);
}}
refetchQueries={["ReusablePaymentSources"]}
>
{(deletePaymentSource) => (
<div className={styles.selectionList}>
{stripePaymentSources.map((payment) => {
const cardIcon =
"brand" in payment.paymentEntity ? (
payment.paymentEntity.brand === "Visa" ? (
<Visa />
) : payment.paymentEntity.brand ===
"MasterCard" ? (
<MasterCard />
) : payment.paymentEntity.brand ===
"American Express" ? (
<AmericanExpress />
) : payment.paymentEntity.brand ===
"Discover" ? (
<Discover />
) : null
) : null;
return (
<div
key={payment.id}
className={classNames(styles.creditCard, {
[styles.creditCardChecked]:
selectedReusablePayment === payment,
})}
>
<div className={styles.creditCardContainer}>
<Radio
value={payment.id}
checked={
selectedReusablePayment ===
payment
}
onChange={this.handleSelectPayment.bind(
this,
payment
)}
>
<div
className={styles.paymentHeader}
>
<div
className={
styles.paymentHeaderContainer
}
>
{cardIcon}
{payment.paymentEntity
.__typename ===
"PaymentCard" && (
<div
className={
styles.textBold
}
>
{
payment
.paymentEntity
.brand
}
</div>
)}
</div>
<div
className={styles.textSmall}
>
{payment.owner &&
(payment.owner
.verifiedName ||
payment.owner
.name)}{" "}
-
{payment.paymentEntity
.__typename ===
"PaymentCard" &&
` xxx ${payment.paymentEntity.last4}`}
</div>
</div>
</Radio>
<div
className={styles.creditCardActions}
>
<Button
onClick={this.handleDeletePaymentSource.bind(
this,
payment.id,
deletePaymentSource
)}
className={
styles.removeCardButton
}
variant="secondary-link"
type="button"
size="xsmall"
>
Remove
</Button>
</div>
</div>
</div>
);
})}
</div>
)}
</DeletePaymentSourceComponent>
);
}
}

View File

@ -0,0 +1,105 @@
class ReusablePayments extends PureComponent {
componentDidMount() {
this.setDefaultReusablePayment();
}
componentDidUpdate(prevProps) {
if (prevProps.reusablePaymentSources !== this.props.reusablePaymentSources) {
this.setDefaultReusablePayment();
}
}
setDefaultReusablePayment(skipPaymentSource) {
var _props = this.props, reusablePaymentSources = _props.reusablePaymentSources, selectedReusablePayment = _props.selectedReusablePayment, onChange = _props.onChange;
var validReusablePaymentSources = reusablePaymentSources.filter((ps)=>ps.__typename === "StripePaymentSource" && ps !== skipPaymentSource
);
if (selectedReusablePayment === null) {
return;
}
if (selectedReusablePayment && validReusablePaymentSources.find((ps)=>ps === selectedReusablePayment
)) {
// selectedReusablePayment still valid
return;
}
if (!validReusablePaymentSources.length) {
onChange(null);
return;
}
var ps1 = validReusablePaymentSources.find((ps)=>ps.isDefault
);
onChange(ps1 || validReusablePaymentSources[0]);
}
render() {
var _props = this.props, selectedReusablePayment = _props.selectedReusablePayment, reusablePaymentSources = _props.reusablePaymentSources;
var stripePaymentSources = reusablePaymentSources.filter((ps)=>ps.__typename === "StripePaymentSource"
);
if (!stripePaymentSources.length) {
return null;
}
return(/*#__PURE__*/ React.createElement(DeletePaymentSourceComponent, {
onCompleted: (param)=>{
var deletePaymentSource = param.deletePaymentSource;
if (deletePaymentSource.success) {
toast.success("Successfully removed Card");
return;
}
toast.error(deletePaymentSource.error);
},
onError: (error)=>{
toast.error(error.message);
},
refetchQueries: [
"ReusablePaymentSources"
]
}, (deletePaymentSource)=>/*#__PURE__*/ React.createElement("div", {
className: styles.selectionList
}, stripePaymentSources.map((payment)=>{
var cardIcon = "brand" in payment.paymentEntity ? payment.paymentEntity.brand === "Visa" ? /*#__PURE__*/ React.createElement(Visa, null) : payment.paymentEntity.brand === "MasterCard" ? /*#__PURE__*/ React.createElement(MasterCard, null) : payment.paymentEntity.brand === "American Express" ? /*#__PURE__*/ React.createElement(AmericanExpress, null) : payment.paymentEntity.brand === "Discover" ? /*#__PURE__*/ React.createElement(Discover, null) : null : null;
return(/*#__PURE__*/ React.createElement("div", {
key: payment.id,
className: classNames(styles.creditCard, {
[styles.creditCardChecked]: selectedReusablePayment === payment
})
}, /*#__PURE__*/ React.createElement("div", {
className: styles.creditCardContainer
}, /*#__PURE__*/ React.createElement(Radio, {
value: payment.id,
checked: selectedReusablePayment === payment,
onChange: this.handleSelectPayment.bind(this, payment)
}, /*#__PURE__*/ React.createElement("div", {
className: styles.paymentHeader
}, /*#__PURE__*/ React.createElement("div", {
className: styles.paymentHeaderContainer
}, cardIcon, payment.paymentEntity.__typename === "PaymentCard" && /*#__PURE__*/ React.createElement("div", {
className: styles.textBold
}, payment.paymentEntity.brand)), /*#__PURE__*/ React.createElement("div", {
className: styles.textSmall
}, payment.owner && (payment.owner.verifiedName || payment.owner.name), " ", "-", payment.paymentEntity.__typename === "PaymentCard" && ` xxx ${payment.paymentEntity.last4}`))), /*#__PURE__*/ React.createElement("div", {
className: styles.creditCardActions
}, /*#__PURE__*/ React.createElement(Button, {
onClick: this.handleDeletePaymentSource.bind(this, payment.id, deletePaymentSource),
className: styles.removeCardButton,
variant: "secondary-link",
type: "button",
size: "xsmall"
}, "Remove")))));
}))
));
}
constructor(...args){
super(...args);
this.handleSelectPayment = (selected)=>{
return this.props.onChange(selected);
};
this.handleDeletePaymentSource = (id, deletePaymentSource)=>{
var _props = this.props, selectedReusablePayment = _props.selectedReusablePayment, onChange = _props.onChange;
if (onChange && selectedReusablePayment && selectedReusablePayment.id === id) {
this.setDefaultReusablePayment(selectedReusablePayment);
}
deletePaymentSource({
variables: {
id
}
});
};
}
}
export { ReusablePayments as default };

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0"
name = "swc_ecma_transforms_compat"
repository = "https://github.com/swc-project/swc.git"
version = "0.55.0"
version = "0.55.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
@ -34,6 +34,7 @@ swc_ecma_transforms_classes = {version = "0.32.0", path = "../swc_ecma_transform
swc_ecma_transforms_macros = {version = "0.3.0", path = "../swc_ecma_transforms_macros"}
swc_ecma_utils = {version = "0.54.0", path = "../swc_ecma_utils"}
swc_ecma_visit = {version = "0.44.0", path = "../swc_ecma_visit"}
tracing = "0.1"
[dev-dependencies]
serde_json = "1.0.66"

View File

@ -16,6 +16,7 @@ use swc_ecma_utils::{
quote_ident, quote_str, ExprFactory, IsDirective, ModuleItemLike, StmtLike,
};
use swc_ecma_visit::{noop_fold_type, noop_visit_type, Fold, FoldWith, Node, Visit, VisitWith};
use tracing::debug;
mod constructor;
mod prop_name;
@ -468,6 +469,7 @@ where
// Black magic to detect injected constructor.
let is_constructor_default = constructor.span.is_dummy();
if is_constructor_default {
debug!("Dropping constructor parameters because the constructor is injected");
constructor.params = vec![];
}

View File

@ -530,7 +530,8 @@ impl Destructuring {
definite: false,
})
}
_ => {}
Pat::Rest(..) | Pat::Expr(..) => params.push(param),
Pat::Invalid(..) => {}
}
}

View File

@ -12,6 +12,7 @@ use swc_ecma_utils::{
member_expr, prepend, prepend_stmts, private_ident, quote_ident, undefined, ExprFactory,
};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
use tracing::trace;
pub fn parameters(c: Config) -> impl 'static + Fold {
as_folder(Params {
@ -448,6 +449,7 @@ impl VisitMut for Params {
}
fn visit_mut_constructor(&mut self, f: &mut Constructor) {
trace!("visit_mut_constructor(parmas.len() = {})", f.params.len());
f.params.visit_mut_with(self);
if let Some(BlockStmt { span: _, stmts }) = &mut f.body {
@ -472,6 +474,11 @@ impl VisitMut for Params {
}
}
}
trace!(
"visit_mut_constructor(parmas.len() = {}, after)",
f.params.len()
);
}
fn visit_mut_expr(&mut self, e: &mut Expr) {

View File

@ -303,6 +303,8 @@ macro_rules! impl_visit_mut_fn {
return;
}
tracing::trace!("visit_mut_constructor(parmas.len() = {})", f.params.len());
f.visit_mut_children_with(self);
let mut params = f
@ -319,6 +321,11 @@ macro_rules! impl_visit_mut_fn {
let (params, body) = self.visit_mut_fn_like(&mut params, &mut f.body.take().unwrap());
tracing::trace!(
"visit_mut_constructor(parmas.len() = {}, after)",
params.len()
);
f.params = params.into_iter().map(ParamOrTsParamProp::Param).collect();
f.body = Some(body);
}

View File

@ -6,7 +6,7 @@ use swc_ecma_ast::*;
use swc_ecma_transforms_base::helper;
use swc_ecma_transforms_classes::super_field::SuperFieldAccessFolder;
use swc_ecma_utils::{
alias_ident_for, constructor::inject_after_super, prepend, private_ident,
alias_ident_for, constructor::inject_after_super, default_constructor, prepend, private_ident,
prop_name_to_expr_value, quote_ident, quote_str, undefined, ExprFactory, IdentExt,
};
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith, Node, Visit, VisitWith};
@ -297,46 +297,17 @@ impl Decorators {
ClassMember::Constructor(c)
}
None => ClassMember::Constructor(Constructor {
span: DUMMY_SP,
key: PropName::Ident(quote_ident!("constructor")),
is_optional: false,
accessibility: Default::default(),
params: if super_class_ident.is_some() {
vec![ParamOrTsParamProp::Param(Param {
span: DUMMY_SP,
decorators: vec![],
pat: Pat::Rest(RestPat {
span: DUMMY_SP,
dot3_token: DUMMY_SP,
arg: Box::new(Pat::Ident(quote_ident!("args").into())),
type_ann: Default::default(),
}),
})]
} else {
vec![]
},
body: Some(BlockStmt {
span: DUMMY_SP,
stmts: if super_class_ident.is_some() {
vec![
CallExpr {
span: DUMMY_SP,
callee: ExprOrSuper::Super(Super { span: DUMMY_SP }),
args: vec![ExprOrSpread {
spread: Some(DUMMY_SP),
expr: Box::new(Expr::Ident(quote_ident!("args"))),
}],
type_args: Default::default(),
}
.into_stmt(),
initialize_call.into_stmt(),
]
} else {
vec![initialize_call.into_stmt()]
},
}),
}),
None => {
let mut c = default_constructor(super_class_ident.is_some());
c.body
.as_mut()
.unwrap()
.stmts
.push(initialize_call.into_stmt());
ClassMember::Constructor(c)
}
}
};

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0"
name = "swc_ecma_utils"
repository = "https://github.com/swc-project/swc.git"
version = "0.54.0"
version = "0.54.1"
[package.metadata.docs.rs]
all-features = true
@ -26,6 +26,7 @@ swc_atoms = {version = "0.2.0", path = "../swc_atoms"}
swc_common = {version = "0.14.3", path = "../swc_common"}
swc_ecma_ast = {version = "0.58.0", path = "../swc_ecma_ast"}
swc_ecma_visit = {version = "0.44.0", path = "../swc_ecma_visit"}
tracing = "0.1"
unicode-xid = "0.2"
[dev-dependencies]

View File

@ -4,10 +4,10 @@ use swc_common::{util::take::Take, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_visit::{noop_fold_type, noop_visit_mut_type, Fold, FoldWith, VisitMut, VisitMutWith};
pub fn inject_after_super(c: &mut Constructor, exprs: Vec<Box<Expr>>) {
pub fn inject_after_super(c: &mut Constructor, mut exprs: Vec<Box<Expr>>) {
// Allow using super multiple time
let mut folder = Injector {
exprs: &exprs,
exprs: &mut exprs,
injected: false,
};
@ -29,7 +29,7 @@ pub fn inject_after_super(c: &mut Constructor, exprs: Vec<Box<Expr>>) {
struct Injector<'a> {
injected: bool,
exprs: &'a [Box<Expr>],
exprs: &'a mut Vec<Box<Expr>>,
}
impl<'a> Fold for Injector<'a> {
@ -63,7 +63,7 @@ impl<'a> Fold for Injector<'a> {
}) => {
self.injected = true;
buf.push(stmt);
buf.extend(self.exprs.iter().cloned().map(|v| v.into_stmt()));
buf.extend(self.exprs.clone().into_iter().map(|v| v.into_stmt()));
return;
}
_ => {}
@ -112,7 +112,7 @@ impl<'a> Fold for Injector<'a> {
/// Handles code like `foo(super())`
struct ExprInjector<'a> {
injected: bool,
exprs: &'a [Box<Expr>],
exprs: &'a mut Vec<Box<Expr>>,
injected_tmp: Option<Ident>,
}
@ -151,7 +151,7 @@ impl VisitMut for ExprInjector<'_> {
op: op!("="),
right: Box::new(e),
})))
.chain(self.exprs.iter().cloned())
.chain(self.exprs.clone())
.chain(iter::once(Box::new(Expr::Ident(
self.injected_tmp.as_ref().cloned().unwrap(),
))))

View File

@ -29,6 +29,7 @@ use swc_ecma_ast::*;
use swc_ecma_visit::{
noop_visit_mut_type, noop_visit_type, Node, Visit, VisitMut, VisitMutWith, VisitWith,
};
use tracing::trace;
use unicode_xid::UnicodeXID;
#[macro_use]
@ -1657,6 +1658,8 @@ pub fn prop_name_to_expr_value(p: PropName) -> Expr {
}
pub fn default_constructor(has_super: bool) -> Constructor {
trace!(has_super = has_super, "Creating a default constructor");
let span = DUMMY_SP;
Constructor {

View File

@ -6,7 +6,10 @@ edition = "2018"
license = "Apache-2.0"
name = "swc_ecma_visit"
repository = "https://github.com/swc-project/swc.git"
version = "0.44.1"
version = "0.44.2"
[features]
debug = []
[dependencies]
num-bigint = {version = "0.2", features = ["serde"]}
@ -14,3 +17,4 @@ swc_atoms = {version = "0.2", path = "../swc_atoms"}
swc_common = {version = "0.14.0", path = "../swc_common"}
swc_ecma_ast = {version = "0.58.0", path = "../swc_ecma_ast"}
swc_visit = {version = "0.2.3", path = "../swc_visit"}
tracing = "0.1"

View File

@ -266,7 +266,16 @@ where
method!(fold_ts_type, TsType);
method!(fold_module, Module);
#[inline(always)]
fn fold_module(&mut self, mut n: Module) -> Module {
#[cfg(all(debug_assertions, feature = "debug"))]
let _tracing = {
let visitor_name = std::any::type_name::<V>();
tracing::span!(tracing::Level::TRACE, "as_folder", visitor = visitor_name).entered()
};
n.visit_mut_with(&mut self.0);
n
}
method!(fold_script, Script);
method!(fold_program, Program);
}

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0"
name = "testing"
repository = "https://github.com/swc-project/swc.git"
version = "0.15.2"
version = "0.15.3"
[dependencies]
ansi_term = "0.12.1"

View File

@ -9,6 +9,7 @@ use std::{
fs::{create_dir_all, File},
io::Write,
path::{Path, PathBuf},
str::FromStr,
sync::RwLock,
thread,
};
@ -32,11 +33,13 @@ mod string_errors;
/// Configures logger
#[must_use]
pub fn init() -> tracing::subscriber::DefaultGuard {
let log_env = env::var("RUST_LOG").unwrap_or_else(|_| "debug".to_string());
let logger = tracing_subscriber::FmtSubscriber::builder()
.without_time()
.with_target(false)
.with_ansi(true)
.with_env_filter(EnvFilter::from_default_env())
.with_env_filter(EnvFilter::from_str(&log_env).unwrap())
.with_test_writer()
.pretty()
.finish();
@ -117,8 +120,6 @@ pub struct Tester {
impl Tester {
pub fn new() -> Self {
let _log = init();
Tester {
cm: Lrc::new(SourceMap::new(FilePathMapping::empty())),
globals: swc_common::Globals::new(),
@ -136,6 +137,8 @@ impl Tester {
where
F: FnOnce(Lrc<SourceMap>, Handler) -> Result<Ret, ()>,
{
let _log = init();
let (handler, errors) =
self::string_errors::new_handler(self.cm.clone(), self.treat_err_as_bug);
let result = swc_common::GLOBALS.set(&self.globals, || op(self.cm.clone(), handler));
@ -151,6 +154,8 @@ impl Tester {
where
F: FnOnce(Lrc<SourceMap>, Handler) -> Result<Ret, ()>,
{
let _log = init();
let (handler, errors) =
self::diag_errors::new_handler(self.cm.clone(), self.treat_err_as_bug);
let result = swc_common::GLOBALS.set(&self.globals, || op(self.cm.clone(), handler));

View File

@ -1,3 +1,5 @@
use tracing::{debug, error};
use crate::paths;
use std::{
fmt,
@ -59,7 +61,7 @@ impl NormalizedOutput {
{
let path = path.as_ref();
let path = path.canonicalize().unwrap_or_else(|err| {
eprintln!(
debug!(
"compare_to_file: failed to canonicalize outfile path `{}`: {:?}",
path.display(),
err
@ -85,13 +87,13 @@ impl NormalizedOutput {
return Ok(());
}
eprintln!("Comparing output to {}", path.display());
debug!("Comparing output to {}", path.display());
create_dir_all(path.parent().unwrap()).expect("failed to run `mkdir -p`");
if std::env::var("UPDATE").unwrap_or(String::from("0")) == "1" {
crate::write_to_file(&path, &self.0);
eprintln!(
error!(
"Assertion failed: \nActual file printed to {}",
path.display()
);

View File

@ -73,6 +73,7 @@
"interner",
"intrinsics",
"ints",
"jsdoc",
"jsonify",
"jsxs",
"jszip",