Transpile and bundle JavaScript and TypeScript in Deno and Deno Deploy.
This is an unstable module, where the API is likely to change over time.
See js/README.md.
Copyright 2018-2024 the Deno authors. All rights reserved. MIT License.
Transpile and bundle JavaScript and TypeScript under Deno and Deno Deploy
Home Page: https://deno.land/x/emit
License: MIT License
Transpile and bundle JavaScript and TypeScript in Deno and Deno Deploy.
This is an unstable module, where the API is likely to change over time.
See js/README.md.
Copyright 2018-2024 the Deno authors. All rights reserved. MIT License.
Deno.emit had a property to pass libs used in the compilation
await Deno.emit(appPath, { bundle: 'module', compilerOptions: {
lib: ['esnext', 'dom'],
} });
This new userland module does not have a similar property on its CompilerOptions
.
Deno.emit supported a bundle: 'module' mode, where the files returned returned 'deno:///bundle.js
with the bundled code.
Its confusing that emit
is not a drop in replacement. It looks like bundle
is closer to this mode though, perhaps rename emit
to avoid people thinking it supports the same contract?
Do you have any examples about migrating from importMap
/importMapPath
to imports
?
Consider the following
const {files} = await Deno.emit(`data:application/javascript;base64,${encode(typescript)}`, {
bundle: "classic",
importMapPath: "imports.json",
importMap: {imports: {"@project/": "/my/custom/path/"}},
})
Tried the following with x/emit
based on typing signature:
const {code} = await bundle(`data:application/javascript;base64,${encode(typescript)}`, {
type: "classic",
imports: {"@project/": ["/my/custom/path/"]},
})
But it results in the following:
Caused by: RuntimeError: memory access out of bounds
at dlmalloc::dlmalloc::Chunk::size::h21e6256dacef1d17 (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:3790959)
at __rdl_realloc (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:1391601)
at __rust_realloc (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:3716800)
at __wbindgen_realloc (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:3586796)
at passStringToWasm0 (https://deno.land/x/[email protected]/lib/deno_emit.generated.js:92:11)
at bundle (https://deno.land/x/[email protected]/lib/deno_emit.generated.js:175:50)
at bundle (https://deno.land/x/[email protected]/mod.ts:83:10)
at async A.#deno (file:///workspaces/itsudeno3/core/components/connection.ts:117:30)
at async A.deno (file:///workspaces/itsudeno3/core/components/connection.ts:76:28)
at async Object.<anonymous> (file:///workspaces/itsudeno3/builtin/connection/local/mod_test.ts:23:35)
With the following example:
// example.ts
import { emit } from "https://deno.land/x/[email protected]/mod.ts";
const url = new URL("./example.ts", import.meta.url);
const result = await emit(url.href);
console.log(result);
If I try to run it with:
deno run --no-prompt emit_example.ts
or if I set:
export DENO_NO_PROMPT=1
Deno still prompts for permissions. This happens with Deno 1.22.0 🦖. I test this with a simple example that just Deno.readFileSync
and the flags work fine there, so it seems specific to Deno Emit.
At the moment, Deno.emit
outputs messy code, that is not in the same format that Deno fmt
produces.
Either by default, or as a new option, it would be great if there was a way to get Deno.emit
to emit formatted code that is accounted for in the source maps that are also emitted.
Until this enhancement is implemented a workaround is a bit tricky. Unfortunately there is not a runtime API yet for fmt
, see: denoland/deno#10731 . So you would need to use the fmt
CLI somehow in a child process? That's something I haven't done yet in Deno. Would there be a way to get the source map to include the changes made by the fmt
CLI?
When you transpile the code which include Deno's unstable APIs, you need to specify "deno.unstable" lib in compilerOptions.
await Deno.emit("https://deno.land/posts/v1.7/dig.ts", {
compilerOptions: {
lib: ["deno.unstable", "deno.window"],
}
});
This could be hard for the users to figure out. So I suggest we should have the option for it.
await Deno.emit("https://deno.land/posts/v1.7/dig.ts", {
unstable: true // => enables unstable APIs of Deno
});
ref #8800
The old Deno.emit
allowed us to pass the source code as a string by doing something like this:
await Deno.emit("/src.ts", {
sources: { "/src.ts": sourceCode },
compilerOptions: { target: "es6" },
});
This seems no longer possible.
The current workaround would be to write the source to a temporary file and to pass the file path, so that it is read in again.
Now using Deno.emit to compile user submitted code that also gets executed could also pose a security issue when allowed to import any http resource. It would be nice if we could have a whitelist of domains that we can pass to the Deno.emit function to secure this.
Originally posted by @EliteScientist in denoland/deno#9866 (comment)
Would love to be able to provide a custom resolve function, much like load
currently.
root.ts
import dateFnsTz from 'https://cdn.skypack.dev/[email protected]?dts';
console.log(dateFnsTz);
repro.ts
import { bundle } from 'https://deno.land/x/[email protected]/mod.ts';
const root = `file:///path/to/root.ts`;
const result = await bundle(root);
console.log(result);
This worked fine under the old Deno.emit(root, { bundle: 'module' })
error: Uncaught (in promise) Error: load_transformed failed
const ret = new Error(getStringFromWasm0(arg0, arg1));
^
at __wbg_new_3047bf4b4f02b802 (https://deno.land/x/[email protected]/lib/deno_emit.generated.js:266:19)
at js_sys::Error::new::h99ffaa64b8859860 (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:3791529)
at <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::h026cccf65c2b3db8 (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:695550)
at <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::h79fa8f0207962e80 (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:2651040)
at <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::hc21402dacb8e890d (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:2152909)
at wasm_bindgen_futures::queue::Queue::new::{{closure}}::h8b5a56bdc387819e (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:2055713)
at <dyn core::ops::function::FnMut<(A,)>+Output = R as wasm_bindgen::closure::WasmClosure>::describe::invoke::h28c77ba1afeb2b6a (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:3655458)
at __wbg_adapter_14 (https://deno.land/x/[email protected]/lib/deno_emit.generated.js:144:6)
at real (https://deno.land/x/[email protected]/lib/deno_emit.generated.js:128:14)
The following code does not work as expected:
const { diagnostics, files, ignoredOptions, stats } = await Deno.emit(
"/foo.ts",
{
check: false,
compilerOptions: {
module: "amd",
sourceMap: false,
},
sources: { "/foo.ts": `export const foo = "foo";` },
},
);
It simply outputs and ESM module. Where as if check
is true
it will output an AMD module.
If swc doesn't support AMD, CommonJS, System and UMD we should throw here with invalid options, if it does, we should implement the functionality.
Deno version: 1.11.3
AFAIK we should report panics - found by myself adding a typo
// tmp.ts
const { diagnostics, files } = await Deno.emit("./tmp2.ts", {
bundle: "module"
})
console.log(diagnostics)
console.log(files)
// tmp2.ts
import { name } from "./tmp3.ts.ts" // <-- Because of this! the file doesnt exist
function send () {
console.log(name)
}
// tmp3.ts
export const name = "hello"
$ deno run -A --unstable tmp.ts
thread 'main' panicked at 'Error 'Unable to output bundle during Graph::bundle().' contains boxed error of unknown type:
Error { context: "Unable to output bundle during Graph::bundle().", source: load_transformed failed
Caused by:
0: failed to analyze module
2: The graph is missing a dependency.
Specifier: file:///D:/Development/ebebbington/destiny/node-usage/tmp3.ts.ts from file:///D:/Development/ebebbington/destiny/node-usage/tmp2.ts }
Error { context: "load_transformed failed", source: failed to analyze module
Caused by:
0: failed to resolve ./tmp3.ts.ts from <file:///D:/Development/ebebbington/destiny/node-usage/tmp2.ts>
1: The graph is missing a dependency.
Specifier: file:///D:/Development/ebebbington/destiny/node-usage/tmp3.ts.ts from file:///D:/Development/ebebbington/destiny/node-usage/tmp2.ts }
Error { context: "failed to analyze module", source: failed to resolve ./tmp3.ts.ts from <file:///D:/Development/ebebbington/destiny/node-usage/tmp2.ts>
Caused by:
The graph is missing a dependency.
Specifier: file:///D:/Development/ebebbington/destiny/node-usage/tmp3.ts.ts from file:///D:/Development/ebebbington/destiny/node-usage/tmp2.ts }
Error { context: "failed to resolve ./tmp3.ts.ts from <file:///D:/Development/ebebbington/destiny/node-usage/tmp2.ts>", source: The graph is missing a dependency.
Specifier: file:///D:/Development/ebebbington/destiny/node-usage/tmp3.ts.ts from file:///D:/Development/ebebbington/destiny/node-usage/tmp2.ts }
MissingDependency(Url { scheme: "file", cannot_be_a_base: false, username: "", password: None, host: None, port: None, path: "/D:/Development/ebebbington/destiny/node-usage/tmp2.ts", query: None, fragment: None }, "file:///D:/Development/ebebbington/destiny/node-usage/tmp3.ts.ts")', cli\errors.rs:32:7
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Bundler Code:
const url = new URL("./src/main/main.ts", import.meta.url);
const emited = await bundle(url.href);
await Deno.writeTextFile("./public/main.js", emited.code);
Code for Bundling:
console.log("Test?")
Versions 0.0.1 and 0.0.2 output bundles.
Version 0.1.0 outputs the following error:
error: Uncaught (in promise) Error: load_transformed failed
const ret = new Error(getStringFromWasm0(arg0, arg1));
^
at __wbg_new_3047bf4b4f02b802 (https://deno.land/x/[email protected]/lib/emit.generated.js:331:19)
at js_sys::Error::new::hd040cb16e3d44f50 (https://deno.land/x/[email protected]/lib/emit_bg.wasm:1:8603350)
at <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::h915169c188c0204d (https://deno.land/x/[email protected]/lib/emit_bg.wasm:1:1828345)
at wasm_bindgen_futures::task::singlethread::Task::run::h78ba7121ec820537 (https://deno.land/x/[email protected]/lib/emit_bg.wasm:1:8123409)
at wasm_bindgen_futures::queue::Queue::new::{{closure}}::h495bec1f71203164 (https://deno.land/x/[email protected]/lib/emit_bg.wasm:1:7394644)
at <dyn core::ops::function::FnMut<(A,)>+Output = R as wasm_bindgen::closure::WasmClosure>::describe::invoke::h54e735cb1ca738f9 (https://deno.land/x/[email protected]/lib/emit_bg.wasm:1:8585860)
at __wbg_adapter_16 (https://deno.land/x/[email protected]/lib/emit.generated.js:209:6)
at real (https://deno.land/x/[email protected]/lib/emit.generated.js:193:14)
Version 0.3.0 outputs the following error:
error: Uncaught (in promise) Error: Unable to output during bundling.
const ret = new Error(getStringFromWasm0(arg0, arg1));
^
at __wbg_new_651776e932b7e9c7 (https://deno.land/x/[email protected]/lib/emit.generated.js:329:19)
at <anonymous> (https://deno.land/x/[email protected]/lib/emit_bg.wasm:1:7194705)
at <anonymous> (https://deno.land/x/[email protected]/lib/emit_bg.wasm:1:1673383)
at <anonymous> (https://deno.land/x/[email protected]/lib/emit_bg.wasm:1:6371297)
at <anonymous> (https://deno.land/x/[email protected]/lib/emit_bg.wasm:1:7147137)
at __wbg_adapter_16 (https://deno.land/x/[email protected]/lib/emit.generated.js:205:6)
at real (https://deno.land/x/[email protected]/lib/emit.generated.js:189:14)
I'm not sure what's going on here?
It looks like, when you provide sources to Deno.emit
, specifiers that don't have a file extension break something. Consider the following example:
await Deno.emit('file:///index.js', {
bundle: 'module',
importMapPath: new URL('import_map.json', import.meta.url).toString(),
importMap: {
imports: {
"external/": "https://example.com/",
},
},
sources: {
"file:///index.js": "import ok from 'external/test';\nok();",
"https://example.com/test": "export default function ok() { console.log('ok'); }",
},
});
This breaks with the following error:
Error 'Unable to output during bundling.' contains boxed error of unknown type:
Error { context: "Unable to output during bundling.", source: load_transformed failed
Caused by:
0: failed to analyze module
1: failed to resolve external/test from file:///index.js
2: Cannot resolve "external/test" from "file:///index.js". }
Error { context: "load_transformed failed", source: failed to analyze module
Caused by:
0: failed to resolve external/test from file:///index.js
1: Cannot resolve "external/test" from "file:///index.js". }
Error { context: "failed to analyze module", source: failed to resolve external/test from file:///index.js
Caused by:
Cannot resolve "external/test" from "file:///index.js". }
Error { context: "failed to resolve external/test from file:///index.js", source: Cannot resolve "external/test" from "file:///index.js". }
"Cannot resolve \"external/test\" from \"file:///index.js\"."
error: Uncaught (in promise) Error: Unable to output during bundling.
However, if we add a .js
extension to the import and the external specifier, it works fine:
await Deno.emit('file:///index.js', {
bundle: 'module',
importMapPath: new URL('import_map.json', import.meta.url).toString(),
importMap: {
imports: {
"external/": "https://example.com/",
},
},
sources: {
"file:///index.js": "import ok from 'external/test.js';\nok();",
"https://example.com/test.js": "export default function ok() { console.log('ok'); }",
},
});
Output:
// deno-fmt-ignore-file
// deno-lint-ignore-file
// This code was bundled using `deno bundle` and it's not recommended to edit it manually
function ok() {
console.log('ok');
}
ok();
I know normally Deno is able to use the content-type
header to know what it's parsing. Maybe the sources
map could accept objects as values that contain the content type of the source, not just its contents. Something like:
interface Source {
kind?: 'js' | 'ts' | 'jsx' | 'tsx'; // or just let users specify a valid `content-type` directly.
content: string;
}
interface EmitOptions {
sources: Record<string, string | Source>;
}
Currently the module dynamically imports deno_cache
which means it isn't portable to Deno Deploy.
Apparently support for importMaps was removed from emit()
-- I'm not sure why. Ideally emit()
would (at least optionally) take an importMap and translate imports accordingly. However, if that's difficult or undesirable for any reason, at the least it would be really nice for it to just (again, at least optionally) ignore import strings like "react" and leave them untouched rather than breaking on them.
It appears inline source maps are included by default.
await bundle(rootSpecifier, { compilerOptions: { inlineSourceMap: false } });
or
await bundle(rootSpecifier, { compilerOptions: { sourceMap: false } });
or both
seem to have no effect.
Deno.emit returned diagnostics about build warnings/errors.
There is no analog to this in either emit
or bundle
Found by john.spurlock on Discord
Trying to bundle files through Deno.emit
that import JSON Modules via import assertions fails. However, the expected behavior should be for the JSON imports to be handled in some way at compilation. Here's an example project:
// mod.ts
import data from "./data.json" assert { type: "json" };
console.log(data);
// build.ts
console.log(await Deno.emit("./mod.ts"));
// data.json
{ "age": 5 }
The expected result is to be a JavaScript bundle that correctly handles for the JSON imports. However, it instead shows the following error in the diagnostics:
{
category: 1,
code: 7042,
start: { line: 0, character: 20 },
end: { line: 0, character: 36 },
messageText: "Module './package.json' was resolved to 'file:///____/data.json', but '--resolveJsonModule' is not used.",
messageChain: null,
source: null,
sourceLine: 'import { age } from "./data.json" assert { type: "json" };',
fileName: "file:///____/build.ts",
relatedInformation: null
}
I have this file which watches when src/View.tsx
changes.
To recreate clone the repo, run src/build.ts
and edit src/View.tsx
.
thread 'main' panicked at 'Error 'Unable to output bundle during Graph::bundle().' contains boxed error of unknown type:
Error { context: "Unable to output bundle during Graph::bundle().", source: load_transformed failed
Caused by:
0: failed to analyze module
1: failed to resolve https://esm.sh/react from <file:///home/luca/OneDrive/Skole/Programmering/RockPaperScissors/src/View.tsx>
2: The graph is missing a dependency.
Specifier: https://esm.sh/react from file:///home/luca/OneDrive/Skole/Programmering/RockPaperScissors/src/View.tsx }
Error { context: "load_transformed failed", source: failed to analyze module
Caused by:
0: failed to resolve https://esm.sh/react from <file:///home/luca/OneDrive/Skole/Programmering/RockPaperScissors/src/View.tsx>
1: The graph is missing a dependency.
Specifier: https://esm.sh/react from file:///home/luca/OneDrive/Skole/Programmering/RockPaperScissors/src/View.tsx }
Error { context: "failed to analyze module", source: failed to resolve https://esm.sh/react from <file:///home/luca/OneDrive/Skole/Programmering/RockPaperScissors/src/View.tsx>
Caused by:
The graph is missing a dependency.
Specifier: https://esm.sh/react from file:///home/luca/OneDrive/Skole/Programmering/RockPaperScissors/src/View.tsx }
Error { context: "failed to resolve https://esm.sh/react from <file:///home/luca/OneDrive/Skole/Programmering/RockPaperScissors/src/View.tsx>", source: The graph is missing a dependency.
Specifier: https://esm.sh/react from file:///home/luca/OneDrive/Skole/Programmering/RockPaperScissors/src/View.tsx }
MissingDependency(Url { scheme: "file", cannot_be_a_base: false, username: "", password: None, host: None, port: None, path: "/home/luca/OneDrive/Skole/Programmering/RockPaperScissors/src/View.tsx", query: None, fragment: None }, "https://esm.sh/react")', cli/errors.rs:32:7
stack backtrace:
0: 0x559af2eaf860 - std::backtrace_rs::backtrace::libunwind::trace::h34055254b57d8e79
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/../../backtrace/src/backtrace/libunwind.rs:90:5
1: 0x559af2eaf860 - std::backtrace_rs::backtrace::trace_unsynchronized::h8f1e3fbd9afff6ec
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
2: 0x559af2eaf860 - std::sys_common::backtrace::_print_fmt::h3a99a796b770c360
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/sys_common/backtrace.rs:67:5
3: 0x559af2eaf860 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h32d1f94a80615d18
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/sys_common/backtrace.rs:46:22
4: 0x559af2eda76c - core::fmt::write::h306731c068f7162c
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/fmt/mod.rs:1110:17
5: 0x559af2ea6f65 - std::io::Write::write_fmt::hd2fa90334eee2a21
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/io/mod.rs:1588:15
6: 0x559af2eb206b - std::sys_common::backtrace::_print::h5abaa2601a852287
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/sys_common/backtrace.rs:49:5
7: 0x559af2eb206b - std::sys_common::backtrace::print::h8d81445442bb638f
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/sys_common/backtrace.rs:36:9
8: 0x559af2eb206b - std::panicking::default_hook::{{closure}}::hcfe804496a9fa747
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/panicking.rs:208:50
9: 0x559af2eb1b41 - std::panicking::default_hook::hbea8e3ccf2ba8901
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/panicking.rs:225:9
10: 0x559af2eb28a4 - std::panicking::rust_panic_with_hook::h7ee9e1a2d0f8975a
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/panicking.rs:622:17
11: 0x559af2eb2357 - std::panicking::begin_panic_handler::{{closure}}::h8ab3b4491718b2c7
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/panicking.rs:519:13
12: 0x559af2eafd5c - std::sys_common::backtrace::__rust_end_short_backtrace::hd489062ffa586a9f
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/sys_common/backtrace.rs:141:18
13: 0x559af2eb22b9 - rust_begin_unwind
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/panicking.rs:515:5
14: 0x559af2eb226b - std::panicking::begin_panic_fmt::h5479575e112541f8
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/panicking.rs:457:5
15: 0x559af3175d7f - deno::errors::get_error_class_name::{{closure}}::h555be96669dc0508
16: 0x559af3175c65 - deno::errors::get_error_class_name::hc53a468b6a7e0056
17: 0x559af3135b8c - deno_core::ops::serialize_op_result::ha3c91cca8892120d
18: 0x559af31334d7 - <futures_util::future::future::Map<Fut,F> as core::future::future::Future>::poll::h4f9ac6385426b605
19: 0x559af34b0ba1 - <futures_util::future::future::Inspect<Fut,F> as core::future::future::Future>::poll::h3afd9a55a48c9791
20: 0x559af3dc972c - futures_util::stream::stream::StreamExt::poll_next_unpin::h6cddc911e30b59f8
21: 0x559af3dfc613 - deno_core::runtime::JsRuntime::poll_event_loop::h56ad98eb21fe2a48
22: 0x559af30f199c - <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::h70b464e1da7dba08
23: 0x559af30ce0b8 - <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::h1867f44bc5d05bdf
24: 0x559af321070a - deno::run_command::{{closure}}::h8bffcf42eeca1b16
25: 0x559af30ec98a - <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::h66f6b40344212a88
26: 0x559af312dbab - <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::hfc82e3dd74d84ae7
27: 0x559af3214ac0 - deno::main::hdf4c5c410d3bcbba
28: 0x559af2f6e808 - std::sys_common::backtrace::__rust_begin_short_backtrace::h201585956898e09e
29: 0x559af2f7434a - std::rt::lang_start::{{closure}}::hb862bae09ce38319
30: 0x559af2eb2ea9 - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::h2aabc384aab89b7b
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/ops/function.rs:259:13
31: 0x559af2eb2ea9 - std::panicking::try::do_call::hc5fcacb7a85fc7b1
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/panicking.rs:401:40
32: 0x559af2eb2ea9 - std::panicking::try::hb5d9603af3abbe3a
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/panicking.rs:365:19
33: 0x559af2eb2ea9 - std::panic::catch_unwind::h98fe6ac3925e64b4
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/panic.rs:434:14
34: 0x559af2eb2ea9 - std::rt::lang_start_internal::h22ac7383c516f93e
at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/rt.rs:34:21
35: 0x559af322ae63 - main
36: 0x7f130f3ffb25 - __libc_start_main
37: 0x559af20b102a - _start
38: 0x0 - <unknown>
I'm experimenting with running the wasm in a constrained environment, and it's very close to fitting, just need a small (~3 to 5%) reduction in the size of the wasm.
Would this be possible?
Thanks,
- John
Upgrade Deno version to 1.21.3
import { bundle } from 'https://deno.land/x/[email protected]/mod.ts';
fails (see below)
It should import successfully, even on downlevel versions where it may not be used at runtime.
error: TS2741 [ERROR]: Property 'imports' is missing in type '{}' but required in type 'BundleOptions'.
options: BundleOptions = {},
~~~~~~~~~~~~~~~~~~~~~~~~~~~
at https://deno.land/x/[email protected]/mod.ts:60:3
'imports' is declared here.
imports: Record<string, string[]>;
~~~~~~~
at https://deno.land/x/[email protected]/mod.ts:28:3
TS2322 [ERROR]: Type '(arg1: string, arg2: boolean) => Promise<{ kind: string; specifier?: string | undefined; headers?: Record<string, string> | undefined; content?: string | undefined; }>' is not assignable to type '(specifier: string, isDynamic: boolean) => Promise<LoadResponse | undefined>'.
Type 'Promise<{ kind: string; specifier?: string | undefined; headers?: Record<string, string> | undefined; content?: string | undefined; }>' is not assignable to type 'Promise<LoadResponse | undefined>'.
Type '{ kind: string; specifier?: string | undefined; headers?: Record<string, string> | undefined; content?: string | undefined; }' is not assignable to type 'LoadResponse'.
Types of property 'specifier' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.
bundleLoad = async (arg1, arg2) => {
~~~~~~~~~~
at https://deno.land/x/[email protected]/mod.ts:78:5
TS2554 [ERROR]: Expected 1 arguments, but got 2.
const r = await cache.load(arg1, arg2);
~~~~
at https://deno.land/x/[email protected]/mod.ts:79:40
TS2554 [ERROR]: Expected 5 arguments, but got 4.
return jsBundle(root, bundleLoad, imports, undefined);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
at https://deno.land/x/[email protected]/mod.ts:83:10
An argument for 'maybe_compiler_options' was not provided.
maybe_compiler_options,
~~~~~~~~~~~~~~~~~~~~~~
at https://deno.land/x/[email protected]/lib/deno_emit.generated.js:167:3
TS7006 [ERROR]: Parameter 'arg1' implicitly has an 'any' type.
const emitLoad = async (arg1, arg2) => {
~~~~
at https://deno.land/x/[email protected]/mod.ts:101:27
TS7006 [ERROR]: Parameter 'arg2' implicitly has an 'any' type.
const emitLoad = async (arg1, arg2) => {
~~~~
at https://deno.land/x/[email protected]/mod.ts:101:33
TS2554 [ERROR]: Expected 1 arguments, but got 2.
const r = await cache.load(arg1, arg2);
~~~~
at https://deno.land/x/[email protected]/mod.ts:102:38
Found 7 errors.
Deno.emit
supported passing in /path/to/app.ts
as the root specifier
the new bundle
fails with:
error: Uncaught (in promise) Error: relative URL without a base
const ret = new Error(getStringFromWasm0(arg0, arg1));
^
at __wbg_new_3047bf4b4f02b802 (https://deno.land/x/[email protected]/lib/deno_emit.generated.js:266:19)
at js_sys::Error::new::h99ffaa64b8859860 (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:3791529)
at <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::hc61726627b994514 (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:1032534)
at <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::h969d4533cb1fa669 (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:2651228)
at <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::hfa8076c654709c85 (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:2153256)
at wasm_bindgen_futures::queue::Queue::new::{{closure}}::h8b5a56bdc387819e (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:2055713)
at <dyn core::ops::function::FnMut<(A,)>+Output = R as wasm_bindgen::closure::WasmClosure>::describe::invoke::h28c77ba1afeb2b6a (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:3655458)
at __wbg_adapter_14 (https://deno.land/x/[email protected]/lib/deno_emit.generated.js:144:6)
at real (https://deno.land/x/[email protected]/lib/deno_emit.generated.js:128:14)
Workaround seems to be prepending file://
I tested some patterns of custom scheme urls as input of Deno.emit, and I found the following 2 cause panic:
let id = 'custom://a.ts';
await Deno.emit(id, { sources: { [id]: "" } }); // panics with UnsupportedImportType
let id = 'custom://a//a.ts';
await Deno.emit(id, { sources: { [id]: "" } }); // => panics with AssertionError
(These types of urls are used in dext. ref: https://github.com/lucacasonato/dext.ts/blob/8483d19/src/plugins/dext.ts#L46 )
On the other hand, the following compiles without problem:
let id = 'custom://a/a.ts';
await Deno.emit(id, { sources: { [id]: "" } }); // => compiles
What do you think are the expected behaviors for the above 2 cases?
Using deno v1.8.3
Actual:
const { diagnostics, files, ignoredOptions } = await Deno.emit(
"/foo.ts",
{
bundle: "esm", // or iife
compilerOptions: { removeComments: false },
sources: {
"/foo.ts": `/** hello comment */\nconsole.log("hello");\n`,
},
},
);
console.log({ diagnostics, files, ignoredOptions });
Outputs
{
diagnostics: [],
files: { "deno:///bundle.js": 'console.log("hello");\n' },
ignoredOptions: null
}
Expected: Comments are preserved, similar to when bundle: "esm"
is removed:
{
diagnostics: [],
files: {
"file:///foo.ts.js.map": '{"version":3,"file":"","sourceRoot":"","sources":["file:///foo.ts"],"names":[],"mappings":";AAAA,oBA...',
"file:///foo.ts.js": '"use strict";\n/** hello comment */\nconsole.log("hello");\n'
},
ignoredOptions: null
}
There are 6 errors when type checking:
error: TS2322 [ERROR]: Type '(arg1: string, arg2: boolean) => Promise<{ kind: string; specifier?: string | undefined; headers?: Record<string, string> | undefined; content?: string | undefined; }>' is not assignable to type '(specifier: string, isDynamic: boolean) => Promise<LoadResponse | undefined>'.
Type 'Promise<{ kind: string; specifier?: string | undefined; headers?: Record<string, string> | undefined; content?: string | undefined; }>' is not assignable to type 'Promise<LoadResponse | undefined>'.
Type '{ kind: string; specifier?: string | undefined; headers?: Record<string, string> | undefined; content?: string | undefined; }' is not assignable to type 'LoadResponse'.
Types of property 'specifier' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.
bundleLoad = async (arg1, arg2) => {
~~~~~~~~~~
at file:///Users/kitsonk/github/deno_emit/mod.ts:82:5
TS2554 [ERROR]: Expected 1 arguments, but got 2.
const r = await cache.load(arg1, arg2);
~~~~
at file:///Users/kitsonk/github/deno_emit/mod.ts:83:40
TS2345 [ERROR]: Argument of type '((specifier: string, isDynamic: boolean) => Promise<LoadResponse | undefined>) | undefined' is not assignable to parameter of type 'Function'.
Type 'undefined' is not assignable to type 'Function'.
return jsBundle(root, bundleLoad, imports, undefined, compilerOptions);
~~~~~~~~~~
at file:///Users/kitsonk/github/deno_emit/mod.ts:87:25
TS7006 [ERROR]: Parameter 'arg1' implicitly has an 'any' type.
const emitLoad = async (arg1, arg2) => {
~~~~
at file:///Users/kitsonk/github/deno_emit/mod.ts:105:27
TS7006 [ERROR]: Parameter 'arg2' implicitly has an 'any' type.
const emitLoad = async (arg1, arg2) => {
~~~~
at file:///Users/kitsonk/github/deno_emit/mod.ts:105:33
TS2554 [ERROR]: Expected 1 arguments, but got 2.
const r = await cache.load(arg1, arg2);
~~~~
at file:///Users/kitsonk/github/deno_emit/mod.ts:106:38
Found 6 errors.
I'm trying to create a bundle for my frontend app with Deno.emit()
. I'm using a bunch of dependencies from jspm which I don't want to include into the bundle and let the browser import them instead. This would allow me to leverage the caching benefits coming from using a CDN.
swc_bundler
that's used under the hood in Deno.emit()
already supports it: https://github.com/swc-project/swc/blob/4e7723a7a0286f49124e7dc1ca10668ad6a237a9/bundler/src/bundler/mod.rs#L34 so it should be pretty straight-forward to add it to Deno. I'm also happy to take on this if you are happy with the proposal.
I was thinking about adding the external_modules
option to EmitOptions
that mimics one of swc_bundler
and accepts an array of absolutely resolved module specifiers but I think a better option could be a function that gets the module specifier in and tells if it should be external or not.
Maybe it can be the same load
function proposed here: denoland/deno#9866 but using some special return value indicating the module import should be left intact. What do you think?
I'll provide an example repo, attached as a zip file and inline files here for examination:
example-1.ts
:
import { assert } from "https://deno.land/[email protected]/testing/asserts.ts";
assert(true);
example-2-asserts.ts
:
export class AssertionError extends Error {
constructor(message: string) {
super(message);
this.name = "AssertionError";
}
}
/** Make an assertion, error will be thrown if `expr` does not have truthy value. */
export function assert(expr: unknown, msg = ""): asserts expr {
if (!expr) {
throw new AssertionError(msg);
}
}
example-2.ts
:
import { assert } from "./example-2-asserts.ts";
assert(true);
main.ts
:
import { assertStrictEquals } from "https://deno.land/[email protected]/testing/asserts.ts";
async function bundleModule(filePath: string): Promise<string> {
const emitResult = await Deno.emit(filePath, { bundle: "module" });
return emitResult.files["deno:///bundle.js"];
}
const filePaths = ["example-1.ts", "example-2.ts"];
const bundles = await Promise.all(
filePaths.map((filePath) => bundleModule(filePath)),
);
try {
assertStrictEquals(...bundles as [string, string]);
console.log("Bundles are identical");
} catch (ex) {
console.error(ex.message);
}
run
:
deno run --unstable --allow-net=deno.land --allow-read=. main.ts
I created two modules which import assert
and invoke it:
assert
from https://deno.land/[email protected]/testing/asserts.tsassert
function (and the AssertionError
class on which it depends)I then bundled them both using Deno.emit
and compared the bundles with assertStrictEquals
:
bundle-example % deno --version
deno 1.12.0 (release, x86_64-apple-darwin)
v8 9.2.230.14
typescript 4.3.2
bundle-example % ./run
Values are not strictly equal:
[Diff] Actual / Expected
- const ANSI_PATTERN = new RegExp([
- "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
- "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))",
- ].join("|"), "g");
- var DiffType;
- (function(DiffType1) {
- DiffType1["removed"] = "removed";
- DiffType1["common"] = "common";
- DiffType1["added"] = "added";
- })(DiffType || (DiffType = {
- }));
class AssertionError extends Error {
constructor(message){
super(message);
this.name = "AssertionError";
}
}
function assert(expr, msg = "") {
if (!expr) {
throw new AssertionError(msg);
}
}
assert(true);
The one using the remote import results in an unrelated regex and string enum being emitted.
I am trying to use Deno to write a full stack web application and one of the things that feels missing to me is the lack of ability of the Deno.emit()
or the CLI bundler to do any code splitting using dynamic imports. This results in huge bundle sizes for end users making the experience not that great. I believe this also has ramifications for servers too where cold start times are increased highly by having these huge bundles.
It would be nice if in a similar way to Webpack/Parcel that the Deno.emit()
functionality could resolve these dynamic imports and create separate bundle files for them and map these imports to the corresponding bundles in the entry point bundle file. Right now as a workaround I have to run multiple deno bundle
/Deno.emit()
, and the imports in my TypeScript code look like,
const something = await import("./something.bundle.js");
which is far from ideal.
We need to separate out the Wasm code (js_sys/wasm_bindgen stuff) from the rest of the rust types in the API we publish on crates.io. This will allow us to consume this crate in the CLI and provide an API for people to use with Rust.
I'm trying to use Deno Emit on a file in the current working directory:
// example.ts
import { emit } from "https://deno.land/x/[email protected]/mod.ts";
const url = new URL("./example.ts", import.meta.url);
const result = await emit(url.href);
console.log(result);
If I start this with --allow-read=.
it complains that I need full system --allow-read
. We should be able to use emit
on a file in the current working directory without requiring full system read access.
I used Deno 🦖 1.22.0
should it be?
Even accepting using 'emit' for the name of the repo as an umbrella term, it makes no sense to call the function for transpiling that.
Deno.emit used to typecheck modules by default when bundling.
The new bundle
does not, any TypeError
s occur only at runtime.
I'm not seeing an option for how to enable it, checkJs: true
in CompilerOptions
did not work
I saw in the README that this module does no typechecking during its bundling process. I would like to do the typechecking prior to bundling, and I do not want to make the deno CLI application a dependency. What library does the typechecking for the CLI?
The code to compile :
export class A {
static a = 'b'
}
Static member should be compiled into A.a = 'b'
if targeting lower versions.
The code calling Deno.emit:
#! /usr/bin/env deno run -A --unstable
const sources = {
'/foo.ts': `
export class A {
static a = 'b'
}
`
}
console.log(
'\nno bundling:\n',
(await Deno.emit('/foo.ts', { sources })).files['file:///foo.ts.js'],
'\nbundle:esm:\n',
(await Deno.emit('/foo.ts', { sources, bundle: 'esm' })).files[
'deno:///bundle.js'
],
'\nbundle:esm, target:es6:\n',
(
await Deno.emit('/foo.ts', {
sources,
bundle: 'esm',
compilerOptions: { target: 'es6' }
})
).files['deno:///bundle.js']
)
The output:
no bundling:
export class A {
}
A.a = 'b';
bundle:esm:
class A1 {
static a = 'b';
}
export { A1 as A };
bundle:esm, target:es6:
class A1 {
static a = 'b';
}
export { A1 as A };
The behavior of bundle:esm, target:es6
is incorrect, static a = 'b'
should be compiled into A.a = 'b'
, but actually not.
The following code currently outputs:
console.log(await Deno.emit("https://deno.land/[email protected]/testing/asserts.ts"));
{
// ...
files: {
"https://deno.land/[email protected]/testing/asserts.ts.js": 'import { bold, gray, green, red, stripColor, white } from "../fmt/colors.ts";\nimport { diff, DiffTyp...',
"https://deno.land/[email protected]/testing/asserts.ts.js.map": '{"version":3,"file":"asserts.js","sourceRoot":"","sources":["https://deno.land/[email protected]/testing/as...',
"https://deno.land/[email protected]/fmt/colors.ts.js": "const noColor = globalThis.Deno?.noColor ?? true;\nlet enabled = !noColor;\nexport function setColorEn...",
"https://deno.land/[email protected]/testing/_diff.ts.js.map": '{"version":3,"file":"_diff.js","sourceRoot":"","sources":["https://deno.land/[email protected]/testing/_dif...',
"https://deno.land/[email protected]/fmt/colors.ts.js.map": '{"version":3,"file":"colors.js","sourceRoot":"","sources":["https://deno.land/[email protected]/fmt/colors....',
"https://deno.land/[email protected]/testing/_diff.ts.js": 'export var DiffType;\n(function (DiffType) {\n DiffType["removed"] = "removed";\n DiffType["commo...'
},
/// ...
}
EmitResult::files
is an unstructured dictionary of files received from tsc. These come with magical specifiers pointing to real fs or network paths which don't contain them and are generally meaningless. Deno users have to jump through hoops to consume this and it's error prone for us to assume all of the expected sources are included.
It should instead output:
{
// ...
modules: [
{
specifier: "https://deno.land/[email protected]/testing/asserts.ts",
code: 'import { bold, gray, green, red, stripColor, white } from "../fmt/colors.ts";\nimport { diff, DiffTyp...',
map: '{"version":3,"file":"asserts.js","sourceRoot":"","sources":["https://deno.land/[email protected]/testing/as...',
},
{
specifier: "https://deno.land/[email protected]/fmt/colors.ts",
code: "const noColor = globalThis.Deno?.noColor ?? true;\nlet enabled = !noColor;\nexport function setColorEn...",
map: '{"version":3,"file":"colors.js","sourceRoot":"","sources":["https://deno.land/[email protected]/fmt/colors....',
},
{
specifier: "https://deno.land/[email protected]/testing/_diff.ts",
code: 'export var DiffType;\n(function (DiffType) {\n DiffType["removed"] = "removed";\n DiffType["commo...',
map: '{"version":3,"file":"_diff.js","sourceRoot":"","sources":["https://deno.land/[email protected]/testing/_dif...',
},
],
// ...
}
export interface EmitResult {
diagnostics: Diagnostic[];
modules: Array<
{
specifier: string;
code: string;
map: string | null;
declaration: string | null;
} | {
specifier: string;
error: string; // Permission errors can go here.
}
>;
ignoredOptions?: string[];
stats: Array<[string, number]>;
}
Having structured output should clearly demonstrate that the bundle capability should (once again) be a different function:
export interface EmitBundleOptions extends EmitOptions {
type: "module" | "classic";
}
export type EmitBundleResult = Omit<EmitResult, "modules"> & { code: string, map: string | null };
export function emitBundle(rootSpecifier: string | URL, options: EmitBundleOptions): Promise<EmitBundleResult>;
// Permission errors are just thrown, since the output is flattened.
Deno.compile()
does not convert local import URLs from .ts
extension to .js
extension, making it impossible to use inside browser and Node.js.
Run this file (deno run https://gist.githubusercontent.com/KSXGitHub/c94a9d7eb8e976f1126b4b9dfeba0497/raw/004a9ddb6872b9a2f05506f13f3efdfd6edd6d69/tmp.ts
).
.js
extension.// foo.js should look like this
export * from "./bar.js";
.d.ts
) should have no extension.// foo.d.ts should look like this
export * from "./bar";
All local import URLs are preserved, which is incorrect.
emit used to support sourceMaps. The new API is drastically more constrained than the prior, preventing me from generating mappable src.
Can the compiler options be re-exposed back into emit
?
Deno.emit
worked just fine with --allow-net --allow-read --allow-env
On 1.22 with this new userland module:
️Deno requests write access to "${HOME}/Library/Caches/deno". Run again with --allow-write to bypass this prompt.
Deno.emit
should make a best effort to capture all diagnostic messages when compiling.
Large numbers of Deno.emit
diagnostic messages result in a RangeError: Invalid string length
due to the resulting JSON string being too large.
main.js
import * as dep from "dep";
console.log(dep);
importMap.json
{
"imports": {
"dep": "https://jspm.dev/npm:joi@13!cjs"
}
}
Executing the following
Deno.emit('./main.js', {
compilerOptions: {
module: "commonjs",
sourceMap: false,
target: "es6",
checkJs: true
},
importMapPath: "importMap.json"
})
yields the RangeError
thread 'main' panicked at 'Error 'RangeError: Invalid string length
at JSON.stringify (<anonymous>)
at encodeJson (deno:core/core.js:191:20)
at Object.jsonOpSync (deno:core/core.js:234:21)
at exec (deno:cli/tsc/99_main_compiler.js:508:10)
at [native_code]:1:12' contains boxed error of unknown type:
JsError { message: "Uncaught RangeError: Invalid string length", source_line: Some(" const s = JSON.stringify(args);"), script_resource_name: Some("deno:core/core.js"), line_number: Some(191), start_column: Some(19), end_column: Some(20), frames: [], stack: Some("RangeError: Invalid string length\n at JSON.stringify (<anonymous>)\n at encodeJson (deno:core/core.js:191:20)\n at Object.jsonOpSync (deno:core/core.js:234:21)\n at exec (deno:cli/tsc/99_main_compiler.js:508:10)\n at [native_code]:1:12") }', cli/errors.rs:35:7
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Changing checkJs: false
results in no errors
Currently the compiler options are ignored (or not available). They need to be passed to Rust and affect the emit just like with CLI.
What's the roadmap for adding the other APIs which were removed in Deno v1.22.0
?
Deno.formatDiagnostics
Deno.applySourceMap
It feels like an oversight that all of it was dropped from Deno, and only some of it was included here. While emit
and bundle
are certainly the core utilities, the above APIs were central to many of their use cases.
import {emit} from "https://deno.land/x/[email protected]/mod.ts";
await emit("https://deno.land/[email protected]/path/mod.ts")
gives
Uncaught Error: Module not found "file:///Users/Jesper/https:/deno.land/[email protected]/path/mod.ts".
at __wbg_new_651776e932b7e9c7 (https://deno.land/x/[email protected]/lib/emit.generated.js:329:19)
at <anonymous> (https://deno.land/x/[email protected]/lib/emit_bg.wasm:1:7194705)
at <anonymous> (https://deno.land/x/[email protected]/lib/emit_bg.wasm:1:2500606)
at <anonymous> (https://deno.land/x/[email protected]/lib/emit_bg.wasm:1:6371297)
at <anonymous> (https://deno.land/x/[email protected]/lib/emit_bg.wasm:1:7147137)
at __wbg_adapter_16 (https://deno.land/x/[email protected]/lib/emit.generated.js:205:6)
at real (https://deno.land/x/[email protected]/lib/emit.generated.js:189:14)
It took me a bit to figure out I had to pass in the first argument as url to make this work. I think it would be nice if strings behaved the same as urls here.
But maybe I'm missing something and this is not possible, in that case I think it would be a good idea to document this somewhere.
I want to emit react component. But got this error.
error: Uncaught (in promise) Error: Relative import path "react" not prefixed with / or ./ or ../
const ret = new Error(getStringFromWasm0(arg0, arg1));
^
at __wbg_new_3047bf4b4f02b802 (https://deno.land/x/[email protected]/lib/deno_emit.generated.js:266:19)
at js_sys::Error::new::h99ffaa64b8859860 (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:3791529)
at <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::hc61726627b994514 (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:1033771)
at <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::h969d4533cb1fa669 (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:2651228)
at <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::hfa8076c654709c85 (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:2153256)
at wasm_bindgen_futures::queue::Queue::new::{{closure}}::h8b5a56bdc387819e (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:2055713)
at <dyn core::ops::function::FnMut<(A,)>+Output = R as wasm_bindgen::closure::WasmClosure>::describe::invoke::h28c77ba1afeb2b6a (https://deno.land/x/[email protected]/lib/deno_emit_bg.wasm:1:3655458)
at __wbg_adapter_14 (https://deno.land/x/[email protected]/lib/deno_emit.generated.js:144:6)
at real (https://deno.land/x/[email protected]/lib/deno_emit.generated.js:128:14)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.