I'm having another issue with node.js, this time I cannot get my javascript code to recognize that a coffeescript module's class has functions.
In my main file, main.js I have the following code:
require('./node_modules/coffee-script/lib/coffee-script/coffee-script');
var Utils = require('./test');
console.log(typeof Utils);
console.log(Utils);
console.log(typeof Utils.myFunction);
And in my module, test.coffe, I have the following code:
class MyModule
myFunction : () ->
console.log("debugging hello world!")
module.exports = MyModule
Here is the output when I run node main.js :
function
[Function: MyModule]
undefined
My question is, why is my main file loading the correct module, but why is it unable to access the function? What am I doing wrong, whether it be with the coffeescript syntax, or with how I am requiring my module? Let me know if I should clarify my question.
Thanks,
Vineet
myFunction is an instance method, so it won't be accessible directly from the class.
If you want it as a class (or static) method, prefix the name with # to refer to the class:
class MyModule
#myFunction : () ->
# ...
You can also export an Object if the intention is for all methods to be static:
module.exports =
myFunction: () ->
# ...
Otherwise, you'll need to create an instance, either in main:
var utils = new Utils();
console.log(typeof utils.myFunction);
Or, as the export object:
module.exports = new Utils
Related
reproduce link https://stackblitz.com/edit/node-sp5xay?file=index.mjs
Assume we have a project like this:
.
├── dep
│ ├── a.mjs
│ ├── b.js
│ └── c.js
└── entry.mjs
// entry.mjs
import { foo } from "./dep/a.mjs";
console.log(foo);
// dep/a.mjs
export * from './b.js'
// dep/b.js
module.exports = require("./c.js"); // 💯
// why this not working ❌
// const m = require("./c.js");
// module.exports = m;
// dep/c.js
exports.foo = "foo";
and we run in terminal
node entry.mjs
it's very confuse it will throw error if in dep/b.js we use:
// why this not working ❌
const m = require("./c.js");
module.exports = m;
if in dep/b.js we use:
module.exports = require("./c.js");
it will work expect!
module.exports=require have something magic? like symlink? if any docs i miss?
The origin of this problem is that I saw the vue3 source code
vue3 core source code export
module.exports=require have something magic?
Yes, it does have some magic. The problem is that you are importing a CommonJS module into an ES module. The latter require a static declaration of exported names, which the former do not provide. See the node.js documentation:
When importing CommonJS modules, the module.exports object is provided as the default export.
So just don't do export * from './b.js', rather import b from './b.js' then refer to b.foo on the CommonJS module object.
However,
For better compatibility with existing usage in the JS ecosystem, Node.js in addition attempts to determine the CommonJS named exports of every imported CommonJS module to provide them as separate ES module exports using a static analysis process.
[…]
The detection of named exports is based on common syntax patterns but does not always correctly detect named exports. In these cases, using the default import form described above can be a better option.
Named exports detection covers many common export patterns, reexport patterns and build tool and transpiler outputs. See cjs-module-lexer for the exact semantics implemented.
(emphasis mine)
And indeed,
module.exports = require("./c.js");
is one of the "reexport patterns" that is detected, but using a temporary variable
const m = require("./c.js");
module.exports = m;
is not. You just can't use named imports from a CommonJS module that does this. The proper solution to fix this is of course to rewrite the module to ESM syntax and use export * from "./c.js";, not any module.exports assignments.
In Node.js, the module.exports object is used to specify what a module should make available when it is imported using require.
The first statement , module.exports = require('module'), sets the module.exports object to the value of the module object that is imported using require. This means that anything that is exported by the module module will be made available to the code that imports it.
On the other hand, module.exports = {...} sets the module.exports object to an object literal containing the properties and values that should be made available to code that imports the module. This allows you to specify exactly what should be made available from the module, rather than relying on the exports of another module.
For example, suppose you have a module called myModule that exports a single function:
module.exports = {
myFunction: () => {
console.log('Hello, world!');
}
};
This module can then be imported and used like this:
const myModule = require('myModule');
myModule.myFunction(); // prints 'Hello, world!'
On the other hand, if you set module.exports to the value of another module's exports, like this:
module.exports = require('anotherModule');
Then the exports of anotherModule will be made available to code that imports myModule. For example:
const myModule = require('myModule');
myModule.someFunction(); // calls a function exported by anotherModule
file structure is
-src
--Visitor
---visitor.model.js
---Sessions
----session.model.js
In visitor.model.js file
const {Sessions} = require('./Sessions/session.model');
const Visitor = {};
Visitor.visitorFunc = () => {
}
Sessions.sessionFunc();
module.exports = {Visitor: Visitor};
In session.model.js file
const {Visitor} = require('../visitor.model.js');
const Session = {};
Sessions.sessionFunc = () => {
}
Visitor.visitorFunc();
module.exports = {Session: Session};
when I do imports like this in Visitor file Session is undefined. What is the reason for that.. Is it calling import recursively ?
Circular dependencies are allowed in node
https://nodejs.org/api/modules.html#modules_cycles
When main.js loads a.js, then a.js in turn loads b.js. At that point, b.js tries to load a.js. In order to prevent an infinite loop, an unfinished copy of the a.js exports object is returned to the b.js module. b.js then finishes loading, and its exports object is provided to the a.js module.
Since Session and Visitor sounds like database models with an M:N relationship circular dependencies are the way to go (e.g.: Join query)
How to deal with cyclic dependencies in Node.js
Node.js Module.Exports Undefined Empty Object
But it would be less messy to avoid them if you can.
As #prashand above has given the reasons you would have to do imports and calling imported functions after exporting current module.. above example is working with a slight change as follows
const Visitor = {};
Visitor.visitorFunc = () => {
console.log('hello from visitor model');
}
module.exports = {Visitor: Visitor};
// import session.model after exporting the current module
const {Session} = require('./Sessions/session.model');
// then call the required function
Session.sessionFunc();
Simply just use exports.someMember = someMember instead of module.exports = { someMember }.
Your visitor.model.js file is outside the Sessions directory. In order to import session.model.js you need to give absolute path to that file. So your require statement should be like this
const { Sessions } = require('../Sessions/session.model.js');
My problem is the following:
I use gulp+browserify to compile my TypeScript to JavaScript that you can use on normal HTML pages, the problem is that my class is never available on the browser:
VM633:1 Uncaught ReferenceError: Test is not defined
at <anonymous>:1:13
This is my TypeScript File:
class Test {
public test(): void {
console.log("aa");
}
}
This is my gulpfile
var gulp = require("gulp");
var browserify = require("browserify");
var source = require('vinyl-source-stream');
var tsify = require("tsify");
gulp.task("default", function () {
return browserify({
//basedir: '.',
debug: true,
entries: ['app/Resources/typescript/Test.ts'],
cache: {},
packageCache: {}
})
.plugin(tsify)
.bundle()
.pipe(source('bundle.js'))
.pipe(gulp.dest("web/bundles/framework/js"));
});
The file compiles without problem, and is included in my index.html (the compiled js file).
But when i try:
var t = new Test();
I get the following error:
VM633:1 Uncaught ReferenceError: Test is not defined
at <anonymous>:1:13
I can't resolve it, I have read a lot and I haven't found anything clear, I tried all and nothing worked.
There are a few things missing here:
If you want your class to be accessible outside of your module, you have to export it:
export class Test {
// ...
}
Browserify creates functions on top of the classes you define. So it won't be accessible globally (which is a good thing). Normally you would import it in another file and use it:
// in any TS file that wants to use `Test` class. Make sure this is included in the gulp entries as well
import {Test} from "test";
var t = new Test();
console.log(t);
Or if really want it to be accessible globally, you can attach it to window object:
// In Test.ts file:
(window as any).Test = Test; // This can be useful when debuging. But don't do this in production code.
So, the general answer for the other question (How do you import a module using typescript) is:
1) Create a blah.d.ts definition file.
2) Use:
/// <reference path="./defs/foo/foo.d.ts"/>
import foo = require("foo");
Critically, you need both the files foo.d.ts and a foo.js somewhere in your node_modules to load; and the NAME foo must exactly match for both. Now...
The question I would like to have answered is how to you write a typescript module that you can import that way?
Lets say I have a module like this:
- xq/
- xq/defs/Q.d.ts
- xq/index.ts
- xq/base.ts
- xq/thing.ts
I want to export the module 'xq' with the classes 'Base' from base.ts, and 'Thing' from thing.ts.
If this was a node module in javascript, my index.ts would look something like:
var base = require('./base');
var thing = require('./thing');
module.exports = {
Base: base.Base,
Thing: thing.Thing
};
Let's try using a similar typescript file:
import base = require('./base');
export module xq {
export var base = base.Base;
}
Invoke it:
tsc base.ts index.ts things.ts ... --sourcemap --declaration --target ES3
--module commonjs --outDir dist/xq
What happens? Well, we get our base.d.ts:
export declare class Base<T> {
...
}
and the thrillingly unuseful index.d.ts:
export declare module xq {
var Base: any; // No type hinting! Great. :(
}
and completely invalid javascript that doesn't event import the module:
(function (xq) {
xq.base = xq.base.Base;
})(exports.xq || (exports.xq = {}));
var xq = exports.xq;
I've tried a pile of variations on the theme and the only thing I can get to work is:
declare var require;
var base = require('./base');
export module xq {
export var base = base.Base;
}
...but that obviously completely destroys the type checker.
So.
Typescript is great, but this module stuff completely sucks.
1) Is it possible to do with the built in definition generator (I'm dubious)
2) How do you do it by hand? I've seen import statements in .d.ts files, which I presume means someone has figured out how to do this; how do those work? How do you do the typescript for a module that has a declaration with an import statement in it?
(eg. I suspect the correct way to do a module declaration is:
/// <reference path="base.d.ts" />
declare module "xq" {
import base = require('./base');
module xq {
// Some how export the symbol base.Base as Base here
}
export = xq;
}
...but I have no idea what the typescript to go along that would be).
For JavaScript :
var base = require('./base');
var thing = require('./thing');
module.exports = {
Base: base.Base,
Thing: thing.Thing
};
TypeScript :
import base = require('./base');
import thing = require('./thing');
var toExport = {
Base: base.Base,
Thing: thing.Thing
};
export = toExport;
Or even this typescript:
import base = require('./base');
import thing = require('./thing');
export var Base = base.Base;
export var Thing = thing.Thin;
Typescript has really improved since this question was asked. In recent versions of Typescript, the language has become a much more strict superset of Javascript.
The right way to import/export modules is now the new ES6 Module syntax:
myLib.ts
export function myFunc() {
return 'test'
}
package.json
{
"name": "myLib",
"main": "myLib.js",
"typings": "myLib.d.ts"
}
Dependents can then import your module using the new ES6 syntax:
dependent.ts
import { myFunc } from 'myLib'
console.log(myFunc())
// => 'test'
For a full example of a node module written in Typescript, please check out this boilerplate:
https://github.com/bitjson/typescript-starter/
If I have this ts module:
export function say(){
console.log("said");
}
and I compile it with the amd option I can use it quite easily from a ts client :
import foo = module("tsmodule")
foo.say();
export var x = 123;
However if I have javascript equivalent to the ts module:
define(["require", "exports"], function(require, exports) {
function say() {
console.log("said");
}
exports.say = say;
})
There is no way to use it easily. The simplest possible solution:
// of course you can use .d.ts for requirejs but that is beside the point
declare var require:any;
// will fail with error module has not been loaded yet for context
// http://requirejs.org/docs/errors.html#notloaded
var useme = require("jsmodule")
useme.say();
export var x = 123;
import foo = module("tsmodule")
foo.say();
fails because of error http://requirejs.org/docs/errors.html#notloaded . Since "jsmodule" was not passed to the define call in the generated typescript.
The two workarounds I have
don't use import / export (language features lost)
use require([]) (still can't export something that depends on the require([]) call)
have limitations : https://github.com/basarat/typescript-requirejs . Is there another way? If not can you vote here : https://typescript.codeplex.com/workitem/948 :)
If you want to load in a JavaScript module you could always use the (badly documented) amd-dependency tag:
/// <amd-dependency path="jsmodule" />
This will put jsmodule in the dependency array of your define call.
And then provide a declaration file in which you would simply state
module useme {
function say(): void;
}