From 8928df7797641b235b068d116aa8490589405a01 Mon Sep 17 00:00:00 2001 From: solpkr1 Date: Tue, 7 Jan 2025 03:19:54 +0000 Subject: [PATCH] add support for utilizing npm package in ESM environments --- .../add-js-extensions.js | 98 ++++++++++++++ .../package-lock.json | 126 +++++++++++++++++- yellowstone-grpc-client-nodejs/package.json | 19 ++- yellowstone-grpc-client-nodejs/src/index.ts | 2 +- .../tsconfig.cjs.json | 18 +++ .../tsconfig.esm.json | 15 +++ yellowstone-grpc-client-nodejs/tsconfig.json | 1 + 7 files changed, 270 insertions(+), 9 deletions(-) create mode 100644 yellowstone-grpc-client-nodejs/add-js-extensions.js create mode 100644 yellowstone-grpc-client-nodejs/tsconfig.cjs.json create mode 100644 yellowstone-grpc-client-nodejs/tsconfig.esm.json diff --git a/yellowstone-grpc-client-nodejs/add-js-extensions.js b/yellowstone-grpc-client-nodejs/add-js-extensions.js new file mode 100644 index 00000000..9aa0b8c7 --- /dev/null +++ b/yellowstone-grpc-client-nodejs/add-js-extensions.js @@ -0,0 +1,98 @@ +const fs = require('fs'); +const path = require('path'); +const recast = require('recast'); + +//list of external packages that require '.js' extensions +const packagesRequiringJsExtension = [ + 'protobufjs/minimal', + //add other package paths as needed +]; + +function shouldAppendJsExtension(source) { + //check if the path has an extension already + if (path.extname(source)) { + return false; + } + + //check if the path is relative + if (source.startsWith('./') || source.startsWith('../')) { + return true; + } + + //check if the path is in the whitelist of external packages + return packagesRequiringJsExtension.some(pkg => source === pkg || source.startsWith(`${pkg}/`)); +} + + +function processFile(filePath) { + const code = fs.readFileSync(filePath, 'utf8'); + const ast = recast.parse(code, { + parser: require('recast/parsers/babel'), // Use Babel parser + }); + + let modified = false; + + recast.types.visit(ast, { + visitImportDeclaration(pathNode) { + const source = pathNode.node.source.value; + if (shouldAppendJsExtension(source)) { + pathNode.node.source.value = `${source}.js`; + modified = true; + } + this.traverse(pathNode); + }, + visitExportNamedDeclaration(pathNode) { + if (pathNode.node.source && pathNode.node.source.value) { + const source = pathNode.node.source.value; + if (shouldAppendJsExtension(source)) { + pathNode.node.source.value = `${source}.js`; + modified = true; + } + } + this.traverse(pathNode); + }, + visitExportAllDeclaration(pathNode) { + if (pathNode.node.source && pathNode.node.source.value) { + const source = pathNode.node.source.value; + if (shouldAppendJsExtension(source)) { + pathNode.node.source.value = `${source}.js`; + modified = true; + } + } + this.traverse(pathNode); + }, + }); + + if (modified) { + const output = recast.print(ast).code; + fs.writeFileSync(filePath, output, 'utf8'); + console.log(`Updated import/export paths in: ${filePath}`); + } +} + + +function traverseDir(dir) { + fs.readdirSync(dir).forEach((file) => { + const fullPath = path.join(dir, file); + const stat = fs.statSync(fullPath); + + if (stat.isDirectory()) { + traverseDir(fullPath); + } else if (stat.isFile() && path.extname(fullPath) === '.js') { + processFile(fullPath); + } + }); +} + +function main() { + const esmDir = path.resolve(__dirname, './dist/esm'); + + if (!fs.existsSync(esmDir)) { + console.error(`Directory not found: ${esmDir}`); + process.exit(1); + } + + traverseDir(esmDir); +} + +main(); diff --git a/yellowstone-grpc-client-nodejs/package-lock.json b/yellowstone-grpc-client-nodejs/package-lock.json index 9c459af3..401df3f4 100644 --- a/yellowstone-grpc-client-nodejs/package-lock.json +++ b/yellowstone-grpc-client-nodejs/package-lock.json @@ -12,8 +12,10 @@ "@grpc/grpc-js": "^1.8.0" }, "devDependencies": { + "@babel/parser": "^7.26.3", "@solana/rpc-api": "=2.0.0", "prettier": "^2.8.3", + "recast": "^0.23.9", "ts-proto": "^1.139.0", "typescript": "=5.2.2" }, @@ -21,6 +23,56 @@ "node": ">=20.18.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", + "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@grpc/grpc-js": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.3.tgz", @@ -494,6 +546,19 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/case-anything": { "version": "2.1.13", "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", @@ -595,14 +660,28 @@ "license": "MIT" }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/fastestsmallesttextencoderdecoder": { "version": "1.0.22", "resolved": "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz", @@ -696,6 +775,23 @@ "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", "license": "Apache-2.0" }, + "node_modules/recast": { + "version": "0.23.9", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.9.tgz", + "integrity": "sha512-Hx/BGIbwj+Des3+xy5uAtAbdCyqK9y9wbBcDFDYanLS9JnMqf7OeF87HQwUimE87OEc72mr6tkKUKMBBL+hF9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -705,6 +801,16 @@ "node": ">=0.10.0" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -731,6 +837,13 @@ "node": ">=8" } }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "dev": true, + "license": "MIT" + }, "node_modules/ts-poet": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/ts-poet/-/ts-poet-6.4.1.tgz", @@ -824,6 +937,13 @@ "pbts": "bin/pbts" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", diff --git a/yellowstone-grpc-client-nodejs/package.json b/yellowstone-grpc-client-nodejs/package.json index 3dd1d4e2..01205d3f 100644 --- a/yellowstone-grpc-client-nodejs/package.json +++ b/yellowstone-grpc-client-nodejs/package.json @@ -4,12 +4,13 @@ "license": "Apache-2.0", "author": "Triton One", "description": "Yellowstone gRPC Geyser Node.js Client", - "main": "dist/index.js", - "types": "dist/index.d.ts", + "main": "dist/cjs/index.js", + "types": "dist/types/index.d.ts", "scripts": { - "build": "npm run grpc-generate && tsc -p .", + "build": "npm run grpc-generate && tsc --project tsconfig.esm.json && tsc --project tsconfig.cjs.json && npm run cp-encoding-files && node add-js-extensions.js", + "cp-encoding-files": "mkdir -p dist/esm/encoding && cp -r src/encoding/* dist/cjs/encoding/ && mkdir -p dist/esm/encoding && cp -r src/encoding/* dist/esm/encoding/", "fmt": "prettier -w .", - "grpc-generate": "mkdir -p src/grpc && protoc -I../yellowstone-grpc-proto/proto --plugin=node_modules/.bin/protoc-gen-ts_proto --ts_proto_opt=forceLong=string --ts_proto_opt=outputServices=grpc-js --experimental_allow_proto3_optional --ts_proto_out=src/grpc geyser.proto" + "grpc-generate": "mkdir -p src/grpc && protoc -I../yellowstone-grpc-proto/proto --plugin=node_modules/.bin/protoc-gen-ts_proto --ts_proto_opt=forceLong=string --ts_proto_opt=outputServices=grpc-js --experimental_allow_proto3_optional --ts_proto_out=src/grpc geyser.proto --ts_proto_opt=esModuleInterop=true" }, "repository": { "type": "git", @@ -29,8 +30,10 @@ "@grpc/grpc-js": "^1.8.0" }, "devDependencies": { + "@babel/parser": "^7.26.3", "@solana/rpc-api": "=2.0.0", "prettier": "^2.8.3", + "recast": "^0.23.9", "ts-proto": "^1.139.0", "typescript": "=5.2.2" }, @@ -39,5 +42,11 @@ }, "files": [ "dist" - ] + ], + "exports": { + ".": { + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js" + } + } } diff --git a/yellowstone-grpc-client-nodejs/src/index.ts b/yellowstone-grpc-client-nodejs/src/index.ts index 47e1b71c..f225886f 100644 --- a/yellowstone-grpc-client-nodejs/src/index.ts +++ b/yellowstone-grpc-client-nodejs/src/index.ts @@ -177,7 +177,7 @@ export default class Client { commitment, accountsDataSlice, }, - (err) => { + (err: any) => { if (err === null || err === undefined) { resolve(); } else { diff --git a/yellowstone-grpc-client-nodejs/tsconfig.cjs.json b/yellowstone-grpc-client-nodejs/tsconfig.cjs.json new file mode 100644 index 00000000..1dfa0dec --- /dev/null +++ b/yellowstone-grpc-client-nodejs/tsconfig.cjs.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "module": "CommonJS", + "esModuleInterop": true, + "noImplicitAny": true, + "removeComments": true, + "preserveConstEnums": true, + "sourceMap": true, + "allowJs": true, + "outDir": "dist/cjs", + "moduleResolution": "Node", + "declaration": true, + "declarationDir": "dist/types" + }, + "files": ["src/index.ts"], + "include": ["src/**/*"], + "exclude": ["node_modules", "**/*.test.js"] +} diff --git a/yellowstone-grpc-client-nodejs/tsconfig.esm.json b/yellowstone-grpc-client-nodejs/tsconfig.esm.json new file mode 100644 index 00000000..7094a982 --- /dev/null +++ b/yellowstone-grpc-client-nodejs/tsconfig.esm.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "ESNext", + "target": "ES2022", + "moduleResolution": "Node", + "esModuleInterop": true, + "outDir": "dist/esm", + "declaration": true, + "declarationDir": "dist/types" + }, + "files": ["src/index.ts"], + "include": ["src/**/*"], + "exclude": ["node_modules", "**/*.test.js"] +} + diff --git a/yellowstone-grpc-client-nodejs/tsconfig.json b/yellowstone-grpc-client-nodejs/tsconfig.json index b51ff906..26ba6c44 100644 --- a/yellowstone-grpc-client-nodejs/tsconfig.json +++ b/yellowstone-grpc-client-nodejs/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "esModuleInterop": true, "outDir": "dist", "rootDir": "src", "declaration": true