Skip to content

Commit 7ca27db

Browse files
authored
Merge pull request #37 from ocsigen/follow-relative-references
Follow relative references
2 parents 5a8e1ed + 18bf19f commit 7ca27db

File tree

14 files changed

+365
-166
lines changed

14 files changed

+365
-166
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
- Add an option `--merge` to merge the input definition files to one binding.
9+
- This is a breaking change; previously `--merge` was the default behavior.
10+
- Add an option `--follow-relative-references` to generate bindings for relevant files at once.
811

912
## [1.1.0] - 2021-11-24
1013
- Upgrade and fixed TypeScript version to >= 4.5.2 < 4.6.0.

build.fsx

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,22 +123,25 @@ module Test =
123123

124124
let packages = [
125125
// "full" package involving a lot of inheritance
126-
"full", !! "node_modules/typescript/lib/typescript.d.ts";
126+
"full", !! "node_modules/typescript/lib/typescript.d.ts", [];
127127

128128
// "full" packages involving a lot of dependencies (which includes some "safe" packages)
129-
"safe", !! "node_modules/@types/scheduler/tracing.d.ts";
130-
"full", !! "node_modules/csstype/index.d.ts";
131-
"safe", !! "node_modules/@types/prop-types/index.d.ts";
132-
"full", !! "node_modules/@types/react/index.d.ts" ++ "node_modules/@types/react/global.d.ts";
133-
"full", !! "node_modules/@types/react-modal/index.d.ts";
129+
"safe", !! "node_modules/@types/scheduler/tracing.d.ts", [];
130+
"full", !! "node_modules/csstype/index.d.ts", [];
131+
"safe", !! "node_modules/@types/prop-types/index.d.ts", [];
132+
"full", !! "node_modules/@types/react/index.d.ts" ++ "node_modules/@types/react/global.d.ts", [];
133+
"full", !! "node_modules/@types/react-modal/index.d.ts", [];
134134

135135
// "safe" package which depends on another "safe" package
136-
"safe", !! "node_modules/@types/yargs-parser/index.d.ts";
137-
"safe", !! "node_modules/@types/yargs/index.d.ts";
136+
"safe", !! "node_modules/@types/yargs-parser/index.d.ts", [];
137+
"safe", !! "node_modules/@types/yargs/index.d.ts", [];
138138
]
139139

140-
for preset, package in packages do
141-
ts2ocaml ["jsoo"; "--verbose"; "--nowarn"; $"--preset {preset}"; $"-o {outputDir}"] package
140+
for preset, package, additionalOptions in packages do
141+
ts2ocaml
142+
(["jsoo"; "--verbose"; "--nowarn"; "--follow-relative-references";
143+
$"--preset {preset}"; $"-o {outputDir}"] @ additionalOptions)
144+
package
142145

143146
let build () =
144147
for file in outputDir |> Shell.copyRecursiveTo true srcDir do

docs/common_options.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,28 @@ You should use `camelCase` key names. For example, if you want to set `--output-
1515
}
1616
```
1717

18+
## Parser Options
19+
20+
### `--follow-relative-references`
21+
22+
Follow and parse relative imports and file references.
23+
24+
If this option is enabled, ts2ocaml tries to find relative `import` statements or `/// <referench path="...">` directives, and add the referenced files to the input files. ts2ocaml repeats this until no new file is added.
25+
26+
> ts2ocaml will not follow package `import` statements and `/// <reference types="...">` directives.
27+
28+
## Output Options
29+
30+
### `--merge`
31+
32+
Merge multiple input definition files to one binding.
33+
34+
By default, ts2ocaml generates multiple bindings if multiple inputs are given.
35+
36+
If this option is enabled, ts2ocaml bundles all the input files and generates one single binding.
37+
38+
> This option may break relative imports. Use it with care.
39+
1840
## Logging Options
1941

2042
### `--verbose`

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"@types/semver": "^7.3.6",
3737
"@types/yargs": "^17.0.0",
3838
"monaco-editor": "^0.24.0",
39+
"react-player": "^2.9.0",
3940
"webpack": "^5.53.0",
4041
"webpack-cli": "^4.8.0",
4142
"webpack-dev-server": "^4.2.1"

src/Common.fs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,24 @@ module Common
44
type GlobalOptions =
55
abstract verbose: bool with get
66
abstract nowarn: bool with get
7+
abstract merge: bool with get, set
8+
abstract followRelativeReferences: bool with get, set
79

810
module GlobalOptions =
911
open Fable.Core.JsInterop
1012

1113
let register (yargs: Yargs.Argv<_>) =
1214
yargs
15+
.group(!^ResizeArray["config"], "General Options:")
16+
.describe(!^"config", "Specify the path to a ts2ocaml configuration JSON file.")
17+
.group(!^ResizeArray["follow-relative-references"], "Parser Options:")
18+
.addFlag("follow-relative-references", (fun (o: GlobalOptions) -> o.followRelativeReferences), descr="Follow and parse relative imports and file references.")
19+
.group(!^ResizeArray["merge"], "Output Options:")
20+
.addFlag("merge", (fun (o: GlobalOptions) -> o.merge), descr="Merge multiple input definition files to one binding", defaultValue=false)
1321
.group(!^ResizeArray["verbose"; "nowarn"], "Logging Options:")
1422
.addFlag("verbose", (fun (o: GlobalOptions) -> o.verbose), descr="Show verbose log")
1523
.addFlag("nowarn", (fun (o: GlobalOptions) -> o.nowarn), descr="Do not show warnings")
16-
.group(
17-
!^ResizeArray[
18-
"config"
19-
],
20-
"General Options:"
21-
)
22-
.describe(!^"config", "Specify the path to a ts2ocaml configuration JSON file.")
24+
2325

2426
module Log =
2527
let tracef (opt: 'Options) fmt : _ when 'Options :> GlobalOptions =

src/Extensions.fs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,15 @@ module Path =
141141
else Node.path.dirname(fromPath)
142142
Node.path.relative(fromPath, toPath)
143143

144+
let dirname (path: string) : string =
145+
Node.path.dirname(path)
146+
147+
let join (paths: string list) : string =
148+
Node.path.join(Array.ofList paths)
149+
150+
let separator =
151+
Node.path.sep
152+
144153
open Yargs
145154

146155
type Argv<'T> with

src/JsHelper.fs

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module Node = Node.Api
77
let getPackageJsonPath (exampleFilePath: string) =
88
let parts =
99
exampleFilePath
10-
|> String.split Node.path.sep
10+
|> String.split Path.separator
1111
|> List.ofArray
1212
match parts |> List.tryFindIndexBack ((=) "node_modules") with
1313
| None -> None
@@ -21,11 +21,10 @@ let getPackageJsonPath (exampleFilePath: string) =
2121
| packageName :: _ -> [packageName]
2222
| _ -> failwith "impossible_getPackageJsonPath_root"
2323
let path =
24-
prefix @ packageName @ ["package.json"] |> String.concat Node.path.sep
24+
prefix @ packageName @ ["package.json"] |> String.concat Path.separator
2525

2626
if not <| Node.fs.existsSync(!^path) then None
27-
else if Node.path.isAbsolute(path) then Some path
28-
else Some (Node.path.resolve(path))
27+
else Some (Path.absolute path)
2928

3029
type IPackageExportItem =
3130
[<EmitIndexer>]
@@ -48,7 +47,7 @@ let getPackageInfo (exampleFilePath: string) : Syntax.PackageInfo option =
4847
| Some path ->
4948
let p = getPackageJson path
5049

51-
let rootPath = Node.path.dirname(path)
50+
let rootPath = Path.dirname path
5251

5352
let name =
5453
if p.name.StartsWith("@types/") then
@@ -73,20 +72,20 @@ let getPackageInfo (exampleFilePath: string) : Syntax.PackageInfo option =
7372
let indexFile =
7473
match Option.orElse p.types p.typings, exports |> List.tryFind (fst >> (=) ".") with
7574
| None, None ->
76-
let index = Node.path.join(rootPath, "index.d.ts")
75+
let index = Path.join [rootPath; "index.d.ts"]
7776
if not <| Node.fs.existsSync(!^index) then None
7877
else
79-
Node.path.relative(Node.``process``.cwd(), index) |> Node.path.normalize |> Some
78+
Path.relative index |> Node.path.normalize |> Some
8079
| Some typings, _
8180
| None, Some (_, typings) ->
82-
Node.path.relative(Node.``process``.cwd(), Node.path.join(rootPath, typings)) |> Node.path.normalize |> Some
81+
Path.join [rootPath; typings] |> Path.relative |> Node.path.normalize |> Some
8382

8483
let exports =
8584
exports
8685
|> List.filter (fst >> (<>) ".")
8786
|> List.map (fun (k, v) ->
8887
{| submodule = k;
89-
file = Node.path.relative(Node.``process``.cwd(), Node.path.join(rootPath, v)) |> Node.path.normalize |})
88+
file = Path.join [rootPath; v] |> Path.relative |> Node.path.normalize |})
9089

9190
Some {
9291
name = name
@@ -106,6 +105,9 @@ module InferenceResult =
106105
let unwrap defaultValue = function
107106
| Valid s | Heuristic s -> s
108107
| Unknown -> defaultValue
108+
let tryUnwrap = function
109+
| Valid s | Heuristic s -> Some s
110+
| Unknown -> None
109111

110112
let inferPackageInfoFromFileName (sourceFile: Path.Relative) : {| name: string; isDefinitelyTyped: bool; rest: string list |} option =
111113
let parts =
@@ -143,17 +145,17 @@ let getJsModuleName (info: Syntax.PackageInfo option) (sourceFile: Path.Relative
143145
// make it relative to the package root directory
144146
let relativePath = Path.diff info.rootPath (Path.absolute sourceFile)
145147
if info.isDefinitelyTyped then
146-
Node.path.join(info.name, stripExtension relativePath) |> Valid
148+
Path.join [info.name; stripExtension relativePath] |> Valid
147149
else
148150
match info.exports |> List.tryFind (fun x -> x.file = sourceFile) with
149-
| Some export -> Node.path.join(info.name, export.submodule) |> Valid
151+
| Some export -> Path.join [info.name; export.submodule] |> Valid
150152
| None -> // heuristic
151153
let submodule =
152154
relativePath
153155
|> String.splitThenRemoveEmptyEntries "/"
154156
|> List.ofArray
155157
|> getSubmodule
156-
Node.path.join(info.name, submodule) |> Heuristic
158+
Path.join [info.name; submodule] |> Heuristic
157159
| None ->
158160
match inferPackageInfoFromFileName sourceFile with
159161
| None -> Unknown
@@ -178,13 +180,21 @@ let deriveModuleName (info: Syntax.PackageInfo option) (srcs: Path.Relative list
178180
| [] -> failwith "impossible_deriveModuleName"
179181
| [src] -> getJsModuleName info src
180182
| srcs ->
181-
let names =
182-
srcs
183-
|> List.choose inferPackageInfoFromFileName
184-
|> List.map (fun info -> info.name)
185-
|> List.groupBy id
186-
|> List.map (fun (name, xs) -> name, List.length xs)
187-
names |> List.maxBy (fun (_, count) -> count) |> fst |> Heuristic
183+
let fallback () =
184+
let names =
185+
srcs
186+
|> List.choose inferPackageInfoFromFileName
187+
|> List.map (fun info -> info.name)
188+
|> List.groupBy id
189+
|> List.map (fun (name, xs) -> name, List.length xs)
190+
names |> List.maxBy (fun (_, count) -> count) |> fst |> Heuristic
191+
match info with
192+
| None -> fallback ()
193+
| Some info ->
194+
if info.indexFile |> Option.exists (fun index -> srcs |> List.exists ((=) index)) then
195+
Valid info.name
196+
else
197+
fallback ()
188198

189199
let deriveOutputFileName
190200
(opts: #GlobalOptions) (info: Syntax.PackageInfo option) (srcs: Path.Relative list)
@@ -202,8 +212,8 @@ let deriveOutputFileName
202212
let resolveRelativeImportPath (info: Syntax.PackageInfo option) (currentFile: Path.Relative) (path: string) =
203213
if path.StartsWith(".") then
204214
let targetPath =
205-
let path = Node.path.join(Node.path.dirname(currentFile), path)
206-
if not <| path.EndsWith(".ts") then Node.path.join(path, "index.d.ts")
215+
let path = Path.join [Path.dirname currentFile; path]
216+
if not <| path.EndsWith(".ts") then Path.join [path; "index.d.ts"]
207217
else path
208218
getJsModuleName info targetPath
209219
else

0 commit comments

Comments
 (0)