Deno V1.8 Release Notes
- Deno 1.8 Release Notes
- Bartek Iwańczuk, Luca Casonato, Ryan Dahl
- Translator: @ hylerrix
- Original publication/translation time: 20210302/20210305
- This article belongs to the Deno Research Technique series. The original translation will be updated to Deno Chinese website (TL; DR. If your intensive reading notes are too long, you can read deno-cn.vercel.app/posts here.
Today we released Deno V1.8.0. This release covers a number of new features and standardization efforts:
- Experimental support for the WebGPU API: Paves the way for GPU-accelerated machine learning out of the box in Deno.
- Enable the built-in internationalization API: Supports all JS standards
Intl
API out of the box. - Retrofit test coverage tools: Test coverage supports output
lcov
The report. - Landing the Import Maps standard: Web-compatible dependency rewrite is now available.
- Support for obtaining private modules: You can obtain remote modules from private servers using authorized tokens.
If you already have Deno installed, you can upgrade to version 1.8 by using the Deno upgrade command. If you are experiencing Deno for the first time, you can try using one of the following commands:
# using Shell (macOS and Linux) $curl - fsSL # https://deno.land/x/install/install.sh | sh using PowerShell (Windows) : # $iwr https://deno.land/x/install/install.ps1 - useb | iex use Homebrew (macOS) : $brew install deno # Scoop install deno # Scoop Install deno # Chocolatey (Windows): $choco install denoCopy the code
Intensive reading notes
- CURL: provides a libcurl library and a cURL command to support the transfer of various types of data over multiple network protocols. Starting in 1996, the author’s original idea was to automatically obtain currency rates available on the Internet for IRC (Internet Relay Chat) users. By 2020, the monthly service traffic of new websites will exceed 10TB.
- PowerShell: Task automation and configuration management framework developed by Microsoft. Originally just a Windows component (based on.net Framew), it was opened in 2016 with cross-platform support (based on.NET Core). It consists of a command line Shell and associated scripting languages.
- Homebrew: Missing package manager for MacOS (or Linux), free and open source, based on Ruby and Git. The hatched Homebrew Cask can be used to manage and distribute binary applications.
- Scoop: Command line installer for Windows. More unix-like than PowerShell, which can install programs with minimal setup. You don’t need administrator permission to install applications in your home directory.
- Chocolatey: a software management tool in Windows. Born in 2011, the original idea was to provide a unified package manager for Windows. Built based on modern automation and DevOps methods.
- Download the latest version of Deno using cURL.
Experimental support for the WebGPU API
The WebGPU API gives developers a low-level, high-performance, and cross-platform way to code on GPU hardware through JavaScript. This API is a strong successor to WebGL on the web. The specification is not yet official, but it is currently being supported by Firefox, Chromium and Safari, and Deno is following suit.
The API allows developers to do GPU rendering and general-purpose GPU computing from within Deno. Once the API standardization is complete and the unstable flag is removed from Deno, this will officially give developers an easy way to access GPU resources from the Web, servers, and development machines.
The GPU allows developers to make certain numerical algorithms highly parallel. This is also useful for rendering graphics and outside of the game. Efficient use of gpus in machine learning opens up complex neural networks — often referred to as “deep learning”. The fields of computer vision, translation, image generation, and reinforcement learning are all growing fast thanks to the efficient use of GPU hardware.
Today, most neural networks are defined in Python, and computing is left to the GPU. We believe that JavaScript, rather than Python, can be an ideal language for expressing mathematical ideas, provided the right infrastructure exists. Providing WebGPU support out of the box in Deno is a step in this direction. Our goal is to run Tensorflow.js on Deno by supporting GPU acceleration. We expect this to happen in the coming weeks and months.
Here is a basic example demonstrating how to access a connected GPU device and read the name and the functionality it supports:
/ / execution ` deno run - unstable ` / / https://deno.land/posts/v1.8/webgpu_discover.ts from the user agent to try to get an adapter adapter const adapter = await navigator.gpu.requestAdapter(); Console. log(' Found adapter: ${adapter.name} '); if (adapter) {// Prints some basic details about the adapter console.log(' Found adapter: ${adapter.name} '); const features = [...adapter.features.values()]; console.log(`Supported features: ${features.join(", ")}`); } else { console.error("No adapter found"); }Copy the code
Here is a small example of how a GPU can render a simple red triangle on a green background using a render shader:
$ deno run --unstable --allow-write=output.png https://raw.githubusercontent.com/crowlKats/webgpu-examples/f3b979f57fd471b11a28c5b0c91d0447221ba77b/hello-triangle/mod. tsCopy the code
Note the PNG written in WebAssembly. More examples can be found at: github.com/crowlKats/w…
The final PR took up 155,000 lines of code and took five months to merge after it was followed up. Thanks to crowlKats for leading the integration of WebGPU into Deno. We are also very grateful to all contributors to the WGPU and GFX-RS projects that laid the foundation for WebGPU support in Deno. Special thanks also to Kvark, editor of the WebGPU specification, and lead developers of WebGPU and GFX-RS for providing excellent guidance on implementing the WebGPU API.
Intensive reading notes:
- GPU: graphics processor. Compared to traditional cpus, it is a microprocessor dedicated to graphics computing and designed for general-purpose computing. The parallel architecture of gpus can run large amounts of computation in parallel, which is useful for 3D rendering, running analytics, deep learning, and machine learning algorithms.
- OpenGL: Open graphics library. A cross-language, cross-platform API for rendering 2D and 3D vector graphics, commonly used in CAD, virtual reality, scientific visualization programs and video game development. Although these apis can be implemented entirely by software, they are designed for most or all use of hardware acceleration. Independent of language and platform, purely focused on rendering.
- DirectX: A series of apis created by Microsoft for multimedia and game development. Direct3D and OpenGL are the two most commonly used graphics programming interfaces for computer graphics software and computer games.
- WebGL: JavaScript binding for OpenGL. Use to render interactive 2D and 3D graphics in any compatible Web browser download without the use of plug-ins. WebGL can use image processing and GPU acceleration as part of the Web Canvas. WebGL is based on OpenGL ES, uses HTML5 Canvas and allows the use of the Document Object Model interface. Some JavaScript can be used to realize automatic memory management.
- WebGPU: Exposes future Web standards and JavaScript apis for accelerating graphics and computing on gpus, aiming to provide “modern 3D graphics and computing capabilities.” Unlike WebGL, WebGPU is not the direct port of any existing native API, and can be regarded as the next generation of WebGL from a certain perspective.
- Hardware acceleration: A technique in a computer to reduce the workload of the CPU by assigning very computation-intensive work to specialized hardware.
- Machine learning: A branch of artificial intelligence. Theory is about designing and analyzing algorithms that allow computers to automatically “learn” — algorithms that automatically analyze patterns in data and use those patterns to predict location data.
- Deep learning: it is one of the branches of machine learning. It is an algorithm based on neural network for representation learning of data.
- Neural network: In machine learning and cognitive science, a mathematical or computational model that mimics the structure and function of a biological neural network and is used to estimate or approximate functions. A neural network consists of a large number of artificial neurons connected to perform calculations. In most cases, artificial neural network can change the internal structure on the basis of external information, which is an adaptive system. Widely used in machine vision and speech recognition and other fields.
- Tensorflow.js: Train and deploy the machine learning model using JavaScript, and use the machine learning model directly in the browser or Node.js. Deno will also support it in the future.
- Wgpu: A native WebGPU implementation based on GFX-HAL, written in Rust.
- Gfx-rs: Open source organization focused on portable graphics and computing in Rust.
- Three.js: JavaScript 3D library. The goal is to use the default WebGL renderer to create an easy-to-use, lightweight 3D library that also provides renderers such as Canvas 2D, SVG and CSS 3D. Support for WebGPU rendering is also planned in the future.
Support the ICU
ICU support has become the second most talked about power feature of Deno. We are pleased to announce that Deno V.1.8 is now shipped with full ICU support.
All ICU based JavaScript apis are now compatible with browser apis.
In the REPL you can try the following:
$deno deno 1.8.0 exit using CTRL +d or close() > const d = new Date(date.UTC (2020, 5, 26, 7, 0, 0)); undefined > d.toLocaleString("de-DE", { weekday: "long", year: "numeric", month: "long", day: "numeric", }); "Freitag, 26. Juni 2020"Copy the code
Intensive reading notes:
- ICU: Unicode international component. For Unicode support, software internationalization, and software globalization, widely portable to many operating systems and environments, providing the same results for applications on all platforms as well as between C, C++, and Java.
- ICU provides: Unicode text processing, full character attributes and character set conversions, Unicode regular expressions, full Unicode sets, character/word and line boundaries, language-sensitive collation and search, comprehensive locale data and resource pack architecture through the Common Locale Database Library (CLDR), multiple calendars and time zones, And rule-based date/time/number/currency/good news formatting and parsing.
- Unicode: Codifies and encodes most of the world’s writing systems, making it easier for computers to render and process text. Unicode-based globalization software maximizes market reach and reduces costs. Globalization software can be built and installed at once, but can handle text from users and users from all over the world, and adapt to their cultural conventions. Keep costs to a minimum by eliminating the need to build, install, and maintain updates in every language.
- Common Unicode based character sets: UTF-8, UTF-16, UTF-32, etc. JavaScript uses the old UCS-2 character set that has been dropped for Unicode compatibility (since UTF-16 was not yet available), and ES6 has enhanced Unicode support. According to the standard, JSON data must be utF-8 encoded. Emoji are four bytes.
- The development history of Chinese character set: GB2312 -> GBK -> GB18030 / DBCS.
Improved coverage toolchain:deno coverage
This release adds some powerful new features by expanding our testing coverage of the infrastructure. The major change is that test coverage is now divided into coverage sets and coverage reports.
Previously, coverage collection and reporting were done in a single subcommand, requiring only the –coverage flag to be specified when executing deno test. Now, the — Coverage flag for deno Test takes an argument — the directory path to store the collected configuration files. This is the coverage set. The next step is to call Deno Coverage and specify the directory path for the storage coverage profile. This subcommand can output formatted, friendly text reports to the console, or lCOV files (– lCOV flags) for use by tools such as Genhtml, Coveralls. IO, or codecov.io.
We have been testing this functionality on deno_std for several days. We uploaded the coverage report synchronously to Codecov.io for each submission. You can check it out here: Codecov. IO/GH/Denoland… Only the following 10 lines have been changed on the Github Actions workflow:
- name: Run tests
- run: deno test --unstable --allow-all
+ run: deno test --coverage=./cov --unstable --allow-all
+
+ - name: Generate lcov
+ run: deno coverage --unstable --lcov ./cov > cov.lcov
+
+ - name: Upload coverage
+ uses: codecov/codecov-action@v1
+ with:
+ name: ${{ matrix.os }}-${{ matrix.deno }}
+ files: cov.lcov
Copy the code
For examples of integration with coverals. IO, refer to this repository: github.com/lucacasonat…
Intensive reading notes:
- LCOV: A graphical front end to the GCC coverage testing tool GCOV that collects GCOV data from multiple source files and creates HTML pages containing source code annotated with coverage information. LCOV supports measurement of statement, function, and branch coverage.
- GCOV: is a source code coverage analysis tool that can track the exact number of executions of each statement, etc.
- Genhtml: a utility that comes with the lCOv command to generate HTML pages for browsing from lCOV formatted files.
- Coveralls. IO: Helps analyze test coverage by showing parts of code not covered in tests, while providing badge ICONS for test coverage of open source projects.
- Codecov. IO: an online site that helps generate code coverage. You can upload lCOV files with Github Action and view coverage online.
Import maps are now stable
Standardized Import maps have been supported in Chrome 89, and we have since implemented updates to match the latest version of the specification, which is now considered stable. This means that the — Unstable flag is no longer required for subsequent use of –import-map.
$ deno run --import-map=./import_map.json ./mod.ts
Copy the code
In addition, the –import-map flag now accepts not only local paths but also URL paths, enabling developers to load import maps from remote servers.
$ deno run --import-map=https://example.com/import_map.json ./mod.ts
Copy the code
Import Maps allow users to use so-called “naked” specifiers to indicate dependencies, rather than relative or absolute file addresses /HTTP urls:
// Deno does not support such specifiers by default // But with import maps users can remap bare specifiers to specified URL import * as HTTP from "STD/HTTP ";Copy the code
{" imports ": {" STD/HTTP" : "https://deno.land/[email protected]/http/mod.ts"}}Copy the code
Users should keep in mind that import maps are not composable: this means you can only provide a single import map for deno Run/deno test. Therefore, library authors should still use regular non-” naked “specifiers (relative or absolute file paths/HTTP URLs); Otherwise, library users will need to manually add your library (and your library dependencies) naked specifiers to their own import maps.
A more useful feature of Import Maps is the ability to remap a regular specifier to a completely different specifier. For example, if you have some broken dependencies deeply nested in your module diagram, you can fix them to the specified version before fixing them upstream. Or if you use a build process that injects hash values into module file names, you can import the file directly into the source code (without the hash value) and just remap the specifier at run time using import Maps.
For more examples and detailed instructions, refer to the Import Maps specification.
Import maps:
- This proposal allows you to control how JavaScript import statements and import() expressions get urls.
- Also allow the “naked import specifier” to work (e.g
import moment from "moment"
). - Import maps can illustrate how the module path is resolved.
- provide
<script type="importmap">
和<link rel="modulepreload" href="import:lodash">
Support. - Like Service Workers, import maps are application-level artifacts and should not be composed manually, but should be generated by the person or tool that controls the entire app perspective. It doesn’t make sense to include import maps in the library; let the entire application decide how to map urls.
- . And more github.com/WICG/import…
Token permission tokens are supported to obtain modules
Not all code is publicly available on the Internet. Previously, Deno could not download code from servers that required authentication. In this release we have added the ability for users to use authentication tokens when they first acquire modules.
To do this, the Deno CLI will try to find an environment variable named DENO_AUTH_TOKENS to determine which authentication token to consider when requesting a remote module. Values of environment variables are semicolons (;). Format of n separated tokens, where each token is in the format {token}@{hostname[:port]}.
For example, a single token looks like this:
[email protected]
Copy the code
Multiple tokens might look like this:
[email protected]; [email protected]:8080Copy the code
When Deno is going to get a remote module, if the hostname hostname of the remote module matches the hostname hostname in the environment variable: Deno will set an Authorization Header field in the request header with a value format of Bearer {token}. This will enable the remote server to recognize authorization requests that the request header is an authenticated user and provide access to the appropriate resources and modules on the server.
For more detailed instructions on how to use and configure the environment to extract information from the private Github repository, refer to the relevant manual entries.
Intensive reading notes:
- Authorization Header: Contains masking for authenticating user agents to servers after they respond with 401 unauthorized status and wwW-Authenticate headers (usually but not necessarily). It is usually Basic (Base 64 encoded, but not meant to be encrypted, sent in clear text and reversible encoding, preferably using HTTPS in conjunction with Basic authentication).
- Bearer of: RFC 6750, these tokens have been used to gain access to OAuth 2.0 related access resources. Others include Basic, Digest (only MD5 hash is supported in Firefox), HOBA (Origin based authentication, based on digital signatures), Mutual, AWS4-HMAC-SHA256.
Deno.test
Exit sweeper is supported
The deno.test API already has two cleaners to help developers ensure that code does not “leak” operations or resources — that is, all open file/network handles are closed and no other pending system calls are made before the test case ends.
Deno 1.8 adds a new cleaner to ensure that tested code does not call deno.exit (). Abnormal exit statements can provide false positive test results and are often abused or forgotten to be deleted.
By default, this sweeper is enabled for all tests, but you can disable it by setting the sanitizeExit Boolean to false in the test definition.
Deno.test({ name: "false success", fn() { Deno.exit(0); }, sanitizeExit: false,}); // This test statement will never execute deno.test ({name: "failing test", fn() {throw new Error("this test fails"); }});Copy the code
You can run this script: deno test at https://deno.land/posts/v1.8/exit_sanitizer.ts.
Deno.permissions
API is now stable
Deno’s security model is based on the privilege mechanism. Currently, these permissions can only be granted when the application is started. This works in most cases. However, in some cases, requesting/revoking permissions at run time leads to a better user experience.
In Deno 1.8, there is now a stable API for Query queries, Request requests, and REVOKE permissions. These apis are included in the deno.premissions object. Here’s an example of how it works:
function homedir() { try { console.log(`Your home dir is: ${Deno.env.get("HOME")}`); } catch (err) { console.log(`Failed to get the home directory: ${err}`); }// Try to get the home directory (this will fail because there is no env permission) homedir(); const { granted } = await Deno.permissions.request({ name: "env" }); if (granted) { console.log(`You have granted the "env" permission.`); } else { console.log(`You have not granted the "env" permission.`); }// Try to get the home directory (this will be successful after the user approves authorization) homedir(); await Deno.permissions.revoke({ name: "env" }); // Try to get the home directory (this will fail because the user has canceled authorization) homedir();Copy the code
You can run this script: deno run https://deno.land/posts/v1.8/permission_api.ts.
Intensive reading notes:
- Deno.permissions object: Contains query, REVOKE, request methods, and PermissionDescriptor classes.
- Deno permission type:
--allow-all
,--alow-env
,--alow-hrtime
,--alow-net
,--alow-plugin
,--alow-read
,--alow-run
,--alow-write
.
Deno.link
和 Deno.symlink
API is now stable
This release brings with it four stable apis related to symbolic links:
Deno.link
Deno.linkSync
Deno.symlink
Deno.symlinkSync
These apis need security checks and proper permissions to use them before they can be stabilized.
Deno.link and denO. linkSync must have read and write permissions on both source and destination paths.
Deno.symlink and denO. symlinkSync require write permission on the destination path.
Intensive reading notes:
Deno.link
和Deno.linkSync
: Creates a new path as a hard link to the old path. The former is asynchronous, while the latter is synchronous.Deno.symlink
和Deno.symlinkSync
: Creates a new path as a link to the old path. The options.type parameter can be set to file or dir.
More granularDeno.metrics
As Deno becomes more stable, it becomes increasingly important for developers to have an easier way to detect their applications. This needs to be supported from the bottom up (the runtime itself). In Deno, all privileged operations from JS (operations that go to Rust) are implemented through a single central interface between JS and Rust. We call requests through this interface “OPS.” For example, calling deno.open invokes op_open_async on the privileged side, which returns the resource ID of the open file (or an error).
More than two years ago, on October 11, 2018, we added a new way for developers to view all ops metrics between Rust and JS: deno.metrics. The API now exposes the number of synchronous/asynchronous operations started and completed, as well as the amount of data sent through the operation interface. Previously it was limited to the combined data of all the different operations. There is no way to determine which OPS is invoked how many times, and there is usually only one overall result.
Running with — Unstable, this release adds a new field called ops to deno.metrics. This field contains information for each operation about how often the API is called and how much data is transferred through the API. This allows for more fine-grained detection of the runtime.
Here’s an example of how to use it:
$deno --unstableDeno 1.8.0exit using CTRL +d or close()> deno.metrics ().ops["op_open_async"]undefined> await Deno.open("./README.md")File {}> Deno.metrics().ops["op_open_async"]{ opsDispatched: 1, opsDispatchedSync: 0, opsDispatchedAsync: 1, opsDispatchedAsyncUnref: 0, opsCompleted: 1, opsCompletedSync: 0, opsCompletedAsync: 1, opsCompletedAsyncUnref: 0, bytesSentControl: 54, bytesSentData: 0, bytesReceived: 22}Copy the code
In a future release, the asynchronous operation cleanup tool in deno.test will use this new information to provide more operable errors when asynchronous operations are not completed before tests are completed. We’ve seen this feature used to detect applications and pipe data into monitoring software.
- Deno.open: Opens a File and parses it as an instance of deno.file. If opened using create or createNew, the file does not need to exist before opening. After finishing the file operation, it is the responsibility of the caller to close the file voluntarily.
- Deno.metrics: Tracks Deno privilege metrics, used in Deno development. Ops is a transition between Deno JavaScript and Deno Rust.
indeno fmt
Supports THE JSON format
Deno FMT now supports formatting as.json and.jsonc files. Just like JS/TS, formatting tools can also format JSON and JSONC code blocks in Markdown files.
Intensive reading notes:
- JSON: ES3-based subset, a text format that is completely language independent. In most languages, this is done through arrays, vectors, lists, or sequences.
- JSONC: JSONC supports annotations.
- Json-c: A variant of JSON, mainly developed for C, JSONC implements the reference counting object model, which makes it easy to construct JSON objects in C, output JSON-formatted strings, and parse jSON-formatted strings back to the C representation of JSON objects.
inDeno.emit
Supports IIFE packages in
The built-in wrapper can package packages in the immediate function expression (IIFE) format.
By default, the output format is still ESM, but users can change this format by setting the emitoptions. bundle option to iife:
const { files } = await Deno.emit("/a.ts", { bundle: "iife", sources: { "/a.ts": `import { b } from "./b.ts"; console.log(b); `, "/b.ts": `export const b = "b"; `,}}); console.log(files["deno:///bundle.js"]);Copy the code
The output is:
(function() { const b = "b"; console.log(b); return { }; }) ();Copy the code
You can run this script: deno run – unstable https://deno.land/posts/v1.8/emit_iife.ts.
This feature is particularly useful when creating packages for older browsers that do not support ESM.
Intensive reading notes:
- ESM module: The standardized JavaScript module is pre-issued and gradually supported by Node and major browsers. Can more easily provide split and lazy loading etc.
- IIFE module: is a JavaScript function that runs immediately after it is defined. It can be a design pattern or a self-executing anonymous function, consisting of two main parts: anonymous functions (to prevent variable contamination, etc.) and expressions that are invoked immediately
(a)
.
deno lsp
Has been stable
Over the past few months, we’ve been working to replace the Deno extension under the old VS Code editor integration. The old extensions only worked with VS Code, and the types resolved did not always match their Deno CLI counterparts.
In the Canary version of Deno 1.6, we released the built-in language server Deno LSP. LSP allows us to provide editor integration to all editors that support THE LSP protocol with just one piece of code. The built-in language server is based on the same architecture as the rest of the Deno CLI — therefore, it provides the same TypeScript diagnostics as the rest of the CLI.
Two weeks ago, in Deno 1.7.5 we stabilized Deno LSPS and switched the official VS Code extension to the latest. We have received good feedback so far and will work hard to address all of the user suggestions. If you encounter problems with the extender, please report the problem in our problem tracker. Because we can’t solve problems we don’t know about.
In addition to the official VS Code integration, a number of community integrations have been created based on deno LSP builds.
- Vim and CoC: github.com/fannheyward…
- Neovim:github.com/neovim/nvim…
- Emacs: Emacs – LSP. Making. IO/LSP mode/pa…
- Kakoune: deno. Land/manual/gett…
- Sublime does: deno. Land/manual/gett…
Intensive reading notes:
- Deno Canary version: Provides more frequent branch changes and supports the ability to predict future versions. It can be downloaded from dl.deno.land/.
- LSP protocol: Language server protocol. Defines a protocol to use between an editor or IDE and a language server that provides language features such as auto-complete, go to definition, find all references, and so on. The Language Server index format (LSIF, similar to “Else If”) is designed to support rich code navigation in development tools or Web UIs without the need for a local copy of source code. It is gradually supported by more languages. Send message format using JSON-RPC.
TypeScript 4.2
Deno 1.8 sync supports the latest stable versions of TypeScript.
You can learn more about new TypeScript 4.2 features in the Announcing TypeScript 4.2 article.
New features in TypeScript 4.2 include but not limited to:
Reference: TypeScript 4.2 release
- Smarter type alias protection. TypeScript 4.2 tracks type constructors by preserving what was originally written and constructed over time, and also tracks and distinguishes instances where aliases are typed into other aliases.
- Rest elements in tuple types can be placed anywhere. Previously TypeScript only allowed REST elements to be in the last position in a tuple; now REST elements can be anywhere in a tuple. The only restriction is that no additional optional elements or REST elements can follow.
- Check the IN operator more closely. In JavaScript, using a non-object type to the right of the IN operator is a runtime error. Now, TypeScript 4.2 ensures that it can be captured at coding time.
- Abstract construction signatures are supported. Adding an abstract modifier to a construction signature representation can be passed in the abstract constructor.
- Support –explainFiles option. When you use this option, the TypeScript compiler gives you some very verbose output that makes sense of how the file ends up accessing all related files.
- through
--strictNullChecks
Support options&&
和||
Expression to optimize uncalled function checking in logical expressions. - The rules between optional attributes and string index signatures are more relaxed.
- Support for quick declaration of missing functions.
Conclusion the translator
The full text of the translation, and in each chapter made a simple intensive reading notes. This translation is the second in the “Close Reading” series, following the “Close Reading: Deno 2020 Official Review and 2021 Outlook”.
Deno’s intensive reading series will focus on the official blog, and every translated article will strive for PR integration into the current Deno Chinese website. Welcome to star on @Hylerrix/DenO-Tutorial repository or follow the public account (@ningowood) to receive timely news and work together to make denO better in 2021!
© github.com/hylerrix/de… 2020 ~ 2021