├── .gitignore ├── test ├── node-test.js ├── bun-test.js └── deno-test.js ├── index.d.ts ├── package.json ├── LICENSE.txt ├── test.js ├── index.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /test/node-test.js: -------------------------------------------------------------------------------- 1 | import isNode from '../index.js' 2 | import assert from 'node:assert' 3 | 4 | assert.strictEqual(true, isNode) 5 | -------------------------------------------------------------------------------- /test/bun-test.js: -------------------------------------------------------------------------------- 1 | globalThis.global = globalThis 2 | 3 | //// Not possible in Bun 4 | // delete globalThis.Bun 5 | 6 | import('./node-test.js') 7 | -------------------------------------------------------------------------------- /test/deno-test.js: -------------------------------------------------------------------------------- 1 | globalThis.global = globalThis 2 | 3 | globalThis.process = {} 4 | 5 | delete globalThis.Deno 6 | 7 | import('./node-test.js') 8 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export default isReallyNode; 2 | /** 3 | * True if we're definitely running inside Node.js. 4 | */ 5 | declare const isReallyNode: boolean; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "is-really-node", 3 | "version": "1.0.0", 4 | "description": "Determine whether the current runtime is _really_ Node.js, or some impostor.", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "engines": { 8 | "node": ">=16" 9 | }, 10 | "scripts": { 11 | "test": "node test" 12 | }, 13 | "type": "module", 14 | "repository": { 15 | "type": "git", 16 | "url": "git+ssh://git@github.com/bengl/is-really-node.git" 17 | }, 18 | "keywords": [ 19 | "Node.js", 20 | "Bun", 21 | "Deno", 22 | "detect", 23 | "environment" 24 | ], 25 | "author": "Bryan English ", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/bengl/is-really-node/issues" 29 | }, 30 | "homepage": "https://github.com/bengl/is-really-node#readme" 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Bryan English and the is-really-node authors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import { exec } from 'child_process' 2 | import test from 'node:test' 3 | import assert from 'assert' 4 | 5 | function run(...args) { 6 | return new Promise((resolve, reject) => { 7 | exec(...args, (err, stdout, stderr) => { 8 | if (err) reject(err) 9 | resolve({ stdout, stderr }) 10 | }) 11 | }) 12 | } 13 | 14 | test('Node.js', async () => { 15 | await run('node test/node-test.js') 16 | }) 17 | 18 | test('Node.js (allow-natives-syntax)', async () => { 19 | await run('node --allow-natives-syntax test/node-test.js') 20 | }) 21 | 22 | test('Node.js (no-allow-natives-syntax)', async () => { 23 | await run('node --no-allow-natives-syntax test/node-test.js') 24 | }) 25 | 26 | test('Deno', async () => { 27 | await assert.rejects( 28 | () => run('deno run test/node-test.js') 29 | ) 30 | }) 31 | 32 | test('Deno (faking Node.js)', async () => { 33 | await assert.rejects( 34 | () => run('deno run test/deno-test.js') 35 | ) 36 | }) 37 | 38 | test('Deno (faking Node.js) (allow-natives-syntax)', async () => { 39 | await assert.rejects( 40 | () => run('deno run --v8-flags="--allow-natives-syntax" test/deno-test.js') 41 | ) 42 | }) 43 | 44 | test('Deno (faking Node.js) (no-allow-natives-syntax)', async () => { 45 | await assert.rejects( 46 | () => run('deno run --v8-flags="--allow-natives-syntax" test/deno-test.js') 47 | ) 48 | }) 49 | 50 | test('Bun', async () => { 51 | await assert.rejects( 52 | () => run('deno run test/node-test.js') 53 | ) 54 | }) 55 | 56 | test('Bun (faking Node.js)', async () => { 57 | await assert.rejects( 58 | () => run('deno run test/deno-test.js') 59 | ) 60 | }) 61 | 62 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Let's check for some basic globals. If these aren't present, game over. 3 | */ 4 | function hasNodeGlobals() { 5 | return typeof global === 'object' && 6 | typeof globalThis === 'object' && 7 | global === globalThis && 8 | global.global === global && 9 | globalThis.globalThis === globalThis && 10 | typeof process === 'object' 11 | } 12 | 13 | /** 14 | * Now, we check for the Bun global object. The Bun object is 15 | * non-configurable, so it would be very hard to get past this if trying to 16 | * pretend to be Node.js. 17 | */ 18 | function hasBunGlobal() { 19 | return typeof Bun !== 'undefined' || 20 | typeof global.Bun !== 'undefined' 21 | } 22 | 23 | /** 24 | * We'll do the same here for Deno. The Deno object _can_ be deleted, so this 25 | * could be faked around, so we need the subsequent test as well. 26 | */ 27 | function hasDenoGlobal() { 28 | return typeof Deno === 'object' || 29 | typeof global.Deno === 'object' 30 | } 31 | 32 | /** 33 | * V8 has "natives syntax" which allows us to do some sneaky things. We'll now 34 | * test that those sneaky things are there and working. If it works, then 35 | * we're definitely not in Bun, which uses JSC. If it does, we'll try to 36 | * disable it, ruling out Deno. 37 | */ 38 | async function isNodeIshV8() { 39 | let v8 40 | try { 41 | v8 = await import('node:v8') 42 | } catch { 43 | return false 44 | } 45 | 46 | function privateSymbol() { 47 | try { 48 | const sym = eval('%CreatePrivateSymbol("testing")') 49 | const obj = {} 50 | obj[sym] = 3 51 | return !Reflect.ownKeys(obj).includes(sym) && obj[sym] === 3 52 | } catch { 53 | return false 54 | } 55 | } 56 | 57 | // JSC/Bun doesn't support V8's native syntax. 58 | if (privateSymbol()) { 59 | // Natives syntax was on. Try turning it off. That' won't work in Deno. 60 | v8.setFlagsFromString('--no-allow-natives-syntax') 61 | return !privateSymbol() 62 | } 63 | 64 | // Deno "supports" this, but it does nothing. So if it failed before, it 65 | // will fail again. 66 | v8.setFlagsFromString('--allow-natives-syntax') 67 | return privateSymbol() 68 | } 69 | 70 | /** 71 | * True if we're definitely running inside Node.js. 72 | */ 73 | const isReallyNode = hasNodeGlobals() && 74 | !hasBunGlobal() && 75 | !hasDenoGlobal() && 76 | await isNodeIshV8() 77 | 78 | export default isReallyNode; 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # is-really-node 2 | 3 | Sometimes you want to make sure that the JavaScript runtime you're expecting to 4 | be running on is the one you're actually running on. With this library, you can 5 | detect whether you're actually running inside Node.js, and not Deno or Bun. A 6 | single (default) export is provided, and it's a boolean that's true if we're 7 | definitely running inside Node.js. 8 | 9 | This is done by first checking various globals, and then attempting to alter v8 10 | options, and testing those options have been altered. This is only currently 11 | possible in Node.js. The aim is to never have false negatives, and also load 12 | quickly if the globals-checking gives us a negative. 13 | 14 | ## Some Details (FAQ) 15 | 16 | * Why not TypeScript or at least JSDoc? 17 | * The module is very small, and the code does some things that would probably 18 | piss off `tsc`. 19 | * I don't use TypeScript in my day job, so I tend not to default to it. Don't 20 | worry, I'm not one of those haters you see on Twitter. 21 | * Submit a PR that works correctly and I'll add it. 22 | * Do you just hate Bun and Deno? Are you some Node.js purist? They're both 23 | Node.js compatible anyway! 24 | * No. A lot of code I work with targets very specific runtime things that 25 | only work in Node.js, so I want an easy way to bail early if that stuff 26 | isn't going to work, even if the runtime is trying to fake that it's 27 | Node.js. That's all. Please feel free to create `is-really-bun` or 28 | `is-really-deno` if that suits your work. 29 | * Isn't checking the globals alone enough? 30 | * Nope. If folks are trying to fake that they're in Node.js, they can always 31 | mess with the globals. Instead we need to do something that's _truly_ 32 | impossible in the other platforms, like messing with v8 options. 33 | * Can't a runtime (or runtime user) pretending to be Node.js just intercept 34 | loading and replace this module with `true`? 35 | * Yes, but if _this_ module is specifically targetted, the runtime (or 36 | runtime user) is putting enough effort into faking Node.js that they can 37 | handle whatever comes out of that assumption on their own. 38 | * Why no CommonJS? 39 | * Deno generally doesn't support CommonJS. Yes, it supports it via `npm:` 40 | imports, but that's difficult to use for testing. I'm lazy. Sorry. 41 | * That's a bad excuse though. Really I should make this work with `require`. 42 | Submit a PR that handles this and I'll add it. 43 | 44 | ## License 45 | 46 | The MIT License. See LICENSE.txt 47 | --------------------------------------------------------------------------------