Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] TS Compiler Options #1118

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 53 additions & 6 deletions js/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,37 @@ function getExtension(
}
}

export interface DenoCompilerOptions {
/** Do not allow JavaScript modules to be imported into the programme. */
disallowJs?: boolean;
/** Do not type check JavaScript files.
*
* Requires `disallowJs` to be false.
*/
noCheckJs?: boolean;
/** Do not allow JSON files to be imported as modules. */
noResolveJsonModule?: boolean;
/** Code will be evaluated in TypeScript's strict mode. */
strict?: boolean;
}

/** A static map of options supported by the Deno compiler. */
const compilerOptionMap = new Map<
keyof DenoCompilerOptions,
{
tsCompilerOption: keyof ts.CompilerOptions;
reverse: boolean;
}
>([
["disallowJs", { tsCompilerOption: "allowJs", reverse: true }],
["noCheckJs", { tsCompilerOption: "checkJs", reverse: true }],
[
"noResolveJsonModule",
{ tsCompilerOption: "resolveJsonModule", reverse: true }
],
["strict", { tsCompilerOption: "strict", reverse: false }]
]);

/** A singleton class that combines the TypeScript Language Service host API
* with Deno specific APIs to provide an interface for compiling and running
* TypeScript and JavaScript modules.
Expand All @@ -153,7 +184,7 @@ export class DenoCompiler
>();
// TODO ideally this are not static and can be influenced by command line
// arguments
private readonly _options: Readonly<ts.CompilerOptions> = {
private readonly _options: ts.CompilerOptions = {
allowJs: true,
checkJs: true,
module: ts.ModuleKind.AMD,
Expand Down Expand Up @@ -358,6 +389,20 @@ export class DenoCompiler
innerMap.set(moduleSpecifier, fileName);
}

/** Update the options for the TypeScript compiler. */
private _setOptions(options: DenoCompilerOptions) {
for (const [option, value] of Object.entries(options) as Array<
[keyof DenoCompilerOptions, boolean]
>) {
this._log("compiler._setOptions", options);
const optionInfo = compilerOptionMap.get(option);
if (optionInfo) {
const { tsCompilerOption, reverse } = optionInfo;
this._options[tsCompilerOption] = reverse ? !value : value;
}
}
}

/** Setup being able to map back source references back to their source
*
* TODO is this the best place for this? It is tightly coupled to how the
Expand Down Expand Up @@ -596,7 +641,6 @@ export class DenoCompiler
}

getCompilationSettings(): ts.CompilerOptions {
this._log("getCompilationSettings()");
return this._options;
}

Expand Down Expand Up @@ -720,9 +764,12 @@ export class DenoCompiler
private static _instance: DenoCompiler | undefined;

/** Returns the instance of `DenoCompiler` or creates a new instance. */
static instance(): DenoCompiler {
return (
DenoCompiler._instance || (DenoCompiler._instance = new DenoCompiler())
);
static instance(options?: DenoCompilerOptions): DenoCompiler {
const compiler =
DenoCompiler._instance || (DenoCompiler._instance = new DenoCompiler());
if (options) {
compiler._setOptions(options);
}
return compiler;
}
}
5 changes: 4 additions & 1 deletion js/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export default function denoMain() {
libdeno.setGlobalErrorHandler(onGlobalError);
libdeno.setPromiseRejectHandler(promiseRejectHandler);
libdeno.setPromiseErrorExaminer(promiseErrorExaminer);
const compiler = DenoCompiler.instance();

// First we send an empty "Start" message to let the privileged side know we
// are ready. The response should be a "StartRes" message containing the CLI
Expand All @@ -52,6 +51,10 @@ export default function denoMain() {

setLogDebug(startResMsg.debugFlag());

const compiler = DenoCompiler.instance({
strict: startResMsg.strictFlag()
});

// handle `--types`
if (startResMsg.typesFlag()) {
const defaultLibFileName = compiler.getDefaultLibFileName();
Expand Down
57 changes: 39 additions & 18 deletions src/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@ macro_rules! svec {

#[derive(Debug, PartialEq, Default)]
pub struct DenoFlags {
pub allow_env: bool,
pub allow_net: bool,
pub allow_write: bool,
pub help: bool,
pub log_debug: bool,
pub version: bool,
pub reload: bool,
pub recompile: bool,
pub allow_write: bool,
pub allow_net: bool,
pub allow_env: bool,
pub reload: bool,
pub strict: bool,
pub types_flag: bool,
pub version: bool,
}

pub fn process(flags: &DenoFlags, usage_string: String) {
Expand Down Expand Up @@ -70,6 +71,7 @@ pub fn set_flags(
opts.optflag("r", "reload", "Reload cached remote resources.");
opts.optflag("", "v8-options", "Print V8 command line options.");
opts.optflag("", "types", "Print runtime TypeScript declarations.");
opts.optflag("s", "strict", "Evaluate TypeScript in strict mode.");

let mut flags = DenoFlags::default();

Expand All @@ -80,33 +82,39 @@ pub fn set_flags(
}
};

if matches.opt_present("allow-env") {
flags.allow_env = true;
}
if matches.opt_present("allow-net") {
flags.allow_net = true;
}
if matches.opt_present("allow-write") {
flags.allow_write = true;
}
if matches.opt_present("help") {
flags.help = true;
}
if matches.opt_present("log-debug") {
flags.log_debug = true;
}
if matches.opt_present("version") {
flags.version = true;
}
if matches.opt_present("reload") {
flags.reload = true;
}
if matches.opt_present("recompile") {
flags.recompile = true;
}
if matches.opt_present("allow-write") {
flags.allow_write = true;
}
if matches.opt_present("allow-net") {
flags.allow_net = true;
if matches.opt_present("reload") {
flags.reload = true;
}
if matches.opt_present("allow-env") {
flags.allow_env = true;
if matches.opt_present("strict") {
flags.strict = true;
}
if matches.opt_present("types") {
flags.types_flag = true;
}
if matches.opt_present("strict") {
flags.strict = true;
}
if matches.opt_present("version") {
flags.version = true;
}

let rest: Vec<_> = matches.free.iter().map(|s| s.clone()).collect();
return Ok((flags, rest, get_usage(&opts)));
Expand Down Expand Up @@ -184,6 +192,19 @@ fn test_set_flags_5() {
)
}

#[test]
fn test_set_flags_6() {
let (flags, rest, _) = set_flags(svec!["deno", "--strict"]).unwrap();
assert_eq!(rest, svec!["deno"]);
assert_eq!(
flags,
DenoFlags {
strict: true,
..DenoFlags::default()
}
)
}

#[test]
fn test_set_bad_flags_1() {
let err = set_flags(svec!["deno", "--unknown-flag"]).unwrap_err();
Expand Down
7 changes: 4 additions & 3 deletions src/msg.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,16 @@ table Start {
}

table StartRes {
cwd: string;
argv: [string];
cwd: string;
debug_flag: bool;
deno_version: string;
deps_flag: bool;
recompile_flag: bool;
strict_flag: bool;
types_flag: bool;
version_flag: bool;
deno_version: string;
v8_version: string;
version_flag: bool;
}

table CodeFetch {
Expand Down
7 changes: 4 additions & 3 deletions src/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,14 +187,15 @@ fn op_start(
let inner = msg::StartRes::create(
&mut builder,
&msg::StartResArgs {
cwd: Some(cwd_off),
argv: Some(argv_off),
cwd: Some(cwd_off),
debug_flag: state.flags.log_debug,
deno_version: Some(deno_version_off),
recompile_flag: state.flags.recompile,
strict_flag: state.flags.strict,
types_flag: state.flags.types_flag,
version_flag: state.flags.version,
v8_version: Some(v8_version_off),
deno_version: Some(deno_version_off),
version_flag: state.flags.version,
..Default::default()
},
);
Expand Down
19 changes: 19 additions & 0 deletions tests/flags/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Flag Tests

Tests located in sub-directories of this one will be executed by passing the
specified flags to Deno on the command line. Multiple flags are denoted by an
`_` in the directory name.

For example if a test was named `tests/flags/foo/test.ts` with a corresponding
`tests/flags/foo/test.ts.out` the command line to Deno would be:

```
$ deno --foo tests/flags/foo/test.ts
```

If the test was named `tests/flags/foo_bar/test.ts` with a corresponding
`tests/flags/foo_bar/test.ts.out` the command line to Deno would be:

```
$ deno --foo --bar tests/flags/foo_bar/test.ts
```
5 changes: 5 additions & 0 deletions tests/flags/strict/error_maybe_undefined.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const map = new Map<string, { bar: string }>();

if (map.get("foo").bar) {
console.log("maybe undefined");
}
5 changes: 5 additions & 0 deletions tests/flags/strict/error_maybe_undefined.ts.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[WILDCARD]/tests/flags/strict/error_maybe_undefined.ts:3:5 - error TS2532: Object is possibly 'undefined'.

3 if (map.get("foo").bar) {
   ~~~~~~~~~~~~~~

72 changes: 72 additions & 0 deletions tools/flag_output_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env python
# Copyright 2018 the Deno authors. All rights reserved. MIT license.
# Given a deno executable, this script executes several integration tests also
# passing command line flags to the deno executable based on the path of the
# test case.
#
# Usage: flag_output_tests.py [path to deno executable]
import os
import sys
import subprocess
from util import pattern_match, parse_exit_code

root_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
flags_path = os.path.join(root_path, "tests", "flags")


def flag_output_tests(deno_executable):
assert os.path.isfile(deno_executable)
switch_dirs = sorted([
filename for filename in os.listdir(flags_path)
if os.path.isdir(os.path.join(flags_path, filename))
])
for switch_dir in switch_dirs:
tests_path = os.path.join(flags_path, switch_dir)
outs = sorted([
filename for filename in os.listdir(tests_path)
if filename.endswith(".out")
])
assert len(outs) > 0
tests = [(os.path.splitext(filename)[0], filename)
for filename in outs]
for (script, out_filename) in tests:
script_abs = os.path.join(tests_path, script)
out_abs = os.path.join(tests_path, out_filename)
with open(out_abs, 'r') as f:
expected_out = f.read()
flags = ["--" + flag for flag in switch_dir.split("_")]
cmd = [deno_executable, script_abs, "--reload"] + flags
expected_code = parse_exit_code(script)
print " ".join(cmd)
actual_code = 0
try:
actual_out = subprocess.check_output(
cmd, universal_newlines=True)
except subprocess.CalledProcessError as e:
actual_code = e.returncode
actual_out = e.output
if expected_code == 0:
print "Expected success but got error. Output:"
print actual_out
sys.exit(1)

if expected_code != actual_code:
print "Expected exit code %d but got %d" % (expected_code,
actual_code)
print "Output:"
print actual_out
sys.exit(1)

if pattern_match(expected_out, actual_out) != True:
print "Expected output does not match actual."
print "Expected: " + expected_out
print "Actual: " + actual_out
sys.exit(1)


def main(argv):
flag_output_tests(argv[1])


if __name__ == '__main__':
sys.exit(main(sys.argv))
3 changes: 3 additions & 0 deletions tools/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import os
import sys
from check_output_test import check_output_test
from flag_output_tests import flag_output_tests
from deno_dir_test import deno_dir_test
from setup_test import setup_test
from util import build_path, enable_ansi_colors, executable_suffix, run, rmtree
Expand Down Expand Up @@ -60,6 +61,8 @@ def main(argv):

check_output_test(deno_exe)

flag_output_tests(deno_exe)

# TODO We currently skip testing the prompt in Windows completely.
# Windows does not support the pty module used for testing the permission
# prompt.
Expand Down