Как проверить, работает ли скрипт под node.js?
У меня есть скрипт, который мне нужен из скрипта node.js, который я хочу сохранить независимым от движка javascript.
Так, например, я хочу сделать:
exports.x = y;
только если он работает под node.js. как я могу выполнить этот тест?
Редактировать: Когда я писал этот вопрос, я не знал, что функция модулей node.js основана на commonjs.
Для конкретного примера я дал более точный вопрос:
Как скрипт может определить, был ли он необходим в качестве модуля commonjs?
25 ответов
Ища поддержку CommonJS, вот как библиотека http://underscorejs.org/ делает это:
Изменить: на ваш обновленный вопрос:
(function () {
// Establish the root object, `window` in the browser, or `global` on the server.
var root = this;
// Create a reference to this
var _ = new Object();
var isNode = false;
// Export the Underscore object for **CommonJS**, with backwards-compatibility
// for the old `require()` API. If we're not in CommonJS, add `_` to the
// global object.
if (typeof module !== 'undefined' && module.exports) {
module.exports = _;
root._ = _;
isNode = true;
} else {
root._ = _;
}
})();
Пример здесь сохраняет шаблон модуля.
Ну, нет надежного способа обнаружить запуск в Node.js, так как каждый веб-сайт может легко объявить одни и те же переменные, так как нет window
Объект в Node.js по умолчанию вы можете пойти другим путем и проверить, работаете ли вы в браузере.
Вот что я использую для библиотек, которые должны работать как в браузере, так и под Node.js:
if (typeof window === 'undefined') {
exports.foo = {};
} else {
window.foo = {};
}
Это может все еще взорваться в случае, если window
определено в Node.js, но для этого нет веской причины, так как вам явно нужно было бы пропустить var
или установите свойство на global
объект.
РЕДАКТИРОВАТЬ
Определить, нужен ли ваш скрипт как модуль CommonJS, опять же нелегко. CommonJS указывает только то, что A: модули будут включены через вызов функции require
и B: модули экспортируют вещи через свойства exports
объект. Теперь, как это реализовать, оставлено на усмотрение базовой системы. Node.js оборачивает содержимое модулей в анонимную функцию.
function (exports, require, module, __filename, __dirname) {
См.: https://github.com/ry/node/blob/master/src/node.js#L325
Но не ходите туда, пытаясь обнаружить это с помощью какого-то arguments.callee.toString()
вместо этого просто используйте мой пример кода выше, который проверяет браузер, Node.js является более чистой средой, поэтому маловероятно, что window
будет объявлено там.
В настоящее время я наткнулся на неправильное обнаружение узла, который не знает об окружении узла в Electron из-за вводящего в заблуждение обнаружения признаков. Следующие решения явно определяют среду процесса.
Определить только Node.js
(typeof process !== 'undefined') && (process.release.name === 'node')
Это обнаружит, если вы работаете в Node-процессе, так как process.release
содержит "метаданные, относящиеся к текущей версии [Node-]".
После появления io.js значение process.release.name
также может стать io.js
(см. документ- процесс). Чтобы правильно определить Node-ready среду, я думаю, вам следует проверить следующее:
Определить узел (>= 3.0.0) или io.js
(typeof process !== 'undefined') &&
(process.release.name.search(/node|io.js/) !== -1)
Это утверждение было проверено с Узлом 5.5.0, Электроном 0.36.9 (с Узлом 5.1.1) и Chrome 48.0.2564.116.
Определить узел (>= 0.10.0) или io.js
(typeof process !== 'undefined') &&
(typeof process.versions.node !== 'undefined')
Комментарий @daluege вдохновил меня подумать о более общем доказательстве. Это должно работать с Node.js> = 0.10. Я не нашел уникальный идентификатор для предыдущих версий.
PS: я публикую этот ответ здесь, так как вопрос привел меня сюда, хотя ОП искал ответ на другой вопрос.
Проблема с попыткой выяснить, в какой среде работает ваш код, состоит в том, что любой объект может быть изменен и объявлен, что делает практически невозможным выяснение того, какие объекты являются естественными для среды, а какие были изменены программой.
Однако есть несколько приемов, которые мы можем использовать, чтобы точно определить, в какой среде вы находитесь.
Давайте начнем с общепринятого решения, которое используется в библиотеке подчеркивания:
typeof module !== 'undefined' && module.exports
Этот метод на самом деле идеально подходит для серверной части, как, например, когда require
функция вызывается, она сбрасывает this
объект к пустому объекту и переопределяет module
для вас снова, то есть вам не нужно беспокоиться о вмешательстве извне. Пока ваш код загружен с require
, ты в безопасности.
Тем не менее, это не работает в браузере, так как любой может легко определить module
чтобы казалось, что это тот объект, который вы ищете. С одной стороны, это может быть желаемое поведение, но оно также определяет, какие переменные пользователь библиотеки может использовать в глобальной области видимости. Может быть, кто-то хочет использовать переменную с именем module
который имеет exports
внутри него для другого использования. Это маловероятно, но кто мы такие, чтобы судить, какие переменные может использовать кто-то другой, просто потому, что другое окружение использует это имя переменной?
Однако хитрость заключается в том, что если мы предполагаем, что ваш скрипт загружается в глобальной области видимости (что будет, если он будет загружен с помощью тега скрипта), переменная не может быть зарезервирована во внешнем закрытии, потому что браузер не позволяет этого, Теперь запомните в узле this
объект является пустым объектом, но module
переменная все еще доступна. Это потому, что он объявлен во внешнем закрытии. Таким образом, мы можем исправить проверку подчеркивания, добавив дополнительную проверку:
this.module !== module
С этим, если кто-то заявляет module
в глобальной области видимости в браузере, он будет помещен в this
объект, который приведет к сбою теста, потому что this.module
будет тем же объектом, что и модуль. На узле, this.module
не существует, и module
существует во внешнем замыкании, поэтому проверка будет успешной, поскольку они не эквивалентны.
Итак, финальный тест:
typeof module !== 'undefined' && this.module !== module
Примечание: хотя теперь это позволяет module
переменная, которая будет свободно использоваться в глобальной области видимости, все еще возможно обойти это в браузере, создав новое закрытие и объявив module
в этом, затем загружая скрипт в этом замыкании. На этом этапе пользователь полностью реплицирует среду узла и, надеюсь, знает, что он делает, и пытается выполнить требования стиля узла. Если код вызывается в теге скрипта, он все равно будет защищен от любых новых внешних замыканий.
Следующее работает в браузере, если намеренно, явно не саботируется:
if(typeof process === 'object' && process + '' === '[object process]'){
// is node
}
else{
// not node
}
Bam.
Вот довольно крутой способ сделать это:
const isBrowser = this.window === this;
Это работает, потому что в браузерах глобальная переменная this имеет собственную ссылку, называемую window. Эта ссылка на себя не существует в узле.
- В браузере "this" является ссылкой на глобальный объект, называемый "окном".
- В узле this это ссылка на объект module.exports.
- "this" не является ссылкой на глобальный объект Node, называемый "global".
- "this" не является ссылкой на пространство объявления переменных модуля.
Чтобы сломать вышеупомянутую проверку браузера, вам нужно сделать что-то вроде следующего
this.window = this;
перед выполнением проверки.
Еще одно обнаружение среды:
(Значение: большинство ответов здесь в порядке.)
function isNode() {
return typeof global === 'object'
&& String(global) === '[object global]'
&& typeof process === 'object'
&& String(process) === '[object process]'
&& global === global.GLOBAL // circular ref
// process.release.name cannot be altered, unlike process.title
&& /node|io\.js/.test(process.release.name)
&& typeof setImmediate === 'function'
&& setImmediate.length === 4
&& typeof __dirname === 'string'
&& Should I go on ?..
}
Немного параноик, верно? Вы можете сделать это более многословным, проверив наличие большего числа глобальных переменных.
Но НЕ!
Все это выше может быть подделано / смоделировано в любом случае.
Например, подделать global
объект:
global = {
toString: function () {
return '[object global]';
},
GLOBAL: global,
setImmediate: function (a, b, c, d) {}
};
setImmediate = function (a, b, c, d) {};
...
Это не будет привязано к исходному глобальному объекту Узла, но будет привязано к window
объект в браузере. Так что это будет означать, что вы находитесь в Node env внутри браузера.
Жизнь коротка!
Неужели мы заботимся о том, что наша среда подделана? Это произойдет, когда какой-нибудь глупый разработчик объявит глобальную переменную global
в глобальном масштабе. Или какой-то злой разработчик как-то внедряет код в нашу среду.
Мы можем запретить выполнение нашего кода при обнаружении этого, но множество других зависимостей нашего приложения могут попасть в это. Так что в конечном итоге код сломается. Если ваш код достаточно хорош, вы не должны заботиться о каждой глупой ошибке, которую могли совершить другие.
И что?
При ориентации на 2 среды: браузер и узел;"use strict"
; и либо просто проверьте window
или же global
; и четко указать, что в документации ваш код поддерживает только эти среды. Это оно!
var isBrowser = typeof window !== 'undefined'
&& ({}).toString.call(window) === '[object Window]';
var isNode = typeof global !== "undefined"
&& ({}).toString.call(global) === '[object global]';
Если возможно для вашего варианта использования; вместо обнаружения среды; сделать синхронное определение функции в блоке try/catch. (это займет несколько миллисекунд для выполнения).
например
function isPromiseSupported() {
var supported = false;
try {
var p = new Promise(function (res, rej) {});
supported = true;
} catch (e) {}
return supported;
}
На момент написания этой статьи этот ответ скорее относится к варианту "скоро", поскольку в нем используются очень новые функции JavaScript.
const runtime = globalThis.process?.release?.name || 'not node'
console.log(runtime)
В runtime
значение будет либо node
или not node
.
Как уже упоминалось, это зависит от нескольких новых функций JavaScript. globalThis
является завершенной функцией спецификации ECMAScript 2020. Необязательное объединение цепочек / нулевое объединение (?
часть globalThis.process?.release?.name
) поддерживается в движке V8, который поставляется с Chrome 80. По состоянию на 8 апреля 2020 года этот код будет работать в браузере, но не будет работать в Node, поскольку ветвь Node 13 использует V8 7.9.xxx. Я считаю, что Node 14 (выпуск должен состояться 21.04.2020) должен использовать V8 8.x+.
Этот подход сопровождается изрядной дозой текущих ограничений. Однако; скорость, с которой выпускаются браузеры /Node, в конечном итоге будет надежным однострочником.
Большинство из предложенных решений могут быть подделаны. Надежный способ проверить внутренние Class
свойство глобального объекта с использованием Object.prototype.toString
, Внутренний класс не может быть подделан в JavaScript:
var isNode =
typeof global !== "undefined" &&
{}.toString.call(global) == '[object global]';
Как насчет использования объекта процесса и проверки execPath для node
?
process.execPath
Это абсолютный путь к исполняемому файлу, который запустил процесс.
Пример:
/ USR / местные / бен / узел
Я использую process
проверить на node.js, как это
if (typeof(process) !== 'undefined' && process.version === 'v0.9.9') {
console.log('You are running Node.js');
} else {
// check for browser
}
или же
if (typeof(process) !== 'undefined' && process.title === 'node') {
console.log('You are running Node.js');
} else {
// check for browser
}
Документировано здесь
Вот мой вариант того, что выше:
(function(publish) {
"use strict";
function House(no) {
this.no = no;
};
House.prototype.toString = function() {
return "House #"+this.no;
};
publish(House);
})((typeof module == 'undefined' || (typeof window != 'undefined' && this == window))
? function(a) {this["House"] = a;}
: function(a) {module.exports = a;});
Чтобы использовать его, вы изменяете "Дом" во второй последней строке, чтобы он был таким, каким вы хотите, чтобы имя модуля было в браузере, и публикуете все, что хотите, чтобы значение модуля было (обычно конструктор или литерал объекта).).
В браузерах глобальным объектом является window, и он имеет ссылку на себя (есть window.window, который == window). Мне кажется, что это вряд ли произойдет, если вы не находитесь в браузере или в среде, которая хочет, чтобы вы верили, что вы в браузере. Во всех других случаях, если объявлена глобальная переменная 'module', она использует ее, в противном случае она использует глобальный объект.
Как скрипт может определить, был ли он необходим в качестве модуля commonjs?
Связанный: чтобы проверить, был ли он необходим, поскольку модуль запускается непосредственно в узле, вы можете проверить require.main !== module
, http://nodejs.org/docs/latest/api/modules.html
Node.js имеет process
объект, так что пока у вас нет другого скрипта, который создает process
Вы можете использовать его, чтобы определить, выполняется ли код на узле.
var isOnNodeJs = false;
if(typeof process != "undefined") {
isOnNodeJs = true;
}
if(isOnNodeJs){
console.log("you are running under node.js");
}
else {
console.log("you are NOT running under node.js");
}
Из источника отладочного пакета:
const isBrowser = typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs
https://github.com/visionmedia/debug/blob/master/src/index.js
Это довольно безопасный и простой способ обеспечения совместимости между javascript на стороне сервера и на стороне клиента, который также будет работать с browserify, RequireJS или CommonJS, включая клиентскую часть:
(function(){
// `this` now refers to `global` if we're in NodeJS
// or `window` if we're in the browser.
}).call(function(){
return (typeof module !== "undefined" &&
module.exports &&
typeof window === 'undefined') ?
global : window;
}())
const isNode =
typeof process !== 'undefined' &&
process.versions != null &&
process.versions.node != null;
const isNode = ({}).toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]';
Изменить: Относительно вашего обновленного вопроса: "Как скрипт может определить, был ли он необходим в качестве модуля commonjs?" Я не думаю, что это возможно. Вы можете проверить, exports
это объект (if (typeof exports === "object")
), поскольку спецификация требует, чтобы она была предоставлена модулям, но все, что вам говорит, это то, что... exports
это объект.:-)
Оригинальный ответ:
Я уверен, что есть какой-то специфичный для NodeJS символ (EventEmitter
, возможно, нет, вы должны использовать require
получить модуль событий; см. ниже), что вы могли бы проверить, но, как сказал Дэвид, в идеале лучше обнаруживать функцию (а не среду), если есть смысл это делать.
Обновление: возможно что-то вроде:
if (typeof require === "function"
&& typeof Buffer === "function"
&& typeof Buffer.byteLength === "function"
&& typeof Buffer.prototype !== "undefined"
&& typeof Buffer.prototype.write === "function") {
Но это просто говорит вам, что вы находитесь в среде с require
и что-то очень, очень похожее на NodeJS Buffer
,:-)
Я думаю, что это довольно просто.
if (process && process.title === 'node') {
// Your code here
}
Вы можете использовать геттеры, чтобы определить, когда вашmodule.exports
читается, и это будет означать, что это былоrequire
д.
let required = false;
function myFunction() {
// your code whatever
return required;
}
if (typeof module == 'object') {
Object.defineProperty(module, 'exports', {
get: () => {
required = true;
return myFunction;
}
})
}
Протестировано в моей консоли в ChromeVersion 106.0.5249.119 (Official Build) (x86_64)
и узелv16.15.0
Это не дает прямого ответа на ваш вопрос, так как вы хотели конкретно проверить Node.js, однако это достаточно полезно, чтобы сказать:
В большинстве случаев, если вы просто хотите различать браузеры и серверный javascript, достаточно просто проверить наличие документа.
if (typeof document !== 'undefined') {} // do stuff
// This one is overkill, but 100% always works:
if (typeof window !== 'undefined' && window && window.window === window) {
if (typeof window.document !== 'undefined' && document.documentElement) {
}
}
Возьмите источник node.js и измените его, чтобы определить переменную, такую как runningOnNodeJS
, Проверьте эту переменную в вашем коде.
Если у вас нет собственной частной версии node.js, откройте запрос на добавление функций в проекте. Попросите, чтобы они определили переменную, которая дает вам версию node.js, в которой вы работаете. Затем проверьте эту переменную.
Очень старый пост, но я решил его, обернув операторы require в try - catch
try {
var fs = require('fs')
} catch(e) {
alert('you are not in node !!!')
}