[javascript] “Vanilla”자바 스크립트 라이브러리를 Node.js에로드

Node.js 서버에서 사용하고 싶은 일부 기능이있는 타사 Javascript 라이브러리가 있습니다. (특히 내가 찾은 QuadTree javascript 라이브러리를 사용하고 싶습니다.) 그러나 이러한 라이브러리는 .js“Node.js 라이브러리”가 아닌 단순한 파일입니다.

따라서 이러한 라이브러리는 exports.var_nameNode.js가 모듈에 대해 기대 하는 구문을 따르지 않습니다 . 지금까지 내가 그 방법을 이해 당신이 할 때, module = require('module_name');또는 module = require('./path/to/file.js');당신은 등 더 공개적으로 액세스 할 수있는 기능을 가진 모듈을하게 될 겁니다

내 질문은 “임의의 자바 스크립트 파일을 Node.js에로드하여 다시 작성하지 않고도 기능을 활용할 수 있도록하는 방법은 무엇 exports입니까?”입니다.

저는 Node.js를 처음 사용하므로 작동 방식을 이해하는 데 눈에 띄는 구멍이 있는지 알려주십시오.


편집 : 더 많은 것을 연구하고 이제 Node.js가 사용하는 모듈 로딩 패턴이 실제로 CommonJS 라는 Javascript 라이브러리를로드하기 위해 최근 개발 된 표준의 일부임을 알았습니다 . Node.js모듈 문서 페이지 에이 내용이 나와 있지만 지금까지 놓쳤습니다.

내 질문에 대한 대답은 “도서관의 작성자가 CommonJS 인터페이스를 작성하거나 직접 수행 할 때까지 기다리십시오”라는 것입니다.



답변

사용하는 것보다 훨씬 더 나은 방법이있다 eval: vm모듈.

예를 들어 다음은 전역 컨텍스트 또는 전역 컨텍스트 에서 execfile스크립트를 평가하는 내 모듈입니다 .pathcontext

var vm = require("vm");
var fs = require("fs");
module.exports = function(path, context) {
  context = context || {};
  var data = fs.readFileSync(path);
  vm.runInNewContext(data, context, path);
  return context;
}

그리고 다음과 같이 사용할 수 있습니다.

> var execfile = require("execfile");
> // `someGlobal` will be a global variable while the script runs
> var context = execfile("example.js", { someGlobal: 42 });
> // And `getSomeGlobal` defined in the script is available on `context`:
> context.getSomeGlobal()
42
> context.someGlobal = 16
> context.getSomeGlobal()
16

example.js포함하는 곳 :

function getSomeGlobal() {
    return someGlobal;
}

이 방법의 가장 큰 장점은 실행 된 스크립트에서 전역 변수를 완벽하게 제어 할 수 있다는 것입니다. 사용자 정의 전역 (을 통해 context)을 전달할 수 있으며 스크립트에 의해 생성 된 모든 전역이에 추가됩니다 context. 구문 오류 등이 올바른 파일 이름으로보고되기 때문에 디버깅도 더 쉽습니다.


답변

이 상황에 대한 ‘가장 올바른’답이라고 생각합니다.

라는 스크립트 파일이 있다고 가정합니다 quadtree.js.

node_module이런 종류의 디렉터리 구조를 가진 사용자 지정 을 만들어야합니다 .

./node_modules/quadtree/quadtree-lib/
./node_modules/quadtree/quadtree-lib/quadtree.js
./node_modules/quadtree/quadtree-lib/README
./node_modules/quadtree/quadtree-lib/some-other-crap.js
./node_modules/quadtree/index.js

./node_modules/quadtree/quadtree-lib/디렉토리의 모든 것은 타사 라이브러리의 파일입니다.

그러면 ./node_modules/quadtree/index.js파일이 파일 시스템에서 해당 라이브러리를로드하고 제대로 내보내기 작업을 수행합니다.

var fs = require('fs');

// Read and eval library
filedata = fs.readFileSync('./node_modules/quadtree/quadtree-lib/quadtree.js','utf8');
eval(filedata);

/* The quadtree.js file defines a class 'QuadTree' which is all we want to export */

exports.QuadTree = QuadTree

이제 quadtree다른 노드 모듈처럼 모듈을 사용할 수 있습니다 .

var qt = require('quadtree');
qt.QuadTree();

타사 라이브러리의 소스 코드를 변경할 필요가 없기 때문에이 방법을 좋아하므로 유지 관리가 더 쉽습니다. 업그레이드시해야 할 일은 소스 코드를 살펴보고 여전히 적절한 개체를 내보내고 있는지 확인하는 것입니다.


답변

가장 간단한 방법은 다음과 같습니다 eval(require('fs').readFileSync('./path/to/file.js', 'utf8'));
. 대화 형 셸에서 테스트 할 때 유용합니다.


답변

AFAIK, 실제로 모듈을로드해야하는 방법입니다. 그러나 내 보낸 모든 함수를 exports개체 에 고정하는 대신 에 this(그렇지 않으면 전역 개체가 될 수있는) 추가 할 수도 있습니다.

따라서 다른 라이브러리를 호환되도록 유지하려면 다음을 수행 할 수 있습니다.

this.quadTree = function () {
  // the function's code
};

또는, 외부 라이브러리가 이미 자신의 네임 스페이스, 예를 들어이있는 경우 jQuery(없는 당신이 사용할 수있는 것을 서버 측 환경에서)

this.jQuery = jQuery;

비 노드 환경 this에서 전역 개체로 확인되어 이미있는 전역 변수가됩니다. 그래서 그것은 아무것도 깨지 않아야합니다.

편집 : James Herdman은 초보자를위한 node.js에 대한 멋진 글을 가지고 있으며 이것도 언급합니다.


답변

다소 해키 한 솔루션이기 때문에 실제로 이것을 사용하게 될지 확실하지 않지만이 문제를 해결하는 한 가지 방법은 이와 같은 작은 미니 모듈 가져 오기 도구를 만드는 것입니다.

파일에서 ./node_modules/vanilla.js:

var fs = require('fs');

exports.require = function(path,names_to_export) {
    filedata = fs.readFileSync(path,'utf8');
    eval(filedata);
    exported_obj = {};
    for (i in names_to_export) {
        to_eval = 'exported_obj[names_to_export[i]] = '
            + names_to_export[i] + ';'
        eval(to_eval);
    }
    return exported_obj;
}

그런 다음 라이브러리의 기능을 사용하려면 내보낼 이름을 수동으로 선택해야합니다.

따라서 파일과 같은 라이브러리의 경우 ./lib/mylibrary.js

function Foo() { //Do something... }
biz = "Blah blah";
var bar = {'baz':'filler'};

Node.js 코드에서 기능을 사용하려는 경우 …

var vanilla = require('vanilla');
var mylibrary = vanilla.require('./lib/mylibrary.js',['biz','Foo'])
mylibrary.Foo // <-- this is Foo()
mylibrary.biz // <-- this is "Blah blah"
mylibrary.bar // <-- this is undefined (because we didn't export it)

이 모든 것이 실제로 얼마나 잘 작동하는지 모르겠습니다.


답변

스크립트를 매우 쉽게 업데이트하고 module.exports =적절한 곳에 간단히 추가하여 작동하도록 만들 수있었습니다 .

예를 들어, 나는 그들의 파일을 가져 와서 ‘./libs/apprise.js’에 복사했습니다. 그런 다음 시작하는 곳

function apprise(string, args, callback){

module.exports =따라서 기능을 할당했습니다 .

module.exports = function(string, args, callback){

따라서 다음과 같이 라이브러리를 코드 로 가져올 수 있습니다 .

window.apprise = require('./libs/apprise.js');

그리고 나는 갈 수 있었다. YMMV, 이것은 webpack 입니다.


답변

include(filename)오류 발생시에 대한 더 나은 오류 메시지 (스택, 파일 이름 등)가 있는 간단한 함수 eval:

var fs = require('fs');
// circumvent nodejs/v8 "bug":
// https://github.com/PythonJS/PythonJS/issues/111
// http://perfectionkills.com/global-eval-what-are-the-options/
// e.g. a "function test() {}" will be undefined, but "test = function() {}" will exist
var globalEval = (function() {
    var isIndirectEvalGlobal = (function(original, Object) {
        try {
            // Does `Object` resolve to a local variable, or to a global, built-in `Object`,
            // reference to which we passed as a first argument?
            return (1, eval)('Object') === original;
        } catch (err) {
            // if indirect eval errors out (as allowed per ES3), then just bail out with `false`
            return false;
        }
    })(Object, 123);
    if (isIndirectEvalGlobal) {
        // if indirect eval executes code globally, use it
        return function(expression) {
            return (1, eval)(expression);
        };
    } else if (typeof window.execScript !== 'undefined') {
        // if `window.execScript exists`, use it
        return function(expression) {
            return window.execScript(expression);
        };
    }
    // otherwise, globalEval is `undefined` since nothing is returned
})();

function include(filename) {
    file_contents = fs.readFileSync(filename, "utf8");
    try {
        //console.log(file_contents);
        globalEval(file_contents);
    } catch (e) {
        e.fileName = filename;
        keys = ["columnNumber", "fileName", "lineNumber", "message", "name", "stack"]
        for (key in keys) {
            k = keys[key];
            console.log(k, " = ", e[k])
        }
        fo = e;
        //throw new Error("include failed");
    }
}

그러나 nodejs를 사용하면 더러워집니다. 다음을 지정해야합니다.

export NODE_MODULE_CONTEXTS=1
nodejs tmp.js

그렇지 않으면에 포함 된 파일에서 전역 변수를 사용할 수 없습니다 include(...).