How to reference type and make sure import is not stripped by typescript compiler - typescript-compiler-api

I'm writing a ts transformer that finds named class decorator then replace decorator's call and passes a map with names and types of all props of decorated class, e.g.:
source file:
import BigNumber from "bignumber.js";
#decorator()
class AClass {
aProp: BigNumber;
}
output:
import BigNumber from "bignumber.js";
#decorator({"aProp": BigNumber})
class AClass {
aProp: BigNumber;
}
I'm getting prop name and type from PropertyDeclaration:
const clazzProps = [];
for (const childNode of clazz.members) {
if (ts.isPropertyDeclaration(childNode)) {
const type = typeChecker.getTypeAtLocation(childNode);
if (type.isClass()) {
clazzProps.push(ts.createPropertyAssignment(
ts.createIdentifier(childNode.name.getText()),
childNode.type.typeName
);
}
}
}
So far so good, however after compilation import statement is removed from output js file, cause in the source file BigNumber was only referenced as a type, not as a value. My question is, how to properly check if BigNumber is not a type only and make sure that import will not be stripped ?

I know it's a bit late, but still.
Typescript compiler has four main phases—parsing, binding, type checking, and emitting. Binding is where the relationships between identifiers are resolved, but transformation happens during the "emitting" phase.
That means on a moment when transform happened typescript already know that BigNumber is used only as type in this context and following it's own rules all imports which point only to type is not emitted to js.
Unfortunately, there is no simple solution and you probably will be needed to add this imports by yourself during the transform.
The same use-case and problem was solved here https://github.com/nestjs/graphql/blob/master/lib/plugin/visitors/model-class.visitor.ts
It's resolving types of property using type checker and finding the file this type pointint to and store this paths in importsToAddPerFile, than in updateImports the are added to the source file.
I'm generally not recommending using a TypeChecker (it's slower, might not be available in some context and can produce weird results if you have few transform relying on it) in transforms and probably for simple cases you can just read what imports you already have and re-create them manually.

Related

Set fallback directory with BABYLON.SceneLoader.AppendAsync and gltf file

I'm trying to modify an existent project, I would like to set a fallback texture when I load a GLTF file
BABYLON.GLTFFileLoader.IncrementalLoading = false;
BABYLON.SceneLoader.AppendAsync(rootPath, 'data:' + gltfContent, scene, undefined, '.gltf').then(function () {
scene.createDefaultCameraOrLight(true);
scene.activeCamera.attachControl(canvas);
scene.activeCamera.wheelDeltaPercentage = 0.005;
I don't know exactly how to do it.
what's the better way to proceed? should I read the GLTF and modify the URI?
I think it is a better solution to use some callback
Anyone is an expert with babylon.js?
Thanks
Babylon's GLTF 2.0 loader has an extension system that can be seen as promise-based plugin for each step of the loading process.
You can see a few examples for the extensions here:
https://github.com/BabylonJS/Babylon.js/tree/master/loaders/src/glTF/2.0/Extensions
What can be very interesting in your case is here:
https://github.com/BabylonJS/Babylon.js/blob/master/loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts
As you can see, this extension contains a function called loadTextureInfoAsync that returns a promise when done, after receiving the texture info that is about to be loaded.
Depending on your use case you can completely replace the load texture functionality or extend it (like in the example above). To override it you should just implement the function yourself:
public loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: BaseTexture) => void): Nullable<Promise<BaseTexture>> {
const texture = loadTheTextureYourselfWithFallback();
return Promise.resolve(texture);
}
Of course, the load function needs to be implemented according to your logic.

Can't infer proper type myself in Kotlin when using JavaFX

I just started to study Kotlin and JavaFX by following tutorial.
I could see blank JavaFX windows, and I proceed next step that using FXML.
import javafx.application.Application
import javafx.fxml.FXMLLoader
import javafx.scene.Scene
import javafx.stage.Stage
class AppMain : Application() {
override fun start(primaryStage: Stage) {
primaryStage.title = "Try JavaFX"
val fxml = javaClass.getResource("fxml/Main.fxml")
val root = FXMLLoader.load(fxml) // ERRORS here! `load`
val scene = Scene(root)
primaryStage.scene = scene
primaryStage.show()
}
}
However, I couldn't figure out that how to avoid the type inferencing error like:
Error:(12, 31) Kotlin: Type inference failed: Not enough information to infer parameter T in fun <T : Any!> load(p0: URL!): T! Please specify it explicitly.
From the message, I understand that I have to write the type of variable fxml explicitly.
But I have no idea that what type should be labeled to fxml.
I tried to read the document about JavaFX, but I couldn't figure it out.(I'm not familiar with Java and Kotlin)
I tried to type like URL but it does not make sense.
Many JavaFX & Kotlin example codes that I could find from google does not seems to have the problem like this. (Are the example codes written in previous version?)
What type should I put for the variable?
Or did I miss something other?
Environment and Codes
Environment
JDK 11
JavaFX 11
Kotlin 1.2.71
My complete trial code
https://github.com/QuietJoon/StudyKotlin-JavaFX/tree/fxml
The problem isn't the parameter to the FXMLLoader.load function (which is a java.net.URL object, as returned by javaClass.getResource). It's that this function returns a generic type:
public static <T> T load(URL location)
The Kotlin compiler needs to know what type your root variable will be (as you've not explicitly defined it), but it can't know that as there's nothing in the code that will allow it to infer this.
A quick Google returned this example which has this code in it (in Java):
Parent root = FXMLLoader.load(getClass().getResource("fxml_example.fxml"));
As you can see here, the root variable is of type Parent. So what you need to do is provide this type (i.e. what you expect the load function to return) in some way. Here are two different ways you could do this:
Specify the type explicitly when declaring the variable: val root: Parent = FXMLLoader.load(fxml)
Specify the generic type when calling the method: val root = FXMLLoader.load<Parent>(fxml)
Note also that in your build.gradle file in your github repo there's a mistake that means the code didn't immediately compile when I fetched it:
compile "org.openjfx.javafx.fxml:11:$platform" should be compile "org.openjfx:javafx-fxml:11:$platform" (one of the dots should be a colon).

In flowtype, how can you deal with types of callbacks in external modules?

For example, in pg, there are callbacks that use an internal type PG_Error. This is not exposed via modules.exports.
So when I have to write a callback, say for
const c = new Pool(...);
c.connect((err: PG_Error, client: PoolClient, done: PoolConnectCallback) => ...);
How do I reference those types, which are not exported in the definition? At the moment I get errors like Could not resolve name.
I can copy these definitions out into a local definition file, but this doesn't seem right.
If you do
import type {PG_ERROR, PoolClient ...} from 'pg';
That ought to make those names available in your namespace.

Sharing declarations between modules

Let's take lodash as an example of a library that exports its functions across many modules.
Given the following imports:
import { flow } from 'lodash'
import flow from 'lodash/flow'
import flow from 'lodash/fp/flow'
The current flow-typed libdef for lodash exports only typedefs for the main lodash entry point which means our first named import gets type checked, however the latter 2 default imports do not.
We can create a new module for lodash/flow and declare the default exports for the flow function as:
declare module 'lodash/flow' {
declare export default function flow(...funcs?: Array<Function>): Function;
declare export default function flow(funcs?: Array<Function>): Function;
}
How can we extract these function declarations in such a way that we can share them across the main lodash entry point, the named lodash/flow module and also the lodash/fp/flow module?
Is there a way of creating/storing a declaration for use in many places?

How to make Flow understand code written for Node.js?

I'm just getting started with Flow, trying to introduce it into an existing Node codebase.
Here are two lines Flow complains about:
import Module from 'module';
const nodeVersion = Number(process.versions.node.split('.')[0]);
The warnings about these lines are, respectively:
module. Required module not found
call of method `split`. Method cannot be called on possibly null value
So it seems like Flow isn't aware of some things that are standard in a Node environment (e.g. process.versions.node is guaranteed to be a string, and there is definitely a Node builtin called module).
But then again, Flow's configuration docs suggest it's Node-aware by default. And I have plenty of other stuff like import fs from 'fs'; which does not cause any warning. So what am I doing wrong?
Module fs works as expected because Flow comes with built-in definitions for it, see declare module "fs" here: https://github.com/facebook/flow/blob/master/lib/node.js#L624
Regarding process.versions.node, you can see in the same file that the versions key is typed as a map of nullable strings, with no mention of the specific node property: versions : { [key: string] : ?string };. So you'll need to either make a PR to improve this definition, or adjust your code for the possibility of that value being null.
I guess the answer about module "module" is obvious now – there are no built-in definitions for that module in Flow in lib/node.js. You could write your own definitions, and optionally send a PR with them to the Flow team. You can also try searching github for these, someone might have done the work already.
That lib directory is very useful by the way, it has Flow definitions for DOM and other stuff as well.

Resources