Skip to content

Commit

Permalink
Added a hybrid module for node (valkey-io#1132)
Browse files Browse the repository at this point in the history
* Added a hybrid module for node

* Added explanation to DEVELOPER.md about the hybrid build process

* PR fixes - DEVELOPER.md wordings, Script naming, CI structure, tsconfig usage in npm folder, enter to change log

* added tsconfig property to jest

* Added comment to fixup script file
  • Loading branch information
avifenesh authored and Guriy Samarin committed Mar 18, 2024
1 parent 1a5e5d9 commit bcaf627
Show file tree
Hide file tree
Showing 18 changed files with 300 additions and 127 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,22 @@ jobs:
- name: test
run: npm test
working-directory: ./node

- name: test hybrid node modules - commonjs
run: |
cd hybrid-node-tests
cd commonjs-test
npm install
npm run build-and-test
working-directory: ./node

- name: test hybrid node modules - ecma
run: |
cd hybrid-node-tests
cd ecmascript-test
npm install
npm run build-and-test
working-directory: ./node

- name: test redis modules
run: npm run test-modules -- --load-module=$GITHUB_WORKSPACE/redisearch.so --load-module=$GITHUB_WORKSPACE/redisjson.so
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#### Features

* Python: Allow chaining function calls on transaction. ([#987](https:/aws/glide-for-redis/pull/987))
* Node: Adding support for GLIDE's usage in projects based on either `CommonJS` or `ECMAScript` modules. ([#1132](https:/aws/glide-for-redis/pull/1132))

## 0.2.0 (2024-02-11)

Expand Down
14 changes: 14 additions & 0 deletions node/DEVELOPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,17 @@ Development on the Node wrapper may involve changes in either the TypeScript or
- [Jest Runner](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner) - in-editor test runner.
- [Jest Test Explorer](https://marketplace.visualstudio.com/items?itemName=kavod-io.vscode-jest-test-adapter) - adapter to the VSCode testing UI.
- [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) - Rust language support for VSCode.
### Hybrid package publishing method
In this project we are using hybrid method for building the package for NodeJS, in order to support GLIDE's usage in projects based on either `CommonJS` or `ECMAScript` modules.
Hybrid method -
In order to build the package for usage in either module system, we use three different tsconfig files:

- `tsconfig-base` is the general tsconfig file, which the others will extend.
- `tsconfig-cjs` for `commonJS` build with `CommonJS` as the target of translation.
- `tsconfig` for `ECMA` build with `ECMA` as the target of translation.

The build is composed of 2 build steps, one will use `tsconfig` and write the results of the package's translation into the `build-ts/mjs` folder, and the other will use `tsconfig-cjs` and will write into the `build-ts/cjs` folder.
Additionaly, we add to `package.json` the following export rule, which presents to the user the correct index file, based on the import statement used - `import` for `ECMA`, and `require` for `CommonJS`.
As part of the build we run the `fixup_pj_files_for_build_type` script, which adds the `type` and `types` entries to the different `package.json` that been created for `ECMAScript` and `CommonJS` with the fitting values.
17 changes: 17 additions & 0 deletions node/fixup_pj_files_for_build_type
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash
# This script add "type" and "types" entries to the different `package.json` that been created for `ECMAScript` and `CommonJS` with the fitting values.
cat >build-ts/cjs/package.json <<!EOF
{
"type": "commonjs",
"types": "build-ts/cjs/index.d.ts"
}
!EOF

cat >build-ts/mjs/package.json <<!EOF
{
"type": "module",
"types": "build-ts/mjs/index.d.ts"
}
!EOF
57 changes: 57 additions & 0 deletions node/hybrid-node-tests/commonjs-test/commonjs-test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const { AsyncClient } = require("glide-rs");
const RedisServer = require("redis-server");
const FreePort = require("find-free-port");
const PORT_NUMBER = 4001;

let server;
let port;

const { exec } = require("child_process");

function flushallOnPort(port) {
return new Promise((resolve, reject) => {
exec(`redis-cli -p ${port} FLUSHALL`, (error, _, stderr) => {
if (error) {
console.error(stderr);
reject(error);
} else {
resolve();
}
});
});
}

FreePort(PORT_NUMBER)
.then(([free_port]) => {
port = free_port;
server = new RedisServer(port);
server.open(async (err) => {
if (err) {
console.error("Error opening server:", err);
throw err;
}

const client = AsyncClient.CreateConnection(
`redis://localhost:${port}`,
);
await client.set("test", "test");
let result = await client.get("test");

if (result !== "test") {
throw new Error("Common Test failed");
} else {
console.log("Common Test passed");
}

await flushallOnPort(port).then(() => {
console.log("db flushed");
});
await server.close().then(() => {
console.log("server closed");
});
});
})
.catch((error) => {
console.error("Error occurred while finding a free port:", error);
});
26 changes: 26 additions & 0 deletions node/hybrid-node-tests/commonjs-test/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "common-test",
"version": "1.0.0",
"description": "",
"main": "Common-test.cjs",
"type": "commonjs",
"scripts": {
"build": "cd ../../../node && npm run build",
"test": "node commonjs-test.cjs",
"build-and-test": "npm run build && npm run test",
"prettier:check:ci": "./node_modules/.bin/prettier --check .",
"prettier:format": "./node_modules/.bin/prettier --write . "
},
"author": "Amazon Web Services",
"license": "Apache-2.0",
"dependencies": {
"child_process": "^1.0.2",
"find-free-port": "^2.0.0",
"glide-for-redis": "file:../../../node/build-ts/cjs"
},
"devDependencies": {
"@types/node": "^18.7.9",
"prettier": "^2.8.8",
"typescript": "^4.8.4"
}
}
49 changes: 49 additions & 0 deletions node/hybrid-node-tests/ecmascript-test/ecmascript-test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { exec } from "child_process";
import findFreePorts from "find-free-ports";
import { AsyncClient } from "glide-rs";
import RedisServer from "redis-server";

const PORT_NUMBER = 4001;
let server;
let port;

export function flushallOnPort(port) {
return new Promise((resolve, reject) => {
exec(`redis-cli -p ${port} FLUSHALL`, (error, _, stderr) => {
if (error) {
console.error(stderr);
reject(error);
} else {
resolve();
}
});
});
}

port = await findFreePorts(PORT_NUMBER).then(([free_port]) => free_port);
server = await new Promise((resolve, reject) => {
const server = new RedisServer(port);
server.open(async (err) => {
if (err) {
reject(err);
}

resolve(server);
});
});
const client = AsyncClient.CreateConnection("redis://localhost:" + port);
await client.set("test", "test");
let result = await client.get("test");

if (result !== "test") {
throw new Error("Ecma Test failed");
} else {
console.log("Ecma Test passed");
}

await flushallOnPort(port).then(() => {
console.log("db flushed");
});
await server.close().then(() => {
console.log("server closed");
});
26 changes: 26 additions & 0 deletions node/hybrid-node-tests/ecmascript-test/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"author": "Amazon Web Services",
"license": "Apache-2.0",
"name": "ecma-test",
"version": "1.0.0",
"description": "",
"main": "Ecma-test.mjs",
"type": "module",
"scripts": {
"build": "cd ../../../node && npm run build",
"test": "node ecmascript-test.mjs",
"build-and-test": "npm run build && npm run test",
"prettier:check:ci": "./node_modules/.bin/prettier --check .",
"prettier:format": "./node_modules/.bin/prettier --write . "
},
"dependencies": {
"child_process": "^1.0.2",
"find-free-ports": "^3.1.1",
"glide-for-redis": "file:../../../node/build-ts/mjs"
},
"devDependencies": {
"@types/node": "^18.7.9",
"prettier": "^2.8.8",
"typescript": "^4.8.4"
}
}
16 changes: 15 additions & 1 deletion node/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@ module.exports = {
transform: { "^.+\\.ts?$": "ts-jest" },
testEnvironment: "node",
testRegex: "/tests/.*\\.(test|spec)?\\.(ts|tsx)$",
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
moduleFileExtensions: [
"ts",
"tsx",
"js",
"jsx",
"json",
"node",
"cjs",
"mjs",
],
testTimeout: 20000,
globals: {
"ts-jest": {
tsconfig: "tsconfig-mjs.json",
},
},
};
14 changes: 10 additions & 4 deletions node/npm/glide/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@
"name": "${scope}${pkg_name}",
"version": "${package_version}",
"description": "An AWS-sponsored, open-source Redis client.",
"main": "build-ts/index.js",
"types": "build-ts/index.d.ts",
"main": "build-ts/cjs/index.js",
"module": "build-ts/mjs/index.js",
"exports": {
".": {
"import": "./build-ts/mjs/index.js",
"require": "./build-ts/cjs/index.js"
}
},
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"clean": "rm -rf build-ts/",
"copy-declaration-files": "cp ../../build-ts/*.d.ts build-ts/ && cp ../../build-ts/src/*.d.ts build-ts/src/",
"build": "tsc && mkdir -p build-ts/src && npm run copy-declaration-files"
"copy-declaration-files": "cp ../../build-ts/mjs/*.d.ts build-ts/mjs/ && cp ../../build-ts/cjs/*.d.ts build-ts/cjs/ && cp ../../build-ts/cjs/src/*.d.ts build-ts/cjs/src/ && cp ../../build-ts/mjs/src/*.d.ts build-ts/mjs/src/",
"build": "tsc -p tsconfig-mjs.json && tsc -p tsconfig-cjs.json && ./../../fixup_pj_files_for_build_type && mkdir -p build-ts/mjs/src && mkdir -p build-ts/cjs/src && npm run copy-declaration-files"
},
"files": [
"/build-ts"
Expand Down
6 changes: 6 additions & 0 deletions node/npm/glide/tsconfig-cjs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "../../tsconfig-cjs.json",
"compilerOptions": {
"outDir": "./build-ts/cjs" /* Specify an output folder for all emitted files. */
}
}
6 changes: 6 additions & 0 deletions node/npm/glide/tsconfig-mjs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "../../tsconfig-mjs.json",
"compilerOptions": {
"outDir": "./build-ts/mjs" /* Specify an output folder for all emitted files. */
}
}
13 changes: 0 additions & 13 deletions node/npm/glide/tsconfig.json

This file was deleted.

14 changes: 10 additions & 4 deletions node/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
{
"name": "@aws/glide-for-redis",
"description": "An AWS-sponsored, open-source Redis client.",
"main": "build-ts/index.js",
"types": "build-ts/index.d.ts",
"main": "build-ts/cjs/index.js",
"module": "build-ts/mjs/index.js",
"exports": {
".": {
"import": "./build-ts/mjs/index.js",
"require": "./build-ts/cjs/index.js"
}
},
"repository": {
"type": "git",
"url": "git+https:/aws/glide-for-redis.git"
Expand All @@ -25,8 +31,8 @@
"build-internal": "cd rust-client && npm run build",
"build-internal:release": "cd rust-client && npm run build:release",
"build-internal:benchmark": "cd rust-client && npm run build:benchmark",
"build-external": "rm -rf build-ts && npx tsc",
"build-external:release": "rm -rf build-ts && npx tsc --stripInternal",
"build-external": "rm -rf build-ts && tsc -p tsconfig-mjs.json && tsc -p tsconfig-cjs.json && ./fixup_pj_files_for_build_type",
"build-external:release": "rm -rf build-ts && tsc -p tsconfig-mjs.json && tsc -p tsconfig-cjs.json && ./fixup_pj_files_for_build_type --stripInternal",
"build-protobuf": "npm run compile-protobuf-files && npm run fix-protobuf-file",
"compile-protobuf-files": "cd src && pbjs -t static-module -o ProtobufMessage.js ../../glide-core/src/protobuf/*.proto && pbts -o ProtobufMessage.d.ts ProtobufMessage.js",
"fix-protobuf-file": "replace 'this\\.encode\\(message, writer\\)\\.ldelim' 'this.encode(message, writer && writer.len ? writer.fork() : writer).ldelim' src/ProtobufMessage.js",
Expand Down
29 changes: 29 additions & 0 deletions node/tsconfig-base.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "ES6" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
"sourceMap": true /* Create source map files for emitted JavaScript files. */,
"declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
"strict": true /* Enable all strict type-checking options. */,
"skipLibCheck": true /* Skip type checking all .d.ts files. */,
"allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */,
"allowSyntheticDefaultImports": true /* Allow 'import x from y' when a module doesn't have a default export. */,
"baseUrl": "./" /* Specify the base directory to resolve non-relative module names. */,
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
"inlineSourceMap": false /* Include sourcemap files inside the emitted JavaScript. */,
"listEmittedFiles": false /* List emitted files. */,
"listFiles": false /* Print names of files part of the compilation */,
"moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
"noFallthroughCasesInSwitch": true /* Enable error reporting for fallthrough cases in switch statements. */,
"pretty": true,
"resolveJsonModule": true /* Enable importing .json files. */,
"rootDir": "./" /* Specify the root folder within your source files. */,
"traceResolution": false /* true to have TypeScript print information about its resolution process for each processed file */,
"lib": [
"esnext"
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */
},
"compileOnSave": false,
"include": ["./*.ts", "src/*.ts", "src/*.js"],
"exclude": ["node_modules", "build-ts"]
}
9 changes: 9 additions & 0 deletions node/tsconfig-cjs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "./tsconfig-base.json",
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
"module": "commonjs" /* Specify what module code is generated. */,
/* Emit */
"outDir": "./build-ts/cjs" /* Specify an output folder for all emitted files. */
}
}
9 changes: 9 additions & 0 deletions node/tsconfig-mjs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "./tsconfig-base.json",
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
"module": "ES2022" /* Specify what module code is generated. */,
/* Emit */
"outDir": "./build-ts/mjs" /* Specify an output folder for all emitted files. */
}
}
Loading

0 comments on commit bcaf627

Please sign in to comment.