mirror of
https://github.com/denoland/deno.git
synced 2025-09-27 12:49:10 +00:00
refactor(cli): rewrite Deno.transpileOnly() to use SWC (#8090)
Co-authored-by: Kitson Kelly <me@kitsonkelly.com>
This commit is contained in:
parent
aebbdd5cc2
commit
57cad53945
8 changed files with 178 additions and 131 deletions
18
cli/ast.rs
18
cli/ast.rs
|
@ -225,7 +225,7 @@ impl From<tsc_config::TsConfig> for EmitOptions {
|
||||||
EmitOptions {
|
EmitOptions {
|
||||||
check_js: options.check_js,
|
check_js: options.check_js,
|
||||||
emit_metadata: options.emit_decorator_metadata,
|
emit_metadata: options.emit_decorator_metadata,
|
||||||
inline_source_map: true,
|
inline_source_map: options.inline_source_map,
|
||||||
jsx_factory: options.jsx_factory,
|
jsx_factory: options.jsx_factory,
|
||||||
jsx_fragment_factory: options.jsx_fragment_factory,
|
jsx_fragment_factory: options.jsx_fragment_factory,
|
||||||
transform_jsx: options.jsx == "react",
|
transform_jsx: options.jsx == "react",
|
||||||
|
@ -356,8 +356,11 @@ impl ParsedModule {
|
||||||
/// - `source` - The source code for the module.
|
/// - `source` - The source code for the module.
|
||||||
/// - `media_type` - The media type for the module.
|
/// - `media_type` - The media type for the module.
|
||||||
///
|
///
|
||||||
|
// NOTE(bartlomieju): `specifier` has `&str` type instead of
|
||||||
|
// `&ModuleSpecifier` because runtime compiler APIs don't
|
||||||
|
// require valid module specifiers
|
||||||
pub fn parse(
|
pub fn parse(
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &str,
|
||||||
source: &str,
|
source: &str,
|
||||||
media_type: &MediaType,
|
media_type: &MediaType,
|
||||||
) -> Result<ParsedModule, AnyError> {
|
) -> Result<ParsedModule, AnyError> {
|
||||||
|
@ -505,8 +508,9 @@ mod tests {
|
||||||
let source = r#"import * as bar from "./test.ts";
|
let source = r#"import * as bar from "./test.ts";
|
||||||
const foo = await import("./foo.ts");
|
const foo = await import("./foo.ts");
|
||||||
"#;
|
"#;
|
||||||
let parsed_module = parse(&specifier, source, &MediaType::JavaScript)
|
let parsed_module =
|
||||||
.expect("could not parse module");
|
parse(specifier.as_str(), source, &MediaType::JavaScript)
|
||||||
|
.expect("could not parse module");
|
||||||
let actual = parsed_module.analyze_dependencies();
|
let actual = parsed_module.analyze_dependencies();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
actual,
|
actual,
|
||||||
|
@ -553,7 +557,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
let module = parse(&specifier, source, &MediaType::TypeScript)
|
let module = parse(specifier.as_str(), source, &MediaType::TypeScript)
|
||||||
.expect("could not parse module");
|
.expect("could not parse module");
|
||||||
let (code, maybe_map) = module
|
let (code, maybe_map) = module
|
||||||
.transpile(&EmitOptions::default())
|
.transpile(&EmitOptions::default())
|
||||||
|
@ -577,7 +581,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
let module = parse(&specifier, source, &MediaType::TSX)
|
let module = parse(specifier.as_str(), source, &MediaType::TSX)
|
||||||
.expect("could not parse module");
|
.expect("could not parse module");
|
||||||
let (code, _) = module
|
let (code, _) = module
|
||||||
.transpile(&EmitOptions::default())
|
.transpile(&EmitOptions::default())
|
||||||
|
@ -608,7 +612,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
let module = parse(&specifier, source, &MediaType::TypeScript)
|
let module = parse(specifier.as_str(), source, &MediaType::TypeScript)
|
||||||
.expect("could not parse module");
|
.expect("could not parse module");
|
||||||
let (code, _) = module
|
let (code, _) = module
|
||||||
.transpile(&EmitOptions::default())
|
.transpile(&EmitOptions::default())
|
||||||
|
|
3
cli/dts/lib.deno.unstable.d.ts
vendored
3
cli/dts/lib.deno.unstable.d.ts
vendored
|
@ -480,8 +480,7 @@ declare namespace Deno {
|
||||||
* source map.
|
* source map.
|
||||||
* @param options An option object of options to send to the compiler. This is
|
* @param options An option object of options to send to the compiler. This is
|
||||||
* a subset of ts.CompilerOptions which can be supported by Deno.
|
* a subset of ts.CompilerOptions which can be supported by Deno.
|
||||||
* Many of the options related to type checking and emitting
|
* If unsupported option is passed then the API will throw an error.
|
||||||
* type declaration files will have no impact on the output.
|
|
||||||
*/
|
*/
|
||||||
export function transpileOnly(
|
export function transpileOnly(
|
||||||
sources: Record<string, string>,
|
sources: Record<string, string>,
|
||||||
|
|
|
@ -320,7 +320,8 @@ impl Module {
|
||||||
/// Parse a module, populating the structure with data retrieved from the
|
/// Parse a module, populating the structure with data retrieved from the
|
||||||
/// source of the module.
|
/// source of the module.
|
||||||
pub fn parse(&mut self) -> Result<(), AnyError> {
|
pub fn parse(&mut self) -> Result<(), AnyError> {
|
||||||
let parsed_module = parse(&self.specifier, &self.source, &self.media_type)?;
|
let parsed_module =
|
||||||
|
parse(self.specifier.as_str(), &self.source, &self.media_type)?;
|
||||||
|
|
||||||
// parse out any triple slash references
|
// parse out any triple slash references
|
||||||
for comment in parsed_module.get_leading_comments().iter() {
|
for comment in parsed_module.get_leading_comments().iter() {
|
||||||
|
@ -639,12 +640,13 @@ impl Graph2 {
|
||||||
let mut ts_config = TsConfig::new(json!({
|
let mut ts_config = TsConfig::new(json!({
|
||||||
"checkJs": false,
|
"checkJs": false,
|
||||||
"emitDecoratorMetadata": false,
|
"emitDecoratorMetadata": false,
|
||||||
|
"inlineSourceMap": true,
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"jsxFactory": "React.createElement",
|
"jsxFactory": "React.createElement",
|
||||||
"jsxFragmentFactory": "React.Fragment",
|
"jsxFragmentFactory": "React.Fragment",
|
||||||
}));
|
}));
|
||||||
let maybe_ignored_options =
|
let maybe_ignored_options =
|
||||||
ts_config.merge_user_config(options.maybe_config_path)?;
|
ts_config.merge_tsconfig(options.maybe_config_path)?;
|
||||||
let emit_options: EmitOptions = ts_config.into();
|
let emit_options: EmitOptions = ts_config.into();
|
||||||
let cm = Rc::new(swc_common::SourceMap::new(
|
let cm = Rc::new(swc_common::SourceMap::new(
|
||||||
swc_common::FilePathMapping::empty(),
|
swc_common::FilePathMapping::empty(),
|
||||||
|
@ -730,7 +732,7 @@ impl Graph2 {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
let maybe_ignored_options =
|
let maybe_ignored_options =
|
||||||
config.merge_user_config(options.maybe_config_path)?;
|
config.merge_tsconfig(options.maybe_config_path)?;
|
||||||
|
|
||||||
// Short circuit if none of the modules require an emit, or all of the
|
// Short circuit if none of the modules require an emit, or all of the
|
||||||
// modules that require an emit have a valid emit. There is also an edge
|
// modules that require an emit have a valid emit. There is also an edge
|
||||||
|
@ -1187,13 +1189,14 @@ impl Graph2 {
|
||||||
let mut ts_config = TsConfig::new(json!({
|
let mut ts_config = TsConfig::new(json!({
|
||||||
"checkJs": false,
|
"checkJs": false,
|
||||||
"emitDecoratorMetadata": false,
|
"emitDecoratorMetadata": false,
|
||||||
|
"inlineSourceMap": true,
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"jsxFactory": "React.createElement",
|
"jsxFactory": "React.createElement",
|
||||||
"jsxFragmentFactory": "React.Fragment",
|
"jsxFragmentFactory": "React.Fragment",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let maybe_ignored_options =
|
let maybe_ignored_options =
|
||||||
ts_config.merge_user_config(options.maybe_config_path)?;
|
ts_config.merge_tsconfig(options.maybe_config_path)?;
|
||||||
|
|
||||||
let emit_options: EmitOptions = ts_config.clone().into();
|
let emit_options: EmitOptions = ts_config.clone().into();
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use crate::ast;
|
||||||
|
use crate::colors;
|
||||||
|
use crate::media_type::MediaType;
|
||||||
use crate::permissions::Permissions;
|
use crate::permissions::Permissions;
|
||||||
use crate::tsc::runtime_bundle;
|
use crate::tsc::runtime_bundle;
|
||||||
use crate::tsc::runtime_compile;
|
use crate::tsc::runtime_compile;
|
||||||
use crate::tsc::runtime_transpile;
|
use crate::tsc_config;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::futures::FutureExt;
|
use deno_core::futures::FutureExt;
|
||||||
|
use deno_core::serde::Serialize;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
|
use deno_core::serde_json::json;
|
||||||
use deno_core::serde_json::Value;
|
use deno_core::serde_json::Value;
|
||||||
use deno_core::BufVec;
|
use deno_core::BufVec;
|
||||||
use deno_core::OpState;
|
use deno_core::OpState;
|
||||||
|
@ -71,16 +76,58 @@ struct TranspileArgs {
|
||||||
options: Option<String>,
|
options: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct RuntimeTranspileEmit {
|
||||||
|
source: String,
|
||||||
|
map: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
async fn op_transpile(
|
async fn op_transpile(
|
||||||
state: Rc<RefCell<OpState>>,
|
state: Rc<RefCell<OpState>>,
|
||||||
args: Value,
|
args: Value,
|
||||||
_data: BufVec,
|
_data: BufVec,
|
||||||
) -> Result<Value, AnyError> {
|
) -> Result<Value, AnyError> {
|
||||||
super::check_unstable2(&state, "Deno.transpile");
|
super::check_unstable2(&state, "Deno.transpileOnly");
|
||||||
let args: TranspileArgs = serde_json::from_value(args)?;
|
let args: TranspileArgs = serde_json::from_value(args)?;
|
||||||
let cli_state = super::global_state2(&state);
|
|
||||||
let program_state = cli_state.clone();
|
let mut compiler_options = tsc_config::TsConfig::new(json!({
|
||||||
let result =
|
"checkJs": true,
|
||||||
runtime_transpile(program_state, &args.sources, &args.options).await?;
|
"emitDecoratorMetadata": false,
|
||||||
|
"jsx": "react",
|
||||||
|
"jsxFactory": "React.createElement",
|
||||||
|
"jsxFragmentFactory": "React.Fragment",
|
||||||
|
"inlineSourceMap": false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
let user_options: HashMap<String, Value> = if let Some(options) = args.options
|
||||||
|
{
|
||||||
|
serde_json::from_str(&options)?
|
||||||
|
} else {
|
||||||
|
HashMap::new()
|
||||||
|
};
|
||||||
|
let maybe_ignored_options =
|
||||||
|
compiler_options.merge_user_config(&user_options)?;
|
||||||
|
// TODO(@kitsonk) these really should just be passed back to the caller
|
||||||
|
if let Some(ignored_options) = maybe_ignored_options {
|
||||||
|
info!("{}: {}", colors::yellow("warning"), ignored_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
let emit_options: ast::EmitOptions = compiler_options.into();
|
||||||
|
let mut emit_map = HashMap::new();
|
||||||
|
|
||||||
|
for (specifier, source) in args.sources {
|
||||||
|
let media_type = MediaType::from(&specifier);
|
||||||
|
let parsed_module = ast::parse(&specifier, &source, &media_type)?;
|
||||||
|
let (source, maybe_source_map) = parsed_module.transpile(&emit_options)?;
|
||||||
|
|
||||||
|
emit_map.insert(
|
||||||
|
specifier.to_string(),
|
||||||
|
RuntimeTranspileEmit {
|
||||||
|
source,
|
||||||
|
map: maybe_source_map,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let result = serde_json::to_value(emit_map)?;
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,17 +129,16 @@ Deno.test({
|
||||||
async fn() {
|
async fn() {
|
||||||
const actual = await Deno.transpileOnly(
|
const actual = await Deno.transpileOnly(
|
||||||
{
|
{
|
||||||
"foo.ts": `export enum Foo { Foo, Bar, Baz };\n`,
|
"foo.ts": `/** This is JSDoc */\nexport enum Foo { Foo, Bar, Baz };\n`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sourceMap: false,
|
removeComments: true,
|
||||||
module: "amd",
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
assert(actual);
|
assert(actual);
|
||||||
assertEquals(Object.keys(actual), ["foo.ts"]);
|
assertEquals(Object.keys(actual), ["foo.ts"]);
|
||||||
assert(actual["foo.ts"].source.startsWith("define("));
|
assert(!actual["foo.ts"].source.includes("This is JSDoc"));
|
||||||
assert(actual["foo.ts"].map == null);
|
assert(actual["foo.ts"].map);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
39
cli/tsc.rs
39
cli/tsc.rs
|
@ -725,41 +725,6 @@ pub async fn runtime_bundle(
|
||||||
Ok(serde_json::from_str::<Value>(&json_str).unwrap())
|
Ok(serde_json::from_str::<Value>(&json_str).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function is used by `Deno.transpileOnly()` API.
|
|
||||||
pub async fn runtime_transpile(
|
|
||||||
program_state: Arc<ProgramState>,
|
|
||||||
sources: &HashMap<String, String>,
|
|
||||||
maybe_options: &Option<String>,
|
|
||||||
) -> Result<Value, AnyError> {
|
|
||||||
let user_options = if let Some(options) = maybe_options {
|
|
||||||
tsc_config::parse_raw_config(options)?
|
|
||||||
} else {
|
|
||||||
json!({})
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut compiler_options = json!({
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"module": "esnext",
|
|
||||||
"sourceMap": true,
|
|
||||||
"scriptComments": true,
|
|
||||||
"target": "esnext",
|
|
||||||
});
|
|
||||||
tsc_config::json_merge(&mut compiler_options, &user_options);
|
|
||||||
|
|
||||||
let req_msg = json!({
|
|
||||||
"type": CompilerRequestType::RuntimeTranspile,
|
|
||||||
"sources": sources,
|
|
||||||
"compilerOptions": compiler_options,
|
|
||||||
})
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let json_str =
|
|
||||||
execute_in_tsc(program_state, req_msg).map_err(extract_js_error)?;
|
|
||||||
let v = serde_json::from_str::<Value>(&json_str)
|
|
||||||
.expect("Error decoding JSON string.");
|
|
||||||
Ok(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct ImportDesc {
|
pub struct ImportDesc {
|
||||||
pub specifier: String,
|
pub specifier: String,
|
||||||
|
@ -793,7 +758,7 @@ pub fn pre_process_file(
|
||||||
analyze_dynamic_imports: bool,
|
analyze_dynamic_imports: bool,
|
||||||
) -> Result<(Vec<ImportDesc>, Vec<TsReferenceDesc>), AnyError> {
|
) -> Result<(Vec<ImportDesc>, Vec<TsReferenceDesc>), AnyError> {
|
||||||
let specifier = ModuleSpecifier::resolve_url_or_path(file_name)?;
|
let specifier = ModuleSpecifier::resolve_url_or_path(file_name)?;
|
||||||
let module = parse(&specifier, source_code, &media_type)?;
|
let module = parse(specifier.as_str(), source_code, &media_type)?;
|
||||||
|
|
||||||
let dependency_descriptors = module.analyze_dependencies();
|
let dependency_descriptors = module.analyze_dependencies();
|
||||||
|
|
||||||
|
@ -894,7 +859,6 @@ fn parse_deno_types(comment: &str) -> Option<String> {
|
||||||
pub enum CompilerRequestType {
|
pub enum CompilerRequestType {
|
||||||
RuntimeCompile = 2,
|
RuntimeCompile = 2,
|
||||||
RuntimeBundle = 3,
|
RuntimeBundle = 3,
|
||||||
RuntimeTranspile = 4,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for CompilerRequestType {
|
impl Serialize for CompilerRequestType {
|
||||||
|
@ -905,7 +869,6 @@ impl Serialize for CompilerRequestType {
|
||||||
let value: i32 = match self {
|
let value: i32 = match self {
|
||||||
CompilerRequestType::RuntimeCompile => 2 as i32,
|
CompilerRequestType::RuntimeCompile => 2 as i32,
|
||||||
CompilerRequestType::RuntimeBundle => 3 as i32,
|
CompilerRequestType::RuntimeBundle => 3 as i32,
|
||||||
CompilerRequestType::RuntimeTranspile => 4 as i32,
|
|
||||||
};
|
};
|
||||||
Serialize::serialize(&value, serializer)
|
Serialize::serialize(&value, serializer)
|
||||||
}
|
}
|
||||||
|
|
|
@ -599,7 +599,6 @@ delete Object.prototype.__proto__;
|
||||||
const CompilerRequestType = {
|
const CompilerRequestType = {
|
||||||
RuntimeCompile: 2,
|
RuntimeCompile: 2,
|
||||||
RuntimeBundle: 3,
|
RuntimeBundle: 3,
|
||||||
RuntimeTranspile: 4,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function createBundleWriteFile(state) {
|
function createBundleWriteFile(state) {
|
||||||
|
@ -999,31 +998,6 @@ delete Object.prototype.__proto__;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function runtimeTranspile(request) {
|
|
||||||
const result = {};
|
|
||||||
const { sources, compilerOptions } = request;
|
|
||||||
|
|
||||||
const parseResult = parseCompilerOptions(
|
|
||||||
compilerOptions,
|
|
||||||
);
|
|
||||||
const options = parseResult.options;
|
|
||||||
// TODO(bartlomieju): this options is excluded by `ts.convertCompilerOptionsFromJson`
|
|
||||||
// however stuff breaks if it's not passed (type_directives_js_main.js, compiler_js_error.ts)
|
|
||||||
options.allowNonTsExtensions = true;
|
|
||||||
|
|
||||||
for (const [fileName, inputText] of Object.entries(sources)) {
|
|
||||||
const { outputText: source, sourceMapText: map } = ts.transpileModule(
|
|
||||||
inputText,
|
|
||||||
{
|
|
||||||
fileName,
|
|
||||||
compilerOptions: options,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
result[fileName] = { source, map };
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function opCompilerRespond(msg) {
|
function opCompilerRespond(msg) {
|
||||||
core.jsonOpSync("op_compiler_respond", msg);
|
core.jsonOpSync("op_compiler_respond", msg);
|
||||||
}
|
}
|
||||||
|
@ -1041,11 +1015,6 @@ delete Object.prototype.__proto__;
|
||||||
opCompilerRespond(result);
|
opCompilerRespond(result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CompilerRequestType.RuntimeTranspile: {
|
|
||||||
const result = runtimeTranspile(request);
|
|
||||||
opCompilerRespond(result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`!!! unhandled CompilerRequestType: ${request.type} (${
|
`!!! unhandled CompilerRequestType: ${request.type} (${
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
|
use deno_core::serde_json::json;
|
||||||
use deno_core::serde_json::Value;
|
use deno_core::serde_json::Value;
|
||||||
use jsonc_parser::JsonValue;
|
use jsonc_parser::JsonValue;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -20,6 +21,7 @@ use std::str::FromStr;
|
||||||
pub struct EmitConfigOptions {
|
pub struct EmitConfigOptions {
|
||||||
pub check_js: bool,
|
pub check_js: bool,
|
||||||
pub emit_decorator_metadata: bool,
|
pub emit_decorator_metadata: bool,
|
||||||
|
pub inline_source_map: bool,
|
||||||
pub jsx: String,
|
pub jsx: String,
|
||||||
pub jsx_factory: String,
|
pub jsx_factory: String,
|
||||||
pub jsx_fragment_factory: String,
|
pub jsx_fragment_factory: String,
|
||||||
|
@ -30,77 +32,82 @@ pub struct EmitConfigOptions {
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct IgnoredCompilerOptions {
|
pub struct IgnoredCompilerOptions {
|
||||||
pub items: Vec<String>,
|
pub items: Vec<String>,
|
||||||
pub path: PathBuf,
|
pub maybe_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for IgnoredCompilerOptions {
|
impl fmt::Display for IgnoredCompilerOptions {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let mut codes = self.items.clone();
|
let mut codes = self.items.clone();
|
||||||
codes.sort();
|
codes.sort();
|
||||||
|
if let Some(path) = &self.maybe_path {
|
||||||
write!(f, "Unsupported compiler options in \"{}\".\n The following options were ignored:\n {}", self.path.to_string_lossy(), codes.join(", "))
|
write!(f, "Unsupported compiler options in \"{}\".\n The following options were ignored:\n {}", path.to_string_lossy(), codes.join(", "))
|
||||||
|
} else {
|
||||||
|
write!(f, "Unsupported compiler options provided.\n The following options were ignored:\n {}", codes.join(", "))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A static slice of all the compiler options that should be ignored that
|
/// A static slice of all the compiler options that should be ignored that
|
||||||
/// either have no effect on the compilation or would cause the emit to not work
|
/// either have no effect on the compilation or would cause the emit to not work
|
||||||
/// in Deno.
|
/// in Deno.
|
||||||
const IGNORED_COMPILER_OPTIONS: [&str; 61] = [
|
const IGNORED_COMPILER_OPTIONS: [&str; 10] = [
|
||||||
"allowSyntheticDefaultImports",
|
"allowSyntheticDefaultImports",
|
||||||
|
"esModuleInterop",
|
||||||
|
"inlineSourceMap",
|
||||||
|
"inlineSources",
|
||||||
|
// TODO(nayeemrmn): Add "isolatedModules" here for 1.6.0.
|
||||||
|
"module",
|
||||||
|
"noLib",
|
||||||
|
"preserveConstEnums",
|
||||||
|
"reactNamespace",
|
||||||
|
"sourceMap",
|
||||||
|
"target",
|
||||||
|
];
|
||||||
|
|
||||||
|
const IGNORED_RUNTIME_COMPILER_OPTIONS: [&str; 50] = [
|
||||||
"allowUmdGlobalAccess",
|
"allowUmdGlobalAccess",
|
||||||
"assumeChangesOnlyAffectDirectDependencies",
|
"assumeChangesOnlyAffectDirectDependencies",
|
||||||
"baseUrl",
|
"baseUrl",
|
||||||
"build",
|
"build",
|
||||||
"composite",
|
"composite",
|
||||||
"declaration",
|
"declaration",
|
||||||
"declarationDir",
|
|
||||||
"declarationMap",
|
"declarationMap",
|
||||||
"diagnostics",
|
"diagnostics",
|
||||||
"downlevelIteration",
|
"downlevelIteration",
|
||||||
"emitBOM",
|
"emitBOM",
|
||||||
"emitDeclarationOnly",
|
"emitDeclarationOnly",
|
||||||
"esModuleInterop",
|
|
||||||
"extendedDiagnostics",
|
"extendedDiagnostics",
|
||||||
"forceConsistentCasingInFileNames",
|
"forceConsistentCasingInFileNames",
|
||||||
"generateCpuProfile",
|
"generateCpuProfile",
|
||||||
"help",
|
"help",
|
||||||
"importHelpers",
|
"importHelpers",
|
||||||
"incremental",
|
"incremental",
|
||||||
"inlineSourceMap",
|
|
||||||
"inlineSources",
|
|
||||||
"init",
|
"init",
|
||||||
// TODO(nayeemrmn): Add "isolatedModules" here for 1.6.0.
|
|
||||||
"listEmittedFiles",
|
"listEmittedFiles",
|
||||||
"listFiles",
|
"listFiles",
|
||||||
"mapRoot",
|
"mapRoot",
|
||||||
"maxNodeModuleJsDepth",
|
"maxNodeModuleJsDepth",
|
||||||
"module",
|
|
||||||
"moduleResolution",
|
"moduleResolution",
|
||||||
"newLine",
|
"newLine",
|
||||||
"noEmit",
|
"noEmit",
|
||||||
"noEmitHelpers",
|
"noEmitHelpers",
|
||||||
"noEmitOnError",
|
"noEmitOnError",
|
||||||
"noLib",
|
|
||||||
"noResolve",
|
"noResolve",
|
||||||
"out",
|
"out",
|
||||||
"outDir",
|
"outDir",
|
||||||
"outFile",
|
"outFile",
|
||||||
"paths",
|
"paths",
|
||||||
"preserveConstEnums",
|
|
||||||
"preserveSymlinks",
|
"preserveSymlinks",
|
||||||
"preserveWatchOutput",
|
"preserveWatchOutput",
|
||||||
"pretty",
|
"pretty",
|
||||||
"reactNamespace",
|
|
||||||
"resolveJsonModule",
|
"resolveJsonModule",
|
||||||
"rootDir",
|
"rootDir",
|
||||||
"rootDirs",
|
"rootDirs",
|
||||||
"showConfig",
|
"showConfig",
|
||||||
"skipDefaultLibCheck",
|
"skipDefaultLibCheck",
|
||||||
"skipLibCheck",
|
"skipLibCheck",
|
||||||
"sourceMap",
|
|
||||||
"sourceRoot",
|
"sourceRoot",
|
||||||
"stripInternal",
|
"stripInternal",
|
||||||
"target",
|
|
||||||
"traceResolution",
|
"traceResolution",
|
||||||
"tsBuildInfoFile",
|
"tsBuildInfoFile",
|
||||||
"types",
|
"types",
|
||||||
|
@ -167,6 +174,34 @@ pub fn parse_raw_config(config_text: &str) -> Result<Value, AnyError> {
|
||||||
Ok(jsonc_to_serde(jsonc))
|
Ok(jsonc_to_serde(jsonc))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_compiler_options(
|
||||||
|
compiler_options: &HashMap<String, Value>,
|
||||||
|
maybe_path: Option<PathBuf>,
|
||||||
|
is_runtime: bool,
|
||||||
|
) -> Result<(Value, Option<IgnoredCompilerOptions>), AnyError> {
|
||||||
|
let mut filtered: HashMap<String, Value> = HashMap::new();
|
||||||
|
let mut items: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
for (key, value) in compiler_options.iter() {
|
||||||
|
let key = key.as_str();
|
||||||
|
if (!is_runtime && IGNORED_COMPILER_OPTIONS.contains(&key))
|
||||||
|
|| IGNORED_RUNTIME_COMPILER_OPTIONS.contains(&key)
|
||||||
|
{
|
||||||
|
items.push(key.to_string());
|
||||||
|
} else {
|
||||||
|
filtered.insert(key.to_string(), value.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let value = serde_json::to_value(filtered)?;
|
||||||
|
let maybe_ignored_options = if !items.is_empty() {
|
||||||
|
Some(IgnoredCompilerOptions { items, maybe_path })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((value, maybe_ignored_options))
|
||||||
|
}
|
||||||
|
|
||||||
/// Take a string of JSONC, parse it and return a serde `Value` of the text.
|
/// Take a string of JSONC, parse it and return a serde `Value` of the text.
|
||||||
/// The result also contains any options that were ignored.
|
/// The result also contains any options that were ignored.
|
||||||
pub fn parse_config(
|
pub fn parse_config(
|
||||||
|
@ -176,29 +211,12 @@ pub fn parse_config(
|
||||||
assert!(!config_text.is_empty());
|
assert!(!config_text.is_empty());
|
||||||
let jsonc = jsonc_parser::parse_to_value(config_text)?.unwrap();
|
let jsonc = jsonc_parser::parse_to_value(config_text)?.unwrap();
|
||||||
let config: TSConfigJson = serde_json::from_value(jsonc_to_serde(jsonc))?;
|
let config: TSConfigJson = serde_json::from_value(jsonc_to_serde(jsonc))?;
|
||||||
let mut compiler_options: HashMap<String, Value> = HashMap::new();
|
|
||||||
let mut items: Vec<String> = Vec::new();
|
|
||||||
|
|
||||||
if let Some(in_compiler_options) = config.compiler_options {
|
if let Some(compiler_options) = config.compiler_options {
|
||||||
for (key, value) in in_compiler_options.iter() {
|
parse_compiler_options(&compiler_options, Some(path.to_owned()), false)
|
||||||
if IGNORED_COMPILER_OPTIONS.contains(&key.as_str()) {
|
|
||||||
items.push(key.to_owned());
|
|
||||||
} else {
|
|
||||||
compiler_options.insert(key.to_owned(), value.to_owned());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let options_value = serde_json::to_value(compiler_options)?;
|
|
||||||
let ignored_options = if !items.is_empty() {
|
|
||||||
Some(IgnoredCompilerOptions {
|
|
||||||
items,
|
|
||||||
path: path.to_path_buf(),
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
Ok((json!({}), None))
|
||||||
};
|
}
|
||||||
|
|
||||||
Ok((options_value, ignored_options))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A structure for managing the configuration of TypeScript
|
/// A structure for managing the configuration of TypeScript
|
||||||
|
@ -237,7 +255,7 @@ impl TsConfig {
|
||||||
///
|
///
|
||||||
/// When there are options ignored out of the file, a warning will be written
|
/// When there are options ignored out of the file, a warning will be written
|
||||||
/// to stderr regarding the options that were ignored.
|
/// to stderr regarding the options that were ignored.
|
||||||
pub fn merge_user_config(
|
pub fn merge_tsconfig(
|
||||||
&mut self,
|
&mut self,
|
||||||
maybe_path: Option<String>,
|
maybe_path: Option<String>,
|
||||||
) -> Result<Option<IgnoredCompilerOptions>, AnyError> {
|
) -> Result<Option<IgnoredCompilerOptions>, AnyError> {
|
||||||
|
@ -263,6 +281,19 @@ impl TsConfig {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Take a map of compiler options, filtering out any that are ignored, then
|
||||||
|
/// merge it with the current configuration, returning any options that might
|
||||||
|
/// have been ignored.
|
||||||
|
pub fn merge_user_config(
|
||||||
|
&mut self,
|
||||||
|
user_options: &HashMap<String, Value>,
|
||||||
|
) -> Result<Option<IgnoredCompilerOptions>, AnyError> {
|
||||||
|
let (value, maybe_ignored_options) =
|
||||||
|
parse_compiler_options(user_options, None, true)?;
|
||||||
|
json_merge(&mut self.0, &value);
|
||||||
|
Ok(maybe_ignored_options)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for TsConfig {
|
impl Serialize for TsConfig {
|
||||||
|
@ -321,11 +352,43 @@ mod tests {
|
||||||
ignored,
|
ignored,
|
||||||
Some(IgnoredCompilerOptions {
|
Some(IgnoredCompilerOptions {
|
||||||
items: vec!["build".to_string()],
|
items: vec!["build".to_string()],
|
||||||
path: config_path,
|
maybe_path: Some(config_path),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tsconfig_merge_user_options() {
|
||||||
|
let mut tsconfig = TsConfig::new(json!({
|
||||||
|
"target": "esnext",
|
||||||
|
"module": "esnext",
|
||||||
|
}));
|
||||||
|
let user_options = serde_json::from_value(json!({
|
||||||
|
"target": "es6",
|
||||||
|
"build": true,
|
||||||
|
"strict": false,
|
||||||
|
}))
|
||||||
|
.expect("could not convert to hashmap");
|
||||||
|
let maybe_ignored_options = tsconfig
|
||||||
|
.merge_user_config(&user_options)
|
||||||
|
.expect("could not merge options");
|
||||||
|
assert_eq!(
|
||||||
|
tsconfig.0,
|
||||||
|
json!({
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "es6",
|
||||||
|
"strict": false,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
maybe_ignored_options,
|
||||||
|
Some(IgnoredCompilerOptions {
|
||||||
|
items: vec!["build".to_string()],
|
||||||
|
maybe_path: None
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_raw_config() {
|
fn test_parse_raw_config() {
|
||||||
let invalid_config_text = r#"{
|
let invalid_config_text = r#"{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue