Skip to content

Commit a783b2b

Browse files
authored
Merge pull request #1 from thought2/dev2
dev -> main
2 parents bbf9f98 + 36bfe4f commit a783b2b

File tree

15 files changed

+443
-895
lines changed

15 files changed

+443
-895
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
![example workflow](https://github.com/thought2/purescript-ts-bridge/actions/workflows/ci.yml/badge.svg)
44

5-
A **PureScript** library for type class based **TypeScript** type generation (`.d.ts` files).
5+
Call your PureScript code from TypeScript.
66

77
## Documentation
88

@@ -28,13 +28,17 @@ spago install ts-bridge
2828
- Polymorphic types optimized for best type inference in TS
2929
- Tried and tested in production
3030

31+
## How it works
32+
33+
Via type classes a set of types are defined that you'd like to support to export to TypeScript.
34+
The library creates an extra CLI entry point to your app, which generates types for given values of those types. They get written to the file system in the form of TypeScript type declaration (`.d.ts`) files.
3135

3236
## Similar Projects
3337

3438
- [purescript-tsd-gen](https://github.com/minoki/purescript-tsd-gen)
3539
This project follows a different approach for type generation. It extracts TypeScript types from the PureScript CST. As such the process is more automated but less customizable.
3640

37-
# Support
41+
## Support
3842

3943
If you find a bug or have a feature idea feel free to make a PR or file an issue.
4044

docs/getting-started.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ And add the following imports are needed for this example:
3636
```hs
3737
import Prelude
3838

39+
import DTS as DTS
3940
import Data.Either (Either)
4041
import Effect (Effect)
4142
import TsBridge as TSB
@@ -47,7 +48,7 @@ Then you should define a typeclass that looks like this:
4748

4849
```hs
4950
class TsBridge (a :: Type) where
50-
tsBridge :: Proxy a -> TSB.StandaloneTsType
51+
tsBridge :: Proxy a -> TSB.TsBridgeM DTS.TsType
5152
```
5253
</li>
5354
<li>
@@ -125,7 +126,7 @@ The same for the values that we want to expose. However, we're making use of
125126
record puns to eliminate the risk of spelling mistakes:
126127

127128
```hs
128-
myTsProgram :: Either TSB.Error TSB.TsProgram
129+
myTsProgram :: Either TSB.AppError DTS.TsProgram
129130
myTsProgram =
130131
TSB.tsProgram
131132
[ TSB.tsModuleFile "Sample"

justfile

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
set shell := ["bash", "-c"]
22

3+
allowed_warnings := "ImplicitQualifiedImport"
4+
35
build:
4-
spago build --purs-args '--stash --censor-lib --censor-codes=ImplicitQualifiedImport'
6+
spago build --purs-args '--stash --censor-lib --censor-codes={{allowed_warnings}}'
57

68
build-strict:
7-
spago build --purs-args '--stash --censor-lib --strict --censor-codes=ImplicitQualifiedImport'
9+
spago build --purs-args '--stash --censor-lib --strict --censor-codes={{allowed_warnings}}'
10+
11+
clean:
12+
rm -rf .spago output .psa-stash
813

914
format:
1015
purs-tidy format-in-place 'src/**/*.purs'
@@ -26,7 +31,7 @@ check-spell:
2631
yarn run cspell "docs/**/*.md" || true
2732
yarn run cspell "README.md" || true
2833

29-
ci: check-format gen-docs check-git-clean build-strict test
34+
ci: clean check-format gen-docs build-strict test check-git-clean
3035

3136
check-git-clean:
3237
[ -z "$(git status --porcelain)" ]

packages.dhall

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,22 @@ let upstream =
22
https://github.com/purescript/package-sets/releases/download/psc-0.15.4-20220901/packages.dhall
33
sha256:f1531b29c21ac437ffe5666c1b6cc76f0a9c29d3c9d107ff047aa2567744994f
44

5-
in upstream
5+
in upstream
6+
7+
8+
with dts =
9+
{ dependencies =
10+
[ "arrays"
11+
, "console"
12+
, "effect"
13+
, "maybe"
14+
, "newtype"
15+
, "ordered-collections"
16+
, "ordered-set"
17+
, "prelude"
18+
, "tuples"
19+
]
20+
, repo = "https://github.com/thought2/purescript-dts.git"
21+
, version = "ea900ff5108f6dec7c7fbab14cb6c09d036c0d63"
22+
}
23+

spago.dhall

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
, "aff-promise"
55
, "arrays"
66
, "console"
7+
, "dts"
78
, "effect"
89
, "either"
910
, "foldable-traversable"

src/TsBridge.purs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ module TsBridge
44

55
import TsBridge.Cli as Exp
66
import TsBridge.Core as Exp
7-
import TsBridge.DTS as Exp
87
import TsBridge.Monad as Exp
9-
import TsBridge.Print as Exp
10-
import TsBridge.DefaultImpls as Exp
8+
import TsBridge.DefaultImpls as Exp
9+
import TsBridge.Types as Exp

src/TsBridge/Cli.purs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ module TsBridge.Cli (mkTypeGenCli) where
55

66
import Prelude
77

8+
import DTS (TsProgram)
9+
import DTS.Print (Path(..), TsSource(..), printTsProgram)
810
import Data.Either (Either(..))
911
import Data.Foldable (fold, for_)
1012
import Data.Map as Map
@@ -21,8 +23,7 @@ import Node.Path (dirname)
2123
import Node.Process as Process
2224
import Options.Applicative (help, helper, info, long, metavar, strOption, value, (<**>))
2325
import Options.Applicative as O
24-
import TsBridge.DTS (TsProgram, Error, printError)
25-
import TsBridge.Print (Path(..), TsSource(..), printTsProgram)
26+
import TsBridge.Types (AppError, printError)
2627

2728
-------------------------------------------------------------------------------
2829
-- Types
@@ -58,14 +59,18 @@ parserInfoTsBridgeCliOpts = info (parserTsBridgeCliOpts <**> helper)
5859
-------------------------------------------------------------------------------
5960
-- App
6061
-------------------------------------------------------------------------------
61-
mkTypeGenCliAff :: Either Error TsProgram -> Aff Unit
62+
mkTypeGenCliAff :: Either AppError TsProgram -> Aff Unit
6263
mkTypeGenCliAff eitherTsProg = do
6364
cliOpts <- liftEffect $ O.execParser parserInfoTsBridgeCliOpts
6465

6566
case eitherTsProg of
6667
Left err -> do
67-
log $ printError err
68-
liftEffect $ Process.exit 0
68+
log ""
69+
log "Cannot generate TypeScript Code. The following error happened:"
70+
log ""
71+
log $ show $ printError err
72+
log ""
73+
liftEffect $ Process.exit 1
6974
Right tsProg -> writeTsProgramToDisk cliOpts tsProg
7075

7176
writeTsProgramToDisk :: TsBridgeCliOpts -> TsProgram -> Aff Unit
@@ -86,6 +91,6 @@ writeTsProgramToDisk cliOpts tsProg = do
8691

8792
-- | Given a `TsProgram` returns an effectful CLI that can be used as an entry
8893
-- | point for a type generator.
89-
mkTypeGenCli :: Either Error TsProgram -> Effect Unit
94+
mkTypeGenCli :: Either AppError TsProgram -> Effect Unit
9095
mkTypeGenCli tsProg = launchAff_ $ mkTypeGenCliAff tsProg
9196

src/TsBridge/Core.purs

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
module TsBridge.Core
2-
( StandaloneTsType
3-
, class TsBridgeBy
2+
( class TsBridgeBy
43
, class TsValues
54
, class TsValuesRL
65
, tsBridgeBy
@@ -15,8 +14,9 @@ module TsBridge.Core
1514

1615
import Prelude
1716

18-
import Control.Monad.Error.Class (class MonadError, catchError, throwError)
17+
import Control.Monad.Error.Class (throwError)
1918
import Control.Monad.Writer (listens, tell)
19+
import DTS as DTS
2020
import Data.Array as A
2121
import Data.Array as Array
2222
import Data.Either (Either)
@@ -32,16 +32,15 @@ import Prim.Row as Row
3232
import Prim.RowList (class RowToList, RowList)
3333
import Prim.RowList as RL
3434
import Safe.Coerce (coerce)
35-
import TsBridge.DTS (TsNameDraft)
36-
import TsBridge.DTS as DTS
3735
import TsBridge.Monad (Scope(..), TsBridgeAccum(..), TsBridgeM, runTsBridgeM)
36+
import TsBridge.Types (AppError(..), mapErr, mkName, mkPursModuleName, toTsName)
3837
import Type.Proxy (Proxy(..))
3938

4039
-- | A `StandaloneTsType` represents a TypeScript type with everything it needs
4140
-- | to be placed inside complete TS program: If the type references nominal
4241
-- | types from other modules, all information is contained that is needed to
4342
-- | render those references.
44-
type StandaloneTsType = TsBridgeM DTS.TsType
43+
--type StandaloneTsType = TsBridgeM DTS.TsType
4544

4645
-- | Type Class that is used by the type generator to recursively traverse
4746
-- | types.
@@ -64,16 +63,19 @@ type StandaloneTsType = TsBridgeM DTS.TsType
6463

6564
class TsBridgeBy :: Type -> Type -> Constraint
6665
class TsBridgeBy tok a where
67-
tsBridgeBy :: tok -> Proxy a -> StandaloneTsType
66+
tsBridgeBy :: tok -> Proxy a -> TsBridgeM DTS.TsType
6867

69-
tsModuleFile :: String -> Array (TsBridgeM (Array DTS.TsDeclaration)) -> Either DTS.Error (Array DTS.TsModuleFile)
70-
tsModuleFile n xs = do
71-
(xs' /\ TsBridgeAccum { typeDefs }) <- runTsBridgeM $ mapErr (DTS.AtModule n) $ join <$> sequence xs
68+
tsModuleFile :: String -> Array (TsBridgeM (Array DTS.TsDeclaration)) -> Either AppError (Array DTS.TsModuleFile)
69+
tsModuleFile n xs =
70+
mapErr (AtModule n)
71+
do
72+
-- TODO: check for duplicate identifiers
7273

73-
pure (typeDefs <> [ DTS.TsModuleFile (DTS.TsFilePath (n <> "/index.d.ts")) (DTS.TsModule xs') ])
74+
_ <- mkPursModuleName n
7475

75-
mapErr :: forall e m a. MonadError e m => (e -> e) -> m a -> m a
76-
mapErr f ma = catchError ma (f >>> throwError)
76+
(xs' /\ TsBridgeAccum { typeDefs }) <- runTsBridgeM $ join <$> sequence xs
77+
78+
pure (typeDefs <> [ DTS.TsModuleFile (DTS.TsFilePath (n <> "/index.d.ts")) (DTS.TsModule xs') ])
7779

7880
mergeModules :: Array DTS.TsModuleFile -> DTS.TsProgram
7981
mergeModules xs =
@@ -87,17 +89,19 @@ mergeModule (DTS.TsModule ds1) (DTS.TsModule ds2) =
8789
DTS.TsModule
8890
(Array.nub (ds1 <> ds2))
8991

90-
tsProgram :: Array (Either DTS.Error (Array DTS.TsModuleFile)) -> Either DTS.Error DTS.TsProgram
91-
tsProgram xs = xs # sequence <#> join >>> mergeModules
92+
tsProgram :: Array (Either AppError (Array DTS.TsModuleFile)) -> Either AppError DTS.TsProgram
93+
tsProgram xs =
94+
-- TODO: check for duplicate modules
95+
xs # sequence <#> join >>> mergeModules
9296

9397
-- | For rare cases where you want to export a type alias. References to this type
9498
-- | alias will be fully resolved in the generated code. So it is more practical
9599
-- | to use a newtype instead, which can be references by name.
96-
tsTypeAlias :: forall tok a. TsBridgeBy tok a => tok -> TsNameDraft -> Proxy a -> TsBridgeM (Array DTS.TsDeclaration)
97-
tsTypeAlias tok n x = ado
100+
tsTypeAlias :: forall tok a. TsBridgeBy tok a => tok -> String -> Proxy a -> TsBridgeM (Array DTS.TsDeclaration)
101+
tsTypeAlias tok aliasName x = ado
98102
x /\ scope <- listens (un TsBridgeAccum >>> _.scope >>> un Scope) t
99-
name <- DTS.mkTsName n
100-
in [ DTS.TsDeclTypeDef name DTS.Public (coerce scope.floating) x ]
103+
name <- mkName aliasName
104+
in [ DTS.TsDeclTypeDef (toTsName name) DTS.Public (coerce scope.floating) x ]
101105
where
102106
t = tsBridgeBy tok x
103107

@@ -117,23 +121,26 @@ tsOpaqueType tok x = do
117121
_ -> pure []
118122

119123
-- | Exports a single PureScript value to TypeScript. `tsValues` may be better choice.
120-
tsValue :: forall tok a. TsBridgeBy tok a => tok -> TsNameDraft -> a -> TsBridgeM (Array DTS.TsDeclaration)
124+
tsValue :: forall tok a. TsBridgeBy tok a => tok -> String -> a -> TsBridgeM (Array DTS.TsDeclaration)
121125
tsValue tok n _ = tsValue' tok n (Proxy :: _ a)
122126

123-
tsValue' :: forall tok a. TsBridgeBy tok a => tok -> TsNameDraft -> Proxy a -> TsBridgeM (Array DTS.TsDeclaration)
124-
tsValue' tok n _ = do
125-
let t = tsBridgeBy tok (Proxy :: _ a)
126-
x /\ scope <- listens (un TsBridgeAccum >>> _.scope >>> un Scope) t
127+
tsValue' :: forall tok a. TsBridgeBy tok a => tok -> String -> Proxy a -> TsBridgeM (Array DTS.TsDeclaration)
128+
tsValue' tok n _ =
129+
mapErr (AtValue n)
130+
do
131+
let t = tsBridgeBy tok (Proxy :: _ a)
132+
x /\ scope <- listens (un TsBridgeAccum >>> _.scope >>> un Scope) t
127133

128-
name <- DTS.mkTsName n
134+
name <- mkName n
129135

130-
when (OSet.length scope.floating /= 0)
131-
$ throwError
132-
$ DTS.ErrUnquantifiedTypeVariables
133-
$ (Set.fromFoldable :: Array _ -> _)
134-
$ OSet.toUnfoldable scope.floating
136+
when (OSet.length scope.floating /= 0)
137+
( throwError
138+
$ ErrUnquantifiedTypeVariables
139+
$ (Set.fromFoldable :: Array _ -> _)
140+
$ OSet.toUnfoldable scope.floating
141+
)
135142

136-
pure [ DTS.TsDeclValueDef name DTS.Public x ]
143+
pure [ DTS.TsDeclValueDef (toTsName name) DTS.Public x ]
137144

138145
--------------------------------------------------------------------------------
139146
-- class TsValues
@@ -169,4 +176,4 @@ instance
169176
tsValuesRL tok r _ = (<>) <$> head <*> tail
170177
where
171178
tail = tsValuesRL tok r (Proxy :: _ rl)
172-
head = tsValue' tok (DTS.TsName $ reflectSymbol (Proxy :: _ sym)) (Proxy :: _ a)
179+
head = tsValue' tok (reflectSymbol (Proxy :: _ sym)) (Proxy :: _ a)

0 commit comments

Comments
 (0)