Unable to use requireJS and Node's Require in the same TypeScript project - node.js

I have a typescript project targeted at both Node and the browser. I'm using Node's require() in some scripts and requireJS's require() in others. My project directory looks like this:
myProject
\-- a.ts
\-- b.ts
\-- node.d.ts
\-- require.d.ts
where a.ts contains:
/// <reference path="./node.d.ts" />
var cp = require('child-process');
var a = 'hello world'
export = a
and b.ts contains:
/// <reference path="./require.d.ts" />
require('hello',function(x){console.log('world')});
var b = 'hello world'
export = b
and where require.d.ts and node.d.ts are obtained from DefinitlyTyped.
When I compile my project, I get these errors:
b.ts(2,1): error TS2346: Supplied parameters do not match any signature of call target.
require.d.ts(396,13): error TS2403: Subsequent variable declarations must have the same type. Variable 'require' must be of type 'NodeRequire', but here has type 'Require'.
I use this idiom to determine which modules to load, so I'm not loading a node module in the browser or vice versa.
if(typeof module !== 'undefined' && module.exports){
// We're in a Node process
}else{
// We're in an AMD module in the browser:
}
Is there a way to use both of these .d.ts files in the same project. It seems using them in separate modules is not enough.

Is there a way to use both of these .d.ts files in the same project
I highly recommend going with commonjs everywhere. That is what the React community has spearheaded and it's a much simpler workflow. Just use CommonJS + webpack (to get lazy require.ensure from here). 🌹
There's also a quickstart for TypeScript in the browser environment.

In the end what I needed was the ability to require() JS content that is compiled on the fly by the server -- which doesn't seem to work with web-pack.
To suppress the errors from the typescript compiler in the original question, I commented out this line from require.d.ts (the RequireJS declaration file):
declare var require: Require;
and I used the {foo:bar}['f'+'oo'] trick to get tsc to 'forget' the type of the ambient require variable when assigning it to the typed requirejs variable, like so:
var requirejs:Require; // requirejs
if(typeof module !== 'undefined' && module.exports){
// We're in a Node process
requirejs = require('requirejs');
}else{
// We're in an AMD module in the browser:
requirejs = {require:require}['req'+'ire'];
}
// use requirejs for dynamic require statements
requirejs(gather_requirements(),do_things);

Related

how to use node module with es6 import syntax in typescript

I have a typescript project which has uses one of our node modules which normally runs in our front-end. We are now looking to use this module in node on our server.
The module uses es6 import syntax import { props } from 'module/file'
When I include a ref in typescript using either of the following methods
import { props } from 'module/file';
var props = require('module/file');
I get the following error from typescript
unexpected token 'import'
(function (exports, require, module, __filename, __dirname) { import
It's a big job to re-write the module, and I've tried using babel with babel-plugin-dynamic-import-node, as well as SystemJS.
The problem with these systems is that they are all asynchronous, so I can't import the module in the standard fashion, so I would need to do a whole bunch of re-write when we get to the point that I can use import natively in node.js.
I can't be the first person to have this issue, but I can't seem to find a working solution.
--------------- update with set-up -------------
In response to #DanielKhoroshko's response. The original module I am trying to import is normally packaged by webpack in order to use on the front-end. I am now trying to use this same module both server-side and in the front-end (via webpack on the front-end) without re-writing the imports to use require and without running webpack to bundle the js to use on the server.
To be clear, the original module is written in JS, our service which is trying to use this module is written in typescript and transpiled. When the typescript tries to require the old module which uses import, it is at this point that we are running into the issue.
------------------ some progress ---------------------------
I've made some progress by creating a file in my imported module which uses babel in node.js to transpile the es6 code into commonJS modules.
I've done this via
var babel = require("babel-core")
var store = babel.transformFileSync(__dirname + '/store.js', {
plugins: ["transform-es2015-modules-commonjs"]
});
module.exports = {
store: store.code
}
I can now get the store in my new node.js project. However, the submodules within the store.js file are not included in the export.
So where in my module, it says
import activities from './reducers/activities';
I now get an error
Cannot find module './reducers/activities'
How can I get babel to do a deep traversal to include the sub-directories?
unexpected token 'import' means you are running es-modules code in environment that doesn't support import/export commands. If you are writing you code in TypeScript it's important to transpile it first before building for the browser or use ts-node to run it server-side.
If you are using webpack there are loaders ts-loader and awesome-typescript-loader
What is your setup?
To describe the module you would need to create an activities.d.ts file in the same folder where the js-version (I understood it is called activities.js and containers a reducer) resides with the following (approx.):
import { Reducer } from 'redux';
export const activities: Reducer<any>;
#Daniel Khoroshko was right in many ways, I ended up finding #std/esm which lets you import es6 modules and worked find for fetching the included imports as well.
var babel = require('babel-register')({
presets: ["env"]
});
require = require('#std/esm')(module);
var store = require('ayvri-viewer/src/store');
exports.default = {
store: store
}
I had to run babel to get a consistent build from es6 to node compatible es5

How does react avoid using require("../../Path/To/Module"), and just use require("Module")

As far as I've seen, npm modules can be require() without a path:
require("module") // --> npm module
and local modules are require() using a path:
require("./module") // --> local module, in this directory
require("../../path/to/module") // path to directory
In react.js, modules are required without a path. See here for example. I'm wondering how they achieve this.
Apparently it uses rewrite-modules Babel plugin with module-map module (see gulpfile.js.)
There's also this Babel plugin that you can use to achieve the same behavior.
If you're using Webpack, you can add path/to/modules into resolve.modulesDirectories array and it will work similarly to requiring from node_modules instead of using relative paths.
resolve: {
modulesDirectories: ['path/to/modules', 'node_modules'],
},
and then
var foo = require('foo');
// Instead of:
// var foo = require('/path/to/modules/foo');
// or
// var foo = require('../../foo');

Can't get "crossroads" to load as requirejs AMD module in typescript

I am trying to port a javascript app to typescript that uses requirejs. I am using the typedefinition from #mhegazy at DefinitelyTyped: https://github.com/borisyankov/DefinitelyTyped/tree/master/crossroads
Typescript compiler is giving me the following error...
error TS2307: Cannot find external module 'crossroads'.
This did all work in javascript using "defines" instead.
I just used this library as a complete example. What am I missing? Documentation is hard to dig up if TSC is really even paying attention to a requirejs config. I know crossroads depends on js-signals.
Is there a problem with definitions and ts files being in different directories?
From what I have read here I should not have to even put the reference comment. But if I then try to use a relative path, it cannot find it either.
Here is my app structure
index.html
Application ts/js
app/config.ts
app/bootup.ts
app/someclass.ts
Typescript definitions
typings/crossroads/crossroads.d.ts
typings/requirejs/require.d.ts
Javascript libraries
bower_components/crossroads/dist/crossroads.js
bower_components/requirejs/require.js
index.html has this script include
<script data-main="app/config" src="bower_components/requirejs/require.js>
</script>
/app/config.ts:
/// <reference path="../typings/requirejs/require.d.ts" />
(function () {
requirejs.config({
baseUrl: ".",
paths: {
"crossroads": "bower_components/crossroads/dist/crossroads"
}
})
require(["app/bootup"]);
}) ();
/app/someclass.ts:
/// <reference path="../typings/crossroads/crossroads.d.ts" />
import crossroads = require("crossroads");
class SomeClass{
// do something with crossroads
}
import crossroads = require("crossroads");
To use import/require you either need:
relative path pointing to a TypeScript file.
a declared external module.
In your case you need to declare an external module for TypeScript to know about it. E.g.:
declare module 'crossroads'{
export = crossroads;
}
UPDATE I've updated the definitions so you don't need to do this : https://github.com/borisyankov/DefinitelyTyped/pull/3866 Enjoy!

How to use node module in client code?

I've installed ReactJs module in my nodejs application, so React appeared in node_modules directory. But I can't use it in client javascript because there is no reference.
So what is the best way to add such a reference ?
(Of course I can manually copy react.js script to scripts folder and add reference manually but it leads to code duplication and looks ugly)
Browserify is the tool you are looking for. It takes Node modules and wraps them so you can require() them in client side JavaScript code.
Example from Browserify's readme
Whip up a file, main.js with some require()s in it. You can use
relative paths like './foo.js' and '../lib/bar.js' or module paths
like 'gamma' that will search node_modules/ using node's module
lookup algorithm.
var foo = require('./foo.js');
var bar = require('../lib/bar.js');
var gamma = require('gamma');
var elem = document.getElementById('result');
var x = foo(100) + bar('baz');
elem.textContent = gamma(x);
Export functionality by assigning onto module.exports or exports:
module.exports = function (n) { return n * 111 }
Now just use the browserify command to build a bundle starting at
main.js:
$ browserify main.js > bundle.js
All of the modules that main.js needs are included in the
bundle.js from a recursive walk of the require() graph using
required.
To use this bundle, just toss a <script src="bundle.js"></script>
into your html!

Typescript AMD external module loading, angular is not defined

I know that there are some similar questions on this topic already, but somehow none of them helped.
I want to use angular.js in a project which uses requirejs and Typescript.
I have a module, where I use angular to create a service module:
/// <reference path="../../../typings/angularjs/angular.d.ts" />
...
var services = angular.module('services', []);
...
export = services;
This code compiles withouth error, but in the created js file, there is angular.js dependency is not there:
define(["require", "exports"], function(require, exports) {
...
}
And when I run the application, the browser complains: Uncaught ReferenceError: angular is not defined
My guess is, that somehow I should import the angular besides referencing it, but none of the path's I tried works.
Here is my require.js configuration, in case it is needed:
require.config({
paths: {
angular: '../lib/angular/angular',
...
},
shim: {
angular : {exports : 'angular'},
...
}
)}
Can u help me, what is missing here?
Thanks.
When you use a reference comment:
/// <reference path="../../../typings/angularjs/angular.d.ts" />
You are telling the compiler "I will ensure this dependency is available at runtime".
If you want the module to be loaded, you need to use an import instead...
import angular = require('angular');
The zero-set-up way of doing this is to place the angular.d.ts file next to the actual angular.js file, so the path you specify in the import statement is the same either way - otherwise, use your require config to shimmy the path.
Have you tried adding the following to the top of your file
/// <amd-dependency path="angular"/>
This should make ts compiler add angular to the define list.

Resources