1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 23:34:47 -05:00

fix: support missing features in --no-check (#7289)

This commit adds "EmitTranspileOptions" to "transpile()" function, 
that allows to configure transpilation process based on the 
currently loaded "tsconfig.json".
This commit is contained in:
Bartek Iwańczuk 2020-09-02 11:12:18 +02:00 committed by GitHub
parent 7f32a4e19b
commit b21f318e68
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 278 additions and 58 deletions

41
Cargo.lock generated
View file

@ -16,6 +16,15 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]]
name = "ahash"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
dependencies = [
"const-random",
]
[[package]]
name = "aho-corasick"
version = "0.7.13"
@ -265,6 +274,26 @@ dependencies = [
"bitflags",
]
[[package]]
name = "const-random"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a"
dependencies = [
"const-random-macro",
"proc-macro-hack",
]
[[package]]
name = "const-random-macro"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a"
dependencies = [
"getrandom",
"proc-macro-hack",
]
[[package]]
name = "crc32fast"
version = "1.2.0"
@ -330,6 +359,17 @@ dependencies = [
"syn 1.0.36",
]
[[package]]
name = "dashmap"
version = "3.11.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f260e2fc850179ef410018660006951c1b55b79e8087e87111a2c388994b9b5"
dependencies = [
"ahash",
"cfg-if",
"num_cpus",
]
[[package]]
name = "deno"
version = "1.3.2"
@ -2262,6 +2302,7 @@ checksum = "c3e99fc5c5b87871fa19036fd8a622ecf6b2a29b30b4d632e48f6a1923e393ea"
dependencies = [
"Inflector",
"arrayvec",
"dashmap",
"either",
"fxhash",
"indexmap",

View file

@ -62,7 +62,7 @@ serde_json = { version = "1.0.57", features = [ "preserve_order" ] }
sys-info = "0.7.0"
sourcemap = "6.0.1"
swc_common = { version = "=0.10.2", features = ["sourcemap"] }
swc_ecmascript = { version = "=0.6.3", features = ["codegen", "parser", "transforms", "visit"] }
swc_ecmascript = { version = "=0.6.3", features = ["codegen", "parser", "react", "transforms", "visit"] }
tempfile = "3.1.0"
termcolor = "1.1.0"
tokio = { version = "0.2.22", features = ["full"] }

View file

@ -29,6 +29,10 @@ use swc_ecmascript::parser::StringInput;
use swc_ecmascript::parser::Syntax;
use swc_ecmascript::parser::TsConfig;
use swc_ecmascript::transforms::fixer;
use swc_ecmascript::transforms::helpers;
use swc_ecmascript::transforms::pass::Optional;
use swc_ecmascript::transforms::proposals::decorators;
use swc_ecmascript::transforms::react;
use swc_ecmascript::transforms::typescript;
use swc_ecmascript::visit::FoldWith;
@ -230,52 +234,6 @@ impl AstParser {
})
}
pub fn strip_types(
&self,
file_name: &str,
media_type: MediaType,
source_code: &str,
) -> Result<String, ErrBox> {
let module = self.parse_module(file_name, media_type, source_code)?;
let program = Program::Module(module);
let mut compiler_pass =
chain!(typescript::strip(), fixer(Some(&self.comments)));
let program = program.fold_with(&mut compiler_pass);
let mut src_map_buf = vec![];
let mut buf = vec![];
{
let writer = Box::new(JsWriter::new(
self.source_map.clone(),
"\n",
&mut buf,
Some(&mut src_map_buf),
));
let config = swc_ecmascript::codegen::Config { minify: false };
let mut emitter = swc_ecmascript::codegen::Emitter {
cfg: config,
comments: Some(&self.comments),
cm: self.source_map.clone(),
wr: writer,
};
program.emit_with(&mut emitter)?;
}
let mut src = String::from_utf8(buf)?;
{
let mut buf = vec![];
self
.source_map
.build_source_map_from(&mut src_map_buf, None)
.to_writer(&mut buf)?;
let map = String::from_utf8(buf)?;
src.push_str("//# sourceMappingURL=data:application/json;base64,");
let encoded_map = base64::encode(map.as_bytes());
src.push_str(&encoded_map);
}
Ok(src)
}
pub fn get_span_location(&self, span: Span) -> swc_common::Loc {
self.source_map.lookup_char_pos(span.lo())
}
@ -290,13 +248,196 @@ impl AstParser {
}
}
#[test]
fn test_strip_types() {
let ast_parser = AstParser::default();
let result = ast_parser
.strip_types("test.ts", MediaType::TypeScript, "const a: number = 10;")
.unwrap();
assert!(result.starts_with(
"const a = 10;\n//# sourceMappingURL=data:application/json;base64,"
));
#[derive(Debug, Clone)]
pub struct EmitTranspileOptions {
/// When emitting a legacy decorator, also emit experimental decorator meta
/// data. Defaults to `false`.
pub emit_metadata: bool,
/// Should the source map be inlined in the emitted code file, or provided
/// as a separate file. Defaults to `true`.
pub inline_source_map: bool,
/// When transforming JSX, what value should be used for the JSX factory.
/// Defaults to `React.createElement`.
pub jsx_factory: String,
/// When transforming JSX, what value should be used for the JSX fragment
/// factory. Defaults to `React.Fragment`.
pub jsx_fragment_factory: String,
/// Should JSX be transformed or preserved. Defaults to `true`.
pub transform_jsx: bool,
}
impl Default for EmitTranspileOptions {
fn default() -> Self {
EmitTranspileOptions {
emit_metadata: false,
inline_source_map: true,
jsx_factory: "React.createElement".into(),
jsx_fragment_factory: "React.Fragment".into(),
transform_jsx: true,
}
}
}
pub fn transpile(
file_name: &str,
media_type: MediaType,
source_code: &str,
options: &EmitTranspileOptions,
) -> Result<(String, Option<String>), ErrBox> {
let ast_parser = AstParser::default();
let module = ast_parser.parse_module(file_name, media_type, source_code)?;
let program = Program::Module(module);
let jsx_pass = react::react(
ast_parser.source_map.clone(),
react::Options {
pragma: options.jsx_factory.clone(),
pragma_frag: options.jsx_fragment_factory.clone(),
// this will use `Object.assign()` instead of the `_extends` helper
// when spreading props.
use_builtins: true,
..Default::default()
},
);
let mut passes = chain!(
Optional::new(jsx_pass, options.transform_jsx),
decorators::decorators(decorators::Config {
legacy: true,
emit_metadata: options.emit_metadata,
}),
typescript::strip(),
fixer(Some(&ast_parser.comments)),
);
let program = swc_common::GLOBALS.set(&Globals::new(), || {
helpers::HELPERS.set(&helpers::Helpers::new(false), || {
program.fold_with(&mut passes)
})
});
let mut src_map_buf = vec![];
let mut buf = vec![];
{
let writer = Box::new(JsWriter::new(
ast_parser.source_map.clone(),
"\n",
&mut buf,
Some(&mut src_map_buf),
));
let config = swc_ecmascript::codegen::Config { minify: false };
let mut emitter = swc_ecmascript::codegen::Emitter {
cfg: config,
comments: Some(&ast_parser.comments),
cm: ast_parser.source_map.clone(),
wr: writer,
};
program.emit_with(&mut emitter)?;
}
let mut src = String::from_utf8(buf)?;
let mut map: Option<String> = None;
{
let mut buf = Vec::new();
ast_parser
.source_map
.build_source_map_from(&mut src_map_buf, None)
.to_writer(&mut buf)?;
if options.inline_source_map {
src.push_str("//# sourceMappingURL=data:application/json;base64,");
let encoded_map = base64::encode(buf);
src.push_str(&encoded_map);
} else {
map = Some(String::from_utf8(buf)?);
}
}
Ok((src, map))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_transpile() {
let source = r#"
enum D {
A,
B,
C,
}
export class A {
private b: string;
protected c: number = 1;
e: "foo";
constructor (public d = D.A) {
const e = "foo" as const;
this.e = e;
}
}
"#;
let result = transpile(
"test.ts",
MediaType::TypeScript,
source,
&EmitTranspileOptions::default(),
)
.unwrap();
let (code, maybe_map) = result;
assert!(code.starts_with("var D;\n(function(D) {\n"));
assert!(
code.contains("\n//# sourceMappingURL=data:application/json;base64,")
);
assert!(maybe_map.is_none());
}
#[test]
fn test_transpile_tsx() {
let source = r#"
export class A {
render() {
return <div><span></span></div>
}
}
"#;
let result = transpile(
"test.ts",
MediaType::TSX,
source,
&EmitTranspileOptions::default(),
)
.unwrap();
let (code, _maybe_source_map) = result;
assert!(code.contains("React.createElement(\"div\", null"));
}
#[test]
fn test_transpile_decorators() {
let source = r#"
function enumerable(value: boolean) {
return function (
_target: any,
_propertyKey: string,
descriptor: PropertyDescriptor,
) {
descriptor.enumerable = value;
};
}
export class A {
@enumerable(false)
a() {
Test.value;
}
}
"#;
let result = transpile(
"test.ts",
MediaType::TypeScript,
source,
&EmitTranspileOptions::default(),
)
.unwrap();
let (code, _maybe_source_map) = result;
assert!(code.contains("_applyDecoratedDescriptor("));
}
}

View file

@ -18,6 +18,7 @@ use crate::permissions::Permissions;
use crate::source_maps::SourceMapGetter;
use crate::startup_data;
use crate::state::State;
use crate::swc_util;
use crate::swc_util::AstParser;
use crate::swc_util::Location;
use crate::swc_util::SwcDiagnosticBuffer;
@ -418,6 +419,16 @@ struct CompileResponse {
stats: Option<Vec<Stat>>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct TranspileTsOptions {
check_js: bool,
emit_decorator_metadata: bool,
jsx: String,
jsx_factory: String,
jsx_fragment_factory: String,
}
// TODO(bartlomieju): possible deduplicate once TS refactor is stabilized
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
@ -795,12 +806,39 @@ impl TsCompiler {
let mut emit_map = HashMap::new();
let mut compiler_options = json!({
"checkJs": false,
"emitDecoratorMetadata": false,
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
});
let compiler_config = self.config.clone();
tsc_config::json_merge(&mut compiler_options, &compiler_config.options);
warn_ignored_options(
compiler_config.maybe_ignored_options,
compiler_config.path.as_ref().unwrap(),
);
let compiler_options: TranspileTsOptions =
serde_json::from_value(compiler_options)?;
let transpile_options = swc_util::EmitTranspileOptions {
emit_metadata: compiler_options.emit_decorator_metadata,
inline_source_map: true,
jsx_factory: compiler_options.jsx_factory,
jsx_fragment_factory: compiler_options.jsx_fragment_factory,
transform_jsx: compiler_options.jsx == "react",
};
for source_file in source_files {
let parser = AstParser::default();
let stripped_source = parser.strip_types(
let (stripped_source, _maybe_source_map) = swc_util::transpile(
&source_file.file_name,
MediaType::TypeScript,
&source_file.source_code,
&transpile_options,
)?;
// TODO(bartlomieju): this is superfluous, just to make caching function happy