diff options
Diffstat (limited to 'docs/runtime/compiler_apis.md')
-rw-r--r-- | docs/runtime/compiler_apis.md | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/docs/runtime/compiler_apis.md b/docs/runtime/compiler_apis.md new file mode 100644 index 000000000..3a06b0b4a --- /dev/null +++ b/docs/runtime/compiler_apis.md @@ -0,0 +1,218 @@ +## Compiler API + +Deno supports runtime access to the built-in TypeScript compiler. There are +three methods in the `Deno` namespace that provide this access. + +### `Deno.compile()` + +This works similar to `deno cache` in that it can fetch and cache the code, +compile it, but not run it. It takes up to three arguments, the `rootName`, +optionally `sources`, and optionally `options`. The `rootName` is the root +module which will be used to generate the resulting program. This is like the +module name you would pass on the command line in +`deno run --reload example.ts`. The `sources` is a hash where the key is the +fully qualified module name, and the value is the text source of the module. If +`sources` is passed, Deno will resolve all the modules from within that hash and +not attempt to resolve them outside of Deno. If `sources` are not provided, Deno +will resolve modules as if the root module had been passed on the command line. +Deno will also cache any of these resources. The `options` argument is a set of +options of type `Deno.CompilerOptions`, which is a subset of the TypeScript +compiler options containing the ones supported by Deno. + +The method resolves with a tuple. The first argument contains any diagnostics +(syntax or type errors) related to the code. The second argument is a map where +the keys are the output filenames and the values are the content. + +An example of providing sources: + +```ts +const [diagnostics, emitMap] = await Deno.compile("/foo.ts", { + "/foo.ts": `import * as bar from "./bar.ts";\nconsole.log(bar);\n`, + "/bar.ts": `export const bar = "bar";\n`, +}); + +assert(diagnostics == null); // ensuring no diagnostics are returned +console.log(emitMap); +``` + +We would expect map to contain 4 "files", named `/foo.js.map`, `/foo.js`, +`/bar.js.map`, and `/bar.js`. + +When not supplying resources, you can use local or remote modules, just like you +could do on the command line. So you could do something like this: + +```ts +const [diagnostics, emitMap] = await Deno.compile( + "https://deno.land/std/examples/welcome.ts" +); +``` + +In this case `emitMap` will contain a simple `console.log()` statement. + +### `Deno.bundle()` + +This works a lot like `deno bundle` does on the command line. It is also like +`Deno.compile()`, except instead of returning a map of files, it returns a +single string, which is a self-contained JavaScript ES module which will include +all of the code that was provided or resolved as well as exports of all the +exports of the root module that was provided. It takes up to three arguments, +the `rootName`, optionally `sources`, and optionally `options`. The `rootName` +is the root module which will be used to generate the resulting program. This is +like module name you would pass on the command line in `deno bundle example.ts`. +The `sources` is a hash where the key is the fully qualified module name, and +the value is the text source of the module. If `sources` is passed, Deno will +resolve all the modules from within that hash and not attempt to resolve them +outside of Deno. If `sources` are not provided, Deno will resolve modules as if +the root module had been passed on the command line. Deno will also cache any of +these resources. The `options` argument is a set of options of type +`Deno.CompilerOptions`, which is a subset of the TypeScript compiler options +containing the ones supported by Deno. + +An example of providing sources: + +```ts +const [diagnostics, emit] = await Deno.bundle("/foo.ts", { + "/foo.ts": `import * as bar from "./bar.ts";\nconsole.log(bar);\n`, + "/bar.ts": `export const bar = "bar";\n`, +}); + +assert(diagnostics == null); // ensuring no diagnostics are returned +console.log(emit); +``` + +We would expect `emit` to be the text for an ES module, which would contain the +output sources for both modules. + +When not supplying resources, you can use local or remote modules, just like you +could do on the command line. So you could do something like this: + +```ts +const [diagnostics, emit] = await Deno.bundle( + "https://deno.land/std/http/server.ts" +); +``` + +In this case `emit` will be a self contained JavaScript ES module with all of +its dependencies resolved and exporting the same exports as the source module. + +### `Deno.transpileOnly()` + +This is based off of the TypeScript function `transpileModule()`. All this does +is "erase" any types from the modules and emit JavaScript. There is no type +checking and no resolution of dependencies. It accepts up to two arguments, the +first is a hash where the key is the module name and the value is the content. +The only purpose of the module name is when putting information into a source +map, of what the source file name was. The second argument contains optional +`options` of the type `Deno.CompilerOptions`. The function resolves with a map +where the key is the source module name supplied, and the value is an object +with a property of `source` and optionally `map`. The first is the output +contents of the module. The `map` property is the source map. Source maps are +provided by default, but can be turned off via the `options` argument. + +An example: + +```ts +const result = await Deno.transpileOnly({ + "/foo.ts": `enum Foo { Foo, Bar, Baz };\n`, +}); + +console.log(result["/foo.ts"].source); +console.log(result["/foo.ts"].map); +``` + +We would expect the `enum` would be rewritten to an IIFE which constructs the +enumerable, and the map to be defined. + +### Referencing TypeScript library files + +When you use `deno run`, or other Deno commands which type check TypeScript, +that code is evaluated against custom libraries which describe the environment +that Deno supports. By default, the compiler runtime APIs which type check +TypeScript also use these libraries (`Deno.compile()` and `Deno.bundle()`). + +But if you want to compile or bundle TypeScript for some other runtime, you may +want to override the default libraries. To do this, the runtime APIs support the +`lib` property in the compiler options. For example, if you had TypeScript code +that is destined for the browser, you would want to use the TypeScript `"dom"` +library: + +```ts +const [errors, emitted] = await Deno.compile( + "main.ts", + { + "main.ts": `document.getElementById("foo");\n`, + }, + { + lib: ["dom", "esnext"], + } +); +``` + +For a list of all the libraries that TypeScript supports, see the +[`lib` compiler option](https://www.typescriptlang.org/docs/handbook/compiler-options.html) +documentation. + +**Don't forget to include the JavaScript library** + +Just like `tsc`, when you supply a `lib` compiler option, it overrides the +default ones, which means that the basic JavaScript library won't be included +and you should include the one that best represents your target runtime (e.g. +`es5`, `es2015`, `es2016`, `es2017`, `es2018`, `es2019`, `es2020` or `esnext`). + +#### Including the `Deno` namespace + +In addition to the libraries that are provided by TypeScript, there are four +libraries that are built into Deno that can be referenced: + +- `deno.ns` - Provides the `Deno` namespace. +- `deno.shared_globals` - Provides global interfaces and variables which Deno + supports at runtime that are then exposed by the final runtime library. +- `deno.window` - Exposes the global variables plus the Deno namespace that are + available in the Deno main worker and is the default for the runtime compiler + APIs. +- `deno.worker` - Exposes the global variables that are available in workers + under Deno. + +So to add the Deno namespace to a compilation, you would include the `deno.ns` +lib in the array. For example: + +```ts +const [errors, emitted] = await Deno.compile( + "main.ts", + { + "main.ts": `document.getElementById("foo");\n`, + }, + { + lib: ["dom", "esnext", "deno.ns"], + } +); +``` + +**Note** that the Deno namespace expects a runtime environment that is at least +ES2018 or later. This means if you use a lib "lower" than ES2018 you will get +errors logged as part of the compilation. + +#### Using the triple slash reference + +You do not have to specify the `lib` in the compiler options. Deno also supports +[the triple-slash reference to a lib](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-lib-). +which can be embedded in the contents of the file. For example, if you have a +`main.ts` like: + +```ts +/// <reference lib="dom" /> + +document.getElementById("foo"); +``` + +It would compile without errors like this: + +```ts +const [errors, emitted] = await Deno.compile("./main.ts", undefined, { + lib: ["esnext"], +}); +``` + +**Note** that the `dom` library conflicts with some of the default globals that +are defined in the default type library for Deno. To avoid this, you need to +specify a `lib` option in the compiler options to the runtime compiler APIs. |