Remove support for extensionless import (#1396)

This commit is contained in:
Ryan Dahl 2018-12-23 11:44:08 -05:00 committed by GitHub
parent bee55fcd20
commit 6cc998f28b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 196 additions and 241 deletions

View file

@ -1,5 +1,5 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
import { test, assert, assertEqual } from "./test_util";
import { test, assert, assertEqual } from "./test_util.ts";
import * as deno from "deno";
import * as ts from "typescript";

View file

@ -322,7 +322,7 @@ function stringifyWithQuotes(
}
}
// @internal
/** TODO Do not expose this from "deno" namespace. */
export function stringifyArgs(
// tslint:disable-next-line:no-any
args: any[],
@ -354,8 +354,8 @@ type PrintFunc = (x: string, isErr?: boolean) => void;
const countMap = new Map<string, number>();
const timerMap = new Map<string, number>();
/** TODO Do not expose this from "deno". */
export class Console {
// @internal
constructor(private printFunc: PrintFunc) {}
/** Writes the arguments to stdout */

View file

@ -1,9 +1,7 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
import { Console, libdeno, stringifyArgs, inspect } from "deno";
import { test, assert, assertEqual } from "./test_util.ts";
import { stringifyArgs, inspect } from "./console.ts";
import { Console } from "./console.ts";
import { libdeno } from "./libdeno";
const console = new Console(libdeno.print);
// tslint:disable-next-line:no-any

View file

@ -55,6 +55,13 @@ export { run, RunOptions, Process, ProcessStatus } from "./process";
export { inspect } from "./console";
export const args: string[] = [];
// TODO Don't expose Console nor stringifyArgs.
export { Console, stringifyArgs } from "./console";
// TODO Don't expose DomIterableMixin.
export { DomIterableMixin } from "./mixins/dom_iterable";
// TODO Don't expose deferred.
export { deferred } from "./util";
// Provide the compiler API in an obfuscated way
import * as compiler from "./compiler";
// @internal

View file

@ -36,7 +36,7 @@ type ReferrerPolicy =
| "origin-when-cross-origin"
| "unsafe-url";
export type BlobPart = BufferSource | Blob | string;
export type FormDataEntryValue = File | string;
export type FormDataEntryValue = DomFile | string;
export type EventListenerOrEventListenerObject =
| EventListener
| EventListenerObject;
@ -175,7 +175,10 @@ interface Event {
readonly NONE: number;
}
export interface File extends Blob {
/* TODO(ry) Re-expose this interface. There is currently some interference
* between deno's File and this one.
*/
export interface DomFile extends Blob {
readonly lastModified: number;
readonly name: string;
}

View file

@ -66,8 +66,10 @@ testPerm({ net: true }, async function fetchMultipartFormDataSuccess() {
assert(formData.has("field_1"));
assertEqual(formData.get("field_1").toString(), "value_1 \r\n");
assert(formData.has("field_2"));
/* TODO(ry) Re-enable this test once we bring back the global File type.
const file = formData.get("field_2") as File;
assertEqual(file.name, "file.js");
*/
// Currently we cannot read from file...
});

View file

@ -1,8 +1,12 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
// TODO Rename this file to js/dom_file.ts it's currently too similarly named to
// js/files.ts
import * as domTypes from "./dom_types";
import * as blob from "./blob";
export class DenoFile extends blob.DenoBlob implements domTypes.File {
// TODO Rename this to DomFileImpl
export class DenoFile extends blob.DenoBlob implements domTypes.DomFile {
lastModified: number;
name: string;

View file

@ -69,10 +69,12 @@ test(function formDataSetEmptyBlobSuccess() {
const formData = new FormData();
formData.set("a", new Blob([]), "blank.txt");
const file = formData.get("a");
/* TODO Fix this test.
assert(file instanceof File);
if (typeof file !== "string") {
assertEqual(file.name, "blank.txt");
}
*/
});
test(function formDataParamsForEachSuccess() {

View file

@ -10,7 +10,6 @@
import * as blob from "./blob";
import * as consoleTypes from "./console";
import * as domTypes from "./dom_types";
import * as file from "./file";
import * as formData from "./form_data";
import * as fetchTypes from "./fetch";
import * as headers from "./headers";
@ -55,8 +54,13 @@ window.setInterval = timers.setInterval;
// being used, which it cannot statically determine within this module.
window.Blob = blob.DenoBlob;
export type Blob = blob.DenoBlob;
window.File = file.DenoFile;
export type File = file.DenoFile;
// TODO(ry) Do not export a class implementing the DOM, export the DOM
// interface. See this comment for implementation hint:
// https://github.com/denoland/deno/pull/1396#discussion_r243711502
// window.File = file.DenoFile;
// export type File = file.DenoFile;
window.URL = url.URL;
export type URL = url.URL;
window.URLSearchParams = urlSearchParams.URLSearchParams;

View file

@ -41,5 +41,4 @@ interface Libdeno {
}
const window = globalEval("this");
// @internal
export const libdeno = window.libdeno as Libdeno;

View file

@ -11,6 +11,7 @@ type Constructor<T = {}> = new (...args: any[]) => T;
/** Mixes in a DOM iterable methods into a base class, assumes that there is
* a private data iterable that is part of the base class, located at
* `[dataSymbol]`.
* TODO Don't expose DomIterableMixin from "deno" namespace.
*/
export function DomIterableMixin<K, V, TBase extends Constructor>(
// tslint:disable-next-line:variable-name

View file

@ -1,6 +1,6 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
import { test, assert, assertEqual } from "../test_util.ts";
import { DomIterableMixin } from "./dom_iterable.ts";
import { DomIterableMixin } from "deno";
function setup() {
const dataSymbol = Symbol("data symbol");

View file

@ -1,7 +1,7 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
import * as deno from "deno";
import { testPerm, assert, assertEqual } from "./test_util.ts";
import { deferred } from "./util.ts";
import { deferred } from "deno";
testPerm({ net: true }, function netListenClose() {
const listener = deno.listen("tcp", "127.0.0.1:4500");

View file

@ -1,4 +1,4 @@
import { test, assert, assertEqual } from "./test_util";
import { test, assert, assertEqual } from "./test_util.ts";
import * as deno from "deno";
// tslint:disable-next-line:no-any

View file

@ -13,7 +13,67 @@
limitations under the License.
*/
export { assert, assertEqual, equal } from "./util";
// Do not add imports in this file in order to be compatible with Node.
export function assertEqual(actual: unknown, expected: unknown, msg?: string) {
if (!equal(actual, expected)) {
let actualString: string;
let expectedString: string;
try {
actualString = String(actual);
} catch (e) {
actualString = "[Cannot display]";
}
try {
expectedString = String(expected);
} catch (e) {
expectedString = "[Cannot display]";
}
console.error(
"assertEqual failed. actual =",
actualString,
"expected =",
expectedString
);
if (!msg) {
msg = `actual: ${actualString} expected: ${expectedString}`;
}
throw new Error(msg);
}
}
export function assert(expr: boolean, msg = "") {
if (!expr) {
throw new Error(msg);
}
}
// TODO(ry) Use unknown here for parameters types.
// tslint:disable-next-line:no-any
export function equal(c: any, d: any): boolean {
const seen = new Map();
return (function compare(a, b) {
if (Object.is(a, b)) {
return true;
}
if (a && typeof a === "object" && b && typeof b === "object") {
if (seen.get(a) === b) {
return true;
}
if (Object.keys(a).length !== Object.keys(b).length) {
return false;
}
for (const key in { ...a, ...b }) {
if (!compare(a[key], b[key])) {
return false;
}
}
seen.set(a, b);
return true;
}
return false;
})(c, d);
}
export type TestFunction = () => void | Promise<void>;

View file

@ -13,43 +13,41 @@
limitations under the License.
*/
import { test } from "./testing.ts";
import { assert } from "./util.ts";
import * as util from "./util.ts";
import { test, assert, assertEqual, equal } from "./testing.ts";
test(function util_equal() {
assert(util.equal("world", "world"));
assert(!util.equal("hello", "world"));
assert(util.equal(5, 5));
assert(!util.equal(5, 6));
assert(util.equal(NaN, NaN));
assert(util.equal({ hello: "world" }, { hello: "world" }));
assert(!util.equal({ world: "hello" }, { hello: "world" }));
test(function testingEqual() {
assert(equal("world", "world"));
assert(!equal("hello", "world"));
assert(equal(5, 5));
assert(!equal(5, 6));
assert(equal(NaN, NaN));
assert(equal({ hello: "world" }, { hello: "world" }));
assert(!equal({ world: "hello" }, { hello: "world" }));
assert(
util.equal(
equal(
{ hello: "world", hi: { there: "everyone" } },
{ hello: "world", hi: { there: "everyone" } }
)
);
assert(
!util.equal(
!equal(
{ hello: "world", hi: { there: "everyone" } },
{ hello: "world", hi: { there: "everyone else" } }
)
);
});
test(function util_assertEqual() {
test(function testingAssertEqual() {
const a = Object.create(null);
a.b = "foo";
util.assertEqual(a, a);
assertEqual(a, a);
});
test(function util_assertEqualActualUncoercable() {
test(function testingAssertEqualActualUncoercable() {
let didThrow = false;
const a = Object.create(null);
try {
util.assertEqual(a, "bar");
assertEqual(a, "bar");
} catch (e) {
didThrow = true;
console.log(e.message);
@ -58,11 +56,11 @@ test(function util_assertEqualActualUncoercable() {
assert(didThrow);
});
test(function util_assertEqualExpectedUncoercable() {
test(function testingAssertEqualExpectedUncoercable() {
let didThrow = false;
const a = Object.create(null);
try {
util.assertEqual("bar", a);
assertEqual("bar", a);
} catch (e) {
didThrow = true;
console.log(e.message);

View file

@ -1,74 +0,0 @@
/*!
Copyright 2018 Propel http://propel.site/. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
export function assertEqual(actual: unknown, expected: unknown, msg?: string) {
if (!equal(actual, expected)) {
let actualString: string;
let expectedString: string;
try {
actualString = String(actual);
} catch (e) {
actualString = "[Cannot display]";
}
try {
expectedString = String(expected);
} catch (e) {
expectedString = "[Cannot display]";
}
console.error(
"assertEqual failed. actual =",
actualString,
"expected =",
expectedString
);
if (!msg) {
msg = `actual: ${actualString} expected: ${expectedString}`;
}
throw new Error(msg);
}
}
export function assert(expr: boolean, msg = "") {
if (!expr) {
throw new Error(msg);
}
}
// TODO(ry) Use unknown here for parameters types.
// tslint:disable-next-line:no-any
export function equal(c: any, d: any): boolean {
const seen = new Map();
return (function compare(a, b) {
if (Object.is(a, b)) {
return true;
}
if (a && typeof a === "object" && b && typeof b === "object") {
if (seen.get(a) === b) {
return true;
}
if (Object.keys(a).length !== Object.keys(b).length) {
return false;
}
for (const key in { ...a, ...b }) {
if (!compare(a[key], b[key])) {
return false;
}
}
seen.set(a, b);
return true;
}
return false;
})(c, d);
}

View file

@ -2,16 +2,18 @@
// This test is executed as part of tools/test.py
// But it can also be run manually: ./target/debug/deno js/unit_tests.ts
import "../website/app_test.js";
import "./blob_test.ts";
import "./buffer_test.ts";
import "./chmod_test.ts";
import "./compiler_test.ts";
import "./console_test.ts";
import "./copy_file_test.ts";
import "./dir_test";
import "./dir_test.ts";
import "./fetch_test.ts";
import "./file_test.ts";
// TODO The global "File" has been temporarily disabled. See the note in
// js/globals.ts
// import "./file_test.ts";
import "./files_test.ts";
import "./form_data_test.ts";
import "./headers_test.ts";
@ -37,3 +39,5 @@ import "./truncate_test.ts";
import "./url_test.ts";
import "./url_search_params_test.ts";
import "./write_file_test.ts";
// TODO import "../website/app_test.js";

View file

@ -103,15 +103,15 @@ export function containsOnlyASCII(str: string): boolean {
return /^[\x00-\x7F]*$/.test(str);
}
// @internal
export interface Deferred {
promise: Promise<void>;
resolve: Function;
reject: Function;
}
/** Create a wrapper around a promise that could be resolved externally. */
// @internal
/** Create a wrapper around a promise that could be resolved externally.
* TODO Do not expose this from "deno" namespace.
*/
export function deferred(): Deferred {
let resolve: Function | undefined;
let reject: Function | undefined;

View file

@ -142,36 +142,31 @@ impl DenoDir {
module_name: &str,
filename: &str,
) -> DenoResult<Option<CodeFetchOutput>> {
let extensions = ["", ".ts", ".js"];
for ext in extensions.iter() {
let filename = [filename, ext].concat();
let module_name = [module_name, ext].concat();
let p = Path::new(&filename);
// We write a special ".mime" file into the `.deno/deps` directory along side the
// cached file, containing just the media type.
let media_type_filename = [&filename, ".mime"].concat();
let mt = Path::new(&media_type_filename);
eprint!("Downloading {}...", &module_name); // no newline
let maybe_source = http_util::fetch_sync_string(&module_name);
if let Ok((source, content_type)) = maybe_source {
eprintln!(""); // next line
match p.parent() {
Some(ref parent) => fs::create_dir_all(parent),
None => Ok(()),
}?;
deno_fs::write_file(&p, &source, 0o666)?;
deno_fs::write_file(&mt, content_type.as_bytes(), 0o666)?;
return Ok(Some(CodeFetchOutput {
module_name,
filename: filename.clone(), // TODO: no clone after NLL rfc
media_type: map_content_type(&p, Some(&content_type)),
source_code: source,
maybe_output_code: None,
maybe_source_map: None,
}));
} else {
eprintln!(" NOT FOUND");
}
let p = Path::new(&filename);
// We write a special ".mime" file into the `.deno/deps` directory along side the
// cached file, containing just the media type.
let media_type_filename = [&filename, ".mime"].concat();
let mt = Path::new(&media_type_filename);
eprint!("Downloading {}...", &module_name); // no newline
let maybe_source = http_util::fetch_sync_string(&module_name);
if let Ok((source, content_type)) = maybe_source {
eprintln!(""); // next line
match p.parent() {
Some(ref parent) => fs::create_dir_all(parent),
None => Ok(()),
}?;
deno_fs::write_file(&p, &source, 0o666)?;
deno_fs::write_file(&mt, content_type.as_bytes(), 0o666)?;
return Ok(Some(CodeFetchOutput {
module_name: module_name.to_string(),
filename: filename.to_string(),
media_type: map_content_type(&p, Some(&content_type)),
source_code: source,
maybe_output_code: None,
maybe_source_map: None,
}));
} else {
eprintln!(" NOT FOUND");
}
Ok(None)
}
@ -182,33 +177,33 @@ impl DenoDir {
module_name: &str,
filename: &str,
) -> DenoResult<Option<CodeFetchOutput>> {
let extensions = ["", ".ts", ".js"];
for ext in extensions.iter() {
let filename = [filename, ext].concat();
let module_name = [module_name, ext].concat();
let p = Path::new(&filename);
if !p.exists() {
continue;
let p = Path::new(&filename);
let media_type_filename = [&filename, ".mime"].concat();
let mt = Path::new(&media_type_filename);
let source_code = match fs::read(p) {
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
return Ok(None);
} else {
return Err(e.into());
}
}
let media_type_filename = [&filename, ".mime"].concat();
let mt = Path::new(&media_type_filename);
let source_code = fs::read(&p)?;
// .mime file might not exists
// this is okay for local source: maybe_content_type_str will be None
let maybe_content_type_string = fs::read_to_string(&mt).ok();
// Option<String> -> Option<&str>
let maybe_content_type_str =
maybe_content_type_string.as_ref().map(String::as_str);
return Ok(Some(CodeFetchOutput {
module_name,
filename: filename.clone(), // TODO: no clone after NLL rfc
media_type: map_content_type(&p, maybe_content_type_str),
source_code,
maybe_output_code: None,
maybe_source_map: None,
}));
}
Ok(None) // cannot find locally
Ok(c) => c,
};
// .mime file might not exists
// this is okay for local source: maybe_content_type_str will be None
let maybe_content_type_string = fs::read_to_string(&mt).ok();
// Option<String> -> Option<&str>
let maybe_content_type_str =
maybe_content_type_string.as_ref().map(String::as_str);
Ok(Some(CodeFetchOutput {
module_name: module_name.to_string(),
filename: filename.to_string(),
media_type: map_content_type(&p, maybe_content_type_str),
source_code,
maybe_output_code: None,
maybe_source_map: None,
}))
}
// Prototype: https://github.com/denoland/deno/blob/golang/os.go#L122-L138
@ -222,19 +217,32 @@ impl DenoDir {
// 1. This is a remote module, but no reload provided
// 2. This is a local module
if !is_module_remote || !self.reload {
let maybe_local_source =
self.fetch_local_source(&module_name, &filename)?;
if let Some(output) = maybe_local_source {
return Ok(output);
debug!(
"fetch local or reload {} is_module_remote {}",
module_name, is_module_remote
);
match self.fetch_local_source(&module_name, &filename)? {
Some(output) => {
debug!("found local source ");
return Ok(output);
}
None => {
debug!("fetch_local_source returned None");
}
}
}
// If not remote file, stop here!
if !is_module_remote {
debug!("not remote file stop here");
return Err(DenoError::from(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("cannot find local file '{}'", filename),
)));
}
debug!("is remote but didn't find module");
// not cached/local, try remote
let maybe_remote_source =
self.fetch_remote_source(&module_name, &filename)?;
@ -514,6 +522,7 @@ fn filter_shebang(code: Vec<u8>) -> Vec<u8> {
mod tests {
use super::*;
use tempfile::TempDir;
use tokio_util;
fn test_setup() -> (TempDir, DenoDir) {
let temp_dir = TempDir::new().expect("tempdir fail");
@ -606,10 +615,9 @@ mod tests {
#[test]
fn test_get_source_code() {
use tokio_util;
let (temp_dir, deno_dir) = test_setup();
// http_util::fetch_sync_string requires tokio
tokio_util::init(|| {
let (temp_dir, deno_dir) = test_setup();
let module_name = "http://localhost:4545/tests/subdir/mod2.ts";
let filename = deno_fs::normalize_path(
deno_dir
@ -620,6 +628,7 @@ mod tests {
let mime_file_name = format!("{}.mime", &filename);
let result = deno_dir.get_source_code(module_name, &filename);
println!("module_name {} filename {}", module_name, filename);
assert!(result.is_ok());
let r = result.unwrap();
let expected: &[u8] =
@ -739,48 +748,6 @@ mod tests {
//println!("code_fetch_output {:?}", code_fetch_output);
}
#[test]
fn test_code_fetch_no_ext() {
let (_temp_dir, deno_dir) = test_setup();
let cwd = std::env::current_dir().unwrap();
let cwd_string = String::from(cwd.to_str().unwrap()) + "/";
// Assuming cwd is the deno repo root.
let specifier = "./js/main";
let referrer = cwd_string.as_str();
let r = deno_dir.code_fetch(specifier, referrer);
assert!(r.is_ok());
// Test .ts extension
// Assuming cwd is the deno repo root.
let specifier = "./js/main";
let referrer = cwd_string.as_str();
let r = deno_dir.code_fetch(specifier, referrer);
assert!(r.is_ok());
let code_fetch_output = r.unwrap();
// could only test .ends_with to avoid include local abs path
assert!(code_fetch_output.module_name.ends_with("/js/main.ts"));
assert!(code_fetch_output.filename.ends_with("/js/main.ts"));
assert!(code_fetch_output.source_code.len() > 10);
// Test .js extension
// Assuming cwd is the deno repo root.
let specifier = "./js/mock_builtin";
let referrer = cwd_string.as_str();
let r = deno_dir.code_fetch(specifier, referrer);
assert!(r.is_ok());
let code_fetch_output = r.unwrap();
// could only test .ends_with to avoid include local abs path
assert!(
code_fetch_output
.module_name
.ends_with("/js/mock_builtin.js")
);
assert!(code_fetch_output.filename.ends_with("/js/mock_builtin.js"));
assert!(code_fetch_output.source_code.len() > 10);
}
#[test]
fn test_src_file_to_url_1() {
let (_temp_dir, deno_dir) = test_setup();

View file

@ -1,2 +0,0 @@
args: tests/015_import_no_ext.ts --reload
output: tests/015_import_no_ext.ts.out

View file

@ -1,10 +0,0 @@
import { isTSFile, printHello, phNoExt } from "./subdir/mod3";
console.log(isTSFile);
console.log(printHello);
console.log(phNoExt);
import { isMod4 } from "./subdir/mod4";
console.log(isMod4);
import { printHello as ph } from "http://localhost:4545/tests/subdir/mod2";
console.log(ph);

View file

@ -1,5 +0,0 @@
true
[Function: printHello]
[Function: printHello]
true
[Function: printHello]

View file

@ -1,5 +1,5 @@
// Run ./tools/http_server.py too in order for this test to run.
import { assert } from "../js/testing/util.ts";
import { assert } from "../js/testing/testing.ts";
// TODO Top level await https://github.com/denoland/deno/issues/471
async function main() {

View file

@ -1,3 +0,0 @@
export const isTSFile = true;
export { printHello } from "./print_hello.ts";
export { printHello as phNoExt } from "./print_hello";