fix(es/module): Handle directives (#8048)

**Related issue:**

 - Closes: #8047
This commit is contained in:
magic-akari 2023-10-02 11:44:05 -05:00 committed by GitHub
parent 0c8d8a3f4a
commit 4d8e1013bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 135 additions and 39 deletions

View File

@ -20,8 +20,8 @@ use crate::{
module_ref_rewriter::{ImportMap, ModuleRefRewriter},
path::{ImportResolver, Resolver},
util::{
clone_first_use_directive, define_es_module, emit_export_stmts, local_name_for_src,
use_strict, ImportInterop,
define_es_module, emit_export_stmts, local_name_for_src, use_strict, ImportInterop,
VecStmtLike,
},
};
@ -143,9 +143,19 @@ where
let mut stmts: Vec<Stmt> = Vec::with_capacity(n.body.len() + 4);
// Collect directives
stmts.extend(
&mut n
.body
.iter_mut()
.take_while(|i| i.directive_continue())
.map(|i| i.take())
.map(ModuleItem::expect_stmt),
);
// "use strict";
if self.config.strict_mode {
stmts.push(clone_first_use_directive(&n.body, true).unwrap_or_else(use_strict));
if self.config.strict_mode && !stmts.has_use_strict() {
stmts.push(use_strict());
}
let ModuleDeclStrip {
@ -170,7 +180,7 @@ where
);
stmts.extend(n.body.take().into_iter().filter_map(|item| match item {
ModuleItem::Stmt(stmt) if !stmt.is_directive() => Some(stmt),
ModuleItem::Stmt(stmt) if !stmt.is_empty() => Some(stmt),
_ => None,
}));

View File

@ -16,8 +16,8 @@ use crate::{
module_ref_rewriter::{ImportMap, ModuleRefRewriter},
path::{ImportResolver, Resolver},
util::{
clone_first_use_directive, define_es_module, emit_export_stmts, local_name_for_src,
prop_name, use_strict, ImportInterop, ObjPropKeyIdent,
define_es_module, emit_export_stmts, local_name_for_src, prop_name, use_strict,
ImportInterop, ObjPropKeyIdent, VecStmtLike,
},
};
@ -113,15 +113,18 @@ where
let mut stmts: Vec<ModuleItem> = Vec::with_capacity(n.body.len() + 6);
stmts.extend(clone_first_use_directive(&n.body, false).map(From::from));
// Collect directives
stmts.extend(
&mut n
.body
.iter_mut()
.take_while(|i| i.directive_continue())
.map(|i| i.take()),
);
// "use strict";
if self.config.strict_mode {
stmts.push(
clone_first_use_directive(&n.body, true)
.unwrap_or_else(use_strict)
.into(),
);
if self.config.strict_mode && !stmts.has_use_strict() {
stmts.push(use_strict().into());
}
let ModuleDeclStrip {
@ -156,7 +159,7 @@ where
);
stmts.extend(n.body.take().into_iter().filter(|item| match item {
ModuleItem::Stmt(stmt) => !stmt.is_directive(),
ModuleItem::Stmt(stmt) => !stmt.is_empty(),
_ => false,
}));

View File

@ -17,8 +17,8 @@ use crate::{
module_ref_rewriter::{ImportMap, ModuleRefRewriter},
path::{ImportResolver, Resolver},
util::{
clone_first_use_directive, define_es_module, emit_export_stmts, local_name_for_src,
use_strict, ImportInterop,
define_es_module, emit_export_stmts, local_name_for_src, use_strict, ImportInterop,
VecStmtLike,
},
};
@ -116,9 +116,18 @@ where
let mut stmts: Vec<Stmt> = Vec::with_capacity(module_items.len() + 4);
// Collect directives
stmts.extend(
module_items
.iter_mut()
.take_while(|i| i.directive_continue())
.map(|i| i.take())
.map(ModuleItem::expect_stmt),
);
// "use strict";
if self.config.config.strict_mode {
stmts.push(clone_first_use_directive(module_items, true).unwrap_or_else(use_strict));
if self.config.config.strict_mode && !stmts.has_use_strict() {
stmts.push(use_strict());
}
let ModuleDeclStrip {
@ -143,7 +152,7 @@ where
);
stmts.extend(module_items.take().into_iter().filter_map(|i| match i {
ModuleItem::Stmt(stmt) if !stmt.is_directive() => Some(stmt),
ModuleItem::Stmt(stmt) if !stmt.is_empty() => Some(stmt),
_ => None,
}));

View File

@ -194,26 +194,33 @@ pub(super) fn define_es_module(exports: Ident) -> Stmt {
.into_stmt()
}
pub(super) fn clone_first_use_directive(
stmts: &[ModuleItem],
want_use_strict: bool,
) -> Option<Stmt> {
if stmts.is_empty() {
return None;
}
pub(super) trait VecStmtLike {
type StmtLike: IsDirective;
stmts.iter().find_map(|item| match item {
ModuleItem::Stmt(stmt) => {
if (want_use_strict && stmt.is_use_strict())
|| (!want_use_strict && !stmt.is_use_strict() && stmt.is_directive())
{
Some(stmt.clone())
} else {
None
}
}
_ => None,
})
fn as_ref(&self) -> &[Self::StmtLike];
fn has_use_strict(&self) -> bool {
self.as_ref()
.iter()
.take_while(|s| s.directive_continue())
.any(IsDirective::is_use_strict)
}
}
impl VecStmtLike for [ModuleItem] {
type StmtLike = ModuleItem;
fn as_ref(&self) -> &[Self::StmtLike] {
self
}
}
impl VecStmtLike for [Stmt] {
type StmtLike = Stmt;
fn as_ref(&self) -> &[Self::StmtLike] {
self
}
}
pub(super) fn use_strict() -> Stmt {

View File

@ -3,6 +3,7 @@ define([
"require",
"exports"
], function(require, exports) {
"use client";
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true

View File

@ -6,6 +6,7 @@
], factory);
else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) factory(global.input = {});
})(this, function(exports) {
"use client";
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true

View File

@ -0,0 +1,12 @@
"use client";
"foo";
"use bar";
"use strict";
// All above are directives
function foo() { }
"use hello"; // This is not directive

View File

@ -0,0 +1,11 @@
define([
"require"
], function(require) {
"use client";
"foo";
"use bar";
"use strict";
// All above are directives
function foo() {}
"use hello"; // This is not directive
});

View File

@ -0,0 +1,7 @@
"use client";
"foo";
"use bar";
"use strict";
// All above are directives
function foo() {}
"use hello"; // This is not directive

View File

@ -0,0 +1,13 @@
(function(global, factory) {
if (typeof module === "object" && typeof module.exports === "object") factory();
else if (typeof define === "function" && define.amd) define([], factory);
else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) factory();
})(this, function() {
"use client";
"foo";
"use bar";
"use strict";
// All above are directives
function foo() {}
"use hello"; // This is not directive
});

View File

@ -2262,6 +2262,7 @@ pub fn prepend_stmts<T: StmtLike>(
pub trait IsDirective {
fn as_ref(&self) -> Option<&Stmt>;
#[deprecated(note = "use directive_continue instead")]
fn is_directive(&self) -> bool {
match self.as_ref() {
Some(Stmt::Expr(expr)) => match &*expr.expr {
@ -2273,6 +2274,15 @@ pub trait IsDirective {
_ => false,
}
}
fn directive_continue(&self) -> bool {
match self.as_ref() {
Some(Stmt::Expr(expr)) => match &*expr.expr {
Expr::Lit(Lit::Str(..)) => true,
_ => false,
},
_ => false,
}
}
fn is_use_strict(&self) -> bool {
match self.as_ref() {
Some(Stmt::Expr(expr)) => match *expr.expr {
@ -2292,6 +2302,18 @@ impl IsDirective for Stmt {
}
}
impl IsDirective for ModuleItem {
fn as_ref(&self) -> Option<&Stmt> {
self.as_stmt()
}
}
impl IsDirective for &ModuleItem {
fn as_ref(&self) -> Option<&Stmt> {
self.as_stmt()
}
}
pub trait IdentExt {
fn prefix(&self, prefix: &str) -> Ident;