1597231440
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.
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
.)
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 (??
).
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();
See the examples folder for more examples.
Also see this OpenProcessing sketch.
To create a new maze, use Maze.create()
. You can optionally pass in an object with the settings (see below).
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.
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.
This method calls .step()
repeatedly until the maze has finished generating (or it has given up). Returns the finished maze
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 |
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:
Prints out the maze as a string to the console.
Maze.create({
width: 10,
height: 10
}).generate().printString()
___________________
|__ _____| | __ |
| | ______|___| __|
| |_|__ | |_ |__ |
| _____| |__ | | __|
| | _____| _| |_ |
|_ |___ __| | _| |
| | | |____ | | _|
| _| | |_ ___| | |
| | |_|_ |_| ____ |
|_______|_____|_____|
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:
It takes in three properties, all optional.
Maze.create()
).display()
)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. |
<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>
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())
.
Author: TheWizardBear
Source Code: https://github.com/TheWizardBear/maze_generator
#javascript #deno #nodejs #node
1597231440
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.
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
.)
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 (??
).
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();
See the examples folder for more examples.
Also see this OpenProcessing sketch.
To create a new maze, use Maze.create()
. You can optionally pass in an object with the settings (see below).
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.
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.
This method calls .step()
repeatedly until the maze has finished generating (or it has given up). Returns the finished maze
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 |
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:
Prints out the maze as a string to the console.
Maze.create({
width: 10,
height: 10
}).generate().printString()
___________________
|__ _____| | __ |
| | ______|___| __|
| |_|__ | |_ |__ |
| _____| |__ | | __|
| | _____| _| |_ |
|_ |___ __| | _| |
| | | |____ | | _|
| _| | |_ ___| | |
| | |_|_ |_| ____ |
|_______|_____|_____|
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:
It takes in three properties, all optional.
Maze.create()
).display()
)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. |
<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>
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())
.
Author: TheWizardBear
Source Code: https://github.com/TheWizardBear/maze_generator
#javascript #deno #nodejs #node
1653964894
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.
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.
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.
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.
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
.)
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.
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.
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;
}
}
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(""));
}
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.
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
1595475300
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
1619614811
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
1605177756
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