start typescript tsc compiler from node - node.js

From the command line I can start the tsc compiler like this:
../../node_modules/.bin/tsc
I want to incorporate this into a node build script.
There is a typescript compiler for node but it seems much more work to set up rather than just shelling out. You have to pull in all the right files etc.
I have this code :
fs.emptyDirSync(paths.appBuild);
const json = ts.parseConfigFileTextToJson(tsconfig, ts.sys.readFile(tsconfig), true);
const { options } = ts.parseJsonConfigFileContent(json.config, ts.sys, path.dirname(tsconfig));
options.configFilePath = paths.tsConfig;
options.outDir = outDir;
options.src = src;
options.noEmitOnError = true;
options.pretty = true;
options.sourceMap = process.argv.includes('--source-map');
let rootFile = path.join(process.cwd(), 'src/index.tsx');
if (!fs.existsSync(rootFile)) {
rootFile = path.join(process.cwd(), 'src/index.ts');
}
const host = ts.createCompilerHost(options, true);
const prog = ts.createProgram([rootFile], options, host);
const result = prog.emit();
But This will miss files that are not imported in the RootFile.
How can I simply shell out to the tsc exe from node?

You could use child_process.exec:
const path = require('path');
const { exec } = require('child_process');
const tscPath = path.join(__dirname, '../../node_modules/.bin/tsc');
const tsc = exec(`${tscPath} ${process.argv.slice(2).join(' ')}`);
tsc.stdout.on('data', data => console.log(data));
tsc.stderr.on('data', data => console.error(data));
tsc.on('close', code => console.log(`tsc exited with code ${code}`));

Related

module.exports cannot find module

I wanted to make a help command for the bot which shows the prefix of the specific guild. Here is my help.js file :-
const Discord = require('discord.js');
module.exports = {
name: 'help',
description: 'Neptune Premium Commands',
execute(client, message, args){
const { guildPrefix } = require('../../main.js');
const embed = new Discord.MessageEmbed()
.setAuthor(`Prefix : ${guildPrefix}`, message.author.displayAvatarURL( {dynamic: true} ))
.setDescription(`Neptune Premium Commands List.`)
.addFields(
{name: `moderation`, value: '`kick` `ban` `lock` `unlock` `purge` `warn` `delwarn` `mute` `unmute`'},
{name: `utility`, value: '`prefix` `timedif` `greet` `userinfo` `serverinfo` `snipe`'},
{name: `misc`, value: '`help` `support` `vote` `invite`'}
)
.setFooter(message.guild.name, message.guild.iconURL( {dynamic: true} ))
message.channel.send(embed)
}
}
Once I use $help it shows Prefix as undefined
Here is my main.js file :-
const Discord = require('discord.js');
const client = new Discord.Client();
const fs = require('fs');
const config = require('./config.json');
const prefix = require('discord-prefix');
const defaultPrefix = config.prefix;
// .. ignoring some part of the code ...
client.on('message', message =>{
// prefix db part
if (!message.guild) return;
let guildPrefix = prefix.getPrefix(message.guild.id);
if (!guildPrefix) guildPrefix = defaultPrefix;
if(message.content === '<#!849854849351942144>'){
message.channel.send(`My Prefix is \`${guildPrefix}\`. Use \`${guildPrefix}help\` for my commands!`)
}
if(message.channel.type === 'dm') return;
// discord.js command handler
if(!message.content.startsWith(guildPrefix) || message.author.bot) return;
const args = message.content.slice(guildPrefix.length).split(/ +/);
const cmd = args.shift().toLowerCase();
const command = client.commands.get(cmd) || client.commands.find(command => command.aliases && command.aliases.includes(cmd));
// ...
I have ignored some part of the main code and only put the prefix part. I'm using a package called discord-prefix for this.
The reason why you're getting undefinded when requiring the prefix from your main.js is that you're never exporting a value.
If you'd want to get your prefix by using require you have to do this:
main.js
const serverPrefix = '!';
exports.prefix = serverPrefix;
// This would also work:
module.exports.prefix = serverPrefix;
help.js
const { prefix } = require('./main.js');
// Or:
const prefix = require('./main.js').prefix;
You can read more about exporting here
But you are using a npm package called discord-prefix and if you take a look at the examples you should notice that there are two interesting methods:
.setPrefix()
.getPrefix()
So if you want to get the prefix that you assigned in you main.js, in your help.js you have to use the .getPrefix() function. But before you can to this you have to set your prefix with .setPrefix() first:
main.js
const prefix = require('discord-prefix');
// This is optional, you could also use message.guild instead
const { guild } = message
if(!prefix.getPrefix(guild.id)) prefix.setPrefix('!', guild.id);
And after that you can get your prefix with the .getPrefix function:
help.js
const prefix = require('discord-require');
const { guild } = message;
const guildPrefix = prefix.getPrefix(guild.id);
Alternatively...
...you can use a .env file. This is much simpler (in my opinion) and I used it too, before moving all per-server-settings to a database. Therefore you have to install dotenv and create a file named .env
Now, if you want to set a prefix for your bot (not for specific servers) you want to set it like this:
Example
PREFIX = !
LOGINTOKEN = 1234567890
WELCOMECHANNEL = 3213213212321
// and so on...
Now that you have successfully created your .env file and defined some variables you have to require that new package in your main.js:
main.js
require ('dotenv').config()
Now you're ready to go and you can get your defined variables anywhere like this:
help.js
// You dont have to assign it to a variable
const prefix = process.env.PREFIX
// This schema will work for every variable you defined in .env:
process.env.LOGINTOKEN
process.env.WELCOMECHANNEL
// and so on...
Note
Please make sure you add the .env file to your .gitignore (if you're using git to store your code)

Bundling node binaries with Parcel V2 (modern-syslog)

I need to add a node binary from modern-syslog package to the bundle (I need to bundle node modules into a single file). With Parcel V1 I used to do it with this code:
const { Asset } = require('parcel-bundler');
const path = require('path');
const URL = require('url');
class NodeAsset extends Asset {
load() {}
generate() {
const pathToAsset = this.urlJoin(
this.options.publicURL,
this.generateBundleName()
);
return [
{
type: 'js',
value: `module.exports=eval('require(${JSON.stringify(`.${pathToAsset}`)})');`
}
];
}
urlJoin(publicURL, assetPath) {
const url = URL.parse(publicURL, false, true);
const assetUrl = URL.parse(assetPath);
url.pathname = path.posix.join(url.pathname, assetUrl.pathname);
url.search = assetUrl.search;
url.hash = assetUrl.hash;
return URL.format(url);
}
}
module.exports = NodeAsset;
I tried to migrate this transformer to Parcel V2. I was able to add .node binary as an asset to the bundle with isIsolated option, but I wasn't able to do the eval('require trick to make it work. Do you have any ideas how to approach this problem?

How to use WebWorker with ts-node? (without webpack)

Is there any way to use ts-node with WebWorkers but without using webpack?
When I do:
const worker = new Worker('path-to/workerFile.ts', { // ... });
I get:
TypeError [ERR_WORKER_UNSUPPORTED_EXTENSION]:
The worker script extension must be ".js" or ".mjs". Received ".ts" at new Worker (internal/worker.js:272:15)
// ....
Any ideas?
Tomer
You can make an function to make the magic, using eval property of WorkerOption parameter.
const workerTs = (file: string, wkOpts: WorkerOptions) => {
wkOpts.eval = true;
if (!wkOpts.workerData) {
wkOpts.workerData = {};
}
wkOpts.workerData.__filename = file;
return new Worker(`
const wk = require('worker_threads');
require('ts-node').register();
let file = wk.workerData.__filename;
delete wk.workerData.__filename;
require(file);
`,
wkOpts
);
}
so you can create the thread like this:
let wk = workerTs('./file.ts', {});
Hope it can help.

Check package version at runtime in nodejs?

I have some of my entries in package.json defined as "*"
"dependencies": {
"express": "4.*",
"passport": "*",
"body-parser": "*",
"express-error-handler": "*"
},
I wan't to freeze those values to the current version. How can I know what version my packages are at run time? I don't mind checking one by one since I don't have many of them :)
BTW: I cannot do npm list --depth=0 because I cannot access the vm directly (PaaS restriction), just the logs.
You can use the fs module to read the directories in the node_modules directory and then read package.json in each of them.
var fs = require('fs');
var dirs = fs.readdirSync('node_modules');
var data = {};
dirs.forEach(function(dir) {
try{
var file = 'node_modules/' + dir + '/package.json';
var json = require(file);
var name = json.name;
var version = json.version;
data[name] = version;
}catch(err){}
});
console.debug(data['express']); //= 4.11.2
Just in case if you need the version on the front-end, there is an npm package just for this and it can be used both on client-side and server-side.
global-package-version
You can use it in your code like this
import globalPackageVersion from 'global-package-version';
// package name is 'lodash'
globalPackageVersion(require('lodash/package.json'));
// You can type 'packageVersion' in browser console to check lodash version
// => packageVersion = { lodash: '4.7.2'}
packageVersion becomes a global object when used in server side and becomes a window object when used on the client side. Works well with webpack and all other bundling tools.
Disclaimer: I am the author of this package :)
I've 'modernised' a bit #laggingreflex answer, this works on ES6+, node 10, tested on a lambda running in aws. It's an endpoint from an express app.
const fs = require("fs");
module.exports.dependencies = async (_req, res) => {
const dirs = fs.readdirSync("./node_modules");
const modulesInfo = dirs.reduce((acc, dir) => {
try {
const file = `${dir}/package.json`;
const { name, version } = require(file);
return { ...acc, [name]: version };
} catch (err) {}
}, {});
res.status(200).json(modulesInfo);
};
The accepted solution can be improved upon in both terms of performance and stability:
1: the package name IS THE directory. In typically cases where you are looking for a specific package, you do not need to load every module.
2: this code will not run on all os due to the way the paths are formed
3: using require means the path needs to be relative to the current file (this would only work if your file is located at the top of your project folder & along side node_modules). In most cases, using readFile or readFileSync is a easier approach.
const fs = require('fs');
const path = require('path');
const dirs = fs.readdirSync('node_modules');
const data = {};
//add ones you care about
const trackedPackages = ['express', 'passport', 'body-parser'];
dirs.forEach(function(dir) {
if(trackedPackages.indexOf(dir) > -1){
try{
const json = JSON.parse(
fs.readFileSync(path.join('node_modules', dir, 'package.json'), 'utf8')
);
data[dir] = json.version;
}catch(e){
console.log(`failed to read/parse package.json for ${dir}`, e);
}
}
});
console.debug(data['express']); //= 4.11.2

piping to a spawn stdin

I would like to pipe some streaming data to feedgnuplot in order to graph it in real time
The following works:
// index.js
readableStream.pipe(process.stdout)
// in bash
$ node index.js | feedgnuplot --stream
But the following doesn't work (and this is what I want to make work):
// index.js
var feedgnuplot = require('child_process').spawn('feedgnuplot', ['--stream'])
readableStream.pipe(feedgnuplot.stdin)
//in bash
$ node index.js
I get an ECONNRESET error
EDIT: example of a readable stream as requested:
var util = require('util')
var Readable = require('stream').Readable
util.inherits(SomeReadableStream, Readable)
function SomeReadableStream () {
Readable.call(this)
}
SomeReadableStream.prototype._read = function () {
this.push(Math.random().toString()+'\n')
}
var someReadableStream = new SomeReadableStream()
someReadableStream.pipe(process.stdout)
For me, with node.js v0.10.26, your script works fine:
Script index.js:
var util = require('util')
var Readable = require('stream').Readable
util.inherits(SomeReadableStream, Readable)
function SomeReadableStream () {
Readable.call(this)
}
SomeReadableStream.prototype._read = function () {
this.push(Math.random().toString()+'\n')
}
var someReadableStream = new SomeReadableStream()
var feedgnuplot = require('child_process').spawn('feedgnuplot', ['--stream'])
someReadableStream.pipe(feedgnuplot.stdin)
And call if from a terminal as nodejs index.js.

Resources