packages feed

aeson-typescript 0.6.0.0 → 0.6.1.0

raw patch · 8 files changed

+100/−44 lines, 8 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ # Change log +## Unreleased++## 0.6.1.0++* Fix a bug which caused enum formatting mode to turn off when multiple declarations were provided (#41)+* Fix some mismatch issues where an enum value doesn't match the desired string.  ## 0.6.0.0 
README.md view
@@ -114,6 +114,6 @@  # See also -If you want a much more opinionated web framework for generating APIs, check out [servant](http://haskell-servant.readthedocs.io/en/stable/). (Although it doesn't seem to support TypeScript client generation at the moment.)+If you want a more opinionated web framework for generating APIs, check out [servant](http://haskell-servant.readthedocs.io/en/stable/). If you use Servant, you may enjoy [servant-typescript](https://github.com/codedownio/servant-typescript), which is based on `aeson-typescript`! This companion package also has the advantage of magically collecting all the types used in your API, so you don't have to list them out manually.  For another very powerful framework that can generate TypeScript client code based on an API specification, see [Swagger/OpenAPI](https://github.com/swagger-api/swagger-codegen).
aeson-typescript.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.1.+-- This file has been generated from package.yaml by hpack version 0.36.0. -- -- see: https://github.com/sol/hpack  name:           aeson-typescript-version:        0.6.0.0+version:        0.6.1.0 synopsis:       Generate TypeScript definition files from your ADTs description:    Please see the README on Github at <https://github.com/codedownio/aeson-typescript#readme> category:       Text, Web, JSON@@ -67,6 +67,7 @@   build-depends:       aeson     , base >=4.7 && <5+    , bytestring     , containers     , mtl     , string-interpolate
src/Data/Aeson/TypeScript/Formatting.hs view
@@ -2,7 +2,9 @@  module Data.Aeson.TypeScript.Formatting where +import Data.Aeson as A import Data.Aeson.TypeScript.Types+import qualified Data.ByteString.Lazy.Char8 as BL8 import Data.Function ((&)) import qualified Data.List as L import Data.Maybe@@ -23,15 +25,31 @@ formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVariables names maybeDoc) =   makeDocPrefix maybeDoc <> mainDeclaration   where-    mainDeclaration = case typeAlternativesFormat of+    mainDeclaration = case chooseTypeAlternativesFormat typeAlternativesFormat of       Enum -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnum} }|]-      EnumWithType -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnumWithType} }#{enumType}|]+        where+          alternativesEnum = T.intercalate ", " $ [toEnumName entry <> "=" <> entry | entry <- T.pack <$> names]+      EnumWithType -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name}Enum { #{alternativesEnumWithType} }#{enumType}|]+        where+          alternativesEnumWithType = T.intercalate ", " $ [toEnumName entry <> "=" <> entry | entry <- T.pack <$> names]+          enumType = [i|\n\ntype #{name} = keyof typeof #{typeNameModifier name}Enum;|] :: T.Text       TypeAlias -> [i|#{exportPrefix exportMode}type #{typeNameModifier name}#{getGenericBrackets genericVariables} = #{alternatives};|]+        where+          alternatives = T.intercalate " | " (fmap T.pack names) -    alternatives = T.intercalate " | " (fmap T.pack names)-    alternativesEnum = T.intercalate ", " $ [toEnumName entry | entry <- T.pack <$> names]-    alternativesEnumWithType = T.intercalate ", " $ [toEnumName entry <> "=" <> entry | entry <- T.pack <$> names]-    enumType = [i|\n\ntype #{name} = keyof typeof #{typeNameModifier name};|] :: T.Text+    -- Only allow certain formats if some checks pass+    chooseTypeAlternativesFormat Enum+      | all isDoubleQuotedString names = Enum+      | otherwise = TypeAlias+    chooseTypeAlternativesFormat EnumWithType+      | all isDoubleQuotedString names = EnumWithType+      | otherwise = TypeAlias+    chooseTypeAlternativesFormat x = x++    isDoubleQuotedString s = case A.eitherDecode (BL8.pack s) of+      Right (A.String _) -> True+      _ -> False+     toEnumName = T.replace "\"" ""  formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceName genericVariables (filter (not . isNoEmitTypeScriptField) -> members) maybeDoc) =@@ -41,6 +59,9 @@       ls = T.intercalate "\n" $ [indentTo numIndentSpaces (T.pack (formatTSField member <> ";")) | member <- members]       modifiedInterfaceName = (\(li, name) -> li <> interfaceNameModifier name) . splitAt 1 $ interfaceName +      formatTSField :: TSField -> String+      formatTSField (TSField optional name typ maybeDoc') = makeDocPrefix maybeDoc' <> [i|#{name}#{if optional then ("?" :: String) else ""}: #{typ}|]+ formatTSDeclaration _ (TSRawDeclaration text) = text  indentTo :: Int -> T.Text -> T.Text@@ -54,18 +75,16 @@ -- | Format a list of TypeScript declarations into a string, suitable for putting directly into a @.d.ts@ file. formatTSDeclarations' :: FormattingOptions -> [TSDeclaration] -> String formatTSDeclarations' options allDeclarations =-  declarations & fmap (T.pack . formatTSDeclaration (validateFormattingOptions options declarations))+  declarations & fmap (T.pack . formatTSDeclaration options)                & T.intercalate "\n\n"                & T.unpack   where-    removedDeclarations = filter isNoEmitTypeScriptDeclaration allDeclarations--    getDeclarationName :: TSDeclaration -> Maybe String-    getDeclarationName (TSInterfaceDeclaration {..}) = Just interfaceName-    getDeclarationName (TSTypeAlternatives {..}) = Just typeName-    _ = Nothing--    removedDeclarationNames = mapMaybe getDeclarationName removedDeclarations+    removedDeclarationNames = mapMaybe getDeclarationName (filter isNoEmitTypeScriptDeclaration allDeclarations)+      where+        getDeclarationName :: TSDeclaration -> Maybe String+        getDeclarationName (TSInterfaceDeclaration {..}) = Just interfaceName+        getDeclarationName (TSTypeAlternatives {..}) = Just typeName+        getDeclarationName _ = Nothing      removeReferencesToRemovedNames :: [String] -> TSDeclaration -> TSDeclaration     removeReferencesToRemovedNames removedNames decl@(TSTypeAlternatives {..}) = decl { alternativeTypes = [x | x <- alternativeTypes, not (x `L.elem` removedNames)] }@@ -75,23 +94,6 @@                  & filter (not . isNoEmitTypeScriptDeclaration)                  & fmap (removeReferencesToRemovedNames removedDeclarationNames) -validateFormattingOptions :: FormattingOptions -> [TSDeclaration] -> FormattingOptions-validateFormattingOptions options@FormattingOptions{..} decls-  | typeAlternativesFormat == Enum && isPlainSumType decls = options-  | typeAlternativesFormat == EnumWithType && isPlainSumType decls = options { typeNameModifier = flip (<>) "Enum" }-  | otherwise = options { typeAlternativesFormat = TypeAlias }-  where-    isInterface :: TSDeclaration -> Bool-    isInterface TSInterfaceDeclaration{} = True-    isInterface _ = False--    -- Plain sum types have only one declaration with multiple alternatives-    -- Units (data U = U) contain two declarations, and thus are invalid-    isPlainSumType ds = (not . any isInterface $ ds) && length ds == 1--formatTSField :: TSField -> String-formatTSField (TSField optional name typ maybeDoc) = makeDocPrefix maybeDoc <> [i|#{name}#{if optional then ("?" :: String) else ""}: #{typ}|]- makeDocPrefix :: Maybe String -> String makeDocPrefix maybeDoc = case maybeDoc of   Nothing -> ""@@ -109,9 +111,11 @@ noEmitTypeScriptAnnotation :: String noEmitTypeScriptAnnotation = "@no-emit-typescript" +isNoEmitTypeScriptField :: TSField -> Bool isNoEmitTypeScriptField (TSField {fieldDoc=(Just doc)}) = noEmitTypeScriptAnnotation `L.isInfixOf` doc isNoEmitTypeScriptField _ = False +isNoEmitTypeScriptDeclaration :: TSDeclaration -> Bool isNoEmitTypeScriptDeclaration (TSInterfaceDeclaration {interfaceDoc=(Just doc)}) = noEmitTypeScriptAnnotation `L.isInfixOf` doc isNoEmitTypeScriptDeclaration (TSTypeAlternatives {typeDoc=(Just doc)}) = noEmitTypeScriptAnnotation `L.isInfixOf` doc isNoEmitTypeScriptDeclaration _ = False
test/Formatting.hs view
@@ -4,7 +4,7 @@ module Formatting (tests) where  import Control.Exception-import Data.Aeson (defaultOptions)+import Data.Aeson (SumEncoding(UntaggedValue), defaultOptions, sumEncoding, tagSingleConstructors) import Data.Aeson.TypeScript.TH import Data.Proxy import Data.String.Interpolate@@ -14,6 +14,17 @@ data D = S | F deriving (Eq, Show) $(deriveTypeScript defaultOptions ''D) +data D2 = S2 | F2 deriving (Eq, Show)+$(deriveTypeScript defaultOptions ''D2)++-- A.encode U --> "[]"+data Unit = U deriving (Eq, Show)+$(deriveTypeScript defaultOptions ''Unit)++-- A.encode UTagSingle --> "\"UTagSingle\""+data UnitTagSingle = UTagSingle deriving (Eq, Show)+$(deriveTypeScript (defaultOptions { tagSingleConstructors = True, sumEncoding = UntaggedValue }) ''UnitTagSingle)+ data PrimeInType' = PrimeInType $(deriveTypeScript defaultOptions ''PrimeInType') @@ -47,15 +58,35 @@         formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @D Proxy) `shouldBe`           [i|type D = "S" | "F";|] -    describe "and the Enum format option is set" $+    describe "and the Enum format option is set" $ do       it "should generate a TS Enum" $         formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @D Proxy) `shouldBe`-          [i|enum D { S, F }|]+          [i|enum D { S="S", F="F" }|] -    describe "and the EnumWithType format option is set" $+      it "should generate a TS Enum with multiple" $+        formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @D Proxy <> getTypeScriptDeclarations @D2 Proxy) `shouldBe`+          [__i|enum D { S="S", F="F" }++               enum D2 { S2="S2", F2="F2" }|]++      it "should generate a normal type from Unit, singe tagSingleConstructors=False by default" $+        formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @Unit Proxy) `shouldBe`+          [__i|type Unit = IU;++               type IU = void[];|]++      it "should generate a suitable enum from UnitTagSingle" $+        formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @UnitTagSingle Proxy) `shouldBe`+          [__i|enum UnitTagSingle { UTagSingle="UTagSingle" }|]++    describe "and the EnumWithType format option is set" $ do       it "should generate a TS Enum with a type declaration" $         formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = EnumWithType }) (getTypeScriptDeclarations @D Proxy) `shouldBe`           [i|enum DEnum { S="S", F="F" }\n\ntype D = keyof typeof DEnum;|]++      it "should also work for UnitTagSingle" $+        formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = EnumWithType }) (getTypeScriptDeclarations @UnitTagSingle Proxy) `shouldBe`+          [i|enum UnitTagSingleEnum { UTagSingle="UTagSingle" }\n\ntype UnitTagSingle = keyof typeof UnitTagSingleEnum;|]    describe "when the name has an apostrophe" $ do     describe "in the type" $ do
test/TestBoilerplate.hs view
@@ -117,7 +117,7 @@                               , (getTypeScriptType (Proxy :: Proxy Optional), A.encode (Optional { optionalInt = Just 1 }))                                , (getTypeScriptType (Proxy :: Proxy AesonTypes), A.encode (AesonTypes {-                                                                                             aesonValue = A.object [("foo" :: A.Key, A.Number 42)]+                                                                                             aesonValue = A.object [("foo" :: AesonKey, A.Number 42)]                                                                                              , aesonObject = aesonFromList [("foo", A.Number 42)]                                                                                              })) 
test/Util.hs view
@@ -70,12 +70,19 @@   writeFile tsFile contents    tsc <- getTSC-  (code, output, _err) <- readProcessWithExitCode tsc ["--strict", "--noEmit", "--skipLibCheck", "--traceResolution", "--noResolve", tsFile] ""+  (code, sout, serr) <- readProcessWithExitCode tsc ["--strict", "--noEmit", "--skipLibCheck", "--traceResolution", "--noResolve", tsFile] "" -  when (code /= ExitSuccess) $ do-    error [i|TSC check failed: #{output}. File contents were\n\n#{contents}|]+  when (code /= ExitSuccess) $+    error [__i|TSC check failed.+               File contents:+               #{contents} -  return ()+               Stdout:+               #{sout}++               Stderr:+               #{serr}+              |]   ensureTSCExists :: IO ()
test/Util/Aeson.hs view
@@ -8,8 +8,15 @@  aesonFromList :: [(K.Key, v)] -> KM.KeyMap v aesonFromList = KM.fromList++type AesonKey = K.Key #else+import Data.Aeson as A import Data.HashMap.Strict as HM+import Data.Text as T +aesonFromList :: [(T.Text, Value)] -> HM.HashMap Text A.Value aesonFromList = HM.fromList++type AesonKey = Text #endif