ts2fable
ts2fable copied to clipboard
Problems with definitions imported across multiple .d.ts files (re pixi.js v6)
I ran into this issue when trying to update fable-pixi from pixi.js v5 to v6.
pixi.js type definition files have code like this:
import { AlphaFilter } from '@pixi/filter-alpha';
which gets translated by ts2fable into this:
type AlphaFilter = @pixi_filter_alpha.AlphaFilter
which is not valid. I'm actually not sure what the correct behavior here is, because the original fable-pixi handles this in a way that seems hand-written (see Fable.Pixi.js).
Here is the script I ran from within the ts2fable/ project dir:
npm install --save-dev pixi.js
npm run-script ts2fable node_modules/pixi.js/*.d.ts node_modules/\@pixi/*/*.d.ts Fable.Pixi.fs
Also, this generated everything in a single module. It was unclear from the ts2fable docs if there was a simple way to put the output of each file into its own module - maybe it just means running ts2fable separately and gluing them together manually?
Maybe you want this part in readme
You can also use --export
(or -e
) option to collect from multiple tsfiles
In below sample: All the related ts files in npm packages uifabric and office-ui-fabric-react will be compiled to OfficeReact.fs
as a bundle
ts2fable node_modules/office-ui-fabric-react/lib/index.d.ts test-compile/OfficeReact.fs -e uifabric office-ui-fabric-react
I have a similar issue with G6
I tried the 'office-ui' sample above and it works fine. However the type of import references being used in G6 (and Pixi) don't seem to work with ts2fable current version (compiled from source).
Here is the root TS file for G6:
import G6 from '@antv/g6-pc';
export * from '@antv/g6-pc';
export default G6;
export declare const version = "4.3.4";
G6 TS references are not relative (or maybe the "@" character is causing issues). ReactXP (which works in ts2fable) has relative references:
import * as ReactXP from './web/ReactXP';
export = ReactXP;
Is there a quick workaround that I can try.
PS: Is there a way to debug F# code in ts2fable. I am running "Watch Test" with mocha but breakpoints are not accepted by vscode.
According to this [corrected] article. Non-relative path import requires the "tsconfig-paths" module. I have added the following to package.json:
"tsconfig-paths": "^3.10.1",
Also needed is a CompilerOptions setting called "paths". I am assuming that the place to do this is in bridge.fs but I can't seem to construct a "MapLike<ResizeArray
let createDummy tsPaths (sourceFiles: SourceFile list) =
let options = jsOptions<CompilerOptions>(fun o ->
o.target <- Some scriptTarget
o.``module`` <- Some ModuleKind.CommonJS
o.paths <- fun s ->
Some
[
"src/*", ResizeArray["src/*"]
]
)
Any suggestions?
Looks like one has to implement MapLike<_> as it is an interface
type TsPaths(vs:Dictionary<string,ResizeArray<string>>) =
interface MapLike<ResizeArray<string>> with
member _.Item with get(v) = vs.[v] and set key value = vs.[key] <- value
but still not sure about what the the path entries need to be to make this all work ...
After much internet search, the bottom line is that the new style of import references in TS is not a javascript standard and hence it is not universally supported. In particular node does not support such references, out of the box.
The "tsconfig-paths" package supports the remapping of TS references to relative references, however, its implementation is not complete.
ts2fable internally loads the typescript compiler (in bridge.fs) to find the imports. It is not clear to me how to enable "tsconfig-paths" for the internally hosted compiler. I tried to require "tsconfig-paths/register" for ts2fable but it did not seem to work.
Admittedly I am not the expert in this area so instead of spending more time on g6, I switched to another graph library, cytoscape. The .ts definitions in DefinitelyTyped for cytoscape were processed correctly by ts2fable.
I have a feeling that fable users will increasingly run into this issue and hopefully more knowledgeable members of the community can find a solution.
in short: ts2fable doesn't seem to really handle re-exports at all....
In the example with ReactXP
ts2fable doesn't actually detect "hey there's an import, let's load this" followed by "that imported stuff gets re-exported here again" -- but instead ts2fable just looks into all sub-directories and exports all .d.ts
files it finds (requires --exports ...
).
So instead of a re-export under the root-name, it's just a redirect to somewhere else (which dependencies usual are -- but not really when re-exported).
To test this with g6
:
- create an empty
index.d.ts
in@antv
:"" > ./node_modules/@antv/index.d.ts
- run ts2fable on that file:
ts2fable ./node_modules/@antv/index.d.ts g6.fs --exports g6
-
--exports g6
: exports all.d.ts
-files in sub-dirs that containg6
-
- That should produce a lot of debug messages and F# file
g6.fs
with all the stuff from all theg6
packages.- But: this is just "export everything", not really re-exported like in
g6/lib/index.d.ts
.... - And: names of modules are file paths (with
_
instead of/
)... - Might be possible to imitate re-export behaviour by using
g6
as root and place all otherg6-...
d.ts
files in sub-dirs
- But: this is just "export everything", not really re-exported like in
In fact ts2fable doesn't handle some of the stuff in g6/lib/index.d.ts
at all:
-
import G6 from '...'
is ignored because the import clause has no named bindings (likeimport {foo} from '...'
): https://github.com/fable-compiler/ts2fable/blob/f9d14a61f93791247ef8fb66e5465e492b6cccfa/src/read.fs#L807-L812-
import * as G6 from '...'
is handled. I think that's the same?
-
-
export * from '....'
isn't handled at all: https://github.com/fable-compiler/ts2fable/blob/f9d14a61f93791247ef8fb66e5465e492b6cccfa/src/read.fs#L867-L869
So yeah....unfortunately no real re-export dependency handling at all (sub-dir or somewhere else) -- just the option to export everything.
I think supporting this would require some major work. But might be reasonable. "Collection"-packages aren't that uncommon (D3 is quite similar).
Most of the work can probably be offloaded on to the TypeScript Compiler.
Some more stuff:
- About new import styles in TS: Isn't that just ESM style? Which is a JavaScript standard. Most tools now support ESM -- but only in recent versions. In Node it seems to be stabilized with
15.3
. ts2fable still usesnode --require esm
to run directly androllup
to convert into umd) -
tsconfig-paths
: According to the docs that's only needed when converting to JS -- which ts2fable isn't doing. TS Compiler can handle absolute paths.
About Breakpoints:
should work:
- Set breakpoint somewhere in F# code
- Start "WatchTest" Task in VS Code (or
dotnet fake build -t WatchTest
in terminal) - In VS Code: F5 or "Run And Debug" -> "Run Mocha Tests"
That should break when reaching your breakpoint.
Though: Debugging isn't that nice: quite a lot of code is Javascript (like all Fable implementations like List). And breakpoint locations are sometimes strange (inside of strings?).
Best set your breakpoint close to the thing you want to debug, instead of manually going there. And focus on a single test by changing it
to only
for that test.
But breakpoints per se should work.
Note: Just to be sure every dependency is downloaded, run ./fake.cmd build
once.
@Booksbaum thanks for your analysis.
I don't know enough about the node.js/javascript/ts ecosystem so your comments are probably right.
I was expecting/hoping that the discovery of the referenced modules could be delegated to the TS compiler. Maybe it is just a matter of being able to supply the right parameters (e.g. a tsconfig.json file)?
PS I am able to debug using the method you have described (thanks!). However, I remember being able to set breakpoints in F# code. That does not seem to work with the latest bits. I can debug the generated javascript - which is quite easy to understand given the quality of the code generation.
I was expecting/hoping that the discovery of the referenced modules could be delegated to the TS compiler. Maybe it is just a matter of being able to supply the right parameters (e.g. a tsconfig.json file)?
Unfortunately it's not just that simple. Resolving a dependency is hopefully something the TS Compiler can do. But then we still need to recognize re-exports (-> dependencies that get exported again) and process the dependency files correctly (translating AND putting the generated code in the correct place).
And ts2fable already has a lot issues with just processing a single file :/ ....
I am able to debug using the method you have described (thanks!). However, I remember being able to set breakpoints in F# code. That does not seem to work with the latest bits. I can debug the generated javascript - which is quite easy to understand given the quality of the code generation.
My bad:
I didn't adjust .vscode/launch.json
when updating the build script some time ago.
Debugging used some old leftovers of an old ts2fable build on my machine. No wonder the break points were in strange places...
-> Fixed in master
With this VS Code Breakpoints should work with source maps (-> F# code).