How can I run a Typescript script on Node without importing anything from it? - node.js

Typescript handbook mentions scripts, as opposed to modules:
Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well).
I have a couple of scripts that don't import anything, just iteratively do some work, like this:
// script.ts
console.log('test')
I want to run them all one by one from the index.ts (which is defined as main in package.json). However, when I just import them:
// index.ts
console.log(1)
import {} from './script'
console.log(2)
It does not do anything in the compiled JS:
// index.js (compiled)
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
console.log(1);
// << shouldn't there be something here?
console.log(2);
//# sourceMappingURL=index.js.map
How can I properly call that script so it root content runs when I run my compiled index.js?

Scripts are imported like this:
import './script'
without using from

Related

NodeJS How to import JS file into TypeScript

I'm new with TypeScript. I'm currently learning NodeJS Loopback 4 framework which use Typescript language. And my question is how to import some function, class which has been exported in JS file into my TS file. After search several way but it still doesn't work with me.
Here's example:
// /src/index.ts
import {plus} from './lib/test';
console.log(plus(1,2));
// /src/lib/test.js
export function plus(x, y) {
return x + y;
}
I also try using definition typescript like this
// /src/lib/test.d.ts
export declare function plus(x: number, y: number): number;
But still got error when import this function in index.ts file
Error: Cannot find module './lib/test'
at Function.Module._resolveFilename (module.js:543:15)
It looks like the tsconfig.json doesn't have 'allowJs' enabled because it exports declarations.
Is there a reason you are not wanting this to be a typescript file? If you change test.js to test.ts, by making it a .ts should allow it to be recognised in your index file.
UPDATE
Full chat history to get to this point can be found here.
Repository used to test found here
OK so easy solution as #maaz-syed-adeeb mentions will work:
import { path } from './lib/test.js'
The reason the extension is important is because the definition file takes priority over a javascript file in a typescript environment. That is why the module import was blowing up.
To avoid specifying the .js extension you can also setup your directory structure like this:
src
|- index.ts
|- lib
|- test.js
|- test.d.ts
|- index.[js|ts]
in ./lib/index file export all from test:
//./src/lib/index.[js|ts]
export * from './test'
and then import all from lib:
// ./src/index.ts
import { path } from './lib'
// or
import { path } from './lib/test.js'
If you are using a blend of javascript and typescript (say you are moving over to typescript with an existing code base), you will need to update your tsconfig.json to include so you don't get the warnings in your IDE:
{
"compilerOptions": {
"allowJs": true,
"declaration": false
}
}
This is so your javascript files will be transpiled to your destination directory along with the typescript files.

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

Commander-plus with gulp

I'm writing a node cli tool with commander-plus.
import program from 'commander-plus';
const prompts = ['a', 'b', 'c'];
program.choose(prompts, (index) => {
// never returns;
});
And want to run it with a gulp task, mostly because its convenient and we're loading .env variables, but on development only.
import env from 'gulp-env';
gulp.task('env', () => {
env();
});
At first i've been trying with gulp-shell. I'm actually using a similar script to kick-off nodemon, which works fine. The cli script runs just fine but commander-plus won't listen to the keyboard input.
import shell from 'gulp-shell';
import gulp from 'gulp';
gulp.task('cli', ['env'], shell.task([
'babel-node src/cli',
]))
Later i found that, either this is how its supposed to work or perhaps its now fixed.
https://github.com/sun-zheng-an/gulp-shell/issues/10
But also that gulp-shell is blacklisted, and thought to try with gulp-exec or child_process.exec instead.
import { exec } from 'child_process';
gulp.task('cli', ['env'], done => {
exec('babel-node src/server/cli', done);
});
Not sure if it qualifies as an answer, but i found way around with node-dotenv. Just without gulp.
First i have a config file that looks like
// only set default for env
configExport.env = process.env.NODE_ENV || 'development';
// and a lot of other variables
And then both in my server and cli tool i load the .env only if its needed.
import dotenv from 'dotenv'
import config from '../server/config';
if(config.env === 'development'){
dotenv.load();
}
dotenv will not fail if .env file is not found, but the reason for the extra if check, is i'm worried a .env might get deployed by accident.
We will also need to make sure .env is not deployed with all appropriate .ignores (.gitignore, modulusignore, .dockerignore) and that should do the job.
Apparently gulp-bg is a working option. With that we can still continue running developer tasks with gulp and avoid dotenv entirely on production.
import bg from 'gulp-bg';
import gulp from 'gulp';
gulp.task('cli', ['env'], bg('node', './src/cli'));

Unable to use requireJS and Node's Require in the same TypeScript project

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);

TypeScript won't resolve module when import is used

// Modules/MyModule.ts --------------------------------
import fs = require("fs");
module Hello {
export function World(): string {
return "Hello World";
}
}
// Main/App.ts ----------------------------------------
console.log(Hello.World()); // Cannot find name 'Hello'
For some reason this produces the error specified above. The weird thing is if I uncomment the import statement I don't get this error anymore. (it's not used anyway)
Both produce the same error:
tsc Main/App.ts --module "commonjs"
tsc Main/App.ts --module "amd"
Is this really a compiler bug or am I missing something. Do I need to specify external module requires somehow different?
This one comes up quite a lot - the key to joy and happiness in TypeScript is to choose either internal modules or external modules and not both.
I have written more extensively about choosing between internal and external modules in TypeScript. The bottom line is choose only one.
In your case, you need to fully commit to external modules. Here is an updated example:
// Modules/Hello.ts --------------------------------
import fs = require("fs");
export function World(): string {
return "Hello World"
}
The module name for the above file is Hello, because it is in a file named Hello.ts.
You can then use it like this...
// Main/App.ts ----------------------------------------
import Hello = require("./Modules/Hello");
console.log(Hello.World());
p.s. Node apps are compiled using commonjs mode - but this works for AMD too.

Resources