Creating a custom visual for PowerBI in NodeJS - "Cannot find name 'IVisualHost' - node.js

I'm trying to follow this tutorial on creating a custom visual for Power BI : https://learn.microsoft.com/en-us/power-bi/developer/custom-visual-develop-tutorial
The test with the default code works properly when I connect to Power BI Cloud, as shown in the part"Testing the custom visual" step 8, of the tutorial.
The problem is when I try to add the class-level properties in the visual.ts file (after I deleted the code as indicated in the part "Developing the visual elements" step 2 of the tutorial ), I get the error "Cannot find name 'IVisualHost'".
"use strict";
import "core-js/stable";
import "./../style/visual.less";
import powerbi from "powerbi-visuals-api";
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
import IVisual = powerbi.extensibility.visual.IVisual;
import EnumerateVisualObjectInstancesOptions = powerbi.EnumerateVisualObjectInstancesOptions;
import VisualObjectInstance = powerbi.VisualObjectInstance;
import DataView = powerbi.DataView;
import VisualObjectInstanceEnumerationObject = powerbi.VisualObjectInstanceEnumerationObject;
import { VisualSettings } from "./settings";
export class Visual implements IVisual {
private host: IVisualHost; ------------ the first error is here
private svg: d3.Selection<SVGElement>;
private container: d3.Selection<SVGElement>;
private circle: d3.Selection<SVGElement>;
private textValue: d3.Selection<SVGElement>;
private textLabel: d3.Selection<SVGElement>;
constructor(options: VisualConstructorOptions) {
this.svg = d3.select(options.element) ----------- the second error is here
.append('svg')
.classed('circleCard', true);
this.container = this.svg.append("g")
.classed('container', true);
this.circle = this.container.append("circle")
.classed('circle', true);
this.textValue = this.container.append("text")
.classed("textValue", true);
this.textLabel = this.container.append("text")
.classed("textLabel", true);
}
I also have this other error in the same visual.ts file : 'd3' refers to a UMD global, but the current file is a module. Consider adding an import instead. I imported the library with the commands : "npm i d3#3.5.5 --save" and "npm i #types/d3#3.5"
This is my pbiviz.json file :
{
"visual": {
"name": "visual9basic",
"displayName": "visual9basic",
"guid": "visual9basic252E75AF09794C8F8CE14414674FBC3E",
"visualClassName": "Visual",
"version": "1.0.0",
"description": "",
"supportUrl": "",
"gitHubUrl": ""
},
"apiVersion": "2.6.0",
"author": {
"name": "",
"email": ""
},
"assets": {
"icon": "assets/icon.png"
},
"externalJS": [
"node_modules/powerbi-visuals-utils-dataviewutils/lib/index.js",
"node_modules/d3/d3.min.js"
],
"style": "style/visual.less",
"capabilities": "capabilities.json",
"dependencies": null,
"stringResources": []
}
Here is my tsconfig.json :
{
"compilerOptions": {
"allowJs": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es6",
"sourceMap": true,
"outDir": "./.tmp/build/",
"moduleResolution": "node",
"declaration": true,
"lib": [
"es2015",
"dom"
]
},
"files": [
"node_modules/powerbi-visuals-utils-dataviewutils/lib/index.js",
"./src/visual.ts",
"./src/settings.ts",
]
}

In Visual.ts, try adding the following import:
import IVisualHost = powerbi.extensibility.IVisualHost;
It should take care of the missing interface reference.
For the d3 reference you can try
import * as d3 from "d3";
Hope this helps. I haven't worked through the tutorial myself yet.

What I found out is that after a certain patch of npm, the ".api" folder isn't created any more. Instead, those files are stored under "node_modules/powerbi-visuals-api/". Also, the file "index.d.ts" replaces the former "PowerBI-visuals.d.ts".
But, I still have the 2 errors I described.

Related

Nestjs: Repl with monorepo mode

I have a nest app that is using monorepo mode. I would like to take advantage of the new repl feature that was released in nest 9.0+.
My directory structure looks as such:
apps/
--inventory-ops/src/app.module
--ticket-office/src/app.module
I have followed the instructions found in the docs creating a repl.ts, but when I run the repl commannd:
npm run start -- --entryFile repl
I get this error output:
Error: Cannot find module '/dist/apps/ticket-office/repl'
Looking at my dist folder, the only build target is main.js, which would explain it not being able to find the repl module. Do I need to update something in my webpack config to make sure repl.ts gets built as well? Any help would be appreciated.
I managed to solve this by adding a new project in nest-cli.json, for example:
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "#nestjs/schematics",
"sourceRoot": "apps/gateway/src",
"monorepo": true,
"root": "apps/gateway",
"compilerOptions": {
"webpack": true,
"tsConfigPath": "apps/gateway/tsconfig.app.json"
},
"projects": {
"gateway": {
"type": "application",
"root": "apps/gateway",
"entryFile": "main",
"sourceRoot": "apps/gateway/src",
"compilerOptions": {
"tsConfigPath": "apps/gateway/tsconfig.app.json"
}
},
"ticket-office": {
"type": "application",
"root": "apps/ticket-office",
"entryFile": "main",
"sourceRoot": "apps/ticket-office/src",
"compilerOptions": {
"tsConfigPath": "apps/ticket-office/tsconfig.app.json"
}
},
"ticket-office:repl": { // <<--- HERE
"type": "application",
"root": "apps/ticket-office",
"entryFile": "repl", // <<-- HERE
"sourceRoot": "apps/ticket-office/src",
"compilerOptions": {
"tsConfigPath": "apps/ticket-office/tsconfig.app.json"
}
},
}
}
Then you can run nest start ticket-office:repl
I hope this helps.
EDIT:
Adapted the answer to your question.
Try to run this:
nest start <your-app> --config nest-cli.json --debug --watch -- --entryFile repl
I faced the same issue and this worked for me.
I don't really know why NestJS take repl file into consideration for building only when explicitly the cli config is provided. It's probably a bug with the CLI.
Alternative
Also, you can add a custom parameter to your command and start the REPL mode conditionally:
script:
nest start <your-app> --watch repl
main.ts file:
async function bootstrap() {
if (process.argv[process.argv.length - 1] === 'repl') {
return repl(AppModule);
}
// Non REPL mode Nest app initialisation
...
}
bootstrap();

How to import esm modules with NodeJS 13, and Typescript 3.8?

I have a problem with some imports in NodeJS. I want to use the new features of Typescript 3.8, like private fields : #myPrivateField
I don't know how to correctly import the module "typescript" in my class. I tried many options, but impossible to solve my problem.
My files :
package.json
{
"name": "test",
"scripts": {
"start": "tsc && node --experimental-modules --es-module-specifier-resolution=node main.js"
},
"dependencies": {
"#types/node": "^13.13.2",
"app-root-path": "^3.0.0",
"fs-extra": "^9.0.0",
"tsutils": "^3.17.1"
},
"devDependencies": {
"ts-node": "~8.3.0",
"typescript": "^3.8.3"
},
"type": "module"
}
tsconfig.json
{
"compilerOptions": {
"lib": [
"ESNext",
"es2016",
"dom",
"es5"
],
"module": "esnext",
"moduleResolution": "Node",
"sourceMap": true,
"target": "es6",
"typeRoots": [
"node_modules/#types"
]
}
}
main.ts
// import ts = require("typescript");
import * as ts from "typescript";
export class Main {
node: ts.Node;
#test = 'zzz';
constructor() {}
process(): void {
ts.forEachChild(this.node, function cb() {
});
console.log('#test', this.#test);
}
}
const main = new Main();
main.process();
With this code, when I run npm run start, I have the error TypeError: ts.forEachChild is not a function
Without the line with ts.forEachClid() it logs correctly the value of the private field #test.
If I try to replace import * as ts from "typescript"; by import ts = require("typescript");, I have the error TS1202: Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead
Of course, I tried many changes in tsconfig.json and in package.json (with `"type" = "module"), but impossible to solve this problem.
I even tried to replace "module": "esnext" by "module": "commonjs", but I have an error exports is not defined.
Remark :
This is not specific to the module "typescript". I have the same problem with other modules like "fs-extra", which are making exports in a different way than most of classic NodeJS modules.
For example, the module "typescript" exports with export = ts.
I found this reference too, but it didn't help me a lot :
https://www.typescriptlang.org/docs/handbook/modules.html
My nodeJs version is 13.3.0 and my typescript version is 3.8.3
Thanks for your help
Finally, the good response was : you need commonjs and es2018 to be able to use the typescript #privateFields in a node module.
Here is the correct tsconfig.json to use :
{
"compileOnSave": false,
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"target": "es2018",
"typeRoots": [
"node_modules/#types"
],
"lib": [
"es2018",
"dom"
]
}
}
ESM Modules are still in experimental mode, there are a lot of modules that do not support it, e.g. uses module.exports (CommonJS) instead of export (ESM).
Your best choice here is to use commonjs module and run node without any flags. Also, type of the package in package.json must be commonjs as well.
Answering your question "How to import ESM module"... In case, module does support ESM modules, you can just use import as you usually do:
import { something } from './something';
UPD: as OP author mentioned, for private fields to work, there is must be set a target es2018. The reasoning behind this is that private fields is not a part from ES2015 specification and you need to upgrade to the minimal supported target to do that.
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"target": "es2018"
}

Cannot get node or ts-node/register to use es2020 anymore (flatMap is not a function)

I am unable to use flatmap with node or ts-node despite previously being able to in my code. I am not sure what changed but I am getting this error 'TypeError: [x].flatMap is not a function' flatMap() should be a function because I explicitly make x an array (this code was working before no problems).
here is my tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"target": "es2020",
"esModuleInterop": true,
"sourceMap": true,
"strictNullChecks": true,
"outDir": "out",
//"strictBindCallApply": true,
//"strict": true
}
}
and here is my lancher
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/src/main.ts",
"outFiles": ["${workspaceFolder}/out/**/*.js"],
"preLaunchTask": "npm: build",
"runtimeArgs": ["-r", "esm"],
"stopOnEntry": true,
"console": "integratedTerminal",
},
{
"type": "node",
"request": "launch",
"name": "Mocha Tests",
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"args": [
"-u",
"tdd",
"--timeout",
"999999",
"--colors",
"--require",
"esm",
"--require",
"ts-node/register",
"--project",
"${workspaceFolder}/tsconfig.json",
"${workspaceFolder}/**/*spec.ts"
],
"console": "integratedTerminal",
"stopOnEntry": false,
}
]
}
and the flatmap call in question
(x: discord.Message | discord.Message[] | null): (discord.Message | null)[] => [x].flatMap(
// #ts-ignore
(t: discord.Message | discord.Message[] | null): (discord.Message | discord.Message[] | null) => t
)
another example that does not work:
const test = [1,3,4,[5,4,2]].flatMap(x => x);
TypeError: [1,3,4,[5,4,2]].flatMap is not a function
Neither ts-node/register or ts-node seems to respect "target": "es2020" anymore and I am not sure what exactly changed. VScode does have context for intellesense for flatMap. Can anyone explain to me why it is not working anymore?
The problem was I had a conflict in the version number of node after an install of a program. I have stopped using the snap package of node and started using nvm.
I had the same issue. I combed through many forums. Could not fix. The below fixed for me. This is one more thing that you can try.
My node version was 14.19.
I remember the same code working sometime back. So I tried to run it on version 12.
I had nvm installed before. I switched my node version to 12 using command nvm use 12. Surprisingly no error with this version.
So I tried updating my node version by running nvm install 14 which updated my version from 14.19 to 14.21. Now when I test, it worked fine.
NVM is a very useful tool when such issues occur. Remember to keep the version match
Thanks
Narayanan

How to debug AWS Lambda Node.js locally using serverless module?

I am completely new to AWS and serverless etc. To speed up development I would like the ability to debug my application locally.
Following this article Running and Debugging AWS Lambda functions locally I have attempted to achieve just that.
In Visual Studio Code when I run the debug configuration, the application exits instantly without error (A break-point is set on the declaration and initialisation of the 'content' variable). I am not sure I have the function name correct. I am trying to enter at the main 'handler' function defined in 'index.js' as:
exports.handler = (event, context, callBack) =>
{
let bIsPostRequest = false, bIsPutRequest = false, bIsGetRequest = false, bIsDelRequest = false;
let content = "";
...
Here is my 'launch.json' configuration file:
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Debugger",
"program":
"${workspaceFolder}\\node_modules\\serverless\\bin\\serverless",
"args":[
"invoke",
"local",
"-f",
"index.handler", // function name
"--data",
"{}"
],
"outFiles": [
"${workspaceFolder}\\index.js"
]
}
]
}
Also, I am not 100% certain on the definition of 'outfiles' in the configuration. I have come to the conclusion it is the file(s) I am trying to debug, however if this is the case 'outfiles' does not seem a fitting name to me.
The local environment I am working in is a windows one.
After coming across this post I managed to get the debugger working. Here is the configuration to match my needs:
const lambdaLocal = require('lambda-local');
var lambdaFunc = require("./index.js");
lambdaLocal.execute({
lambdaFunc: lambdaFunc,
lambdaHandler: "handler",
event: {
context: {
"resource-path": "/products",
"http-method": "GET"
},
"body-json": {
name : "ProductA"
}
}
}).then(function(done) {
console.log(done);
}).catch(function(err) {
console.log(err);
});
I saved this file as 'debugLocal.js' in my main working directory. The launch.json file now looks as follows:
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Debugger",
"program": "${workspaceFolder}\\debugLocal.js"
}
]
}
So far everything appears to be replicated fairly well. One thing to note is the file paths on includes had to be changed slightly i.e. require("./js/inc/globalDefines.js"); instead of require("js/inc/globalDefines.js");

How have IDE type awareness with app-module-path in Visual Studio Code

I'm in a node environment where they are using app-module-path so that the requires can always be written from a base path.
So all path's can be: const something = require('/api/something') instead of having to back down the folder structure with something like: const something = require('../../api/something).
Without the exact path, VSCode says all types are of type any. This means functionality like 'go to definition' won't work.
Is there a way to configure VSCode to address this?
After some more searching I found this can be done by using a jsconfig.son file with the following settings.
{
"compilerOptions": {
"target": "ES6",
"allowSyntheticDefaultImports": true,
"jsx": "react",
"module": "commonjs",
"baseUrl": ".",
"paths": {
"*": [
"*",
"app/*"
]
}
},
"include": [
"app/**/*.js"
]
}

Resources