Javascript module for easily generating mazes,compatible with both Deno and Node

maze_generator

maze_generator is a Javascript module for easily generating mazes.

⚠️ This module is work-in-progress, so much of it is unstable.

Feedback is welcome. The best way to provide feedback is to open an issue.

This module is heavily influenced by Jamis Buck’s Coffeescript mazes. It is structured a little differently though and is written entirely in Javascript rather than Coffeescript. It also functions as a module, rather than a library. I aim to eventually add all the functionality that Jamis’s CS maze library has.

Importing the module

In the browser and in Deno

If you are inside a Javascript module file or a type="module" script tag, then add the following to the top of your code:

import {Maze} from "https://x.nest.land/maze_generator@0.1.2/mod.js";

If not, you can use the dynamic import() function. For example:

let Maze;
import("https://x.nest.land/maze_generator@0.1.2/mod.js")
  .then(module => {
    Maze = module.Maze;
  })
  .catch(error => {
    console.log(`Error loading maze_generator module: ${error}`)
  })

You can also import the module from deno.land/x if you prefer: https://deno.land/x/maze_generator@v0.1.2/mod.js. (Note the v here, which is not present when importing from nest.land.)

In Node

Run:

npm install @thewizardbear/maze_generator

And then import the maze_generator module in your code by adding the following to the top of every file that uses it:

import {Maze} from "@thewizardbear/maze_generator";

Alternately, you can you require:

let {Maze} = require("@thewizardbear/maze_generator")`

Please note that Node version 14+ is required as maze_generator makes use of some ES2020 features such as the nullish coalescing operator (??).

Example Usage

import {Maze} from "https://x.nest.land/maze_generator@0.1.2/mod.js"

let mazeSettings = {
  width: 12,
  height: 12,
  algorithm: "recursive backtracker"
}

//initialize the maze
let m = Maze.create(mazeSettings);

//generate it
m.generate();

//print the maze to the console
m.printString();

Other examples

See the examples folder for more examples.

Also see this OpenProcessing sketch.

Maze.create()

To create a new maze, use Maze.create(). You can optionally pass in an object with the settings (see below).

Maze settings object

These are all the properties of the object you can pass in when you write Maze.create(mazeSettings)

Property Description Valid Values Default Value
width (or xSize) The width of the maze. (How many columns there should be.) A positive integer. height or 30
height (or ySize) The height of the maze. (How many rows there should be.) A positive integer. width or 30
algorithm The algorithm to use. Any one of the following: "recursive backtracker", "eller's", "sidewinder", "kruskal's", "simplified prim's", "modified prim's", "true prims", "hunt and kill", "binary tree", "aldous broder", "recursive division", "10 print", "random" (random algorithm). This isn’t case sensitive. Characters other than the letters a-z and digits 0-9 are ignored. "recursive backtracker" ("prims" defaults to "true prims")
start Where to start the maze generation from (if there is an option). An object with both an x and y property (both integers) or a string referencing a certain point ("random" or a certain side or corner such as "north east") "random" for all algorithms except Eller’s, binary tree and sidewinder, which are all {x: 0, y: 0}.
entrance Where the solution should start from. An object with both an x and y property (and an optional direction property: "N", "S", "E", or "W") or a string referencing a certain point ("random" or a certain side or corner such as "north east") "top left"
exit Where the solution should end. An object with both an x and y property (and an optional direction property: "N", "S", "E", or "W") or a string referencing a certain point ("random" or a certain side or corner such as "north east") "bottom right"

You can also use the size property instead of width and height to set both dimentions.

.step()

Call .step() to advance the maze one step.

Returns true if the maze hasn’t finished generating yet and false if there are no more steps left to take. You can also check if a maze has finished generating with .finishedGenerating, which is a boolean value.

.generate()

This method calls .step() repeatedly until the maze has finished generating (or it has given up). Returns the finished maze

.display()

This is the function which displays the maze.

Note that this function (currently) only works in a html document with a canvas element.

It optionally takes in an object with the properties listed below.

Property Description Valid Values Default Value
canvas The canvas to display the maze on. A canvas element (e.g. document. getElementsByTagName("canvas")[0]). The first canvas element in the html.
coloringMode How the cells are colored. "normal": regular coloring, "distance": each cell is colored by a distance from a point . More coloring modes coming soon hopefully. Anything other than the valid values specififed defaults to normal coloring. "normal"
colorScheme The color scheme to use when coloringMode is not "normal". This can either be "grayscale", "rainbow" or an array or hex codes. "rainbow"
mainColor This is the color of the walls (or line). A hex value as a string "#000"
backgroundColor Background color and color of the space between walls (unless colorMode isn’t "normal") A hex value as a string "#FFF"
antiAliasing Whether or not to apply anti-aliasing to the image drawn on the canvas (imageSmoothingEnabled). Setting it to false gives crisp edges but it can distort the output for small canvases where the cells do not line up with the canvas pixels well. true or false false
showSolution Whether or not to show the solution when the maze is complete true or false false
solutionColor The color of the solution if showSolution is true A hex value as a string "#F00"
strokeWeight The thickness of the lines drawn A number ≥ 0 4
removeWallsAtEntranceAndExit Whether or not the walls should be removed at the entrance and exit of the maze. Note that this doesn’t change the maze, it just means these walls won’t be displayed. true or false false
lineCap Changes the lineCap canvas context property. "butt", "round" or "square" "square"
distanceFrom Where the distance should be measured from if displayMode is set to "distance" A valid cell position or "solution" The start cell
displayMode How the maze should be displayed. 0 (line), 1 (block walls), 2 (cell walls) 1

.display() example usage

let kruskalMaze = Maze.create({
  width: 20,
  height: 20,
  algorithm: "Kruskal's"
}).generate();

kruskalMaze.display({
  canvas: document.getElementById("maze-canvas") //Replace this with your canvas element you want to display the maze on.
})

This will display a maze similar to this:

Kruskal's maze

.printString()

Prints out the maze as a string to the console.

.printString() example usage

Basic code
Maze.create({
  width: 10,
  height: 10
}).generate().printString()
Example output
 ___________________
|__  _____|   |  __ |
|   | ______|___| __|
| |_|__ |   |_  |__ |
|  _____| |__ | | __|
| |  _____|  _| |_  |
|_  |___  __| |  _| |
| | |   |____ | |  _|
|  _| | |_   ___| | |
| | |_|_  |_|  ____ |
|_______|_____|_____|

Maze.createWidget()

Maze.createWidget() is work-in-progress and particuarly unstable.

Call Maze.createWidget() somewhere with access to the document API (the browser) and it should add a little interactive animated HTML widget to your page that looks something like this:

maze widget example image

It takes in three properties, all optional.

  1. Maze settings object (see Maze.create())
  2. Display settings object (see .display())
  3. Maze widget settings object (see below)

Maze widget settings object

Property Description Valid Values Default Value
paused Whether the generation should be animated when the maze starts. true or false false
imageButtons Whether the buttons should be images (true) or text (false). This is still a bit dodgy at the moment. true or false false
containerElement The element that the maze widget should be placed inside of A HTML element An element with the id or class maze-widget-container, or if none is found the body of the HTML document.

Example code

<div id="prims-demo"></div>
<script>
  import("https://x.nest.land/maze_generator@0.1.2/mod.js")
    .then(({Maze}) => {
      const mazeSettings = {
        algorithm: "true prims"
      }

      const widgetSettings = {
        paused: true,
        containerElement: document.getElementById("prims-demo")
      }

      Maze.createWidget(mazeSettings, {}, widgetSettings)
    })
    .catch(error => {
      document.getElementById("prims-demo").innerHTML = `Error loading maze_generator module: ${error}`
    })
</script>

.getSolution()

This is a method that returns the solution to the maze in the form of an array of cell positions.

You can optionally pass in a start and end point, but it will default to the top left and bottom right of the maze.

Try console.log(Maze.create().generate().getSolution()).

Download Details:

Author: TheWizardBear

Source Code: https://github.com/TheWizardBear/maze_generator

#javascript #deno #nodejs #node

What is GEEK

Buddha Community

Javascript module for easily generating mazes,compatible with both Deno and Node

Javascript module for easily generating mazes,compatible with both Deno and Node

maze_generator

maze_generator is a Javascript module for easily generating mazes.

⚠️ This module is work-in-progress, so much of it is unstable.

Feedback is welcome. The best way to provide feedback is to open an issue.

This module is heavily influenced by Jamis Buck’s Coffeescript mazes. It is structured a little differently though and is written entirely in Javascript rather than Coffeescript. It also functions as a module, rather than a library. I aim to eventually add all the functionality that Jamis’s CS maze library has.

Importing the module

In the browser and in Deno

If you are inside a Javascript module file or a type="module" script tag, then add the following to the top of your code:

import {Maze} from "https://x.nest.land/maze_generator@0.1.2/mod.js";

If not, you can use the dynamic import() function. For example:

let Maze;
import("https://x.nest.land/maze_generator@0.1.2/mod.js")
  .then(module => {
    Maze = module.Maze;
  })
  .catch(error => {
    console.log(`Error loading maze_generator module: ${error}`)
  })

You can also import the module from deno.land/x if you prefer: https://deno.land/x/maze_generator@v0.1.2/mod.js. (Note the v here, which is not present when importing from nest.land.)

In Node

Run:

npm install @thewizardbear/maze_generator

And then import the maze_generator module in your code by adding the following to the top of every file that uses it:

import {Maze} from "@thewizardbear/maze_generator";

Alternately, you can you require:

let {Maze} = require("@thewizardbear/maze_generator")`

Please note that Node version 14+ is required as maze_generator makes use of some ES2020 features such as the nullish coalescing operator (??).

Example Usage

import {Maze} from "https://x.nest.land/maze_generator@0.1.2/mod.js"

let mazeSettings = {
  width: 12,
  height: 12,
  algorithm: "recursive backtracker"
}

//initialize the maze
let m = Maze.create(mazeSettings);

//generate it
m.generate();

//print the maze to the console
m.printString();

Other examples

See the examples folder for more examples.

Also see this OpenProcessing sketch.

Maze.create()

To create a new maze, use Maze.create(). You can optionally pass in an object with the settings (see below).

Maze settings object

These are all the properties of the object you can pass in when you write Maze.create(mazeSettings)

Property Description Valid Values Default Value
width (or xSize) The width of the maze. (How many columns there should be.) A positive integer. height or 30
height (or ySize) The height of the maze. (How many rows there should be.) A positive integer. width or 30
algorithm The algorithm to use. Any one of the following: "recursive backtracker", "eller's", "sidewinder", "kruskal's", "simplified prim's", "modified prim's", "true prims", "hunt and kill", "binary tree", "aldous broder", "recursive division", "10 print", "random" (random algorithm). This isn’t case sensitive. Characters other than the letters a-z and digits 0-9 are ignored. "recursive backtracker" ("prims" defaults to "true prims")
start Where to start the maze generation from (if there is an option). An object with both an x and y property (both integers) or a string referencing a certain point ("random" or a certain side or corner such as "north east") "random" for all algorithms except Eller’s, binary tree and sidewinder, which are all {x: 0, y: 0}.
entrance Where the solution should start from. An object with both an x and y property (and an optional direction property: "N", "S", "E", or "W") or a string referencing a certain point ("random" or a certain side or corner such as "north east") "top left"
exit Where the solution should end. An object with both an x and y property (and an optional direction property: "N", "S", "E", or "W") or a string referencing a certain point ("random" or a certain side or corner such as "north east") "bottom right"

You can also use the size property instead of width and height to set both dimentions.

.step()

Call .step() to advance the maze one step.

Returns true if the maze hasn’t finished generating yet and false if there are no more steps left to take. You can also check if a maze has finished generating with .finishedGenerating, which is a boolean value.

.generate()

This method calls .step() repeatedly until the maze has finished generating (or it has given up). Returns the finished maze

.display()

This is the function which displays the maze.

Note that this function (currently) only works in a html document with a canvas element.

It optionally takes in an object with the properties listed below.

Property Description Valid Values Default Value
canvas The canvas to display the maze on. A canvas element (e.g. document. getElementsByTagName("canvas")[0]). The first canvas element in the html.
coloringMode How the cells are colored. "normal": regular coloring, "distance": each cell is colored by a distance from a point . More coloring modes coming soon hopefully. Anything other than the valid values specififed defaults to normal coloring. "normal"
colorScheme The color scheme to use when coloringMode is not "normal". This can either be "grayscale", "rainbow" or an array or hex codes. "rainbow"
mainColor This is the color of the walls (or line). A hex value as a string "#000"
backgroundColor Background color and color of the space between walls (unless colorMode isn’t "normal") A hex value as a string "#FFF"
antiAliasing Whether or not to apply anti-aliasing to the image drawn on the canvas (imageSmoothingEnabled). Setting it to false gives crisp edges but it can distort the output for small canvases where the cells do not line up with the canvas pixels well. true or false false
showSolution Whether or not to show the solution when the maze is complete true or false false
solutionColor The color of the solution if showSolution is true A hex value as a string "#F00"
strokeWeight The thickness of the lines drawn A number ≥ 0 4
removeWallsAtEntranceAndExit Whether or not the walls should be removed at the entrance and exit of the maze. Note that this doesn’t change the maze, it just means these walls won’t be displayed. true or false false
lineCap Changes the lineCap canvas context property. "butt", "round" or "square" "square"
distanceFrom Where the distance should be measured from if displayMode is set to "distance" A valid cell position or "solution" The start cell
displayMode How the maze should be displayed. 0 (line), 1 (block walls), 2 (cell walls) 1

.display() example usage

let kruskalMaze = Maze.create({
  width: 20,
  height: 20,
  algorithm: "Kruskal's"
}).generate();

kruskalMaze.display({
  canvas: document.getElementById("maze-canvas") //Replace this with your canvas element you want to display the maze on.
})

This will display a maze similar to this:

Kruskal's maze

.printString()

Prints out the maze as a string to the console.

.printString() example usage

Basic code
Maze.create({
  width: 10,
  height: 10
}).generate().printString()
Example output
 ___________________
|__  _____|   |  __ |
|   | ______|___| __|
| |_|__ |   |_  |__ |
|  _____| |__ | | __|
| |  _____|  _| |_  |
|_  |___  __| |  _| |
| | |   |____ | |  _|
|  _| | |_   ___| | |
| | |_|_  |_|  ____ |
|_______|_____|_____|

Maze.createWidget()

Maze.createWidget() is work-in-progress and particuarly unstable.

Call Maze.createWidget() somewhere with access to the document API (the browser) and it should add a little interactive animated HTML widget to your page that looks something like this:

maze widget example image

It takes in three properties, all optional.

  1. Maze settings object (see Maze.create())
  2. Display settings object (see .display())
  3. Maze widget settings object (see below)

Maze widget settings object

Property Description Valid Values Default Value
paused Whether the generation should be animated when the maze starts. true or false false
imageButtons Whether the buttons should be images (true) or text (false). This is still a bit dodgy at the moment. true or false false
containerElement The element that the maze widget should be placed inside of A HTML element An element with the id or class maze-widget-container, or if none is found the body of the HTML document.

Example code

<div id="prims-demo"></div>
<script>
  import("https://x.nest.land/maze_generator@0.1.2/mod.js")
    .then(({Maze}) => {
      const mazeSettings = {
        algorithm: "true prims"
      }

      const widgetSettings = {
        paused: true,
        containerElement: document.getElementById("prims-demo")
      }

      Maze.createWidget(mazeSettings, {}, widgetSettings)
    })
    .catch(error => {
      document.getElementById("prims-demo").innerHTML = `Error loading maze_generator module: ${error}`
    })
</script>

.getSolution()

This is a method that returns the solution to the maze in the form of an array of cell positions.

You can optionally pass in a start and end point, but it will default to the top left and bottom right of the maze.

Try console.log(Maze.create().generate().getSolution()).

Download Details:

Author: TheWizardBear

Source Code: https://github.com/TheWizardBear/maze_generator

#javascript #deno #nodejs #node

Connor Mills

Connor Mills

1653964894

Deno vs Node.js | Converting an Existing Node.js Module to Deno

A general purpose methodology for converting a Node.js package to Deno.

Deno is a new runtime for JavaScript that supports TypeScript natively without the need for compilation. It was created by Ryan Dahl, the creator of Node.js, to solve some of Node’s fundamental design and security flaws and embrace modern best practices like ES Modules and TypeScript.

At EdgeDB, we built and maintain a first-party client library for Node.js that’s available as the edgedb module on NPM. However, Deno uses a totally different set of practices to handle dependencies, namely direct URL imports from public package registries like deno.land/x. We set out to find a simple way to “Denoify” our codebase; that is, generate a Deno-compatible module from our existing Node.js implementation with minimal refactoring. This frees us from the complexity of maintaining and synchronizing two nearly identical codebases.

We landed on a “runtime adapter” pattern that we think represents a general-purpose approach that may be useful to other library authors looking to support Deno.

Node.js vs Deno​

There are a few key differences between the Node.js and Deno runtimes that we will have to take into account when adapting our library written for Node.js:

TypeScript support: Deno can directly execute TypeScript files, while Node.js can only run JavaScript code.

Module resolution: By default, Node.js imports modules in the CommonJS format and expects require/module.exports syntax. It also has a complex resolution algorithm, which will load plain module names like "react" from node_modules, append .js or .json as needed to extensionless imports, and import the index.js file if the import path is a directory.

Deno simplifies this dramatically. It uses the ECMAScript module syntax import and export, also known as “ES modules” or just “ESM”. This syntax is also used by TypeScript. All imports must be either a) a relative path with an explicit file extensions or b) a URL.

This means there is no node_modules or package manager like npm or yarn; external modules are simply imported directly via a URL from a publicly available code repository, like deno.land/x or GitHub.

Standard library: Node.js has a builtin set of standard modules like fs, path, crypto, and http, among others. These modules can be directly required with require('fs') and their names are reserved by Node.js. By contrast, the Deno standard library is imported via URL from the https://deno.land/std/ registry. The functionality of the two standard libraries also differs, with Deno abandoning some old or outdated Node.js APIs, introducing a new standard library (inspired by that of Go), and uniformly supporting modern JavaScript features like Promises (whereas many Node.js APIs still rely on the older callback style).

Built-in globals: Deno contains all of its core APIs under a single global variable called Deno and otherwise only exposes standard web APIs. Unlike Node.js, there is no global Buffer or process variable available.

So how can we work around these differences and get our Node.js library running in Deno as painlessly as possible? Let’s go through these changes one by one.

TypeScript and module syntax​

Fortunately, we don’t need to worry much about converting CommonJS require/module.exports syntax to ESM import/export. We wrote edgedb-js entirely in TypeScript, which already uses ESM syntax. During compilation, tsc converts our files to plain JavaScript files utilizing CommonJS require syntax. The compiled files are directly consumable by Node.js.

The rest of this post will discuss how we modify our TypeScript source files to a format that is directly consumable by Deno.

Dependencies​

Fortunately edgedb-js doesn’t have any third-party dependencies, so we don’t have to worry about the Deno compatibility of any external libraries. However, we need to replace all imports from the Node.js standard library (e.g. path, fs, etc) with a Deno equivalent.

If your package does depend on external packages, check deno.land/x to see if a Deno version if available. If so, read on; if not, you’ll need to work with the module author to make a Deno version available.

This task is made far simpler by the existence of the Node.js compatibility module provided by the Deno standard library. This module provides a wrapper over Deno’s standard library that attempts to adhere as faithfully as possible to Node’s API.

import * as crypto from "crypto";
import * as crypto from "https://deno.land/std@0.114.0/node/crypto.ts";

To simplify things, we moved all imports of Node.js APIs to a single file called adapter.node.ts and re-export only the functionality we need.

// adapter.node.ts
import * as path from "path";
import * as util from "util";
import * as crypto from "crypto";

export {path, net, crypto};

We then implement the same adapter for Deno in a file called adapter.deno.ts.

// adapter.deno.ts
import * as crypto from "https://deno.land/std@0.114.0/node/crypto.ts";
import path from "https://deno.land/std@0.114.0/node/path.ts";
import util from "https://deno.land/std@0.114.0/node/util.ts";

export {path, util, crypto};

Whenever we need Node.js specific functionality, we import it from adapter.node.ts directly. This way, we can make edgedb-js Deno-compatible by simply rewriting all imports of adapter.node.ts to adapter.deno.ts. As long as these files re-export the same functionality, everything should work as expected.

Practically speaking, how do we actually rewrite these imports? Well, we need to write a simple codemod script. And just to make it a bit more poetic, we’ll write this script using Deno itself.

Writing a Deno-ifier​

Before we get started building, let’s outline what steps this tool needs to do:

Rewrite the Node.js-style imports to the more explicit style Deno. This includes adding the .ts file extension and adding /index.ts to all directory imports.

Swapping out all imports from adapter.node to adapter.deno.ts.

Inject Node.js globals like process and Buffer into the Deno-ified code. While we could simply export these variables from our adapters, we’d have to refactor our Node.js files to explicitly import them. To simplify things, we’ll detect where Node.js globals are used and inject an import as needed.

Rename the src directory to _src to denote that it holds the internals of edgedb-js and shouldn’t be imported directly

Move the main src/index.node.ts file to the project root and rename it mod.ts. This is idiomatic in Deno. (Note: the naming of index.node.ts here doesn’t indicate that it’s Node-specific; it’s indented to differentiate it from index.browser.ts, which exports all browser-compatible parts of edgedb-js.)

Create a list of all files​

Let’s jump in. First, we need to compute a list of our source files. The walk function provided by Deno’s native fs module will do:

import {walk} from "https://deno.land/std@0.114.0/fs/mod.ts";

const sourceDir = "./src";
for await (const entry of walk(sourceDir, {includeDirs: false})) {
  // iterate through all files
}

Note that we’re using Deno’s native std/fs module, not the Node compatibility version std/node/fs.

Let’s declare a set of rewrite rules and initialize a Map that will map from a source file path to the rewritten destination path.

const sourceDir = "./src";
const destDir = "./edgedb-deno";
const pathRewriteRules = [
  {match: /^src\/index.node.ts$/, replace: "mod.ts"},
  {match: /^src\//, replace: "_src/"},
];

const sourceFilePathMap = new Map<string, string>();

for await (const entry of walk(sourceDir, {includeDirs: false})) {
  const sourcePath = entry.path;
  sourceFilePathMap.set(sourcePath, resolveDestPath(sourcePath));
}

function resolveDestPath(sourcePath: string) {
  let destPath = sourcePath;
  // apply all rewrite rules
  for (const rule of pathRewriteRules) {
    destPath = destPath.replace(rule.match, rule.replace);
  }
  return join(destDir, destPath);
}

That’s the easy part; now lets start modifying the source code itself.

Rewrite imports and exports​

To rewrite the import paths, we need to know where they are in the file; fortunately TypeScript exposes its Compiler API, which we’ll use to parse the source files into an abstract syntax tree (AST) and find the import declarations.

To do so, we need to import the compiler API directly from the "typescript" NPM module. Fortunately, Deno’s compatibility module provides a way to require CommonJS modules without much hassle. This does require the use of the --unstable flag when running Deno, but for a build step like this, it’s not a problem.

import {createRequire} from "https://deno.land/std@0.114.0/node/module.ts";

const require = createRequire(import.meta.url);
const ts = require("typescript");

Let’s iterate through the files and parse each one in turn.

import {walk, ensureDir} from "https://deno.land/std@0.114.0/fs/mod.ts";
import {createRequire} from "https://deno.land/std@0.114.0/node/module.ts";

const require = createRequire(import.meta.url);
const ts = require("typescript");

for (const [sourcePath, destPath] of sourceFilePathMap) {
  compileFileForDeno(sourcePath, destPath);
}

async function compileFileForDeno(sourcePath: string, destPath: string) {
  const file = await Deno.readTextFile(sourcePath);
  await ensureDir(dirname(destPath));

  // if file ends with '.deno.ts', copy the file unchanged
  if (destPath.endsWith(".deno.ts")) return Deno.writeTextFile(destPath, file);
  // if file ends with '.node.ts', skip file
  if (destPath.endsWith(".node.ts")) return;

  // parse the source file using the typescript Compiler API
  const parsedSource = ts.createSourceFile(
    basename(sourcePath),
    file,
    ts.ScriptTarget.Latest,
    false,
    ts.ScriptKind.TS
  );
}

For each parsed AST, let’s iterate through its top-level nodes to find import and export declarations. We don’t need to look deeper, because import/export are always top-level statements (with the exception of dynamic import(), but we don’t use those in edgedb-js).

From these nodes, we extract the start and end offsets of the import/export path in the source file. We can then rewrite the import by slicing out the current contents and inserting a modified path.

const parsedSource = ts.createSourceFile(/*...*/);

const rewrittenFile: string[] = [];
let cursor = 0;
parsedSource.forEachChild((node: any) => {
  if (
    (node.kind === ts.SyntaxKind.ImportDeclaration ||
      node.kind === ts.SyntaxKind.ExportDeclaration) &&
    node.moduleSpecifier
  ) {
    const pos = node.moduleSpecifier.pos + 2;
    const end = node.moduleSpecifier.end - 1;
    const importPath = file.slice(pos, end);

    rewrittenFile.push(file.slice(cursor, pos));
    cursor = end;

    // replace the adapter import with Deno version
    let resolvedImportPath = resolveImportPath(importPath, sourcePath);
    if (resolvedImportPath.endsWith("/adapter.node.ts")) {
      resolvedImportPath = resolvedImportPath.replace(
        "/adapter.node.ts",
        "/adapter.deno.ts"
      );
    }

    rewrittenFile.push(resolvedImportPath);
  }
});

rewrittenFile.push(file.slice(cursor));

The key part here is the resolveImportPath function, which converts a Node-style local import to the Deno style through trial-and-error. First it checks if the path corresponds to an actual file on disk; it that fails, it tries appending .ts; if that fails, it tries appending /index.ts; if that fails, an error is thrown.

Inject Node.js globals​

The final step is to handle the Node.js globals. First, we create a globals.deno.ts file in our project directory. This file should export the compatibility versions of all Node.js globals that are used in your package.

export {Buffer} from "https://deno.land/std@0.114.0/node/buffer.ts";
export {process} from "https://deno.land/std@0.114.0/node/process.ts";

The compiled AST helpfully provides a Set of all identifiers used in the source file. We’ll use that to inject an import statement in any file that references these globals.

const sourceDir = "./src";
const destDir = "./edgedb-deno";
const pathRewriteRules = [
  {match: /^src\/index.node.ts$/, replace: "mod.ts"},
  {match: /^src\//, replace: "_src/"},
];
const injectImports = {
  imports: ["Buffer", "process"],
  from: "src/globals.deno.ts",
};

// ...

const rewrittenFile: string[] = [];
let cursor = 0;
let isFirstNode = true;
parsedSource.forEachChild((node: any) => {
  if (isFirstNode) {  // only run once per file
    isFirstNode = false;

    const neededImports = injectImports.imports.filter((importName) =>
      parsedSource.identifiers?.has(importName)
    );

    if (neededImports.length) {
      const imports = neededImports.join(", ");
      const importPath = resolveImportPath(
        relative(dirname(sourcePath), injectImports.from),
        sourcePath
      );
      const importDecl = `import {${imports}} from "${importPath}";\n\n`;

      const injectPos = node.getLeadingTriviaWidth?.(parsedSource) ?? node.pos;
      rewrittenFile.push(file.slice(cursor, injectPos));
      rewrittenFile.push(importDecl);
      cursor = injectPos;
    }
  }

Writing the files​

Finally, we’re ready to write the rewritten source file to its new home in the destination directory. First, we delete any existing contents, then write each file in turn.

try {
  await Deno.remove(destDir, {recursive: true});
} catch {}

const sourceFilePathMap = new Map<string, string>();
for (const [sourcePath, destPath] of sourceFilePathMap) {
  // rewrite file
  await Deno.writeTextFile(destPath, rewrittenFile.join(""));
}

Continuous integration​

A common pattern is to maintain a separate auto-generated repo for the Deno version of your package. In our case, we generate the Deno version of edgedb-js inside GitHub Actions whenever a new commit is merged into master. The generated files are then published to a sister repository called edgedb-deno. Below is a simplified version of our workflow file.

# .github/workflows/deno-release.yml
name: Deno Release
on:
  push:
    branches:
      - master
jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout edgedb-js
        uses: actions/checkout@v2
      - name: Checkout edgedb-deno
        uses: actions/checkout@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          repository: edgedb/edgedb-deno
          path: edgedb-deno
      - uses: actions/setup-node@v2
      - uses: denoland/setup-deno@v1
      - name: Install deps
        run: yarn install
      - name: Get version from package.json
        id: package-version
        uses: martinbeentjes/npm-get-version-action@v1.1.0
      - name: Write version to file
        run: echo "${{ steps.package-version.outputs.current-version}}" > edgedb-deno/version.txt
      - name: Compile for Deno
        run: deno run --unstable --allow-env --allow-read --allow-write tools/compileForDeno.ts
      - name: Push to edgedb-deno
        run: cd edgedb-deno && git add . -f && git commit -m "Build from $GITHUB_SHA" && git push

An additional workflow inside edgedb-deno then creates a GitHub release, which publishes a new version to deno.land/x. That’s left as an exercise for the reader, though you can use our workflow as a starting point.

Wrapping up​

This is a broadly generalizable pattern for converting an existing Node.js module to Deno. Refer to the edgedb-js repo for the full Deno compilation script, cross- workflow.

Original article source at https://www.edgedb.com

#deno #node #javascript #programming 

Deion  Hilpert

Deion Hilpert

1595475300

Deno tutorial and introduction & Deno vs Node comparison

Deno is the latest alternative to Node created by one of Node’s original founders. Will it match Node’s popularity? Tutorial and comparison.

#cto's guidebook #developer stories #node.js #backend #deno #javascript #node

anita maity

anita maity

1619614811

Random Password Generator Using JavaScript, HTML & CSS

Random Password Generator is a program that automatically generates a password randomly. Those generated passwords are mix with numbers, alphabets, symbols, and punctuations. This type of program helps the user to create a strong password.

Step By Step Tutorial :https://cutt.ly/ZbiDeyL

#password generator #random password generator #python password generator #random password generator javascript #html #javascript

Vincent Lab

Vincent Lab

1605177756

JavaScript Password Generator

In this video, I will be showing you how to build a password generator in JavaScript.

#password generator #random password generator #password #javascript #javascript project #javascript fun project