mirror of
https://github.com/denoland/deno.git
synced 2025-01-11 16:42:21 -05:00
parent
73be183864
commit
1a0f53a807
14 changed files with 438 additions and 25 deletions
|
@ -52,9 +52,10 @@ ts_sources = [
|
|||
"../js/buffer.ts",
|
||||
"../js/build.ts",
|
||||
"../js/chmod.ts",
|
||||
"../js/console_table.ts",
|
||||
"../js/colors.ts",
|
||||
"../js/compiler.ts",
|
||||
"../js/console.ts",
|
||||
"../js/console_table.ts",
|
||||
"../js/copy_file.ts",
|
||||
"../js/core.ts",
|
||||
"../js/custom_event.ts",
|
||||
|
|
|
@ -158,6 +158,23 @@ fn req(specifier: &str, referrer: &str, cmd_id: u32) -> Buf {
|
|||
.into_boxed_bytes()
|
||||
}
|
||||
|
||||
/// Returns an optional tuple which represents the state of the compiler
|
||||
/// configuration where the first is canonical name for the configuration file
|
||||
/// and a vector of the bytes of the contents of the configuration file.
|
||||
pub fn get_compiler_config(
|
||||
parent_state: &ThreadSafeState,
|
||||
_compiler_type: &str,
|
||||
) -> Option<(String, Vec<u8>)> {
|
||||
// The compiler type is being passed to make it easier to implement custom
|
||||
// compilers in the future.
|
||||
match (&parent_state.config_path, &parent_state.config) {
|
||||
(Some(config_path), Some(config)) => {
|
||||
Some((config_path.to_string(), config.to_vec()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile_async(
|
||||
parent_state: ThreadSafeState,
|
||||
specifier: &str,
|
||||
|
@ -306,4 +323,12 @@ mod tests {
|
|||
|
||||
assert_eq!(parse_cmd_id(res_json), cmd_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_compiler_config_no_flag() {
|
||||
let compiler_type = "typescript";
|
||||
let state = ThreadSafeState::mock();
|
||||
let out = get_compiler_config(&state, compiler_type);
|
||||
assert_eq!(out, None);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,12 +51,18 @@ pub struct DenoDir {
|
|||
// This splits to http and https deps
|
||||
pub deps_http: PathBuf,
|
||||
pub deps_https: PathBuf,
|
||||
/// The active configuration file contents (or empty array) which applies to
|
||||
/// source code cached by `DenoDir`.
|
||||
pub config: Vec<u8>,
|
||||
}
|
||||
|
||||
impl DenoDir {
|
||||
// Must be called before using any function from this module.
|
||||
// https://github.com/denoland/deno/blob/golang/deno_dir.go#L99-L111
|
||||
pub fn new(custom_root: Option<PathBuf>) -> std::io::Result<Self> {
|
||||
pub fn new(
|
||||
custom_root: Option<PathBuf>,
|
||||
state_config: &Option<Vec<u8>>,
|
||||
) -> std::io::Result<Self> {
|
||||
// Only setup once.
|
||||
let home_dir = dirs::home_dir().expect("Could not get home directory.");
|
||||
let fallback = home_dir.join(".deno");
|
||||
|
@ -73,12 +79,22 @@ impl DenoDir {
|
|||
let deps_http = deps.join("http");
|
||||
let deps_https = deps.join("https");
|
||||
|
||||
// Internally within DenoDir, we use the config as part of the hash to
|
||||
// determine if a file has been transpiled with the same configuration, but
|
||||
// we have borrowed the `State` configuration, which we want to either clone
|
||||
// or create an empty `Vec` which we will use in our hash function.
|
||||
let config = match state_config {
|
||||
Some(config) => config.clone(),
|
||||
_ => b"".to_vec(),
|
||||
};
|
||||
|
||||
let deno_dir = Self {
|
||||
root,
|
||||
gen,
|
||||
deps,
|
||||
deps_http,
|
||||
deps_https,
|
||||
config,
|
||||
};
|
||||
|
||||
// TODO Lazily create these directories.
|
||||
|
@ -102,7 +118,8 @@ impl DenoDir {
|
|||
filename: &str,
|
||||
source_code: &[u8],
|
||||
) -> (PathBuf, PathBuf) {
|
||||
let cache_key = source_code_hash(filename, source_code, version::DENO);
|
||||
let cache_key =
|
||||
source_code_hash(filename, source_code, version::DENO, &self.config);
|
||||
(
|
||||
self.gen.join(cache_key.to_string() + ".js"),
|
||||
self.gen.join(cache_key.to_string() + ".js.map"),
|
||||
|
@ -156,6 +173,11 @@ impl DenoDir {
|
|||
|
||||
let gen = self.gen.clone();
|
||||
|
||||
// If we don't clone the config, we then end up creating an implied lifetime
|
||||
// which gets returned in the future, so we clone here so as to not leak the
|
||||
// move below when the future is resolving.
|
||||
let config = self.config.clone();
|
||||
|
||||
Either::B(
|
||||
get_source_code_async(
|
||||
self,
|
||||
|
@ -191,8 +213,12 @@ impl DenoDir {
|
|||
return Ok(out);
|
||||
}
|
||||
|
||||
let cache_key =
|
||||
source_code_hash(&out.filename, &out.source_code, version::DENO);
|
||||
let cache_key = source_code_hash(
|
||||
&out.filename,
|
||||
&out.source_code,
|
||||
version::DENO,
|
||||
&config,
|
||||
);
|
||||
let (output_code_filename, output_source_map_filename) = (
|
||||
gen.join(cache_key.to_string() + ".js"),
|
||||
gen.join(cache_key.to_string() + ".js.map"),
|
||||
|
@ -468,15 +494,19 @@ fn load_cache2(
|
|||
Ok((read_output_code, read_source_map))
|
||||
}
|
||||
|
||||
/// Generate an SHA1 hash for source code, to be used to determine if a cached
|
||||
/// version of the code is valid or invalid.
|
||||
fn source_code_hash(
|
||||
filename: &str,
|
||||
source_code: &[u8],
|
||||
version: &str,
|
||||
config: &[u8],
|
||||
) -> String {
|
||||
let mut ctx = ring::digest::Context::new(&ring::digest::SHA1);
|
||||
ctx.update(version.as_bytes());
|
||||
ctx.update(filename.as_bytes());
|
||||
ctx.update(source_code);
|
||||
ctx.update(config);
|
||||
let digest = ctx.finish();
|
||||
let mut out = String::new();
|
||||
// TODO There must be a better way to do this...
|
||||
|
@ -860,8 +890,9 @@ mod tests {
|
|||
|
||||
fn test_setup() -> (TempDir, DenoDir) {
|
||||
let temp_dir = TempDir::new().expect("tempdir fail");
|
||||
let deno_dir =
|
||||
DenoDir::new(Some(temp_dir.path().to_path_buf())).expect("setup fail");
|
||||
let config = Some(b"{}".to_vec());
|
||||
let deno_dir = DenoDir::new(Some(temp_dir.path().to_path_buf()), &config)
|
||||
.expect("setup fail");
|
||||
(temp_dir, deno_dir)
|
||||
}
|
||||
|
||||
|
@ -904,7 +935,8 @@ mod tests {
|
|||
let (temp_dir, deno_dir) = test_setup();
|
||||
let filename = "hello.js";
|
||||
let source_code = b"1+2";
|
||||
let hash = source_code_hash(filename, source_code, version::DENO);
|
||||
let config = b"{}";
|
||||
let hash = source_code_hash(filename, source_code, version::DENO, config);
|
||||
assert_eq!(
|
||||
(
|
||||
temp_dir.path().join(format!("gen/{}.js", hash)),
|
||||
|
@ -914,6 +946,24 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cache_path_config() {
|
||||
// We are changing the compiler config from the "mock" and so we expect the
|
||||
// resolved files coming back to not match the calculated hash.
|
||||
let (temp_dir, deno_dir) = test_setup();
|
||||
let filename = "hello.js";
|
||||
let source_code = b"1+2";
|
||||
let config = b"{\"compilerOptions\":{}}";
|
||||
let hash = source_code_hash(filename, source_code, version::DENO, config);
|
||||
assert_ne!(
|
||||
(
|
||||
temp_dir.path().join(format!("gen/{}.js", hash)),
|
||||
temp_dir.path().join(format!("gen/{}.js.map", hash))
|
||||
),
|
||||
deno_dir.cache_path(filename, source_code)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_code_cache() {
|
||||
let (_temp_dir, deno_dir) = test_setup();
|
||||
|
@ -922,7 +972,8 @@ mod tests {
|
|||
let source_code = b"1+2";
|
||||
let output_code = b"1+2 // output code";
|
||||
let source_map = b"{}";
|
||||
let hash = source_code_hash(filename, source_code, version::DENO);
|
||||
let config = b"{}";
|
||||
let hash = source_code_hash(filename, source_code, version::DENO, config);
|
||||
let (cache_path, source_map_path) =
|
||||
deno_dir.cache_path(filename, source_code);
|
||||
assert!(cache_path.ends_with(format!("gen/{}.js", hash)));
|
||||
|
@ -949,23 +1000,23 @@ mod tests {
|
|||
#[test]
|
||||
fn test_source_code_hash() {
|
||||
assert_eq!(
|
||||
"7e44de2ed9e0065da09d835b76b8d70be503d276",
|
||||
source_code_hash("hello.ts", b"1+2", "0.2.11")
|
||||
"830c8b63ba3194cf2108a3054c176b2bf53aee45",
|
||||
source_code_hash("hello.ts", b"1+2", "0.2.11", b"{}")
|
||||
);
|
||||
// Different source_code should result in different hash.
|
||||
assert_eq!(
|
||||
"57033366cf9db1ef93deca258cdbcd9ef5f4bde1",
|
||||
source_code_hash("hello.ts", b"1", "0.2.11")
|
||||
"fb06127e9b2e169bea9c697fa73386ae7c901e8b",
|
||||
source_code_hash("hello.ts", b"1", "0.2.11", b"{}")
|
||||
);
|
||||
// Different filename should result in different hash.
|
||||
assert_eq!(
|
||||
"19657f90b5b0540f87679e2fb362e7bd62b644b0",
|
||||
source_code_hash("hi.ts", b"1+2", "0.2.11")
|
||||
"3a17b6a493ff744b6a455071935f4bdcd2b72ec7",
|
||||
source_code_hash("hi.ts", b"1+2", "0.2.11", b"{}")
|
||||
);
|
||||
// Different version should result in different hash.
|
||||
assert_eq!(
|
||||
"e2b4b7162975a02bf2770f16836eb21d5bcb8be1",
|
||||
source_code_hash("hi.ts", b"1+2", "0.2.0")
|
||||
"d6b2cfdc39dae9bd3ad5b493ee1544eb22e7475f",
|
||||
source_code_hash("hi.ts", b"1+2", "0.2.0", b"{}")
|
||||
);
|
||||
}
|
||||
|
||||
|
|
24
cli/flags.rs
24
cli/flags.rs
|
@ -13,6 +13,9 @@ pub struct DenoFlags {
|
|||
pub log_debug: bool,
|
||||
pub version: bool,
|
||||
pub reload: bool,
|
||||
/// When the `--config`/`-c` flag is used to pass the name, this will be set
|
||||
/// the path passed on the command line, otherwise `None`.
|
||||
pub config_path: Option<String>,
|
||||
pub allow_read: bool,
|
||||
pub allow_write: bool,
|
||||
pub allow_net: bool,
|
||||
|
@ -79,6 +82,13 @@ pub fn create_cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||
.short("r")
|
||||
.long("reload")
|
||||
.help("Reload source code cache (recompile TypeScript)"),
|
||||
).arg(
|
||||
Arg::with_name("config")
|
||||
.short("c")
|
||||
.long("config")
|
||||
.value_name("FILE")
|
||||
.help("Load compiler configuration file")
|
||||
.takes_value(true),
|
||||
).arg(
|
||||
Arg::with_name("v8-options")
|
||||
.long("v8-options")
|
||||
|
@ -146,6 +156,7 @@ pub fn parse_flags(matches: ArgMatches) -> DenoFlags {
|
|||
if matches.is_present("reload") {
|
||||
flags.reload = true;
|
||||
}
|
||||
flags.config_path = matches.value_of("config").map(ToOwned::to_owned);
|
||||
if matches.is_present("allow-read") {
|
||||
flags.allow_read = true;
|
||||
}
|
||||
|
@ -353,4 +364,17 @@ mod tests {
|
|||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_flags_11() {
|
||||
let flags =
|
||||
flags_from_vec(svec!["deno", "-c", "tsconfig.json", "script.ts"]);
|
||||
assert_eq!(
|
||||
flags,
|
||||
DenoFlags {
|
||||
config_path: Some("tsconfig.json".to_owned()),
|
||||
..DenoFlags::default()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
11
cli/msg.fbs
11
cli/msg.fbs
|
@ -3,6 +3,8 @@ union Any {
|
|||
Chdir,
|
||||
Chmod,
|
||||
Close,
|
||||
CompilerConfig,
|
||||
CompilerConfigRes,
|
||||
CopyFile,
|
||||
Cwd,
|
||||
CwdRes,
|
||||
|
@ -174,6 +176,15 @@ table StartRes {
|
|||
no_color: bool;
|
||||
}
|
||||
|
||||
table CompilerConfig {
|
||||
compiler_type: string;
|
||||
}
|
||||
|
||||
table CompilerConfigRes {
|
||||
path: string;
|
||||
data: [ubyte];
|
||||
}
|
||||
|
||||
table FormatError {
|
||||
error: string;
|
||||
}
|
||||
|
|
38
cli/ops.rs
38
cli/ops.rs
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
use atty;
|
||||
use crate::ansi;
|
||||
use crate::compiler::get_compiler_config;
|
||||
use crate::errors;
|
||||
use crate::errors::{DenoError, DenoResult, ErrorKind};
|
||||
use crate::fs as deno_fs;
|
||||
|
@ -146,6 +147,8 @@ pub fn dispatch_all(
|
|||
|
||||
pub fn op_selector_compiler(inner_type: msg::Any) -> Option<OpCreator> {
|
||||
match inner_type {
|
||||
msg::Any::CompilerConfig => Some(op_compiler_config),
|
||||
msg::Any::Cwd => Some(op_cwd),
|
||||
msg::Any::FetchModuleMetaData => Some(op_fetch_module_meta_data),
|
||||
msg::Any::WorkerGetMessage => Some(op_worker_get_message),
|
||||
msg::Any::WorkerPostMessage => Some(op_worker_post_message),
|
||||
|
@ -443,6 +446,41 @@ fn op_fetch_module_meta_data(
|
|||
}()))
|
||||
}
|
||||
|
||||
/// Retrieve any relevant compiler configuration.
|
||||
fn op_compiler_config(
|
||||
state: &ThreadSafeState,
|
||||
base: &msg::Base<'_>,
|
||||
data: deno_buf,
|
||||
) -> Box<OpWithError> {
|
||||
assert_eq!(data.len(), 0);
|
||||
let inner = base.inner_as_compiler_config().unwrap();
|
||||
let cmd_id = base.cmd_id();
|
||||
let compiler_type = inner.compiler_type().unwrap();
|
||||
|
||||
Box::new(futures::future::result(|| -> OpResult {
|
||||
let builder = &mut FlatBufferBuilder::new();
|
||||
let (path, out) = match get_compiler_config(state, compiler_type) {
|
||||
Some(val) => val,
|
||||
_ => ("".to_owned(), "".as_bytes().to_owned()),
|
||||
};
|
||||
let data_off = builder.create_vector(&out);
|
||||
let msg_args = msg::CompilerConfigResArgs {
|
||||
path: Some(builder.create_string(&path)),
|
||||
data: Some(data_off),
|
||||
};
|
||||
let inner = msg::CompilerConfigRes::create(builder, &msg_args);
|
||||
Ok(serialize_response(
|
||||
cmd_id,
|
||||
builder,
|
||||
msg::BaseArgs {
|
||||
inner: Some(inner.as_union_value()),
|
||||
inner_type: msg::Any::CompilerConfigRes,
|
||||
..Default::default()
|
||||
},
|
||||
))
|
||||
}()))
|
||||
}
|
||||
|
||||
fn op_chdir(
|
||||
_state: &ThreadSafeState,
|
||||
base: &msg::Base<'_>,
|
||||
|
|
50
cli/state.rs
50
cli/state.rs
|
@ -15,6 +15,7 @@ use futures::future::Shared;
|
|||
use std;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
@ -51,6 +52,12 @@ pub struct State {
|
|||
pub argv: Vec<String>,
|
||||
pub permissions: DenoPermissions,
|
||||
pub flags: flags::DenoFlags,
|
||||
/// When flags contains a `.config_path` option, the content of the
|
||||
/// configuration file will be resolved and set.
|
||||
pub config: Option<Vec<u8>>,
|
||||
/// When flags contains a `.config_path` option, the fully qualified path
|
||||
/// name of the passed path will be resolved and set.
|
||||
pub config_path: Option<String>,
|
||||
pub metrics: Metrics,
|
||||
pub worker_channels: Mutex<WorkerChannels>,
|
||||
pub global_timer: Mutex<GlobalTimer>,
|
||||
|
@ -97,11 +104,52 @@ impl ThreadSafeState {
|
|||
let external_channels = (worker_in_tx, worker_out_rx);
|
||||
let resource = resources::add_worker(external_channels);
|
||||
|
||||
// take the passed flag and resolve the file name relative to the cwd
|
||||
let config_file = match &flags.config_path {
|
||||
Some(config_file_name) => {
|
||||
debug!("Compiler config file: {}", config_file_name);
|
||||
let cwd = std::env::current_dir().unwrap();
|
||||
Some(cwd.join(config_file_name))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Convert the PathBuf to a canonicalized string. This is needed by the
|
||||
// compiler to properly deal with the configuration.
|
||||
let config_path = match &config_file {
|
||||
Some(config_file) => Some(
|
||||
config_file
|
||||
.canonicalize()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_owned(),
|
||||
),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Load the contents of the configuration file
|
||||
let config = match &config_file {
|
||||
Some(config_file) => {
|
||||
debug!("Attempt to load config: {}", config_file.to_str().unwrap());
|
||||
match fs::read(&config_file) {
|
||||
Ok(config_data) => Some(config_data.to_owned()),
|
||||
_ => panic!(
|
||||
"Error retrieving compiler config file at \"{}\"",
|
||||
config_file.to_str().unwrap()
|
||||
),
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
ThreadSafeState(Arc::new(State {
|
||||
dir: deno_dir::DenoDir::new(custom_root).unwrap(),
|
||||
dir: deno_dir::DenoDir::new(custom_root, &config).unwrap(),
|
||||
argv: argv_rest,
|
||||
permissions: DenoPermissions::from_flags(&flags),
|
||||
flags,
|
||||
config,
|
||||
config_path,
|
||||
metrics: Metrics::default(),
|
||||
worker_channels: Mutex::new(internal_channels),
|
||||
global_timer: Mutex::new(GlobalTimer::new()),
|
||||
|
|
40
js/colors.ts
Normal file
40
js/colors.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// TODO(kitsonk) Replace with `deno_std/colors/mod.ts` when we can load modules
|
||||
// which end in `.ts`.
|
||||
|
||||
import { noColor } from "./os";
|
||||
|
||||
interface Code {
|
||||
open: string;
|
||||
close: string;
|
||||
regexp: RegExp;
|
||||
}
|
||||
|
||||
let enabled = !noColor;
|
||||
|
||||
function code(open: number, close: number): Code {
|
||||
return {
|
||||
open: `\x1b[${open}m`,
|
||||
close: `\x1b[${close}m`,
|
||||
regexp: new RegExp(`\\x1b\\[${close}m`, "g")
|
||||
};
|
||||
}
|
||||
|
||||
function run(str: string, code: Code): string {
|
||||
return enabled
|
||||
? `${code.open}${str.replace(code.regexp, code.open)}${code.close}`
|
||||
: str;
|
||||
}
|
||||
|
||||
export function bold(str: string): string {
|
||||
return run(str, code(1, 22));
|
||||
}
|
||||
|
||||
export function yellow(str: string): string {
|
||||
return run(str, code(33, 39));
|
||||
}
|
||||
|
||||
export function cyan(str: string): string {
|
||||
return run(str, code(36, 39));
|
||||
}
|
162
js/compiler.ts
162
js/compiler.ts
|
@ -3,8 +3,12 @@ import * as ts from "typescript";
|
|||
import * as msg from "gen/cli/msg_generated";
|
||||
import { window } from "./window";
|
||||
import { assetSourceCode } from "./assets";
|
||||
import { bold, cyan, yellow } from "./colors";
|
||||
import { Console } from "./console";
|
||||
import { core } from "./core";
|
||||
import { cwd } from "./dir";
|
||||
import { sendSync } from "./dispatch";
|
||||
import * as flatbuffers from "./flatbuffers";
|
||||
import * as os from "./os";
|
||||
import { TextDecoder, TextEncoder } from "./text_encoding";
|
||||
import { clearTimer, setTimeout } from "./timers";
|
||||
|
@ -55,17 +59,81 @@ interface CompilerLookup {
|
|||
interface Os {
|
||||
fetchModuleMetaData: typeof os.fetchModuleMetaData;
|
||||
exit: typeof os.exit;
|
||||
noColor: typeof os.noColor;
|
||||
}
|
||||
|
||||
/** Abstraction of the APIs required from the `typescript` module so they can
|
||||
* be easily mocked.
|
||||
*/
|
||||
interface Ts {
|
||||
convertCompilerOptionsFromJson: typeof ts.convertCompilerOptionsFromJson;
|
||||
createLanguageService: typeof ts.createLanguageService;
|
||||
formatDiagnosticsWithColorAndContext: typeof ts.formatDiagnosticsWithColorAndContext;
|
||||
formatDiagnostics: typeof ts.formatDiagnostics;
|
||||
parseConfigFileTextToJson: typeof ts.parseConfigFileTextToJson;
|
||||
}
|
||||
|
||||
/** Options that either do nothing in Deno, or would cause undesired behavior
|
||||
* if modified. */
|
||||
const ignoredCompilerOptions: ReadonlyArray<string> = [
|
||||
"allowSyntheticDefaultImports",
|
||||
"baseUrl",
|
||||
"build",
|
||||
"composite",
|
||||
"declaration",
|
||||
"declarationDir",
|
||||
"declarationMap",
|
||||
"diagnostics",
|
||||
"downlevelIteration",
|
||||
"emitBOM",
|
||||
"emitDeclarationOnly",
|
||||
"esModuleInterop",
|
||||
"extendedDiagnostics",
|
||||
"forceConsistentCasingInFileNames",
|
||||
"help",
|
||||
"importHelpers",
|
||||
"incremental",
|
||||
"inlineSourceMap",
|
||||
"inlineSources",
|
||||
"init",
|
||||
"isolatedModules",
|
||||
"lib",
|
||||
"listEmittedFiles",
|
||||
"listFiles",
|
||||
"mapRoot",
|
||||
"maxNodeModuleJsDepth",
|
||||
"module",
|
||||
"moduleResolution",
|
||||
"newLine",
|
||||
"noEmit",
|
||||
"noEmitHelpers",
|
||||
"noEmitOnError",
|
||||
"noLib",
|
||||
"noResolve",
|
||||
"out",
|
||||
"outDir",
|
||||
"outFile",
|
||||
"paths",
|
||||
"preserveSymlinks",
|
||||
"preserveWatchOutput",
|
||||
"pretty",
|
||||
"rootDir",
|
||||
"rootDirs",
|
||||
"showConfig",
|
||||
"skipDefaultLibCheck",
|
||||
"skipLibCheck",
|
||||
"sourceMap",
|
||||
"sourceRoot",
|
||||
"stripInternal",
|
||||
"target",
|
||||
"traceResolution",
|
||||
"tsBuildInfoFile",
|
||||
"types",
|
||||
"typeRoots",
|
||||
"version",
|
||||
"watch"
|
||||
];
|
||||
|
||||
/** A simple object structure for caching resolved modules and their contents.
|
||||
*
|
||||
* Named `ModuleMetaData` to clarify it is just a representation of meta data of
|
||||
|
@ -201,6 +269,18 @@ class Compiler implements ts.LanguageServiceHost, ts.FormatDiagnosticsHost {
|
|||
);
|
||||
}
|
||||
|
||||
/** Log TypeScript diagnostics to the console and exit */
|
||||
private _logDiagnostics(diagnostics: ts.Diagnostic[]): never {
|
||||
const errMsg = this._os.noColor
|
||||
? this._ts.formatDiagnostics(diagnostics, this)
|
||||
: this._ts.formatDiagnosticsWithColorAndContext(diagnostics, this);
|
||||
|
||||
console.log(errMsg);
|
||||
// TODO The compiler isolate shouldn't exit. Errors should be forwarded to
|
||||
// to the caller and the caller exit.
|
||||
return this._os.exit(1);
|
||||
}
|
||||
|
||||
/** Given a `moduleSpecifier` and `containingFile` retrieve the cached
|
||||
* `fileName` for a given module. If the module has yet to be resolved
|
||||
* this will return `undefined`.
|
||||
|
@ -354,13 +434,7 @@ class Compiler implements ts.LanguageServiceHost, ts.FormatDiagnosticsHost {
|
|||
...service.getSemanticDiagnostics(fileName)
|
||||
];
|
||||
if (diagnostics.length > 0) {
|
||||
const errMsg = os.noColor
|
||||
? this._ts.formatDiagnostics(diagnostics, this)
|
||||
: this._ts.formatDiagnosticsWithColorAndContext(diagnostics, this);
|
||||
|
||||
console.log(errMsg);
|
||||
// All TypeScript errors are terminal for deno
|
||||
this._os.exit(1);
|
||||
this._logDiagnostics(diagnostics);
|
||||
}
|
||||
|
||||
assert(
|
||||
|
@ -392,6 +466,40 @@ class Compiler implements ts.LanguageServiceHost, ts.FormatDiagnosticsHost {
|
|||
return { outputCode, sourceMap };
|
||||
}
|
||||
|
||||
/** Take a configuration string, parse it, and use it to merge with the
|
||||
* compiler's configuration options. The method returns an array of compiler
|
||||
* options which were ignored, or `undefined`.
|
||||
*/
|
||||
configure(path: string, configurationText: string): string[] | undefined {
|
||||
this._log("compile.configure", path);
|
||||
const { config, error } = this._ts.parseConfigFileTextToJson(
|
||||
path,
|
||||
configurationText
|
||||
);
|
||||
if (error) {
|
||||
this._logDiagnostics([error]);
|
||||
}
|
||||
const { options, errors } = this._ts.convertCompilerOptionsFromJson(
|
||||
config.compilerOptions,
|
||||
cwd()
|
||||
);
|
||||
if (errors.length) {
|
||||
this._logDiagnostics(errors);
|
||||
}
|
||||
const ignoredOptions: string[] = [];
|
||||
for (const key of Object.keys(options)) {
|
||||
if (
|
||||
ignoredCompilerOptions.includes(key) &&
|
||||
(!(key in this._options) || options[key] !== this._options[key])
|
||||
) {
|
||||
ignoredOptions.push(key);
|
||||
delete options[key];
|
||||
}
|
||||
}
|
||||
Object.assign(this._options, options);
|
||||
return ignoredOptions.length ? ignoredOptions : undefined;
|
||||
}
|
||||
|
||||
// TypeScript Language Service and Format Diagnostic Host API
|
||||
|
||||
getCanonicalFileName(fileName: string): string {
|
||||
|
@ -541,6 +649,46 @@ window.compilerMain = function compilerMain(): void {
|
|||
};
|
||||
};
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
// Perform the op to retrieve the compiler configuration if there was any
|
||||
// provided on startup.
|
||||
function getCompilerConfig(
|
||||
compilerType: string
|
||||
): { path: string; data: string } {
|
||||
const builder = flatbuffers.createBuilder();
|
||||
const compilerType_ = builder.createString(compilerType);
|
||||
msg.CompilerConfig.startCompilerConfig(builder);
|
||||
msg.CompilerConfig.addCompilerType(builder, compilerType_);
|
||||
const inner = msg.CompilerConfig.endCompilerConfig(builder);
|
||||
const baseRes = sendSync(builder, msg.Any.CompilerConfig, inner);
|
||||
assert(baseRes != null);
|
||||
assert(msg.Any.CompilerConfigRes === baseRes!.innerType());
|
||||
const res = new msg.CompilerConfigRes();
|
||||
assert(baseRes!.inner(res) != null);
|
||||
|
||||
// the privileged side does not normalize path separators in windows, so we
|
||||
// will normalize them here
|
||||
const path = res.path()!.replace(/\\/g, "/");
|
||||
assert(path != null);
|
||||
const dataArray = res.dataArray()!;
|
||||
assert(dataArray != null);
|
||||
const data = decoder.decode(dataArray);
|
||||
return { path, data };
|
||||
}
|
||||
|
||||
export default function denoMain(): void {
|
||||
os.start("TS");
|
||||
|
||||
const { path, data } = getCompilerConfig("typescript");
|
||||
if (data.length) {
|
||||
const ignoredOptions = compiler.configure(path, data);
|
||||
if (ignoredOptions) {
|
||||
console.warn(
|
||||
yellow(`Unsupported compiler options in "${path}"\n`) +
|
||||
cyan(` The following options were ignored:\n`) +
|
||||
` ${ignoredOptions.map((value): string => bold(value)).join(", ")}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -229,9 +229,11 @@ export default function makeConfig(commandOptions) {
|
|||
// rollup requires them to be explicitly defined to make them available in the
|
||||
// bundle
|
||||
[typescriptPath]: [
|
||||
"convertCompilerOptionsFromJson",
|
||||
"createLanguageService",
|
||||
"formatDiagnostics",
|
||||
"formatDiagnosticsWithColorAndContext",
|
||||
"parseConfigFileTextToJson",
|
||||
"version",
|
||||
"Extension",
|
||||
"ModuleKind",
|
||||
|
|
4
tests/config.test
Normal file
4
tests/config.test
Normal file
|
@ -0,0 +1,4 @@
|
|||
args: --reload --config tests/config.tsconfig.json tests/config.ts
|
||||
check_stderr: true
|
||||
exit_code: 1
|
||||
output: tests/config.ts.out
|
5
tests/config.ts
Normal file
5
tests/config.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
const map = new Map<string, { foo: string }>();
|
||||
|
||||
if (map.get("bar").foo) {
|
||||
console.log("here");
|
||||
}
|
9
tests/config.ts.out
Normal file
9
tests/config.ts.out
Normal file
|
@ -0,0 +1,9 @@
|
|||
Unsupported compiler options in "[WILDCARD]tests/config.tsconfig.json"
|
||||
The following options were ignored:
|
||||
module, target
|
||||
Compiling file://[WILDCARD]tests/config.ts
|
||||
[WILDCARD]tests/config.ts:3:5 - error TS2532: Object is possibly 'undefined'.
|
||||
|
||||
3 if (map.get("bar").foo) {
|
||||
~~~~~~~~~~~~~~
|
||||
|
7
tests/config.tsconfig.json
Normal file
7
tests/config.tsconfig.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "amd",
|
||||
"strict": true,
|
||||
"target": "es5"
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue