feat(es/modules): Support AMD triple slash directives (#5091)

This commit is contained in:
magic-akari 2022-07-04 12:17:34 +08:00 committed by GitHub
parent 04c4af8475
commit ddb31d1fd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 220 additions and 1 deletions

1
Cargo.lock generated
View File

@ -3652,6 +3652,7 @@ dependencies = [
"is-macro", "is-macro",
"path-clean", "path-clean",
"pathdiff", "pathdiff",
"regex",
"serde", "serde",
"serde_json", "serde_json",
"swc_atoms", "swc_atoms",

View File

@ -33,6 +33,7 @@ swc_ecma_parser = { version = "0.108.0", path = "../swc_ecma_parser" }
swc_ecma_transforms_base = { version = "0.94.0", path = "../swc_ecma_transforms_base" } swc_ecma_transforms_base = { version = "0.94.0", path = "../swc_ecma_transforms_base" }
swc_ecma_utils = { version = "0.90.0", path = "../swc_ecma_utils" } swc_ecma_utils = { version = "0.90.0", path = "../swc_ecma_utils" }
swc_ecma_visit = { version = "0.67.0", path = "../swc_ecma_visit" } swc_ecma_visit = { version = "0.67.0", path = "../swc_ecma_visit" }
regex = "1"
tracing = "0.1.32" tracing = "0.1.32"
[dev-dependencies] [dev-dependencies]

View File

@ -1,7 +1,12 @@
use anyhow::Context; use anyhow::Context;
use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use swc_atoms::{js_word, JsWord}; use swc_atoms::{js_word, JsWord};
use swc_common::{comments::Comments, util::take::Take, FileName, Mark, Span, DUMMY_SP}; use swc_common::{
comments::{CommentKind, Comments},
util::take::Take,
FileName, Mark, Span, Spanned, DUMMY_SP,
};
use swc_ecma_ast::*; use swc_ecma_ast::*;
use swc_ecma_transforms_base::{feature::FeatureFlag, helper_expr}; use swc_ecma_transforms_base::{feature::FeatureFlag, helper_expr};
use swc_ecma_utils::{ use swc_ecma_utils::{
@ -128,6 +133,12 @@ where
noop_visit_mut_type!(); noop_visit_mut_type!();
fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) { fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
if let Some(first) = n.first() {
if self.module_id.is_none() {
self.module_id = self.get_amd_module_id_from_comments(first.span());
}
}
let import_interop = self.config.import_interop(); let import_interop = self.config.import_interop();
let mut strip = ModuleDeclStrip::default(); let mut strip = ModuleDeclStrip::default();
@ -463,6 +474,31 @@ where
} }
span span
} }
fn get_amd_module_id_from_comments(&self, span: Span) -> Option<String> {
// https://github.com/microsoft/TypeScript/blob/1b9c8a15adc3c9a30e017a7048f98ef5acc0cada/src/compiler/parser.ts#L9648-L9658
let amd_module_re = Regex::new(
r##"(?i)^/\s*<amd-module.*?name\s*=\s*(?:(?:'([^']*)')|(?:"([^"]*)")).*?/>"##,
)
.unwrap();
self.comments.as_ref().and_then(|comments| {
comments
.get_leading(span.lo)
.iter()
.flatten()
.rev()
.find_map(|cmt| {
if cmt.kind != CommentKind::Line {
return None;
}
amd_module_re
.captures(&cmt.text)
.and_then(|cap| cap.get(1).or_else(|| cap.get(2)))
})
.map(|m| m.as_str().to_string())
})
}
} }
/// new Promise((resolve, reject) => require([arg], m => resolve(m), reject)) /// new Promise((resolve, reject) => require([arg], m => resolve(m), reject))

View File

@ -0,0 +1,8 @@
///<amd-module name='NamedModule'/>
class Foo {
x: number;
constructor() {
this.x = 5;
}
}
export = Foo;

View File

@ -0,0 +1,13 @@
define("NamedModule", [
"require"
], function(require) {
"use strict";
///<amd-module name='NamedModule'/>
class Foo {
x: number;
constructor(){
this.x = 5;
}
}
return Foo;
});

View File

@ -0,0 +1,9 @@
"use strict";
///<amd-module name='NamedModule'/>
class Foo {
x: number;
constructor(){
this.x = 5;
}
}
module.exports = Foo;

View File

@ -0,0 +1,15 @@
(function(global, factory) {
if (typeof module === "object" && typeof module.exports === "object") module.exports = factory();
else if (typeof define === "function" && define.amd) define([], factory);
else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) module.exports = factory();
})(this, function() {
"use strict";
///<amd-module name='NamedModule'/>
class Foo {
x: number;
constructor(){
this.x = 5;
}
}
return Foo;
});

View File

@ -0,0 +1,9 @@
///<amd-module name='FirstModuleName'/>
///<amd-module name='SecondModuleName'/>
class Foo {
x: number;
constructor() {
this.x = 5;
}
}
export = Foo;

View File

@ -0,0 +1,14 @@
define("SecondModuleName", [
"require"
], function(require) {
"use strict";
///<amd-module name='FirstModuleName'/>
///<amd-module name='SecondModuleName'/>
class Foo {
x: number;
constructor(){
this.x = 5;
}
}
return Foo;
});

View File

@ -0,0 +1,10 @@
"use strict";
///<amd-module name='FirstModuleName'/>
///<amd-module name='SecondModuleName'/>
class Foo {
x: number;
constructor(){
this.x = 5;
}
}
module.exports = Foo;

View File

@ -0,0 +1,16 @@
(function(global, factory) {
if (typeof module === "object" && typeof module.exports === "object") module.exports = factory();
else if (typeof define === "function" && define.amd) define([], factory);
else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) module.exports = factory();
})(this, function() {
"use strict";
///<amd-module name='FirstModuleName'/>
///<amd-module name='SecondModuleName'/>
class Foo {
x: number;
constructor(){
this.x = 5;
}
}
return Foo;
});

View File

@ -0,0 +1,8 @@
///<AmD-moDulE nAme='NamedModule'/>
class Foo {
x: number;
constructor() {
this.x = 5;
}
}
export = Foo;

View File

@ -0,0 +1,13 @@
define("NamedModule", [
"require"
], function(require) {
"use strict";
///<AmD-moDulE nAme='NamedModule'/>
class Foo {
x: number;
constructor(){
this.x = 5;
}
}
return Foo;
});

View File

@ -0,0 +1,9 @@
"use strict";
///<AmD-moDulE nAme='NamedModule'/>
class Foo {
x: number;
constructor(){
this.x = 5;
}
}
module.exports = Foo;

View File

@ -0,0 +1,15 @@
(function(global, factory) {
if (typeof module === "object" && typeof module.exports === "object") module.exports = factory();
else if (typeof define === "function" && define.amd) define([], factory);
else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) module.exports = factory();
})(this, function() {
"use strict";
///<AmD-moDulE nAme='NamedModule'/>
class Foo {
x: number;
constructor(){
this.x = 5;
}
}
return Foo;
});

View File

@ -0,0 +1,8 @@
/*/<amd-module name='should-ignore'/> */
class Foo {
x: number;
constructor() {
this.x = 5;
}
}
export = Foo;

View File

@ -0,0 +1,12 @@
define([
"require"
], function(require) {
"use strict";
/*/<amd-module name='should-ignore'/> */ class Foo {
x: number;
constructor(){
this.x = 5;
}
}
return Foo;
});

View File

@ -0,0 +1,8 @@
"use strict";
/*/<amd-module name='should-ignore'/> */ class Foo {
x: number;
constructor(){
this.x = 5;
}
}
module.exports = Foo;

View File

@ -0,0 +1,14 @@
(function(global, factory) {
if (typeof module === "object" && typeof module.exports === "object") module.exports = factory();
else if (typeof define === "function" && define.amd) define([], factory);
else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) module.exports = factory();
})(this, function() {
"use strict";
/*/<amd-module name='should-ignore'/> */ class Foo {
x: number;
constructor(){
this.x = 5;
}
}
return Foo;
});