# Deno 命名空間與編譯器 API

本篇章要為各位讀者帶來的是： Deno 的編譯 API 。筆者會先從 Deno namespace 開始介紹，再進入 Compiler APIs 的介紹！

### 全域空間： Deno

所有非 `Web 標準` 的 API 都會被歸類到命名空間 `Deno` 當中。

其中包含了像是： 檔案讀取、 `TCP Socket` 、運行子程序...等功能。

詳細的內容都被紀錄到了 [Deno doc](https://doc.deno.land/https/raw.githubusercontent.com/denoland/deno/master/cli/dts/lib.deno.ns.d.ts) 上面，若讀者對於這些 API 有興趣都可以在上面找到答案。

> 此外，官方文件也有提供該命名空間的 TypeScript 定義文件 (.d.ts) ，請參考[該連結](https://github.com/denoland/deno/blob/v1.4.5/cli/dts/lib.deno.ns.d.ts)。

#### 什麼是命名空間

相信如果各位接觸過 `C 語言` 的開發者對命名空間的概念肯定不陌生。如果沒有接觸過也沒關係，就跟著筆者繼續往下看吧！

> 命名空間（英語：Namespace），也稱名字空間、名稱空間等，它表示著一個識別碼（identifier）的可見範圍。 一個識別碼可在多個命名空間中定義，它在不同命名空間中的含義是互不相干的。
>
> \--[wikipedia](https://zh.wikipedia.org/zh-tw/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4)

用白話文來說，假設我們有兩個來自不同外部模組且用途不同的變數或是函式。碰巧的是，他們的名稱一模一樣，這時如果直接執行便會造成衝突。

使用 namespace 就能很好的處理這個問題，開發者可以利用不同的命名空間讓名稱相同但功能不同的函式、變數區隔開來。

### 進入正題

Deno 原生支持了在 TypeScript 編譯器運行時訪問的功能。Deno 命名空間中提供了三種訪問方法。

#### Deno.compile()

該功能與 `deno.cache` 相似，它能夠載入並將程式碼暫存下來、編譯程式碼，但無法執行它。

該 API 包含 3 個參數：

* rootName

  編譯後目標程式的名稱。
* sources

  Sources 是一個 Hash table ，它的 `Key` 為模組名稱、 `Value` 則為模組的內容。

  > 筆者不太確定為何在官方文件中會以 `Hash` 形容它。實際上，在填入選項時就只是輸入一個鍵值對而已，如果我有找到答案會在第一時間更新到這邊。
* options

  options 是一组 `Deno.CompilerOptions` 類型的選項，它包含了 Deno 支持的 TypeScript 編譯器選項。

介紹完三個可選參數後，我們來進一步看看範例：

```typescript
import { assert } from "https://deno.land/std@0.73.0/testing/asserts.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`,
  },{
      outDir: "dist"
  }
  );

  assert(diagnostics == null); // ensuring no diagnostics are returned
  console.log(emitMap);
```

輸出：

```
{
  "dist/bar.js.map": '{"version":3,"file":"bar.js","sourceRoot":"","sources":["file:///bar.ts"],"names":[],"mappings":"AAA...',
  "dist/bar.js": 'export const bar = "bar";\n//# sourceMappingURL=bar.js.map',
  "dist/foo.js.map": '{"version":3,"file":"foo.js","sourceRoot":"","sources":["file:///foo.ts"],"names":[],"mappings":"AAA...',
  "dist/foo.js": 'import * as bar from "./bar.ts";\nconsole.log(bar);\n//# sourceMappingURL=foo.js.map'
}
```

> 注意！本章介紹的三種 API 都是不穩定的功能，所以在執行時都需要加入 `--unstable` 唷！
>
> 像是： deno run --unstable compile.ts
>
> 此外，關於什麼是 map ，筆者也有將相關文章放到本日的延伸閱讀唷！

#### Deno.bundle()

該 API 類似於 `Deno.compiler()` 。不同的是，它會直接回傳打包後的結果。

至於可選參數都與 `Deno.compiler()` 相同。

範例：

```typescript
import { assert } from "https://deno.land/std@0.73.0/testing/asserts.ts";
const [diagnostics, emitMap] = await Deno.bundle("/foo.ts", {
    "/foo.ts": `import * as bar from "./bar.ts";\nconsole.log(bar);\n`,
    "/bar.ts": `export const bar = "bar";\n`,
  },{
      outDir: "dist"
  }
  );

  assert(diagnostics == null); // ensuring no diagnostics are returned
  console.log(emitMap);
```

輸出：

```typescript
/ Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.

// This is a specialised implementation of a System module loader.

"use strict";

// @ts-nocheck
/* eslint-disable */
let System, __instantiate;
(() => {
  const r = new Map();

  System = {
    register(id, d, f) {
      r.set(id, { d, f, exp: {} });
    },
  };
  async function dI(mid, src) {
    let id = mid.replace(/\.\w+$/i, "");
    if (id.includes("./")) {
      const [o, ...ia] = id.split("/").reverse(),
        [, ...sa] = src.split("/").reverse(),
        oa = [o];
      let s = 0,
        i;
      while ((i = ia.shift())) {
        if (i === "..") s++;
        else if (i === ".") break;
        else oa.push(i);
      }
      if (s < sa.length) oa.push(...sa.slice(s));
      id = oa.reverse().join("/");
    }
    return r.has(id) ? gExpA(id) : import(mid);
  }

  function gC(id, main) {
    return {
      id,
      import: (m) => dI(m, id),
      meta: { url: id, main },
    };
  }

  function gE(exp) {
    return (id, v) => {
      const e = typeof id === "string" ? { [id]: v } : id;
      for (const [id, value] of Object.entries(e)) {
        Object.defineProperty(exp, id, {
          value,
          writable: true,
          enumerable: true,
        });
      }
      return v;
    };
  }

  function rF(main) {
    for (const [id, m] of r.entries()) {
      const { f, exp } = m;
      const { execute: e, setters: s } = f(gE(exp), gC(id, id === main));
      delete m.f;
      m.e = e;
      m.s = s;
    }
  }

  async function gExpA(id) {
    if (!r.has(id)) return;
    const m = r.get(id);
    if (m.s) {
      const { d, e, s } = m;
      delete m.s;
      delete m.e;
      for (let i = 0; i < s.length; i++) s[i](await gExpA(d[i]));
      const r = e();
      if (r) await r;
    }
    return m.exp;
  }

  function gExp(id) {
    if (!r.has(id)) return;
    const m = r.get(id);
    if (m.s) {
      const { d, e, s } = m;
      delete m.s;
      delete m.e;
      for (let i = 0; i < s.length; i++) s[i](gExp(d[i]));
      e();
    }
    return m.exp;
  }
  __instantiate = (m, a) => {
    System = __instantiate = undefined;
    rF(m);
    return a ? gExpA(m) : gExp(m);
  };
})();

System.register("bar", [], function (exports_1, context_1) {
    "use strict";
    var bar;
    var __moduleName = context_1 && context_1.id;
    return {
        setters: [],
        execute: function () {
            exports_1("bar", bar = "bar");
        }
    };
});
System.register("foo", ["bar"], function (exports_2, context_2) {
    "use strict";
    var bar;
    var __moduleName = context_2 && context_2.id;
    return {
        setters: [
            function (bar_1) {
                bar = bar_1;
            }
        ],
        execute: function () {
            console.log(bar);
        }
    };
});

__instantiate("foo", false);
```

#### Deno.transpileOnly()

該 API 是基於 TypeScript 的 `transpileModule()` ，該功能會將任何型別去除並輸出成 JavaScript 程式。同時，它也不具有型別檢查和依賴模組解析的功能，相較於前兩個 API ，它僅接受兩個參數： options 和 sources 。

接著，讓我們看看範例：

```typescript
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);
```

執行後，我們會在命令列看到 enum 被輸出成了一個 IIFE 以及 map 檔案的內容：

```typescript
var Foo;
(function (Foo) {
    Foo[Foo["Foo"] = 0] = "Foo";
    Foo[Foo["Bar"] = 1] = "Bar";
    Foo[Foo["Baz"] = 2] = "Baz";
})(Foo || (Foo = {}));
;
```

```
//# sourceMappingURL=foo.js.map
{"version":3,"file":"foo.js","sourceRoot":"","sources":["foo.ts"],"names":[],"mappings":"AAAA,IAAK,GAAqB;AAA1B,WAAK,GAAG;IAAG,2BAAG,CAAA;IAAE,2BAAG,CAAA;IAAE,2BAAG,CAAA;AAAC,CAAC,EAArB,GAAG,KAAH,GAAG,QAAkB;AAAA,CAAC"}
```

### 引用 TypeScript Library

當使用者有其他特殊需求，可能會需要將 Deno 默認的 Library 覆蓋掉、重新載入，這時我們可以在 `options` 加入 `lib` 選項：

```typescript
let greeter: HTMLElement | null = document.getElementById("greeter")!; // Please forgive the Non-Null Assertion Operator
greeter.innerText = "Hello world!";
```

```typescript
const [diagnostics, emitMap] = await Deno.compile("test-dom.ts", undefined, {
  lib: ["dom", "esnext"], // include "deno.ns" for deno namespace
  outDir: "dist",
});
```

> 注意：就像 `tsc` 一樣，當你提供一個 `lib` 選項時，它會將默認的選項覆蓋掉。這時，基本的 JavaScript Library 也會消失，使用者需要加上自己使用的 es version ，像是： `es5`, `es2015`, `es2016`, `es2017`, `es2018`... 等。
>
> 可以參考 [`lib` compiler option](https://www.typescriptlang.org/docs/handbook/compiler-options.html) 更進一步暸解 `lib` 選項。

#### 引用 Deno 命名空間

除了 TypeScript 提供的 Library 之外， Deno 也提供了 4 個 Library 供開發者使用：

* `deno.ns` -  `Deno` 命名空間。
* `deno.shared_globals` - 提供 Deno 運作時支持的全域介面和變數。
* `deno.window` - 公開全域變數和 Deno 主線程中可用的 Deno 命名空間，是編譯器的默認 API 。
* `deno.worker` - 公開在 Deno 下的工作線程中可用的全域變數。

#### 三斜杠引用

除了使用 `lib` 選項外，我們也可以利用該方法將需要使用的 Library 導入：

```typescript
/// <reference lib="dom" />

document.getElementById("foo");
```

### 延伸閱讀

> 同樣的事情在不同人眼中可能會有不同的見解、看法。
>
> 在讀完本篇以後，筆者也強烈建議大家去看看以下文章，或許會對型別、變數宣告...等觀念有更深層的看法唷！

* [Deno manual](https://deno.land/manual@v1.4.5/runtime/compiler_apis)
* [JavaScript Source Map 详解](https://www.ruanyifeng.com/blog/2013/01/javascript_source_map.html)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ianchen0119.gitbook.io/deno/deno-cli/xiang-guan-gong-ju-ji-ce-shi/deno-ming-ming-kong-jian-yu-bian-yi-qi-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
