1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-23 15:49:44 -05:00

BREAKING: Remove unstable Deno.emit and Deno.formatDiagnostics APIs (#14463)

This commit is contained in:
Bartek Iwańczuk 2022-05-17 19:50:31 +02:00 committed by GitHub
parent 9766399a3f
commit f57aac77ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 7 additions and 1957 deletions

View file

@ -5,7 +5,6 @@ use crate::errors::get_error_class_name;
use crate::file_fetcher::FileFetcher;
use deno_core::error::AnyError;
use deno_core::futures::future;
use deno_core::futures::FutureExt;
use deno_core::serde::Deserialize;
use deno_core::serde::Serialize;
@ -16,7 +15,6 @@ use deno_graph::source::LoadFuture;
use deno_graph::source::LoadResponse;
use deno_graph::source::Loader;
use deno_runtime::permissions::Permissions;
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Deserialize, Serialize)]
@ -248,106 +246,3 @@ impl CacherLoader for FetchCacher {
self
}
}
/// An in memory cache that is used by the runtime `Deno.emit()` API to provide
/// the same behavior as the disk cache when sources are provided.
#[derive(Debug)]
pub struct MemoryCacher {
sources: HashMap<String, Arc<String>>,
declarations: HashMap<ModuleSpecifier, String>,
emits: HashMap<ModuleSpecifier, String>,
maps: HashMap<ModuleSpecifier, String>,
build_infos: HashMap<ModuleSpecifier, String>,
versions: HashMap<ModuleSpecifier, String>,
}
impl MemoryCacher {
pub fn new(sources: HashMap<String, Arc<String>>) -> Self {
Self {
sources,
declarations: HashMap::default(),
emits: HashMap::default(),
maps: HashMap::default(),
build_infos: HashMap::default(),
versions: HashMap::default(),
}
}
}
impl Loader for MemoryCacher {
fn load(
&mut self,
specifier: &ModuleSpecifier,
_is_dynamic: bool,
) -> LoadFuture {
let mut specifier_str = specifier.to_string();
if !self.sources.contains_key(&specifier_str) {
specifier_str = specifier_str.replace("file:///", "/");
if !self.sources.contains_key(&specifier_str) {
specifier_str = specifier_str[3..].to_string();
}
}
let response =
self
.sources
.get(&specifier_str)
.map(|c| LoadResponse::Module {
specifier: specifier.clone(),
maybe_headers: None,
content: c.to_owned(),
});
Box::pin(future::ready(Ok(response)))
}
}
impl Cacher for MemoryCacher {
fn get(
&self,
cache_type: CacheType,
specifier: &ModuleSpecifier,
) -> Option<String> {
match cache_type {
CacheType::Declaration => self.declarations.get(specifier).cloned(),
CacheType::Emit => self.emits.get(specifier).cloned(),
CacheType::SourceMap => self.maps.get(specifier).cloned(),
CacheType::TypeScriptBuildInfo => {
self.build_infos.get(specifier).cloned()
}
CacheType::Version => self.versions.get(specifier).cloned(),
}
}
fn set(
&mut self,
cache_type: CacheType,
specifier: &ModuleSpecifier,
value: String,
) -> Result<(), AnyError> {
match cache_type {
CacheType::Declaration => {
self.declarations.insert(specifier.clone(), value)
}
CacheType::Emit => self.emits.insert(specifier.clone(), value),
CacheType::SourceMap => self.maps.insert(specifier.clone(), value),
CacheType::TypeScriptBuildInfo => {
self.build_infos.insert(specifier.clone(), value)
}
CacheType::Version => self.versions.insert(specifier.clone(), value),
};
Ok(())
}
}
impl CacherLoader for MemoryCacher {
fn as_cacher(&self) -> &dyn Cacher {
self
}
fn as_mut_loader(&mut self) -> &mut dyn Loader {
self
}
fn as_mut_cacher(&mut self) -> &mut dyn Cacher {
self
}
}

View file

@ -6,7 +6,6 @@ use deno_core::serde::Deserialize;
use deno_core::serde::Deserializer;
use deno_core::serde::Serialize;
use deno_core::serde::Serializer;
use deno_graph::ModuleGraphError;
use once_cell::sync::Lazy;
use regex::Regex;
use std::error::Error;
@ -16,7 +15,6 @@ const MAX_SOURCE_LINE_LENGTH: usize = 150;
const UNSTABLE_DENO_PROPS: &[&str] = &[
"BenchDefinition",
"CompilerOptions",
"CreateHttpClientOptions",
"DatagramConn",
"Diagnostic",
@ -40,8 +38,6 @@ const UNSTABLE_DENO_PROPS: &[&str] = &[
"connect",
"consoleSize",
"createHttpClient",
"emit",
"formatDiagnostics",
"futime",
"futimeSync",
"hostname",
@ -357,21 +353,6 @@ impl Diagnostics {
Diagnostics(diagnostics)
}
pub fn extend_graph_errors(&mut self, errors: Vec<ModuleGraphError>) {
self.0.extend(errors.into_iter().map(|err| Diagnostic {
category: DiagnosticCategory::Error,
code: 900001,
start: None,
end: None,
message_text: Some(err.to_string()),
message_chain: None,
source: None,
source_line: None,
file_name: Some(err.specifier().to_string()),
related_information: None,
}));
}
/// Return a set of diagnostics where only the values where the predicate
/// returns `true` are included.
pub fn filter<P>(&self, predicate: P) -> Self

View file

@ -553,348 +553,6 @@ declare namespace Deno {
code: number;
}
/** **UNSTABLE**: new API, yet to be vetted.
*
* Format an array of diagnostic items and return them as a single string in a
* user friendly format. If there are no diagnostics then it will return an
* empty string.
*
* ```ts
* const { diagnostics } = await Deno.emit("file_with_compile_issues.ts");
* console.table(diagnostics); // Prints raw diagnostic data
* console.log(Deno.formatDiagnostics(diagnostics)); // User friendly output of diagnostics
* console.log(Deno.formatDiagnostics([])); // An empty string
* ```
*
* @param diagnostics An array of diagnostic items to format
*/
export function formatDiagnostics(diagnostics: Diagnostic[]): string;
/** **UNSTABLE**: new API, yet to be vetted.
*
* A specific subset TypeScript compiler options that can be supported by the
* Deno TypeScript compiler. */
export interface CompilerOptions {
/** Allow JavaScript files to be compiled. Defaults to `true`. */
allowJs?: boolean;
/** Allow default imports from modules with no default export. This does not
* affect code emit, just typechecking. Defaults to `false`. */
allowSyntheticDefaultImports?: boolean;
/** Allow accessing UMD globals from modules. Defaults to `false`. */
allowUmdGlobalAccess?: boolean;
/** Do not report errors on unreachable code. Defaults to `false`. */
allowUnreachableCode?: boolean;
/** Do not report errors on unused labels. Defaults to `false` */
allowUnusedLabels?: boolean;
/** Parse in strict mode and emit `"use strict"` for each source file.
* Defaults to `true`. */
alwaysStrict?: boolean;
/** Base directory to resolve non-relative module names. Defaults to
* `undefined`. */
baseUrl?: string;
/** The character set of the input files. Defaults to `"utf8"`. */
charset?: string;
/** Report errors in `.js` files. Use in conjunction with `allowJs`. Defaults
* to `false`. */
checkJs?: boolean;
/** Generates corresponding `.d.ts` file. Defaults to `false`. */
declaration?: boolean;
/** Output directory for generated declaration files. */
declarationDir?: string;
/** Generates a source map for each corresponding `.d.ts` file. Defaults to
* `false`. */
declarationMap?: boolean;
/** Provide full support for iterables in `for..of`, spread and
* destructuring when targeting ES5 or ES3. Defaults to `false`. */
downlevelIteration?: boolean;
/** Only emit `.d.ts` declaration files. Defaults to `false`. */
emitDeclarationOnly?: boolean;
/** Emit design-type metadata for decorated declarations in source. See issue
* [microsoft/TypeScript#2577](https://github.com/Microsoft/TypeScript/issues/2577)
* for details. Defaults to `false`. */
emitDecoratorMetadata?: boolean;
/** Emit `__importStar` and `__importDefault` helpers for runtime babel
* ecosystem compatibility and enable `allowSyntheticDefaultImports` for type
* system compatibility. Defaults to `true`. */
esModuleInterop?: boolean;
/** Enables experimental support for ES decorators. Defaults to `true`. */
experimentalDecorators?: boolean;
/** Import emit helpers (e.g. `__extends`, `__rest`, etc..) from
* [tslib](https://www.npmjs.com/package/tslib). */
importHelpers?: boolean;
/** This flag controls how `import` works, there are 3 different options:
*
* - `remove`: The default behavior of dropping import statements which only
* reference types.
* - `preserve`: Preserves all `import` statements whose values or types are
* never used. This can cause imports/side-effects to be preserved.
* - `error`: This preserves all imports (the same as the preserve option),
* but will error when a value import is only used as a type. This might
* be useful if you want to ensure no values are being accidentally
* imported, but still make side-effect imports explicit.
*
* This flag works because you can use `import type` to explicitly create an
* `import` statement which should never be emitted into JavaScript. */
importsNotUsedAsValues?: "remove" | "preserve" | "error";
/** Emit a single file with source maps instead of having a separate file.
* Defaults to `false`. */
inlineSourceMap?: boolean;
/** Emit the source alongside the source maps within a single file; requires
* `inlineSourceMap` or `sourceMap` to be set. Defaults to `false`. */
inlineSources?: boolean;
/** Support JSX in `.tsx` files: `"react"`, `"preserve"`, `"react-native"`,
* `"react-jsx", `"react-jsxdev"`.
* Defaults to `"react"`. */
jsx?: "react" | "preserve" | "react-native" | "react-jsx" | "react-jsx-dev";
/** Specify the JSX factory function to use when targeting react JSX emit,
* e.g. `React.createElement` or `h`. Defaults to `React.createElement`. */
jsxFactory?: string;
/** Specify the JSX fragment factory function to use when targeting react
* JSX emit, e.g. `Fragment`. Defaults to `React.Fragment`. */
jsxFragmentFactory?: string;
/** Declares the module specifier to be used for importing the `jsx` and
* `jsxs` factory functions when using jsx as `"react-jsx"` or
* `"react-jsxdev"`. Defaults to `"react"`. */
jsxImportSource?: string;
/** Resolve keyof to string valued property names only (no numbers or
* symbols). Defaults to `false`. */
keyofStringsOnly?: string;
/** List of library files to be included in the compilation. If omitted,
* then the Deno main runtime libs are used. */
lib?: string[];
/** The locale to use to show error messages. */
locale?: string;
/** Specifies the location where debugger should locate map files instead of
* generated locations. Use this flag if the `.map` files will be located at
* run-time in a different location than the `.js` files. The location
* specified will be embedded in the source map to direct the debugger where
* the map files will be located. Defaults to `undefined`. */
mapRoot?: string;
/** Specify the module format for the emitted code. Defaults to
* `"esnext"`. */
module?:
| "none"
| "commonjs"
| "amd"
| "system"
| "umd"
| "es6"
| "es2015"
| "es2020"
| "esnext";
/** Do not generate custom helper functions like `__extends` in compiled
* output. Defaults to `false`. */
noEmitHelpers?: boolean;
/** Report errors for fallthrough cases in switch statement. Defaults to
* `false`. */
noFallthroughCasesInSwitch?: boolean;
/** Raise error on expressions and declarations with an implied any type.
* Defaults to `true`. */
noImplicitAny?: boolean;
/** Report an error when not all code paths in function return a value.
* Defaults to `false`. */
noImplicitReturns?: boolean;
/** Raise error on `this` expressions with an implied `any` type. Defaults to
* `true`. */
noImplicitThis?: boolean;
/** Do not emit `"use strict"` directives in module output. Defaults to
* `false`. */
noImplicitUseStrict?: boolean;
/** Do not include the default library file (`lib.d.ts`). Defaults to
* `false`. */
noLib?: boolean;
/** Do not add triple-slash references or module import targets to the list of
* compiled files. Defaults to `false`. */
noResolve?: boolean;
/** Disable strict checking of generic signatures in function types. Defaults
* to `false`. */
noStrictGenericChecks?: boolean;
/** Include 'undefined' in index signature results. Defaults to `false`. */
noUncheckedIndexedAccess?: boolean;
/** Report errors on unused locals. Defaults to `false`. */
noUnusedLocals?: boolean;
/** Report errors on unused parameters. Defaults to `false`. */
noUnusedParameters?: boolean;
/** List of path mapping entries for module names to locations relative to the
* `baseUrl`. Defaults to `undefined`. */
paths?: Record<string, string[]>;
/** Do not erase const enum declarations in generated code. Defaults to
* `false`. */
preserveConstEnums?: boolean;
/** Remove all comments except copy-right header comments beginning with
* `/*!`. Defaults to `true`. */
removeComments?: boolean;
/** Specifies the root directory of input files. Only use to control the
* output directory structure with `outDir`. Defaults to `undefined`. */
rootDir?: string;
/** List of _root_ folders whose combined content represent the structure of
* the project at runtime. Defaults to `undefined`. */
rootDirs?: string[];
/** Generates corresponding `.map` file. Defaults to `false`. */
sourceMap?: boolean;
/** Specifies the location where debugger should locate TypeScript files
* instead of source locations. Use this flag if the sources will be located
* at run-time in a different location than that at design-time. The location
* specified will be embedded in the sourceMap to direct the debugger where
* the source files will be located. Defaults to `undefined`. */
sourceRoot?: string;
/** Skip type checking of all declaration files (`*.d.ts`). */
skipLibCheck?: boolean;
/** Enable all strict type checking options. Enabling `strict` enables
* `noImplicitAny`, `noImplicitThis`, `alwaysStrict`, `strictBindCallApply`,
* `strictNullChecks`, `strictFunctionTypes` and
* `strictPropertyInitialization`. Defaults to `true`. */
strict?: boolean;
/** Enable stricter checking of the `bind`, `call`, and `apply` methods on
* functions. Defaults to `true`. */
strictBindCallApply?: boolean;
/** Disable bivariant parameter checking for function types. Defaults to
* `true`. */
strictFunctionTypes?: boolean;
/** Ensure non-undefined class properties are initialized in the constructor.
* This option requires `strictNullChecks` be enabled in order to take effect.
* Defaults to `true`. */
strictPropertyInitialization?: boolean;
/** In strict null checking mode, the `null` and `undefined` values are not in
* the domain of every type and are only assignable to themselves and `any`
* (the one exception being that `undefined` is also assignable to `void`). */
strictNullChecks?: boolean;
/** Suppress excess property checks for object literals. Defaults to
* `false`. */
suppressExcessPropertyErrors?: boolean;
/** Suppress `noImplicitAny` errors for indexing objects lacking index
* signatures. */
suppressImplicitAnyIndexErrors?: boolean;
/** Specify ECMAScript target version. Defaults to `esnext`. */
target?:
| "es3"
| "es5"
| "es6"
| "es2015"
| "es2016"
| "es2017"
| "es2018"
| "es2019"
| "es2020"
| "esnext";
/** List of names of type definitions to include when type checking.
* Defaults to `undefined`.
*
* The type definitions are resolved according to the normal Deno resolution
* irrespective of if sources are provided on the call. In addition, unlike
* passing the `--config` option on startup, there is no base to resolve
* relative specifiers, so the specifiers here have to be fully qualified
* URLs or paths. For example:
*
* ```ts
* Deno.emit("./a.ts", {
* compilerOptions: {
* types: [
* "https://deno.land/x/pkg/types.d.ts",
* "/Users/me/pkg/types.d.ts",
* ]
* }
* });
* ```
*/
types?: string[];
/** Emit class fields with ECMAScript-standard semantics. Defaults to
* `false`. */
useDefineForClassFields?: boolean;
}
interface ImportMap {
imports: Record<string, string>;
scopes?: Record<string, Record<string, string>>;
}
/**
* **UNSTABLE**: new API, yet to be vetted.
*
* The options for `Deno.emit()` API.
*/
export interface EmitOptions {
/** Indicate that the source code should be emitted to a single file
* JavaScript bundle that is a single ES module (`"module"`) or a single
* file self contained script executed in an immediately invoked function
* when loaded (`"classic"`). */
bundle?: "module" | "classic";
/** If `true` then the sources will be typed checked, returning any
* diagnostic errors in the result. If `false` type checking will be
* skipped. Defaults to `true`.
*
* *Note* by default, only TypeScript will be type checked, just like on
* the command line. Use the `compilerOptions` options of `checkJs` to
* enable type checking of JavaScript. */
check?: boolean;
/** A set of options that are aligned to TypeScript compiler options that
* are supported by Deno. */
compilerOptions?: CompilerOptions;
/** An [import-map](https://deno.land/manual/linking_to_external_code/import_maps#import-maps)
* which will be applied to the imports. */
importMap?: ImportMap;
/** An absolute path to an [import-map](https://deno.land/manual/linking_to_external_code/import_maps#import-maps).
* Required to be specified if an `importMap` is specified to be able to
* determine resolution of relative paths. If a `importMap` is not
* specified, then it will assumed the file path points to an import map on
* disk and will be attempted to be loaded based on current runtime
* permissions.
*/
importMapPath?: string;
/** A record of sources to use when doing the emit. If provided, Deno will
* use these sources instead of trying to resolve the modules externally. */
sources?: Record<string, string>;
}
/**
* **UNSTABLE**: new API, yet to be vetted.
*
* The result of `Deno.emit()` API.
*/
export interface EmitResult {
/** Diagnostic messages returned from the type checker (`tsc`).
*
* Can be used with `Deno.formatDiagnostics` to display a user
* friendly string. */
diagnostics: Diagnostic[];
/** Any emitted files. If bundled, then the JavaScript will have the
* key of `deno:///bundle.js` with an optional map (based on
* `compilerOptions`) in `deno:///bundle.js.map`. */
files: Record<string, string>;
/** An optional array of any compiler options that were ignored by Deno. */
ignoredOptions?: string[];
/** An array of internal statistics related to the emit, for diagnostic
* purposes. */
stats: Array<[string, number]>;
}
/**
* **UNSTABLE**: new API, yet to be vetted.
*
* Similar to the command line functionality of `deno run` or `deno cache`,
* `Deno.emit()` provides a way to provide Deno arbitrary JavaScript
* or TypeScript and have it return JavaScript based on the options and
* settings provided. The source code can either be provided or the modules
* can be fetched and resolved in line with the behavior of the command line.
*
* Requires `allow-read` and/or `allow-net` if sources are not provided.
*
* @param rootSpecifier The specifier that will be used as the entry point.
* If no sources are provided, then the specifier would
* be the same as if you typed it on the command line for
* `deno run`. If sources are provided, it should match
* one of the names of the sources.
* @param options A set of options to be used with the emit.
*
* @returns The result of the emit. If diagnostics are found, they can be used
* with `Deno.formatDiagnostics` to construct a user friendly string, which
* has the same format as CLI diagnostics.
*/
export function emit(
rootSpecifier: string | URL,
options?: EmitOptions,
): Promise<EmitResult>;
export type SetRawOptions = {
cbreak: boolean;
};

View file

@ -146,8 +146,6 @@ pub enum ConfigType {
Check { lib: TypeLib, tsc_emit: bool },
/// Return a configuration to use swc to emit single module files.
Emit,
/// Return a configuration as a base for the runtime `Deno.emit()` API.
RuntimeEmit { tsc_emit: bool },
}
/// For a given configuration type and optionally a configuration file, return a
@ -216,44 +214,6 @@ pub fn get_ts_config(
"jsxFragmentFactory": "React.Fragment",
"resolveJsonModule": true,
})),
ConfigType::RuntimeEmit { tsc_emit } => {
let mut ts_config = TsConfig::new(json!({
"allowJs": true,
"allowSyntheticDefaultImports": true,
"checkJs": false,
"emitDecoratorMetadata": false,
"experimentalDecorators": true,
"importsNotUsedAsValues": "remove",
"incremental": true,
"isolatedModules": true,
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
"lib": TypeLib::DenoWindow,
"module": "esnext",
"removeComments": true,
"inlineSourceMap": false,
"inlineSources": false,
"sourceMap": true,
"strict": true,
"target": "esnext",
"tsBuildInfoFile": "deno:///.tsbuildinfo",
"useDefineForClassFields": true,
// TODO(@kitsonk) remove for Deno 2.0
"useUnknownInCatchVariables": false,
}));
if tsc_emit {
ts_config.merge(&json!({
"importsNotUsedAsValues": "remove",
"outDir": "deno://",
}));
} else {
ts_config.merge(&json!({
"noEmit": true,
}));
}
ts_config
}
};
let maybe_ignored_options = if let Some(user_options) = maybe_user_config {
ts_config.merge_user_config(user_options)?
@ -946,47 +906,6 @@ impl fmt::Display for GraphError {
}
}
/// Convert a module graph to a map of "files", which are used by the runtime
/// emit to be passed back to the caller.
pub fn to_file_map(
graph: &ModuleGraph,
cache: &dyn Cacher,
) -> HashMap<String, String> {
let mut files = HashMap::new();
for (_, result) in graph.specifiers().into_iter() {
if let Ok((specifier, _, media_type)) = result {
if let Some(emit) = cache.get(CacheType::Emit, &specifier) {
files.insert(format!("{}.js", specifier), emit);
if let Some(map) = cache.get(CacheType::SourceMap, &specifier) {
files.insert(format!("{}.js.map", specifier), map);
}
} else if matches!(
media_type,
MediaType::JavaScript
| MediaType::Mjs
| MediaType::Cjs
| MediaType::Json
| MediaType::Unknown
) {
if let Some(module) = graph.get(&specifier) {
files.insert(
specifier.to_string(),
module
.maybe_source
.as_ref()
.map(|s| s.to_string())
.unwrap_or_else(|| "".to_string()),
);
}
}
if let Some(declaration) = cache.get(CacheType::Declaration, &specifier) {
files.insert(format!("{}.d.ts", specifier), declaration);
}
}
}
files
}
/// This contains the logic for Deno to rewrite the `import.meta` when bundling.
pub struct BundleHook;

View file

@ -39,7 +39,6 @@ pub fn format_location(frame: &JsStackFrame) -> String {
result
}
// Keep in sync with `runtime/js/40_error_stack.js`.
fn format_frame(frame: &JsStackFrame) -> String {
let _internal = frame
.file_name

View file

@ -139,7 +139,7 @@ fn create_web_worker_callback(
let preload_module_cb =
create_web_worker_preload_module_callback(ps.clone());
let extensions = ops::cli_exts(ps.clone(), args.use_deno_namespace);
let extensions = ops::cli_exts(ps.clone());
let options = WebWorkerOptions {
bootstrap: BootstrapOptions {
@ -237,7 +237,7 @@ pub fn create_main_worker(
.join(checksum::gen(&[key.as_bytes()]))
});
let mut extensions = ops::cli_exts(ps.clone(), true);
let mut extensions = ops::cli_exts(ps.clone());
extensions.append(&mut custom_extensions);
let options = WorkerOptions {

View file

@ -1,21 +0,0 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
use crate::diagnostics::Diagnostics;
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::serde_json::Value;
use deno_core::Extension;
pub fn init() -> Extension {
Extension::builder()
.ops(vec![op_format_diagnostic::decl()])
.build()
}
#[op]
fn op_format_diagnostic(args: Value) -> Result<Value, AnyError> {
let diagnostic: Diagnostics = serde_json::from_value(args)?;
Ok(json!(diagnostic.to_string()))
}

View file

@ -4,20 +4,10 @@ use crate::proc_state::ProcState;
use deno_core::Extension;
pub mod bench;
mod errors;
mod runtime_compiler;
pub mod testing;
pub fn cli_exts(ps: ProcState, enable_compiler: bool) -> Vec<Extension> {
if enable_compiler {
vec![
init_proc_state(ps),
errors::init(),
runtime_compiler::init(),
]
} else {
vec![init_proc_state(ps), errors::init()]
}
pub fn cli_exts(ps: ProcState) -> Vec<Extension> {
vec![init_proc_state(ps)]
}
fn init_proc_state(ps: ProcState) -> Extension {

View file

@ -1,328 +0,0 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
use crate::cache;
use crate::config_file::IgnoredCompilerOptions;
use crate::diagnostics::Diagnostics;
use crate::emit;
use crate::errors::get_error_class_name;
use crate::flags;
use crate::graph_util::graph_valid;
use crate::proc_state::import_map_from_text;
use crate::proc_state::ProcState;
use crate::resolver::ImportMapResolver;
use crate::resolver::JsxResolver;
use deno_core::anyhow::Context;
use deno_core::error::custom_error;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::parking_lot::RwLock;
use deno_core::resolve_url_or_path;
use deno_core::serde_json;
use deno_core::serde_json::Value;
use deno_core::Extension;
use deno_core::ModuleSpecifier;
use deno_core::OpState;
use deno_graph::ModuleKind;
use deno_runtime::permissions::Permissions;
use serde::Deserialize;
use serde::Serialize;
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::HashSet;
use std::rc::Rc;
use std::sync::Arc;
pub fn init() -> Extension {
Extension::builder().ops(vec![op_emit::decl()]).build()
}
#[derive(Debug, Deserialize)]
enum RuntimeBundleType {
#[serde(rename = "module")]
Module,
#[serde(rename = "classic")]
Classic,
}
impl<'a> From<&'a RuntimeBundleType> for emit::BundleType {
fn from(bundle_type: &'a RuntimeBundleType) -> Self {
match bundle_type {
RuntimeBundleType::Classic => Self::Classic,
RuntimeBundleType::Module => Self::Module,
}
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct EmitArgs {
bundle: Option<RuntimeBundleType>,
check: Option<bool>,
compiler_options: Option<HashMap<String, Value>>,
import_map: Option<Value>,
import_map_path: Option<String>,
root_specifier: String,
sources: Option<HashMap<String, Arc<String>>>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct EmitResult {
diagnostics: Diagnostics,
files: HashMap<String, String>,
#[serde(rename = "ignoredOptions")]
maybe_ignored_options: Option<IgnoredCompilerOptions>,
stats: emit::Stats,
}
/// Provides inferred imported modules from configuration options, like the
/// `"types"` and `"jsxImportSource"` imports.
fn to_maybe_imports(
referrer: &ModuleSpecifier,
maybe_options: Option<&HashMap<String, Value>>,
) -> Option<Vec<(ModuleSpecifier, Vec<String>)>> {
let options = maybe_options?;
let mut imports = Vec::new();
if let Some(types_value) = options.get("types") {
if let Ok(types) =
serde_json::from_value::<Vec<String>>(types_value.clone())
{
imports.extend(types);
}
}
if let Some(jsx_value) = options.get("jsx") {
if let Ok(jsx) = serde_json::from_value::<String>(jsx_value.clone()) {
let jsx_import_source =
if let Some(jsx_import_source_value) = options.get("jsxImportSource") {
if let Ok(jsx_import_source) =
serde_json::from_value::<String>(jsx_import_source_value.clone())
{
jsx_import_source
} else {
"react".to_string()
}
} else {
"react".to_string()
};
match jsx.as_str() {
"react-jsx" => {
imports.push(format!("{}/jsx-runtime", jsx_import_source));
}
"react-jsxdev" => {
imports.push(format!("{}/jsx-dev-runtime", jsx_import_source));
}
_ => (),
}
}
}
if !imports.is_empty() {
Some(vec![(referrer.clone(), imports)])
} else {
None
}
}
/// Converts the compiler options to the JSX import source module that will be
/// loaded when transpiling JSX.
fn to_maybe_jsx_import_source_module(
maybe_options: Option<&HashMap<String, Value>>,
) -> Option<String> {
let options = maybe_options?;
let jsx_value = options.get("jsx")?;
let jsx: String = serde_json::from_value(jsx_value.clone()).ok()?;
match jsx.as_str() {
"react-jsx" => Some("jsx-runtime".to_string()),
"react-jsxdev" => Some("jsx-dev-runtime".to_string()),
_ => None,
}
}
#[op]
async fn op_emit(
state: Rc<RefCell<OpState>>,
args: EmitArgs,
) -> Result<EmitResult, AnyError> {
deno_runtime::ops::check_unstable2(&state, "Deno.emit");
let root_specifier = args.root_specifier;
let ps = {
let state = state.borrow();
state.borrow::<ProcState>().clone()
};
let mut runtime_permissions = {
let state = state.borrow();
state.borrow::<Permissions>().clone()
};
let mut cache: Box<dyn cache::CacherLoader> =
if let Some(sources) = &args.sources {
Box::new(cache::MemoryCacher::new(sources.clone()))
} else {
Box::new(cache::FetchCacher::new(
ps.dir.gen_cache.clone(),
ps.file_fetcher.clone(),
runtime_permissions.clone(),
runtime_permissions.clone(),
))
};
let maybe_import_map_resolver = if let Some(import_map_str) =
args.import_map_path
{
let import_map_specifier = resolve_url_or_path(&import_map_str)
.with_context(|| {
format!("Bad URL (\"{}\") for import map.", import_map_str)
})?;
let import_map_source = if let Some(value) = args.import_map {
Arc::new(value.to_string())
} else {
let file = ps
.file_fetcher
.fetch(&import_map_specifier, &mut runtime_permissions)
.await
.map_err(|e| {
generic_error(format!(
"Unable to load '{}' import map: {}",
import_map_specifier, e
))
})?;
file.source
};
let import_map =
import_map_from_text(&import_map_specifier, &import_map_source)?;
Some(ImportMapResolver::new(Arc::new(import_map)))
} else if args.import_map.is_some() {
return Err(generic_error("An importMap was specified, but no importMapPath was provided, which is required."));
} else {
None
};
let maybe_jsx_resolver =
to_maybe_jsx_import_source_module(args.compiler_options.as_ref())
.map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone()));
let maybe_resolver = if maybe_jsx_resolver.is_some() {
maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver())
} else {
maybe_import_map_resolver
.as_ref()
.map(|imr| imr.as_resolver())
};
let roots = vec![(resolve_url_or_path(&root_specifier)?, ModuleKind::Esm)];
let maybe_imports =
to_maybe_imports(&roots[0].0, args.compiler_options.as_ref());
let graph = Arc::new(
deno_graph::create_graph(
roots,
true,
maybe_imports,
cache.as_mut_loader(),
maybe_resolver,
None,
None,
None,
)
.await,
);
let check = args.check.unwrap_or(true);
// There are certain graph errors that we want to return as an error of an op,
// versus something that gets returned as a diagnostic of the op, this is
// handled here.
if let Err(err) = graph_valid(&graph, check, true) {
if get_error_class_name(&err) == "PermissionDenied" {
return Err(err);
}
}
let debug = ps.flags.log_level == Some(log::Level::Debug);
let tsc_emit = check && args.bundle.is_none();
let (ts_config, maybe_ignored_options) = emit::get_ts_config(
emit::ConfigType::RuntimeEmit { tsc_emit },
None,
args.compiler_options.as_ref(),
)?;
let (files, mut diagnostics, stats) = if check && args.bundle.is_none() {
let emit_result = emit::check_and_maybe_emit(
&graph.roots,
Arc::new(RwLock::new(graph.as_ref().into())),
cache.as_mut_cacher(),
emit::CheckOptions {
type_check_mode: flags::TypeCheckMode::All,
debug,
emit_with_diagnostics: true,
maybe_config_specifier: None,
ts_config,
log_checks: false,
reload: ps.flags.reload || args.sources.is_some(),
// TODO(nayeemrmn): Determine reload exclusions.
reload_exclusions: Default::default(),
},
)?;
let files = emit::to_file_map(graph.as_ref(), cache.as_mut_cacher());
(files, emit_result.diagnostics, emit_result.stats)
} else if let Some(bundle) = &args.bundle {
let (diagnostics, stats) = if check {
if ts_config.get_declaration() {
return Err(custom_error("TypeError", "The bundle option is set, but the compiler option of `declaration` is true which is not currently supported."));
}
let emit_result = emit::check_and_maybe_emit(
&graph.roots,
Arc::new(RwLock::new(graph.as_ref().into())),
cache.as_mut_cacher(),
emit::CheckOptions {
type_check_mode: flags::TypeCheckMode::All,
debug,
emit_with_diagnostics: true,
maybe_config_specifier: None,
ts_config: ts_config.clone(),
log_checks: false,
reload: ps.flags.reload || args.sources.is_some(),
// TODO(nayeemrmn): Determine reload exclusions.
reload_exclusions: Default::default(),
},
)?;
(emit_result.diagnostics, emit_result.stats)
} else {
(Diagnostics::default(), Default::default())
};
let (emit, maybe_map) = emit::bundle(
graph.as_ref(),
emit::BundleOptions {
bundle_type: bundle.into(),
ts_config,
emit_ignore_directives: true,
},
)?;
let mut files = HashMap::new();
files.insert("deno:///bundle.js".to_string(), emit);
if let Some(map) = maybe_map {
files.insert("deno:///bundle.js.map".to_string(), map);
}
(files, diagnostics, stats)
} else {
if ts_config.get_declaration() {
return Err(custom_error("TypeError", "The option of `check` is false, but the compiler option of `declaration` is true which is not currently supported."));
}
let emit_result = emit::emit(
graph.as_ref(),
cache.as_mut_cacher(),
emit::EmitOptions {
ts_config,
reload: ps.flags.reload || args.sources.is_some(),
// TODO(nayeemrmn): Determine reload exclusions.
reload_exclusions: HashSet::default(),
},
)?;
let files = emit::to_file_map(graph.as_ref(), cache.as_mut_cacher());
(files, emit_result.diagnostics, emit_result.stats)
};
// we want to add any errors that were returned as an `Err` earlier by adding
// them to the diagnostics.
diagnostics.extend_graph_errors(graph.errors());
Ok(EmitResult {
diagnostics,
files,
maybe_ignored_options,
stats,
})
}

View file

@ -288,7 +288,7 @@ pub async fn run(
unstable: metadata.unstable,
user_agent: version::get_user_agent(),
},
extensions: ops::cli_exts(ps.clone(), true),
extensions: ops::cli_exts(ps.clone()),
unsafely_ignore_certificate_errors: metadata
.unsafely_ignore_certificate_errors,
root_cert_store: Some(root_cert_store),

View file

@ -225,76 +225,6 @@ fn standalone_follow_redirects() {
assert_eq!(output.stdout, b"Hello\n");
}
#[test]
fn standalone_compiler_ops() {
let dir = TempDir::new();
let exe = if cfg!(windows) {
dir.path().join("standalone_compiler_ops.exe")
} else {
dir.path().join("standalone_compiler_ops")
};
let output = util::deno_cmd()
.current_dir(util::testdata_path())
.arg("compile")
.arg("--unstable")
.arg("--output")
.arg(&exe)
.arg("./standalone_compiler_ops.ts")
.stdout(std::process::Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
.unwrap();
assert!(output.status.success());
let output = Command::new(exe)
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
.unwrap();
assert!(output.status.success());
assert_eq!(output.stdout, b"Hello, Compiler API!\n");
}
#[test]
fn compile_with_directory_output_flag() {
let dir = TempDir::new();
let output_path = if cfg!(windows) {
dir.path().join(r"args\random\")
} else {
dir.path().join("args/random/")
};
let output = util::deno_cmd()
.current_dir(util::testdata_path())
.arg("compile")
.arg("--unstable")
.arg("--output")
.arg(&output_path)
.arg("./standalone_compiler_ops.ts")
.stdout(std::process::Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
.unwrap();
assert!(output.status.success());
let exe = if cfg!(windows) {
output_path.join("standalone_compiler_ops.exe")
} else {
output_path.join("standalone_compiler_ops")
};
assert!(&exe.exists());
let output = Command::new(exe)
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
.unwrap();
assert!(output.status.success());
assert_eq!(output.stdout, b"Hello, Compiler API!\n");
}
#[test]
fn compile_with_file_exists_error() {
let dir = TempDir::new();

View file

@ -443,22 +443,6 @@ console.log("finish");
assert!(end - start < Duration::new(10, 0));
}
#[test]
fn compiler_api() {
let status = util::deno_cmd()
.current_dir(util::testdata_path())
.arg("test")
.arg("--unstable")
.arg("--reload")
.arg("--allow-read")
.arg("compiler_api_test.ts")
.spawn()
.unwrap()
.wait()
.unwrap();
assert!(status.success());
}
#[test]
fn broken_stdout() {
let (reader, writer) = os_pipe::pipe().unwrap();

View file

@ -450,12 +450,6 @@ itest!(_079_location_authentication {
output: "079_location_authentication.ts.out",
});
itest!(_080_deno_emit_permissions {
args: "run --unstable 080_deno_emit_permissions.ts",
output: "080_deno_emit_permissions.ts.out",
exit_code: 1,
});
itest!(_081_location_relative_fetch_redirect {
args: "run --location http://127.0.0.1:4546/ --allow-net 081_location_relative_fetch_redirect.ts",
output: "081_location_relative_fetch_redirect.ts.out",
@ -986,26 +980,6 @@ itest!(runtime_decorators {
output: "runtime_decorators.ts.out",
});
itest!(lib_dom_asynciterable {
args: "run --quiet --unstable --reload lib_dom_asynciterable.ts",
output: "lib_dom_asynciterable.ts.out",
});
itest!(lib_dom_extras {
args: "run --quiet --unstable --reload lib_dom_extras.ts",
output: "lib_dom_extras.ts.out",
});
itest!(lib_ref {
args: "run --quiet --unstable --reload lib_ref.ts",
output: "lib_ref.ts.out",
});
itest!(lib_runtime_api {
args: "run --quiet --unstable --reload lib_runtime_api.ts",
output: "lib_runtime_api.ts.out",
});
itest!(seed_random {
args: "run --seed=100 seed_random.js",
output: "seed_random.js.out",
@ -2417,24 +2391,6 @@ itest!(eval_context_throw_dom_exception {
envs: vec![("DENO_FUTURE_CHECK".to_string(), "1".to_string())],
});
#[test]
fn issue12453() {
let _g = util::http_server();
let deno_dir = util::new_deno_dir();
let mut deno_cmd = util::deno_cmd_with_deno_dir(&deno_dir);
let status = deno_cmd
.current_dir(util::testdata_path())
.arg("run")
.arg("--unstable")
.arg("--allow-net")
.arg("issue12453.js")
.spawn()
.unwrap()
.wait()
.unwrap();
assert!(status.success());
}
/// Regression test for https://github.com/denoland/deno/issues/12740.
#[test]
fn issue12740() {

View file

@ -1 +0,0 @@
await Deno.emit(new URL("001_hello.js", import.meta.url).href);

View file

@ -1,2 +0,0 @@
[WILDCARD]error: Uncaught (in promise) PermissionDenied: Requires read access to "[WILDCARD]001_hello.js", run again with the --allow-read flag
[WILDCARD]

View file

@ -1,664 +0,0 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import {
assert,
assertEquals,
assertRejects,
assertStringIncludes,
} from "../../../test_util/std/testing/asserts.ts";
Deno.test({
name: "Deno.emit() - sources provided",
async fn() {
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"/foo.ts",
{
sources: {
"/foo.ts": `import * as bar from "./bar.ts";\n\nconsole.log(bar);\n`,
"/bar.ts": `export const bar = "bar";\n`,
},
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
const keys = Object.keys(files).sort();
assert(keys[0].endsWith("/bar.ts.js"));
assert(keys[1].endsWith("/bar.ts.js.map"));
assert(keys[2].endsWith("/foo.ts.js"));
assert(keys[3].endsWith("/foo.ts.js.map"));
},
});
Deno.test({
name: "Deno.emit() - no sources provided",
async fn() {
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"./subdir/mod1.ts",
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
const keys = Object.keys(files).sort();
assertEquals(keys.length, 6);
assert(keys[0].endsWith("subdir/mod1.ts.js"));
assert(keys[1].endsWith("subdir/mod1.ts.js.map"));
},
});
Deno.test({
name: "Deno.emit() - data url",
async fn() {
const data =
"data:application/javascript;base64,Y29uc29sZS5sb2coImhlbGxvIHdvcmxkIik7";
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(data);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 0);
const keys = Object.keys(files);
assertEquals(keys.length, 1);
assertEquals(keys[0], data);
assertStringIncludes(files[keys[0]], 'console.log("hello world");');
},
});
Deno.test({
name: "Deno.emit() - compiler options effects emit",
async fn() {
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"/foo.ts",
{
compilerOptions: {
module: "amd",
sourceMap: false,
},
sources: { "/foo.ts": `export const foo = "foo";` },
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
const keys = Object.keys(files);
assertEquals(keys.length, 1);
const key = keys[0];
assert(key.endsWith("/foo.ts.js"));
assert(files[key].startsWith("define("));
},
});
Deno.test({
name: "Deno.emit() - pass lib in compiler options",
async fn() {
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"file:///foo.ts",
{
compilerOptions: {
lib: ["dom", "es2018", "deno.ns"],
},
sources: {
"file:///foo.ts": `console.log(document.getElementById("foo"));
console.log(Deno.args);`,
},
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
const keys = Object.keys(files).sort();
assertEquals(keys, ["file:///foo.ts.js", "file:///foo.ts.js.map"]);
},
});
Deno.test({
name: "Deno.emit() - type references can be loaded",
async fn() {
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"file:///a.ts",
{
sources: {
"file:///a.ts": `/// <reference types="./b.d.ts" />
const b = new B();
console.log(b.b);`,
"file:///b.d.ts": `declare class B {
b: string;
}`,
},
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
const keys = Object.keys(files).sort();
assertEquals(keys, ["file:///a.ts.js", "file:///a.ts.js.map"]);
},
});
Deno.test({
name: "Deno.emit() - compilerOptions.types",
async fn() {
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"file:///a.ts",
{
compilerOptions: {
types: ["file:///b.d.ts"],
},
sources: {
"file:///a.ts": `const b = new B();
console.log(b.b);`,
"file:///b.d.ts": `declare class B {
b: string;
}`,
},
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
const keys = Object.keys(files).sort();
assertEquals(keys, ["file:///a.ts.js", "file:///a.ts.js.map"]);
},
});
Deno.test({
name: "Deno.emit() - import maps",
async fn() {
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"file:///a.ts",
{
importMap: {
imports: {
"b": "./b.ts",
},
},
importMapPath: "file:///import-map.json",
sources: {
"file:///a.ts": `import * as b from "b"
console.log(b);`,
"file:///b.ts": `export const b = "b";`,
},
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
const keys = Object.keys(files).sort();
assertEquals(
keys,
[
"file:///a.ts.js",
"file:///a.ts.js.map",
"file:///b.ts.js",
"file:///b.ts.js.map",
],
);
},
});
Deno.test({
name: "Deno.emit() - allowSyntheticDefaultImports true by default",
async fn() {
const { diagnostics, files, ignoredOptions } = await Deno.emit(
"file:///a.ts",
{
sources: {
"file:///a.ts": `import b from "./b.js";\n`,
"file:///b.js":
`/// <reference types="./b.d.ts";\n\nconst b = "b";\n\nexport default b;\n`,
"file:///b.d.ts": `declare const b: "b";\nexport = b;\n`,
},
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
const keys = Object.keys(files).sort();
assertEquals(keys, [
"file:///a.ts.js",
"file:///a.ts.js.map",
"file:///b.js",
]);
},
});
Deno.test({
name: "Deno.emit() - no check",
async fn() {
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"/foo.ts",
{
check: false,
sources: {
"/foo.ts": `export enum Foo { Foo, Bar, Baz };\n`,
},
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 3);
const keys = Object.keys(files).sort();
assert(keys[0].endsWith("/foo.ts.js"));
assert(keys[1].endsWith("/foo.ts.js.map"));
assert(files[keys[0]].startsWith("export var Foo;"));
},
});
Deno.test({
name: "Deno.emit() - no check - config effects emit",
async fn() {
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"/foo.ts",
{
check: false,
compilerOptions: { removeComments: true },
sources: {
"/foo.ts":
`/** This is JSDoc */\nexport enum Foo { Foo, Bar, Baz };\n`,
},
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 3);
const keys = Object.keys(files).sort();
assert(keys[0].endsWith("/foo.ts.js"));
assert(keys[1].endsWith("/foo.ts.js.map"));
assert(!files[keys[0]].includes("This is JSDoc"));
},
});
Deno.test({
name: "Deno.emit() - bundle as module script - with sources",
async fn() {
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"/foo.ts",
{
bundle: "module",
sources: {
"/foo.ts": `export * from "./bar.ts";\n`,
"/bar.ts": `export const bar = "bar";\n`,
},
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
assertEquals(
Object.keys(files).sort(),
["deno:///bundle.js", "deno:///bundle.js.map"].sort(),
);
assert(files["deno:///bundle.js"].includes(`const bar = "bar"`));
},
});
Deno.test({
name: "Deno.emit() - bundle as module script - no sources",
async fn() {
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"./subdir/mod1.ts",
{
bundle: "module",
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 12);
assertEquals(
Object.keys(files).sort(),
["deno:///bundle.js", "deno:///bundle.js.map"].sort(),
);
assert(files["deno:///bundle.js"].length);
},
});
Deno.test({
name: "Deno.emit() - bundle as module script - include js modules",
async fn() {
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"/foo.js",
{
bundle: "module",
sources: {
"/foo.js": `export * from "./bar.js";\n`,
"/bar.js": `export const bar = "bar";\n`,
},
},
);
assertEquals(diagnostics.length, 0);
assert(!ignoredOptions);
assertEquals(stats.length, 0);
assertEquals(
Object.keys(files).sort(),
["deno:///bundle.js.map", "deno:///bundle.js"].sort(),
);
assert(files["deno:///bundle.js"].includes(`const bar = "bar"`));
},
});
Deno.test({
name: "Deno.emit() - generates diagnostics",
async fn() {
const { diagnostics, files } = await Deno.emit(
"/foo.ts",
{
sources: {
"/foo.ts": `document.getElementById("foo");`,
},
},
);
assertEquals(diagnostics.length, 1);
const keys = Object.keys(files).sort();
assert(keys[0].endsWith("/foo.ts.js"));
assert(keys[1].endsWith("/foo.ts.js.map"));
},
});
// See https://github.com/denoland/deno/issues/6908
Deno.test({
name: "Deno.emit() - invalid syntax does not panic",
async fn() {
const { diagnostics } = await Deno.emit("/main.js", {
sources: {
"/main.js": `
export class Foo {
constructor() {
console.log("foo");
}
export get() {
console.log("bar");
}
}`,
},
});
assertEquals(diagnostics.length, 1);
assert(
diagnostics[0].messageText!.startsWith(
"The module's source code could not be parsed: Unexpected token `get`. Expected * for generator, private key, identifier or async at file:",
),
);
},
});
Deno.test({
name: 'Deno.emit() - allows setting of "importsNotUsedAsValues"',
async fn() {
const { diagnostics } = await Deno.emit("/a.ts", {
sources: {
"/a.ts": `import { B } from "./b.ts";
const b: B = { b: "b" };`,
"/b.ts": `export interface B {
b:string;
};`,
},
compilerOptions: {
importsNotUsedAsValues: "error",
},
});
assert(diagnostics);
assertEquals(diagnostics.length, 1);
assert(diagnostics[0].messageText);
assert(diagnostics[0].messageText.includes("This import is never used"));
},
});
Deno.test({
name: "Deno.emit() - Unknown media type does not panic",
async fn() {
await Deno.emit("https://example.com/foo", {
sources: {
"https://example.com/foo": `let foo: string = "foo";`,
},
});
},
});
Deno.test({
name: "Deno.emit() - non-normalized specifier and source can compile",
async fn() {
const specifier = "https://example.com/foo//bar.ts";
const { files } = await Deno.emit(specifier, {
sources: {
[specifier]: `export let foo: string = "foo";`,
},
});
assertEquals(files[`${specifier}.js`], 'export let foo = "foo";\n');
assert(typeof files[`${specifier}.js.map`] === "string");
},
});
Deno.test({
name: `Deno.emit() - bundle as classic script iife`,
async fn() {
const { diagnostics, files } = await Deno.emit("/a.ts", {
bundle: "classic",
sources: {
"/a.ts": `import { b } from "./b.ts";
console.log(b);`,
"/b.ts": `export const b = "b";`,
},
});
const ignoreDirecives = [
"// deno-fmt-ignore-file",
"// deno-lint-ignore-file",
"// This code was bundled using `deno bundle` and it's not recommended to edit it manually",
"",
"",
].join("\n");
assert(diagnostics);
assertEquals(diagnostics.length, 0);
assertEquals(Object.keys(files).length, 2);
assert(
files["deno:///bundle.js"].startsWith(
ignoreDirecives + "(function() {\n",
),
);
assert(files["deno:///bundle.js"].endsWith("})();\n"));
assert(files["deno:///bundle.js.map"]);
},
});
Deno.test({
name: `Deno.emit() - throws descriptive error when unable to load import map`,
async fn() {
await assertRejects(
async () => {
await Deno.emit("/a.ts", {
bundle: "classic",
sources: {
"/a.ts": `console.log("hello");`,
},
importMapPath: "file:///import_map_does_not_exist.json",
});
},
Error,
"Unable to load 'file:///import_map_does_not_exist.json' import map",
);
},
});
Deno.test({
name: `Deno.emit() - support source maps with bundle option`,
async fn() {
{
const { diagnostics, files } = await Deno.emit("/a.ts", {
bundle: "classic",
sources: {
"/a.ts": `import { b } from "./b.ts";
console.log(b);`,
"/b.ts": `export const b = "b";`,
},
compilerOptions: {
inlineSourceMap: true,
sourceMap: false,
},
});
assert(diagnostics);
assertEquals(diagnostics.length, 0);
assertEquals(Object.keys(files).length, 1);
assertStringIncludes(files["deno:///bundle.js"], "sourceMappingURL");
}
const { diagnostics, files } = await Deno.emit("/a.ts", {
bundle: "classic",
sources: {
"/a.ts": `import { b } from "./b.ts";
console.log(b);`,
"/b.ts": `export const b = "b";`,
},
});
assert(diagnostics);
assertEquals(diagnostics.length, 0);
assertEquals(Object.keys(files).length, 2);
assert(files["deno:///bundle.js"]);
assert(files["deno:///bundle.js.map"]);
},
});
Deno.test({
name: `Deno.emit() - graph errors as diagnostics`,
ignore: Deno.build.os === "windows",
async fn() {
const { diagnostics } = await Deno.emit("/a.ts", {
sources: {
"/a.ts": `import { b } from "./b.ts";
console.log(b);`,
},
});
assert(diagnostics);
assertEquals(diagnostics, [
{
category: 1,
code: 2305,
start: { line: 0, character: 9 },
end: { line: 0, character: 10 },
messageText:
`Module '"deno:///missing_dependency.d.ts"' has no exported member 'b'.`,
messageChain: null,
source: null,
sourceLine: 'import { b } from "./b.ts";',
fileName: "file:///a.ts",
relatedInformation: null,
},
{
category: 1,
code: 900001,
start: null,
end: null,
messageText: 'Module not found "file:///b.ts".',
messageChain: null,
source: null,
sourceLine: null,
fileName: "file:///b.ts",
relatedInformation: null,
},
]);
assert(
Deno.formatDiagnostics(diagnostics).includes(
'Module not found "file:///b.ts".',
),
);
},
});
Deno.test({
name: "Deno.emit() - no check respects inlineSources compiler option",
async fn() {
const { files } = await Deno.emit(
"file:///a.ts",
{
check: false,
compilerOptions: {
types: ["file:///b.d.ts"],
inlineSources: true,
},
sources: {
"file:///a.ts": `const b = new B();
console.log(b.b);`,
"file:///b.d.ts": `declare class B {
b: string;
}`,
},
},
);
const sourceMap: { sourcesContent?: string[] } = JSON.parse(
files["file:///a.ts.js.map"],
);
assert(sourceMap.sourcesContent);
assertEquals(sourceMap.sourcesContent.length, 1);
},
});
Deno.test({
name: "Deno.emit() - JSX import source pragma",
async fn() {
const { files } = await Deno.emit(
"file:///a.tsx",
{
sources: {
"file:///a.tsx": `/** @jsxImportSource https://example.com/jsx */
export function App() {
return (
<div><></></div>
);
}`,
"https://example.com/jsx/jsx-runtime": `export function jsx(
_type,
_props,
_key,
_source,
_self,
) {}
export const jsxs = jsx;
export const jsxDEV = jsx;
export const Fragment = Symbol("Fragment");
console.log("imported", import.meta.url);
`,
},
},
);
assert(files["file:///a.tsx.js"]);
assert(
files["file:///a.tsx.js"].startsWith(
`import { Fragment as _Fragment, jsx as _jsx } from "https://example.com/jsx/jsx-runtime";\n`,
),
);
},
});
Deno.test({
name: "Deno.emit() - JSX import source no pragma",
async fn() {
const { files } = await Deno.emit(
"file:///a.tsx",
{
compilerOptions: {
jsx: "react-jsx",
jsxImportSource: "https://example.com/jsx",
},
sources: {
"file:///a.tsx": `export function App() {
return (
<div><></></div>
);
}`,
"https://example.com/jsx/jsx-runtime": `export function jsx(
_type,
_props,
_key,
_source,
_self,
) {}
export const jsxs = jsx;
export const jsxDEV = jsx;
export const Fragment = Symbol("Fragment");
console.log("imported", import.meta.url);
`,
},
},
);
assert(files["file:///a.tsx.js"]);
assert(
files["file:///a.tsx.js"].startsWith(
`import { Fragment as _Fragment, jsx as _jsx } from "https://example.com/jsx/jsx-runtime";\n`,
),
);
},
});

View file

@ -1,4 +0,0 @@
const i = setInterval(() => {
Deno.emit("http://localhost:4545/subdir/mt_text_typescript.t1.ts");
clearInterval(i);
}, 1);

View file

@ -1,23 +0,0 @@
const { diagnostics, files } = await Deno.emit("/main.ts", {
compilerOptions: {
target: "esnext",
lib: ["esnext", "dom", "dom.iterable", "dom.asynciterable"],
},
sources: {
"/main.ts": `const rs = new ReadableStream<string>({
start(c) {
c.enqueue("hello");
c.enqueue("deno");
c.close();
}
});
for await (const s of rs) {
console.log("s");
}
`,
},
});
console.log(diagnostics);
console.log(Object.keys(files).sort());

View file

@ -1,2 +0,0 @@
[]
[ "[WILDCARD]/main.ts.js", "[WILDCARD]/main.ts.js.map" ]

View file

@ -1,17 +0,0 @@
const { diagnostics, files } = await Deno.emit("/main.ts", {
compilerOptions: {
target: "esnext",
lib: ["esnext", "dom"],
},
sources: {
"/main.ts": `const as = new AbortSignal();
console.log(as.reason);
const up = new URLPattern("https://example.com/books/:id");
console.log(up);
`,
},
});
console.log(diagnostics);
console.log(Object.keys(files).sort());

View file

@ -1,2 +0,0 @@
[]
[ "[WILDCARD]/main.ts.js", "[WILDCARD]/main.ts.js.map" ]

View file

@ -1,16 +0,0 @@
const { diagnostics, files } = await Deno.emit(
"/main.ts",
{
sources: {
"/main.ts":
`/// <reference lib="dom" />\n\ndocument.getElementById("foo");\nDeno.args;`,
},
compilerOptions: {
target: "es2018",
lib: ["es2018", "deno.ns"],
},
},
);
console.log(diagnostics);
console.log(Object.keys(files).sort());

View file

@ -1,2 +0,0 @@
[]
[ "file:///[WILDCARD]main.ts.js", "file:///[WILDCARD]main.ts.js.map" ]

View file

@ -1,14 +0,0 @@
const { diagnostics, files } = await Deno.emit(
"/main.ts",
{
sources: {
"/main.ts": `document.getElementById("foo");`,
},
compilerOptions: {
lib: ["dom", "esnext"],
},
},
);
console.log(diagnostics);
console.log(Object.keys(files).sort());

View file

@ -1,2 +0,0 @@
[]
[ "file:///[WILDCARD]main.ts.js", "file:///[WILDCARD]main.ts.js.map" ]

View file

@ -1,12 +0,0 @@
const { files } = await Deno.emit("/mod.ts", {
bundle: "classic",
sources: {
"/mod.ts": `import { hello } from "/hello.ts"; console.log(hello);`,
"/hello.ts": `export const hello: string = "Hello, Compiler API!"`,
},
compilerOptions: {
sourceMap: false,
},
});
eval(files["deno:///bundle.js"]);

View file

@ -1,2 +1,2 @@
[Function: query]
[Function: emit]
[Function: setRaw]

View file

@ -1,5 +1,5 @@
console.log(Deno.permissions.query);
console.log(Deno.emit);
console.log(Deno.setRaw);
self.onmessage = () => {
self.close();
};

View file

@ -1,39 +0,0 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { assert } from "./test_util.ts";
Deno.test(function formatDiagnosticBasic() {
const fixture: Deno.Diagnostic[] = [
{
start: {
line: 0,
character: 0,
},
end: {
line: 0,
character: 7,
},
fileName: "test.ts",
messageText:
"Cannot find name 'console'. Do you need to change your target library? Try changing the `lib` compiler option to include 'dom'.",
sourceLine: `console.log("a");`,
category: 1,
code: 2584,
},
];
const out = Deno.formatDiagnostics(fixture);
assert(out.includes("Cannot find name"));
assert(out.includes("test.ts"));
});
Deno.test(function formatDiagnosticError() {
let thrown = false;
// deno-lint-ignore no-explicit-any
const bad = ([{ hello: 123 }] as any) as Deno.Diagnostic[];
try {
Deno.formatDiagnostics(bad);
} catch (e) {
assert(e instanceof Deno.errors.InvalidData);
thrown = true;
}
assert(thrown);
});

View file

@ -1,97 +0,0 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// @ts-check
// This file contains the runtime APIs which will dispatch work to the internal
// compiler within Deno.
"use strict";
((window) => {
const core = window.Deno.core;
const util = window.__bootstrap.util;
const {
StringPrototypeMatch,
PromiseReject,
TypeError,
} = window.__bootstrap.primordials;
/**
* @typedef {object} ImportMap
* @property {Record<string, string>} imports
* @property {Record<string, Record<string, string>>=} scopes
*/
/**
* @typedef {object} OpEmitRequest
* @property {"module" | "classic"=} bundle
* @property {boolean=} check
* @property {Record<string, any>=} compilerOptions
* @property {ImportMap=} importMap
* @property {string=} importMapPath
* @property {string} rootSpecifier
* @property {Record<string, string>=} sources
*/
/**
* @typedef OpEmitResponse
* @property {any[]} diagnostics
* @property {Record<string, string>} files
* @property {string[]=} ignoredOptions
* @property {Array<[string, number]>} stats
*/
/**
* @param {OpEmitRequest} request
* @returns {Promise<OpEmitResponse>}
*/
function opEmit(request) {
return core.opAsync("op_emit", request);
}
/**
* @param {string} specifier
* @returns {string}
*/
function checkRelative(specifier) {
return StringPrototypeMatch(
specifier,
/^([\.\/\\]|https?:\/{2}|file:\/{2}|data:)/,
)
? specifier
: `./${specifier}`;
}
/**
* @typedef {object} EmitOptions
* @property {"module" | "classic"=} bundle
* @property {boolean=} check
* @property {Record<string, any>=} compilerOptions
* @property {ImportMap=} importMap
* @property {string=} importMapPath
* @property {Record<string, string>=} sources
*/
/**
* @param {string | URL} rootSpecifier
* @param {EmitOptions=} options
* @returns {Promise<OpEmitResponse>}
*/
function emit(rootSpecifier, options = {}) {
util.log(`Deno.emit`, { rootSpecifier });
if (!rootSpecifier) {
return PromiseReject(
new TypeError("A root specifier must be supplied."),
);
}
if (!(typeof rootSpecifier === "string")) {
rootSpecifier = rootSpecifier.toString();
}
if (!options.sources) {
rootSpecifier = checkRelative(rootSpecifier);
}
return opEmit({ rootSpecifier, ...options });
}
window.__bootstrap.compilerApi = {
emit,
};
})(this);

View file

@ -1,14 +0,0 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
"use strict";
((window) => {
const core = window.Deno.core;
function opFormatDiagnostics(diagnostics) {
return core.opSync("op_format_diagnostic", diagnostics);
}
window.__bootstrap.errorStack = {
opFormatDiagnostics,
};
})(this);

View file

@ -116,7 +116,6 @@
};
__bootstrap.denoNsUnstable = {
emit: __bootstrap.compilerApi.emit,
setRaw: __bootstrap.tty.setRaw,
consoleSize: __bootstrap.tty.consoleSize,
DiagnosticCategory: __bootstrap.diagnostics.DiagnosticCategory,
@ -126,7 +125,6 @@
systemMemoryInfo: __bootstrap.os.systemMemoryInfo,
networkInterfaces: __bootstrap.os.networkInterfaces,
getUid: __bootstrap.os.getUid,
formatDiagnostics: __bootstrap.errorStack.opFormatDiagnostics,
sleepSync: __bootstrap.timers.sleepSync,
listen: __bootstrap.netUnstable.listen,
connect: __bootstrap.netUnstable.connect,