Skip to content

Commit 792afdd

Browse files
committed
Add docs about imports and exports
1 parent 949b59f commit 792afdd

File tree

1 file changed

+111
-0
lines changed

1 file changed

+111
-0
lines changed

docs/js_of_ocaml.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,117 @@ let () =
153153
> * [JavaScript compilation in dune](https://dune.readthedocs.io/en/latest/jsoo.html)
154154
> * [`gen_js_api`](https://github.com/LexiFi/gen_js_api/blob/master/INSTALL_AND_USE.md)
155155
156+
## Handling `import` and `export`
157+
158+
To work with multiple files and packages, ts2ocaml has some conventions around the name of the generated OCaml source codes.
159+
160+
1. If not known, ts2ocaml computes the JS module name of the input `.d.ts` file by [heuristics](#how-the-heuristics-work).
161+
2. ts2ocaml converts the JS module name to a OCaml module name by the followings:
162+
- Removes `@` at the top of the module name
163+
- Replaces `/` with `__`
164+
- Replaces any other signs (such as `-`) to `_`
165+
3. ts2ocaml uses the OCaml module name as the output file name.
166+
167+
### How the heuristics work
168+
169+
* If the filename is equal to `types` or `typings` of `package.json`, then ts2ocaml will use the package name itself.
170+
- input: `node_modules/typescript/lib/typescript.d.ts`
171+
- `package.json`: `"typings": "./lib/typescript.d.ts",`
172+
- `getJsModuleName`: `typescript`
173+
- output file: `typescript.mli`
174+
* If the filename is present in `exports` of `package.json`, then ts2ocaml will combine the package name and the exported module name.
175+
- input: `node_modules/@angular/common/http/http.d.ts`
176+
- `package.json`: `"exports": { .., "./http": { "types": "./http/http.d.ts", .. }, .. }`
177+
- `getJsModuleName`: `@angualr/common/http`
178+
- output file: `angular__common__http.mli`
179+
* Otherwise, ts2ocaml uses a heuristic module name: it will combine the package name and the filename. `index.d.ts` is handled specially.
180+
- input: `node_modules/cassandra-driver/lib/auth/index.d.ts`
181+
- `getJsModuleName`: `cassandra-driver/auth`
182+
- output file: `cassandra_driver__auth.mli`
183+
- if `package.json` is not present, the package name is also inferred heuristically from the filename.
184+
185+
### How the `import` statements are translated
186+
187+
* `import` of another package from `node_modules` will be converted to an `open` statement or a module alias.
188+
- The OCaml module name of the imported package is computed by the step 2 of [the above](#handling-import-and-export).
189+
190+
```typescript
191+
// node_modules/@types/react/index.d.ts
192+
import * as CSS from 'csstype';
193+
import { Interaction as SchedulerInteraction } from 'scheduler/tracing';
194+
...
195+
```
196+
```ocaml
197+
(* react.mli *)
198+
module CSS = Csstype.Export
199+
module SchedulerInteraction = Scheduler__tracing.Export.Interaction
200+
...
201+
```
202+
203+
* `import` of relative path will be converted to an `open` statement or a module alias.
204+
- The OCaml module name of the imported file will also be inferred by [heuristics](#how-the-heuristics-work).
205+
```typescript
206+
// node_modules/cassandra-driver/index.d.ts
207+
import { auth } from './lib/auth';
208+
```
209+
210+
```ocaml
211+
(* cassandra_driver.mli *)
212+
module Auth = Cassandra_driver__auth.Export.Auth
213+
```
214+
215+
```typescript
216+
// node_modules/cassandra-driver/lib/mapping/index.d.ts
217+
import { Client } from '../../';
218+
```
219+
220+
```ocaml
221+
(* cassandra_driver__mapping.mli *)
222+
module Client = Cassandra_driver.Export.Client
223+
```
224+
225+
* Indirect `import` using identifiers is not yet be supported.
226+
227+
```typescript
228+
import { types } from './lib/types';
229+
import Uuid = types.Uuid; // we should be able to convert this to `module Uuid = Type.Uuid`, but not yet
230+
```
231+
232+
* Direct `export` of an external module **will not be supported**.
233+
234+
```typescript
235+
export { someFunction } from './lib/functions'; // this is VERY hard to do in OCaml!
236+
```
237+
238+
### How the `export` statements are translated
239+
240+
ts2ocaml will create a module named `Export` to represent the exported definitions.
241+
242+
* If an export assignment `export = Something` is used, the `Export` module will be an alias to the `Something` module.
243+
```ocaml
244+
(* export = Something *)
245+
module Export = Something
246+
```
247+
248+
* If ES6 exports `export interface Foo` or `export { Bar }` are used, the `Export` module will contain the exported modules.
249+
```ocaml
250+
module Export : sig
251+
(* export interface Foo *)
252+
module Foo = Foo
253+
(* export { Bar } *)
254+
module Bar = Bar
255+
(* export { Baz as Buzz } *)
256+
module Buzz = Baz
257+
end
258+
```
259+
260+
This is why you are advised to use the generated bindings with the following:
261+
262+
```ocaml
263+
(* This is analogous to `import * as TypeScript from "typescript";` *)
264+
module TypeScript = Typescript.Export
265+
```
266+
156267
# Usage
157268

158269
```bash

0 commit comments

Comments
 (0)