Why force require() usage in typescript langage
mathieutu opened this issue · comments
Hi,
When in typescript language, the generate file uses const fooBS = require('./foo.bs');
instead of import * as fooBS from'./foo.bs'
.
This is because of this switch:
With that configuration, I have a Uncaught ReferenceError: require is not defined
error at runtime (using es6 modules directly in browser, with vitejs).
Why did you make this choise, instead of using standard imports, wich are the standard way of doing in typescript?
Thanks.
Mathieu
pinging @cristianoc on that, as he worked on this topic.
Edit: I've found this comment: #67 (comment)
@mathieutu the use of @genType.import
is indeed what triggers this.
No easy solution, as the .bs.js
uses .gen.tsx
and .gen.tsx
uses .bs.js
.
One thing that comes to mind is: if the import requires no conversion, so no conversion code is generated in the .gen.tsx
, then there's in principle no need for the .bs.js
to use .gen.tsx
. I fear forbidding types requiring conversion in imports would be too restrictive, though that would be a big simplification.
One could also consider e.g. @genType.check
that only performs type checks, and fails or something if conversion is required.
Hey @cristianoc ! I read your past comments, as well as this one, and I still don't quite understand. Is there something deeper than the issue that importing individual files in TS will error because of the lack of types (for which you've found a pretty cool trick I think here #67 (comment)).
I'm asking because I'd really love to use snowpack for my development, but I can't seem to make it accept the files generated by gentype, as they have require
in them and I haven't yet figured out how to transform the files to embed the require runtime.
Could you give us an example of .bs.js
using .gen.tsx
?
Files .bs.js
use .gen.tsx
whenever there is a @genType.import
annotation on a value. (The implicit annotation @bs.module "Filename.gen"
is inserted automatically by the compiler).
E.g. in https://github.com/reason-association/genType/blob/master/examples/typescript-react-example/src/ImportJsValue.bs.js#L3.
The reason being the imported function might require conversion, defined in the .gen.tsx
file.
So the result is .bs.js
and .gen.tsx
use each other. The only trick I found to make this work is to use a require
in the middle of the generated file:
https://github.com/reason-association/genType/blob/master/examples/typescript-react-example/src/ImportJsValue.gen.tsx#L86
So the problem only exists when @genType.import
is used, and I don't know of a solution to this.
One direction for exploration I was mentioning above, is to introduce a new annotation e.g. @genType.check
which acts like @genType.import
and only works when a conversion is not required.
Why is it also the case for components?
@wokalski I'm probably missing something. Why do you think components would be different from other values in this respect?
Btw looking at this issue again, some thought came to mind, which could be worth exploring. What if, whenever a value is imported, we forbid any (typed) export from that file. That would be some kind of unidirectional import/export restriction. So when importing a value, the .bs.js would need to access the .gen file, but not the other way round.
If I'm reading the code correctly (and certainly looking at the generated code - I don't have any genType.imports
), using gentype on any component such as:
[@genType.as "GlobalHost"]
let globalHost = GlobalHost.make;
causes a require to appear in the genType'ed code.
Another thought that came to my mind is what if we removed this circular dependency altogether? What if genType.import didn't depend on .gen
and rather emitted appropriate externals?
I wanted to change this but it looks like it's handled internally by rescript compiler now:
https://github.com/rescript-association/genType/blob/eab8055ef7d3d7e2172f4b9f370ecef4e8e36b19/Changes.md#2170
I don't fully understand this but it seems to me like this issue could be avoided altogether if Bucklescript files imported from the referenced modules directly rather than going through .gen. When is it useful to go TS -> .gen -> .bs.js
rather than Ts -> .gen
and .bs.js -> .gen
directly?
When genType needs to perform a conversion (because the runtime representation is not the same), that needs to happen in .gen
. And .bs.js
uses it.
Summarizing the issue again. GenType imports need importing in one direction. GenType exports need importing in the other direction. By carefully ordering things, and using a require, one can support these circular references. (I haven't found another way, but I'm no way an expert in that).
So one possible alternative solution is to prevent circular references. E.g. by forbidding genType exports when genType imports are used.
I think there's also bug as I noted above.
[@genType.as "GlobalHost"]
let globalHost = GlobalHost.make;
This (where GlobalHost
is a React component module) causes a require
to appear. Also, Curry
seems to be require
d, too. Could you confirm it's a bug indeed or another misunderstanding on my part?
I have confirmed I have 0 occurrences of genType.import
in my codebase yet require
s appear in all places - notably all .bs
imports and Curry
imports are performed using require
.
My bsconfig.json:
{
"name": "web",
"version": "0.1.0",
"reason": {
"react-jsx": 3
},
"sources": [
{
"dir": "src",
"subdirs": true
}
],
"package-specs": {
"module": "es6",
"in-source": true
},
"suffix": ".bs.js",
"bs-dependencies": [
"@ahrefs/bs-atdgen-codec-runtime",
"@ahrefs/bs-emotion",
"bs-fetch",
"bs-webapi",
"dialomorphic",
"reason-react",
"reason-relay",
"reason-promise",
"re-classnames",
"shared"
],
"pinned-dependencies": [
"dialomorphic",
"shared"
],
"ppx-flags": [
"reason-relay/ppx",
"@davesnx/styled-ppx/styled-ppx"
],
"warnings": {
"number": "-44",
"error": "+101"
},
"refmt": 3,
"gentypeconfig": {
"module": "es6",
"language": "typescript",
"shims": {
"ReasonRelay": "ReasonRelayShim"
},
"debug": {
"all": false,
"basic": false
}
}
}
More discussion in #520
Update: after @wokalski's changes in #520, there is only the case of circular dependencies, using require
. And these can be avoided with a simple reccommendation:
- If a file uses
@genType.import
for a value, avoid exporting things wiht@genType
in the same file.
By doing the above, no require
's will be generated.
Really happy to finally be able to close this issue, and have a simple recommendation to avoid require
's and circular dependencies in all back-ends.
Thanks @wokalski