diff --git a/go.json b/go.json index 78dcc7d03..709c117cc 100644 --- a/go.json +++ b/go.json @@ -1,15 +1,15 @@ { - "permissions": "/runtime/manual/basics/permissions/", + "permissions": "/runtime/fundamentals/permissions/", "config": "/runtime/manual/getting_started/configuration_file/", "ide": "/runtime/manual/getting_started/setup_your_environment/#using-an-editor%2Fide", - "--allow-env": "/runtime/manual/basics/permissions/#environment-access", - "--allow-ffi": "/runtime/manual/basics/permissions/#ffi-(foreign-function-interface)", - "--allow-hrtime": "/runtime/manual/basics/permissions/#high-resolution-time", - "--allow-net": "/runtime/manual/basics/permissions/#network-access", - "--allow-read": "/runtime/manual/basics/permissions/#file-system-read-access", - "--allow-run": "/runtime/manual/basics/permissions/#running-subprocesses", - "--allow-sys": "/runtime/manual/basics/permissions/#system-information", - "--allow-write": "/runtime/manual/basics/permissions/#file-system-write-access", - "--allow-all": "/runtime/manual/basics/permissions/#all-permissions", - "--unsafely-ignore-certificate-errors": "/runtime/manual/basics/permissions/#certification-errors" + "--allow-env": "/runtime/fundamentals/permissions/#environment-access", + "--allow-ffi": "/runtime/fundamentals/permissions/#ffi-(foreign-function-interface)", + "--allow-hrtime": "/runtime/fundamentals/permissions/#high-resolution-time", + "--allow-net": "/runtime/fundamentals/permissions/#network-access", + "--allow-read": "/runtime/fundamentals/permissions/#file-system-read-access", + "--allow-run": "/runtime/fundamentals/permissions/#running-subprocesses", + "--allow-sys": "/runtime/fundamentals/permissions/#system-information", + "--allow-write": "/runtime/fundamentals/permissions/#file-system-write-access", + "--allow-all": "/runtime/fundamentals/permissions/#all-permissions", + "--unsafely-ignore-certificate-errors": "/runtime/fundamentals/permissions/#certification-errors" } diff --git a/runtime/_data.ts b/runtime/_data.ts index 8fe2b1ac9..152d9f5e4 100644 --- a/runtime/_data.ts +++ b/runtime/_data.ts @@ -5,27 +5,20 @@ export const sidebar = [ title: "Getting Started", items: [ { - label: "Quick Start", - id: "/runtime/manual/", - }, - { - label: "Deno Basics", - items: [ - "/runtime/manual/getting_started/first_steps/", - "/runtime/manual/getting_started/setup_your_environment/", - "/runtime/manual/getting_started/command_line_interface/", - "/runtime/manual/getting_started/configuration_file/", - "/runtime/manual/getting_started/web_frameworks/", - "/runtime/manual/basics/permissions/", - "/runtime/manual/basics/standard_library/", - "/runtime/manual/basics/import_maps/", - "/runtime/manual/basics/env_variables/", - "/runtime/manual/basics/debugging_your_code/", - "/runtime/manual/basics/connecting_to_databases/", - "/runtime/manual/basics/react/", - "/runtime/manual/getting_started/installation/", - ], + label: "Hello World", + id: "/runtime/", }, + "/runtime/getting_started/first_project/", + "/runtime/getting_started/setup_your_environment/", + "/runtime/getting_started/command_line_interface/", + ], + }, + { + title: "Fundamentals", + items: [ + "/runtime/fundamentals/ts_support/", + "/runtime/fundamentals/permissions/", + "/runtime/fundamentals/testing/", ], }, { @@ -199,19 +192,6 @@ export const sidebar = [ }, ], }, - { - label: "Testing", - items: [ - "/runtime/manual/basics/testing/", - "/runtime/manual/basics/testing/assertions/", - "/runtime/manual/basics/testing/coverage/", - "/runtime/manual/basics/testing/mocking/", - "/runtime/manual/basics/testing/sanitizers/", - "/runtime/manual/basics/testing/documentation/", - "/runtime/manual/basics/testing/behavior_driven_development/", - "/runtime/manual/basics/testing/snapshot_testing/", - ], - }, { label: "Workspaces", id: "/runtime/manual/basics/workspaces/", @@ -256,8 +236,6 @@ export const sidebar = [ { label: "TypeScript in Deno", items: [ - "/runtime/manual/advanced/typescript/overview/", - "/runtime/manual/advanced/typescript/types/", "/runtime/manual/advanced/typescript/configuration/", "/runtime/manual/advanced/typescript/migration/", "/runtime/manual/advanced/typescript/faqs/", diff --git a/runtime/fundamentals/_category_.json b/runtime/fundamentals/_category_.json new file mode 100644 index 000000000..b40be674f --- /dev/null +++ b/runtime/fundamentals/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Fundamentals", + "position": 2, + "link": { + "type": "doc", + "id": "index" + } +} diff --git a/runtime/manual/basics/permissions.md b/runtime/fundamentals/permissions.md similarity index 74% rename from runtime/manual/basics/permissions.md rename to runtime/fundamentals/permissions.md index caa6f1c9f..826c812b3 100644 --- a/runtime/manual/basics/permissions.md +++ b/runtime/fundamentals/permissions.md @@ -3,33 +3,69 @@ title: "Permissions" oldUrl: - /runtime/manual/basics/permissionsDeno/ - /manual/basics/permissions + - /runtime/manual/basics/permissions --- Deno is secure by default. Therefore, unless you specifically enable it, a -program run with Deno has no file, network, or environment access. Access to -security sensitive functionality requires that permissions have been granted to -an executing script through command line flags, or a runtime permission prompt. -This is a major difference from Node, where dependencies are automatically -granting full access to everything, introducing hidden vulnerabilities in your -project. +program run with Deno has no access to sensitive APIs, such as file system +access, network connectivity, or environment access. You must explicitly grant +access to these resources with command line flags or with the runtime permission +prompt. This is a major difference from Node, where dependencies are +automatically granted full access to everything, potentially introducing hidden +vulnerabilities into your project. -## Run untrusted code with confidence +## Granting permissions -Since Deno provides no I/O access by default, it's useful for running untrusted -code and auditing third-party code. If you're building or extending a platform -that runs user generated code, you can use Deno for running third-party code -securely and host this code through -[Deno Subhosting](https://deno.com/subhosting) or any other cloud platform of -your choice. - -For the following example `mod.ts` has been granted read-only access to the file -system. It cannot write to the file system, or perform any other security -sensitive functions. +To grant a permission to a script, you can use the `--allow-` flag +when running the script. For example, to grant read access to the file system, +you can use the `--allow-read` or short`-R` flag: ```shell deno run --allow-read mod.ts ``` +`mod.ts` has been granted read-only access to the file system. It cannot write +to the file system, or perform any other security sensitive functions. For more +examples of what you can do with different permissions, check out +[Deno by Example](https://docs.deno.com/examples/). + +## Denying permissions + +Although permissions are denied by default, you can explicitly deny permissions +to provide additional security and clarity. + +If you use both `--allow-*` and `--deny-*` flags, the deny flags take +precedence. This allows you to fine-tune permissions more precisely. For +example, you might allow network access but deny access to specific domains: + +```shell +deno run --allow-net --deny-net=example.com script.ts +``` + +Explicitly denying permissions can prevent accidental access to sensitive +resources, especially in complex projects where multiple scripts and +dependencies are involved. + +```shell +deno run --allow-read --deny-read=secrets.txt script.ts +# or +deno run --allow-read=/Users --deny-read=/Users/baduser script.ts +``` + +By explicitly denying permissions, you make your intentions clear in the code. +This can be useful for documentation or for other developers who might work on +the project, ensuring they understand which permissions are intentionally +restricted. + +## Run untrusted code with confidence + +Since Deno provides no I/O access by default, it is perfect for running +untrusted code and auditing third-party code. If you're building or extending a +platform that runs user generated code, you can use Deno for running third-party +code securely and host this code through +[Deno Subhosting](https://deno.com/subhosting) or any other cloud platform of +your choice. + ## Permissions list The following permissions are available: @@ -44,11 +80,14 @@ or a deny-list of environment variables. > Note for Windows users: environment variables are case insensitive on Windows, > so Deno also matches them case insensitively (on Windows only). -Definition: `--allow-env[=...]` +Definition: `--allow-env[=...]` or `-E[=...]` ```sh # Allow access to all environment variables +deno run -E script.ts +# or deno run --allow-env script.ts + # Allow HOME and FOO environment variable deno run --allow-env=HOME,FOO script.ts ``` @@ -58,6 +97,7 @@ Definition: `--deny-env[=...]` ```sh # Deny access to all environment variables deno run --deny-env script.ts + # Deny access to HOME and FOO environment variable deno run --deny-env=HOME,FOO script.ts ``` @@ -86,6 +126,7 @@ Definition: `--allow-ffi[=...]` ```sh # Allow loading dynamic libraries deno run --allow-ffi script.ts + # Allow loading dynamic libraries from a specific path deno run --allow-ffi=./libfoo.so script.ts ``` @@ -95,6 +136,7 @@ Definition: `--deny-ffi[=...]` ```sh # Deny loading dynamic libraries deno run --deny-ffi script.ts + # Deny loading dynamic libraries from a specific path deno run --deny-ffi=./libfoo.so script.ts ``` @@ -127,17 +169,23 @@ Allow or deny network access. You can specify an optional, comma-separated list of IP addresses or hostnames (optionally with ports) to provide an allow-list of allowed network addresses or a deny-list of denied network addresses. -Definition: `--allow-net[=...]` +Definition: `--allow-net[=...]` or `-N[=...]` ```sh # Allow network access +deno run -N script.ts +# or deno run --allow-net script.ts + # Allow network access to github.com and jsr.io deno run --allow-net=github.com,jsr.io script.ts + # A hostname at port 80: deno run --allow-net=example.com:80 script.ts + # An IPv4 address on port 443 -deno run --allow-net=1.1.1.1:443 script.ts +deno run --allow-net=1.1.1.1:443 script. + # An IPv6 address, all ports allowed deno run --allow-net=[2606:4700:4700::1111] script.ts ``` @@ -147,6 +195,7 @@ Definition: `--deny-net[=...]` ```sh # Deny network access deno run --deny-net script.ts + # Deny network access to github.com and jsr.io deno run --deny-net=github.com,jsr.io script.ts ``` @@ -160,11 +209,14 @@ Allow or deny file system read access. You can specify an optional, comma-separated list of directories or files to provide an allow-list of allowed file system access or a deny-list of denied file system access respectively. -Definition: `--allow-read[=...]` +Definition: `--allow-read[=...]` or `-R[=...]` ```sh # Allow all reads from file system +deno run -R script.ts +# or deno run --allow-read script.ts + # Allow reads from file foo.txt and bar.txt only deno run --allow-read=foo.txt,bar.txt script.ts ``` @@ -174,6 +226,7 @@ Definition: `--deny-read[=...]` ```sh # Deny reads from file system deno run --deny-read script.ts + # Deny reads from file foo.txt and bar.txt only deno run --deny-read=foo.txt,bar.txt script.ts ``` @@ -208,6 +261,7 @@ Definition: `--allow-run[=...]` ```sh # Allow running subprocesses deno run --allow-run script.ts + # Allow running "whoami" and "ps" subprocesses deno run --allow-run="whoami,ps" script.ts ``` @@ -217,6 +271,7 @@ Definition: `--deny-run[=...]` ```sh # Deny running subprocesses deno run --deny-run script.ts + # Deny running "whoami" and "ps" subprocesses deno run --deny-run="whoami,ps" script.ts ``` @@ -234,11 +289,14 @@ comma-separated list of allowed interfaces from the following list: `hostname`, provide OS info, like [Deno.systemMemoryInfo](https://docs.deno.com/api/deno/~/Deno.SystemMemoryInfo). -Definition: `--allow-sys[=...]` +Definition: `--allow-sys[=...]` or `-S[=...]` ```sh # Allow all system information APIs +deno run -S script.ts +# or deno run --allow-sys script.ts + # Allow systemMemoryInfo and osRelease APIs deno run --allow-sys="systemMemoryInfo,osRelease" script.ts ``` @@ -248,6 +306,7 @@ Definition: `--deny-sys[=...]` ```sh # Deny all system information APIs deno run --deny-sys script.ts + # Deny systemMemoryInfo and osRelease APIs deno run --deny-sys="systemMemoryInfo,osRelease" script.ts ``` @@ -258,11 +317,14 @@ Allow or deny file system write access. You can specify an optional, comma-separated list of directories or files to provide an allow-list of allowed file system access or a deny-list of denied file system access respectively. -Definition: `--allow-write[=...]` +Definition: `--allow-write[=...]` or `-W[=...]` ```sh # Allow all writes to file system +deno run -W script.ts +# or deno run --allow-write script.ts + # Allow writes to file foo.txt and bar.txt only deno run --allow-write=foo.txt,bar.txt script.ts ``` diff --git a/runtime/fundamentals/testing.md b/runtime/fundamentals/testing.md new file mode 100644 index 000000000..e9bf40724 --- /dev/null +++ b/runtime/fundamentals/testing.md @@ -0,0 +1,739 @@ +--- +title: "Testing" +oldUrl: + - /runtime/manual/advanced/language_server/testing_api/ + - /runtime/manual/basics/testing/ + - /runtime/manual/basics/testing/coverage/ + - /runtime/manual/basics/testing/assertions/ + - /runtime/manual/basics/testing/mocking/ + - /runtime/manual/basics/testing/behavior_driven_development + - /runtime/manual/testing/documentation/ + - /runtime/manual/basics/testing/sanitizers/ + - /runtime/manual/basics/testing/snapshot_testing/ +--- + +Deno provides a built-in test runner for writing and running tests in both +JavaScript and TypeScript. This makes it easy to ensure your code is reliable +and functions as expected without needing to install any additional dependencies +or tools. + +The Deno test runner fully supports typescript and async tests and allows for +fine-grained control over permissions for each test, enhancing your code +security. + +## Writing Tests + +To define a test in Deno, you use the `Deno.test()` function. Here are some +examples: + +```ts title="my_test.ts" +import { assertEquals } from "@std/assert"; + +// basic test +Deno.test("simple test", () => { + const x = 1 + 2; + assertEquals(x, 3); +}); + +// a named test +Deno.test("addition test", () => { + const sum = 2 + 3; + assertEquals(sum, 5); +}); + +// async test: +import { delay } from "@std/async"; + +Deno.test("async test", async () => { + const x = 1 + 2; + await delay(100); + assertEquals(x, 3); +}); + +// test with permissions +Deno.test({ + name: "read file test", + permissions: { read: true }, + fn: () => { + const data = Deno.readTextFileSync("./somefile.txt"); + assertEquals(data, "expected content"); + }, +}); +``` + +## Running Tests + +To run your tests, use the [`deno test`](TODO:deno-test-link) subcommand. + +If run without a file name or directory name, this subcommand will automatically +find and execute all tests in the current directory (recursively) that match the +glob `{*_,*.,}test.{ts, tsx, mts, js, mjs, jsx}`. + +```sh +# Run all tests in the current directory and all sub-directories +deno test + +# Run all tests in the util directory +deno test util/ + +# Run just my_test.ts +deno test my_test.ts + +# Run test modules in parallel +deno test --parallel + +# Pass additional arguments to the test file +deno test my_test.ts -- -e --foo --bar +``` + +## Test Steps + +Deno also supports test steps, which allow you to break down tests into smaller, +manageable parts. This is useful for setup and teardown operations within a +test: + +```ts +Deno.test("database operations", async (t) => { + await t.step("insert user", async () => { + // Insert user logic + }); + await t.step("insert book", async () => { + // Insert book logic + }); +}); +``` + +## Command line filtering + +Deno allows you to run specific tests or groups of tests using the `--filter` +option on the command line. This option accepts either a string or a pattern to +match test names. + +Consider the following tests: + +```ts +Deno.test({ name: "my-test", fn: myTest }); +Deno.test({ name: "test-1", fn: test1 }); +Deno.test({ name: "test-2", fn: test2 }); +``` + +### Filtering by string + +To run all tests that contain the word "test" in their names, use: + +```sh +deno test --filter "test" tests/ +``` + +This command will execute `my-test`, `test-1`, and `test-2` because they all +include "test" in their names. + +### Filtering by Pattern + +To run tests that match a specific pattern, use: + +```sh +deno test --filter "/test-*\d/" tests/ +``` + +This command will run `test-1` and `test-2` because they match the pattern +`test-*` followed by a digit. + +To indicate that you are using a pattern (regular expression), wrap your filter +value with forward slashes `/`, much like JavaScript’s syntax for regular +expressions. + +### Including and excluding paths in the configuration file + +You can also filter tests by specifying paths to include or exclude in the Deno +configuration file. + +For example, if you want to only test `src/fetch_test.ts` and +`src/signal_test.ts` and exclude everything in `out/`: + +```json +{ + "test": { + "include": [ + "src/fetch_test.ts", + "src/signal_test.ts" + ] + } +} +``` + +Or more likely: + +```json +{ + "test": { + "exclude": ["out/"] + } +} +``` + +Then running `deno test` in the same directory tree as the configuration file +will take these options into account. + +## Test definition filtering + +Deno provides two options for filtering tests within the test definitions +themselves: ignoring tests and focusing on specific tests. + +### Filtering Out (Ignoring Tests) + +You can ignore certain tests based on specific conditions using the `ignore` +boolean in the test definition. If `ignore` is set to `true`, the test will be +skipped. This is useful, for example, if you only want a test to run on a +specific operating system. + +```ts +Deno.test({ + name: "do macOS feature", + ignore: Deno.build.os !== "darwin", // This test will be ignored if not running on macOS + fn() { + // do MacOS feature here + }, +}); +``` + +### Filtering In (Only Running Specific Tests) + +If you want to focus on a particular test and ignore the rest, you can use the +`only` option. This tells the test framework to run only the tests with only set +to true. Multiple tests can have this option set. However, if any test is +flagged with only, the overall test run will always fail, as this is intended to +be a temporary measure for debugging. + +```ts +Deno.test({ + name: "Focus on this test only", + only: true, // Only this test will run + fn() { + // test complicated stuff here + }, +}); +``` + +## Failing fast + +If you have a long-running test suite and wish for it to stop on the first +failure, you can specify the `--fail-fast` flag when running the suite. + +```shell +deno test --fail-fast +``` + +## Reporters + +Deno includes three built-in reporters to format test output: + +- `pretty` (default): Provides a detailed and readable output. +- `dot`: Offers a concise output, useful for quickly seeing test results. +- `junit`: Produces output in JUnit XML format, which is useful for integrating + with CI/CD tools. + +You can specify which reporter to use with the --reporter flag: + +```sh +# Use the default pretty reporter +deno test + +# Use the dot reporter for concise output +deno test --reporter=dot + +# Use the JUnit reporter +deno test --reporter=junit +``` + +Additionally, you can write the JUnit report to a file while still getting +human-readable output in the terminal by using the `--junit-path` flag: + +```sh +deno test --junit-path=./report.xml +``` + +## Spying, mocking (test doubles), stubbing and faking time + +The [Deno standard library](./standard_library.md) provides a set of functions +to help you write tests that involve spying, mocking, and stubbing. Check out +the [documentation on JSR](https://jsr.io/@std/testing) for more information on +each of these utilities. + +## Assertions + +To help developers write tests, the +[Deno standard library](./standard_library.md) comes with a built-in assertions +module which can be imported from `jsr:@std/assert`. Check out the +[documentation on JSR for its usage](https://jsr.io/@std/assert). + +## Coverage + +Deno will collect test coverage into a directory for your code if you specify +the `--coverage` flag when starting `deno test`. This coverage information is +acquired directly from the V8 JavaScript engine, ensuring high accuracy. + +This can then be further processed from the internal format into well known +formats with the [`deno coverage`](TODO:coverage-link) tool. + +## Behavior-Driven Development + +With the [@std/testing/bdd](https://jsr.io/@std/testing/doc/bdd/~) module you +can write your tests in a familiar format for grouping tests and adding +setup/teardown hooks used by other JavaScript testing frameworks like Jasmine, +Jest, and Mocha. + +The `describe` function creates a block that groups together several related +tests. The `it` function registers an individual test case. Check out the +[documentation on JSR](https://jsr.io/@std/testing/doc/bdd/~) for more +information on these functions and hooks. + +## Documentation Tests + +Deno allows you to type-check the examples in your documentation to ensure they +are up-to-date and functional. + +### Example code blocks + +````ts title="example.ts" +/** + * # Examples + * + * ```ts + * const x = 42; + * ``` + */ +```` + +The triple backticks mark the start and end of code blocks, the language is +determined by the language identifier attribute which may be `js`, `jsx`, `ts` +or `tsx`. If no language identifier is specified then the language is inferred +from media type of the source document that the code block is extracted from. + +```sh +deno test --doc example.ts +``` + +The above command will extract this example, and then type-check it as a +standalone module living in the same directory as the module being documented. + +### Documenting exports + +To document your exports, import the module using a relative path specifier: + +````ts +/** + * # Examples + * + * ```ts + * import { foo } from "./foo.ts"; + * ``` + */ +export function foo(): string { + return "foo"; +} +```` + +## Sanitizers + +The test runner offers several sanitizers to ensure that the test behaves in a +reasonable and expected way. + +### Resource sanitizer + +Ensures that all resources created during a test are closed to prevent leaks. +Enabled by default, it can be disabled with `sanitizeResources: false`: + +```ts +Deno.test({ + name: "leaky resource test", + async fn() { + await Deno.open("hello.txt"); + }, + sanitizeResources: false, +}); +``` + +### Op sanitizer + +Ensures that all async operations started in a test are completed before the +test ends. Enabled by default, it can be disabled with `sanitizeOps: false`: + +```ts +Deno.test({ + name: "leaky operation test", + fn() { + crypto.subtle.digest( + "SHA-256", + new TextEncoder().encode("a".repeat(100000000)), + ); + }, + sanitizeOps: false, +}); +``` + +### Exit sanitizer + +Ensures that tested code doesn’t call `Deno.exit()`, which could signal a false +test success. Enabled by default, it can be disabled with `sanitizeExit: false`. + +```ts +Deno.test({ + name: "false success", + fn() { + Deno.exit(0); + }, + sanitizeExit: false, +}); + +// This test never runs, because the process exits during "false success" test +Deno.test({ + name: "failing test", + fn() { + throw new Error("this test fails"); + }, +}); +``` + +## Snapshot testing + +The [Deno standard library](./standard_library.md) includes a +[snapshot module](https://jsr.io/@std/testing/doc/snapshot/~) that allows +developers to write tests by comparing values against reference snapshots. These +snapshots are serialized representations of the original values and are stored +alongside the test files. + +Snapshot testing enables catching a wide array of bugs with very little code. It +is particularly helpful in situations where it is difficult to precisely express +what should be asserted, without requiring a prohibitive amount of code, or +where the assertions a test makes are expected to change often. + +--- + +## The testing API + +The Deno language server supports a custom set of APIs to enable testing. These +provide information to enable +[vscode's Testing API](https://code.visualstudio.com/api/extension-guides/testing) +and can be used by other language server clients to provide a similar interface. + +### Capabilities + +Both the client and the server should support the experimental `testingApi` +capability: + +```ts +interface ClientCapabilities { + experimental?: { + testingApi: boolean; + }; +} +``` + +```ts +interface ServerCapabilities { + experimental?: { + testingApi: boolean; + }; +} +``` + +When a version of Deno that supports the testing API encounters a client which +supports the capability, it will initialize the code which handles the test +detection and will start providing the notifications which enable it. + +It should also be noted that when the testing API capabilities are enabled, the +testing code lenses will no longer be sent to the client. + +### Settings + +There are specific settings which change the behavior of the language server: + +- `deno.testing.args` - An array of strings which will be provided as arguments + when executing tests. This works in the same fashion as the `deno test` + subcommand. +- `deno.testing.enable` - A binary flag that enables or disables the testing + server + +### Notifications + +The server will send notifications to the client under certain conditions. + +#### `deno/testModule` + +When a module containing tests is discovered by the server, it will notify the +client by sending a `deno/testModule` notification along with a payload of +`TestModuleParams`. + +Deno structures in this fashion: + +- A module can contain _n_ tests. +- A test can contain _n_ steps. +- A step can contain _n_ steps. + +When Deno does static analysis of a test module, it attempts to identify any +tests and test steps. Because of the dynamic way tests can be declared in Deno, +they cannot always be statically identified and can only be identified when the +module is executed. The notification is designed to handle both of these +situations when updating the client. When tests are discovered statically, the +notification `kind` will be `"replace"`, when tests or steps are discovered at +execution time, the notification `kind` will be `"insert"`. + +As a test document is edited in the editor, and `textDocument/didChange` +notifications are received from the client, the static analysis of those changes +will be performed server side and if the tests have changed, the client will +receive a notification. + +When a client receives a `"replace"` notification, it can safely "replace" a +test module representation, where when an `"insert"` it received, it should +recursively try to add to existing representations. + +For test modules the `textDocument.uri` should be used as the unique ID for any +representation (as it the string URL to the unique module). `TestData` items +contain a unique `id` string. This `id` string is a SHA-256 hash of identifying +information that the server tracks for a test. + +```ts +interface TestData { + /** The unique ID for this test/step. */ + id: string; + + /** The display label for the test/step. */ + label: string; + + /** Any test steps that are associated with this test/step */ + steps?: TestData[]; + + /** The range of the owning text document that applies to the test. */ + range?: Range; +} + +interface TestModuleParams { + /** The text document identifier that the tests are related to. */ + textDocument: TextDocumentIdentifier; + + /** A indication if tests described are _newly_ discovered and should be + * _inserted_ or if the tests associated are a replacement for any existing + * tests. */ + kind: "insert" | "replace"; + + /** The text label for the test module. */ + label: string; + + /** An array of tests that are owned by this test module. */ + tests: TestData[]; +} +``` + +#### `deno/testModuleDelete` + +When a test module is deleted that the server is observing, the server will +issue a `deno/testModuleDelete` notification. When receiving the notification +the client should remove the representation of the test module and all of its +children tests and test steps. + +```ts +interface TestModuleDeleteParams { + /** The text document identifier that has been removed. */ + textDocument: TextDocumentIdentifier; +} +``` + +#### `deno/testRunProgress` + +When a [`deno/testRun`](#denotestrun) is requested from the client, the server +will support progress of that test run via the `deno/testRunProgress` +notification. + +The client should process these messages and update any UI representation. + +The state change is represented in the `.message.kind` property of the +`TestRunProgressParams`. The states are: + +- `"enqueued"` - A test or test step has been enqueued for testing. +- `"skipped"` - A test or test step was skipped. This occurs when the Deno test + has the `ignore` option set to `true`. +- `"started"` - A test or test step has started. +- `"passed"` - A test or test step has passed. +- `"failed"` - A test or test step has failed. This is intended to indicate an + error with the test harness instead of the test itself, but Deno currently + does not support this distinction. +- `"errored"` - The test or test step has errored. Additional information about + the error will be in the `.message.messages` property. +- `"end"` - The test run has ended. + +```ts +interface TestIdentifier { + /** The test module the message is related to. */ + textDocument: TextDocumentIdentifier; + + /** The optional ID of the test. If not present, then the message applies to + * all tests in the test module. */ + id?: string; + + /** The optional ID of the step. If not present, then the message only applies + * to the test. */ + stepId?: string; +} + +interface TestMessage { + /** The content of the message. */ + message: MarkupContent; + + /** An optional string which represents the expected output. */ + expectedOutput?: string; + + /** An optional string which represents the actual output. */ + actualOutput?: string; + + /** An optional location related to the message. */ + location?: Location; +} + +interface TestEnqueuedStartedSkipped { + /** The state change that has occurred to a specific test or test step. + * + * - `"enqueued"` - the test is now enqueued to be tested + * - `"started"` - the test has started + * - `"skipped"` - the test was skipped + */ + type: "enqueued" | "started" | "skipped"; + + /** The test or test step relating to the state change. */ + test: TestIdentifier; +} + +interface TestFailedErrored { + /** The state change that has occurred to a specific test or test step. + * + * - `"failed"` - The test failed to run properly, versus the test erroring. + * currently the Deno language server does not support this. + * - `"errored"` - The test errored. + */ + type: "failed" | "errored"; + + /** The test or test step relating to the state change. */ + test: TestIdentifier; + + /** Messages related to the state change. */ + messages: TestMessage[]; + + /** An optional duration, in milliseconds from the start to the current + * state. */ + duration?: number; +} + +interface TestPassed { + /** The state change that has occurred to a specific test or test step. */ + type: "passed"; + + /** The test or test step relating to the state change. */ + test: TestIdentifier; + + /** An optional duration, in milliseconds from the start to the current + * state. */ + duration?: number; +} + +interface TestOutput { + /** The test or test step has output information / logged information. */ + type: "output"; + + /** The value of the output. */ + value: string; + + /** The associated test or test step if there was one. */ + test?: TestIdentifier; + + /** An optional location associated with the output. */ + location?: Location; +} + +interface TestEnd { + /** The test run has ended. */ + type: "end"; +} + +type TestRunProgressMessage = + | TestEnqueuedStartedSkipped + | TestFailedErrored + | TestPassed + | TestOutput + | TestEnd; + +interface TestRunProgressParams { + /** The test run ID that the progress message applies to. */ + id: number; + + /** The message*/ + message: TestRunProgressMessage; +} +``` + +### Requests + +The server handles two different requests: + +#### `deno/testRun` + +To request the language server to perform a set of tests, the client sends a +`deno/testRun` request, which includes that ID of the test run to be used in +future responses to the client, the type of the test run, and any test modules +or tests to include or exclude. + +Currently Deno only supports the `"run"` kind of test run. Both `"debug"` and +`"coverage"` are planned to be supported in the future. + +When there are no test modules or tests that are included, it implies that all +discovered tests modules and tests should be executed. When a test module is +included, but not any test ids, it implies that all tests within that test +module should be included. Once all the tests are identified, any excluded tests +are removed and the resolved set of tests are returned in the response as +`"enqueued"`. + +It is not possible to include or exclude test steps via this API, because of the +dynamic nature of how test steps are declared and run. + +```ts +interface TestRunRequestParams { + /** The id of the test run to be used for future messages. */ + id: number; + + /** The run kind. Currently Deno only supports `"run"` */ + kind: "run" | "coverage" | "debug"; + + /** Test modules or tests to exclude from the test run. */ + exclude?: TestIdentifier[]; + + /** Test modules or tests to include in the test run. */ + include?: TestIdentifier[]; +} + +interface EnqueuedTestModule { + /** The test module the enqueued test IDs relate to */ + textDocument: TextDocumentIdentifier; + + /** The test IDs which are now enqueued for testing */ + ids: string[]; +} + +interface TestRunResponseParams { + /** Test modules and test IDs that are now enqueued for testing. */ + enqueued: EnqueuedTestModule[]; +} +``` + +#### `deno/testRunCancel` + +If a client wishes to cancel a currently running test run, it sends a +`deno/testRunCancel` request with the test ID to cancel. The response back will +be a boolean of `true` if the test is cancelled or `false` if it was not +possible. Appropriate test progress notifications will still be sent as the test +is being cancelled. + +```ts +interface TestRunCancelParams { + /** The test id to be cancelled. */ + id: number; +} +``` diff --git a/runtime/fundamentals/ts_support.md b/runtime/fundamentals/ts_support.md new file mode 100644 index 000000000..fe30dc243 --- /dev/null +++ b/runtime/fundamentals/ts_support.md @@ -0,0 +1,337 @@ +--- +title: "TypeScript support" +oldUrl: + - /runtime/manual/advanced/typescript/ + - /runtime/manual/typescript/ + - /runtime/manual/typescript/overview/ + - /runtime/manual/getting_started/typescript/ + - /manual/advanced/typescript + - /runtime/manual/advanced/typescript/overview/ + - /runtime/manual/advanced/typescript/types/ +--- + +TypeScript is a first class language in Deno, just like JavaScript or +WebAssembly, so you can run or import TypeScript without installing anything +more than the Deno CLI. + +## The TS Compiler + +Deno has a built-in TypeScript compiler, which is used to compile TypeScript +code to JavaScript. No extra installation or config is required making your +development process simpler. + +### Automatic conversion and caching + +Deno converts TypeScript (as well as TSX and JSX) into JavaScript. It does this +with a combination of the built in +[TypeScript compiler](https://github.com/microsoft/TypeScript), and a Rust +library called [swc](https://swc.rs/). When the code has been type checked and +transformed, it is stored in a cache, ready for the next run without the need to +convert it from its source to JavaScript again. + +You can see this cache location by running `deno info`: + +```shell +$ deno info +DENO_DIR location: "/path/to/cache/deno" +Remote modules cache: "/path/to/cache/deno/deps" +TypeScript compiler cache: "/path/to/cache/deno/gen" +``` + +If you were to look in the cache, you would see a directory structure that +mimics your source directory structure and individual `.js` and `.meta` files. +The `.js` file is the transformed source file while the `.meta` file contains +meta data we want to cache about the file. The metadata contains a _hash_ of the +source module that helps Deno to manage cache invalidation. You might also see a +`.buildinfo` file, a TypeScript compiler incremental build information file, +used to help speed up type checking. + +## Type Checking + +One of the main advantages of TypeScript is that it can make your code type +safe, catching errors during development rather than runtime. TypeScript is a +superset of JavaScript meaning that syntactically valid JavaScript becomes +TypeScript with warnings about being "unsafe". + +:::note + +**Deno type checks TypeScript in `strict mode` by default**, the TypeScript core +team +[recommends strict mode as a sensible default](https://www.typescriptlang.org/play/?#example/new-compiler-defaults). + +::: + +Deno allows you to type-check your code (without executing it) with the +[`deno check`](TODO:check-link) subcommand: + +```shell +deno check module.ts +# or also type check remote modules and npm packages +deno check --all module.ts +``` + +:::note + +Type checking can take a significant amount of time, especially if you are +working on a codebase where you are making a lot of changes. Deno optimizes type +checking, but it still comes at a cost. Therefore, **by default, TypeScript +modules are not type-checked before they are executed**. + +::: + +When using the `deno run` command, Deno will only transpile the module before +executing it, ignoring any potential type-related issues. In order to perform a +type check of the module before execution occurs, you can use the `--check` flag +with `deno run`: + +```shell +deno run --check module.ts +# or also type check remote modules and npm packages +deno run --check=all module.ts +``` + +Deno treats type checking issues as terminal. If you have used the `--check` +flag then type-related diagnostics will prevent the program from running: it +will halt on these warnings, and exit the process before executing the code. + +In order to avoid this, you will either need to resolve the issue, use the +`// @ts-ignore` or `// @ts-expect-error` pragmas, or skip type checking all +together. + +## Determining the type of file + +Since Deno supports JavaScript, TypeScript, JSX, TSX modules, Deno has to make a +decision about how to treat each of these kinds of files. For local modules, +Deno makes this determination based on the extension. When the extension is +absent in a local file, it is assumed to be JavaScript. The module type can be +overridden using the `--ext` argument. This is useful if the module does not +have a file extension, e.g. because it is embedded in another file. + +A `.d.ts` file and a `.ts` file have different semantics in TypeScript and +different ways they need to be handled in Deno. While we expect to convert a +`.ts` file into JavaScript, a `.d.ts` file contains no "runnable" code, it is +simply describing types. When Deno fetches a remote module, the media type for a +`.ts.` and `.d.ts` file looks the same. So the path is checked, if a file has a +path that ends with `.d.ts` it will be treated as type definition only file +instead of "runnable" TypeScript. + +### Supported media types + +The following table provides a list of media types which Deno supports when +identifying the type of file of a remote module: + +| Media Type | How File is Handled | +| -------------------------- | ----------------------------------------------------------- | +| `application/typescript` | TypeScript (with path extension influence) | +| `text/typescript` | TypeScript (with path extension influence) | +| `video/vnd.dlna.mpeg-tts` | TypeScript (with path extension influence) | +| `video/mp2t` | TypeScript (with path extension influence) | +| `application/x-typescript` | TypeScript (with path extension influence) | +| `application/javascript` | JavaScript (with path extensions influence) | +| `text/javascript` | JavaScript (with path extensions influence) | +| `application/ecmascript` | JavaScript (with path extensions influence) | +| `text/ecmascript` | JavaScript (with path extensions influence) | +| `application/x-javascript` | JavaScript (with path extensions influence) | +| `application/node` | JavaScript (with path extensions influence) | +| `text/jsx` | JSX | +| `text/tsx` | TSX | +| `text/plain` | Attempt to determine that path extension, otherwise unknown | +| `application/octet-stream` | Attempt to determine that path extension, otherwise unknown | + +## Mixing JavaScript and TypeScript + +By default, Deno does not type check JavaScript. Check out the documentation on +[configuring your project](./configuration.md) to find out more about how to +configure your project to do this. Deno does support JavaScript importing +TypeScript and TypeScript importing JavaScript, in complex scenarios. + +An important note though is that when type checking TypeScript, by default Deno +will "read" all the JavaScript in order to be able to evaluate how it might have +an impact on the TypeScript types. The type checker will do the best it can to +figure out the types of the JavaScript you import into TypeScript, including +reading any JSDoc comments. Details of this are discussed in detail in the +[Types and type declarations](./types.md) section. + +## Type resolution + +Deno adheres to a design principle of no non-standard module resolution, meaning +it deals with explicit module specifiers. This can cause issues when importing +JavaScript files that have corresponding type definition files. To address this, +Deno offers these solutions: + +1. **Provide types when importing** to ensure that TypeScript uses the specified + type definition file for type checking. Use the `@ts-types` compiler hint to + specify a type definition file for a JavaScript module: + + ```ts + // @ts-types="./coolLib.d.ts" + import * as coolLib from "./coolLib.js"; + ``` + +2. **Provide types when hosting**. Use the `@ts-self-types` pragma in the + JavaScript file to point to the type definition file: + + ```ts + // @ts-self-types="./coolLib.d.ts" + ``` + +This informs Deno to use the type definition file for type checking while still +loading the JavaScript file for execution. + +3. **Using HTTP headers for remote modules**. Deno supports a header for remote + modules that instructs the runtime on where to locate the types for a given + module. For example, a response for https://example.com/coolLib.js might look + like this: + +```ts +HTTP/1.1 200 OK +Content-Type: application/javascript; charset=UTF-8 +Content-Length: 648 +X-TypeScript-Types: ./coolLib.d.ts +``` + +When seeing this header, Deno would attempt to retrieve +https://example.com/coolLib.d.ts and use that when type checking the original +module. + +## Using ambient or global types + +Using module/UMD type definitions with Deno is generally preferred, as it allows +a module to explicitly import the types it depends on. Modular type definitions +can augment the global scope using `declare` global. For example: + +```ts +declare global { + var AGlobalString: string; +} +``` + +This makes `AGlobalString` available globally when the type definition is +imported. + +However, when using existing type libraries, it might not always be possible to +use modular type definitions. In such cases, there are methods to include +arbitrary type definitions during type checking. + +### Using a triple-slash directive + +You can couple type definitions directly to the code by adding a triple-slash +types directive in a TypeScript file. This includes the type definition during +type checking. For example: + +```ts +/// +``` + +The specifier can be a relative path or a fully qualified URL: + +```ts +/// +``` + +### Using a configuration file + +Another option is to use a configuration file to include type definitions by +specifying a "types" value in the "compilerOptions". For example: + +```json +{ + "compilerOptions": { + "types": [ + "./types.d.ts", + "https://deno.land/x/pkg@1.0.0/types.d.ts", + "/Users/me/pkg/types.d.ts" + ] + } +} +``` + +Relative specifiers in the "types" array are resolved relative to the config +file’s path. Use the `--config=path/to/file` flag to tell Deno to use this +configuration file. + +## Type Checking Web Workers + +When Deno loads a TypeScript module in a web worker, it automatically type +checks the module and its dependencies against the Deno web worker library. In +contexts such as `deno cache` or in editors you may want to instruct Deno to use +the web worker library. You can do this a couple of ways: + +### Using triple-slash directives + +This option couples the library settings with the code itself. By adding the +following triple-slash directives near the top of the entry point file for the +worker script, Deno will type check it as a Deno worker script, irrespective of +how the module is analyzed: + +```ts +/// +/// +``` + +- The first directive ensures no other default libraries are used, preventing + conflicting type definitions. +- The second directive instructs Deno to apply the built-in Deno worker type + definitions and dependent libraries. + +These directives help Deno automatically detect and apply the correct libraries +during type checking when using commands like deno cache or deno bundle, or when +using an IDE with the Deno language server. However, this approach makes the +code less portable to non-Deno platforms like tsc, as only Deno includes the +"deno.worker" library. + +### Using a configuration file + +Another option is to use a configuration file that is configured to apply the +library files. A minimal file that would work would look something like this: + +```json +{ + "compilerOptions": { + "target": "esnext", + "lib": ["deno.worker"] + } +} +``` + +Then when running a command on the command line, you would need to pass the +`--config path/to/file` argument, or if you are using an IDE which leverages the +Deno language server, set the `deno.config` setting. + +If you also have non-worker scripts, you will either need to omit the `--config` +argument, or have one that is configured to meet the needs of your non-worker +scripts. + +## Important points + +### Type Declaration Semantics + +- Type declaration files (`.d.ts files`) in Deno are treated as module + declarations (UMD) rather than ambient/global declarations. + +- Importing within type declarations follows Deno’s normal import rules, which + may cause compatibility issues with some `.d.ts` files available online. + +- The CDN [esm.sh](esm.sh) provides type declarations by default, which can be + disabled by appending `?no-dts` to the import URL. + +### Behavior of JavaScript When Type Checking + +- When importing JavaScript into TypeScript in Deno, the TypeScript compiler + performs static analysis on the JavaScript module to determine the shape of + its exports, even if `checkJs` is set to `false`. + +- This can cause issues with modules that have special packaging or are global + UMD modules, leading to misleading errors. Providing types using one of the + mentioned methods can help. + +### Internals + +- Deno generates a module graph by parsing the root module and its dependencies, + filling two potential slots: the code slot and the type slot. + +- During type checking, Deno offers the type slot to the TypeScript compiler if + it is filled, otherwise, it offers the code slot. + +- This ensures that when importing a `.d.ts` module or using alternative type + modules for JavaScript code, the correct types are provided to TypeScript. diff --git a/runtime/getting_started/_category_.json b/runtime/getting_started/_category_.json new file mode 100644 index 000000000..d2d6e6b57 --- /dev/null +++ b/runtime/getting_started/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Getting Started", + "position": 1, + "link": { + "type": "doc", + "id": "index" + } +} diff --git a/runtime/getting_started/command_line_interface.md b/runtime/getting_started/command_line_interface.md new file mode 100644 index 000000000..0f06f84c0 --- /dev/null +++ b/runtime/getting_started/command_line_interface.md @@ -0,0 +1,230 @@ +--- +title: Command Line Interface +oldUrl: /manual/getting_started/command_line_interface +--- + +Deno is a command line program. The Deno command line interface (CLI) can be +used to run scripts, manage dependencies, and even compile your code into +standalone executables. You may be familiar with some simple commands having +followed the examples thus far. This page will provide a more detailed overview +of the Deno CLI. + +The Deno CLI has a number of subcommands (like `run`, `init` and `test`, etc.). +They are used to perform different tasks within the Deno runtime environment. +Each subcommand has its own set of flags and options (eg --version) that can be +used to customize its behavior. + +You can view all of the available commands and flags by running the `deno help` +subcommand in your terminal, or using the `-h` or `--help` flags. + +Check out the [CLI reference](TODO:cli-link) for a further documentation on all +the subcommands and flags available. We'll take a look at a few commands in a +bit more detail below to see how they can be used and configured. + +## An example subcommand - `deno run` + +You can run a local TypeScript or JavaScript file by specifying its path +relative to the current working directory: + +```shell +deno run main.ts +``` + +Deno supports running scripts directly from URLs. This is particularly useful +for quickly testing or running code without downloading it first: + +```shell +deno run https://docs.deno.com/examples/hello-world.ts +``` + +You can also run a script by piping it through standard input. This is useful +for integrating with other command-line tools or dynamically generating scripts: + +```shell +cat main.ts | deno run - +``` + +## Passing script arguments + +Script arguments are additional parameters you can pass to your script when +running it from the command line. These arguments can be used to customize the +behavior of your program based on the input provided at runtime. Arguments +should be passed **after** the script name. + +To test this out we can make a script that will log the arguments passed to it: + +```ts title="main.ts" +console.log(Deno.args); +``` + +When we run that script and pass it some arguments it will log them to the +console: + +```shell +$ deno run main.ts arg1 arg2 arg3 +[ "arg1", "arg2", "arg3" ] +``` + +## Argument and flag ordering + +_Note that anything passed after the script name will be passed as a script +argument and not consumed as a Deno runtime flag._ This leads to the following +pitfall: + +```shell +# Good. We grant net permission to net_client.ts. +deno run --allow-net net_client.ts + +# Bad! --allow-net was passed to Deno.args, throws a net permission error. +deno run net_client.ts --allow-net +``` + +## Common flags + +Some flags can be used with multiple related subcommands. We discuss these +below. + +### Watch mode + +You can supply the `--watch` flag to `deno run`, `deno test`, `deno compile`, +and `deno fmt` to enable the built-in file watcher. The watcher enables +automatic reloading of your application whenever changes are detected in the +source files. This is particularly useful during development, as it allows you +to see the effects of your changes immediately without manually restarting the +application. + +The files that are watched will depend on the subcommand used: + +- for `deno run`, `deno test`, and `deno compile` the entrypoint, and all local + files that the entrypoint statically imports will be watched. +- for `deno fmt` all local files and directories specified as command line + arguments (or the working directory if no specific files/directories is + passed) are watched. + +```shell +deno run --watch main.ts +deno test --watch +deno fmt --watch +``` + +You can exclude paths or patterns from watching by providing the +`--watch-exclude` flag. The syntax is `--watch-exclude=path1,path2`. For +example: + +```shell +deno run --watch --watch-exclude=file1.ts,file2.ts main.ts +``` + +This will exclude file1.ts and file2.ts from being watched. + +To exclude a pattern, remember to surround it in quotes to prevent your shell +from expanding the glob: + +```shell +deno run --watch --watch-exclude='*.js' main.ts +``` + +### Hot Module Replacement mode + +You can use `--watch-hmr` flag with `deno run` to enable the hot module +replacement mode. Instead of restarting the program, the runtime will try to +update the program in-place. If updating in-place fails, the program will still +be restarted. + +```shell +deno run --watch-hmr main.ts +``` + +When a hot module replacement is triggered, the runtime will dispatch a +`CustomEvent` of type `hmr` that will include `path` property in its `detail` +object. You can listen for this event and perform any additional logic that you +need to do when a module is updated (eg. notify a browser over a WebSocket +connection). + +```ts +addEventListener("hmr", (e) => { + console.log("HMR triggered", e.detail.path); +}); +``` + +### Integrity flags (lock files) + +Affect commands which can download resources to the cache: `deno cache`, +`deno run`, `deno test`, `deno doc`, and `deno compile`. + +```terminal +--lock Check the specified lock file +--lock-write Write lock file. Use with --lock. +``` + +Find out more about these [here](../basics/modules/integrity_checking.md). + +### Cache and compilation flags + +Affect commands which can populate the cache: `deno cache`, `deno run`, +`deno test`, `deno doc`, and `deno compile`. As well as the flags above, this +includes those which affect module resolution, compilation configuration etc. + +```terminal +--config Load configuration file +--import-map Load import map file +--no-remote Do not resolve remote modules +--reload= Reload source code cache (recompile TypeScript) +--unstable Enable unstable APIs +``` + +### Runtime flags + +Affect commands which execute user code: `deno run` and `deno test`. These +include all of the above as well as the following. + +### Type checking flags + +You can type-check your code (without executing it) using the command: + +```shell +> deno check main.ts +``` + +You can also type-check your code before execution by using the `--check` +argument to deno run: + +```shell +> deno run --check main.ts +``` + +This flag affects `deno run`, `deno eval`, `deno repl` and `deno cache`. The +following table describes the type-checking behavior of various subcommands. +Here "Local" means that only errors from local code will induce type-errors, +modules imported from https URLs (remote) may have type errors that are not +reported. (To turn on type-checking for all modules, use `--check=all`.) + +| Subcommand | Type checking mode | +| -------------- | ------------------ | +| `deno bench` | 📁 Local | +| `deno cache` | ❌ None | +| `deno check` | 📁 Local | +| `deno compile` | 📁 Local | +| `deno eval` | ❌ None | +| `deno repl` | ❌ None | +| `deno run` | ❌ None | +| `deno test` | 📁 Local | + +### Permission flags + +These are listed [here](../basics/permissions.md#permissions-list). + +### Other runtime flags + +More flags which affect the execution environment. + +```terminal +--cached-only Require that remote dependencies are already cached +--inspect= activate inspector on host:port ... +--inspect-brk= activate inspector on host:port and break at ... +--inspect-wait= activate inspector on host:port and wait for ... +--location Value of 'globalThis.location' used by some web APIs +--prompt Fallback to prompt if required permission wasn't passed +--seed Seed Math.random() +--v8-flags= Set V8 command line options. For help: ... +``` diff --git a/runtime/getting_started/first_project.md b/runtime/getting_started/first_project.md new file mode 100644 index 000000000..a7729605d --- /dev/null +++ b/runtime/getting_started/first_project.md @@ -0,0 +1,69 @@ +--- +title: Making a Deno project +oldUrl: /runtime/manual/getting_started/first_steps/ +--- + +Deno has many [built in tools](TODO:-cli-link) to make your development +experience as smooth as possible. One of these tools is the +[project initializer](TODO:init-link), which creates a new Deno project with a +basic file structure and configuration. + +While you are welcome to use JavaScript, Deno has built-in support for +[TypeScript](https://www.typescriptlang.org/) as well, so we'll be using +TypeScript in this guide. If you'd prefer to use JavaScript, you can rename the +files to `.js` and remove the type annotations. + +## Initialize a new project + +To initialize a new Deno project, run the following command in your terminal: + +```bash +deno init my_project +``` + +This will create a new directory called `my_project` with the following +structure: + +```plaintext +my_project +├── deno.json +├── main_test.ts +└── main.ts +``` + +A `deno.json` file is created to [configure your project](TODO:config-link), and +two TypeScript files are created; `main.ts` and `main_test.ts`. The `main.ts` +file is where you'll write your application code, on initial creation it will +contain a simple program which adds two numbers together. The `main_test.ts` +file is where you can write tests, initially it will contain a test for your +addition program. + +## Run your project + +You can run this program with the following command: + +```bash +$ deno run main.ts +Add 2 + 3 = 5 +``` + +## Run your tests + +Deno has a [built in test runner](TODO:testing-link). You can write tests for +your code and run them with the `deno test` command. Run the tests in your new +project with: + +```bash +$ deno test main_test.ts +running 1 test from ./main_test.ts +addTest ... ok (1ms) + +ok | 1 passed | 0 failed (3ms) +``` + +Now that you have a basic project set up you can start building your +application. Check out our [tutorials](./tutorials) and [examples](./examples/) +for more ideas on what to build with Deno. + +You can +[learn more about using TypeScript in Deno here](../fundamentals/ts_support.md). diff --git a/runtime/getting_started/images/vscode-setup.png b/runtime/getting_started/images/vscode-setup.png new file mode 100644 index 000000000..abe1e8b55 Binary files /dev/null and b/runtime/getting_started/images/vscode-setup.png differ diff --git a/runtime/getting_started/images/webstorm_setup.png b/runtime/getting_started/images/webstorm_setup.png new file mode 100644 index 000000000..c6556780f Binary files /dev/null and b/runtime/getting_started/images/webstorm_setup.png differ diff --git a/runtime/getting_started/setup_your_environment.md b/runtime/getting_started/setup_your_environment.md new file mode 100644 index 000000000..76681e065 --- /dev/null +++ b/runtime/getting_started/setup_your_environment.md @@ -0,0 +1,405 @@ +--- +title: "Set Up Your Environment" +oldUrl: /runtime/manual/getting_started/setup_your_environment/ +--- + +Deno comes with many of the tools that are commonly needed for developing +applications, including a full [language server (LSP)](TODO:lsp-link) to help +power your IDE of choice. This page will help you set up your environment to get +the most out of Deno while you are developing. + +We'll cover: + +- How to use Deno with your favorite editor/IDE +- How to generate shell completions + +## Setting up your editor/IDE + +### Visual Studio Code + +If you haven’t already, download and install Visual Studio Code from the +[official website](https://code.visualstudio.com/). + +In the Extensions tab, search for "Deno" and install the +[extension by Denoland](https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno). + +Next, open the Command Palette by pressing `Ctrl+Shift+P` and type +`Deno: Initialize Workspace Configuration`. Select this option to configure Deno +for your workspace. + +![The VSCode command palette with the Deno: Initialize Workspace Configuration option selected.](./images/vscode-setup.png) + +A file called `.vscode/settings.json` will be created in your workspace with the +following configuration: + +```json +{ + "deno.enable": true, + "deno.lint": true, + "deno.unstable": true +} +``` + +That’s it! You’ve successfully set up your developer environment for Deno using +VSCode. You will now get all the benefits of Deno’s LSP, including IntelliSense, +code formatting, linting, and more. + +### JetBrains IDEs + +To install the Deno Plugin, open your IDE and go to **File** > **Settings**. +Navigate to **Plugins** and search for `Deno`. Install the official Deno plugin. + +![The WebStorm plugins settings](./images/webstorm_setup.png) + +To configure the Plugin, go to **File** > **Settings** again. Navigate to +**Languages & Frameworks** > **Deno**. Check **Enable Deno for your project** +and specify the path to the Deno executable (if it has not been auto-detected). + +Check out +[this blog post](https://blog.jetbrains.com/webstorm/2020/06/deno-support-in-jetbrains-ides/) +to learn more about how to get started with Deno in Jetbrains IDEs. + +### Vim/Neovim via plugins + +Deno is well-supported on both [Vim](https://www.vim.org/) and +[Neovim](https://neovim.io/) via +[coc.nvim](https://github.com/neoclide/coc.nvim), +[vim-easycomplete](https://github.com/jayli/vim-easycomplete) and +[ALE](https://github.com/dense-analysis/ale). coc.nvim offers plugins to +integrate to the Deno language server while ALE supports it _out of the box_. + +### Neovim 0.6+ using the built-in language server + +To use the Deno language server install +[nvim-lspconfig](https://github.com/neovim/nvim-lspconfig/) and follow the +instructions to enable the +[supplied Deno configuration](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#denols). + +Note that if you also have `tsserver` as an LSP client, you may run into issues +where both `tsserver` and `denols` are attached to your current buffer. To +resolve this, make sure to set some unique `root_dir` for both `tsserver` and +`denols`. You may also need to set `single_file_support` to `false` for +`tsserver` to prevent it from running in `single file mode`. Here is an example +of such a configuration: + +```lua +local nvim_lsp = require('lspconfig') +nvim_lsp.denols.setup { + on_attach = on_attach, + root_dir = nvim_lsp.util.root_pattern("deno.json", "deno.jsonc"), +} + +nvim_lsp.tsserver.setup { + on_attach = on_attach, + root_dir = nvim_lsp.util.root_pattern("package.json"), + single_file_support = false +} +``` + +For Deno, the example above assumes a `deno.json` or `deno.jsonc` file exists at +the root of the project. + +#### coc.nvim + +Once you have +[coc.nvim](https://github.com/neoclide/coc.nvim/wiki/Install-coc.nvim) +installed, you need to install the required +[coc-deno](https://github.com/fannheyward/coc-deno) via `:CocInstall coc-deno`. + +Once the plugin is installed, and you want to enable Deno for a workspace, run +the command `:CocCommand deno.initializeWorkspace` and you should be able to +utilize commands like `gd` (goto definition) and `gr` (go/find references). + +#### ALE + +ALE supports Deno via the Deno language server out of the box and in many uses +cases doesn't require additional configuration. Once you have +[ALE installed](https://github.com/dense-analysis/ale#installation) you can +perform the command +[`:help ale-typescript-deno`](https://github.com/dense-analysis/ale/blob/master/doc/ale-typescript.txt) +to get information on the configuration options available. + +For more information on how to setup ALE (like key bindings) refer to the +[official documentation](https://github.com/dense-analysis/ale#usage). + +#### Vim-EasyComplete + +Vim-EasyComplete supports Deno without any other configuration. Once you have +[vim-easycomplete installed](https://github.com/jayli/vim-easycomplete#installation), +you need install deno via `:InstallLspServer deno` if you haven't installed +deno. You can get more information from +[official documentation](https://github.com/jayli/vim-easycomplete). + +### Emacs + +#### lsp-mode + +Emacs supports Deno via the Deno language server using +[lsp-mode](https://emacs-lsp.github.io/lsp-mode/). Once +[lsp-mode is installed](https://emacs-lsp.github.io/lsp-mode/page/installation/) +it should support Deno, which can be +[configured](https://emacs-lsp.github.io/lsp-mode/page/lsp-deno/) to support +various settings. + +#### eglot + +You can also use built-in Deno language server by using +[`eglot`](https://github.com/joaotavora/eglot). + +An example configuration for Deno via eglot: + +```elisp +(add-to-list 'eglot-server-programs '((js-mode typescript-mode) . (eglot-deno "deno" "lsp"))) + + (defclass eglot-deno (eglot-lsp-server) () + :documentation "A custom class for deno lsp.") + + (cl-defmethod eglot-initialization-options ((server eglot-deno)) + "Passes through required deno initialization options" + (list :enable t + :lint t)) +``` + +### Pulsar + +The [Pulsar editor, formerly known as Atom](https://pulsar-edit.dev/) supports +integrating with the Deno language server via the +[atom-ide-deno](https://web.pulsar-edit.dev/packages/atom-ide-deno) package. +`atom-ide-deno` requires that the Deno CLI be installed and the +[atom-ide-base](https://web.pulsar-edit.dev/packages/atom-ide-base) package to +be installed as well. + +### Sublime Text + +[Sublime Text](https://www.sublimetext.com/) supports connecting to the Deno +language server via the [LSP package](https://packagecontrol.io/packages/LSP). +You may also want to install the +[TypeScript package](https://packagecontrol.io/packages/TypeScript) to get full +syntax highlighting. + +Once you have the LSP package installed, you will want to add configuration to +your `.sublime-project` configuration like the below: + +```jsonc +{ + "settings": { + "LSP": { + "deno": { + "command": ["deno", "lsp"], + "initializationOptions": { + // "config": "", // Sets the path for the config file in your project + "enable": true, + // "importMap": "", // Sets the path for the import-map in your project + "lint": true, + "unstable": false + }, + "enabled": true, + "languages": [ + { + "languageId": "javascript", + "scopes": ["source.js"], + "syntaxes": [ + "Packages/Babel/JavaScript (Babel).sublime-syntax", + "Packages/JavaScript/JavaScript.sublime-syntax" + ] + }, + { + "languageId": "javascriptreact", + "scopes": ["source.jsx"], + "syntaxes": [ + "Packages/Babel/JavaScript (Babel).sublime-syntax", + "Packages/JavaScript/JavaScript.sublime-syntax" + ] + }, + { + "languageId": "typescript", + "scopes": ["source.ts"], + "syntaxes": [ + "Packages/TypeScript-TmLanguage/TypeScript.tmLanguage", + "Packages/TypeScript Syntax/TypeScript.tmLanguage" + ] + }, + { + "languageId": "typescriptreact", + "scopes": ["source.tsx"], + "syntaxes": [ + "Packages/TypeScript-TmLanguage/TypeScriptReact.tmLanguage", + "Packages/TypeScript Syntax/TypeScriptReact.tmLanguage" + ] + } + ] + } + } + } +} +``` + +### Nova + +The [Nova editor](https://nova.app) can integrate the Deno language server via +the +[Deno extension](https://extensions.panic.com/extensions/jaydenseric/jaydenseric.deno). + +### GitHub Codespaces + +[GitHub Codespaces](https://github.com/features/codespaces) allows you to +develop fully online or remotely on your local machine without needing to +configure or install Deno. It is currently in early access. + +If a project is a Deno enabled project and contains the `.devcontainer` +configuration as part of the repository, opening the project in GitHub +Codespaces should just "work". If you are starting a new project, or you want to +add Deno support to an existing code space, it can be added by selecting the +`Codespaces: Add Development Container Configuration Files...` from the command +pallet and then selecting `Show All Definitions...` and then searching for the +`Deno` definition. + +Once selected, you will need to rebuild your container so that the Deno CLI is +added to the container. After the container is rebuilt, the code space will +support Deno. + +### Kakoune + +[Kakoune](https://kakoune.org/) supports connecting to the Deno language server +via the [kak-lsp](https://github.com/kak-lsp/kak-lsp) client. Once +[kak-lsp is installed](https://github.com/kak-lsp/kak-lsp#installation) an +example of configuring it up to connect to the Deno language server is by adding +the following to your `kak-lsp.toml`: + +```toml +[language.typescript] +filetypes = ["typescript", "javascript"] +roots = [".git"] +command = "deno" +args = ["lsp"] +[language.typescript.settings.deno] +enable = true +lint = true +``` + +### Helix + +[Helix](https://helix-editor.com) comes with built-in language server support. +Enabling connection to the Deno language server requires changes in the +`languages.toml` configuration file. + +```toml +[[language]] +name = "typescript" +language-id = "typescript" +scope = "source.ts" +injection-regex = "^(ts|typescript)$" +file-types = ["ts"] +shebangs = ["deno"] +roots = ["deno.json", "deno.jsonc", "package.json"] +auto-format = true +language-servers = ["deno-lsp"] + +[language-server.deno-lsp] +command = "deno" +args = ["lsp"] + +[language-server.deno-lsp.config.deno] +enable = true +``` + +## Shell completions + +Built into the Deno CLI is support to generate shell completion information for +the CLI itself. By using `deno completions `, the Deno CLI will output to +stdout the completions. Current shells that are supported: + +- bash +- elvish +- fish +- powershell +- zsh + +### bash example + +Output the completions and add them to the environment: + +```shell +> deno completions bash > /usr/local/etc/bash_completion.d/deno.bash +> source /usr/local/etc/bash_completion.d/deno.bash +``` + +### PowerShell example + +Output the completions: + +```shell +> deno completions powershell >> $profile +> .$profile +``` + +This will create a Powershell profile at +`$HOME\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1`, and it +will be run whenever you launch the PowerShell. + +### zsh example + +You should have a directory where the completions can be saved: + +```shell +> mkdir ~/.zsh +``` + +Then output the completions: + +```shell +> deno completions zsh > ~/.zsh/_deno +``` + +And ensure the completions get loaded in your `~/.zshrc`: + +```shell +fpath=(~/.zsh $fpath) +autoload -Uz compinit +compinit -u +``` + +If after reloading your shell and completions are still not loading, you may +need to remove `~/.zcompdump/` to remove previously generated completions and +then `compinit` to generate them again. + +### zsh example with ohmyzsh and antigen + +[ohmyzsh](https://github.com/ohmyzsh/ohmyzsh) is a configuration framework for +zsh and can make it easier to manage your shell configuration. +[antigen](https://github.com/zsh-users/antigen) is a plugin manager for zsh. + +Create the directory to store the completions and output the completions: + +```shell +> mkdir ~/.oh-my-zsh/custom/plugins/deno +> deno completions zsh > ~/.oh-my-zsh/custom/plugins/deno/_deno +``` + +Then your `.zshrc` might look something like this: + +```shell +source /path-to-antigen/antigen.zsh + +# Load the oh-my-zsh's library. +antigen use oh-my-zsh + +antigen bundle deno +``` + +### fish example + +Output the completions to a `deno.fish` file into the completions directory in +the fish config folder: + +```shell +> deno completions fish > ~/.config/fish/completions/deno.fish +``` + +## Other tools + +If you are writing or supporting a community integration using the Deno language +server, you will find +[documentation](https://github.com/denoland/deno/tree/main/cli/lsp#deno-language-server) +located in the Deno CLI code repository, but also feel free to join our +[Discord community](https://discord.gg/deno) in the `#dev-lsp` channel. diff --git a/runtime/index.md b/runtime/index.md new file mode 100644 index 000000000..c52aeb931 --- /dev/null +++ b/runtime/index.md @@ -0,0 +1,120 @@ +--- +title: "Getting Started" +pagination_next: /runtime/getting_started/first_project/ +oldUrl: + - /manual/ + - /runtime/manual/introduction/ + - /manual/introduction/ + - /runtime/manual/ +--- + +[Deno](https://www.deno.com) +([/ˈdiːnoʊ/](http://ipa-reader.xyz/?text=%CB%88di%CB%90no%CA%8A), pronounced +`dee-no`) is an +[open source](https://github.com/denoland/deno/blob/main/LICENSE.md) JavaScript, +TypeScript, and WebAssembly runtime with secure defaults and a great developer +experience. It's built on [V8](https://v8.dev/), +[Rust](https://www.rust-lang.org/), and [Tokio](https://tokio.rs/). + +Let's create and run your first Deno program in under five minutes. + +## Install Deno + +Install the Deno runtime on your system using one of the terminal commands +below. + + + + +```sh +curl -fsSL https://deno.land/install.sh | sh +``` + + + + +```powershell +irm https://deno.land/install.ps1 | iex +``` + + + + +```sh +curl -fsSL https://deno.land/install.sh | sh +``` + + + + +[Additional installation options can be found here](./resources/installation.md). +After installation, you should will the `deno` executable available on your +system path. You can verify the installation by running: + +```sh +deno --version +``` + +## Hello World + +Deno can run JavaScript and TypeScript with no additional tools or configuration +required. We'll make and run a simple "hello world" program and run it with +Deno. + +Create a [TypeScript](https://www.typescriptlang.org/) or JavaScript file called +`main` and include the following code: + + + + +```ts title="main.ts" +function greet(name: string): string { + return `Hello, ${name}!`; +} + +console.log(greet("world")); +``` + + + + +```js title="main.js" +function greet(name) { + return `Hello, ${name}!`; +} + +console.log(greet("world")); +``` + + + + +Save the file and run it with Deno: + + + + +```sh +$ deno run main.ts +Hello, world! +``` + + + + +```sh +$ deno run main.js +Hello, world! +``` + + + + +## Next Steps + +Congratulations! You've just run your first Deno program. Read on to learn more +about the Deno runtime. + +- [Making a Deno project](./installation.md) +- [Setting up your environment](./setup_your_environment.md) +- [Using the CLI](./setup_your_environment.md) diff --git a/runtime/manual/advanced/typescript/overview.md b/runtime/manual/advanced/typescript/overview.md deleted file mode 100644 index dcad09021..000000000 --- a/runtime/manual/advanced/typescript/overview.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: "Overview of TypeScript in Deno" -oldUrl: - - /runtime/manual/advanced/typescript/ - - /runtime/manual/typescript/ - - /runtime/manual/typescript/overview/ - - /runtime/manual/getting_started/typescript/ - - /manual/advanced/typescript ---- - -One of the benefits of Deno is that it treats TypeScript as a first class -language, just like JavaScript or WebAssembly, when running code in Deno. What -that means is you can run or import TypeScript without installing anything more -than the Deno CLI. - -_But wait a minute, does Deno really run TypeScript?_ you might be asking -yourself. Well, depends on what you mean by run. One could argue that in a -browser you don't actually _run_ JavaScript either. The JavaScript engine in the -browser translates the JavaScript to a series of operation codes, which it then -executes in a sandbox. So it translates JavaScript to something close to -assembly. Even WebAssembly goes through a similar translation, in that -WebAssembly is architecture agnostic while it needs to be translated into the -machine specific operation codes needed for the particular platform architecture -it is running on. So when we say TypeScript is a first class language in Deno, -we mean that we try to make the user experience in authoring and running -TypeScript as easy and straightforward as JavaScript and WebAssembly. - -Behind the scenes, we use a combination of technologies, in Rust and JavaScript, -to provide that experience. - -## How does it work? - -At a high level, Deno converts TypeScript (as well as TSX and JSX) into -JavaScript. It does this via a combination of the -[TypeScript compiler](https://github.com/microsoft/TypeScript), which we build -into Deno, and a Rust library called [swc](https://swc.rs/). When the code has -been type checked and transformed, it is stored in a cache, ready for the next -run without the need to convert it from its source to JavaScript again. - -You can see this cache location by running `deno info`: - -```shell -> deno info -DENO_DIR location: "/path/to/cache/deno" -Remote modules cache: "/path/to/cache/deno/deps" -TypeScript compiler cache: "/path/to/cache/deno/gen" -``` - -If you were to look in that cache, you would see a directory structure that -mimics that source directory structure and individual `.js` and `.meta` files -(also potentially `.map` files). The `.js` file is the transformed source file -while the `.meta` file contains meta data we want to cache about the file, which -at the moment contains a _hash_ of the source module that helps us manage cache -invalidation. You might also see a `.buildinfo` file as well, which is a -TypeScript compiler incremental build information file, which we cache to help -speed up type checking. - -## Type Checking - -One of the main advantages of TypeScript is that you can make code more type -safe, so that what would be syntactically valid JavaScript becomes TypeScript -with warnings about being "unsafe". - -You can type-check your code (without executing it) using the following command: - -```shell -deno check module.ts -# or also type check remote modules and npm packages -deno check --all module.ts -``` - -Type checking can take a significant amount of time, especially if you are -working on a code base where you are making a lot of changes. We have tried to -optimize type checking, but it still comes at a cost. **Therefore, by default, -TypeScript modules are not type-checked before they are executed.** - -```shell -deno run module.ts -``` - -When using the command above, Deno will simply transpile the module before -executing it, ignoring any potential type-related issues. In order to perform a -type-check of the module before execution occurs, the `--check` argument must be -used with `deno run`: - -```shell -deno run --check module.ts -# or also type check remote modules and npm packages -deno run --check=all module.ts -``` - -While `tsc` will (by default) still emit JavaScript when encountering diagnostic -(type-checking) issues, Deno currently treats them as terminal. When using -`deno run` _with_ the `--check` argument, type-related diagnostics will prevent -the program from running: it will halt on these warnings, and exit the process -before executing the code. - -In order to avoid this, you will either need to resolve the issue, utilise the -`// @ts-ignore` or `// @ts-expect-error` pragmas, or skip type checking all -together. - -You can learn more about type-checking arguments -[here](../../getting_started/command_line_interface.md#type-checking-flags). - -## Determining the type of file - -Since Deno supports JavaScript, TypeScript, JSX, TSX modules, Deno has to make a -decision about how to treat each of these kinds of files. For local modules, -Deno makes this determination based on the extension. When the extension is -absent in a local file, it is assumed to be JavaScript. The module type can be -overridden using the `--ext` argument. This is useful if the module does not -have a file extension, e.g. because it is embedded in another file. - -For remote modules, the media type (mime-type) is used to determine the type of -the module, where the path of the module is used to help influence the file -type, when it is ambiguous what type of file it is. - -For example, a `.d.ts` file and a `.ts` file have different semantics in -TypeScript as well as have different ways they need to be handled in Deno. While -we expect to convert a `.ts` file into JavaScript, a `.d.ts` file contains no -"runnable" code, and is simply describing types (often of "plain" JavaScript). -So when we fetch a remote module, the media type for a `.ts.` and `.d.ts` file -looks the same. So we look at the path, and if we see something that has a path -that ends with `.d.ts` we treat it as a type definition only file instead of -"runnable" TypeScript. - -### Supported media types - -The following table provides a list of media types which Deno supports when -identifying the type of file of a remote module: - -| Media Type | How File is Handled | -| -------------------------- | ----------------------------------------------------------- | -| `application/typescript` | TypeScript (with path extension influence) | -| `text/typescript` | TypeScript (with path extension influence) | -| `video/vnd.dlna.mpeg-tts` | TypeScript (with path extension influence) | -| `video/mp2t` | TypeScript (with path extension influence) | -| `application/x-typescript` | TypeScript (with path extension influence) | -| `application/javascript` | JavaScript (with path extensions influence) | -| `text/javascript` | JavaScript (with path extensions influence) | -| `application/ecmascript` | JavaScript (with path extensions influence) | -| `text/ecmascript` | JavaScript (with path extensions influence) | -| `application/x-javascript` | JavaScript (with path extensions influence) | -| `application/node` | JavaScript (with path extensions influence) | -| `text/jsx` | JSX | -| `text/tsx` | TSX | -| `text/plain` | Attempt to determine that path extension, otherwise unknown | -| `application/octet-stream` | Attempt to determine that path extension, otherwise unknown | - -## Strict by default - -Deno type checks TypeScript in _strict_ mode by default, and the TypeScript core -team recommends _strict_ mode as a sensible default. This mode generally enables -features of TypeScript that probably should have been there from the start, but -as TypeScript continued to evolve, would be breaking changes for existing code. - -## Mixing JavaScript and TypeScript - -By default, Deno does not type check JavaScript. This can be changed, and is -discussed further in [Configuring TypeScript in Deno](./configuration.md). Deno -does support JavaScript importing TypeScript and TypeScript importing -JavaScript, in complex scenarios. - -An important note though is that when type checking TypeScript, by default Deno -will "read" all the JavaScript in order to be able to evaluate how it might have -an impact on the TypeScript types. The type checker will do the best it can to -figure out what the types are of the JavaScript you import into TypeScript, -including reading any JSDoc comments. Details of this are discussed in detail in -the [Types and type declarations](./types.md) section. - -## Type resolution - -One of the core design principles of Deno is to avoid non-standard module -resolution, and this applies to type resolution as well. If you want to utilise -JavaScript that has type definitions (e.g. a `.d.ts` file), you have to -explicitly tell Deno about this. The details of how this is accomplished are -covered in the [Types and type declarations](./types.md) section. diff --git a/runtime/manual/advanced/typescript/types.md b/runtime/manual/advanced/typescript/types.md deleted file mode 100644 index b98ed5199..000000000 --- a/runtime/manual/advanced/typescript/types.md +++ /dev/null @@ -1,290 +0,0 @@ ---- -title: "Types and Type Declarations" -oldUrl: /runtime/manual/typescript/types/ ---- - -One of the design principles of Deno is no non-standard module resolution. When -TypeScript is type checking a file, it only cares about the types for the file, -and the `tsc` compiler has a lot of logic to try to resolve those types. By -default, it expects _ambiguous_ module specifiers with an extension, and will -attempt to look for the file under the `.ts` specifier, then `.d.ts`, and -finally `.js` (plus a whole other set of logic when the module resolution is set -to `"node"`). Deno deals with explicit specifiers. - -This can cause a couple problems though. For example, let's say I want to -consume a TypeScript file that has already been transpiled to JavaScript along -with a type definition file. So I have `mod.js` and `mod.d.ts`. If I try to -import `mod.js` into Deno, it will only do what I ask it to do, and import -`mod.js`, but that means my code won't be as well type checked as if TypeScript -was considering the `mod.d.ts` file in place of the `mod.js` file. - -In order to support this in Deno, Deno has two solutions, of which there is a -variation of a solution to enhance support. The two main situations you come -across would be: - -- As the importer of a JavaScript module, I know what types should be applied to - the module. -- As the supplier of the JavaScript module, I know what types should be applied - to the module. - -The latter case is the better case, meaning you as the provider or host of the -module, everyone can consume it without having to figure out how to resolve the -types for the JavaScript module, but when consuming modules that you may not -have direct control over, the ability to do the former is also required. - -## Providing types when importing - -If you are consuming a JavaScript module and you have either created types (a -`.d.ts` file) or have otherwise obtained the types you want to use, you can -instruct Deno to use that file when type checking instead of the JavaScript file -using the `@ts-types` compiler hint. `@ts-types` needs to be a single line -double slash comment, where when used impacts the next import or re-export -statement. - -For example if I have a JavaScript module `coolLib.js` and I had a separate -`coolLib.d.ts` file that I wanted to use, I would import it like this: - -```ts -// @ts-types="./coolLib.d.ts" -import * as coolLib from "./coolLib.js"; -``` - -When type checking `coolLib` and your usage of it in the file, the -`coolLib.d.ts` types will be used instead of looking at the JavaScript file. - -The pattern matching for the compiler hint is somewhat forgiving and will accept -quoted and non-quoted values for the specifier as well as accepting whitespace -before and after the equals sign. - -> ℹ️ _Note_: The directive `@deno-types` can be used as an alias for `@ts-types`. -> This is not recommended anymore. - -## Providing types when hosting - -If you are in control of the source code of the module, or you are in control of -how the file is hosted on a web server, there are two ways to inform Deno of the -types for a given module, without requiring the importer to do anything special. - -### Using `@ts-self-types` pragma - -If you are providing a JavaScript file, and want to provide a declaration file -that contains the types for this file, you can specify a `@ts-self-types` -directive in the JS file, pointing to the declaration file. - -For example, if I had created `coolLib.js` and along side of it I had created my -type definitions for my library in `coolLib.d.ts` I could do the following in -the `coolLib.js` file: - -```js -// @ts-self-types="./coolLib.d.ts" - -// ... the rest of the JavaScript ... -``` - -When Deno encounters this directive, it would resolve the `./coolLib.d.ts` file -and use that instead of the JavaScript file when TypeScript was type checking -the file, but still load the JavaScript file when running the program. - -> ℹ️ _Note_: Instead of `@ts-self-types`, a triple slash directive in the form of -> `/// ` can be used. This is not -> recommended anymore. This is a repurposed directive for TypeScript that only -> applies to JavaScript files. Using the triple-slash reference directive of -> `types` in a TypeScript file works under Deno as well, but has essentially the -> same behavior as the `path` directive. - -### Using X-TypeScript-Types header - -Similar to the triple-slash directive, Deno supports a header for remote modules -that instructs Deno where to locate the types for a given module. For example, a -response for `https://example.com/coolLib.js` might look something like this: - -```console -HTTP/1.1 200 OK -Content-Type: application/javascript; charset=UTF-8 -Content-Length: 648 -X-TypeScript-Types: ./coolLib.d.ts -``` - -When seeing this header, Deno would attempt to retrieve -`https://example.com/coolLib.d.ts` and use that when type checking the original -module. - -## Using ambient or global types - -Overall it is better to use module/UMD type definitions with Deno, where a -module expressly imports the types it depends upon. Modular type definitions can -express -[augmentation of the global scope](https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html) -via the `declare global` in the type definition. For example: - -```ts -declare global { - var AGlobalString: string; -} -``` - -This would make `AGlobalString` available in the global namespace when importing -the type definition. - -In some cases though, when leveraging other existing type libraries, it may not -be possible to leverage modular type definitions. Therefore there are ways to -include arbitrary type definitions when type checking programmes. - -### Using a triple-slash directive - -This option couples the type definitions to the code itself. By adding a -triple-slash `types` directive in a TS file (not a JS file!), near the type of a -module, type checking the file will include the type definition. For example: - -```ts -/// -``` - -The specifier provided is resolved just like any other specifier in Deno, which -means it requires an extension, and is relative to the module referencing it. It -can be a fully qualified URL as well: - -```ts -/// -``` - -### Using a configuration file - -Another option is to use a configuration file that is configured to include the -type definitions, by supplying a `"types"` value to the `"compilerOptions"`. For -example: - -```json -{ - "compilerOptions": { - "types": [ - "./types.d.ts", - "https://deno.land/x/pkg@1.0.0/types.d.ts", - "/Users/me/pkg/types.d.ts" - ] - } -} -``` - -Like the triple-slash reference above, the specifier supplied in the `"types"` -array will be resolved like other specifiers in Deno. In the case of relative -specifiers, it will be resolved relative to the path to the config file. Make -sure to tell Deno to use this file by specifying `--config=path/to/file` flag. - -## Type Checking Web Workers - -When Deno loads a TypeScript module in a web worker, it will automatically type -check the module and its dependencies against the Deno web worker library. This -can present a challenge in other contexts like `deno cache` or in editors. There -are a couple of ways to instruct Deno to use the worker libraries instead of the -standard Deno libraries. - -### Using triple-slash directives - -This option couples the library settings with the code itself. By adding the -following triple-slash directives near the top of the entry point file for the -worker script, Deno will now type check it as a Deno worker script, irrespective -of how the module is analyzed: - -```ts -/// -/// -``` - -The first directive ensures that no other default libraries are used. If this is -omitted, you will get some conflicting type definitions, because Deno will try -to apply the standard Deno library as well. The second instructs Deno to apply -the built-in Deno worker type definitions plus dependent libraries (like -`"esnext"`). - -When you run a `deno cache` or `deno bundle` command or use an IDE which uses -the Deno language server, Deno should automatically detect these directives and -apply the correct libraries when type checking. - -The one disadvantage of this, is that it makes the code less portable to other -non-Deno platforms like `tsc`, as it is only Deno which has the `"deno.worker"` -library built into it. - -### Using a configuration file - -Another option is to use a configuration file that is configured to apply the -library files. A minimal file that would work would look something like this: - -```json -{ - "compilerOptions": { - "target": "esnext", - "lib": ["deno.worker"] - } -} -``` - -Then when running a command on the command line, you would need to pass the -`--config path/to/file` argument, or if you are using an IDE which leverages the -Deno language server, set the `deno.config` setting. - -If you also have non-worker scripts, you will either need to omit the `--config` -argument, or have one that is configured to meet the needs of your non-worker -scripts. - -## Important points - -### Type declaration semantics - -Type declaration files (`.d.ts` files) follow the same semantics as other files -in Deno. This means that declaration files are assumed to be module declarations -(_UMD declarations_) and not ambient/global declarations. It is unpredictable -how Deno will handle ambient/global declarations. - -In addition, if a type declaration imports something else, like another `.d.ts` -file, its resolution follow the normal import rules of Deno. For a lot of the -`.d.ts` files that are generated and available on the web, they may not be -compatible with Deno. - -[esm.sh](https://esm.sh) is a CDN which provides type declarations by default -(via the `X-TypeScript-Types` header). It can be disabled by appending `?no-dts` -to the import URL: - -```ts -import React from "https://esm.sh/react?no-dts"; -``` - -## Behavior of JavaScript when type checking - -If you import JavaScript into TypeScript in Deno and there are no types, even if -you have `checkJs` set to `false` (the default for Deno), the TypeScript -compiler will still access the JavaScript module and attempt to do some static -analysis on it, to at least try to determine the shape of the exports of that -module to validate the import in the TypeScript file. - -This is usually never a problem when trying to import a "regular" ES module, but -in some cases if the module has special packaging, or is a global _UMD_ module, -TypeScript's analysis of the module can fail and cause misleading errors. The -best thing to do in this situation is provide some form of types using one of -the methods mention above. - -### Internals - -While it isn't required to understand how Deno works internally to be able to -leverage TypeScript with Deno well, it can help to understand how it works. - -Before any code is executed or compiled, Deno generates a module graph by -parsing the root module, and then detecting all of its dependencies, and then -retrieving and parsing those modules, recursively, until all the dependencies -are retrieved. - -For each dependency, there are two potential "slots" that are used. There is the -code slot and the type slot. As the module graph is filled out, if the module is -something that is or can be emitted to JavaScript, it fills the code slot, and -type only dependencies, like `.d.ts` files fill the type slot. - -When the module graph is built, and there is a need to type check the graph, -Deno starts up the TypeScript compiler and feeds it the names of the modules -that need to be potentially emitted as JavaScript. During that process, the -TypeScript compiler will request additional modules, and Deno will look at the -slots for the dependency, offering it the type slot if it is filled before -offering it the code slot. - -This means when you import a `.d.ts` module, or you use one of the solutions -above to provide alternative type modules for JavaScript code, that is what is -provided to TypeScript instead when resolving the module. diff --git a/runtime/manual/basics/index.md b/runtime/manual/basics/index.md index 5ca5f60b5..4deb8916f 100644 --- a/runtime/manual/basics/index.md +++ b/runtime/manual/basics/index.md @@ -6,7 +6,7 @@ In this chapter, you will find Deno basics, including: - [Modules](./modules) - [Standard Library](./standard_library.md) -- [Permissions](./permissions.md) +- [Permissions](../../fundamentals/permissions.md) - [Connecting to Databases](./connecting_to_databases.md) - [Using React with Deno](./react.md) - [Environment Variables](./env_variables.md) diff --git a/runtime/manual/basics/testing/assertions.md b/runtime/manual/basics/testing/assertions.md deleted file mode 100644 index 312bda16e..000000000 --- a/runtime/manual/basics/testing/assertions.md +++ /dev/null @@ -1,330 +0,0 @@ ---- -title: "Assertions" ---- - -To help developers write tests the Deno standard library comes with a built-in -[assertions module](https://jsr.io/@std/assert) which can be imported from -`jsr:@std/assert@1`. - -```js -import { assert } from "jsr:@std/assert@1"; - -Deno.test("Hello Test", () => { - assert("Hello"); -}); -``` - -> ⚠️ Some popular assertion libraries, like [Chai](https://www.chaijs.com/), can -> be used in Deno too, for example usage see -> [chai_example.ts](https://gist.github.com/lucacasonato/002dbcf6bb974cc788678b89257ecc01). - -The assertions module provides 14 assertions: - -- `assert(expr: unknown, msg = ""): asserts expr` -- `assertEquals(actual: unknown, expected: unknown, msg?: string): void` -- `assertExists(actual: unknown, msg?: string): void` -- `assertNotEquals(actual: unknown, expected: unknown, msg?: string): void` -- `assertStrictEquals(actual: unknown, expected: unknown, msg?: string): void` -- `assertAlmostEquals(actual: number, expected: number, epsilon = 1e-7, msg?: string): void` -- `assertInstanceOf(actual: unknown, expectedType: unknown, msg?: string): void` -- `assertStringIncludes(actual: string, expected: string, msg?: string): void` -- `assertArrayIncludes(actual: unknown[], expected: unknown[], msg?: string): void` -- `assertMatch(actual: string, expected: RegExp, msg?: string): void` -- `assertNotMatch(actual: string, expected: RegExp, msg?: string): void` -- `assertObjectMatch( actual: Record, expected: Record): void` -- `assertThrows(fn: () => void, ErrorClass?: Constructor, msgIncludes?: string | undefined, msg?: string | undefined): Error` -- `assertRejects(fn: () => Promise, ErrorClass?: Constructor, msgIncludes?: string | undefined, msg?: string | undefined): Promise` - -In addition to the above assertions, the -[snapshot module](https://jsr.io/@std/testing/doc/snapshot/~) also exposes an -`assertSnapshot` function. The documentation for this module can be found -[here](./snapshot_testing.md). - -## Assert - -The assert method is a simple 'truthy' assertion and can be used to assert any -value which can be inferred as true. - -```js -Deno.test("Test Assert", () => { - assert(1); - assert("Hello"); - assert(true); -}); -``` - -## Exists - -The `assertExists` can be used to check if a value is not `null` or `undefined`. - -```js -assertExists("Denosaurus"); -Deno.test("Test Assert Exists", () => { - assertExists("Denosaurus"); - assertExists(false); - assertExists(0); -}); -``` - -## Equality - -There are three equality assertions available, `assertEquals()`, -`assertNotEquals()` and `assertStrictEquals()`. - -The `assertEquals()` and `assertNotEquals()` methods provide a general equality -check and are capable of asserting equality between primitive types and objects. - -```js -Deno.test("Test Assert Equals", () => { - assertEquals(1, 1); - assertEquals("Hello", "Hello"); - assertEquals(true, true); - assertEquals(undefined, undefined); - assertEquals(null, null); - assertEquals(new Date(), new Date()); - assertEquals(new RegExp("abc"), new RegExp("abc")); - - class Foo {} - const foo1 = new Foo(); - const foo2 = new Foo(); - - assertEquals(foo1, foo2); -}); - -Deno.test("Test Assert Not Equals", () => { - assertNotEquals(1, 2); - assertNotEquals("Hello", "World"); - assertNotEquals(true, false); - assertNotEquals(undefined, ""); - assertNotEquals(new Date(), Date.now()); - assertNotEquals(new RegExp("abc"), new RegExp("def")); -}); -``` - -By contrast `assertStrictEquals()` provides a simpler, stricter equality check -based on the `===` operator. As a result it will not assert two instances of -identical objects as they won't be referentially the same. - -```js -Deno.test("Test Assert Strict Equals", () => { - assertStrictEquals(1, 1); - assertStrictEquals("Hello", "Hello"); - assertStrictEquals(true, true); - assertStrictEquals(undefined, undefined); -}); -``` - -The `assertStrictEquals()` assertion is best used when you wish to make a -precise check against two primitive types. - -### Equality for numbers - -When testing equality between numbers, it is important to keep in mind that some -of them cannot be accurately depicted by IEEE-754 double-precision -floating-point representation. - -That's especially true when working with decimal numbers, where -`assertStrictEquals()` may work in some cases but not in others: - -```ts -import { assertStrictEquals, assertThrows } from "jsr:@std/assert@1"; - -Deno.test("Test Assert Strict Equals with float numbers", () => { - assertStrictEquals(0.25 + 0.25, 0.25); - assertThrows(() => assertStrictEquals(0.1 + 0.2, 0.3)); - //0.1 + 0.2 will be stored as 0.30000000000000004 instead of 0.3 -}); -``` - -Instead, `assertAlmostEquals()` provides a way to test that given numbers are -close enough to be considered equals. Default tolerance is set to `1e-7` though -it is possible to change it by passing a third optional parameter. - -```ts -import { assertAlmostEquals, assertThrows } from "jsr:@std/assert@1"; - -Deno.test("Test Assert Almost Equals", () => { - assertAlmostEquals(0.1 + 0.2, 0.3); - assertAlmostEquals(0.1 + 0.2, 0.3, 1e-16); - assertThrows(() => assertAlmostEquals(0.1 + 0.2, 0.3, 1e-17)); -}); -``` - -### Instance types - -To check if an object is an instance of a specific constructor, you can use -`assertInstanceOf()`. This has the added benefit that it lets TypeScript know -the passed in variable has a specific type: - -```ts -import { assertInstanceOf } from "jsr:@std/assert@1"; - -Deno.test("Test Assert Instance Type", () => { - const variable = new Date() as unknown; - - assertInstanceOf(variable, Date); - - // This won't cause type errors now that - // it's type has been asserted against. - variable.getDay(); -}); -``` - -## Contains - -There are two methods available to assert a value contains a value, -`assertStringIncludes()` and `assertArrayIncludes()`. - -The `assertStringIncludes()` assertion does a simple includes check on a string -to see if it contains the expected string. - -```js -Deno.test("Test Assert String Contains", () => { - assertStringIncludes("Hello World", "Hello"); -}); -``` - -The `assertArrayIncludes()` assertion is slightly more advanced and can find -both a value within an array and an array of values within an array. - -```js -Deno.test("Test Assert Array Contains", () => { - assertArrayIncludes([1, 2, 3], [1]); - assertArrayIncludes([1, 2, 3], [1, 2]); - assertArrayIncludes(Array.from("Hello World"), Array.from("Hello")); -}); -``` - -## Regex - -You can assert regular expressions via `assertMatch()` and `assertNotMatch()` -assertions. - -```js -Deno.test("Test Assert Match", () => { - assertMatch("abcdefghi", new RegExp("def")); - - const basicUrl = new RegExp("^https?://[a-z.]+.com$"); - assertMatch("https://www.google.com", basicUrl); - assertMatch("http://facebook.com", basicUrl); -}); - -Deno.test("Test Assert Not Match", () => { - assertNotMatch("abcdefghi", new RegExp("jkl")); - - const basicUrl = new RegExp("^https?://[a-z.]+.com$"); - assertNotMatch("https://deno.land/", basicUrl); -}); -``` - -## Object - -Use `assertObjectMatch` to check that a JavaScript object matches a subset of -the properties of an object. - -```js -// Simple subset -assertObjectMatch( - { foo: true, bar: false }, - { - foo: true, - }, -); -``` - -## Throws - -There are two ways to assert whether something throws an error in Deno, -`assertThrows()` and `assertRejects()`. Both assertions allow you to check an -[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) -has been thrown, the type of error thrown and what the message was. - -The difference between the two assertions is `assertThrows()` accepts a standard -function and `assertRejects()` accepts a function which returns a -[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). - -The `assertThrows()` assertion will check an error has been thrown, and -optionally will check the thrown error is of the correct type, and assert the -error message is as expected. - -```js -Deno.test("Test Assert Throws", () => { - assertThrows( - () => { - throw new Error("Panic!"); - }, - Error, - "Panic!", - ); -}); -``` - -The `assertRejects()` assertion is a little more complicated, mainly because it -deals with Promises. But basically it will catch thrown errors or rejections in -Promises. You can also optionally check for the error type and error message. -This can be used similar to `assertThrows()` but with async functions. - -```js -Deno.test("Test Assert Throws Async", async () => { - await assertRejects( - () => { - return new Promise(() => { - throw new Error("Panic! Threw Error"); - }); - }, - Error, - "Panic! Threw Error", - ); - - await assertRejects( - () => { - return Promise.reject(new Error("Panic! Reject Error")); - }, - Error, - "Panic! Reject Error", - ); -}); -``` - -## Custom Messages - -Each of Deno's built-in assertions allow you to overwrite the standard CLI error -message if you wish. For instance this example will output "Values Don't Match!" -rather than the standard CLI error message. - -```js -Deno.test("Test Assert Equal Fail Custom Message", () => { - assertEquals(1, 2, "Values Don't Match!"); -}); -``` - -## Custom Tests - -While Deno comes with powerful [assertions modules](https://jsr.io/@jsr/assert) -but there is always something specific to the project you can add. Creating -`custom assertion function` can improve readability and reduce the amount of -code. - -```ts -import { AssertionError } from "jsr:@std/assert@1"; - -function assertPowerOf(actual: number, expected: number, msg?: string): void { - let received = actual; - while (received % expected === 0) received = received / expected; - if (received !== 1) { - if (!msg) { - msg = `actual: "${actual}" expected to be a power of : "${expected}"`; - } - throw new AssertionError(msg); - } -} -``` - -Use this matcher in your code like this: - -```js -Deno.test("Test Assert PowerOf", () => { - assertPowerOf(8, 2); - assertPowerOf(11, 4); -}); -``` diff --git a/runtime/manual/basics/testing/behavior_driven_development.md b/runtime/manual/basics/testing/behavior_driven_development.md deleted file mode 100644 index 448bb7842..000000000 --- a/runtime/manual/basics/testing/behavior_driven_development.md +++ /dev/null @@ -1,318 +0,0 @@ ---- -title: "Behavior-Driven Development" ---- - -With the [@std/testing/bdd](https://jsr.io/@std/testing/doc/bdd/~) module you -can write your tests in a familiar format for grouping tests and adding -setup/teardown hooks used by other JavaScript testing frameworks like Jasmine, -Jest, and Mocha. - -The `describe` function creates a block that groups together several related -tests. The `it` function registers an individual test case. - -## Hooks - -There are 4 types of hooks available for test suites. A test suite can have -multiples of each type of hook, they will be called in the order that they are -registered. The `afterEach` and `afterAll` hooks will be called whether or not -the test case passes. The `*All` hooks will be called once for the whole group -while the `*Each` hooks will be called for each individual test case. - -- `beforeAll`: Runs before all of the tests in the test suite. -- `afterAll`: Runs after all of the tests in the test suite finish. -- `beforeEach`: Runs before each of the individual test cases in the test suite. -- `afterEach`: Runs after each of the individual test cases in the test suite. - -If a hook is registered at the top level, a global test suite will be registered -and all tests will belong to it. Hooks registered at the top level must be -registered before any individual test cases or test suites. - -## Focusing tests - -If you would like to run only specific test cases, you can do so by calling -`it.only` instead of `it`. If you would like to run only specific test suites, -you can do so by calling `describe.only` instead of `describe`. - -There is one limitation to this when using the flat test grouping style. When -`describe` is called without being nested, it registers the test with -`Deno.test`. If a child test case or suite is registered with `it.only` or -`describe.only`, it will be scoped to the top test suite instead of the file. To -make them the only tests that run in the file, you would need to register the -top test suite with `describe.only` too. - -## Ignoring tests - -If you would like to not run specific individual test cases, you can do so by -calling `it.ignore` instead of `it`. If you would like to not run specific test -suites, you can do so by calling `describe.ignore` instead of `describe`. - -## Sanitization options - -Like `Deno.TestDefinition`, the `DescribeDefinition` and `ItDefinition` have -sanitization options. They work in the same way. - -- `sanitizeExit`: Ensure the test case does not prematurely cause the process to - exit, for example via a call to Deno.exit. Defaults to true. -- `sanitizeOps`: Check that the number of async completed ops after the test is - the same as number of dispatched ops. Defaults to true. -- `sanitizeResources`: Ensure the test case does not "leak" resources - ie. the - resource table after the test has exactly the same contents as before the - test. Defaults to true. - -## Permissions option - -Like `Deno.TestDefinition`, the `DescribeDefinition` and `ItDefinition` have a -`permissions` option. They specify the permissions that should be used to run an -individual test case or test suite. Set this to `"inherit"` to keep the calling -thread's permissions. Set this to `"none"` to revoke all permissions. - -This setting defaults to `"inherit"`. - -There is currently one limitation to this, you cannot use the permissions option -on an individual test case or test suite that belongs to another test suite. -That's because internally those tests are registered with `t.step` which does -not support the permissions option. - -## Comparing to Deno\.test - -The default way of writing tests is using `Deno.test` and `t.step`. The -`describe` and `it` functions have similar call signatures to `Deno.test`, -making it easy to switch between the default style and the behavior-driven -development style of writing tests. Internally, `describe` and `it` are -registering tests with `Deno.test` and `t.step`. - -Below is an example of a test file using `Deno.test` and `t.step`. In the -following sections there are examples of how the same test could be written with -`describe` and `it` using nested test grouping, flat test grouping, or a mix of -both styles. - -```ts -// user.ts -export class User { - static users: Map = new Map(); - age?: number; - - constructor(public name: string) { - if (User.users.has(name)) { - throw new Deno.errors.AlreadyExists(`User ${name} already exists`); - } - User.users.set(name, this); - } - - getAge(): number { - if (!this.age) { - throw new Error("Age unknown"); - } - return this.age; - } - - setAge(age: number) { - this.age = age; - } -} -``` - -```ts -import { - assertEquals, - assertStrictEquals, - assertThrows, -} from "jsr:@std/assert@1"; -import { User } from "./user.ts"; - -Deno.test("User.users initially empty", () => { - assertEquals(User.users.size, 0); -}); - -Deno.test("User constructor", () => { - try { - const user = new User("Kyle"); - assertEquals(user.name, "Kyle"); - assertStrictEquals(User.users.get("Kyle"), user); - } finally { - User.users.clear(); - } -}); - -Deno.test("User age", async (t) => { - const user = new User("Kyle"); - - await t.step("getAge", () => { - assertThrows(() => user.getAge(), Error, "Age unknown"); - user.age = 18; - assertEquals(user.getAge(), 18); - }); - - await t.step("setAge", () => { - user.setAge(18); - assertEquals(user.getAge(), 18); - }); -}); -``` - -### Nested test grouping - -Tests created within the callback of a `describe` function call will belong to -the new test suite it creates. The hooks can be created within it or be added to -the options argument for describe. - -```ts -import { - assertEquals, - assertStrictEquals, - assertThrows, -} from "jsr:@std/assert@1"; -import { afterEach, beforeEach, describe, it } from "@std/testing@0.225.3/bdd"; -import { User } from "./user.ts"; - -describe("User", () => { - it("users initially empty", () => { - assertEquals(User.users.size, 0); - }); - - it("constructor", () => { - try { - const user = new User("Kyle"); - assertEquals(user.name, "Kyle"); - assertStrictEquals(User.users.get("Kyle"), user); - } finally { - User.users.clear(); - } - }); - - describe("age", () => { - let user: User; - - beforeEach(() => { - user = new User("Kyle"); - }); - - afterEach(() => { - User.users.clear(); - }); - - it("getAge", function () { - assertThrows(() => user.getAge(), Error, "Age unknown"); - user.age = 18; - assertEquals(user.getAge(), 18); - }); - - it("setAge", function () { - user.setAge(18); - assertEquals(user.getAge(), 18); - }); - }); -}); -``` - -### Flat test grouping - -The `describe` function returns a unique symbol that can be used to reference -the test suite for adding tests to it without having to create them within a -callback. The gives you the ability to have test grouping without any extra -indentation in front of the grouped tests. - -```ts -import { - assertEquals, - assertStrictEquals, - assertThrows, -} from "jsr:@std/assert@1"; -import { describe, it } from "@std/testing@0.225.3/bdd"; -import { User } from "./user.ts"; - -const userTests = describe("User"); - -it(userTests, "users initially empty", () => { - assertEquals(User.users.size, 0); -}); - -it(userTests, "constructor", () => { - try { - const user = new User("Kyle"); - assertEquals(user.name, "Kyle"); - assertStrictEquals(User.users.get("Kyle"), user); - } finally { - User.users.clear(); - } -}); - -const ageTests = describe({ - name: "age", - suite: userTests, - beforeEach(this: { user: User }) { - this.user = new User("Kyle"); - }, - afterEach() { - User.users.clear(); - }, -}); - -it(ageTests, "getAge", function () { - const { user } = this; - assertThrows(() => user.getAge(), Error, "Age unknown"); - user.age = 18; - assertEquals(user.getAge(), 18); -}); - -it(ageTests, "setAge", function () { - const { user } = this; - user.setAge(18); - assertEquals(user.getAge(), 18); -}); -``` - -### Mixed test grouping - -Both nested test grouping and flat test grouping can be used together. This can -be useful if you'd like to create deep groupings without all the extra -indentation in front of each line. - -```ts -import { - assertEquals, - assertStrictEquals, - assertThrows, -} from "jsr:@std/assert@1"; -import { describe, it } from "jsr:@std/testing@0.225.3/bdd"; -import { User } from "./user.ts"; - -describe("User", () => { - it("users initially empty", () => { - assertEquals(User.users.size, 0); - }); - - it("constructor", () => { - try { - const user = new User("Kyle"); - assertEquals(user.name, "Kyle"); - assertStrictEquals(User.users.get("Kyle"), user); - } finally { - User.users.clear(); - } - }); - - const ageTests = describe({ - name: "age", - beforeEach(this: { user: User }) { - this.user = new User("Kyle"); - }, - afterEach() { - User.users.clear(); - }, - }); - - it(ageTests, "getAge", function () { - const { user } = this; - assertThrows(() => user.getAge(), Error, "Age unknown"); - user.age = 18; - assertEquals(user.getAge(), 18); - }); - - it(ageTests, "setAge", function () { - const { user } = this; - user.setAge(18); - assertEquals(user.getAge(), 18); - }); -}); -``` diff --git a/runtime/manual/basics/testing/coverage.md b/runtime/manual/basics/testing/coverage.md deleted file mode 100644 index e425afeb0..000000000 --- a/runtime/manual/basics/testing/coverage.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -title: "Test Coverage" ---- - -Deno will collect test coverage into a directory for your code if you specify -the `--coverage` flag when starting `deno test`. This coverage information is -acquired directly from the JavaScript engine (V8), ensuring high accuracy. - -This can then be further processed from the internal format into well known -formats by the `deno coverage` tool. - -> ⚠️ To ensure consistent coverage results, make sure to process coverage data -> immediately after running tests. Otherwise source code and collected coverage -> data might get out of sync and unexpectedly show uncovered lines. - -The `--clean` flag has been introduced to the test runner -[with Deno v1.44](https://deno.com/blog/v1.44#clean-coverage-directory-on-test-runs). -This flag empties the coverage directory before running the test suite, -preventing outdated coverage data from long-deleted files from lingering. -However, be aware that this flag will cause conflicts when running multiple -`deno test` commands in parallel or in series, and then viewing the aggregated -coverage report. If you are running tests in parallel, you should not use this -flag. If running in series, only pass this flag to the first `deno test` -invocation. - -```bash -# Go into your project's working directory -git clone https://github.com/oakserver/oak && cd oak - -# Collect your coverage profile with deno test --coverage= -deno test --coverage=cov_profile - -# Use the --clean flag if you need to ensure old coverage data is cleared -deno test --coverage=cov_profile --clean - -# From this you can get a pretty printed diff of uncovered lines -deno coverage cov_profile - -# Or generate an HTML report -deno coverage cov_profile --html - -# Or generate an lcov report -deno coverage cov_profile --lcov --output=cov_profile.lcov - -# Which can then be further processed by tools like genhtml -genhtml -o cov_profile/html cov_profile.lcov -``` - -By default, `deno coverage` will exclude any files matching the regular -expression `test\.(ts|tsx|mts|js|mjs|jsx|cjs|cts)` and only consider including -specifiers matching the regular expression `^file:` - ie. remote files will be -excluded from coverage report. - -These filters can be overridden using the `--exclude` and `--include` flags. A -module specifier must _match_ the include_regular expression and _not match_ the -exclude_ expression for it to be a part of the report. diff --git a/runtime/manual/basics/testing/documentation.md b/runtime/manual/basics/testing/documentation.md deleted file mode 100644 index f9b379d02..000000000 --- a/runtime/manual/basics/testing/documentation.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: "Documentation Tests" -oldUrl: /runtime/manual/testing/documentation/ ---- - -Deno supports type-checking your documentation examples. - -This makes sure that examples within your documentation are up to date and -working. - -The basic idea is this: - -````ts -/** - * # Examples - * - * ```ts - * const x = 42; - * ``` - */ -```` - -The triple backticks mark the start and end of code blocks, the language is -determined by the language identifier attribute which may be any of the -following: - -- `js` -- `jsx` -- `ts` -- `tsx` - -If no language identifier is specified then the language is inferred from media -type of the source document that the code block is extracted from. - -If this example was in a file named foo.ts, running `deno test --doc foo.ts` -will extract this example, and then type-check it as a standalone module living -in the same directory as the module being documented. - -To document your exports, import the module using a relative path specifier: - -````ts -/** - * # Examples - * - * ```ts - * import { foo } from "./foo.ts"; - * ``` - */ -export function foo(): string { - return "foo"; -} -```` diff --git a/runtime/manual/basics/testing/index.md b/runtime/manual/basics/testing/index.md deleted file mode 100644 index ce5d0c1a2..000000000 --- a/runtime/manual/basics/testing/index.md +++ /dev/null @@ -1,514 +0,0 @@ ---- -title: "Testing in Deno" -oldUrl: /runtime/manual/testing/ ---- - -Deno has a built-in test runner that you can use for testing JavaScript or -TypeScript code. - -## Quickstart - -Firstly, let's create a file `url_test.ts` and register a test case using -`Deno.test()` function. - -```ts -// url_test.ts -import { assertEquals } from "jsr:@std/assert@1"; - -Deno.test("url test", () => { - const url = new URL("./foo.js", "https://deno.land/"); - assertEquals(url.href, "https://deno.land/foo.js"); -}); -``` - -Secondly, run the test using `deno test` subcommand. - -```sh -$ deno test url_test.ts -running 1 test from file:///dev/url_test.js -test url test ... ok (2ms) - -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (9ms) -``` - -## Writing tests - -To define a test you need to register it with a call to `Deno.test` API. There -are multiple overloads of this API to allow for greatest flexibility and easy -switching between the forms (eg. when you need to quickly focus a single test -for debugging, using `only: true` option): - -```ts -import { assertEquals } from "jsr:@std/assert@1"; - -// Compact form: name and function -Deno.test("hello world #1", () => { - const x = 1 + 2; - assertEquals(x, 3); -}); - -// Compact form: named function. -Deno.test(function helloWorld3() { - const x = 1 + 2; - assertEquals(x, 3); -}); - -// Longer form: test definition. -Deno.test({ - name: "hello world #2", - fn: () => { - const x = 1 + 2; - assertEquals(x, 3); - }, -}); - -// Similar to compact form, with additional configuration as a second argument. -Deno.test("hello world #4", { permissions: { read: true } }, () => { - const x = 1 + 2; - assertEquals(x, 3); -}); - -// Similar to longer form, with test function as a second argument. -Deno.test( - { name: "hello world #5", permissions: { read: true } }, - () => { - const x = 1 + 2; - assertEquals(x, 3); - }, -); - -// Similar to longer form, with a named test function as a second argument. -Deno.test({ permissions: { read: true } }, function helloWorld6() { - const x = 1 + 2; - assertEquals(x, 3); -}); -``` - -### Async functions - -You can also test asynchronous code by passing a test function that returns a -promise. For this you can use the `async` keyword when defining a function: - -```ts -import { delay } from "jsr:@std/async@1/delay"; - -Deno.test("async hello world", async () => { - const x = 1 + 2; - - // await some async task - await delay(100); - - if (x !== 3) { - throw Error("x should be equal to 3"); - } -}); -``` - -### Test steps - -The test steps API provides a way to report distinct steps within a test and do -setup and teardown code within that test. - -```ts -import { assertEquals } from "jsr:@std/assert@1"; -import { Client } from "https://deno.land/x/postgres@v0.15.0/mod.ts"; - -interface User { - id: number; - name: string; -} - -interface Book { - id: number; - title: string; -} - -Deno.test("database", async (t) => { - const client = new Client({ - user: "user", - database: "test", - hostname: "localhost", - port: 5432, - }); - await client.connect(); - - // provide a step name and function - await t.step("insert user", async () => { - const users = await client.queryObject( - "INSERT INTO users (name) VALUES ('Deno') RETURNING *", - ); - assertEquals(users.rows.length, 1); - assertEquals(users.rows[0].name, "Deno"); - }); - - // or provide a test definition - await t.step({ - name: "insert book", - fn: async () => { - const books = await client.queryObject( - "INSERT INTO books (name) VALUES ('The Deno Manual') RETURNING *", - ); - assertEquals(books.rows.length, 1); - assertEquals(books.rows[0].title, "The Deno Manual"); - }, - ignore: false, - // these default to the parent test or step's value - sanitizeOps: true, - sanitizeResources: true, - sanitizeExit: true, - }); - - // nested steps are also supported - await t.step("update and delete", async (t) => { - await t.step("update", () => { - // even though this test throws, the outer promise does not reject - // and the next test step will run - throw new Error("Fail."); - }); - - await t.step("delete", () => { - // ...etc... - }); - }); - - // steps return a value saying if they ran or not - const testRan = await t.step({ - name: "copy books", - fn: () => { - // ...etc... - }, - ignore: true, // was ignored, so will return `false` - }); - - // steps can be run concurrently if sanitizers are disabled on sibling steps - const testCases = [1, 2, 3]; - await Promise.all(testCases.map((testCase) => - t.step({ - name: `case ${testCase}`, - fn: async () => { - // ...etc... - }, - sanitizeOps: false, - sanitizeResources: false, - sanitizeExit: false, - }) - )); - - client.end(); -}); -``` - -Outputs: - -```console -test database ... - test insert user ... ok (2ms) - test insert book ... ok (14ms) - test update and delete ... - test update ... FAILED (17ms) - Error: Fail. - at - test delete ... ok (19ms) - FAILED (46ms) - test copy books ... ignored (0ms) - test case 1 ... ok (14ms) - test case 2 ... ok (14ms) - test case 3 ... ok (14ms) -FAILED (111ms) -``` - -Notes: - -1. Test steps **must be awaited** before the parent test/step function resolves - or you will get a runtime error. -2. Test steps cannot be run concurrently unless sanitizers on a sibling step or - parent test are disabled. -3. If nesting steps, ensure you specify a parameter for the parent step. - ```ts - Deno.test("my test", async (t) => { - await t.step("step", async (t) => { - // note the `t` used here is for the parent step and not the outer `Deno.test` - await t.step("sub-step", () => { - }); - }); - }); - ``` - -## Running tests - -To run the test, call `deno test` with the file that contains your test -function. You can also omit the file name, in which case all tests in the -current directory (recursively) that match the glob -`{*_,*.,}test.{ts, tsx, mts, js, mjs, jsx}` will be run. If you pass a -directory, all files in the directory that match this glob will be run. - -The glob expands to: - -- files named `test.{ts, tsx, mts, js, mjs, jsx}`, -- or files ending with `.test.{ts, tsx, mts, js, mjs, jsx}`, -- or files ending with `_test.{ts, tsx, mts, js, mjs, jsx}` - -```shell -# Run all tests in the current directory and all sub-directories -deno test - -# Run all tests in the util directory -deno test util/ - -# Run just my_test.ts -deno test my_test.ts - -# Run test modules in parallel -deno test --parallel -``` - -Note that starting in Deno v1.24, some test options can be configured via -[a configuration file](../../getting_started/configuration_file.md). - -> ⚠️ If you want to pass additional CLI arguments to the test files use `--` to -> inform Deno that remaining arguments are scripts arguments. - -```shell -# Pass additional arguments to the test file -deno test my_test.ts -- -e --foo --bar -``` - -`deno test` uses the same permission model as `deno run` and therefore will -require, for example, `--allow-write` to write to the file system during -testing. - -To see all runtime options with `deno test`, you can reference the command line -help: - -```shell -deno help test -``` - -## Filtering - -There are a number of options to filter the tests you are running. - -### Command line filtering - -Tests can be run individually or in groups using the command line `--filter` -option. - -The filter flags accept a string or a pattern as value. - -Assuming the following tests: - -```ts -Deno.test({ name: "my-test", fn: myTest }); -Deno.test({ name: "test-1", fn: test1 }); -Deno.test({ name: "test-2", fn: test2 }); -``` - -This command will run all of these tests because they all contain the word -"test". - -```shell -deno test --filter "test" tests/ -``` - -On the flip side, the following command uses a pattern and will run the second -and third tests. - -```shell -deno test --filter "/test-*\d/" tests/ -``` - -_To let Deno know that you want to use a pattern, wrap your filter with -forward-slashes like the JavaScript syntactic sugar for a REGEX._ - -### Including and excluding paths in the configuration file - -You can also filter tests by specifying paths to include or exclude in the Deno -configuration file. - -For example, if you want to only test `src/fetch_test.ts` and -`src/signal_test.ts` and exclude everything in `out/`: - -```json -{ - "test": { - "include": [ - "src/fetch_test.ts", - "src/signal_test.ts" - ] - } -} -``` - -Or more likely: - -```json -{ - "test": { - "exclude": ["out/"] - } -} -``` - -Then running `deno test` in the same directory tree as the configuration file -will take these options into account. - -### Test definition filtering - -Within the tests themselves, you have two options for filtering. - -#### Filtering out (Ignoring these tests) - -Sometimes you want to ignore tests based on some sort of condition (for example -you only want a test to run on Windows). For this you can use the `ignore` -boolean in the test definition. If it is set to true the test will be skipped. - -```ts -Deno.test({ - name: "do macOS feature", - ignore: Deno.build.os !== "darwin", - fn() { - // do MacOS feature here - }, -}); -``` - -#### Filtering in (Only run these tests) - -Sometimes you may be in the middle of a problem within a large test class and -you would like to focus on just that test and ignore the rest for now. For this -you can use the `only` option to tell the test framework to only run tests with -this set to true. Multiple tests can set this option. While the test run will -report on the success or failure of each test, the overall test run will always -fail if any test is flagged with `only`, as this is a temporary measure only -which disables nearly all of your tests. - -```ts -Deno.test({ - name: "Focus on this test only", - only: true, - fn() { - // test complicated stuff here - }, -}); -``` - -## Failing fast - -If you have a long-running test suite and wish for it to stop on the first -failure, you can specify the `--fail-fast` flag when running the suite. - -```shell -deno test --fail-fast -``` - -## Reporters - -Deno ships with three built-in reporters: - -- `pretty` (default) -- `dot` -- `junit` - -You can specify the reporter to use with the `--reporter` flag. - -```shell -# use default pretty reporter -$ deno test - -# use dot reporter with concise output -$ deno test --reporter=dot - -# use JUnit reporter -$ deno test --reporter=junit -``` - -You can also write the output of machine-readable JUnit report to a file, while -still enjoying human-readable output in the terminal. In such situations specify -`--junit-path` flag: - -```shell -$ deno test --junit-path=./report.xml -``` - -### Example: spying on a function with Sinon - -Test spies are function stand-ins that are used to assert if a function's -internal behavior matches expectations. Sinon is a widely used testing library -that provides test spies and can be used in Deno by importing it from NPM: - -```js -import sinon from "npm:sinon"; -``` - -Say we have two functions, `foo` and `bar` and want to assert that `bar` is -called during execution of `foo`. There are a few ways to achieve this with -Sinon, one is to have function `foo` take another function as a parameter: - -```js -// my_file.js -export function bar() {/*...*/} - -export function foo(fn) { - fn(); -} -``` - -This way, we can call `foo(bar)` in the application code or wrap a spy function -around `bar` and call `foo(spy)` in the testing code: - -```js -import sinon from "npm:sinon"; -import { assertEquals } from "jsr:@std/assert@1"; -import { bar, foo } from "./my_file.js"; - -Deno.test("calls bar during execution of foo", () => { - // create a test spy that wraps 'bar' - const spy = sinon.spy(bar); - - // call function 'foo' and pass the spy as an argument - foo(spy); - - assertEquals(spy.called, true); - assertEquals(spy.getCalls().length, 1); -}); -``` - -If you prefer not to add additional parameters for testing purposes only, you -can also use `sinon` to wrap a method on an object instead. In other JavaScript -environments `bar` might have been accessible via a global such as `window` and -callable via `sinon.spy(window, "bar")`, but in Deno this will not work and -instead you can `export` an object with the functions to be tested. This means -rewriting `my_file.js` to something like this: - -```js -// my_file.js -function bar() {/*...*/} - -export const funcs = { - bar, -}; - -// 'foo' no longer takes a parameter, but calls 'bar' from an object -export function foo() { - funcs.bar(); -} -``` - -And then `import` in a test file: - -```js -import sinon from "npm:sinon"; -import { assertEquals } from "jsr:@std/assert@1"; -import { foo, funcs } from "./my_file.js"; - -Deno.test("calls bar during execution of foo", () => { - // create a test spy that wraps 'bar' on the 'funcs' object - const spy = sinon.spy(funcs, "bar"); - - // call function 'foo' without an argument - foo(); - - assertEquals(spy.called, true); - assertEquals(spy.getCalls().length, 1); -}); -``` diff --git a/runtime/manual/basics/testing/mocking.md b/runtime/manual/basics/testing/mocking.md deleted file mode 100644 index cf03c36ca..000000000 --- a/runtime/manual/basics/testing/mocking.md +++ /dev/null @@ -1,241 +0,0 @@ ---- -title: "Mocking" ---- - -Test spies are function stand-ins that are used to assert if a function's -internal behavior matches expectations. Test spies on methods keep the original -behavior but allow you to test how the method is called and what it returns. -Test stubs are an extension of test spies that also replaces the original -method's behavior. - -## Spying - -Say we have two functions, `square` and `multiply`, if we want to assert that -the `multiply` function is called during execution of the `square` function we -need a way to spy on the `multiply` function. There are a few ways to achieve -this with Spies, one is to have the `square` function take the `multiply` as a -parameter. - -```ts title="math.ts" -export function multiply(a: number, b: number): number { - return a * b; -} - -export function square( - multiplyFn: (a: number, b: number) => number, - value: number, -): number { - return multiplyFn(value, value); -} -``` - -This way, we can call `square(multiply, value)` in the application code or wrap -a spy function around the `multiply` function and call -`square(multiplySpy, value)` in the testing code. - -```ts title="math_test.ts" -import { - assertSpyCall, - assertSpyCalls, - spy, -} from "jsr:@std/testing@0.225.3/mock"; -import { assertEquals } from "jsr:@std/assert@1"; -import { multiply, square } from "./math.ts"; - -Deno.test("square calls multiply and returns results", () => { - const multiplySpy = spy(multiply); - - assertEquals(square(multiplySpy, 5), 25); - - // asserts that multiplySpy was called at least once and details about the first call. - assertSpyCall(multiplySpy, 0, { - args: [5, 5], - returned: 25, - }); - - // asserts that multiplySpy was only called once. - assertSpyCalls(multiplySpy, 1); -}); -``` - -If you prefer not adding additional parameters for testing purposes only, you -can use spy to wrap a method on an object instead. In the following example, the -exported `_internals` object has the `multiply` function we want to call as a -method and the `square` function calls `_internals.multiply` instead of -`multiply`. - -```ts title="math_with_internals.ts" -export function multiply(a: number, b: number): number { - return a * b; -} - -export function square(value: number): number { - return _internals.multiply(value, value); -} - -export const _internals = { multiply }; -``` - -This way, we can call `square(value)` in both the application code and testing -code. Then spy on the `multiply` method on the `_internals` object in the -testing code to be able to spy on how the `square` function calls the `multiply` -function. - -```ts title="math_with_internals_test.ts" -import { - assertSpyCall, - assertSpyCalls, - spy, -} from "jsr:@std/testing@0.225.3/mock"; -import { assertEquals } from "jsr:@std/assert@1"; -import { _internals, square } from "./math_with_internals.ts"; - -Deno.test("square calls multiply and returns results", () => { - const multiplySpy = spy(_internals, "multiply"); - - try { - assertEquals(square(5), 25); - } finally { - // unwraps the multiply method on the _internals object - multiplySpy.restore(); - } - - // asserts that multiplySpy was called at least once and details about the first call. - assertSpyCall(multiplySpy, 0, { - args: [5, 5], - returned: 25, - }); - - // asserts that multiplySpy was only called once. - assertSpyCalls(multiplySpy, 1); -}); -``` - -One difference you may have noticed between these two examples is that in the -second we call the `restore` method on `multiplySpy` function. That is needed to -remove the spy wrapper from the `_internals` object's `multiply` method. The -`restore` method is called in a finally block to ensure that it is restored -whether or not the assertion in the try block is successful. The `restore` -method didn't need to be called in the first example because the `multiply` -function was not modified in any way like the `_internals` object was in the -second example. - -## Stubbing - -Say we have two functions, `randomMultiple` and `randomInt`, if we want to -assert that `randomInt` is called during execution of `randomMultiple` we need a -way to spy on the `randomInt` function. That could be done with either of the -spying techniques previously mentioned. To be able to verify that the -`randomMultiple` function returns the value we expect it to for what `randomInt` -returns, the easiest way would be to replace the `randomInt` function's behavior -with more predictable behavior. - -You could use the first spying technique to do that but that would require -adding a `randomInt` parameter to the `randomMultiple` function. - -You could also use the second spying technique to do that, but your assertions -would not be as predictable due to the `randomInt` function returning random -values. - -Say we want to verify it returns correct values for both negative and positive -random integers. We could easily do that with stubbing. The below example is -similar to the second spying technique example but instead of passing the call -through to the original `randomInt` function, we are going to replace -`randomInt` with a function that returns pre-defined values. - -```ts title="random.ts" -export function randomInt(lowerBound: number, upperBound: number): number { - return lowerBound + Math.floor(Math.random() * (upperBound - lowerBound)); -} - -export function randomMultiple(value: number): number { - return value * _internals.randomInt(-10, 10); -} - -export const _internals = { randomInt }; -``` - -The mock module includes some helper functions to make creating common stubs -easy. The `returnsNext` function takes an array of values we want it to return -on consecutive calls. - -```ts title="random_test.ts" -import { - assertSpyCall, - assertSpyCalls, - returnsNext, - stub, -} from "jsr:@std/testing@0.225.3/mock"; -import { assertEquals } from "jsr:@std/assert@1"; -import { _internals, randomMultiple } from "./random.ts"; - -Deno.test("randomMultiple uses randomInt to generate random multiples between -10 and 10 times the value", () => { - const randomIntStub = stub(_internals, "randomInt", returnsNext([-3, 3])); - - try { - assertEquals(randomMultiple(5), -15); - assertEquals(randomMultiple(5), 15); - } finally { - // unwraps the randomInt method on the _internals object - randomIntStub.restore(); - } - - // asserts that randomIntStub was called at least once and details about the first call. - assertSpyCall(randomIntStub, 0, { - args: [-10, 10], - returned: -3, - }); - // asserts that randomIntStub was called at least twice and details about the second call. - assertSpyCall(randomIntStub, 1, { - args: [-10, 10], - returned: 3, - }); - - // asserts that randomIntStub was only called twice. - assertSpyCalls(randomIntStub, 2); -}); -``` - -## Faking time - -Say we have a function that has time based behavior that we would like to test. -With real time, that could cause tests to take much longer than they should. If -you fake time, you could simulate how your function would behave over time -starting from any point in time. Below is an example where we want to test that -the callback is called every second. - -```ts title="interval.ts" -export function secondInterval(cb: () => void): number { - return setInterval(cb, 1000); -} -``` - -With `FakeTime` we can do that. When the `FakeTime` instance is created, it -splits from real time. The `Date`, `setTimeout`, `clearTimeout`, `setInterval` -and `clearInterval` globals are replaced with versions that use the fake time -until real time is restored. You can control how time ticks forward with the -`tick` method on the `FakeTime` instance. - -```ts title="interval_test.ts" -import { assertSpyCalls, spy } from "jsr:@std/testing@0.225.3/mock"; -import { FakeTime } from "jsr:@std/testing@0.225.3/time.ts"; -import { secondInterval } from "./interval.ts"; - -Deno.test("secondInterval calls callback every second and stops after being cleared", () => { - using time = new FakeTime(); - - const cb = spy(); - const intervalId = secondInterval(cb); - assertSpyCalls(cb, 0); - time.tick(500); - assertSpyCalls(cb, 0); - time.tick(500); - assertSpyCalls(cb, 1); - time.tick(3500); - assertSpyCalls(cb, 4); - - clearInterval(intervalId); - time.tick(1000); - assertSpyCalls(cb, 4); -}); -``` diff --git a/runtime/manual/basics/testing/sanitizers.md b/runtime/manual/basics/testing/sanitizers.md deleted file mode 100644 index 2d11f8f00..000000000 --- a/runtime/manual/basics/testing/sanitizers.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: "Test Sanitizers" ---- - -The test runner offers several sanitizers to ensure that the test behaves in a -reasonable and expected way. - -## Resource sanitizer - -Certain actions in Deno create resources in the resource table -([learn more here](../../references/contributing/architecture.md)). - -These resources should be closed after you are done using them. - -For each test definition, the test runner checks that all resources created in -this test have been closed. This is to prevent resource 'leaks'. This is enabled -by default for all tests, but can be disabled by setting the `sanitizeResources` -boolean to false in the test definition. - -```ts -Deno.test({ - name: "leaky resource test", - async fn() { - await Deno.open("hello.txt"); - }, - sanitizeResources: false, -}); -``` - -## Op sanitizer - -The same is true for async operation like interacting with the filesystem. The -test runner checks that each operation you start in the test is completed before -the end of the test. This is enabled by default for all tests, but can be -disabled by setting the `sanitizeOps` boolean to false in the test definition. - -```ts -Deno.test({ - name: "leaky operation test", - fn() { - crypto.subtle.digest( - "SHA-256", - new TextEncoder().encode("a".repeat(100000000)), - ); - }, - sanitizeOps: false, -}); -``` - -## Exit sanitizer - -There's also the exit sanitizer which ensures that tested code doesn't call -`Deno.exit()` signaling a false test success. - -This is enabled by default for all tests, but can be disabled by setting the -`sanitizeExit` boolean to false in the test definition. - -```ts -Deno.test({ - name: "false success", - fn() { - Deno.exit(0); - }, - sanitizeExit: false, -}); - -// This test never runs, because the process exits during "false success" test -Deno.test({ - name: "failing test", - fn() { - throw new Error("this test fails"); - }, -}); -``` diff --git a/runtime/manual/basics/testing/snapshot_testing.md b/runtime/manual/basics/testing/snapshot_testing.md deleted file mode 100644 index e895e8f2e..000000000 --- a/runtime/manual/basics/testing/snapshot_testing.md +++ /dev/null @@ -1,434 +0,0 @@ ---- -title: "Snapshot Testing" ---- - -The Deno standard library comes with a -[snapshot module](https://jsr.io/@std/testing/doc/snapshot/~), which enables -developers to write tests which assert a value against a reference snapshot. -This reference snapshot, is a serialized representation of the original value -and is stored alongside the test file. - -Snapshot testing can be useful in many cases, as it enables catching a wide -array of bugs with very little code. It is particularly helpful in situations -where it is difficult to precisely express what should be asserted, without -requiring a prohibitive amount of code, or where the assertions a test makes are -expected to change often. It therefore lends itself especially well to use in -the development of front ends and CLIs. - -## Basic usage - -The `assertSnapshot` function will create a snapshot of a value and compare it -to a reference snapshot, which is stored alongside the test file in the -`__snapshots__` directory. - -```ts title="example_test.ts" -import { assertSnapshot } from "jsr:@std/testing@0.225.3/snapshot"; - -Deno.test("isSnapshotMatch", async function (t): Promise { - const a = { - hello: "world!", - example: 123, - }; - await assertSnapshot(t, a); -}); -``` - -```js title="__snapshots__/example_test.ts.snap" -export const snapshot = {}; - -snapshot[`isSnapshotMatch 1`] = ` -{ - example: 123, - hello: "world!", -} -`; -``` - -Calling `assertSnapshot` in a test will throw an `AssertionError`, causing the -test to fail, if the snapshot created during the test does not match the one in -the snapshot file. - -## Creating and updating snapshots - -When adding new snapshot assertions to your test suite, or when intentionally -making changes which cause your snapshots to fail, you can update your snapshots -by running the snapshot tests in update mode. Tests can be run in update mode by -passing the `--update` or `-u` flag as an argument when running the test. When -this flag is passed, then any snapshots which do not match will be updated. - -```sh -deno test --allow-all -- --update -``` - -Additionally, new snapshots will only be created when this flag is present. - -## Permissions - -When running snapshot tests, the `--allow-read` permission must be enabled, or -else any calls to `assertSnapshot` will fail due to insufficient permissions. -Additionally, when updating snapshots, the `--allow-write` permission must also -be enabled, as this is required in order to update snapshot files. - -The `assertSnapshot` function will only attempt to read from and write to -snapshot files. As such, the allow list for `--allow-read` and `--allow-write` -can be limited to only include existing snapshot files, if so desired. - -## Version Control - -Snapshot testing works best when changes to snapshot files are committed -alongside other code changes. This allows for changes to reference snapshots to -be reviewed along side the code changes that caused them, and ensures that when -others pull your changes, their tests will pass without needing to update -snapshots locally. - -## Advanced Usage - -### Options - -The `assertSnapshot` function can also be called with an options object which -offers greater flexibility and enables some non standard use cases. - -```ts -import { assertSnapshot } from "jsr:@std/testing@0.225.3/snapshot"; - -Deno.test("isSnapshotMatch", async function (t): Promise { - const a = { - hello: "world!", - example: 123, - }; - await assertSnapshot(t, a, { - // options - }); -}); -``` - -**`serializer`** - -The `serializer` option allows you to provide a custom serializer function. This -will be called by `assertSnapshot` and be passed the value being asserted. It -should return a string. It is important that the serializer function is -deterministic i.e. that it will always produce the same output, given the same -input. - -The result of the serializer function will be written to the snapshot file in -update mode, and in assert mode will be compared to the snapshot stored in the -snapshot file. - -```ts title="example_test.ts" -import { assertSnapshot, serialize } from "jsr:@std/testing@0.225.3/snapshot"; -import { stripColor } from "jsr:@std/fmt@0.225.6/colors"; - -/** - * Serializes `actual` and removes ANSI escape codes. - */ -function customSerializer(actual: string) { - return serialize(stripColor(actual)); -} - -Deno.test("Custom Serializer", async function (t): Promise { - const output = "\x1b[34mHello World!\x1b[39m"; - await assertSnapshot(t, output, { - serializer: customSerializer, - }); -}); -``` - -```js title="__snapshots__/example_test.ts.snap" -export const snapshot = {}; - -snapshot[`Custom Serializer 1`] = `"Hello World!"`; -``` - -Custom serializers can be useful in a variety of cases. One possible use case is -to discard information which is not relevant and/or to present the serialized -output in a more human readable form. - -For example, the above code snippet shows how a custom serializer could be used -to remove ANSI escape codes (which encode font color and styles in CLI -applications), making the snapshot more readable than it would be otherwise. - -Other common use cases would be to obfuscate values which are non-deterministic -or which you may not want to write to disk for other reasons. For example, -timestamps or file paths. - -Note that the default serializer is exported from the snapshot module so that -its functionality can be easily extended. - -**`dir` and `path`** - -The `dir` and `path` options allow you to control where the snapshot file will -be saved to and read from. These can be absolute paths or relative paths. If -relative, the they will be resolved relative to the test file. - -For example, if your test file is located at `/path/to/test.ts` and the `dir` -option is set to `snapshots`, then the snapshot file would be written to -`/path/to/snapshots/test.ts.snap`. - -As shown in the above example, the `dir` option allows you to specify the -snapshot directory, while still using the default format for the snapshot file -name. - -In contrast, the `path` option allows you to specify the directory and file name -of the snapshot file. - -For example, if your test file is located at `/path/to/test.ts` and the `path` -option is set to `snapshots/test.snapshot`, then the snapshot file would be -written to `/path/to/snapshots/test.snapshot`. - -If both `dir` and `path` are specified, the `dir` option will be ignored and the -`path` option will be handled as normal. - -**`mode`** - -The `mode` option can be either `assert` or `update`. When set, the `--update` -and `-u` flags will be ignored. - -If the `mode` option is set to `assert`, then `assertSnapshot` will always -behave as though the update flag is not passed i.e. if the snapshot does not -match the one saved in the snapshot file, then an `AssertionError` will be -thrown. - -If the `mode` option is set to `update`, then `assertSnapshot` will always -behave as though the update flag has been passed i.e. if the snapshot does not -match the one saved in the snapshot file, then the snapshot will be updated -after all tests have run. - -**`name`** - -The `name` option specifies the name of the snapshot. By default, the name of -the test step will be used. However, if specified, the `name` option will be -used instead. - -```ts title="example_test.ts" -import { assertSnapshot } from "jsr:@std/testing@0.225.3/snapshot"; - -Deno.test("isSnapshotMatch", async function (t): Promise { - const a = { - hello: "world!", - example: 123, - }; - await assertSnapshot(t, a, { - name: "Test Name", - }); -}); -``` - -```js title="__snapshots__/example_test.ts.snap" -export const snapshot = {}; - -snapshot[`Test Name 1`] = ` -{ - example: 123, - hello: "world!", -} -`; -``` - -When `assertSnapshot` is run multiple times with the same value for `name`, then -the suffix will be incremented as normal. i.e. `Test Name 1`, `Test Name 2`, -`Test Name 3`, etc. - -**`msg`** - -Allows setting a custom error message to use. This will overwrite the default -error message, which includes the diff for failed snapshots. - -### Default Options - -You can configure default options for `assertSnapshot`. - -```ts title="example_test.ts" -import { createAssertSnapshot } from "jsr:@std/testing@0.225.3/snapshot"; - -const assertSnapshot = createAssertSnapshot({ - // options -}); -``` - -When configuring default options like this, the resulting `assertSnapshot` -function will function the same as the default function exported from the -snapshot module. If passed an optional options object, this will take precedence -over the default options, where the value provided for an option differs. - -It is possible to "extend" an `assertSnapshot` function which has been -configured with default options. - -```ts title="example_test.ts" -import { createAssertSnapshot } from "jsr:@std/testing@0.225.3/snapshot"; -import { stripColor } from "jsr:@std/fmt@0.225.6/colors"; - -const assertSnapshot = createAssertSnapshot({ - dir: ".snaps", -}); - -const assertMonochromeSnapshot = createAssertSnapshot( - { serializer: stripColor }, - assertSnapshot, -); - -Deno.test("isSnapshotMatch", async function (t): Promise { - const a = "\x1b[32mThis green text has had it's colours stripped\x1b[39m"; - await assertMonochromeSnapshot(t, a); -}); -``` - -```js title=".snaps/example_test.ts.snap" -export const snapshot = {}; - -snapshot[`isSnapshotMatch 1`] = `This green text has had it's colours stripped`; -``` - -### Serialization with `Deno.customInspect` - -The default serialization behaviour can be customised in two ways. The first is -by specifying the `serializer` option. This allows you to control the -serialisation of any value which is passed to a specific `assertSnapshot` call. -See the [above documentation](#options) on the correct usage of this option. - -The second option is to make use of `Deno.customInspect`. Because the default -serializer used by `assertSnapshot` uses `Deno.inspect` under the hood, you can -set property `Symbol.for("Deno.customInspect")` to a custom serialization -function. - -Doing so will ensure that the custom serialization will, by default, be used -whenever the object is passed to `assertSnapshot`. This can be useful in many -cases. One example is shown in the code snippet below. - -```ts title="example_test.ts" -import { assertSnapshot } from "jsr:@std/testing@0.225.3/snapshot"; - -class HTMLTag { - constructor( - public name: string, - public children: Array = [], - ) {} - - public render(depth: number) { - const indent = " ".repeat(depth); - let output = `${indent}<${this.name}>\n`; - for (const child of this.children) { - if (child instanceof HTMLTag) { - output += `${child.render(depth + 1)}\n`; - } else { - output += `${indent} ${child}\n`; - } - } - output += `${indent}`; - return output; - } - - public [Symbol.for("Deno.customInspect")]() { - return this.render(0); - } -} - -Deno.test("Page HTML Tree", async (t) => { - const page = new HTMLTag("html", [ - new HTMLTag("head", [ - new HTMLTag("title", [ - "Simple SSR Example", - ]), - ]), - new HTMLTag("body", [ - new HTMLTag("h1", [ - "Simple SSR Example", - ]), - new HTMLTag("p", [ - "Ex of customInspect for a snapshot of an SSR representation", - ]), - ]), - ]); - - await assertSnapshot(t, page); -}); -``` - -This test will produce the following snapshot. - -```js title="__snapshots__/example_test.ts.snap" -export const snapshot = {}; - -snapshot[`Page HTML Tree 1`] = ` - - - - Simple SSR Example - - - -

- Simple SSR Example -

-

- Ex of customInspect for a snapshot of an SSR representation -

- - -`; -``` - -In contrast, when we remove the `Deno.customInspect` method, the test will -produce the following snapshot. - -```js title="__snapshots__/example_test.ts.snap" -export const snapshot = {}; - -snapshot[`Page HTML Tree 1`] = ` -HTMLTag { - children: [ - HTMLTag { - children: [ - HTMLTag { - children: [ - "Simple SSR Example", - ], - name: "title", - }, - ], - name: "head", - }, - HTMLTag { - children: [ - HTMLTag { - children: [ - "Simple SSR Example", - ], - name: "h1", - }, - HTMLTag { - children: [ - "Ex of customInspect for a snapshot of an SSR representation", - ], - name: "p", - }, - ], - name: "body", - }, - ], - name: "html", -} -`; -``` - -You can see that this snapshot is much less readable. This is because: - -1. The keys are sorted alphabetically, so the name of the element is displayed - after its children -2. It includes a lot of extra information, causing the snapshot to be more than - twice as long -3. It is not an accurate serialization of the HTML which the data represents - -Note that in this example it would be entirely possible to achieve the same -result by calling: - -```ts -await assertSnapshot(t, page.render(0)); -``` - -However, depending on the public API you choose to expose, this may not be -practical in other cases. - -It is also worth considering that this will have an impact beyond just snapshot -testing. For example, `Deno.customInspect` is also used to serialize objects -when calling `console.log` and in some other cases. This may or may not be -desirable. diff --git a/runtime/manual/getting_started/command_line_interface.md b/runtime/manual/getting_started/command_line_interface.md index 92af941f1..a6de2af53 100644 --- a/runtime/manual/getting_started/command_line_interface.md +++ b/runtime/manual/getting_started/command_line_interface.md @@ -213,7 +213,7 @@ reported. (To turn on type-checking for all modules, use `--check=all`.) ### Permission flags -These are listed [here](../basics/permissions.md#permissions-list). +These are listed [here](TODO:permissions-link). ### Other runtime flags diff --git a/runtime/manual/node/migrate.md b/runtime/manual/node/migrate.md index 3edd4cce0..22b04115c 100644 --- a/runtime/manual/node/migrate.md +++ b/runtime/manual/node/migrate.md @@ -94,12 +94,12 @@ $ deno run server.js └ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all net permissions) > ``` -Deno features [runtime security by default](../basics/permissions.md), meaning -that you as the developer must opt in to giving your code access to the -filesystem, network, system environment, and more. Doing this prevents supply -chain attacks and other potential vulnerabilities in your code. By comparison, -Node.js has no concept of runtime security, with all code executed with the same -level of permission as the user running the code. +Deno features [runtime security by default](TODO:permissions-link), meaning that +you as the developer must opt in to giving your code access to the filesystem, +network, system environment, and more. Doing this prevents supply chain attacks +and other potential vulnerabilities in your code. By comparison, Node.js has no +concept of runtime security, with all code executed with the same level of +permission as the user running the code. To run your code like it would in Node.js, you can pass the `-A` flag to enable all permissions. @@ -109,7 +109,7 @@ deno run -A server.js ``` For more granular control, you can enable access to specific features by opting -in to [individual permissions](../basics/permissions.md). +in to [individual permissions](TODO:permissions-link/#permissions-list). ## Running scripts from `package.json` diff --git a/runtime/manual/node/npm_specifiers.md b/runtime/manual/node/npm_specifiers.md index 288d71792..b076d5089 100644 --- a/runtime/manual/node/npm_specifiers.md +++ b/runtime/manual/node/npm_specifiers.md @@ -41,7 +41,8 @@ import chalk from "npm:chalk@5"; ``` Some packages do not though, but you can specify their types with a -[`@deno-types`](../advanced/typescript/types.md) directive. For example, using a +[`@deno-types`](./runtime/fundamentals/ts_support.md) directive. For example, +using a [`@types`](https://www.typescriptlang.org/docs/handbook/2/type-declarations.html#definitelytyped--types) package: diff --git a/runtime/manual/tools/env_variables.md b/runtime/manual/tools/env_variables.md new file mode 100644 index 000000000..d528e4caf --- /dev/null +++ b/runtime/manual/tools/env_variables.md @@ -0,0 +1,93 @@ +--- +title: "Configuring Deno behavior" +--- + +There are several environment variables which can impact the behavior of Deno: + +### DENO_AUTH_TOKENS + +A list of authorization tokens which can be used to allow Deno to access remote +private code. See the +[Private modules and repositories](../advanced/private_repositories.md) section +for more details. + +### DENO_TLS_CA_STORE + +A list of certificate stores which will be used when establishing TLS +connections. The available stores are `mozilla` and `system`. You can specify +one, both or none. Certificate chains attempt to resolve in the same order in +which you specify them. The default value is `mozilla`. The `mozilla` store will +use the bundled Mozilla certs provided by +[`webpki-roots`](https://crates.io/crates/webpki-roots). The `system` store will +use your platform's +[native certificate store](https://crates.io/crates/rustls-native-certs). The +exact set of Mozilla certs will depend on the version of Deno you are using. If +you specify no certificate stores, then no trust will be given to any TLS +connection without also specifying `DENO_CERT` or `--cert` or specifying a +specific certificate per TLS connection. + +### DENO_CERT + +Load a certificate authority from a PEM encoded file. This "overrides" the +`--cert` option. See the [Proxies](../basics/modules/proxies.md) section for +more information. + +### DENO_DIR + +this will set the directory where cached information from the CLI is stored. +This includes items like cached remote modules, cached transpiled modules, +language server cache information and persisted data from local storage. This +defaults to the operating system's default cache location and then under the +`deno` path. + +### DENO_INSTALL_ROOT + +When using `deno install` where the installed scripts are stored. This defaults +to `$HOME/.deno/bin`. + +### DENO_NO_PACKAGE_JSON + +Set to disable auto-resolution of package.json files. + +### DENO_NO_PROMPT + +Set to disable permission prompts on access (alternative to passing +`--no-prompt` on invocation). + +### DENO_NO_UPDATE_CHECK + +Set to disable checking if a newer Deno version is available. + +### DENO_WEBGPU_TRACE + +The directory to use for WebGPU traces. + +### HTTP_PROXY + +The proxy address to use for HTTP requests. See the +[Proxies](../basics/modules/proxies.md) section for more information. + +### HTTPS_PROXY + +The proxy address to use for HTTPS requests. See the +[Proxies](../basics/modules/proxies.md) section for more information. + +### NO_COLOR + +If set, this will prevent the Deno CLI from sending ANSI color codes when +writing to stdout and stderr. See the website +[https://no-color.org](https://no-color.org/) for more information on this _de +facto_ standard. The value of this flag can be accessed at runtime without +permission to read the environment variables by checking the value of +`Deno.noColor`. + +### NO_PROXY + +Indicates hosts which should bypass the proxy set in the other environment +variables. See the [Proxies](../basics/modules/proxies.md) section for more +information. + +### NPM_CONFIG_REGISTRY + +The npm registry to use when loading modules via +[npm specifiers](../node/npm_specifiers.md) diff --git a/runtime/tutorials/fetch_data.md b/runtime/tutorials/fetch_data.md index c7d9eb06b..a829a50a0 100644 --- a/runtime/tutorials/fetch_data.md +++ b/runtime/tutorials/fetch_data.md @@ -10,7 +10,7 @@ oldUrl: [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). - Deno is secure by default, meaning explicit permission must be granted to access the network. -- See also: Deno's [permissions](../manual/basics/permissions.md) model. +- See also: Deno's [permissions](TODO:permissions-link) model. ## Overview