Does anyone have any examples of how to use reflect-metadata in nodejs? I am using atom-typescript also. I downloaded using it via npm but i keep getting random errors. I see Reflect is a blank object. Just looking for an example of how to incorporate this into a module or node project.
When I faced the same problem and nothing worked, I opened Reflect.ts file in node_modules. At the end, you can see it hooking Reflect at global level.
// hook global Reflect
(function(__global: any) {
if (typeof __global.Reflect !== "undefined") {
if (__global.Reflect !== Reflect) {
for (var p in Reflect) {
__global.Reflect[p] = (<any>Reflect)[p];
}
}
}
else {
__global.Reflect = Reflect;
}
})(
typeof window !== "undefined" ? window :
typeof WorkerGlobalScope !== "undefined" ? self :
typeof global !== "undefined" ? global :
Function("return this;")());
So, I removed require reflect-metadata from all other files, and moved it to main file.
require('reflect-metadata/Reflect');
Now, I can use it inside all modules (without requiring reflect-metadata) with the following syntax,
(<any>global).Reflect.getMetadata("design:type", target, key); // In Typescript
EDIT:
We can also reference reflect-metadata.d.ts file from node_modules and then use the API directly.
/// <reference path="../../node_modules/reflect-metadata/reflect-metadata.d.ts" />
Reflect.getMetadata("design:type", target, propertyKey);
In TypeScript#2.0+, you can use it like this:
npm i -S reflect-metadata
npm i -D #types/reflect-metadata
// your.ts
import 'reflect-metadata'
Reflect.getMetadata(...)
Reflect.metadata(...)
Related
My title is a bit vague, here is what I'm trying to do:
I have a typescript npm package
I want it to be useable on both node and browser.
I'm building it using a simple tsc command (no bundling), in order to get proper typings
My module has 1 entry point, an index.ts file, which exposes (re-exports) everything.
Some functions in this module are meant to be used on node-only, so there are node imports in some files, like:
import { fileURLToPath } from 'url'
import { readFile } from 'fs/promises'
import { resolve } from 'path'
// ...
I would like to find a way to:
Not trip-up bundlers with this
Not force users of this package to add "hacks" to their bundler config, like mentioned here: Node cannot find module "fs" when using webpack
Throw sensible Errors in case they are trying to use node-only features
Use proper typings inside my module, utilizing #types/node in my code
My main problem is, that no matter what, I have to import or require the node-only modules, which breaks requirement 1 (trips up bundlers, or forces the user to add some polyfill).
The only way I found that's working, is what isomorphic packages use, which is to have 2 different entry points, and mark it in my package.json like so:
{
// The entry point for node modules
"main": "lib/index.node.js",
// The entry point for bundlers
"browser": "lib/index.browser.js",
// Common typings
"typings": "lib/index.browser.d.ts"
}
This is however very impractical, and forces me to do a lots of repetition, as I don't have 2 different versions of the package, just some code that should throw in the browser when used.
Is there a way to make something like this work?
// create safe-fs.ts locally and use it instead of the real "fs" module
import * as fs from 'fs'
function createModuleProxy(moduleName: string): any {
return new Proxy(
{},
{
get(target, property) {
return () => {
throw new Error(`Function "${String(property)}" from module "${moduleName}" should only be used on node.js`)
}
},
},
)
}
const isNode = typeof window === undefined && typeof process === 'object'
const safeFs: typeof fs = isNode ? fs : createModuleProxy('fs')
export default safeFs
As it stands, this trips up bundlers, as I'm still importing fs.
Running a script via NodeJS which imports from a library is failing because the library references the window variable. Even though in the library, it looks like this:
{
baseUrl: (typeof window !== undefined ? window.location.origin : '') + "/blah",
}
This line still results in the following error when running the script via NodeJS:
ReferenceError: window is not defined
I thought it was enough to add the typeof window !== undefined check but clearly that is not the case. What am I missing here? (FWIW the imported module is a UMD module.)
typeof gives you a string so you need to be comparing to "undefined", not undefined. Change your code to this:
{
baseUrl: (typeof window !== "undefined" ? window.location.origin : '') + "/blah",
}
I'm currently learning nodeJS and running version v6.10.2, I was taking a look at the fs.js module source code located in the graceful-js folder and noticed this code in it:
'use strict'
var fs = require('fs')
module.exports = clone(fs)
function clone (obj) {
if (obj === null || typeof obj !== 'object')
return obj
if (obj instanceof Object)
var copy = { __proto__: obj.__proto__ }
else
var copy = Object.create(null)
Object.getOwnPropertyNames(obj).forEach(function (key) {
Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key))
})
return copy
}
How can a module require itself does it have to do with the clone keyword? I couldn't find any other module named fs.js either.
You're looking at part of the code for the graceful-fs package which is a wrapper around the builtin fs module in NodeJS. So this code is not requiring itself - the require statement imports the builtin fs module and the rest of the code clones it so that other parts of the graceful-fs package can override some of the default fs functionality without affecting the original module.
I'm having trouble rewriting this to work in 'strict' mode. Since 'this' is not defined explicitly I'm getting jshint errors on compile. I'm thinking my brain is just not thinking abstractly enough to find a creative solution. Any help would be appreciated. Code adapted from the Universal Module Definition Github repo: https://github.com/umdjs/umd/blob/master/returnExports.js
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD Module
define([], factory);
} else if (typeof module === 'object' && module.exports) {
// Node Module
module.exports = factory();
} else {
// Browser Global
root.returnExports = factory();
}
}(this, function () {
return {};
}));
Looking at your code, I see that root is only actually used in the case that you are in a browser, which simplifies things.
That means that we can replace this with the following expression:
typeof window !== "undefined" ? window : undefined
This is valid in strict mode (I tried it in Node, it returns undefined, no errors), and JSHint.com allowed it.
If you need the global object in other cases as well, you can chain the ternary expressions.
How can I detect whether my Node.JS file was called using SH:node path-to-file or JS:require('path-to-file')?
This is the Node.JS equivalent to my previous question in Perl: How can I run my Perl script only if it wasn't loaded with require?
if (require.main === module) {
console.log('called directly');
} else {
console.log('required as a module');
}
See documentation for this here: https://nodejs.org/docs/latest/api/modules.html#modules_accessing_the_main_module
There is another, slightly shorter way (not outlined in the mentioned docs).
var runningAsScript = !module.parent;
I outlined more details about how this all works under the hood in this blog post.
For those using ES Modules (and Node 10.12+), you can use import.meta.url:
import path from 'path';
import { fileURLToPath } from 'url'
const nodePath = path.resolve(process.argv[1]);
const modulePath = path.resolve(fileURLToPath(import.meta.url))
const isRunningDirectlyViaCLI = nodePath === modulePath
Things like require.main, module.parent and __dirname/__filename aren’t available in ESM.
Note: If using ESLint it may choke on this syntax, in which case you’ll need to update to ESLint ^7.2.0 and turn your ecmaVersion up to 11 (2020).
More info: process.argv, import.meta.url
I was a little confused by the terminology used in the explanation(s). So I had to do a couple quick tests.
I found that these produce the same results:
var isCLI = !module.parent;
var isCLI = require.main === module;
And for the other confused people (and to answer the question directly):
var isCLI = require.main === module;
var wasRequired = !isCLI;
Try this if you are using ES6 modules:
if (process.mainModule.filename === __filename) {
console.log('running as main module')
}
I always find myself trying to recall how to write this goddamn code snippet, so I decided to create a simple module for it. It took me a bit to make it work since accessing caller's module info is not straightforward, but it was fun to see how it could be done.
So the idea is to call a module and ask it if the caller module is the main one. We have to figure out the module of the caller function. My first approach was a variation of the accepted answer:
module.exports = function () {
return require.main === module.parent;
};
But that is not guaranteed to work. module.parent points to the module which loaded us into memory, not the one calling us. If it is the caller module that loaded this helper module into memory, we're good. But if it isn't, it won't work. So we need to try something else. My solution was to generate a stack trace and get the caller's module name from there:
module.exports = function () {
// generate a stack trace
const stack = (new Error()).stack;
// the third line refers to our caller
const stackLine = stack.split("\n")[2];
// extract the module name from that line
const callerModuleName = /\((.*):\d+:\d+\)$/.exec(stackLine)[1];
return require.main.filename === callerModuleName;
};
Save this as is-main-module.js and now you can do:
const isMainModule = require("./is-main-module");
if (isMainModule()) {
console.info("called directly");
} else {
console.info("required as a module");
}
Which is easier to remember.
First, let's define the problem better. My assumption is that what you are really looking for is whether your script owns process.argv (i.e. whether your script is responsible for processing process.argv). With this assumption in mind, the code and tests below are accurate.
module.parent works excellently, but it is deprecated for good reasons (a module might have multiple parents, in which case module.parent only represents the first parent), so use the following future-proof condition to cover all cases:
if (
typeof process === 'object' && process && process.argv
&& (
(
typeof module === 'object' && module
&& (
!module.parent
|| require.main === module
|| (process.mainModule && process.mainModule.filename === __filename)
|| (__filename === "[stdin]" && __dirname === ".")
)
)
|| (
typeof document === "object"
&& (function() {
var scripts = document.getElementsByTagName("script");
try { // in case we are in a special environment without path
var normalize = require("path").normalize;
for (var i=0,len=scripts.length|0; i < len; i=i+1|0)
if (normalize(scripts[i].src.replace(/^file:/i,"")) === __filename)
return true;
} catch(e) {}
})()
)
)
) {
// this module is top-level and invoked directly by the CLI
console.log("Invoked from CLI");
} else {
console.log("Not invoked from CLI");
}
It works correctly in all of the scripts in all of the following cases and never throws any errors†:
Requiring the script (e.x. require('./main.js'))
Directly invoking the script (e.x. nodejs cli.js)
Preloading another script (e.x. nodejs -r main.js cli.js)
Piping into node CLI (e.x. cat cli.js | nodejs)
Piping with preloading (e.x. cat cli.js | nodejs -r main.js)
In workers (e.x. new Worker('./worker.js'))
In evaled workers (e.x. new Worker('if (<test for CLI>) ...', {eval: true}))
Inside ES6 modules (e.x. nodejs --experimental-modules cli-es6.js)
Modules with preload (e.x. nodejs --experimental-modules -r main-es6.js cli-es6.js)
Piped ES6 modules (e.x. cat cli-es6.js | nodejs --experimental-modules)
Pipe+preload module (e.x. cat cli-es6.js | nodejs --experimental-modules -r main-es6.js)
In the browser (in which case, CLI is false because there is no process.argv)
In mixed browser+server environments (e.x. ElectronJS, in which case both inline scripts and all modules loaded via <script> tags are considered CLI)
The only case where is does not work is when you preload the top-level script (e.x. nodejs -r cli.js cli.js). This problem cannot be solved by piping (e.x. cat cli.js | nodejs -r cli.js) because that executes the script twice (once as a required module and once as top-level). I do not believe there is any possible fix for this because there is no way to know what the main script will be from inside a preloaded script.
† Theoretically, errors might be thrown from inside of a getter for an object (e.x. if someone were crazy enough to do Object.defineProperty(globalThis, "process", { get(){throw 0} });), however this will never happen under default circumstances for the properties used in the code snippet in any environment.
How can I detect whether my node.js file was called directly from console (windows and unix systems) or loaded using the ESM module import ( import {foo} from 'bar.js')
Such functionality is not exposed. For the moment you should separate your cli and library logic into separate files.
Answer from node.js core contributor devsnek replying to nodejs/help/issues/2420
It's the right answer in my point of view