Add window.location

This commit is contained in:
Ryan Dahl 2019-02-12 21:14:02 -05:00
parent 27afbd1351
commit 42408febe8
26 changed files with 241 additions and 41 deletions

View file

@ -80,6 +80,7 @@ ts_sources = [
"js/io.ts",
"js/libdeno.ts",
"js/lib.web_assembly.d.ts",
"js/location.ts",
"js/main.ts",
"js/make_temp_dir.ts",
"js/metrics.ts",

View file

@ -536,3 +536,73 @@ export interface Response extends Body {
/** Creates a clone of a `Response` object. */
clone(): Response;
}
export interface Location {
/**
* Returns a DOMStringList object listing the origins of the ancestor browsing
* contexts, from the parent browsing context to the top-level browsing
* context.
*/
readonly ancestorOrigins: string[];
/**
* Returns the Location object's URL's fragment (includes leading "#" if
* non-empty).
* Can be set, to navigate to the same URL with a changed fragment (ignores
* leading "#").
*/
hash: string;
/**
* Returns the Location object's URL's host and port (if different from the
* default port for the scheme). Can be set, to navigate to the same URL with
* a changed host and port.
*/
host: string;
/**
* Returns the Location object's URL's host. Can be set, to navigate to the
* same URL with a changed host.
*/
hostname: string;
/**
* Returns the Location object's URL. Can be set, to navigate to the given
* URL.
*/
href: string;
/** Returns the Location object's URL's origin. */
readonly origin: string;
/**
* Returns the Location object's URL's path.
* Can be set, to navigate to the same URL with a changed path.
*/
pathname: string;
/**
* Returns the Location object's URL's port.
* Can be set, to navigate to the same URL with a changed port.
*/
port: string;
/**
* Returns the Location object's URL's scheme.
* Can be set, to navigate to the same URL with a changed scheme.
*/
protocol: string;
/**
* Returns the Location object's URL's query (includes leading "?" if
* non-empty). Can be set, to navigate to the same URL with a changed query
* (ignores leading "?").
*/
search: string;
/**
* Navigates to the given URL.
*/
assign(url: string): void;
/**
* Reloads the current page.
*/
reload(): void;
/** @deprecated */
reload(forcedReload: boolean): void;
/**
* Removes the current page from the session history and navigates to the
* given URL.
*/
replace(url: string): void;
}

View file

@ -58,6 +58,8 @@ window.clearInterval = timers.clearTimer;
window.console = new consoleTypes.Console(libdeno.print);
window.setTimeout = timers.setTimeout;
window.setInterval = timers.setInterval;
// tslint:disable-next-line:no-any
window.location = (undefined as unknown) as domTypes.Location;
// When creating the runtime type library, we use modifications to `window` to
// determine what is in the global namespace. When we put a class in the

View file

@ -161,3 +161,8 @@ declare namespace WebAssembly {
constructor(message: string, fileName?: string, lineNumber?: string);
}
}
// TODO Move ImportMeta intos its own lib.import_meta.d.ts file?
interface ImportMeta {
url: string;
}

52
js/location.ts Normal file
View file

@ -0,0 +1,52 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { URL } from "./url";
import { notImplemented } from "./util";
import { Location } from "./dom_types";
import { window } from "./globals";
export function setLocation(url: string): void {
window.location = new LocationImpl(url);
Object.freeze(window.location);
}
export class LocationImpl implements Location {
constructor(url: string) {
const u = new URL(url);
this.url = u;
this.hash = u.hash;
this.host = u.host;
this.href = u.href;
this.hostname = u.hostname;
this.origin = u.protocol + "//" + u.host;
this.pathname = u.pathname;
this.protocol = u.protocol;
this.port = u.port;
this.search = u.search;
}
private url: URL;
toString(): string {
return this.url.toString();
}
readonly ancestorOrigins: string[] = [];
hash: string;
host: string;
hostname: string;
href: string;
readonly origin: string;
pathname: string;
port: string;
protocol: string;
search: string;
assign(url: string): void {
throw notImplemented();
}
reload(): void {
throw notImplemented();
}
replace(url: string): void {
throw notImplemented();
}
}

8
js/location_test.ts Normal file
View file

@ -0,0 +1,8 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { test, assert } from "./test_util.ts";
test(function locationBasic() {
// location example: file:///Users/rld/src/deno/js/unit_tests.ts
console.log("location", window.location.toString());
assert(window.location.toString().endsWith("unit_tests.ts"));
});

View file

@ -4,12 +4,13 @@
import "./globals";
import { log } from "./util";
import { assert, log } from "./util";
import * as os from "./os";
import { libdeno } from "./libdeno";
import { args } from "./deno";
import { replLoop } from "./repl";
import { setVersions } from "./version";
import { setLocation } from "./location";
// builtin modules
import * as deno from "./deno";
@ -42,6 +43,12 @@ export default function denoMain() {
os.exit(0);
}
const mainModule = startResMsg.mainModule();
if (mainModule) {
assert(mainModule.length > 0);
setLocation(mainModule);
}
const cwd = startResMsg.cwd();
log("cwd", cwd);
@ -51,8 +58,7 @@ export default function denoMain() {
log("args", args);
Object.freeze(args);
const inputFn = args[0];
if (!inputFn) {
if (!mainModule) {
replLoop();
}
}

View file

@ -22,6 +22,7 @@ import "./files_test.ts";
import "./form_data_test.ts";
import "./globals_test.ts";
import "./headers_test.ts";
import "./location_test.ts";
import "./make_temp_dir_test.ts";
import "./metrics_test.ts";
import "./mixins/dom_iterable_test.ts";

View file

@ -400,14 +400,11 @@ impl DenoDir {
}
/// Returns (module name, local filename)
pub fn resolve_module(
pub fn resolve_module_url(
self: &Self,
specifier: &str,
referrer: &str,
) -> Result<(String, String), url::ParseError> {
let module_name;
let filename;
) -> Result<Url, url::ParseError> {
let specifier = self.src_file_to_url(specifier);
let mut referrer = self.src_file_to_url(referrer);
@ -422,8 +419,7 @@ impl DenoDir {
referrer = referrer_path.to_str().unwrap().to_string() + "/";
}
let j: Url = if is_remote(&specifier) || Path::new(&specifier).is_absolute()
{
let j = if is_remote(&specifier) || Path::new(&specifier).is_absolute() {
parse_local_or_remote(&specifier)?
} else if referrer.ends_with('/') {
let r = Url::from_directory_path(&referrer);
@ -437,21 +433,29 @@ impl DenoDir {
let base = parse_local_or_remote(&referrer)?;
base.join(specifier.as_ref())?
};
Ok(j)
}
/// Returns (module name, local filename)
pub fn resolve_module(
self: &Self,
specifier: &str,
referrer: &str,
) -> Result<(String, String), url::ParseError> {
let j = self.resolve_module_url(specifier, referrer)?;
let module_name = j.to_string();
let filename;
match j.scheme() {
"file" => {
let p = deno_fs::normalize_path(j.to_file_path().unwrap().as_ref());
module_name = p.clone();
filename = p;
filename = deno_fs::normalize_path(j.to_file_path().unwrap().as_ref());
}
"https" => {
module_name = j.to_string();
filename = deno_fs::normalize_path(
get_cache_filename(self.deps_https.as_path(), &j).as_ref(),
)
}
"http" => {
module_name = j.to_string();
filename = deno_fs::normalize_path(
get_cache_filename(self.deps_http.as_path(), &j).as_ref(),
)
@ -517,7 +521,7 @@ fn is_remote(module_name: &str) -> bool {
}
fn parse_local_or_remote(p: &str) -> Result<url::Url, url::ParseError> {
if is_remote(p) {
if is_remote(p) || p.starts_with("file:") {
Url::parse(p)
} else {
Url::from_file_path(p).map_err(|_err| url::ParseError::IdnaError)
@ -605,6 +609,16 @@ mod tests {
};
}
macro_rules! file_url {
($path:expr) => {
if cfg!(target_os = "windows") {
concat!("file:///C:", $path)
} else {
concat!("file://", $path)
}
};
}
#[test]
fn test_get_cache_filename() {
let url = Url::parse("http://example.com:1234/path/to/file.ts").unwrap();
@ -1019,25 +1033,25 @@ mod tests {
(
"./subdir/print_hello.ts",
add_root!("/Users/rld/go/src/github.com/denoland/deno/testdata/006_url_imports.ts"),
add_root!("/Users/rld/go/src/github.com/denoland/deno/testdata/subdir/print_hello.ts"),
file_url!("/Users/rld/go/src/github.com/denoland/deno/testdata/subdir/print_hello.ts"),
add_root!("/Users/rld/go/src/github.com/denoland/deno/testdata/subdir/print_hello.ts"),
),
(
"testdata/001_hello.js",
add_root!("/Users/rld/go/src/github.com/denoland/deno/"),
add_root!("/Users/rld/go/src/github.com/denoland/deno/testdata/001_hello.js"),
file_url!("/Users/rld/go/src/github.com/denoland/deno/testdata/001_hello.js"),
add_root!("/Users/rld/go/src/github.com/denoland/deno/testdata/001_hello.js"),
),
(
add_root!("/Users/rld/src/deno/hello.js"),
".",
add_root!("/Users/rld/src/deno/hello.js"),
file_url!("/Users/rld/src/deno/hello.js"),
add_root!("/Users/rld/src/deno/hello.js"),
),
(
add_root!("/this/module/got/imported.js"),
add_root!("/that/module/did/it.js"),
add_root!("/this/module/got/imported.js"),
file_url!("/this/module/got/imported.js"),
add_root!("/this/module/got/imported.js"),
),
];
@ -1169,8 +1183,7 @@ mod tests {
let specifier = "http_test.ts";
let referrer = add_root!("/Users/rld/src/deno_net/");
let expected_module_name =
add_root!("/Users/rld/src/deno_net/http_test.ts");
let expected_module_name = file_url!("/Users/rld/src/deno_net/http_test.ts");
let expected_filename = add_root!("/Users/rld/src/deno_net/http_test.ts");
let (module_name, filename) =
@ -1187,8 +1200,9 @@ mod tests {
let cwd = std::env::current_dir().unwrap();
let expected_path = cwd.join(specifier);
let expected_module_name = deno_fs::normalize_path(&expected_path);
let expected_filename = expected_module_name.clone();
let expected_module_name =
Url::from_file_path(&expected_path).unwrap().to_string();
let expected_filename = deno_fs::normalize_path(&expected_path);
let (module_name, filename) =
deno_dir.resolve_module(specifier, ".").unwrap();
@ -1209,8 +1223,9 @@ mod tests {
let cwd = std::env::current_dir().unwrap();
let expected_path = cwd.join("..").join(specifier);
let expected_module_name = deno_fs::normalize_path(&expected_path);
let expected_filename = expected_module_name.clone();
let expected_module_name =
Url::from_file_path(&expected_path).unwrap().to_string();
let expected_filename = deno_fs::normalize_path(&expected_path);
let (module_name, filename) =
deno_dir.resolve_module(specifier, "..").unwrap();

View file

@ -100,6 +100,22 @@ impl IsolateState {
}
}
pub fn main_module(&self) -> Option<String> {
if self.argv.len() <= 1 {
None
} else {
let specifier = self.argv[1].clone();
let referrer = ".";
match self.dir.resolve_module_url(&specifier, referrer) {
Ok(url) => Some(url.to_string()),
Err(e) => {
debug!("Potentially swallowed error {}", e);
None
}
}
}
}
#[cfg(test)]
pub fn mock() -> Arc<IsolateState> {
let argv = vec![String::from("./deno"), String::from("hello.js")];

View file

@ -105,19 +105,18 @@ fn main() {
.map_err(errors::RustOrJsError::from)
.unwrap_or_else(print_err_and_exit);
// Execute input file.
if isolate.state.argv.len() > 1 {
let input_filename = isolate.state.argv[1].clone();
// Execute main module.
if let Some(main_module) = isolate.state.main_module() {
debug!("main_module {}", main_module);
isolate
.execute_mod(&input_filename, should_prefetch)
.execute_mod(&main_module, should_prefetch)
.unwrap_or_else(print_err_and_exit);
if should_display_info {
// Display file info and exit. Do not run file
modules::print_file_info(
&isolate.modules.borrow(),
&isolate.state.dir,
input_filename,
main_module,
);
std::process::exit(0);
}

View file

@ -158,6 +158,7 @@ table StartRes {
pid: uint32;
argv: [string];
exec_path: string;
main_module: string; // Absolute URL.
debug_flag: bool;
deps_flag: bool;
types_flag: bool;

View file

@ -261,12 +261,15 @@ fn op_start(
let deno_version = version::DENO;
let deno_version_off = builder.create_string(deno_version);
let main_module = state.main_module().map(|m| builder.create_string(&m));
let inner = msg::StartRes::create(
&mut builder,
&msg::StartResArgs {
cwd: Some(cwd_off),
pid: std::process::id(),
argv: Some(argv_off),
main_module,
debug_flag: state.flags.log_debug,
types_flag: state.flags.types,
version_flag: state.flags.version,

View file

@ -1 +1 @@
[WILDCARD]Cannot resolve module "not-a-valid-filename.ts" from "."
[WILDCARD]Cannot resolve module "file:[WILDCARD]not-a-valid-filename.ts" from "."

2
tests/if_main.test Normal file
View file

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

7
tests/if_main.ts Normal file
View file

@ -0,0 +1,7 @@
if (window.location.toString() == import.meta.url) {
console.log("main");
} else {
console.log("import.meta.url", import.meta.url);
console.log("window.location", window.location.toString());
throw Error("not main");
}

1
tests/if_main.ts.out Normal file
View file

@ -0,0 +1 @@
main

2
tests/import_meta.test Normal file
View file

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

3
tests/import_meta.ts Normal file
View file

@ -0,0 +1,3 @@
console.log("import_meta", import.meta.url);
import "import_meta2.ts";

2
tests/import_meta.ts.out Normal file
View file

@ -0,0 +1,2 @@
import_meta2 [WILDCARD]import_meta2.ts
import_meta [WILDCARD]import_meta.ts

1
tests/import_meta2.ts Normal file
View file

@ -0,0 +1 @@
console.log("import_meta2", import.meta.url);

View file

@ -1,3 +0,0 @@
console.log("imports_meta", import.meta.url);
import "imports_meta2.js";

View file

@ -1,2 +0,0 @@
imports_meta2 [WILDCARD]imports_meta2.js
imports_meta [WILDCARD]imports_meta.js

View file

@ -1,2 +0,0 @@
args: tests/imports_meta.js --reload
output: tests/imports_meta.js.out

View file

@ -1 +0,0 @@
console.log("imports_meta2", import.meta.url);

View file

@ -466,6 +466,17 @@ import { test, assertEqual } from "./package.ts";
This design circumvents a plethora of complexity spawned by package management
software, centralized code repositories, and superfluous file formats.
### Testing if current file is the main program
By using `window.location` and `import.meta.url` one can test if the current
script has been executed as the main input to the program.
```ts
if (window.location.toString() == import.meta.url) {
console.log("main");
}
```
## Command line interface
### Flags