How can an extension expand configuration variables in VSCode? - javascript

I tried to write an extension for vscode, the extension needs to read pasteImage.parth variable from ./vscode/settings.json file
{
"pasteImage.path": "${workspaceRoot}/assets/images"
}
I tried to use vscode.workspace.getConfiguration() function to get the variable like this:
let folderPathFromConfig = vscode.workspace.getConfiguration('pasteImage')['path'];
But it got ${workspaceRoot}/assets/images, I want ${workspaceRoot} to expand to the real path of the workspace root, what should I do?
Thanks for any hint!

There doesn't seem to be a service for it, you'd have to extend the AbstractVariableResolverService similarly to the debug host to do this.
Unfortunately you cannot import these things from within your extension.
The actual implementation is quite straightforward but I do wonder what they thought when they decided against this.
IProcessEnvironment is just a stupid interface for the built-in process.env prop.

According to the vscode extensibility documentation you can use
let workspaceRoot = vscode.workspace.workspaceFolders[0];
to get the root WorkspaceFolder. You can get the absolute path of a WorkspaceFolder by using its Uri:
let workspaceRootPath = workspaceRoot.uri.path;
Then you just replace the "${workspaceRoot}" sub string of the folderPathFromConfig.
To conclude:
let folderPathFromConfig = vscode.workspace.getConfiguration('pasteImage')['path'];
if (vscode.workspace.workspaceFolders !== undefined) { // is undefined if no folder is opened
let workspaceRoot = vscode.workspace.workspaceFolders[0];
folderPathFromConfig = folderPathFromConfig.replace('${workspaceRoot}', workspaceRoot.uri.path);
}

Related

Why is a function provided by require undefined in a class instance? [duplicate]

This question already has an answer here:
Require returns an empty object
(1 answer)
Closed 1 year ago.
I'm asking this mostly as a sanity check, I'm unsure if I have made a mistake somewhere or if this fundamentally does not work.
I am converting some Java to Nodejs, A class called Site populates itself from a database via the dataFunctions.getSiteInfo(id) function. It also stores a list of Page instances.
The issue is that dataFunctions is returning as undefined when an instance of Site is created.
Have I misunderstood how classes and modules work in this case? Is it possible for instances of this class to access the dataFunctions module? Likewise is it possible for the Site class to reference the Page class as well? Or have I made some other silly mistake?
Site.js
let Page = require('./Page.js');
let dataFunctions = require('../DataFunctions.js');
module.exports = class Site {
constructor(id) {
this.id = id;
this.pages = [];
console.log(dataFunctions);
this.siteInfo = dataFunctions.getSiteInfo(id);
DataFunctions.js
let Site = require('./classes/Site.js');
function makeASite() {
let id = 2;
let site = new Site(id);
}
function getSiteInfo(id) {
etc etc
}
module.exports = {
getSiteInfo: function (id) {
return getSiteInfo(id);
},
};
Based on the code provided, I believe you forgot to create a new dataFunctions instance.
const df = new dataFunctions();
console.log(df);
If dataFunctions is providing static functions, make sure that the getSiteInfo method is static.
Otherwise, additional details may be needed.
Edit
It looked like the project structure and the require paths were wonky, but it's hard to tell.
When I run the code provided using the project structure inferred by the require statements, I'm not able to reproduce your issue.
However, one problem that I do see is that you're requiring the Site.js module from the DataFunctions.js module and requiring the DataFunctions.js module from the Site.js module, creating circular dependencies. Try eliminating these by moving makeASite to the Site.js module, for instance.

Using different Class Methods, depending on the used target compiler option

How can I tell TypeScript to use different-written methods (on the same class), depending on the used target option within the tsconfig.json file?
I'm currently re-writing one of my scripts into TypeScript to just "manage" one source, because at the moment I'm working with an ES5 and an ES6 file next to each other. Since TypeScript supports to just change the output target in the tsconfig.json file, I would just need one version to update and maintain.
The problem is I'm using a generator in the ES6 version, which theoretically shouldn't be this issue because TypeScript "adds" a pseudo generator to the top of my ES5 file. BUT: My Pseudo-Generator code, which I'm currently using on the ES5 file, is "cleaner" and way less code.
The Question
Is it possible to overwrite the respective "generator" method or using any special comment annotation (such as //#ts-target) to tell the compiler which code (function body) should be used depending on the used target in the configuration file? (Even If I couldn't found such solution on the official documentation).
An additional function or script, which can be added into the TypeScript compiler process would also help, I guess, because I'm compiling them using a small node.js script (which compiles both ES files without changing the tsconfig.json file directly.)
Or is there any kind of extension, to move different methods of the same class into different files? This way I could "extract" the respective "generator" methods, but this would cause another question: How can I link them depending on the target, because I'm using /// <reference /> linking on the main script file to get all together.
Any other idea?
class Option {
///#ts-target ES5
__walkerVariable1:any undefined
__walkerVariable2:any undefined
__walkerVariable3:any undefined
walker() {
/* Some Pseudo-Walker Code for ES5 */
}
///#ts-target ES6
*walker() {
/* Real Walker Code for ES6 */
}
}
Currently, a Pseudo-Generator code gets added in the ES5 version of my script. I want to prevent this by using a different method / function body with my own Pseudo-Generator. Therefore, I need to tell TypeScript that he should "ignore" the Pseudo-Generator in ES6 / the Real-Generator in ES5 and just render one of them depending on the used target option.
That was a bit tricky, but I found a solution thanks to this GitHub issue.
As mentioned above, I'm using my own node.js script to compile the TypeScript files into two different JavaScript versions (ES5 and ES6). Therefore, I'm using the TypeScript API with the ts.createProgram method. This method allows to add a host object as third parameter, which takes over some compiler processes and one of those is the file loader, called getSourceFile.
The rest is then relatively easy: Searching for a custom comment annotation (in my case \\\#ts-target:ES5 and \\\#ts-target:ES6 respectively) and filter them using RegExp. Maybe not the best solution, but it works!
function compileTS(){
let config = readConfig("ts/tsconfig.json");
let host = ts.createCompilerHost(config.options);
let sourceFile = host.getSourceFile;
// ES5 JavaScript
(function(config){
host.getSourceFile = function(filename) {
if(filename === "ts/options.ts"){
let file = fs.readFileSync("./ts/options.ts").toString();
file = file.replace(/[ ]+\/\/\/\#ts\-target\:ES6\s+([\s\S]*)\/\/\/\#ts\-target\:ES6/gm, "");
return ts.createSourceFile(filename, file, ts.ScriptTarget.ES5, true);
}
return sourceFile.call(host, filename);
}
let program = ts.createProgram(config.fileNames, config.options, host);
let emitResult = program.emit();
report(ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics));
if(emitResult.emitSkipped){
process.exit(1);
}
}(config));
// ES6 JavaScript
config.options.target = 2;
config.options.outFile = "../dist/js/tail.select-es6.js";
(function(config){
host.getSourceFile = function(filename) {
if(filename === "ts/options.ts"){
let file = fs.readFileSync("./ts/options.ts").toString();
file = file.replace(/[ ]+\/\/\/\#ts\-target\:ES5\s+([\s\S]*)\/\/\/\#ts\-target\:ES5/gm, "");
return ts.createSourceFile(filename, file, ts.ScriptTarget.ES2015, true);
}
return sourceFile.call(host, filename);
}
let program = ts.createProgram(config.fileNames, config.options, host);
let emitResult = program.emit();
report(ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics));
if(emitResult.emitSkipped){
process.exit(1);
}
}(config));
}

Node.js console.log() in txt file

I have another question (last question). At the moment i am working on a Node.js project and in this I have many console.log() functions. This has worked okay so far but I also want everything that's written to the console to also be written in a log-file. Can someone please help me?
For example:
Console.log('The value of array position [5] is '+ array[5]);
In my real code its a bit more but this should give you an idea.
Thank you hopefully.
Just run the script in your terminal like this...
node script-file.js > log-file.txt
This tells the shell to write the standard output of the command node script-file.js to your log file instead of the default, which is printing it to the console.
This is called redirection and its very powerful. Say you wanted to write all errors to a separate file...
node script-file.js >log-file.txt 2>error-file.txt
Now all console.log are written to log-file.txt and all console.error are written to error-file.txt
I would use a library instead of re-inventing the wheel. I looked for a log4j-type library on npm, and it came up with https://github.com/nomiddlename/log4js-node
if you want to log to the console and to a file:
var log4js = require('log4js');
log4js.configure({
appenders: [
{ type: 'console' },
{ type: 'file', filename: 'logs/cheese.log', category: 'cheese' }
]
});
now your code can create a new logger with
var logger = log4js.getLogger('cheese');
and use the logger in your code
logger.warn('Cheese is quite smelly.');
logger.info('Cheese is Gouda.');
logger.debug('Cheese is not a food.');
const fs = require('fs');
const myConsole = new console.Console(fs.createWriteStream('./output.txt'));
myConsole.log('hello world');
This will create an output file with all the output which can been triggered through console.log('hello world') inside the console.
This is the easiest way to convert the console.log() output into a text file.`
You could try overriding the built in console.log to do something different.
var originalLog = console.log;
console.log = function(str){
originalLog(str);
// Your extra code
}
However, this places the originalLog into the main scope, so you should try wrapping it in a function. This is called a closure, and you can read more about them here.
(function(){
var originalLog = console.log;
console.log = function(str){
originalLog(str);
// Your extra code
})();
To write files, see this stackoverflow question, and to override console.log even better than the way I showed, see this. Combining these two answers will get you the best possible solution.
Just write your own log function:
function log(message) {
console.log(message);
fs.writeFileSync(...);
}
Then replace all your existing calls to console.log() with log().
#activedecay's answer seems the way to go. However, as of april 30th 2018, I have had trouble with that specific model (node crashed due to the structure of the object passed on to .configure, which seems not to work in the latest version). In spite of that, I've managed to work around an updated solution thanks to nodejs debugging messages...
const myLoggers = require('log4js');
myLoggers.configure({
appenders: { mylogger: { type:"file", filename: "path_to_file/filename" } },
categories: { default: { appenders:["mylogger"], level:"ALL" } }
});
const logger = myLoggers.getLogger("default");
Now if you want to log to said file, you can do it just like activedecay showed you:
logger.warn('Cheese is quite smelly.');
logger.info('Cheese is Gouda.');
logger.debug('Cheese is not a food.');
This however, will not log anything to the console, and since I haven't figured out how to implement multiple appenders in one logger, you can still implement the good old console.log();
PD: I know that this is a somewhat old thread, and that OP's particular problem was already solved, but since I came here for the same purpose, I may as well leave my experience so as to help anyone visiting this thread in the future
Here is simple solution for file logging
#grdon/logger
const logger = require('#grdon/logger')({
defaultLogDirectory : __dirname + "/logs",
})
// ...
logger(someParams, 'logfile.txt')
logger(anotherParams, 'anotherLogFile.log')

NodeJS: Single object with all requires, or "standard" paths in code?

So, I'm a big fan of creating global namespaces in javascript. For example, if my app is named Xyz I normally have an object XYZ which I fill with properties and nested objects, for an example:
XYZ.Resources.ErrorMessage // = "An error while making request, please try again"
XYZ.DAL.City // = { getAll: function() { ... }, getById: function(id) { .. } }
XYZ.ViewModels.City // = { .... }
XYZ.Models.City // = { .... }
I sort of picked this up while working on a project with Knockout, and I really like it because there are no wild references to some objects declare in god-knows-where. Everything is in one place.
Now. This is ok for front-end, however, I'm currently developing a basic skeleton for a project which will start in a month, and it uses Node.
What I wanted was, instead of all the requires in .js files, I'd have a single object ('XYZ') which would hold all requires in one place. For example:
Instead of:
// route.js file
var cityModel = require('./models/city');
var cityService = require('./services/city');
app.get('/city', function() { ...........});
I would make an object:
XYZ.Models.City = require('./models/city');
XYZ.DAL.City = require('./services/city');
And use it like:
// route.js file
var cityModel = XYZ.Models.City;
var cityService = XYZ.DAL.City;
app.get('/city', function() { ...........});
I don't really have in-depth knowledge but all of the requires get cached and are served, if cached, from memory so re-requiring in multiple files isn't a problem.
Is this an ok workflow, or should I just stick to the standard procedure of referencing dependencies?
edit: I forgot to say, would this sort-of-factory pattern block the main thread, or delay the starting of the server? I just need to know what are the downsides... I don't mind the requires in code, but I just renamed a single folder and had to go through five files to change the paths... Which is really inconvenient.
I think that's a bad idea, because you are going to serve a ton of modules every single time, and you may not need them always. Your namespaced object will get quite monstrous. require will check the module cache first, so I'd use standard requires for each request / script that you need on the server.

Getting name of a module in node.js

Does anyone know how to get the name of a module in node.js / javascript
so lets say you do
var RandomModule = require ("fs")
.
.
.
console.log (RandomModule.name)
// -> "fs"
If you are trying to trace your dependencies, you can try using require hooks.
Create a file called myRequireHook.js
var Module = require('module');
var originalRequire = Module.prototype.require;
Module.prototype.require = function(path) {
console.log('*** Importing lib ' + path + ' from module ' + this.filename);
return originalRequire(path);
};
This code will hook every require call and log it into your console.
Not exactly what you asked first, but maybe it helps you better.
And you need to call just once in your main .js file (the one you start with node main.js).
So in your main.js, you just do that:
require('./myRequireHook');
var fs = require('fs');
var myOtherModule = require('./myOtherModule');
It will trace require in your other modules as well.
This is the way transpilers like babel work. They hook every require call and transform your code before load.
I don't know why you would need that, but there is a way.
The module variable, which is loaded automatically in every node.js file, contains an array called children. This array contains every child module loaded by require in your current file.
This way, you need to strict compare your loaded reference with the cached object version in this array in order to discover which element of the array corresponds to your module.
Look this sample:
var pieces = require('../src/routes/headers');
var childModuleId = discoverChildModuleId(pieces, module);
console.log(childModuleId);
function discoverChildModuleId(object, moduleObj) {
"use strict";
var childModule = moduleObj.children.find(function(child) {
return object === child.exports;
});
return childModule && childModule.id;
}
This code will find the correspondence in children object and bring its id.
I put module as a parameter of my function so you can export it to a file. Otherwise, it would show you modules of where discoverChildModule function resides (if it is in the same file won't make any difference, but if exported it will).
Notes:
Module ids have the full path name. So don't expect finding ../src/routes/headers. You will find something like: /Users/david/git/...
My algorithm won't detect exported attributes like var Schema = require('mongoose').Schema. It is possible to make a function which is capable of this, but it will suffer many issues.
From within a module (doesn't work at the REPL) you can...
console.log( global.process.mainModule.filename );
And you'll get '/home/ubuntu/workspace/src/admin.js'

Categories