From d1240447b7c82135bddb4998cb44eb9c60be855f Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 20 Dec 2020 03:47:04 -0700 Subject: [PATCH 001/208] Add TSRawDeclaration --- src/Data/Aeson/TypeScript/Formatting.hs | 2 ++ src/Data/Aeson/TypeScript/TH.hs | 2 +- src/Data/Aeson/TypeScript/Types.hs | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index d824cd1..ec9e26b 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -23,6 +23,8 @@ formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceNa }|] where lines = T.intercalate "\n" $ fmap T.pack [(replicate numIndentSpaces ' ') <> formatTSField member <> ";"| member <- members] modifiedInterfaceName = (\(i, name) -> i <> interfaceNameModifier name) . splitAt 1 $ interfaceName +formatTSDeclaration (FormattingOptions {..}) (TSRawDeclaration text) = text + -- | Format a list of TypeScript declarations into a string, suitable for putting directly into a @.d.ts@ file. formatTSDeclarations' :: FormattingOptions -> [TSDeclaration] -> String formatTSDeclarations' options declarations = T.unpack $ T.intercalate "\n\n" (fmap (T.pack . formatTSDeclaration options) declarations) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index e990428..d2856f6 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -104,7 +104,7 @@ module Data.Aeson.TypeScript.TH ( TypeScript(..), TSType(..), - TSDeclaration, + TSDeclaration(TSRawDeclaration), -- * Formatting declarations formatTSDeclarations, diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index e688854..3a5e667 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -73,6 +73,7 @@ data TSDeclaration = TSInterfaceDeclaration { interfaceName :: String | TSTypeAlternatives { typeName :: String , typeGenericVariables :: [String] , alternativeTypes :: [String]} + | TSRawDeclaration { text :: String } deriving (Show, Eq, Ord) data TSField = TSField { fieldOptional :: Bool From 41e5b2b60d8302164d9ce5556a9cbaec623aab48 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 20 Dec 2020 03:47:49 -0700 Subject: [PATCH 002/208] LookupTypes.hs turned out to be a bad idea --- aeson-typescript.cabal | 4 +- package.yaml | 1 - src/Data/Aeson/TypeScript/LookupTypes.hs | 54 ------------------------ 3 files changed, 1 insertion(+), 58 deletions(-) delete mode 100644 src/Data/Aeson/TypeScript/LookupTypes.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 347a5e0..48c531f 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -4,7 +4,7 @@ cabal-version: 1.12 -- -- see: https://github.com/sol/hpack -- --- hash: e1e74b37dded848299e515c32291f9f35f71998ce4f89f9a5622d634b197109d +-- hash: 40c4e69d4430c0e44e17b3fad921383e3b44cd50412fa222fea682b295570800 name: aeson-typescript version: 0.2.0.0 @@ -35,7 +35,6 @@ source-repository head library exposed-modules: Data.Aeson.TypeScript.TH - Data.Aeson.TypeScript.LookupTypes Data.Aeson.TypeScript.Recursive other-modules: Data.Aeson.TypeScript.Formatting @@ -75,7 +74,6 @@ test-suite aeson-typescript-test Util Data.Aeson.TypeScript.Formatting Data.Aeson.TypeScript.Instances - Data.Aeson.TypeScript.LookupTypes Data.Aeson.TypeScript.Recursive Data.Aeson.TypeScript.TH Data.Aeson.TypeScript.Types diff --git a/package.yaml b/package.yaml index 257ae88..89a2730 100644 --- a/package.yaml +++ b/package.yaml @@ -40,7 +40,6 @@ library: source-dirs: src exposed-modules: - Data.Aeson.TypeScript.TH - - Data.Aeson.TypeScript.LookupTypes - Data.Aeson.TypeScript.Recursive tests: diff --git a/src/Data/Aeson/TypeScript/LookupTypes.hs b/src/Data/Aeson/TypeScript/LookupTypes.hs deleted file mode 100644 index db6142a..0000000 --- a/src/Data/Aeson/TypeScript/LookupTypes.hs +++ /dev/null @@ -1,54 +0,0 @@ -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE TemplateHaskell #-} - - -module Data.Aeson.TypeScript.LookupTypes where - - -import Control.Monad -import qualified Data.Aeson as A -import Data.Aeson.TypeScript.Types -import Data.Maybe -import Data.Proxy -import qualified Data.Text as T -import Data.Void -import Language.Haskell.TH -import Language.Haskell.TH.Datatype - --- | Given a data type whose constructors all evaluate to string keys, and a type family, --- build a TS interface declaration whose keys are the GADT strings and whose values are the --- types of the value the constructor is *mapped to under the type family*. --- --- This is useful when the data type is a GADT (so the different constructors can be tagged --- with different types), and you want to produce a TypeScript "lookup type" --- corresponding to the type family. --- --- Will ignore Void values returned by the type family. --- -typeFamilyToLookupType :: Name -> Name -> Q Exp -typeFamilyToLookupType gadt typeFamily = do - DatatypeInfo {datatypeCons} <- reifyDatatype gadt - - members <- forM (fmap constructorName datatypeCons) $ \consName -> do - fieldName <- [|case A.toJSON $(conE consName) of A.String t -> T.unpack t; _ -> error "Constructor must serialize to string"|] - proxyExpr <- [|(Proxy :: Proxy ($(return $ PromotedT typeFamily) $(return $ PromotedT consName)))|] - return [|if TSType $(return proxyExpr) == TSType (Proxy :: Proxy Void) then Nothing else - Just (TSField False $(return fieldName) (getTypeScriptType $(return proxyExpr)))|] - - let name = nameBase typeFamily <> "Lookup" - - [|TSInterfaceDeclaration $(stringE name) [] (catMaybes $(listE members))|] - - --- | Given a data type like the one in 'typeFamilyToLookupType', generate a list of 'TSType' --- representing the types of (TypeFamily Constructor), for every Constructor of the data type. --- This is useful for gathering the TypeScript declarations for types in the image of the --- type family. -getImageOfConstructorsUnderTypeFamily :: Name -> Name -> Q Exp -getImageOfConstructorsUnderTypeFamily gadt typeFamily = do - DatatypeInfo {datatypeCons} <- reifyDatatype gadt - - exprs <- forM (fmap constructorName datatypeCons) $ \consName -> do - [|TSType (Proxy :: Proxy ($(return $ PromotedT typeFamily) $(return $ PromotedT consName)))|] - - return $ ListE exprs From 35e72077f135bac292165a4a40c20b1771165793 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 13 Jan 2021 23:10:37 -0800 Subject: [PATCH 003/208] Simplifying to try to address higher kind issues --- .hlint.yaml | 69 +++++++++++++ aeson-typescript.cabal | 2 + src/Data/Aeson/TypeScript/TH.hs | 154 +++-------------------------- src/Data/Aeson/TypeScript/Types.hs | 27 ++++- src/Data/Aeson/TypeScript/Util.hs | 122 +++++++++++++++++++++++ stack.yaml | 8 +- stack.yaml.lock | 23 ++++- 7 files changed, 259 insertions(+), 146 deletions(-) create mode 100644 .hlint.yaml create mode 100644 src/Data/Aeson/TypeScript/Util.hs diff --git a/.hlint.yaml b/.hlint.yaml new file mode 100644 index 0000000..274c361 --- /dev/null +++ b/.hlint.yaml @@ -0,0 +1,69 @@ +# HLint configuration file +# https://github.com/ndmitchell/hlint +########################## + +# This file contains a template configuration file, which is typically +# placed as .hlint.yaml in the root of your project + + +# Warnings currently triggered by your code +- ignore: {name: "Redundant bracket"} +- ignore: {name: "Redundant do"} +- ignore: {name: "Use if"} +- ignore: {name: "Move brackets to avoid $"} +- ignore: {name: "Use <$>"} +- ignore: {name: "Reduce duplication"} +- ignore: {name: "Redundant multi-way if"} + +# Specify additional command line arguments +# +# - arguments: [--color, -XQuasiQuotes] + + +# Control which extensions/flags/modules/functions can be used +# +# - extensions: +# - default: false # all extension are banned by default +# - name: [PatternGuards, ViewPatterns] # only these listed extensions can be used +# - {name: CPP, within: CrossPlatform} # CPP can only be used in a given module +# +# - flags: +# - {name: -w, within: []} # -w is allowed nowhere +# +# - modules: +# - {name: [Data.Set, Data.HashSet], as: Set} # if you import Data.Set qualified, it must be as 'Set' +# - {name: Control.Arrow, within: []} # Certain modules are banned entirely +# +# - functions: +# - {name: unsafePerformIO, within: []} # unsafePerformIO can only appear in no modules + + +# Add custom hints for this project +# +# Will suggest replacing "wibbleMany [myvar]" with "wibbleOne myvar" +# - error: {lhs: "wibbleMany [x]", rhs: wibbleOne x} + + +# Turn on hints that are off by default +# +# Ban "module X(module X) where", to require a real export list +# - warn: {name: Use explicit module export list} +# +# Replace a $ b $ c with a . b $ c +# - group: {name: dollar, enabled: true} +# +# Generalise map to fmap, ++ to <> +# - group: {name: generalise, enabled: true} + + +# Ignore some builtin hints +# - ignore: {name: Use let} +# - ignore: {name: Use const, within: SpecialModule} # Only within certain modules + + +# Define some custom infix operators +# - fixity: infixr 3 ~^#^~ + + +# To generate a suitable file for HLint do: +# $ hlint --default > .hlint.yaml diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 48c531f..b2d93e7 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -40,6 +40,7 @@ library Data.Aeson.TypeScript.Formatting Data.Aeson.TypeScript.Instances Data.Aeson.TypeScript.Types + Data.Aeson.TypeScript.Util Paths_aeson_typescript hs-source-dirs: src @@ -77,6 +78,7 @@ test-suite aeson-typescript-test Data.Aeson.TypeScript.Recursive Data.Aeson.TypeScript.TH Data.Aeson.TypeScript.Types + Data.Aeson.TypeScript.Util Paths_aeson_typescript hs-source-dirs: test diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index d2856f6..caa1d06 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE CPP, QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, ExistentialQuantification, FlexibleInstances, NamedFieldPuns, MultiWayIf, ViewPatterns #-} +{-# LANGUAGE CPP, QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, ExistentialQuantification, FlexibleInstances, NamedFieldPuns, MultiWayIf, ViewPatterns, PolyKinds #-} {-| Module: Data.Aeson.TypeScript.TH @@ -116,48 +116,25 @@ module Data.Aeson.TypeScript.TH ( HasJSONOptions(..), deriveJSONAndTypeScript, + T(..), + module Data.Aeson.TypeScript.Instances ) where -import Control.Monad import Data.Aeson as A import Data.Aeson.TH as A import Data.Aeson.TypeScript.Formatting -import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Types +import Data.Aeson.TypeScript.Instances () +import Data.Aeson.TypeScript.Util import qualified Data.Map as M import Data.Maybe import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString -import qualified Data.Text as T import Language.Haskell.TH hiding (stringE) import Language.Haskell.TH.Datatype -data T = T -data T1 = T1 -data T2 = T2 -data T3 = T3 -data T4 = T4 -data T5 = T5 -data T6 = T6 -data T7 = T7 -data T8 = T8 -data T9 = T9 -data T10 = T10 - -instance TypeScript T where getTypeScriptType _ = "T" -instance TypeScript T1 where getTypeScriptType _ = "T1" -instance TypeScript T2 where getTypeScriptType _ = "T2" -instance TypeScript T3 where getTypeScriptType _ = "T3" -instance TypeScript T4 where getTypeScriptType _ = "T4" -instance TypeScript T5 where getTypeScriptType _ = "T5" -instance TypeScript T6 where getTypeScriptType _ = "T6" -instance TypeScript T7 where getTypeScriptType _ = "T7" -instance TypeScript T8 where getTypeScriptType _ = "T8" -instance TypeScript T9 where getTypeScriptType _ = "T9" -instance TypeScript T10 where getTypeScriptType _ = "T10" - -- | Generates a 'TypeScript' instance declaration for the given data type. deriveTypeScript :: Options @@ -168,6 +145,8 @@ deriveTypeScript :: Options deriveTypeScript options name = do datatypeInfo@(DatatypeInfo {..}) <- reifyDatatype name + -- reportError [i|Reified: #{datatypeInfo}|] + assertExtensionsTurnedOn datatypeInfo let getFreeVariableName (SigT (VarT n) _kind) = Just n @@ -205,15 +184,6 @@ deriveTypeScript options name = do return $ fullyGenericInstance : otherInstances --- | For the fully generic instance, the parent types are the types inside the constructors -getGenericParentTypesExpression :: DatatypeInfo -> Q Exp -getGenericParentTypesExpression (DatatypeInfo {..}) = return $ ListE [AppE (ConE 'TSType) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) typ)) | typ <- types] - where types = mconcat $ fmap constructorFields $ datatypeCons - --- | For the non-generic instances, the parent type is the generic type -getNonGenericParentTypesExpression :: DatatypeInfo -> Q Exp -getNonGenericParentTypesExpression (DatatypeInfo {..}) = return $ ListE [AppE (ConE 'TSType) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) (ConT datatypeName)))] - getDeclarationFunctionBody :: Options -> p -> DatatypeInfo -> Q Dec getDeclarationFunctionBody options _name datatypeInfo@(DatatypeInfo {..}) = do -- If name is higher-kinded, add generic variables to the type and interface declarations @@ -238,10 +208,6 @@ getDeclarationFunctionBody options _name datatypeInfo@(DatatypeInfo {..}) = do return $ FunD 'getTypeScriptDeclarations [Clause [WildP] declarationFnBody []] -dropLeadingIFromInterfaceName :: TSDeclaration -> TSDeclaration -dropLeadingIFromInterfaceName decl@(TSInterfaceDeclaration {interfaceName=('I':xs)}) = decl { interfaceName = xs } -dropLeadingIFromInterfaceName decl@(TSTypeAlternatives {typeName=('I':xs)}) = decl { typeName = xs } -dropLeadingIFromInterfaceName x = x -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> (Exp, Maybe Exp, Bool) @@ -357,102 +323,12 @@ deriveJSONAndTypeScript options name = do json <- A.deriveJSON options name return $ ts <> json --- * Util stuff - -lastNameComponent :: String -> String -lastNameComponent x = T.unpack $ last $ T.splitOn "." (T.pack x) - -lastNameComponent' :: Name -> String -lastNameComponent' = lastNameComponent . show - -getTypeName :: Name -> String -getTypeName x = lastNameComponent $ show x - -allConstructorsAreNullary :: [ConstructorInfo] -> Bool -allConstructorsAreNullary constructors = and $ fmap isConstructorNullary constructors - -isConstructorNullary :: ConstructorInfo -> Bool -isConstructorNullary (ConstructorInfo {constructorVariant, constructorFields}) = (constructorVariant == NormalConstructor) && (constructorFields == []) - --- In Template Haskell 2.10.0.0 and later, Pred is just a synonm for Type --- In earlier versions, it has constructors -getDatatypePredicate :: Type -> Pred -#if MIN_VERSION_template_haskell(2,10,0) -getDatatypePredicate typ = AppT (ConT ''TypeScript) typ -#else -getDatatypePredicate typ = ClassP ''TypeScript [typ] -#endif - -getTypeAsStringExp :: Type -> Exp -getTypeAsStringExp typ = AppE (VarE 'getTypeScriptType) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) typ)) - -getOptionalAsBoolExp :: Type -> Exp -getOptionalAsBoolExp typ = AppE (VarE 'getTypeScriptOptional) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) typ)) - -isTaggedObject (sumEncoding -> TaggedObject _ _) = True -isTaggedObject _ = False - --- | Get the type of a tuple of constructor fields, as when we're packing a record-less constructor into a list -getTupleType constructorFields = case length constructorFields of - 0 -> AppT ListT (ConT ''()) - 1 -> head constructorFields - x -> applyToArgsT (ConT $ tupleTypeName x) constructorFields - --- | Helper to apply a type constructor to a list of type args -applyToArgsT :: Type -> [Type] -> Type -applyToArgsT constructor [] = constructor -applyToArgsT constructor (x:xs) = applyToArgsT (AppT constructor x) xs - --- | Helper to apply a function a list of args -applyToArgsE :: Exp -> [Exp] -> Exp -applyToArgsE f [] = f -applyToArgsE f (x:xs) = applyToArgsE (AppE f x) xs - -stringE = LitE . StringL - --- Between Template Haskell 2.10 and 2.11, InstanceD got an additional argument -#if MIN_VERSION_template_haskell(2,11,0) -mkInstance context typ decs = InstanceD Nothing context typ decs -#else -mkInstance context typ decs = InstanceD context typ decs -#endif - --- Between Aeson 1.1.2.0 and 1.2.0.0, tagSingleConstructors was added -getTagSingleConstructors :: Options -> Bool -#if MIN_VERSION_aeson(1,2,0) -getTagSingleConstructors options = tagSingleConstructors options -#else -getTagSingleConstructors _ = False -#endif - --- Between Template Haskell 2.10 and 2.11, the ability to look up which extensions are turned on was added -assertExtensionsTurnedOn :: DatatypeInfo -> Q () -#if MIN_VERSION_template_haskell(2,11,0) -assertExtensionsTurnedOn (DatatypeInfo {..}) = do - -- Check that necessary language extensions are turned on - scopedTypeVariablesEnabled <- isExtEnabled ScopedTypeVariables - kindSignaturesEnabled <- isExtEnabled KindSignatures - when (not scopedTypeVariablesEnabled) $ error [i|The ScopedTypeVariables extension is required; please enable it before calling deriveTypeScript. (For example: put {-# LANGUAGE ScopedTypeVariables #-} at the top of the file.)|] - when ((not kindSignaturesEnabled) && (length datatypeVars > 0)) $ error [i|The KindSignatures extension is required since type #{datatypeName} is a higher order type; please enable it before calling deriveTypeScript. (For example: put {-# LANGUAGE KindSignatures #-} at the top of the file.)|] -#else -assertExtensionsTurnedOn _ = return () -#endif - --- Older versions of Aeson don't have an Eq instance for SumEncoding so we do this -isObjectWithSingleField ObjectWithSingleField = True -isObjectWithSingleField _ = False - --- Older versions of Aeson don't have an Eq instance for SumEncoding so we do this -isTwoElemArray TwoElemArray = True -isTwoElemArray _ = False - --- Older versions of Aeson don't have an Eq instance for SumEncoding so we do this --- UntaggedValue was added between Aeson 0.11.3.0 and 1.0.0.0 -#if MIN_VERSION_aeson(1,0,0) -isUntaggedValue UntaggedValue = True -#endif -isUntaggedValue _ = False +-- | For the fully generic instance, the parent types are the types inside the constructors +getGenericParentTypesExpression :: DatatypeInfo -> Q Exp +getGenericParentTypesExpression (DatatypeInfo {..}) = return $ ListE [AppE (ConE 'TSType) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) typ)) | typ <- types] + where types = mconcat $ fmap constructorFields $ datatypeCons -fst3 (x, _, _) = x -snd3 (_, y, _) = y -thd3 (_, _, z) = z +-- | For the non-generic instances, the parent type is the generic type +getNonGenericParentTypesExpression :: DatatypeInfo -> Q Exp +getNonGenericParentTypesExpression (DatatypeInfo {..}) = return $ ListE [AppE (ConE 'TSType) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) (ConT datatypeName)))] + diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 3a5e667..b026a5e 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -3,7 +3,6 @@ module Data.Aeson.TypeScript.Types where import qualified Data.Aeson as A -import qualified Data.Aeson.TH as A import Data.Proxy import Data.String import Data.Typeable @@ -96,6 +95,7 @@ data FormattingOptions = FormattingOptions -- ^ Function applied to generated type names } +defaultFormattingOptions :: FormattingOptions defaultFormattingOptions = FormattingOptions { numIndentSpaces = 2 , interfaceNameModifier = id @@ -105,3 +105,28 @@ defaultFormattingOptions = FormattingOptions -- | Convenience typeclass class you can use to "attach" a set of Aeson encoding options to a type. class HasJSONOptions a where getJSONOptions :: (Proxy a) -> A.Options + +data T = T +data T1 = T1 +data T2 = T2 +data T3 = T3 +data T4 = T4 +data T5 = T5 +data T6 = T6 +data T7 = T7 +data T8 = T8 +data T9 = T9 +data T10 = T10 + +instance TypeScript T where getTypeScriptType _ = "T" +instance TypeScript T1 where getTypeScriptType _ = "T1" +instance TypeScript T2 where getTypeScriptType _ = "T2" +instance TypeScript T3 where getTypeScriptType _ = "T3" +instance TypeScript T4 where getTypeScriptType _ = "T4" +instance TypeScript T5 where getTypeScriptType _ = "T5" +instance TypeScript T6 where getTypeScriptType _ = "T6" +instance TypeScript T7 where getTypeScriptType _ = "T7" +instance TypeScript T8 where getTypeScriptType _ = "T8" +instance TypeScript T9 where getTypeScriptType _ = "T9" +instance TypeScript T10 where getTypeScriptType _ = "T10" + diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs new file mode 100644 index 0000000..7c8e05f --- /dev/null +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -0,0 +1,122 @@ +{-# LANGUAGE CPP, QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, ExistentialQuantification, FlexibleInstances, NamedFieldPuns, MultiWayIf, ViewPatterns, PolyKinds #-} + +module Data.Aeson.TypeScript.Util where + +import Control.Monad +import Data.Aeson as A +import Data.Aeson.TH as A +import Data.Aeson.TypeScript.Formatting +import Data.Aeson.TypeScript.Instances () +import Data.Aeson.TypeScript.Types +import qualified Data.Map as M +import Data.Maybe +import Data.Monoid +import Data.Proxy +import Data.String.Interpolate.IsString +import qualified Data.Text as T +import Language.Haskell.TH hiding (stringE) +import Language.Haskell.TH.Datatype + + +dropLeadingIFromInterfaceName :: TSDeclaration -> TSDeclaration +dropLeadingIFromInterfaceName decl@(TSInterfaceDeclaration {interfaceName=('I':xs)}) = decl { interfaceName = xs } +dropLeadingIFromInterfaceName decl@(TSTypeAlternatives {typeName=('I':xs)}) = decl { typeName = xs } +dropLeadingIFromInterfaceName x = x + +lastNameComponent :: String -> String +lastNameComponent x = T.unpack $ last $ T.splitOn "." (T.pack x) + +lastNameComponent' :: Name -> String +lastNameComponent' = lastNameComponent . show + +getTypeName :: Name -> String +getTypeName x = lastNameComponent $ show x + +allConstructorsAreNullary :: [ConstructorInfo] -> Bool +allConstructorsAreNullary constructors = and $ fmap isConstructorNullary constructors + +isConstructorNullary :: ConstructorInfo -> Bool +isConstructorNullary (ConstructorInfo {constructorVariant, constructorFields}) = (constructorVariant == NormalConstructor) && (constructorFields == []) + +-- In Template Haskell 2.10.0.0 and later, Pred is just a synonm for Type +-- In earlier versions, it has constructors +getDatatypePredicate :: Type -> Pred +#if MIN_VERSION_template_haskell(2,10,0) +getDatatypePredicate typ = AppT (ConT ''TypeScript) typ +#else +getDatatypePredicate typ = ClassP ''TypeScript [typ] +#endif + +getTypeAsStringExp :: Type -> Exp +getTypeAsStringExp typ = AppE (VarE 'getTypeScriptType) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) typ)) + +getOptionalAsBoolExp :: Type -> Exp +getOptionalAsBoolExp typ = AppE (VarE 'getTypeScriptOptional) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) typ)) + +isTaggedObject (sumEncoding -> TaggedObject _ _) = True +isTaggedObject _ = False + +-- | Get the type of a tuple of constructor fields, as when we're packing a record-less constructor into a list +getTupleType constructorFields = case length constructorFields of + 0 -> AppT ListT (ConT ''()) + 1 -> head constructorFields + x -> applyToArgsT (ConT $ tupleTypeName x) constructorFields + +-- | Helper to apply a type constructor to a list of type args +applyToArgsT :: Type -> [Type] -> Type +applyToArgsT constructor [] = constructor +applyToArgsT constructor (x:xs) = applyToArgsT (AppT constructor x) xs + +-- | Helper to apply a function a list of args +applyToArgsE :: Exp -> [Exp] -> Exp +applyToArgsE f [] = f +applyToArgsE f (x:xs) = applyToArgsE (AppE f x) xs + +stringE = LitE . StringL + +-- Between Template Haskell 2.10 and 2.11, InstanceD got an additional argument +#if MIN_VERSION_template_haskell(2,11,0) +mkInstance context typ decs = InstanceD Nothing context typ decs +#else +mkInstance context typ decs = InstanceD context typ decs +#endif + +-- Between Aeson 1.1.2.0 and 1.2.0.0, tagSingleConstructors was added +getTagSingleConstructors :: Options -> Bool +#if MIN_VERSION_aeson(1,2,0) +getTagSingleConstructors options = tagSingleConstructors options +#else +getTagSingleConstructors _ = False +#endif + +-- Between Template Haskell 2.10 and 2.11, the ability to look up which extensions are turned on was added +assertExtensionsTurnedOn :: DatatypeInfo -> Q () +#if MIN_VERSION_template_haskell(2,11,0) +assertExtensionsTurnedOn (DatatypeInfo {..}) = do + -- Check that necessary language extensions are turned on + scopedTypeVariablesEnabled <- isExtEnabled ScopedTypeVariables + kindSignaturesEnabled <- isExtEnabled KindSignatures + when (not scopedTypeVariablesEnabled) $ error [i|The ScopedTypeVariables extension is required; please enable it before calling deriveTypeScript. (For example: put {-# LANGUAGE ScopedTypeVariables #-} at the top of the file.)|] + when ((not kindSignaturesEnabled) && (length datatypeVars > 0)) $ error [i|The KindSignatures extension is required since type #{datatypeName} is a higher order type; please enable it before calling deriveTypeScript. (For example: put {-# LANGUAGE KindSignatures #-} at the top of the file.)|] +#else +assertExtensionsTurnedOn _ = return () +#endif + +-- Older versions of Aeson don't have an Eq instance for SumEncoding so we do this +isObjectWithSingleField ObjectWithSingleField = True +isObjectWithSingleField _ = False + +-- Older versions of Aeson don't have an Eq instance for SumEncoding so we do this +isTwoElemArray TwoElemArray = True +isTwoElemArray _ = False + +-- Older versions of Aeson don't have an Eq instance for SumEncoding so we do this +-- UntaggedValue was added between Aeson 0.11.3.0 and 1.0.0.0 +#if MIN_VERSION_aeson(1,0,0) +isUntaggedValue UntaggedValue = True +#endif +isUntaggedValue _ = False + +fst3 (x, _, _) = x +snd3 (_, y, _) = y +thd3 (_, _, z) = z diff --git a/stack.yaml b/stack.yaml index fe66b24..c492488 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,5 +1,11 @@ -resolver: lts-14.21 +resolver: lts-16.16 packages: - . + +extra-deps: +- git: https://github.com/thomasjm/beam.git + commit: d4564da8625961b32a3aafacb358c00ebd3e5370 + subdirs: + - beam-core diff --git a/stack.yaml.lock b/stack.yaml.lock index 7eef5f4..79bd9f9 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -3,10 +3,23 @@ # For more information, please see the documentation at: # https://docs.haskellstack.org/en/stable/lock_files -packages: [] +packages: +- completed: + subdir: beam-core + name: beam-core + version: 0.9.0.0 + git: https://github.com/thomasjm/beam.git + pantry-tree: + size: 2704 + sha256: e54dabae556fcac9079587adcce1b2bb107c196688c476dabdd84ca86ad556af + commit: d4564da8625961b32a3aafacb358c00ebd3e5370 + original: + subdir: beam-core + git: https://github.com/thomasjm/beam.git + commit: d4564da8625961b32a3aafacb358c00ebd3e5370 snapshots: - completed: - size: 524162 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/14/21.yaml - sha256: 9a55dd75853718f2bbbe951872b36a3b7802fcd71796e0f25b8664f24e34c666 - original: lts-14.21 + size: 532380 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/16/16.yaml + sha256: d6b004b095fe2a0b8b14fbc30014ee97e58843b9c9362ddb9244273dda62649e + original: lts-16.16 From bfc9329580a42b126a6750de5d40d51a2ee99d7b Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 14 Jan 2021 02:53:03 -0800 Subject: [PATCH 004/208] Code cleanups in preparation for working on higher kinded types --- src/Data/Aeson/TypeScript/TH.hs | 48 +++++++------------------------ src/Data/Aeson/TypeScript/Util.hs | 16 ++++++++--- weeder.dhall | 1 + 3 files changed, 24 insertions(+), 41 deletions(-) create mode 100644 weeder.dhall diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index caa1d06..e7c1a9a 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -127,6 +127,7 @@ import Data.Aeson.TypeScript.Formatting import Data.Aeson.TypeScript.Types import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Util +import qualified Data.List as L import qualified Data.Map as M import Data.Maybe import Data.Monoid @@ -156,15 +157,9 @@ deriveTypeScript options name = do 1 -> [ConT ''T] _ -> take (length datatypeVars) [ConT ''T1, ConT ''T2, ConT ''T3, ConT ''T4, ConT ''T5, ConT ''T6, ConT ''T7, ConT ''T8, ConT ''T9, ConT ''T10] -#if MIN_VERSION_th_abstraction(0,3,0) - let subMap = M.fromList $ zip (mapMaybe getFreeVariableName datatypeInstTypes) templateVarsToUse - let fullyQualifiedDatatypeInfo = (datatypeInfo {datatypeInstTypes = templateVarsToUse - , datatypeCons = fmap (applySubstitution subMap) datatypeCons}) -#else - let subMap = M.fromList $ zip (mapMaybe getFreeVariableName datatypeVars) templateVarsToUse - let fullyQualifiedDatatypeInfo = (datatypeInfo {datatypeVars = templateVarsToUse - , datatypeCons = fmap (applySubstitution subMap) datatypeCons}) -#endif + let subMap = M.fromList $ zip (mapMaybe getFreeVariableName (getDataTypeVars datatypeInfo)) templateVarsToUse + let fullyQualifiedDatatypeInfo = setDataTypeVars (datatypeInfo { datatypeCons = fmap (applySubstitution subMap) datatypeCons}) templateVarsToUse + getTypeFn <- getTypeExpression fullyQualifiedDatatypeInfo >>= \expr -> return $ FunD 'getTypeScriptType [Clause [WildP] (NormalB expr) []] getDeclarationFn <- getDeclarationFunctionBody options name fullyQualifiedDatatypeInfo getGenericParentTypesFn <- getGenericParentTypesExpression fullyQualifiedDatatypeInfo >>= \expr -> return $ FunD 'getParentTypes [Clause [WildP] (NormalB expr) []] @@ -175,11 +170,7 @@ deriveTypeScript options name = do otherInstances <- case null datatypeVars of False -> do otherGetTypeFn <- getTypeExpression datatypeInfo >>= \expr -> return $ FunD 'getTypeScriptType [Clause [WildP] (NormalB expr) []] -#if MIN_VERSION_th_abstraction(0,3,0) - return [mkInstance (fmap getDatatypePredicate datatypeInstTypes) (AppT (ConT ''TypeScript) (foldl AppT (ConT name) datatypeInstTypes)) [otherGetTypeFn, getNonGenericParentTypesFn]] -#else - return [mkInstance (fmap getDatatypePredicate datatypeVars) (AppT (ConT ''TypeScript) (foldl AppT (ConT name) datatypeVars)) [otherGetTypeFn, getNonGenericParentTypesFn]] -#endif + return [mkInstance (fmap getDatatypePredicate (getDataTypeVars datatypeInfo)) (AppT (ConT ''TypeScript) (foldl AppT (ConT name) (getDataTypeVars datatypeInfo))) [otherGetTypeFn, getNonGenericParentTypesFn]] True -> return [] return $ fullyGenericInstance : otherInstances @@ -286,25 +277,12 @@ getFieldType _ typ = (getTypeAsStringExp typ, getOptionalAsBoolExp typ) -- | Get an expression to be used for getTypeScriptType. -- For datatypes of kind * this is easy, since we can just evaluate the string literal in TH. -- For higher-kinded types, we need to make an expression which evaluates the template types and fills it in. -#if MIN_VERSION_th_abstraction(0,3,0) -getTypeExpression :: DatatypeInfo -> Q Exp -getTypeExpression (DatatypeInfo {datatypeInstTypes=[], ..}) = return $ stringE $ getTypeName datatypeName -getTypeExpression (DatatypeInfo {datatypeInstTypes=vars, ..}) = do -#else getTypeExpression :: DatatypeInfo -> Q Exp -getTypeExpression (DatatypeInfo {datatypeVars=[], ..}) = return $ stringE $ getTypeName datatypeName -getTypeExpression (DatatypeInfo {datatypeVars=vars, ..}) = do -#endif - let baseName = stringE $ getTypeName datatypeName +getTypeExpression di@(getDataTypeVars -> []) = return $ stringE $ getTypeName (datatypeName di) +getTypeExpression di@(getDataTypeVars -> vars) = do + let baseName = getTypeName (datatypeName di) let typeNames = ListE [getTypeAsStringExp typ | typ <- vars] - let headType = AppE (VarE 'head) typeNames - let tailType = AppE (VarE 'tail) typeNames - let comma = stringE ", " - x <- newName "x" - let tailsWithCommas = AppE (VarE 'mconcat) (CompE [BindS (VarP x) tailType, NoBindS (AppE (AppE (VarE 'mappend) comma) (VarE x))]) - let brackets = AppE (VarE 'mconcat) (ListE [stringE "<", headType, tailsWithCommas, stringE ">"]) - - return $ (AppE (AppE (VarE 'mappend) baseName) brackets) + [|baseName <> "<" <> (L.intercalate ", " $(return typeNames)) <> ">"|] -- * Convenience functions @@ -318,17 +296,13 @@ deriveJSONAndTypeScript :: Options -> Name -- ^ Name of the type for which to generate 'A.ToJSON', 'A.FromJSON', and 'TypeScript' instance declarations. -> Q [Dec] -deriveJSONAndTypeScript options name = do - ts <- deriveTypeScript options name - json <- A.deriveJSON options name - return $ ts <> json +deriveJSONAndTypeScript options name = (<>) <$> (deriveTypeScript options name) <*> (A.deriveJSON options name) -- | For the fully generic instance, the parent types are the types inside the constructors getGenericParentTypesExpression :: DatatypeInfo -> Q Exp getGenericParentTypesExpression (DatatypeInfo {..}) = return $ ListE [AppE (ConE 'TSType) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) typ)) | typ <- types] - where types = mconcat $ fmap constructorFields $ datatypeCons + where types = mconcat $ fmap constructorFields datatypeCons -- | For the non-generic instances, the parent type is the generic type getNonGenericParentTypesExpression :: DatatypeInfo -> Q Exp getNonGenericParentTypesExpression (DatatypeInfo {..}) = return $ ListE [AppE (ConE 'TSType) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) (ConT datatypeName)))] - diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index 7c8e05f..c865fb3 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -18,6 +18,18 @@ import Language.Haskell.TH hiding (stringE) import Language.Haskell.TH.Datatype +#if MIN_VERSION_th_abstraction(0,3,0) +getDataTypeVars (DatatypeInfo {datatypeInstTypes}) = datatypeInstTypes +#else +getDataTypeVars (DatatypeInfo {datatypeVars}) = datatypeVars +#endif + +#if MIN_VERSION_th_abstraction(0,3,0) +setDataTypeVars dti@(DatatypeInfo {}) vars = dti { datatypeInstTypes = vars } +#else +setDataTypeVars dti@(DatatypeInfo {}) vars = dti { datatypeVars = vars } +#endif + dropLeadingIFromInterfaceName :: TSDeclaration -> TSDeclaration dropLeadingIFromInterfaceName decl@(TSInterfaceDeclaration {interfaceName=('I':xs)}) = decl { interfaceName = xs } dropLeadingIFromInterfaceName decl@(TSTypeAlternatives {typeName=('I':xs)}) = decl { typeName = xs } @@ -53,9 +65,6 @@ getTypeAsStringExp typ = AppE (VarE 'getTypeScriptType) (SigE (ConE 'Proxy) (App getOptionalAsBoolExp :: Type -> Exp getOptionalAsBoolExp typ = AppE (VarE 'getTypeScriptOptional) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) typ)) -isTaggedObject (sumEncoding -> TaggedObject _ _) = True -isTaggedObject _ = False - -- | Get the type of a tuple of constructor fields, as when we're packing a record-less constructor into a list getTupleType constructorFields = case length constructorFields of 0 -> AppT ListT (ConT ''()) @@ -119,4 +128,3 @@ isUntaggedValue _ = False fst3 (x, _, _) = x snd3 (_, y, _) = y -thd3 (_, _, z) = z diff --git a/weeder.dhall b/weeder.dhall new file mode 100644 index 0000000..e3bbc53 --- /dev/null +++ b/weeder.dhall @@ -0,0 +1 @@ +{ roots = [ "formatTSDeclarations", "deriveJSONAndTypeScript", "main", "getTransitiveClosure" ], type-class-roots = True } From 574bf6e45bc780b8b592d05531358bb1b2e2963d Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 14 Jan 2021 04:43:38 -0800 Subject: [PATCH 005/208] Higher kinded type progress -- not actually correct, but works for Beam tables --- src/Data/Aeson/TypeScript/TH.hs | 21 ++++++++++++++++----- src/Data/Aeson/TypeScript/Types.hs | 30 +++++++++++++++++++++++++++++- test/HigherKind.hs | 12 +++++++++++- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index e7c1a9a..547a18f 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -146,18 +146,21 @@ deriveTypeScript :: Options deriveTypeScript options name = do datatypeInfo@(DatatypeInfo {..}) <- reifyDatatype name - -- reportError [i|Reified: #{datatypeInfo}|] + -- reportWarning [i|Reified: #{datatypeInfo}|] assertExtensionsTurnedOn datatypeInfo let getFreeVariableName (SigT (VarT n) _kind) = Just n getFreeVariableName _ = Nothing - let templateVarsToUse = case length datatypeVars of - 1 -> [ConT ''T] - _ -> take (length datatypeVars) [ConT ''T1, ConT ''T2, ConT ''T3, ConT ''T4, ConT ''T5, ConT ''T6, ConT ''T7, ConT ''T8, ConT ''T9, ConT ''T10] + let templateVarsToUse = case datatypeVars of + [KindedTV _ StarT] -> [ConT ''T] + vars -> chooseDataTypeVars allStarConstructors allPolyStarConstructors vars let subMap = M.fromList $ zip (mapMaybe getFreeVariableName (getDataTypeVars datatypeInfo)) templateVarsToUse + + -- reportWarning [i|subMap: #{subMap}|] + let fullyQualifiedDatatypeInfo = setDataTypeVars (datatypeInfo { datatypeCons = fmap (applySubstitution subMap) datatypeCons}) templateVarsToUse getTypeFn <- getTypeExpression fullyQualifiedDatatypeInfo >>= \expr -> return $ FunD 'getTypeScriptType [Clause [WildP] (NormalB expr) []] @@ -282,7 +285,7 @@ getTypeExpression di@(getDataTypeVars -> []) = return $ stringE $ getTypeName (d getTypeExpression di@(getDataTypeVars -> vars) = do let baseName = getTypeName (datatypeName di) let typeNames = ListE [getTypeAsStringExp typ | typ <- vars] - [|baseName <> "<" <> (L.intercalate ", " $(return typeNames)) <> ">"|] + [|$(return (stringE baseName)) <> "<" <> (L.intercalate ", " $(return typeNames)) <> ">"|] -- * Convenience functions @@ -306,3 +309,11 @@ getGenericParentTypesExpression (DatatypeInfo {..}) = return $ ListE [AppE (ConE -- | For the non-generic instances, the parent type is the generic type getNonGenericParentTypesExpression :: DatatypeInfo -> Q Exp getNonGenericParentTypesExpression (DatatypeInfo {..}) = return $ ListE [AppE (ConE 'TSType) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) (ConT datatypeName)))] + + +chooseDataTypeVars _ _ [] = [] +chooseDataTypeVars starConstructors polyStarConstructors (x:xs) = case x of + PlainTV _ -> (head starConstructors) : chooseDataTypeVars (tail starConstructors) polyStarConstructors xs + KindedTV _ StarT -> (head starConstructors) : chooseDataTypeVars (tail starConstructors) polyStarConstructors xs + -- higher -> (higher) : chooseDataTypeVars starConstructors (tail polyStarConstructors) xs + _ -> (head allPolyStarConstructors) : chooseDataTypeVars starConstructors (tail polyStarConstructors) xs diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index b026a5e..1158547 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -6,6 +6,7 @@ import qualified Data.Aeson as A import Data.Proxy import Data.String import Data.Typeable +import Language.Haskell.TH -- | The typeclass that defines how a type is turned into TypeScript. -- @@ -129,4 +130,31 @@ instance TypeScript T7 where getTypeScriptType _ = "T7" instance TypeScript T8 where getTypeScriptType _ = "T8" instance TypeScript T9 where getTypeScriptType _ = "T9" instance TypeScript T10 where getTypeScriptType _ = "T10" - + +allStarConstructors = [ConT ''T1, ConT ''T2, ConT ''T3, ConT ''T4, ConT ''T5, ConT ''T6, ConT ''T7, ConT ''T8, ConT ''T9, ConT ''T10] + +data F (a :: k) = F +data F1 a = F1 +data F2 a = F2 +data F3 a = F3 +data F4 a = F4 +data F5 a = F5 +data F6 a = F6 +data F7 a = F7 +data F8 a = F8 +data F9 a = F9 +data F10 a = F10 + +instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) +instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F1 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) +instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F2 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) +instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F3 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) +instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F4 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) +instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F5 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) +instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F6 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) +instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F7 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) +instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F8 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) +instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F9 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) +instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F10 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) + +allPolyStarConstructors = [ConT ''F1, ConT ''F2, ConT ''F3, ConT ''F4, ConT ''F5, ConT ''F6, ConT ''F7, ConT ''F8, ConT ''F9, ConT ''F10] diff --git a/test/HigherKind.hs b/test/HigherKind.hs index f913b06..5e3e1d0 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -1,4 +1,13 @@ -{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, KindSignatures #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilies #-} module HigherKind (tests) where @@ -6,6 +15,7 @@ import Data.Aeson as A import Data.Aeson.TH as A import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types +import Data.Kind import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString From eb49abea42f4beca686b0fc38a0013365d3e2b37 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 14 Jan 2021 04:44:10 -0800 Subject: [PATCH 006/208] Ability to generate HIE files for weeder --- .gitignore | 3 ++- stack.yaml | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 012bd9f..781cfa4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .stack-work/ *~ .dir-locals.el -dist-newstyle \ No newline at end of file +dist-newstyle +*.hie diff --git a/stack.yaml b/stack.yaml index c492488..fc099ee 100644 --- a/stack.yaml +++ b/stack.yaml @@ -4,6 +4,9 @@ resolver: lts-16.16 packages: - . +# ghc-options: +# "$locals": -fwrite-ide-info + extra-deps: - git: https://github.com/thomasjm/beam.git commit: d4564da8625961b32a3aafacb358c00ebd3e5370 From a37385d31cb05558316152b0ecc00d2f68972654 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 14 Jan 2021 04:53:00 -0800 Subject: [PATCH 007/208] Expose poly kinded constructors for trying this out --- src/Data/Aeson/TypeScript/TH.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 547a18f..f54172d 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -117,6 +117,9 @@ module Data.Aeson.TypeScript.TH ( deriveJSONAndTypeScript, T(..), + F1(..), + F2(..), + F3(..), -- TODO: expose the rest of these if necessary module Data.Aeson.TypeScript.Instances ) where From d3366bbabb201ba2aeeba4fb6088389eedcc4f73 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 15 Jan 2021 20:09:56 -0800 Subject: [PATCH 008/208] Expose a few more constructors for testing this --- src/Data/Aeson/TypeScript/TH.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index f54172d..d519526 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -117,6 +117,9 @@ module Data.Aeson.TypeScript.TH ( deriveJSONAndTypeScript, T(..), + T1(..), + T2(..), + T3(..), F1(..), F2(..), F3(..), -- TODO: expose the rest of these if necessary From ac6fb39e12ddea56e92ac926eb2069bcadfbbfe5 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 14 Jan 2021 04:45:48 -0800 Subject: [PATCH 009/208] Various test files for experimenting with HKT scenarios --- aeson-typescript.cabal | 9 +++++- package.yaml | 3 ++ test/HigherKindBeam.hs | 67 ++++++++++++++++++++++++++++++++++++++++++ test/Live.hs | 60 +++++++++++++++++++++++++++++++++++++ test/LiveLogging.hs | 45 ++++++++++++++++++++++++++++ 5 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 test/HigherKindBeam.hs create mode 100644 test/Live.hs create mode 100644 test/LiveLogging.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index b2d93e7..dbd8e1f 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -4,7 +4,7 @@ cabal-version: 1.12 -- -- see: https://github.com/sol/hpack -- --- hash: 40c4e69d4430c0e44e17b3fad921383e3b44cd50412fa222fea682b295570800 +-- hash: 96e15cc4fd8d1fc36ede72e6e1bfec4d8b9d9afb57339271b7d7f50461ebdfde name: aeson-typescript version: 0.2.0.0 @@ -47,12 +47,14 @@ library build-depends: aeson , base >=4.7 && <5 + , beam-core , containers , interpolate , mtl , template-haskell , text , th-abstraction <0.4 + , time , unordered-containers default-language: Haskell2010 @@ -61,6 +63,9 @@ test-suite aeson-typescript-test main-is: Spec.hs other-modules: HigherKind + HigherKindBeam + Live + LiveLogging NoOmitNothingFields ObjectWithSingleFieldNoTagSingleConstructors ObjectWithSingleFieldTagSingleConstructors @@ -88,6 +93,7 @@ test-suite aeson-typescript-test aeson , aeson-typescript , base >=4.7 && <5 + , beam-core , bytestring , containers , directory @@ -100,5 +106,6 @@ test-suite aeson-typescript-test , temporary , text , th-abstraction <0.4 + , time , unordered-containers default-language: Haskell2010 diff --git a/package.yaml b/package.yaml index 89a2730..e9b9e1e 100644 --- a/package.yaml +++ b/package.yaml @@ -35,6 +35,9 @@ dependencies: - text - th-abstraction < 0.4 - unordered-containers +- beam-core +- time + library: source-dirs: src diff --git a/test/HigherKindBeam.hs b/test/HigherKindBeam.hs new file mode 100644 index 0000000..55acf7c --- /dev/null +++ b/test/HigherKindBeam.hs @@ -0,0 +1,67 @@ +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilies #-} + +module HigherKindBeam where + +import Data.Aeson as A +import Data.Aeson.TH as A +import Data.Aeson.TypeScript.TH +import Data.Aeson.TypeScript.Types +import Data.Kind +import Data.Monoid +import Data.Proxy +import Data.String.Interpolate.IsString +import qualified Data.Text as T +import Data.Time +import Database.Beam +import Prelude hiding (Double) +-- import Test.Hspec + + + + +data UserT f = User { + _userUsername :: Columnar f T.Text + , _userCreatedAt :: Columnar f UTCTime + } + +instance TypeScript Identity where + getTypeScriptType _ = "any" +instance TypeScript UTCTime where + getTypeScriptType _ = "any" + + +-- $(deriveTypeScript A.defaultOptions ''UserT) +instance (TypeScript (f_1 :: * -> *), TypeScript (Columnar f_1 T.Text), TypeScript (Columnar f_1 UTCTime)) => TypeScript (HigherKindBeam.UserT (f_1 :: * -> *)) where + getTypeScriptType _ = mappend "UserT" (mconcat ["<", head [getTypeScriptType (Proxy :: Proxy f_1)] + , mconcat [mappend ", " x_0 | x_0 <- tail [getTypeScriptType (Proxy :: Proxy f_1)]] + ,">"]) + getTypeScriptDeclarations _ = [TSTypeAlternatives "UserT" ["T"] ["IUser"] + , TSInterfaceDeclaration "IUser" ["T"] [ + TSField (getTypeScriptOptional (Proxy :: Proxy (Columnar f_1 T.Text))) "_userUsername" (getTypeScriptType (Proxy :: Proxy (Columnar f_1 T.Text))) + , TSField (getTypeScriptOptional (Proxy :: Proxy (Columnar f_1 UTCTime))) "_userCreatedAt" (getTypeScriptType (Proxy :: Proxy (Columnar f_1 UTCTime))) + ]] + getParentTypes _ = [TSType (Proxy :: Proxy (Columnar f_1 T.Text)), TSType (Proxy :: Proxy (Columnar f_1 UTCTime))] + + +-- instance TypeScript (f_1 :: * -> *) => TypeScript (HigherKindBeam.UserT (f_1 :: * -> *)) where +-- getTypeScriptType _ = mappend "UserT" (mconcat ["<", head [getTypeScriptType (Proxy :: Proxy (f_1 :: * -> *))] +-- , mconcat [mappend ", " x_2 | x_2 <- tail [getTypeScriptType (Proxy :: Proxy (f_1 :: * -> *))]] +-- , ">"]) +-- getParentTypes _ = [TSType (Proxy :: Proxy HigherKindBeam.UserT)] + + + +-- main = putStrLn $ formatTSDeclarations ( +-- (getTypeScriptDeclarations (Proxy :: Proxy (UserT Identity))) +-- ) diff --git a/test/Live.hs b/test/Live.hs new file mode 100644 index 0000000..d485d4a --- /dev/null +++ b/test/Live.hs @@ -0,0 +1,60 @@ +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilies #-} + +module Live where + +import Data.Aeson as A +import Data.Aeson.TH as A +import Data.Aeson.TypeScript.TH +import Data.Aeson.TypeScript.Types +import Data.Kind +import Data.Monoid +import Data.Proxy +import Data.String.Interpolate.IsString +import qualified Data.Text as T +import Data.Time +import Database.Beam +import Language.Haskell.TH +import Prelude hiding (Double) +import Database.Beam + + + +data TestT a = TestT { + listOfA :: [a] + , maybeA :: Maybe a + } + +$(deriveTypeScript A.defaultOptions ''TestT) + + + +instance TypeScript F1 where + getTypeScriptType _ = "any" + +instance TypeScript Identity where + getTypeScriptType _ = "any" + +instance TypeScript UTCTime where + getTypeScriptType _ = "DateTime" + +data UserT f = User { + _userUsername :: Columnar f T.Text + , _userCreatedAt :: Columnar f UTCTime + } + + +$(deriveTypeScript A.defaultOptions ''UserT) + +-- main = do +-- putStrLn $(stringE . pprint =<< (deriveTypeScript A.defaultOptions ''TestT)) diff --git a/test/LiveLogging.hs b/test/LiveLogging.hs new file mode 100644 index 0000000..9086af0 --- /dev/null +++ b/test/LiveLogging.hs @@ -0,0 +1,45 @@ +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilies #-} + +module Live where + +import Data.Aeson as A +import Data.Aeson.TH as A +import Data.Aeson.TypeScript.TH +import Data.Aeson.TypeScript.Types +import Data.Kind +import Data.Monoid +import Data.Proxy +import Data.String.Interpolate.IsString +import qualified Data.Text as T +import Data.Time +import Database.Beam +import Language.Haskell.TH hiding (Type) +import Prelude hiding (Double) +import Database.Beam + + + +data LoggingSource = SGeneral + +data LoggingSourceTagged s where + General :: LoggingSourceTagged 'SGeneral + +type family ParamsFamily (q :: LoggingSource) :: Type where + ParamsFamily 'SGeneral = String + +data HigherKindWithTypeFamily s = TapMessageParams { params :: ParamsFamily s } +-- $(deriveTypeScript A.defaultOptions ''HigherKindWithTypeFamily) + +-- main = do +-- putStrLn $(stringE . pprint =<< (deriveTypeScript A.defaultOptions ''TestT)) From 417f51bf9a55da298485422e266eb24b725f82a9 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sat, 16 Jan 2021 23:31:55 -0800 Subject: [PATCH 010/208] More experimentation --- test/HigherKindBeam.hs | 31 ++++++++++++++++++------------- test/LiveLogging.hs | 2 +- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/test/HigherKindBeam.hs b/test/HigherKindBeam.hs index 55acf7c..85a633e 100644 --- a/test/HigherKindBeam.hs +++ b/test/HigherKindBeam.hs @@ -3,6 +3,7 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE TypeFamilyDependencies #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE KindSignatures #-} @@ -24,15 +25,28 @@ import Data.String.Interpolate.IsString import qualified Data.Text as T import Data.Time import Database.Beam +import Language.Haskell.TH hiding (Type) import Prelude hiding (Double) --- import Test.Hspec +data SingleDE = SingleDE +data K8SDE = K8SDE +data K8SEnvironment = K8SEnvironment + deriving (Eq, Show) -data UserT f = User { +data SingleNodeEnvironment = SingleNodeEnvironment + deriving (Eq, Show) + +type family DeployEnvironment env = result | result -> env where + DeployEnvironment SingleNodeEnvironment = SingleDE + DeployEnvironment K8SEnvironment = K8SDE + + +data UserT env f = User { _userUsername :: Columnar f T.Text - , _userCreatedAt :: Columnar f UTCTime + , _userCreatedAt :: Columnar f UTCTime + , _userDeployEnvironment :: Columnar f (DeployEnvironment env) } instance TypeScript Identity where @@ -41,17 +55,8 @@ instance TypeScript UTCTime where getTypeScriptType _ = "any" + -- $(deriveTypeScript A.defaultOptions ''UserT) -instance (TypeScript (f_1 :: * -> *), TypeScript (Columnar f_1 T.Text), TypeScript (Columnar f_1 UTCTime)) => TypeScript (HigherKindBeam.UserT (f_1 :: * -> *)) where - getTypeScriptType _ = mappend "UserT" (mconcat ["<", head [getTypeScriptType (Proxy :: Proxy f_1)] - , mconcat [mappend ", " x_0 | x_0 <- tail [getTypeScriptType (Proxy :: Proxy f_1)]] - ,">"]) - getTypeScriptDeclarations _ = [TSTypeAlternatives "UserT" ["T"] ["IUser"] - , TSInterfaceDeclaration "IUser" ["T"] [ - TSField (getTypeScriptOptional (Proxy :: Proxy (Columnar f_1 T.Text))) "_userUsername" (getTypeScriptType (Proxy :: Proxy (Columnar f_1 T.Text))) - , TSField (getTypeScriptOptional (Proxy :: Proxy (Columnar f_1 UTCTime))) "_userCreatedAt" (getTypeScriptType (Proxy :: Proxy (Columnar f_1 UTCTime))) - ]] - getParentTypes _ = [TSType (Proxy :: Proxy (Columnar f_1 T.Text)), TSType (Proxy :: Proxy (Columnar f_1 UTCTime))] -- instance TypeScript (f_1 :: * -> *) => TypeScript (HigherKindBeam.UserT (f_1 :: * -> *)) where diff --git a/test/LiveLogging.hs b/test/LiveLogging.hs index 9086af0..90c3994 100644 --- a/test/LiveLogging.hs +++ b/test/LiveLogging.hs @@ -11,7 +11,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} -module Live where +module LiveLogging where import Data.Aeson as A import Data.Aeson.TH as A From 206fe10f6431a913b3166899cfb863b6a0de90c8 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sat, 16 Jan 2021 23:32:10 -0800 Subject: [PATCH 011/208] Using more quasiquoters in TH.hs --- src/Data/Aeson/TypeScript/TH.hs | 52 +++++++++++++++------------------ 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index d519526..4f4ed95 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -169,45 +169,44 @@ deriveTypeScript options name = do let fullyQualifiedDatatypeInfo = setDataTypeVars (datatypeInfo { datatypeCons = fmap (applySubstitution subMap) datatypeCons}) templateVarsToUse - getTypeFn <- getTypeExpression fullyQualifiedDatatypeInfo >>= \expr -> return $ FunD 'getTypeScriptType [Clause [WildP] (NormalB expr) []] - getDeclarationFn <- getDeclarationFunctionBody options name fullyQualifiedDatatypeInfo - getGenericParentTypesFn <- getGenericParentTypesExpression fullyQualifiedDatatypeInfo >>= \expr -> return $ FunD 'getParentTypes [Clause [WildP] (NormalB expr) []] - getNonGenericParentTypesFn <- getNonGenericParentTypesExpression fullyQualifiedDatatypeInfo >>= \expr -> return $ FunD 'getParentTypes [Clause [WildP] (NormalB expr) []] - - let fullyGenericInstance = mkInstance [] (AppT (ConT ''TypeScript) (ConT name)) [getTypeFn, getDeclarationFn, getGenericParentTypesFn] + fullyGenericInstance <- [d|instance TypeScript $(conT name) where + getTypeScriptType _ = $(getTypeExpression fullyQualifiedDatatypeInfo) + getTypeScriptDeclarations _ = $(getDeclarationFunctionBody options name fullyQualifiedDatatypeInfo) + getParentTypes _ = $(getGenericParentTypesExpression fullyQualifiedDatatypeInfo) + |] otherInstances <- case null datatypeVars of False -> do - otherGetTypeFn <- getTypeExpression datatypeInfo >>= \expr -> return $ FunD 'getTypeScriptType [Clause [WildP] (NormalB expr) []] - return [mkInstance (fmap getDatatypePredicate (getDataTypeVars datatypeInfo)) (AppT (ConT ''TypeScript) (foldl AppT (ConT name) (getDataTypeVars datatypeInfo))) [otherGetTypeFn, getNonGenericParentTypesFn]] + let predicates :: [Pred] = fmap (getDatatypePredicate) (getDataTypeVars datatypeInfo) + let constraints = foldl AppT (TupleT (length predicates)) predicates + [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where + getTypeScriptType _ = $(getTypeExpression datatypeInfo); + getParentTypes _ = [TSType (Proxy :: Proxy $(conT name))] + |] True -> return [] - return $ fullyGenericInstance : otherInstances + return $ fullyGenericInstance <> otherInstances -getDeclarationFunctionBody :: Options -> p -> DatatypeInfo -> Q Dec +getDeclarationFunctionBody :: Options -> p -> DatatypeInfo -> Q Exp getDeclarationFunctionBody options _name datatypeInfo@(DatatypeInfo {..}) = do -- If name is higher-kinded, add generic variables to the type and interface declarations let genericVariables :: [String] = if | length datatypeVars == 1 -> ["T"] | otherwise -> ["T" <> show j | j <- [1..(length datatypeVars)]] let genericVariablesExp = ListE [stringE x | x <- genericVariables] - declarationFnBody <- do - let interfaceNamesAndDeclarations = fmap (handleConstructor options datatypeInfo genericVariables) datatypeCons - let interfaceDeclarations = catMaybes $ fmap snd3 interfaceNamesAndDeclarations - - case interfaceNamesAndDeclarations of - [(_, Just interfaceDecl, True)] | datatypeVars == [] -> do - -- The type declaration is just a reference to a single interface, so we can omit the type part and drop the "I" from the interface name - return $ NormalB $ ListE [AppE (VarE 'dropLeadingIFromInterfaceName) interfaceDecl] - - _ -> do - let interfaceNames = fmap fst3 interfaceNamesAndDeclarations + let interfaceNamesAndDeclarations = fmap (handleConstructor options datatypeInfo genericVariables) datatypeCons + let interfaceDeclarations = catMaybes $ fmap snd3 interfaceNamesAndDeclarations - let typeDeclaration = applyToArgsE (ConE 'TSTypeAlternatives) [stringE $ getTypeName datatypeName, genericVariablesExp, ListE interfaceNames] - return $ NormalB $ ListE (typeDeclaration : interfaceDeclarations) + case interfaceNamesAndDeclarations of + [(_, Just interfaceDecl, True)] | datatypeVars == [] -> do + -- The type declaration is just a reference to a single interface, so we can omit the type part and drop the "I" from the interface name + return $ ListE [AppE (VarE 'dropLeadingIFromInterfaceName) interfaceDecl] - return $ FunD 'getTypeScriptDeclarations [Clause [WildP] declarationFnBody []] + _ -> do + let interfaceNames = fmap fst3 interfaceNamesAndDeclarations + let typeDeclaration = applyToArgsE (ConE 'TSTypeAlternatives) [stringE $ getTypeName datatypeName, genericVariablesExp, ListE interfaceNames] + return $ ListE (typeDeclaration : interfaceDeclarations) -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> (Exp, Maybe Exp, Bool) @@ -312,11 +311,6 @@ getGenericParentTypesExpression :: DatatypeInfo -> Q Exp getGenericParentTypesExpression (DatatypeInfo {..}) = return $ ListE [AppE (ConE 'TSType) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) typ)) | typ <- types] where types = mconcat $ fmap constructorFields datatypeCons --- | For the non-generic instances, the parent type is the generic type -getNonGenericParentTypesExpression :: DatatypeInfo -> Q Exp -getNonGenericParentTypesExpression (DatatypeInfo {..}) = return $ ListE [AppE (ConE 'TSType) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) (ConT datatypeName)))] - - chooseDataTypeVars _ _ [] = [] chooseDataTypeVars starConstructors polyStarConstructors (x:xs) = case x of PlainTV _ -> (head starConstructors) : chooseDataTypeVars (tail starConstructors) polyStarConstructors xs From 761c150c545b7ca9798787aacb6eff344825d6fa Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sat, 16 Jan 2021 23:41:45 -0800 Subject: [PATCH 012/208] Tests pass if we add ConstraintKinds --- test/HigherKind.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/HigherKind.hs b/test/HigherKind.hs index 5e3e1d0..c4ea39e 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -5,6 +5,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE KindSignatures #-} +{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} From 0ffc9c245f38aab8126054ab1649a2897ac95409 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 00:17:49 -0800 Subject: [PATCH 013/208] Changing up how constraints work --- src/Data/Aeson/TypeScript/TH.hs | 40 +++-------- test/HigherKind.hs | 20 +++--- test/HigherKindBeam.hs | 72 ------------------- test/Live.hs | 30 ++++++-- test/NoOmitNothingFields.hs | 11 ++- ...tWithSingleFieldNoTagSingleConstructors.hs | 11 ++- ...ectWithSingleFieldTagSingleConstructors.hs | 11 ++- test/OmitNothingFields.hs | 11 ++- test/TaggedObjectNoTagSingleConstructors.hs | 11 ++- test/TaggedObjectTagSingleConstructors.hs | 11 ++- test/TestBoilerplate.hs | 13 +++- test/TwoElemArrayNoTagSingleConstructors.hs | 11 ++- test/TwoElemArrayTagSingleConstructors.hs | 11 ++- test/UntaggedNoTagSingleConstructors.hs | 11 ++- test/UntaggedTagSingleConstructors.hs | 11 ++- 15 files changed, 156 insertions(+), 129 deletions(-) delete mode 100644 test/HigherKindBeam.hs diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 4f4ed95..853d1e8 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -152,40 +152,18 @@ deriveTypeScript :: Options deriveTypeScript options name = do datatypeInfo@(DatatypeInfo {..}) <- reifyDatatype name - -- reportWarning [i|Reified: #{datatypeInfo}|] - assertExtensionsTurnedOn datatypeInfo - let getFreeVariableName (SigT (VarT n) _kind) = Just n - getFreeVariableName _ = Nothing - - let templateVarsToUse = case datatypeVars of - [KindedTV _ StarT] -> [ConT ''T] - vars -> chooseDataTypeVars allStarConstructors allPolyStarConstructors vars - - let subMap = M.fromList $ zip (mapMaybe getFreeVariableName (getDataTypeVars datatypeInfo)) templateVarsToUse - - -- reportWarning [i|subMap: #{subMap}|] - - let fullyQualifiedDatatypeInfo = setDataTypeVars (datatypeInfo { datatypeCons = fmap (applySubstitution subMap) datatypeCons}) templateVarsToUse - - fullyGenericInstance <- [d|instance TypeScript $(conT name) where - getTypeScriptType _ = $(getTypeExpression fullyQualifiedDatatypeInfo) - getTypeScriptDeclarations _ = $(getDeclarationFunctionBody options name fullyQualifiedDatatypeInfo) - getParentTypes _ = $(getGenericParentTypesExpression fullyQualifiedDatatypeInfo) - |] - - otherInstances <- case null datatypeVars of - False -> do - let predicates :: [Pred] = fmap (getDatatypePredicate) (getDataTypeVars datatypeInfo) - let constraints = foldl AppT (TupleT (length predicates)) predicates - [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where - getTypeScriptType _ = $(getTypeExpression datatypeInfo); - getParentTypes _ = [TSType (Proxy :: Proxy $(conT name))] - |] - True -> return [] + let constructorPreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- mconcat $ fmap constructorFields datatypeCons] + let typeVariablePreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- getDataTypeVars datatypeInfo] + let predicates = constructorPreds <> typeVariablePreds - return $ fullyGenericInstance <> otherInstances + let constraints = foldl AppT (TupleT (length predicates)) predicates + [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where + getTypeScriptType _ = $(getTypeExpression datatypeInfo); + getTypeScriptDeclarations _ = $(getDeclarationFunctionBody options name datatypeInfo) + getParentTypes _ = $(getGenericParentTypesExpression datatypeInfo) + |] getDeclarationFunctionBody :: Options -> p -> DatatypeInfo -> Q Exp getDeclarationFunctionBody options _name datatypeInfo@(DatatypeInfo {..}) = do diff --git a/test/HigherKind.hs b/test/HigherKind.hs index c4ea39e..2fb10ac 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -9,6 +9,8 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE UndecidableInstances #-} module HigherKind (tests) where @@ -46,7 +48,7 @@ $(deriveJSON A.defaultOptions ''HigherKindWithUnary) tests = describe "Higher kinds" $ do describe "Kind * -> *" $ do it [i|makes the declaration and types correctly|] $ do - (getTypeScriptDeclarations (Proxy :: Proxy HigherKind)) `shouldBe` ([ + (getTypeScriptDeclarations (Proxy :: Proxy (HigherKind T))) `shouldBe` ([ TSTypeAlternatives "HigherKind" ["T"] ["IHigherKind"], TSInterfaceDeclaration "IHigherKind" ["T"] [TSField False "higherKindList" "T[]"] ]) @@ -61,14 +63,14 @@ tests = describe "Higher kinds" $ do ]) it [i|works with an interface inside|] $ do - (getTypeScriptDeclarations (Proxy :: Proxy HigherKindWithUnary)) `shouldBe` ([ + (getTypeScriptDeclarations (Proxy :: Proxy (HigherKindWithUnary T))) `shouldBe` ([ TSTypeAlternatives "HigherKindWithUnary" ["T"] ["IUnary"], TSTypeAlternatives "IUnary" ["T"] ["number"] ]) describe "Kind * -> * -> *" $ do it [i|makes the declaration and type correctly|] $ do - (getTypeScriptDeclarations (Proxy :: Proxy DoubleHigherKind)) `shouldBe` ([ + (getTypeScriptDeclarations (Proxy :: Proxy (DoubleHigherKind T1 T2))) `shouldBe` ([ TSTypeAlternatives "DoubleHigherKind" ["T1","T2"] ["IDoubleHigherKind"], TSInterfaceDeclaration "IDoubleHigherKind" ["T1","T2"] [TSField False "someList" "T2[]" , TSField False "higherKindThing" "HigherKind"] @@ -79,9 +81,9 @@ tests = describe "Higher kinds" $ do describe "TSC compiler checks" $ do it "type checks everything with tsc" $ do - let declarations = ((getTypeScriptDeclarations (Proxy :: Proxy HigherKind)) <> - (getTypeScriptDeclarations (Proxy :: Proxy DoubleHigherKind)) <> - (getTypeScriptDeclarations (Proxy :: Proxy HigherKindWithUnary)) + let declarations = ((getTypeScriptDeclarations (Proxy :: Proxy (HigherKind T))) <> + (getTypeScriptDeclarations (Proxy :: Proxy (DoubleHigherKind T1 T2))) <> + (getTypeScriptDeclarations (Proxy :: Proxy (HigherKindWithUnary T))) ) let typesAndValues = [(getTypeScriptType (Proxy :: Proxy (HigherKind Int)) , A.encode (HigherKind [42 :: Int])) @@ -100,7 +102,7 @@ main = hspec tests main' = putStrLn $ formatTSDeclarations ( - (getTypeScriptDeclarations (Proxy :: Proxy HigherKind)) <> - (getTypeScriptDeclarations (Proxy :: Proxy DoubleHigherKind)) <> - (getTypeScriptDeclarations (Proxy :: Proxy HigherKindWithUnary)) + (getTypeScriptDeclarations (Proxy :: Proxy (HigherKind T))) <> + (getTypeScriptDeclarations (Proxy :: Proxy (DoubleHigherKind T1 T2))) <> + (getTypeScriptDeclarations (Proxy :: Proxy (HigherKindWithUnary T))) ) diff --git a/test/HigherKindBeam.hs b/test/HigherKindBeam.hs deleted file mode 100644 index 85a633e..0000000 --- a/test/HigherKindBeam.hs +++ /dev/null @@ -1,72 +0,0 @@ -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE TypeFamilyDependencies #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilies #-} - -module HigherKindBeam where - -import Data.Aeson as A -import Data.Aeson.TH as A -import Data.Aeson.TypeScript.TH -import Data.Aeson.TypeScript.Types -import Data.Kind -import Data.Monoid -import Data.Proxy -import Data.String.Interpolate.IsString -import qualified Data.Text as T -import Data.Time -import Database.Beam -import Language.Haskell.TH hiding (Type) -import Prelude hiding (Double) - - -data SingleDE = SingleDE -data K8SDE = K8SDE - -data K8SEnvironment = K8SEnvironment - deriving (Eq, Show) - -data SingleNodeEnvironment = SingleNodeEnvironment - deriving (Eq, Show) - -type family DeployEnvironment env = result | result -> env where - DeployEnvironment SingleNodeEnvironment = SingleDE - DeployEnvironment K8SEnvironment = K8SDE - - -data UserT env f = User { - _userUsername :: Columnar f T.Text - , _userCreatedAt :: Columnar f UTCTime - , _userDeployEnvironment :: Columnar f (DeployEnvironment env) - } - -instance TypeScript Identity where - getTypeScriptType _ = "any" -instance TypeScript UTCTime where - getTypeScriptType _ = "any" - - - --- $(deriveTypeScript A.defaultOptions ''UserT) - - --- instance TypeScript (f_1 :: * -> *) => TypeScript (HigherKindBeam.UserT (f_1 :: * -> *)) where --- getTypeScriptType _ = mappend "UserT" (mconcat ["<", head [getTypeScriptType (Proxy :: Proxy (f_1 :: * -> *))] --- , mconcat [mappend ", " x_2 | x_2 <- tail [getTypeScriptType (Proxy :: Proxy (f_1 :: * -> *))]] --- , ">"]) --- getParentTypes _ = [TSType (Proxy :: Proxy HigherKindBeam.UserT)] - - - --- main = putStrLn $ formatTSDeclarations ( --- (getTypeScriptDeclarations (Proxy :: Proxy (UserT Identity))) --- ) diff --git a/test/Live.hs b/test/Live.hs index d485d4a..617d7d7 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} @@ -10,6 +11,7 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeFamilyDependencies #-} module Live where @@ -38,19 +40,37 @@ data TestT a = TestT { $(deriveTypeScript A.defaultOptions ''TestT) +instance TypeScript UTCTime where + getTypeScriptType _ = "DateTime" -instance TypeScript F1 where - getTypeScriptType _ = "any" +instance (Typeable a) => TypeScript (Identity a) where + getTypeScriptType x = getTypeScriptType x instance TypeScript Identity where getTypeScriptType _ = "any" -instance TypeScript UTCTime where - getTypeScriptType _ = "DateTime" +data SingleDE = SingleDE +instance TypeScript SingleDE where getTypeScriptType _ = "single" + +data K8SDE = K8SDE +instance TypeScript K8SDE where getTypeScriptType _ = "k8s" + +data SingleNodeEnvironment = SingleNodeEnvironment + deriving (Eq, Show) +instance TypeScript SingleNodeEnvironment where getTypeScriptType _ = "single_node_env" + +data K8SEnvironment = K8SEnvironment + deriving (Eq, Show) +instance TypeScript K8SEnvironment where getTypeScriptType _ = "k8s_env" + +type family DeployEnvironment env = result | result -> env where + DeployEnvironment SingleNodeEnvironment = SingleDE + DeployEnvironment K8SEnvironment = K8SDE -data UserT f = User { +data UserT env f = User { _userUsername :: Columnar f T.Text , _userCreatedAt :: Columnar f UTCTime + , _userDeployEnvironment :: Columnar f (DeployEnvironment env) } diff --git a/test/NoOmitNothingFields.hs b/test/NoOmitNothingFields.hs index 2a3f2c6..bfa55de 100644 --- a/test/NoOmitNothingFields.hs +++ b/test/NoOmitNothingFields.hs @@ -1,4 +1,13 @@ -{-# LANGUAGE CPP, QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, KindSignatures #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} module NoOmitNothingFields (tests) where diff --git a/test/ObjectWithSingleFieldNoTagSingleConstructors.hs b/test/ObjectWithSingleFieldNoTagSingleConstructors.hs index 531cb00..4dcd75d 100644 --- a/test/ObjectWithSingleFieldNoTagSingleConstructors.hs +++ b/test/ObjectWithSingleFieldNoTagSingleConstructors.hs @@ -1,4 +1,13 @@ -{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, KindSignatures #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} module ObjectWithSingleFieldNoTagSingleConstructors (tests) where diff --git a/test/ObjectWithSingleFieldTagSingleConstructors.hs b/test/ObjectWithSingleFieldTagSingleConstructors.hs index ad5f4e6..027d92d 100644 --- a/test/ObjectWithSingleFieldTagSingleConstructors.hs +++ b/test/ObjectWithSingleFieldTagSingleConstructors.hs @@ -1,4 +1,13 @@ -{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, KindSignatures #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} module ObjectWithSingleFieldTagSingleConstructors (tests) where diff --git a/test/OmitNothingFields.hs b/test/OmitNothingFields.hs index 5a0de8c..77b53d5 100644 --- a/test/OmitNothingFields.hs +++ b/test/OmitNothingFields.hs @@ -1,4 +1,13 @@ -{-# LANGUAGE CPP, QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, KindSignatures #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} module OmitNothingFields (tests) where diff --git a/test/TaggedObjectNoTagSingleConstructors.hs b/test/TaggedObjectNoTagSingleConstructors.hs index 28684f0..a6f5ada 100644 --- a/test/TaggedObjectNoTagSingleConstructors.hs +++ b/test/TaggedObjectNoTagSingleConstructors.hs @@ -1,4 +1,13 @@ -{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, KindSignatures #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} module TaggedObjectNoTagSingleConstructors (tests) where diff --git a/test/TaggedObjectTagSingleConstructors.hs b/test/TaggedObjectTagSingleConstructors.hs index 3d927cb..a0d9b1c 100644 --- a/test/TaggedObjectTagSingleConstructors.hs +++ b/test/TaggedObjectTagSingleConstructors.hs @@ -1,4 +1,13 @@ -{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, KindSignatures #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} module TaggedObjectTagSingleConstructors (tests) where diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index e172d55..2433b8c 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -1,4 +1,13 @@ -{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, KindSignatures #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} module TestBoilerplate where @@ -66,7 +75,7 @@ testDeclarations testName aesonOptions = do <> getTypeScriptDeclarations (Proxy :: Proxy TwoField) <> getTypeScriptDeclarations (Proxy :: Proxy Hybrid) <> getTypeScriptDeclarations (Proxy :: Proxy TwoConstructor) - <> getTypeScriptDeclarations (Proxy :: Proxy Complex) + <> getTypeScriptDeclarations (Proxy :: Proxy (Complex Int)) <> getTypeScriptDeclarations (Proxy :: Proxy Optional) |] diff --git a/test/TwoElemArrayNoTagSingleConstructors.hs b/test/TwoElemArrayNoTagSingleConstructors.hs index f7a051d..34381b4 100644 --- a/test/TwoElemArrayNoTagSingleConstructors.hs +++ b/test/TwoElemArrayNoTagSingleConstructors.hs @@ -1,4 +1,13 @@ -{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, KindSignatures #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} module TwoElemArrayNoTagSingleConstructors (tests) where diff --git a/test/TwoElemArrayTagSingleConstructors.hs b/test/TwoElemArrayTagSingleConstructors.hs index 585a2c2..7f03dd8 100644 --- a/test/TwoElemArrayTagSingleConstructors.hs +++ b/test/TwoElemArrayTagSingleConstructors.hs @@ -1,4 +1,13 @@ -{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, KindSignatures #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} module TwoElemArrayTagSingleConstructors (tests) where diff --git a/test/UntaggedNoTagSingleConstructors.hs b/test/UntaggedNoTagSingleConstructors.hs index f72f324..3c8acd2 100644 --- a/test/UntaggedNoTagSingleConstructors.hs +++ b/test/UntaggedNoTagSingleConstructors.hs @@ -1,4 +1,13 @@ -{-# LANGUAGE CPP, QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, KindSignatures #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} module UntaggedNoTagSingleConstructors (tests) where diff --git a/test/UntaggedTagSingleConstructors.hs b/test/UntaggedTagSingleConstructors.hs index 9bd19ba..7eec0c8 100644 --- a/test/UntaggedTagSingleConstructors.hs +++ b/test/UntaggedTagSingleConstructors.hs @@ -1,4 +1,13 @@ -{-# LANGUAGE CPP, QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, KindSignatures #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} module UntaggedTagSingleConstructors (tests) where From 048178e3b38c5f6f8b15a3cf338aaefbbac3f6fd Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 02:15:35 -0800 Subject: [PATCH 014/208] Working on generic vars --- aeson-typescript.cabal | 3 +- src/Data/Aeson/TypeScript/TH.hs | 44 +++++++++++------------------- src/Data/Aeson/TypeScript/Types.hs | 25 ----------------- src/Data/Aeson/TypeScript/Util.hs | 4 --- 4 files changed, 17 insertions(+), 59 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index dbd8e1f..6ae1fdc 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -4,7 +4,7 @@ cabal-version: 1.12 -- -- see: https://github.com/sol/hpack -- --- hash: 96e15cc4fd8d1fc36ede72e6e1bfec4d8b9d9afb57339271b7d7f50461ebdfde +-- hash: ea1ac4278bda71f49e401fefc154441d5396b793b6de3612d989007c4de95cd3 name: aeson-typescript version: 0.2.0.0 @@ -63,7 +63,6 @@ test-suite aeson-typescript-test main-is: Spec.hs other-modules: HigherKind - HigherKindBeam Live LiveLogging NoOmitNothingFields diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 853d1e8..a333f93 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -120,9 +120,6 @@ module Data.Aeson.TypeScript.TH ( T1(..), T2(..), T3(..), - F1(..), - F2(..), - F3(..), -- TODO: expose the rest of these if necessary module Data.Aeson.TypeScript.Instances ) where @@ -130,8 +127,8 @@ module Data.Aeson.TypeScript.TH ( import Data.Aeson as A import Data.Aeson.TH as A import Data.Aeson.TypeScript.Formatting -import Data.Aeson.TypeScript.Types import Data.Aeson.TypeScript.Instances () +import Data.Aeson.TypeScript.Types import Data.Aeson.TypeScript.Util import qualified Data.List as L import qualified Data.Map as M @@ -141,6 +138,7 @@ import Data.Proxy import Data.String.Interpolate.IsString import Language.Haskell.TH hiding (stringE) import Language.Haskell.TH.Datatype +import qualified Language.Haskell.TH.Lib as TH -- | Generates a 'TypeScript' instance declaration for the given data type. @@ -154,13 +152,23 @@ deriveTypeScript options name = do assertExtensionsTurnedOn datatypeInfo + -- Build constraints: a TypeScript constraint for every constructor type and one for every type variable. + -- Probably overkill/not exactly right, but it's a start. let constructorPreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- mconcat $ fmap constructorFields datatypeCons] let typeVariablePreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- getDataTypeVars datatypeInfo] let predicates = constructorPreds <> typeVariablePreds - let constraints = foldl AppT (TupleT (length predicates)) predicates + + -- Build generic args: one for every T, T1, T2, etc. passed in + let isGenericVariable t = t `L.elem` allStarConstructors + let typeNames = [getTypeAsStringExp typ | typ <- getDataTypeVars datatypeInfo + , isGenericVariable typ] + let genericBrackets = case typeNames of + [] -> [|""|] + _ -> [|"<" <> (L.intercalate ", " $(listE $ fmap return typeNames)) <> ">"|] + [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where - getTypeScriptType _ = $(getTypeExpression datatypeInfo); + getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $genericBrackets; getTypeScriptDeclarations _ = $(getDeclarationFunctionBody options name datatypeInfo) getParentTypes _ = $(getGenericParentTypesExpression datatypeInfo) |] @@ -173,10 +181,9 @@ getDeclarationFunctionBody options _name datatypeInfo@(DatatypeInfo {..}) = do let genericVariablesExp = ListE [stringE x | x <- genericVariables] let interfaceNamesAndDeclarations = fmap (handleConstructor options datatypeInfo genericVariables) datatypeCons - let interfaceDeclarations = catMaybes $ fmap snd3 interfaceNamesAndDeclarations case interfaceNamesAndDeclarations of - [(_, Just interfaceDecl, True)] | datatypeVars == [] -> do + [(_, Just interfaceDecl, True)] | L.null datatypeVars -> do -- The type declaration is just a reference to a single interface, so we can omit the type part and drop the "I" from the interface name return $ ListE [AppE (VarE 'dropLeadingIFromInterfaceName) interfaceDecl] @@ -184,7 +191,7 @@ getDeclarationFunctionBody options _name datatypeInfo@(DatatypeInfo {..}) = do let interfaceNames = fmap fst3 interfaceNamesAndDeclarations let typeDeclaration = applyToArgsE (ConE 'TSTypeAlternatives) [stringE $ getTypeName datatypeName, genericVariablesExp, ListE interfaceNames] - return $ ListE (typeDeclaration : interfaceDeclarations) + return $ ListE (typeDeclaration : (mapMaybe snd3 interfaceNamesAndDeclarations)) -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> (Exp, Maybe Exp, Bool) @@ -258,18 +265,6 @@ getFieldType options (AppT (ConT name) t) getFieldType _ typ = (getTypeAsStringExp typ, getOptionalAsBoolExp typ) --- * Getting type expression - --- | Get an expression to be used for getTypeScriptType. --- For datatypes of kind * this is easy, since we can just evaluate the string literal in TH. --- For higher-kinded types, we need to make an expression which evaluates the template types and fills it in. -getTypeExpression :: DatatypeInfo -> Q Exp -getTypeExpression di@(getDataTypeVars -> []) = return $ stringE $ getTypeName (datatypeName di) -getTypeExpression di@(getDataTypeVars -> vars) = do - let baseName = getTypeName (datatypeName di) - let typeNames = ListE [getTypeAsStringExp typ | typ <- vars] - [|$(return (stringE baseName)) <> "<" <> (L.intercalate ", " $(return typeNames)) <> ">"|] - -- * Convenience functions -- | Convenience function to generate 'A.ToJSON', 'A.FromJSON', and 'TypeScript' instances simultaneously, so the instances are guaranteed to be in sync. @@ -288,10 +283,3 @@ deriveJSONAndTypeScript options name = (<>) <$> (deriveTypeScript options name) getGenericParentTypesExpression :: DatatypeInfo -> Q Exp getGenericParentTypesExpression (DatatypeInfo {..}) = return $ ListE [AppE (ConE 'TSType) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) typ)) | typ <- types] where types = mconcat $ fmap constructorFields datatypeCons - -chooseDataTypeVars _ _ [] = [] -chooseDataTypeVars starConstructors polyStarConstructors (x:xs) = case x of - PlainTV _ -> (head starConstructors) : chooseDataTypeVars (tail starConstructors) polyStarConstructors xs - KindedTV _ StarT -> (head starConstructors) : chooseDataTypeVars (tail starConstructors) polyStarConstructors xs - -- higher -> (higher) : chooseDataTypeVars starConstructors (tail polyStarConstructors) xs - _ -> (head allPolyStarConstructors) : chooseDataTypeVars starConstructors (tail polyStarConstructors) xs diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 1158547..3da0fe1 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -133,28 +133,3 @@ instance TypeScript T10 where getTypeScriptType _ = "T10" allStarConstructors = [ConT ''T1, ConT ''T2, ConT ''T3, ConT ''T4, ConT ''T5, ConT ''T6, ConT ''T7, ConT ''T8, ConT ''T9, ConT ''T10] -data F (a :: k) = F -data F1 a = F1 -data F2 a = F2 -data F3 a = F3 -data F4 a = F4 -data F5 a = F5 -data F6 a = F6 -data F7 a = F7 -data F8 a = F8 -data F9 a = F9 -data F10 a = F10 - -instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) -instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F1 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) -instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F2 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) -instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F3 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) -instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F4 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) -instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F5 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) -instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F6 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) -instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F7 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) -instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F8 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) -instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F9 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) -instance (Typeable a, Typeable k, TypeScript a) => TypeScript (F10 (a :: k)) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) - -allPolyStarConstructors = [ConT ''F1, ConT ''F2, ConT ''F3, ConT ''F4, ConT ''F5, ConT ''F6, ConT ''F7, ConT ''F8, ConT ''F9, ConT ''F10] diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index c865fb3..e1fe0a3 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -4,12 +4,8 @@ module Data.Aeson.TypeScript.Util where import Control.Monad import Data.Aeson as A -import Data.Aeson.TH as A -import Data.Aeson.TypeScript.Formatting import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Types -import qualified Data.Map as M -import Data.Maybe import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString From d704d6d332b4d0a770b6d75d5bc9050dfff51772 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 02:32:47 -0800 Subject: [PATCH 015/208] More cleaning in TH.hs --- src/Data/Aeson/TypeScript/TH.hs | 34 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index a333f93..c5422bd 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -170,7 +170,7 @@ deriveTypeScript options name = do [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $genericBrackets; getTypeScriptDeclarations _ = $(getDeclarationFunctionBody options name datatypeInfo) - getParentTypes _ = $(getGenericParentTypesExpression datatypeInfo) + getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] | t <- mconcat $ fmap constructorFields datatypeCons]) |] getDeclarationFunctionBody :: Options -> p -> DatatypeInfo -> Q Exp @@ -180,18 +180,14 @@ getDeclarationFunctionBody options _name datatypeInfo@(DatatypeInfo {..}) = do | otherwise -> ["T" <> show j | j <- [1..(length datatypeVars)]] let genericVariablesExp = ListE [stringE x | x <- genericVariables] - let interfaceNamesAndDeclarations = fmap (handleConstructor options datatypeInfo genericVariables) datatypeCons - - case interfaceNamesAndDeclarations of + case fmap (handleConstructor options datatypeInfo genericVariables) datatypeCons of [(_, Just interfaceDecl, True)] | L.null datatypeVars -> do -- The type declaration is just a reference to a single interface, so we can omit the type part and drop the "I" from the interface name - return $ ListE [AppE (VarE 'dropLeadingIFromInterfaceName) interfaceDecl] - - _ -> do - let interfaceNames = fmap fst3 interfaceNamesAndDeclarations + [|dropLeadingIFromInterfaceName $(return interfaceDecl)|] - let typeDeclaration = applyToArgsE (ConE 'TSTypeAlternatives) [stringE $ getTypeName datatypeName, genericVariablesExp, ListE interfaceNames] - return $ ListE (typeDeclaration : (mapMaybe snd3 interfaceNamesAndDeclarations)) + xs -> do + typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName datatypeName) $(return genericVariablesExp) $(listE $ fmap (return . fst3) xs)|] + return $ ListE (typeDeclaration : (mapMaybe snd3 xs)) -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> (Exp, Maybe Exp, Bool) @@ -221,7 +217,7 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn interfaceName = getInterfaceName ci interfaceNameWithBrackets = interfaceName <> getGenericBrackets genericVariables - tupleEncoding = Just $ applyToArgsE (ConE 'TSTypeAlternatives) [stringE $ interfaceName + tupleEncoding = Just $ applyToArgsE (ConE 'TSTypeAlternatives) [stringE interfaceName , ListE [stringE x | x <- genericVariables] , ListE [getTypeAsStringExp contentsTupleType]] @@ -257,12 +253,12 @@ getTSFields options namesAndTypes = fieldTyp) | (nameString, typ) <- namesAndTypes , let (fieldTyp, optAsBool) = getFieldType options typ] - -getFieldType :: Options -> Type -> (Exp, Exp) -getFieldType options (AppT (ConT name) t) - | not (omitNothingFields options) && name == ''Maybe - = (AppE (AppE (VarE 'mappend) (getTypeAsStringExp t)) (stringE " | null"), getOptionalAsBoolExp t) -getFieldType _ typ = (getTypeAsStringExp typ, getOptionalAsBoolExp typ) + where + getFieldType :: Options -> Type -> (Exp, Exp) + getFieldType options (AppT (ConT name) t) + | not (omitNothingFields options) && name == ''Maybe + = (AppE (AppE (VarE 'mappend) (getTypeAsStringExp t)) (stringE " | null"), getOptionalAsBoolExp t) + getFieldType _ typ = (getTypeAsStringExp typ, getOptionalAsBoolExp typ) -- * Convenience functions @@ -279,7 +275,3 @@ deriveJSONAndTypeScript :: Options -> Q [Dec] deriveJSONAndTypeScript options name = (<>) <$> (deriveTypeScript options name) <*> (A.deriveJSON options name) --- | For the fully generic instance, the parent types are the types inside the constructors -getGenericParentTypesExpression :: DatatypeInfo -> Q Exp -getGenericParentTypesExpression (DatatypeInfo {..}) = return $ ListE [AppE (ConE 'TSType) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) typ)) | typ <- types] - where types = mconcat $ fmap constructorFields datatypeCons From bcdaf7f9781c76cd961831dfa78dfccf8131575f Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 02:40:18 -0800 Subject: [PATCH 016/208] Make handleConstructor run in Q --- src/Data/Aeson/TypeScript/TH.hs | 38 ++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index c5422bd..1ea588a 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -1,4 +1,16 @@ -{-# LANGUAGE CPP, QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, ExistentialQuantification, FlexibleInstances, NamedFieldPuns, MultiWayIf, ViewPatterns, PolyKinds #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE LambdaCase #-} {-| Module: Data.Aeson.TypeScript.TH @@ -180,7 +192,7 @@ getDeclarationFunctionBody options _name datatypeInfo@(DatatypeInfo {..}) = do | otherwise -> ["T" <> show j | j <- [1..(length datatypeVars)]] let genericVariablesExp = ListE [stringE x | x <- genericVariables] - case fmap (handleConstructor options datatypeInfo genericVariables) datatypeCons of + mapM (handleConstructor options datatypeInfo genericVariables) datatypeCons >>= \case [(_, Just interfaceDecl, True)] | L.null datatypeVars -> do -- The type declaration is just a reference to a single interface, so we can omit the type part and drop the "I" from the interface name [|dropLeadingIFromInterfaceName $(return interfaceDecl)|] @@ -190,20 +202,20 @@ getDeclarationFunctionBody options _name datatypeInfo@(DatatypeInfo {..}) = do return $ ListE (typeDeclaration : (mapMaybe snd3 xs)) -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration -handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> (Exp, Maybe Exp, Bool) +handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> Q (Exp, Maybe Exp, Bool) handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = - if | isSingleConstructorType && not (getTagSingleConstructors options) -> (stringE interfaceNameWithBrackets, singleConstructorEncoding, True) + if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> return (stringE interfaceNameWithBrackets, singleConstructorEncoding, True) - | allConstructorsAreNullary datatypeCons && allNullaryToStringTag options -> stringEncoding + | allConstructorsAreNullary datatypeCons && allNullaryToStringTag options -> return stringEncoding -- With UntaggedValue, nullary constructors are encoded as strings - | (isUntaggedValue $ sumEncoding options) && isConstructorNullary ci -> stringEncoding + | (isUntaggedValue $ sumEncoding options) && isConstructorNullary ci -> return stringEncoding -- Treat as a sum - | isObjectWithSingleField $ sumEncoding options -> (stringE [i|{#{show constructorNameToUse}: #{interfaceNameWithBrackets}}|], singleConstructorEncoding, False) - | isTwoElemArray $ sumEncoding options -> (stringE [i|[#{show constructorNameToUse}, #{interfaceNameWithBrackets}]|], singleConstructorEncoding, False) - | isUntaggedValue $ sumEncoding options -> (stringE interfaceNameWithBrackets, singleConstructorEncoding, True) - | otherwise -> (stringE interfaceNameWithBrackets, taggedConstructorEncoding, True) + | isObjectWithSingleField $ sumEncoding options -> return (stringE [i|{#{show constructorNameToUse}: #{interfaceNameWithBrackets}}|], singleConstructorEncoding, False) + | isTwoElemArray $ sumEncoding options -> return (stringE [i|[#{show constructorNameToUse}, #{interfaceNameWithBrackets}]|], singleConstructorEncoding, False) + | isUntaggedValue $ sumEncoding options -> return (stringE interfaceNameWithBrackets, singleConstructorEncoding, True) + | otherwise -> return (stringE interfaceNameWithBrackets, taggedConstructorEncoding, True) where stringEncoding = (stringE [i|"#{(constructorTagModifier options) $ getTypeName (constructorName ci)}"|], Nothing, True) @@ -214,7 +226,7 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn taggedConstructorEncoding = Just $ assembleInterfaceDeclaration (ListE (tagField ++ getTSFields options namesAndTypes)) -- * Type declaration to use - interfaceName = getInterfaceName ci + interfaceName = "I" <> (lastNameComponent' $ constructorName ci) interfaceNameWithBrackets = interfaceName <> getGenericBrackets genericVariables tupleEncoding = Just $ applyToArgsE (ConE 'TSTypeAlternatives) [stringE interfaceName @@ -234,10 +246,6 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn (stringE [i|"#{constructorNameToUse}"|]))] _ -> [] - isSingleConstructorType = length datatypeCons == 1 - - getInterfaceName (constructorName -> x) = "I" <> (lastNameComponent' x) - constructorNameToUse = (constructorTagModifier options) $ lastNameComponent' (constructorName ci) contentsTupleType = getTupleType (constructorFields ci) From d3aed880fd32e5fc258f3ead30be0c5fa06dae2f Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 02:53:56 -0800 Subject: [PATCH 017/208] More Q stuff in T.hs --- src/Data/Aeson/TypeScript/TH.hs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 1ea588a..442b7fc 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -3,6 +3,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleInstances #-} @@ -204,7 +205,7 @@ getDeclarationFunctionBody options _name datatypeInfo@(DatatypeInfo {..}) = do -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> Q (Exp, Maybe Exp, Bool) handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = - if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> return (stringE interfaceNameWithBrackets, singleConstructorEncoding, True) + if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> ((stringE interfaceNameWithBrackets, , True) . Just) <$> singleConstructorEncoding | allConstructorsAreNullary datatypeCons && allNullaryToStringTag options -> return stringEncoding @@ -212,26 +213,29 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn | (isUntaggedValue $ sumEncoding options) && isConstructorNullary ci -> return stringEncoding -- Treat as a sum - | isObjectWithSingleField $ sumEncoding options -> return (stringE [i|{#{show constructorNameToUse}: #{interfaceNameWithBrackets}}|], singleConstructorEncoding, False) - | isTwoElemArray $ sumEncoding options -> return (stringE [i|[#{show constructorNameToUse}, #{interfaceNameWithBrackets}]|], singleConstructorEncoding, False) - | isUntaggedValue $ sumEncoding options -> return (stringE interfaceNameWithBrackets, singleConstructorEncoding, True) - | otherwise -> return (stringE interfaceNameWithBrackets, taggedConstructorEncoding, True) + | isObjectWithSingleField $ sumEncoding options -> ((stringE [i|{#{show constructorNameToUse}: #{interfaceNameWithBrackets}}|], , False) . Just) <$> singleConstructorEncoding + | isTwoElemArray $ sumEncoding options -> ((stringE [i|[#{show constructorNameToUse}, #{interfaceNameWithBrackets}]|], , False) . Just) <$> singleConstructorEncoding + | isUntaggedValue $ sumEncoding options -> ((stringE interfaceNameWithBrackets, , True) . Just) <$> singleConstructorEncoding + | otherwise -> do + tagField :: [Exp] <- case sumEncoding options of + TaggedObject tagFieldName _ -> (\x -> [x]) <$> [|TSField False $(TH.stringE tagFieldName) $(TH.stringE [i|"#{constructorNameToUse}"|])|] + _ -> return [] + + let decl = assembleInterfaceDeclaration (ListE (tagField ++ getTSFields options namesAndTypes)) + + return (stringE interfaceNameWithBrackets, Just decl, True) where stringEncoding = (stringE [i|"#{(constructorTagModifier options) $ getTypeName (constructorName ci)}"|], Nothing, True) singleConstructorEncoding = if | constructorVariant ci == NormalConstructor -> tupleEncoding - | otherwise -> Just $ assembleInterfaceDeclaration (ListE (getTSFields options namesAndTypes)) - - taggedConstructorEncoding = Just $ assembleInterfaceDeclaration (ListE (tagField ++ getTSFields options namesAndTypes)) + | otherwise -> return $ assembleInterfaceDeclaration (ListE (getTSFields options namesAndTypes)) -- * Type declaration to use interfaceName = "I" <> (lastNameComponent' $ constructorName ci) interfaceNameWithBrackets = interfaceName <> getGenericBrackets genericVariables - tupleEncoding = Just $ applyToArgsE (ConE 'TSTypeAlternatives) [stringE interfaceName - , ListE [stringE x | x <- genericVariables] - , ListE [getTypeAsStringExp contentsTupleType]] + tupleEncoding = [|TSTypeAlternatives $(TH.stringE interfaceName) $(listE [TH.stringE x | x <- genericVariables]) (getTypeScriptType (Proxy :: Proxy $(return contentsTupleType)))|] namesAndTypes :: [(String, Type)] = case constructorVariant ci of RecordConstructor names -> zip (fmap ((fieldLabelModifier options) . lastNameComponent') names) (constructorFields ci) @@ -240,12 +244,6 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn | otherwise -> [(contentsFieldName, contentsTupleType)] _ -> [(constructorNameToUse, contentsTupleType)] - tagField = case sumEncoding options of - TaggedObject tagFieldName _ -> [(AppE (AppE (AppE (ConE 'TSField) (ConE 'False)) - (stringE tagFieldName)) - (stringE [i|"#{constructorNameToUse}"|]))] - _ -> [] - constructorNameToUse = (constructorTagModifier options) $ lastNameComponent' (constructorName ci) contentsTupleType = getTupleType (constructorFields ci) From 3aec96160febf87aa1bbe5acc2c19af6de2274f7 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 02:56:54 -0800 Subject: [PATCH 018/208] More Q-ification --- src/Data/Aeson/TypeScript/TH.hs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 442b7fc..e78a260 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -218,10 +218,10 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn | isUntaggedValue $ sumEncoding options -> ((stringE interfaceNameWithBrackets, , True) . Just) <$> singleConstructorEncoding | otherwise -> do tagField :: [Exp] <- case sumEncoding options of - TaggedObject tagFieldName _ -> (\x -> [x]) <$> [|TSField False $(TH.stringE tagFieldName) $(TH.stringE [i|"#{constructorNameToUse}"|])|] + TaggedObject tagFieldName _ -> (: []) <$> [|TSField False $(TH.stringE tagFieldName) $(TH.stringE [i|"#{constructorNameToUse}"|])|] _ -> return [] - let decl = assembleInterfaceDeclaration (ListE (tagField ++ getTSFields options namesAndTypes)) + decl <- assembleInterfaceDeclaration (ListE (tagField ++ getTSFields options namesAndTypes)) return (stringE interfaceNameWithBrackets, Just decl, True) @@ -229,7 +229,7 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn stringEncoding = (stringE [i|"#{(constructorTagModifier options) $ getTypeName (constructorName ci)}"|], Nothing, True) singleConstructorEncoding = if | constructorVariant ci == NormalConstructor -> tupleEncoding - | otherwise -> return $ assembleInterfaceDeclaration (ListE (getTSFields options namesAndTypes)) + | otherwise -> assembleInterfaceDeclaration (ListE (getTSFields options namesAndTypes)) -- * Type declaration to use interfaceName = "I" <> (lastNameComponent' $ constructorName ci) @@ -247,8 +247,7 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn constructorNameToUse = (constructorTagModifier options) $ lastNameComponent' (constructorName ci) contentsTupleType = getTupleType (constructorFields ci) - assembleInterfaceDeclaration members = AppE (AppE (AppE (ConE 'TSInterfaceDeclaration) (stringE interfaceName)) genericVariablesExp) members where - genericVariablesExp = (ListE [stringE x | x <- genericVariables]) + assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) $(listE [TH.stringE x | x <- genericVariables]) $(return members)|] -- | Helper for handleConstructor From bf24666a07100833f7d9438273412e6c71aa25db Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 03:19:33 -0800 Subject: [PATCH 019/208] Progress, still need to fix higher kind tests --- src/Data/Aeson/TypeScript/TH.hs | 32 +++++++++++++++----------------- test/Live.hs | 6 ++++-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index e78a260..1cde454 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -144,7 +144,6 @@ import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Types import Data.Aeson.TypeScript.Util import qualified Data.List as L -import qualified Data.Map as M import Data.Maybe import Data.Monoid import Data.Proxy @@ -174,33 +173,32 @@ deriveTypeScript options name = do -- Build generic args: one for every T, T1, T2, etc. passed in let isGenericVariable t = t `L.elem` allStarConstructors - let typeNames = [getTypeAsStringExp typ | typ <- getDataTypeVars datatypeInfo - , isGenericVariable typ] + let typeNames = [typ | typ <- getDataTypeVars datatypeInfo, isGenericVariable typ] let genericBrackets = case typeNames of [] -> [|""|] - _ -> [|"<" <> (L.intercalate ", " $(listE $ fmap return typeNames)) <> ">"|] + _ -> [|"<" <> (L.intercalate ", " $(listE $ fmap (return . getTypeAsStringExp) typeNames)) <> ">"|] + + let typeNameToString (ConT n) = nameBase n + typeNameToString _ = "?" + let stringTypeNames = fmap typeNameToString typeNames [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $genericBrackets; - getTypeScriptDeclarations _ = $(getDeclarationFunctionBody options name datatypeInfo) + getTypeScriptDeclarations _ = $(getDeclarationFunctionBody options datatypeInfo stringTypeNames) getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] | t <- mconcat $ fmap constructorFields datatypeCons]) |] -getDeclarationFunctionBody :: Options -> p -> DatatypeInfo -> Q Exp -getDeclarationFunctionBody options _name datatypeInfo@(DatatypeInfo {..}) = do - -- If name is higher-kinded, add generic variables to the type and interface declarations - let genericVariables :: [String] = if | length datatypeVars == 1 -> ["T"] - | otherwise -> ["T" <> show j | j <- [1..(length datatypeVars)]] - let genericVariablesExp = ListE [stringE x | x <- genericVariables] - +getDeclarationFunctionBody :: Options -> DatatypeInfo -> [String] -> Q Exp +getDeclarationFunctionBody options datatypeInfo@(DatatypeInfo {..}) genericVariables = do mapM (handleConstructor options datatypeInfo genericVariables) datatypeCons >>= \case [(_, Just interfaceDecl, True)] | L.null datatypeVars -> do -- The type declaration is just a reference to a single interface, so we can omit the type part and drop the "I" from the interface name - [|dropLeadingIFromInterfaceName $(return interfaceDecl)|] - + [| [dropLeadingIFromInterfaceName $(return interfaceDecl)] |] xs -> do - typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName datatypeName) $(return genericVariablesExp) $(listE $ fmap (return . fst3) xs)|] - return $ ListE (typeDeclaration : (mapMaybe snd3 xs)) + typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName datatypeName) + $(listE [TH.stringE x | x <- genericVariables]) + $(listE $ fmap (return . fst3) xs)|] + [| $(return typeDeclaration) : $(listE (fmap return $ mapMaybe snd3 xs)) |] -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> Q (Exp, Maybe Exp, Bool) @@ -235,7 +233,7 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn interfaceName = "I" <> (lastNameComponent' $ constructorName ci) interfaceNameWithBrackets = interfaceName <> getGenericBrackets genericVariables - tupleEncoding = [|TSTypeAlternatives $(TH.stringE interfaceName) $(listE [TH.stringE x | x <- genericVariables]) (getTypeScriptType (Proxy :: Proxy $(return contentsTupleType)))|] + tupleEncoding = [|TSTypeAlternatives $(TH.stringE interfaceName) $(listE [TH.stringE x | x <- genericVariables]) [getTypeScriptType (Proxy :: Proxy $(return contentsTupleType))]|] namesAndTypes :: [(String, Type)] = case constructorVariant ci of RecordConstructor names -> zip (fmap ((fieldLabelModifier options) . lastNameComponent') names) (constructorFields ci) diff --git a/test/Live.hs b/test/Live.hs index 617d7d7..0374deb 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -76,5 +76,7 @@ data UserT env f = User { $(deriveTypeScript A.defaultOptions ''UserT) --- main = do --- putStrLn $(stringE . pprint =<< (deriveTypeScript A.defaultOptions ''TestT)) + +data HigherKind a = HigherKind { higherKindList :: [a] } +$(deriveTypeScript A.defaultOptions ''HigherKind) + From 4b956b00d5d479aacf7eb3dd65dd6c2096b49c3b Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 18:27:11 -0800 Subject: [PATCH 020/208] More quasiquoting --- src/Data/Aeson/TypeScript/TH.hs | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 1cde454..5b7d675 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -137,6 +137,7 @@ module Data.Aeson.TypeScript.TH ( module Data.Aeson.TypeScript.Instances ) where +import Control.Monad import Data.Aeson as A import Data.Aeson.TH as A import Data.Aeson.TypeScript.Formatting @@ -185,7 +186,8 @@ deriveTypeScript options name = do [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $genericBrackets; getTypeScriptDeclarations _ = $(getDeclarationFunctionBody options datatypeInfo stringTypeNames) - getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] | t <- mconcat $ fmap constructorFields datatypeCons]) + getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] + | t <- mconcat $ fmap constructorFields datatypeCons]) |] getDeclarationFunctionBody :: Options -> DatatypeInfo -> [String] -> Q Exp @@ -219,7 +221,8 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn TaggedObject tagFieldName _ -> (: []) <$> [|TSField False $(TH.stringE tagFieldName) $(TH.stringE [i|"#{constructorNameToUse}"|])|] _ -> return [] - decl <- assembleInterfaceDeclaration (ListE (tagField ++ getTSFields options namesAndTypes)) + tsFields <- getTSFields options namesAndTypes + decl <- assembleInterfaceDeclaration (ListE (tagField ++ tsFields)) return (stringE interfaceNameWithBrackets, Just decl, True) @@ -227,7 +230,9 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn stringEncoding = (stringE [i|"#{(constructorTagModifier options) $ getTypeName (constructorName ci)}"|], Nothing, True) singleConstructorEncoding = if | constructorVariant ci == NormalConstructor -> tupleEncoding - | otherwise -> assembleInterfaceDeclaration (ListE (getTSFields options namesAndTypes)) + | otherwise -> do + tsFields <- getTSFields options namesAndTypes + assembleInterfaceDeclaration (ListE tsFields) -- * Type declaration to use interfaceName = "I" <> (lastNameComponent' $ constructorName ci) @@ -249,19 +254,15 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn -- | Helper for handleConstructor -getTSFields :: Options -> [(String, Type)] -> [Exp] -getTSFields options namesAndTypes = - [ (AppE (AppE (AppE (ConE 'TSField) optAsBool) - (stringE nameString)) - fieldTyp) - | (nameString, typ) <- namesAndTypes - , let (fieldTyp, optAsBool) = getFieldType options typ] - where - getFieldType :: Options -> Type -> (Exp, Exp) - getFieldType options (AppT (ConT name) t) - | not (omitNothingFields options) && name == ''Maybe - = (AppE (AppE (VarE 'mappend) (getTypeAsStringExp t)) (stringE " | null"), getOptionalAsBoolExp t) - getFieldType _ typ = (getTypeAsStringExp typ, getOptionalAsBoolExp typ) +getTSFields :: Options -> [(String, Type)] -> Q [Exp] +getTSFields options namesAndTypes = do + forM namesAndTypes $ \(nameString, typ) -> do + (fieldTyp, optAsBool) <- case typ of + (AppT (ConT name) t) | not (omitNothingFields options) && name == ''Maybe -> do + fieldTyp <- [|$(return $ getTypeAsStringExp t) <> " | null"|] + return (fieldTyp, getOptionalAsBoolExp t) + _ -> return (getTypeAsStringExp typ, getOptionalAsBoolExp typ) + [| TSField $(return optAsBool) nameString $(return fieldTyp) |] -- * Convenience functions From 0c972b15aa2e15492813af921d0bb918ddcdfc5e Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 18:28:06 -0800 Subject: [PATCH 021/208] Another tidying --- src/Data/Aeson/TypeScript/TH.hs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 5b7d675..0571d23 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -252,17 +252,15 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) $(listE [TH.stringE x | x <- genericVariables]) $(return members)|] - --- | Helper for handleConstructor -getTSFields :: Options -> [(String, Type)] -> Q [Exp] -getTSFields options namesAndTypes = do - forM namesAndTypes $ \(nameString, typ) -> do - (fieldTyp, optAsBool) <- case typ of - (AppT (ConT name) t) | not (omitNothingFields options) && name == ''Maybe -> do - fieldTyp <- [|$(return $ getTypeAsStringExp t) <> " | null"|] - return (fieldTyp, getOptionalAsBoolExp t) - _ -> return (getTypeAsStringExp typ, getOptionalAsBoolExp typ) - [| TSField $(return optAsBool) nameString $(return fieldTyp) |] + getTSFields :: Options -> [(String, Type)] -> Q [Exp] + getTSFields options namesAndTypes = do + forM namesAndTypes $ \(nameString, typ) -> do + (fieldTyp, optAsBool) <- case typ of + (AppT (ConT name) t) | not (omitNothingFields options) && name == ''Maybe -> do + fieldTyp <- [|$(return $ getTypeAsStringExp t) <> " | null"|] + return (fieldTyp, getOptionalAsBoolExp t) + _ -> return (getTypeAsStringExp typ, getOptionalAsBoolExp typ) + [| TSField $(return optAsBool) nameString $(return fieldTyp) |] -- * Convenience functions From e6d85ec0f6c73550d5b7e4bde62de723e4d9d8f4 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 18:59:16 -0800 Subject: [PATCH 022/208] Write deriveTypeScriptLookupType --- src/Data/Aeson/TypeScript/TH.hs | 23 +++++++++++++++++++++++ test/Live.hs | 9 +++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 0571d23..15e8026 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -112,6 +112,7 @@ main = putStrLn $ 'formatTSDeclarations' ( module Data.Aeson.TypeScript.TH ( deriveTypeScript, + deriveTypeScriptLookupType, -- * The main typeclass TypeScript(..), @@ -263,6 +264,28 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn [| TSField $(return optAsBool) nameString $(return fieldTyp) |] +-- | Generates a 'TypeScript' declaration for a closed type family as a lookup type. +deriveTypeScriptLookupType :: Name + -- ^ Name of a type family. + -> String + -- ^ Name of the declaration to derive. + -> Q [Dec] +deriveTypeScriptLookupType name declNameStr = do + info <- reify name + reportWarning [i|Got datatypeInfo: #{info}|] + case info of + FamilyI (ClosedTypeFamilyD (TypeFamilyHead name vars sig maybeInject) eqns) decs -> do + fields <- forM eqns $ \case + TySynEqn Nothing (AppT (ConT _) (ConT arg)) (ConT result) -> + [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(conT result))) |] + x -> fail [i|Don't know how to handle type family equation: '#{x}'|] + + expr <- [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] $(listE $ fmap return fields) |] + + return [FunD (mkName declNameStr) [Clause [] (NormalB (ListE [expr])) []]] + + _ -> fail [i|Expected a close type family; got #{info}|] + -- * Convenience functions -- | Convenience function to generate 'A.ToJSON', 'A.FromJSON', and 'TypeScript' instances simultaneously, so the instances are guaranteed to be in sync. diff --git a/test/Live.hs b/test/Live.hs index 0374deb..8ebdf9e 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -50,18 +50,18 @@ instance TypeScript Identity where getTypeScriptType _ = "any" data SingleDE = SingleDE -instance TypeScript SingleDE where getTypeScriptType _ = "single" +instance TypeScript SingleDE where getTypeScriptType _ = [i|"single"|] data K8SDE = K8SDE -instance TypeScript K8SDE where getTypeScriptType _ = "k8s" +instance TypeScript K8SDE where getTypeScriptType _ = [i|"k8s"|] data SingleNodeEnvironment = SingleNodeEnvironment deriving (Eq, Show) -instance TypeScript SingleNodeEnvironment where getTypeScriptType _ = "single_node_env" +instance TypeScript SingleNodeEnvironment where getTypeScriptType _ = [i|"single_node_env"|] data K8SEnvironment = K8SEnvironment deriving (Eq, Show) -instance TypeScript K8SEnvironment where getTypeScriptType _ = "k8s_env" +instance TypeScript K8SEnvironment where getTypeScriptType _ = [i|"k8s_env"|] type family DeployEnvironment env = result | result -> env where DeployEnvironment SingleNodeEnvironment = SingleDE @@ -73,6 +73,7 @@ data UserT env f = User { , _userDeployEnvironment :: Columnar f (DeployEnvironment env) } +$(deriveTypeScriptLookupType ''DeployEnvironment "deployEnvDecl") $(deriveTypeScript A.defaultOptions ''UserT) From d08848d389e8579ba9c12bb2feff84aba48cea15 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 19:06:12 -0800 Subject: [PATCH 023/208] Move lookup derivation to its own file --- aeson-typescript.cabal | 4 ++- src/Data/Aeson/TypeScript/Lookup.hs | 48 +++++++++++++++++++++++++++++ src/Data/Aeson/TypeScript/TH.hs | 23 +------------- 3 files changed, 52 insertions(+), 23 deletions(-) create mode 100644 src/Data/Aeson/TypeScript/Lookup.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 6ae1fdc..33236be 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -4,7 +4,7 @@ cabal-version: 1.12 -- -- see: https://github.com/sol/hpack -- --- hash: ea1ac4278bda71f49e401fefc154441d5396b793b6de3612d989007c4de95cd3 +-- hash: a8a23c594d80e27e34cb591cabb2a8ed3849b7bb2e7fe79c05fe526992ad6898 name: aeson-typescript version: 0.2.0.0 @@ -39,6 +39,7 @@ library other-modules: Data.Aeson.TypeScript.Formatting Data.Aeson.TypeScript.Instances + Data.Aeson.TypeScript.Lookup Data.Aeson.TypeScript.Types Data.Aeson.TypeScript.Util Paths_aeson_typescript @@ -79,6 +80,7 @@ test-suite aeson-typescript-test Util Data.Aeson.TypeScript.Formatting Data.Aeson.TypeScript.Instances + Data.Aeson.TypeScript.Lookup Data.Aeson.TypeScript.Recursive Data.Aeson.TypeScript.TH Data.Aeson.TypeScript.Types diff --git a/src/Data/Aeson/TypeScript/Lookup.hs b/src/Data/Aeson/TypeScript/Lookup.hs new file mode 100644 index 0000000..2881e30 --- /dev/null +++ b/src/Data/Aeson/TypeScript/Lookup.hs @@ -0,0 +1,48 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE LambdaCase #-} + +module Data.Aeson.TypeScript.Lookup where + +import Control.Monad +import Data.Aeson.TypeScript.Instances () +import Data.Aeson.TypeScript.Types +import Data.Monoid +import Data.Proxy +import Data.String.Interpolate.IsString +import Language.Haskell.TH hiding (stringE) +import qualified Language.Haskell.TH.Lib as TH + + +-- | Generates a 'TypeScript' declaration for a closed type family as a lookup type. +deriveTypeScriptLookupType :: Name + -- ^ Name of a type family. + -> String + -- ^ Name of the declaration to derive. + -> Q [Dec] +deriveTypeScriptLookupType name declNameStr = do + info <- reify name + reportWarning [i|Got datatypeInfo: #{info}|] + case info of + FamilyI (ClosedTypeFamilyD (TypeFamilyHead name vars sig maybeInject) eqns) decs -> do + fields <- forM eqns $ \case + TySynEqn Nothing (AppT (ConT _) (ConT arg)) (ConT result) -> + [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(conT result))) |] + x -> fail [i|Don't know how to handle type family equation: '#{x}'|] + + expr <- [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] $(listE $ fmap return fields) |] + + return [FunD (mkName declNameStr) [Clause [] (NormalB (ListE [expr])) []]] + + _ -> fail [i|Expected a close type family; got #{info}|] diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 15e8026..cce0c0a 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -143,6 +143,7 @@ import Data.Aeson as A import Data.Aeson.TH as A import Data.Aeson.TypeScript.Formatting import Data.Aeson.TypeScript.Instances () +import Data.Aeson.TypeScript.Lookup import Data.Aeson.TypeScript.Types import Data.Aeson.TypeScript.Util import qualified Data.List as L @@ -264,28 +265,6 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn [| TSField $(return optAsBool) nameString $(return fieldTyp) |] --- | Generates a 'TypeScript' declaration for a closed type family as a lookup type. -deriveTypeScriptLookupType :: Name - -- ^ Name of a type family. - -> String - -- ^ Name of the declaration to derive. - -> Q [Dec] -deriveTypeScriptLookupType name declNameStr = do - info <- reify name - reportWarning [i|Got datatypeInfo: #{info}|] - case info of - FamilyI (ClosedTypeFamilyD (TypeFamilyHead name vars sig maybeInject) eqns) decs -> do - fields <- forM eqns $ \case - TySynEqn Nothing (AppT (ConT _) (ConT arg)) (ConT result) -> - [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(conT result))) |] - x -> fail [i|Don't know how to handle type family equation: '#{x}'|] - - expr <- [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] $(listE $ fmap return fields) |] - - return [FunD (mkName declNameStr) [Clause [] (NormalB (ListE [expr])) []]] - - _ -> fail [i|Expected a close type family; got #{info}|] - -- * Convenience functions -- | Convenience function to generate 'A.ToJSON', 'A.FromJSON', and 'TypeScript' instances simultaneously, so the instances are guaranteed to be in sync. From 983eac31b170b94dcdd178fe12431430c4d34b6a Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 19:17:55 -0800 Subject: [PATCH 024/208] Simplifying so we can get to WriterT --- src/Data/Aeson/TypeScript/TH.hs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index cce0c0a..121e943 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -194,20 +194,16 @@ deriveTypeScript options name = do getDeclarationFunctionBody :: Options -> DatatypeInfo -> [String] -> Q Exp getDeclarationFunctionBody options datatypeInfo@(DatatypeInfo {..}) genericVariables = do - mapM (handleConstructor options datatypeInfo genericVariables) datatypeCons >>= \case - [(_, Just interfaceDecl, True)] | L.null datatypeVars -> do - -- The type declaration is just a reference to a single interface, so we can omit the type part and drop the "I" from the interface name - [| [dropLeadingIFromInterfaceName $(return interfaceDecl)] |] - xs -> do - typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName datatypeName) - $(listE [TH.stringE x | x <- genericVariables]) - $(listE $ fmap (return . fst3) xs)|] - [| $(return typeDeclaration) : $(listE (fmap return $ mapMaybe snd3 xs)) |] + xs <- mapM (handleConstructor options datatypeInfo genericVariables) datatypeCons + typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName datatypeName) + $(listE [TH.stringE x | x <- genericVariables]) + $(listE $ fmap (return . fst) xs)|] + [| $(return typeDeclaration) : $(listE (fmap return $ mapMaybe snd xs)) |] -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration -handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> Q (Exp, Maybe Exp, Bool) +handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> Q (Exp, Maybe Exp) handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = - if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> ((stringE interfaceNameWithBrackets, , True) . Just) <$> singleConstructorEncoding + if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> ((stringE interfaceNameWithBrackets, ) . Just) <$> singleConstructorEncoding | allConstructorsAreNullary datatypeCons && allNullaryToStringTag options -> return stringEncoding @@ -215,9 +211,9 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn | (isUntaggedValue $ sumEncoding options) && isConstructorNullary ci -> return stringEncoding -- Treat as a sum - | isObjectWithSingleField $ sumEncoding options -> ((stringE [i|{#{show constructorNameToUse}: #{interfaceNameWithBrackets}}|], , False) . Just) <$> singleConstructorEncoding - | isTwoElemArray $ sumEncoding options -> ((stringE [i|[#{show constructorNameToUse}, #{interfaceNameWithBrackets}]|], , False) . Just) <$> singleConstructorEncoding - | isUntaggedValue $ sumEncoding options -> ((stringE interfaceNameWithBrackets, , True) . Just) <$> singleConstructorEncoding + | isObjectWithSingleField $ sumEncoding options -> ((stringE [i|{#{show constructorNameToUse}: #{interfaceNameWithBrackets}}|], ) . Just) <$> singleConstructorEncoding + | isTwoElemArray $ sumEncoding options -> ((stringE [i|[#{show constructorNameToUse}, #{interfaceNameWithBrackets}]|], ) . Just) <$> singleConstructorEncoding + | isUntaggedValue $ sumEncoding options -> ((stringE interfaceNameWithBrackets, ) . Just) <$> singleConstructorEncoding | otherwise -> do tagField :: [Exp] <- case sumEncoding options of TaggedObject tagFieldName _ -> (: []) <$> [|TSField False $(TH.stringE tagFieldName) $(TH.stringE [i|"#{constructorNameToUse}"|])|] @@ -226,10 +222,10 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn tsFields <- getTSFields options namesAndTypes decl <- assembleInterfaceDeclaration (ListE (tagField ++ tsFields)) - return (stringE interfaceNameWithBrackets, Just decl, True) + return (stringE interfaceNameWithBrackets, Just decl) where - stringEncoding = (stringE [i|"#{(constructorTagModifier options) $ getTypeName (constructorName ci)}"|], Nothing, True) + stringEncoding = (stringE [i|"#{(constructorTagModifier options) $ getTypeName (constructorName ci)}"|], Nothing) singleConstructorEncoding = if | constructorVariant ci == NormalConstructor -> tupleEncoding | otherwise -> do From e73122d7016e6fbdec4e1c28d17f8643ee8b5e59 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 19:28:41 -0800 Subject: [PATCH 025/208] WriterT switch looks good --- src/Data/Aeson/TypeScript/TH.hs | 59 +++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 121e943..77b54ff 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -139,6 +139,7 @@ module Data.Aeson.TypeScript.TH ( ) where import Control.Monad +import Control.Monad.Writer import Data.Aeson as A import Data.Aeson.TH as A import Data.Aeson.TypeScript.Formatting @@ -194,49 +195,65 @@ deriveTypeScript options name = do getDeclarationFunctionBody :: Options -> DatatypeInfo -> [String] -> Q Exp getDeclarationFunctionBody options datatypeInfo@(DatatypeInfo {..}) genericVariables = do - xs <- mapM (handleConstructor options datatypeInfo genericVariables) datatypeCons + (types, extraDecls) <- runWriterT $ mapM (handleConstructor options datatypeInfo genericVariables) datatypeCons typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName datatypeName) $(listE [TH.stringE x | x <- genericVariables]) - $(listE $ fmap (return . fst) xs)|] - [| $(return typeDeclaration) : $(listE (fmap return $ mapMaybe snd xs)) |] + $(listE $ fmap return types)|] + [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration -handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> Q (Exp, Maybe Exp) -handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = - if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> ((stringE interfaceNameWithBrackets, ) . Just) <$> singleConstructorEncoding +handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> WriterT [Exp] Q Exp +handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = + if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> do + writeSingleConstructorEncoding + lift $ TH.stringE interfaceNameWithBrackets - | allConstructorsAreNullary datatypeCons && allNullaryToStringTag options -> return stringEncoding + | allConstructorsAreNullary datatypeCons && allNullaryToStringTag options -> stringEncoding -- With UntaggedValue, nullary constructors are encoded as strings - | (isUntaggedValue $ sumEncoding options) && isConstructorNullary ci -> return stringEncoding + | (isUntaggedValue $ sumEncoding options) && isConstructorNullary ci -> stringEncoding -- Treat as a sum - | isObjectWithSingleField $ sumEncoding options -> ((stringE [i|{#{show constructorNameToUse}: #{interfaceNameWithBrackets}}|], ) . Just) <$> singleConstructorEncoding - | isTwoElemArray $ sumEncoding options -> ((stringE [i|[#{show constructorNameToUse}, #{interfaceNameWithBrackets}]|], ) . Just) <$> singleConstructorEncoding - | isUntaggedValue $ sumEncoding options -> ((stringE interfaceNameWithBrackets, ) . Just) <$> singleConstructorEncoding + | isObjectWithSingleField $ sumEncoding options -> do + writeSingleConstructorEncoding + lift $ TH.stringE [i|{#{show constructorNameToUse}: #{interfaceNameWithBrackets}}|] + | isTwoElemArray $ sumEncoding options -> do + writeSingleConstructorEncoding + lift $ TH.stringE [i|[#{show constructorNameToUse}, #{interfaceNameWithBrackets}]|] + | isUntaggedValue $ sumEncoding options -> do + writeSingleConstructorEncoding + lift $ TH.stringE interfaceNameWithBrackets | otherwise -> do - tagField :: [Exp] <- case sumEncoding options of + tagField :: [Exp] <- lift $ case sumEncoding options of TaggedObject tagFieldName _ -> (: []) <$> [|TSField False $(TH.stringE tagFieldName) $(TH.stringE [i|"#{constructorNameToUse}"|])|] _ -> return [] - tsFields <- getTSFields options namesAndTypes - decl <- assembleInterfaceDeclaration (ListE (tagField ++ tsFields)) + tsFields <- lift $ getTSFields options namesAndTypes + decl <- lift $ assembleInterfaceDeclaration (ListE (tagField ++ tsFields)) - return (stringE interfaceNameWithBrackets, Just decl) + tell [decl] + + lift $ TH.stringE interfaceNameWithBrackets where - stringEncoding = (stringE [i|"#{(constructorTagModifier options) $ getTypeName (constructorName ci)}"|], Nothing) + stringEncoding = lift $ TH.stringE [i|"#{(constructorTagModifier options) $ getTypeName (constructorName ci)}"|] - singleConstructorEncoding = if | constructorVariant ci == NormalConstructor -> tupleEncoding - | otherwise -> do - tsFields <- getTSFields options namesAndTypes - assembleInterfaceDeclaration (ListE tsFields) + writeSingleConstructorEncoding = if + | constructorVariant ci == NormalConstructor -> do + encoding <- lift tupleEncoding + tell [encoding] + | otherwise -> do + tsFields <- lift $ getTSFields options namesAndTypes + decl <- lift $ assembleInterfaceDeclaration (ListE tsFields) + tell [decl] -- * Type declaration to use interfaceName = "I" <> (lastNameComponent' $ constructorName ci) interfaceNameWithBrackets = interfaceName <> getGenericBrackets genericVariables - tupleEncoding = [|TSTypeAlternatives $(TH.stringE interfaceName) $(listE [TH.stringE x | x <- genericVariables]) [getTypeScriptType (Proxy :: Proxy $(return contentsTupleType))]|] + tupleEncoding = [|TSTypeAlternatives $(TH.stringE interfaceName) + $(listE [TH.stringE x | x <- genericVariables]) + [getTypeScriptType (Proxy :: Proxy $(return contentsTupleType))]|] namesAndTypes :: [(String, Type)] = case constructorVariant ci of RecordConstructor names -> zip (fmap ((fieldLabelModifier options) . lastNameComponent') names) (constructorFields ci) From cc9cd55d8ab8e470caf8b5a7c6f2f7c13591d91a Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 19:36:22 -0800 Subject: [PATCH 026/208] Make getTSFields be able to WriterT --- src/Data/Aeson/TypeScript/TH.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 77b54ff..36a707e 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -228,7 +228,7 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn TaggedObject tagFieldName _ -> (: []) <$> [|TSField False $(TH.stringE tagFieldName) $(TH.stringE [i|"#{constructorNameToUse}"|])|] _ -> return [] - tsFields <- lift $ getTSFields options namesAndTypes + tsFields <- getTSFields options namesAndTypes decl <- lift $ assembleInterfaceDeclaration (ListE (tagField ++ tsFields)) tell [decl] @@ -243,7 +243,7 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn encoding <- lift tupleEncoding tell [encoding] | otherwise -> do - tsFields <- lift $ getTSFields options namesAndTypes + tsFields <- getTSFields options namesAndTypes decl <- lift $ assembleInterfaceDeclaration (ListE tsFields) tell [decl] @@ -267,15 +267,15 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) $(listE [TH.stringE x | x <- genericVariables]) $(return members)|] - getTSFields :: Options -> [(String, Type)] -> Q [Exp] + getTSFields :: Options -> [(String, Type)] -> WriterT [Exp] Q [Exp] getTSFields options namesAndTypes = do forM namesAndTypes $ \(nameString, typ) -> do (fieldTyp, optAsBool) <- case typ of (AppT (ConT name) t) | not (omitNothingFields options) && name == ''Maybe -> do - fieldTyp <- [|$(return $ getTypeAsStringExp t) <> " | null"|] + fieldTyp <- lift $ [|$(return $ getTypeAsStringExp t) <> " | null"|] return (fieldTyp, getOptionalAsBoolExp t) _ -> return (getTypeAsStringExp typ, getOptionalAsBoolExp typ) - [| TSField $(return optAsBool) nameString $(return fieldTyp) |] + lift $ [| TSField $(return optAsBool) nameString $(return fieldTyp) |] -- * Convenience functions From b1ba5000acd32b19358f9da527c9943926fd34b8 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 23:43:22 -0800 Subject: [PATCH 027/208] Working on gathering type variables --- src/Data/Aeson/TypeScript/TH.hs | 68 ++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 36a707e..244e6d1 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -175,34 +175,36 @@ deriveTypeScript options name = do let predicates = constructorPreds <> typeVariablePreds let constraints = foldl AppT (TupleT (length predicates)) predicates - -- Build generic args: one for every T, T1, T2, etc. passed in - let isGenericVariable t = t `L.elem` allStarConstructors - let typeNames = [typ | typ <- getDataTypeVars datatypeInfo, isGenericVariable typ] - let genericBrackets = case typeNames of - [] -> [|""|] - _ -> [|"<" <> (L.intercalate ", " $(listE $ fmap (return . getTypeAsStringExp) typeNames)) <> ">"|] - - let typeNameToString (ConT n) = nameBase n - typeNameToString _ = "?" - let stringTypeNames = fmap typeNameToString typeNames - [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where - getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $genericBrackets; - getTypeScriptDeclarations _ = $(getDeclarationFunctionBody options datatypeInfo stringTypeNames) + getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) + getTypeScriptDeclarations _ = $(getDeclarationFunctionBody options datatypeInfo []) getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] | t <- mconcat $ fmap constructorFields datatypeCons]) |] getDeclarationFunctionBody :: Options -> DatatypeInfo -> [String] -> Q Exp getDeclarationFunctionBody options datatypeInfo@(DatatypeInfo {..}) genericVariables = do - (types, extraDecls) <- runWriterT $ mapM (handleConstructor options datatypeInfo genericVariables) datatypeCons + (types, extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options datatypeInfo genericVariables) datatypeCons + typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName datatypeName) $(listE [TH.stringE x | x <- genericVariables]) $(listE $ fmap return types)|] + + let extraDecls = [x | ExtraDecl x <- extraDeclsOrGenericInfos] + let genericInfos = [(x, y) | GenericInfo x y <- extraDeclsOrGenericInfos] + reportWarning [i|Got genericInfos: #{genericInfos}|] + [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] +data ExtraDeclOrGenericInfo = ExtraDecl Exp + | GenericInfo Name GenericInfoExtra + +data GenericInfoExtra = NormalStar + | TypeFamilyKey Name + deriving Show + -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration -handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> WriterT [Exp] Q Exp +handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> do writeSingleConstructorEncoding @@ -228,10 +230,10 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn TaggedObject tagFieldName _ -> (: []) <$> [|TSField False $(TH.stringE tagFieldName) $(TH.stringE [i|"#{constructorNameToUse}"|])|] _ -> return [] - tsFields <- getTSFields options namesAndTypes + tsFields <- getTSFields decl <- lift $ assembleInterfaceDeclaration (ListE (tagField ++ tsFields)) - tell [decl] + tell [ExtraDecl decl] lift $ TH.stringE interfaceNameWithBrackets @@ -241,11 +243,11 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn writeSingleConstructorEncoding = if | constructorVariant ci == NormalConstructor -> do encoding <- lift tupleEncoding - tell [encoding] + tell [ExtraDecl encoding] | otherwise -> do - tsFields <- getTSFields options namesAndTypes + tsFields <- getTSFields decl <- lift $ assembleInterfaceDeclaration (ListE tsFields) - tell [decl] + tell [ExtraDecl decl] -- * Type declaration to use interfaceName = "I" <> (lastNameComponent' $ constructorName ci) @@ -267,16 +269,30 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) $(listE [TH.stringE x | x <- genericVariables]) $(return members)|] - getTSFields :: Options -> [(String, Type)] -> WriterT [Exp] Q [Exp] - getTSFields options namesAndTypes = do + getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] + getTSFields = do forM namesAndTypes $ \(nameString, typ) -> do + searchGenericInfos typ + (fieldTyp, optAsBool) <- case typ of - (AppT (ConT name) t) | not (omitNothingFields options) && name == ''Maybe -> do - fieldTyp <- lift $ [|$(return $ getTypeAsStringExp t) <> " | null"|] - return (fieldTyp, getOptionalAsBoolExp t) - _ -> return (getTypeAsStringExp typ, getOptionalAsBoolExp typ) + (AppT (ConT name) t) + | name == ''Maybe && not (omitNothingFields options) -> do + fieldTyp <- lift $ [|$(return $ getTypeAsStringExp t) <> " | null"|] + return (fieldTyp, getOptionalAsBoolExp t) + x -> return (getTypeAsStringExp typ, getOptionalAsBoolExp typ) lift $ [| TSField $(return optAsBool) nameString $(return fieldTyp) |] + searchGenericInfos :: Type -> WriterT [ExtraDeclOrGenericInfo] Q () + searchGenericInfos (AppT (ConT name) typ) = lift (reify name) >>= \case + FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) _) _ -> do + lift $ reportWarning [i|Found a type family application! #{name}|] + tell [GenericInfo (mkName "unknown") (TypeFamilyKey typeFamilyName)] + _ -> searchGenericInfos typ + searchGenericInfos (AppT t1 t2) = do + searchGenericInfos t1 + searchGenericInfos t2 + searchGenericInfos (VarT name) = tell [GenericInfo name NormalStar] + searchGenericInfos _ = return () -- * Convenience functions From e1a26ff078bfd824fa713039feca171251dbc1d2 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 17 Jan 2021 23:54:54 -0800 Subject: [PATCH 028/208] Adding options for type family mapping --- src/Data/Aeson/TypeScript/TH.hs | 58 +++++++++++++++--------------- src/Data/Aeson/TypeScript/Types.hs | 16 +++++++++ 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 244e6d1..3f073ae 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -147,8 +147,6 @@ import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Lookup import Data.Aeson.TypeScript.Types import Data.Aeson.TypeScript.Util -import qualified Data.List as L -import Data.Maybe import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString @@ -158,12 +156,14 @@ import qualified Language.Haskell.TH.Lib as TH -- | Generates a 'TypeScript' instance declaration for the given data type. -deriveTypeScript :: Options - -- ^ Encoding options. - -> Name - -- ^ Name of the type for which to generate a 'TypeScript' instance declaration. - -> Q [Dec] -deriveTypeScript options name = do +deriveTypeScript' :: Options + -- ^ Encoding options. + -> Name + -- ^ Name of the type for which to generate a 'TypeScript' instance declaration. + -> ExtraTypeScriptOptions + -- ^ Extra options to control advanced features. + -> Q [Dec] +deriveTypeScript' options name extraOptions = do datatypeInfo@(DatatypeInfo {..}) <- reifyDatatype name assertExtensionsTurnedOn datatypeInfo @@ -175,33 +175,24 @@ deriveTypeScript options name = do let predicates = constructorPreds <> typeVariablePreds let constraints = foldl AppT (TupleT (length predicates)) predicates - [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where - getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) - getTypeScriptDeclarations _ = $(getDeclarationFunctionBody options datatypeInfo []) - getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] - | t <- mconcat $ fmap constructorFields datatypeCons]) - |] - -getDeclarationFunctionBody :: Options -> DatatypeInfo -> [String] -> Q Exp -getDeclarationFunctionBody options datatypeInfo@(DatatypeInfo {..}) genericVariables = do + -- Build the declarations + let genericVariables = [] (types, extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options datatypeInfo genericVariables) datatypeCons - typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName datatypeName) $(listE [TH.stringE x | x <- genericVariables]) $(listE $ fmap return types)|] - let extraDecls = [x | ExtraDecl x <- extraDeclsOrGenericInfos] let genericInfos = [(x, y) | GenericInfo x y <- extraDeclsOrGenericInfos] reportWarning [i|Got genericInfos: #{genericInfos}|] + declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] - [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] - -data ExtraDeclOrGenericInfo = ExtraDecl Exp - | GenericInfo Name GenericInfoExtra + [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where + getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) + getTypeScriptDeclarations _ = $(return declarationsFunctionBody) + getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] + | t <- mconcat $ fmap constructorFields datatypeCons]) + |] -data GenericInfoExtra = NormalStar - | TypeFamilyKey Name - deriving Show -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp @@ -232,9 +223,7 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn tsFields <- getTSFields decl <- lift $ assembleInterfaceDeclaration (ListE (tagField ++ tsFields)) - tell [ExtraDecl decl] - lift $ TH.stringE interfaceNameWithBrackets where @@ -260,8 +249,9 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn namesAndTypes :: [(String, Type)] = case constructorVariant ci of RecordConstructor names -> zip (fmap ((fieldLabelModifier options) . lastNameComponent') names) (constructorFields ci) NormalConstructor -> case sumEncoding options of - TaggedObject _ contentsFieldName -> if | isConstructorNullary ci -> [] - | otherwise -> [(contentsFieldName, contentsTupleType)] + TaggedObject _ contentsFieldName + | isConstructorNullary ci -> [] + | otherwise -> [(contentsFieldName, contentsTupleType)] _ -> [(constructorNameToUse, contentsTupleType)] constructorNameToUse = (constructorTagModifier options) $ lastNameComponent' (constructorName ci) @@ -308,3 +298,11 @@ deriveJSONAndTypeScript :: Options -> Q [Dec] deriveJSONAndTypeScript options name = (<>) <$> (deriveTypeScript options name) <*> (A.deriveJSON options name) + +-- | Generates a 'TypeScript' instance declaration for the given data type. +deriveTypeScript :: Options + -- ^ Encoding options. + -> Name + -- ^ Name of the type for which to generate a 'TypeScript' instance declaration. + -> Q [Dec] +deriveTypeScript options name = deriveTypeScript' options name defaultExtraTypeScriptOptions diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 3da0fe1..fab8329 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -131,5 +131,21 @@ instance TypeScript T8 where getTypeScriptType _ = "T8" instance TypeScript T9 where getTypeScriptType _ = "T9" instance TypeScript T10 where getTypeScriptType _ = "T10" +allStarConstructors :: [Type] allStarConstructors = [ConT ''T1, ConT ''T2, ConT ''T3, ConT ''T4, ConT ''T5, ConT ''T6, ConT ''T7, ConT ''T8, ConT ''T9, ConT ''T10] +-- | Type variable gathering + +data ExtraTypeScriptOptions = ExtraTypeScriptOptions { + typeFamiliesToMapToTypeScript :: [Name] + } + +defaultExtraTypeScriptOptions :: ExtraTypeScriptOptions +defaultExtraTypeScriptOptions = ExtraTypeScriptOptions [] + +data ExtraDeclOrGenericInfo = ExtraDecl Exp + | GenericInfo Name GenericInfoExtra + +data GenericInfoExtra = NormalStar + | TypeFamilyKey Name + deriving Show From 7e7db65d61f537dbbc219bbe096191807355e42d Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 00:18:54 -0800 Subject: [PATCH 029/208] More refactoring to expose namesAndTypes --- src/Data/Aeson/TypeScript/TH.hs | 28 ++++++++++------------------ src/Data/Aeson/TypeScript/Util.hs | 13 +++++++++++++ 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 3f073ae..fe20c01 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -209,16 +209,16 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn -- Treat as a sum | isObjectWithSingleField $ sumEncoding options -> do writeSingleConstructorEncoding - lift $ TH.stringE [i|{#{show constructorNameToUse}: #{interfaceNameWithBrackets}}|] + lift $ TH.stringE [i|{#{show $ constructorNameToUse options ci}: #{interfaceNameWithBrackets}}|] | isTwoElemArray $ sumEncoding options -> do writeSingleConstructorEncoding - lift $ TH.stringE [i|[#{show constructorNameToUse}, #{interfaceNameWithBrackets}]|] + lift $ TH.stringE [i|[#{show $ constructorNameToUse options ci}, #{interfaceNameWithBrackets}]|] | isUntaggedValue $ sumEncoding options -> do writeSingleConstructorEncoding lift $ TH.stringE interfaceNameWithBrackets | otherwise -> do tagField :: [Exp] <- lift $ case sumEncoding options of - TaggedObject tagFieldName _ -> (: []) <$> [|TSField False $(TH.stringE tagFieldName) $(TH.stringE [i|"#{constructorNameToUse}"|])|] + TaggedObject tagFieldName _ -> (: []) <$> [|TSField False $(TH.stringE tagFieldName) $(TH.stringE [i|"#{constructorNameToUse options ci}"|])|] _ -> return [] tsFields <- getTSFields @@ -244,24 +244,15 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn tupleEncoding = [|TSTypeAlternatives $(TH.stringE interfaceName) $(listE [TH.stringE x | x <- genericVariables]) - [getTypeScriptType (Proxy :: Proxy $(return contentsTupleType))]|] + [getTypeScriptType (Proxy :: Proxy $(return $ contentsTupleType ci))]|] - namesAndTypes :: [(String, Type)] = case constructorVariant ci of - RecordConstructor names -> zip (fmap ((fieldLabelModifier options) . lastNameComponent') names) (constructorFields ci) - NormalConstructor -> case sumEncoding options of - TaggedObject _ contentsFieldName - | isConstructorNullary ci -> [] - | otherwise -> [(contentsFieldName, contentsTupleType)] - _ -> [(constructorNameToUse, contentsTupleType)] - - constructorNameToUse = (constructorTagModifier options) $ lastNameComponent' (constructorName ci) - contentsTupleType = getTupleType (constructorFields ci) - - assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) $(listE [TH.stringE x | x <- genericVariables]) $(return members)|] + assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) + $(listE [TH.stringE x | x <- genericVariables]) + $(return members)|] getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] getTSFields = do - forM namesAndTypes $ \(nameString, typ) -> do + forM (namesAndTypes options ci) $ \(nameString, typ) -> do searchGenericInfos typ (fieldTyp, optAsBool) <- case typ of @@ -288,7 +279,8 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn -- | Convenience function to generate 'A.ToJSON', 'A.FromJSON', and 'TypeScript' instances simultaneously, so the instances are guaranteed to be in sync. -- --- This function is given mainly as an illustration. If you want some other permutation of instances, such as 'A.ToJSON' and 'A.TypeScript' only, just take a look at the source and write your own version. +-- This function is given mainly as an illustration. +-- If you want some other permutation of instances, such as 'A.ToJSON' and 'A.TypeScript' only, just take a look at the source and write your own version. -- -- @since 0.1.0.4 deriveJSONAndTypeScript :: Options diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index e1fe0a3..d135768 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -124,3 +124,16 @@ isUntaggedValue _ = False fst3 (x, _, _) = x snd3 (_, y, _) = y + +namesAndTypes :: Options -> ConstructorInfo -> [(String, Type)] +namesAndTypes options ci = case constructorVariant ci of + RecordConstructor names -> zip (fmap ((fieldLabelModifier options) . lastNameComponent') names) (constructorFields ci) + NormalConstructor -> case sumEncoding options of + TaggedObject _ contentsFieldName + | isConstructorNullary ci -> [] + | otherwise -> [(contentsFieldName, contentsTupleType ci)] + _ -> [(constructorNameToUse options ci, contentsTupleType ci)] + +constructorNameToUse options ci = (constructorTagModifier options) $ lastNameComponent' (constructorName ci) + +contentsTupleType ci = getTupleType (constructorFields ci) From 12ea5d43f5d1e714ffbe865b0585d2f69752159a Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 01:49:33 -0800 Subject: [PATCH 030/208] First steps toward unifying constraints --- src/Data/Aeson/TypeScript/TH.hs | 53 ++++++++++++++++++++---------- src/Data/Aeson/TypeScript/Types.hs | 9 ++++- test/Live.hs | 12 +++---- 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index fe20c01..8bc48e3 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -112,6 +112,7 @@ main = putStrLn $ 'formatTSDeclarations' ( module Data.Aeson.TypeScript.TH ( deriveTypeScript, + deriveTypeScript', deriveTypeScriptLookupType, -- * The main typeclass @@ -147,6 +148,8 @@ import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Lookup import Data.Aeson.TypeScript.Types import Data.Aeson.TypeScript.Util +import qualified Data.List as L +import Data.Maybe import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString @@ -175,19 +178,27 @@ deriveTypeScript' options name extraOptions = do let predicates = constructorPreds <> typeVariablePreds let constraints = foldl AppT (TupleT (length predicates)) predicates + let eligibleGenericVars = catMaybes $ flip fmap (getDataTypeVars datatypeInfo) $ \case + SigT (VarT n) StarT -> Just n + _ -> Nothing + genericVariables <- forM (zip eligibleGenericVars allStarConstructors') $ \tuple@(var, genericVar) -> do + (_, genericInfos) <- runWriterT $ forM_ datatypeCons $ \ci -> + forM_ (namesAndTypes options ci) $ \(_, typ) -> do + lift $ reportWarning [i|Searching typ #{typ}|] + searchForConstraints extraOptions typ tuple + return $ unifyGenericVariable genericVar genericInfos + reportWarning [i|Got generic variables: '#{genericVariables}'|] + -- Build the declarations - let genericVariables = [] (types, extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options datatypeInfo genericVariables) datatypeCons typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName datatypeName) $(listE [TH.stringE x | x <- genericVariables]) $(listE $ fmap return types)|] let extraDecls = [x | ExtraDecl x <- extraDeclsOrGenericInfos] - let genericInfos = [(x, y) | GenericInfo x y <- extraDeclsOrGenericInfos] - reportWarning [i|Got genericInfos: #{genericInfos}|] declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where - getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) + getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> getGenericBrackets $(listE $ fmap TH.stringE genericVariables) getTypeScriptDeclarations _ = $(return declarationsFunctionBody) getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] | t <- mconcat $ fmap constructorFields datatypeCons]) @@ -253,27 +264,35 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] getTSFields = do forM (namesAndTypes options ci) $ \(nameString, typ) -> do - searchGenericInfos typ - (fieldTyp, optAsBool) <- case typ of (AppT (ConT name) t) | name == ''Maybe && not (omitNothingFields options) -> do fieldTyp <- lift $ [|$(return $ getTypeAsStringExp t) <> " | null"|] return (fieldTyp, getOptionalAsBoolExp t) x -> return (getTypeAsStringExp typ, getOptionalAsBoolExp typ) - lift $ [| TSField $(return optAsBool) nameString $(return fieldTyp) |] + lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) |] - searchGenericInfos :: Type -> WriterT [ExtraDeclOrGenericInfo] Q () - searchGenericInfos (AppT (ConT name) typ) = lift (reify name) >>= \case +searchForConstraints :: ExtraTypeScriptOptions -> Type -> (Name, Name) -> WriterT [GenericInfo] Q () +searchForConstraints eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) tup@(varName, genericName) + | typ == VarT varName && (name `L.elem` typeFamiliesToMapToTypeScript) = lift (reify name) >>= \case FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) _) _ -> do - lift $ reportWarning [i|Found a type family application! #{name}|] - tell [GenericInfo (mkName "unknown") (TypeFamilyKey typeFamilyName)] - _ -> searchGenericInfos typ - searchGenericInfos (AppT t1 t2) = do - searchGenericInfos t1 - searchGenericInfos t2 - searchGenericInfos (VarT name) = tell [GenericInfo name NormalStar] - searchGenericInfos _ = return () + tell [GenericInfo varName genericName (TypeFamilyKey typeFamilyName)] + searchForConstraints eo typ tup + _ -> searchForConstraints eo typ tup + | otherwise = searchForConstraints eo typ tup +searchForConstraints eo (AppT typ1 typ2) tup = searchForConstraints eo typ1 tup >> searchForConstraints eo typ2 tup +searchForConstraints eo (AppKindT typ _) tup = searchForConstraints eo typ tup +searchForConstraints eo (SigT typ _) tup = searchForConstraints eo typ tup +searchForConstraints eo (InfixT typ1 _ typ2) tup = searchForConstraints eo typ1 tup >> searchForConstraints eo typ2 tup +searchForConstraints eo (UInfixT typ1 _ typ2) tup = searchForConstraints eo typ1 tup >> searchForConstraints eo typ2 tup +searchForConstraints eo (ParensT typ) tup = searchForConstraints eo typ tup +searchForConstraints eo (ImplicitParamT _ typ) tup = searchForConstraints eo typ tup +searchForConstraints eo _ _ = return () + +unifyGenericVariable :: Name -> [GenericInfo] -> String +unifyGenericVariable genericVar genericInfos = case [nameBase name | GenericInfo _ _ (TypeFamilyKey name) <- genericInfos] of + [] -> nameBase genericVar + names -> nameBase genericVar <> " extends keyof " <> (L.intercalate " & " names) -- * Convenience functions diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index fab8329..1f3bdce 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -134,6 +134,9 @@ instance TypeScript T10 where getTypeScriptType _ = "T10" allStarConstructors :: [Type] allStarConstructors = [ConT ''T1, ConT ''T2, ConT ''T3, ConT ''T4, ConT ''T5, ConT ''T6, ConT ''T7, ConT ''T8, ConT ''T9, ConT ''T10] +allStarConstructors' :: [Name] +allStarConstructors' = [''T1, ''T2, ''T3, ''T4, ''T5, ''T6, ''T7, ''T8, ''T9, ''T10] + -- | Type variable gathering data ExtraTypeScriptOptions = ExtraTypeScriptOptions { @@ -144,7 +147,11 @@ defaultExtraTypeScriptOptions :: ExtraTypeScriptOptions defaultExtraTypeScriptOptions = ExtraTypeScriptOptions [] data ExtraDeclOrGenericInfo = ExtraDecl Exp - | GenericInfo Name GenericInfoExtra + | ExtraGeneric GenericInfo + deriving Show + +data GenericInfo = GenericInfo Name Name GenericInfoExtra + deriving Show data GenericInfoExtra = NormalStar | TypeFamilyKey Name diff --git a/test/Live.hs b/test/Live.hs index 8ebdf9e..f338748 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -31,15 +31,12 @@ import Prelude hiding (Double) import Database.Beam - data TestT a = TestT { listOfA :: [a] , maybeA :: Maybe a } - $(deriveTypeScript A.defaultOptions ''TestT) - instance TypeScript UTCTime where getTypeScriptType _ = "DateTime" @@ -66,6 +63,7 @@ instance TypeScript K8SEnvironment where getTypeScriptType _ = [i|"k8s_env"|] type family DeployEnvironment env = result | result -> env where DeployEnvironment SingleNodeEnvironment = SingleDE DeployEnvironment K8SEnvironment = K8SDE + DeployEnvironment T = () data UserT env f = User { _userUsername :: Columnar f T.Text @@ -73,11 +71,11 @@ data UserT env f = User { , _userDeployEnvironment :: Columnar f (DeployEnvironment env) } -$(deriveTypeScriptLookupType ''DeployEnvironment "deployEnvDecl") +-- $(deriveTypeScriptLookupType ''DeployEnvironment "deployEnvDecl") -$(deriveTypeScript A.defaultOptions ''UserT) +$(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) -data HigherKind a = HigherKind { higherKindList :: [a] } -$(deriveTypeScript A.defaultOptions ''HigherKind) +-- data HigherKind a = HigherKind { higherKindList :: [a] } +-- $(deriveTypeScript A.defaultOptions ''HigherKind) From 532d738ca2b481367f3a2c3f93aa15325e3fd7d6 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 02:12:46 -0800 Subject: [PATCH 031/208] Progress on type variables --- src/Data/Aeson/TypeScript/TH.hs | 42 +++++++++++++++++-------------- src/Data/Aeson/TypeScript/Util.hs | 6 +++++ test/Live.hs | 4 +-- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 8bc48e3..fd79e58 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -181,24 +181,25 @@ deriveTypeScript' options name extraOptions = do let eligibleGenericVars = catMaybes $ flip fmap (getDataTypeVars datatypeInfo) $ \case SigT (VarT n) StarT -> Just n _ -> Nothing - genericVariables <- forM (zip eligibleGenericVars allStarConstructors') $ \tuple@(var, genericVar) -> do + genericVariablesAndSuffixes <- forM (zip eligibleGenericVars allStarConstructors') $ \tuple@(var, genericVar) -> do (_, genericInfos) <- runWriterT $ forM_ datatypeCons $ \ci -> forM_ (namesAndTypes options ci) $ \(_, typ) -> do lift $ reportWarning [i|Searching typ #{typ}|] searchForConstraints extraOptions typ tuple - return $ unifyGenericVariable genericVar genericInfos - reportWarning [i|Got generic variables: '#{genericVariables}'|] + return (var, unifyGenericVariable genericInfos) + reportWarning [i|Got generic variables: '#{genericVariablesAndSuffixes}'|] + let genericVariables = fmap fst genericVariablesAndSuffixes -- Build the declarations (types, extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options datatypeInfo genericVariables) datatypeCons typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName datatypeName) - $(listE [TH.stringE x | x <- genericVariables]) + $(listE [TH.stringE $ nameBase x | x <- genericVariables]) $(listE $ fmap return types)|] let extraDecls = [x | ExtraDecl x <- extraDeclsOrGenericInfos] declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where - getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> getGenericBrackets $(listE $ fmap TH.stringE genericVariables) + getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $(getBracketsExpression genericVariables) getTypeScriptDeclarations _ = $(return declarationsFunctionBody) getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] | t <- mconcat $ fmap constructorFields datatypeCons]) @@ -206,12 +207,12 @@ deriveTypeScript' options name extraOptions = do -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration -handleConstructor :: Options -> DatatypeInfo -> [String] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp +handleConstructor :: Options -> DatatypeInfo -> [Name] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> do writeSingleConstructorEncoding - lift $ TH.stringE interfaceNameWithBrackets - + brackets <- lift $ getBracketsExpression genericVariables + lift $ [|$(TH.stringE interfaceName) <> $(return brackets)|] | allConstructorsAreNullary datatypeCons && allNullaryToStringTag options -> stringEncoding -- With UntaggedValue, nullary constructors are encoded as strings @@ -220,13 +221,16 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn -- Treat as a sum | isObjectWithSingleField $ sumEncoding options -> do writeSingleConstructorEncoding - lift $ TH.stringE [i|{#{show $ constructorNameToUse options ci}: #{interfaceNameWithBrackets}}|] + brackets <- lift $ getBracketsExpression genericVariables + lift $ [|"{" <> $(TH.stringE $ show $ constructorNameToUse options ci) <> "}: " <> $(TH.stringE interfaceName) <> $(return brackets) <> "}"|] | isTwoElemArray $ sumEncoding options -> do writeSingleConstructorEncoding - lift $ TH.stringE [i|[#{show $ constructorNameToUse options ci}, #{interfaceNameWithBrackets}]|] + brackets <- lift $ getBracketsExpression genericVariables + lift $ [|"[" <> $(TH.stringE $ show $ constructorNameToUse options ci) <> ", " <> $(TH.stringE interfaceName) <> $(return brackets) <> "]"|] | isUntaggedValue $ sumEncoding options -> do writeSingleConstructorEncoding - lift $ TH.stringE interfaceNameWithBrackets + brackets <- lift $ getBracketsExpression genericVariables + lift $ [|$(TH.stringE interfaceName) <> $(return brackets)|] | otherwise -> do tagField :: [Exp] <- lift $ case sumEncoding options of TaggedObject tagFieldName _ -> (: []) <$> [|TSField False $(TH.stringE tagFieldName) $(TH.stringE [i|"#{constructorNameToUse options ci}"|])|] @@ -235,7 +239,8 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn tsFields <- getTSFields decl <- lift $ assembleInterfaceDeclaration (ListE (tagField ++ tsFields)) tell [ExtraDecl decl] - lift $ TH.stringE interfaceNameWithBrackets + brackets <- lift $ getBracketsExpression genericVariables + lift $ [|$(TH.stringE interfaceName) <> $(return brackets)|] where stringEncoding = lift $ TH.stringE [i|"#{(constructorTagModifier options) $ getTypeName (constructorName ci)}"|] @@ -251,14 +256,13 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn -- * Type declaration to use interfaceName = "I" <> (lastNameComponent' $ constructorName ci) - interfaceNameWithBrackets = interfaceName <> getGenericBrackets genericVariables tupleEncoding = [|TSTypeAlternatives $(TH.stringE interfaceName) - $(listE [TH.stringE x | x <- genericVariables]) + $(listE [ [|getTypeScriptType (Proxy :: Proxy $(varT x))|] | x <- genericVariables]) [getTypeScriptType (Proxy :: Proxy $(return $ contentsTupleType ci))]|] assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) - $(listE [TH.stringE x | x <- genericVariables]) + $(listE [ [|getTypeScriptType (Proxy :: Proxy $(varT x))|] | x <- genericVariables]) $(return members)|] getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] @@ -289,10 +293,10 @@ searchForConstraints eo (ParensT typ) tup = searchForConstraints eo typ tup searchForConstraints eo (ImplicitParamT _ typ) tup = searchForConstraints eo typ tup searchForConstraints eo _ _ = return () -unifyGenericVariable :: Name -> [GenericInfo] -> String -unifyGenericVariable genericVar genericInfos = case [nameBase name | GenericInfo _ _ (TypeFamilyKey name) <- genericInfos] of - [] -> nameBase genericVar - names -> nameBase genericVar <> " extends keyof " <> (L.intercalate " & " names) +unifyGenericVariable :: [GenericInfo] -> String +unifyGenericVariable genericInfos = case [nameBase name | GenericInfo _ _ (TypeFamilyKey name) <- genericInfos] of + [] -> "" + names -> " extends keyof " <> (L.intercalate " & " names) -- * Convenience functions diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index d135768..c208393 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -6,12 +6,14 @@ import Control.Monad import Data.Aeson as A import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Types +import qualified Data.List as L import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString import qualified Data.Text as T import Language.Haskell.TH hiding (stringE) import Language.Haskell.TH.Datatype +import qualified Language.Haskell.TH.Lib as TH #if MIN_VERSION_th_abstraction(0,3,0) @@ -137,3 +139,7 @@ namesAndTypes options ci = case constructorVariant ci of constructorNameToUse options ci = (constructorTagModifier options) $ lastNameComponent' (constructorName ci) contentsTupleType ci = getTupleType (constructorFields ci) + +getBracketsExpression :: [Name] -> Q Exp +getBracketsExpression [] = [|""|] +getBracketsExpression names = [|"<" <> L.intercalate ", " $(listE [ [|getTypeScriptType (Proxy :: Proxy $(varT x))|] | x <- names]) <> ">"|] diff --git a/test/Live.hs b/test/Live.hs index f338748..b42f86a 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -73,8 +73,8 @@ data UserT env f = User { -- $(deriveTypeScriptLookupType ''DeployEnvironment "deployEnvDecl") -$(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) - +-- $(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) + -- data HigherKind a = HigherKind { higherKindList :: [a] } -- $(deriveTypeScript A.defaultOptions ''HigherKind) From fd3e63fbf72323394322950dc986edf195fe6bb9 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 03:26:21 -0800 Subject: [PATCH 032/208] All tests passing! --- src/Data/Aeson/TypeScript/Lookup.hs | 1 - src/Data/Aeson/TypeScript/TH.hs | 12 +++++------- src/Data/Aeson/TypeScript/Types.hs | 28 ++++++++++++++++------------ src/Data/Aeson/TypeScript/Util.hs | 13 ++++++++++++- test/HigherKind.hs | 5 +++-- test/TestBoilerplate.hs | 2 +- 6 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Lookup.hs b/src/Data/Aeson/TypeScript/Lookup.hs index 2881e30..9e7868b 100644 --- a/src/Data/Aeson/TypeScript/Lookup.hs +++ b/src/Data/Aeson/TypeScript/Lookup.hs @@ -33,7 +33,6 @@ deriveTypeScriptLookupType :: Name -> Q [Dec] deriveTypeScriptLookupType name declNameStr = do info <- reify name - reportWarning [i|Got datatypeInfo: #{info}|] case info of FamilyI (ClosedTypeFamilyD (TypeFamilyHead name vars sig maybeInject) eqns) decs -> do fields <- forM eqns $ \case diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index fd79e58..f7aeb3a 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -184,22 +184,20 @@ deriveTypeScript' options name extraOptions = do genericVariablesAndSuffixes <- forM (zip eligibleGenericVars allStarConstructors') $ \tuple@(var, genericVar) -> do (_, genericInfos) <- runWriterT $ forM_ datatypeCons $ \ci -> forM_ (namesAndTypes options ci) $ \(_, typ) -> do - lift $ reportWarning [i|Searching typ #{typ}|] searchForConstraints extraOptions typ tuple return (var, unifyGenericVariable genericInfos) - reportWarning [i|Got generic variables: '#{genericVariablesAndSuffixes}'|] let genericVariables = fmap fst genericVariablesAndSuffixes -- Build the declarations (types, extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options datatypeInfo genericVariables) datatypeCons typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName datatypeName) - $(listE [TH.stringE $ nameBase x | x <- genericVariables]) + $(genericVariablesListExpr genericVariables) $(listE $ fmap return types)|] let extraDecls = [x | ExtraDecl x <- extraDeclsOrGenericInfos] declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where - getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $(getBracketsExpression genericVariables) + getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $(getBracketsExpressionAllTypes genericVariables) getTypeScriptDeclarations _ = $(return declarationsFunctionBody) getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] | t <- mconcat $ fmap constructorFields datatypeCons]) @@ -222,7 +220,7 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn | isObjectWithSingleField $ sumEncoding options -> do writeSingleConstructorEncoding brackets <- lift $ getBracketsExpression genericVariables - lift $ [|"{" <> $(TH.stringE $ show $ constructorNameToUse options ci) <> "}: " <> $(TH.stringE interfaceName) <> $(return brackets) <> "}"|] + lift $ [|"{" <> $(TH.stringE $ show $ constructorNameToUse options ci) <> ": " <> $(TH.stringE interfaceName) <> $(return brackets) <> "}"|] | isTwoElemArray $ sumEncoding options -> do writeSingleConstructorEncoding brackets <- lift $ getBracketsExpression genericVariables @@ -258,11 +256,11 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn interfaceName = "I" <> (lastNameComponent' $ constructorName ci) tupleEncoding = [|TSTypeAlternatives $(TH.stringE interfaceName) - $(listE [ [|getTypeScriptType (Proxy :: Proxy $(varT x))|] | x <- genericVariables]) + $(genericVariablesListExpr genericVariables) [getTypeScriptType (Proxy :: Proxy $(return $ contentsTupleType ci))]|] assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) - $(listE [ [|getTypeScriptType (Proxy :: Proxy $(varT x))|] | x <- genericVariables]) + $(genericVariablesListExpr genericVariables) $(return members)|] getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 1f3bdce..26064a0 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -52,9 +52,13 @@ class (Typeable a) => TypeScript a where getTypeScriptOptional _ = False getParentTypes :: Proxy a -> [TSType] - getParentTypes _ = [] -- ^ Get the types that this type depends on. This is useful for generating transitive closures of necessary types. + getParentTypes _ = [] + isGenericVariable :: Proxy a -> Bool + -- ^ Special flag to indicate whether this type corresponds to a template variable. + isGenericVariable _ = False + -- | An existential wrapper for any TypeScript instance. data TSType = forall a. (Typeable a, TypeScript a) => TSType { unTSType :: Proxy a } @@ -119,17 +123,17 @@ data T8 = T8 data T9 = T9 data T10 = T10 -instance TypeScript T where getTypeScriptType _ = "T" -instance TypeScript T1 where getTypeScriptType _ = "T1" -instance TypeScript T2 where getTypeScriptType _ = "T2" -instance TypeScript T3 where getTypeScriptType _ = "T3" -instance TypeScript T4 where getTypeScriptType _ = "T4" -instance TypeScript T5 where getTypeScriptType _ = "T5" -instance TypeScript T6 where getTypeScriptType _ = "T6" -instance TypeScript T7 where getTypeScriptType _ = "T7" -instance TypeScript T8 where getTypeScriptType _ = "T8" -instance TypeScript T9 where getTypeScriptType _ = "T9" -instance TypeScript T10 where getTypeScriptType _ = "T10" +instance TypeScript T where getTypeScriptType _ = "T"; isGenericVariable _ = True +instance TypeScript T1 where getTypeScriptType _ = "T1"; isGenericVariable _ = True +instance TypeScript T2 where getTypeScriptType _ = "T2"; isGenericVariable _ = True +instance TypeScript T3 where getTypeScriptType _ = "T3"; isGenericVariable _ = True +instance TypeScript T4 where getTypeScriptType _ = "T4"; isGenericVariable _ = True +instance TypeScript T5 where getTypeScriptType _ = "T5"; isGenericVariable _ = True +instance TypeScript T6 where getTypeScriptType _ = "T6"; isGenericVariable _ = True +instance TypeScript T7 where getTypeScriptType _ = "T7"; isGenericVariable _ = True +instance TypeScript T8 where getTypeScriptType _ = "T8"; isGenericVariable _ = True +instance TypeScript T9 where getTypeScriptType _ = "T9"; isGenericVariable _ = True +instance TypeScript T10 where getTypeScriptType _ = "T10"; isGenericVariable _ = True allStarConstructors :: [Type] allStarConstructors = [ConT ''T1, ConT ''T2, ConT ''T3, ConT ''T4, ConT ''T5, ConT ''T6, ConT ''T7, ConT ''T8, ConT ''T9, ConT ''T10] diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index c208393..33c66ac 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -7,6 +7,7 @@ import Data.Aeson as A import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Types import qualified Data.List as L +import Data.Maybe import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString @@ -142,4 +143,14 @@ contentsTupleType ci = getTupleType (constructorFields ci) getBracketsExpression :: [Name] -> Q Exp getBracketsExpression [] = [|""|] -getBracketsExpression names = [|"<" <> L.intercalate ", " $(listE [ [|getTypeScriptType (Proxy :: Proxy $(varT x))|] | x <- names]) <> ">"|] +getBracketsExpression names = [|case $(genericVariablesListExpr names) of [] -> ""; vars -> "<" <> L.intercalate ", " vars <> ">"|] + +getBracketsExpressionAllTypes :: [Name] -> Q Exp +getBracketsExpressionAllTypes [] = [|""|] +getBracketsExpressionAllTypes names = [|"<" <> L.intercalate ", " $(listE [ [|getTypeScriptType (Proxy :: Proxy $(varT x))|] | x <- names]) <> ">"|] + +genericVariablesListExpr :: [Name] -> Q Exp +genericVariablesListExpr genericVariables = [|catMaybes $(listE (fmap (\x -> + [|if isGenericVariable (Proxy :: Proxy $(varT x)) then Just (getTypeScriptType (Proxy :: Proxy $(varT x))) else Nothing|]) + genericVariables))|] + diff --git a/test/HigherKind.hs b/test/HigherKind.hs index 2fb10ac..4a661dc 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -58,8 +58,9 @@ tests = describe "Higher kinds" $ do it [i|works when referenced in another type|] $ do (getTypeScriptDeclarations (Proxy :: Proxy Foo)) `shouldBe` ([ - TSInterfaceDeclaration "Foo" [] [TSField False "fooString" "string" - , TSField False "fooHigherKindReference" "HigherKind"] + TSTypeAlternatives "Foo" [] ["IFoo"], + TSInterfaceDeclaration "IFoo" [] [TSField False "fooString" "string" + , TSField False "fooHigherKindReference" "HigherKind"] ]) it [i|works with an interface inside|] $ do diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index 2433b8c..3b24e09 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -75,7 +75,7 @@ testDeclarations testName aesonOptions = do <> getTypeScriptDeclarations (Proxy :: Proxy TwoField) <> getTypeScriptDeclarations (Proxy :: Proxy Hybrid) <> getTypeScriptDeclarations (Proxy :: Proxy TwoConstructor) - <> getTypeScriptDeclarations (Proxy :: Proxy (Complex Int)) + <> getTypeScriptDeclarations (Proxy :: Proxy (Complex T)) <> getTypeScriptDeclarations (Proxy :: Proxy Optional) |] From daa5c141bf242b8864b07091cddb238978ce8458 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 04:23:20 -0800 Subject: [PATCH 033/208] Include constraint suffixes --- src/Data/Aeson/TypeScript/TH.hs | 23 +++++++++++------------ src/Data/Aeson/TypeScript/Util.hs | 20 +++++++++++--------- test/Live.hs | 2 +- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index f7aeb3a..70d109b 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -186,18 +186,17 @@ deriveTypeScript' options name extraOptions = do forM_ (namesAndTypes options ci) $ \(_, typ) -> do searchForConstraints extraOptions typ tuple return (var, unifyGenericVariable genericInfos) - let genericVariables = fmap fst genericVariablesAndSuffixes -- Build the declarations - (types, extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options datatypeInfo genericVariables) datatypeCons + (types, extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options datatypeInfo genericVariablesAndSuffixes) datatypeCons typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName datatypeName) - $(genericVariablesListExpr genericVariables) + $(genericVariablesListExpr True genericVariablesAndSuffixes) $(listE $ fmap return types)|] let extraDecls = [x | ExtraDecl x <- extraDeclsOrGenericInfos] declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where - getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $(getBracketsExpressionAllTypes genericVariables) + getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $(getBracketsExpressionAllTypesNoSuffix genericVariablesAndSuffixes) getTypeScriptDeclarations _ = $(return declarationsFunctionBody) getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] | t <- mconcat $ fmap constructorFields datatypeCons]) @@ -205,11 +204,11 @@ deriveTypeScript' options name extraOptions = do -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration -handleConstructor :: Options -> DatatypeInfo -> [Name] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp +handleConstructor :: Options -> DatatypeInfo -> [(Name, String)] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> do writeSingleConstructorEncoding - brackets <- lift $ getBracketsExpression genericVariables + brackets <- lift $ getBracketsExpression False genericVariables lift $ [|$(TH.stringE interfaceName) <> $(return brackets)|] | allConstructorsAreNullary datatypeCons && allNullaryToStringTag options -> stringEncoding @@ -219,15 +218,15 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn -- Treat as a sum | isObjectWithSingleField $ sumEncoding options -> do writeSingleConstructorEncoding - brackets <- lift $ getBracketsExpression genericVariables + brackets <- lift $ getBracketsExpression False genericVariables lift $ [|"{" <> $(TH.stringE $ show $ constructorNameToUse options ci) <> ": " <> $(TH.stringE interfaceName) <> $(return brackets) <> "}"|] | isTwoElemArray $ sumEncoding options -> do writeSingleConstructorEncoding - brackets <- lift $ getBracketsExpression genericVariables + brackets <- lift $ getBracketsExpression False genericVariables lift $ [|"[" <> $(TH.stringE $ show $ constructorNameToUse options ci) <> ", " <> $(TH.stringE interfaceName) <> $(return brackets) <> "]"|] | isUntaggedValue $ sumEncoding options -> do writeSingleConstructorEncoding - brackets <- lift $ getBracketsExpression genericVariables + brackets <- lift $ getBracketsExpression False genericVariables lift $ [|$(TH.stringE interfaceName) <> $(return brackets)|] | otherwise -> do tagField :: [Exp] <- lift $ case sumEncoding options of @@ -237,7 +236,7 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn tsFields <- getTSFields decl <- lift $ assembleInterfaceDeclaration (ListE (tagField ++ tsFields)) tell [ExtraDecl decl] - brackets <- lift $ getBracketsExpression genericVariables + brackets <- lift $ getBracketsExpression False genericVariables lift $ [|$(TH.stringE interfaceName) <> $(return brackets)|] where @@ -256,11 +255,11 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn interfaceName = "I" <> (lastNameComponent' $ constructorName ci) tupleEncoding = [|TSTypeAlternatives $(TH.stringE interfaceName) - $(genericVariablesListExpr genericVariables) + $(genericVariablesListExpr True genericVariables) [getTypeScriptType (Proxy :: Proxy $(return $ contentsTupleType ci))]|] assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) - $(genericVariablesListExpr genericVariables) + $(genericVariablesListExpr True genericVariables) $(return members)|] getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index 33c66ac..f0ac4dd 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -141,16 +141,18 @@ constructorNameToUse options ci = (constructorTagModifier options) $ lastNameCom contentsTupleType ci = getTupleType (constructorFields ci) -getBracketsExpression :: [Name] -> Q Exp -getBracketsExpression [] = [|""|] -getBracketsExpression names = [|case $(genericVariablesListExpr names) of [] -> ""; vars -> "<" <> L.intercalate ", " vars <> ">"|] +getBracketsExpression :: Bool -> [(Name, String)] -> Q Exp +getBracketsExpression _ [] = [|""|] +getBracketsExpression includeSuffix names = [|case $(genericVariablesListExpr includeSuffix names) of [] -> ""; vars -> "<" <> L.intercalate ", " vars <> ">"|] -getBracketsExpressionAllTypes :: [Name] -> Q Exp -getBracketsExpressionAllTypes [] = [|""|] -getBracketsExpressionAllTypes names = [|"<" <> L.intercalate ", " $(listE [ [|getTypeScriptType (Proxy :: Proxy $(varT x))|] | x <- names]) <> ">"|] +getBracketsExpressionAllTypesNoSuffix :: [(Name, String)] -> Q Exp +getBracketsExpressionAllTypesNoSuffix [] = [|""|] +getBracketsExpressionAllTypesNoSuffix names = [|"<" <> L.intercalate ", " $(listE [ [|(getTypeScriptType (Proxy :: Proxy $(varT x)))|] | (x, _suffix) <- names]) <> ">"|] -genericVariablesListExpr :: [Name] -> Q Exp -genericVariablesListExpr genericVariables = [|catMaybes $(listE (fmap (\x -> +genericVariablesListExpr :: Bool -> [(Name, String)] -> Q Exp +genericVariablesListExpr True genericVariables = [|catMaybes $(listE (fmap (\(x, suffix) -> + [|if isGenericVariable (Proxy :: Proxy $(varT x)) then Just ((getTypeScriptType (Proxy :: Proxy $(varT x))) <> suffix) else Nothing|]) + genericVariables))|] +genericVariablesListExpr False genericVariables = [|catMaybes $(listE (fmap (\(x, suffix) -> [|if isGenericVariable (Proxy :: Proxy $(varT x)) then Just (getTypeScriptType (Proxy :: Proxy $(varT x))) else Nothing|]) genericVariables))|] - diff --git a/test/Live.hs b/test/Live.hs index b42f86a..c9d0df9 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -73,7 +73,7 @@ data UserT env f = User { -- $(deriveTypeScriptLookupType ''DeployEnvironment "deployEnvDecl") --- $(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) +$(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) -- data HigherKind a = HigherKind { higherKindList :: [a] } From a281cf1fbfd132b1cf5c55022e7bdd052e7ffd89 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 04:56:20 -0800 Subject: [PATCH 034/208] Q-ify a couple more helpers --- src/Data/Aeson/TypeScript/TH.hs | 15 ++++++--------- src/Data/Aeson/TypeScript/Util.hs | 8 ++++---- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 70d109b..a5643cf 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -263,15 +263,12 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn $(return members)|] getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] - getTSFields = do - forM (namesAndTypes options ci) $ \(nameString, typ) -> do - (fieldTyp, optAsBool) <- case typ of - (AppT (ConT name) t) - | name == ''Maybe && not (omitNothingFields options) -> do - fieldTyp <- lift $ [|$(return $ getTypeAsStringExp t) <> " | null"|] - return (fieldTyp, getOptionalAsBoolExp t) - x -> return (getTypeAsStringExp typ, getOptionalAsBoolExp typ) - lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) |] + getTSFields = forM (namesAndTypes options ci) $ \(nameString, typ) -> do + (fieldTyp, optAsBool) <- lift $ case typ of + (AppT (ConT name) t) | name == ''Maybe && not (omitNothingFields options) -> + ( , ) <$> [|$(getTypeAsStringExp t) <> " | null"|] <*> getOptionalAsBoolExp t + x -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ + lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) |] searchForConstraints :: ExtraTypeScriptOptions -> Type -> (Name, Name) -> WriterT [GenericInfo] Q () searchForConstraints eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) tup@(varName, genericName) diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index f0ac4dd..fb23463 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -58,11 +58,11 @@ getDatatypePredicate typ = AppT (ConT ''TypeScript) typ getDatatypePredicate typ = ClassP ''TypeScript [typ] #endif -getTypeAsStringExp :: Type -> Exp -getTypeAsStringExp typ = AppE (VarE 'getTypeScriptType) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) typ)) +getTypeAsStringExp :: Type -> Q Exp +getTypeAsStringExp typ = [|getTypeScriptType (Proxy :: Proxy $(return typ))|] -getOptionalAsBoolExp :: Type -> Exp -getOptionalAsBoolExp typ = AppE (VarE 'getTypeScriptOptional) (SigE (ConE 'Proxy) (AppT (ConT ''Proxy) typ)) +getOptionalAsBoolExp :: Type -> Q Exp +getOptionalAsBoolExp typ = [|getTypeScriptOptional (Proxy :: Proxy $(return typ))|] -- | Get the type of a tuple of constructor fields, as when we're packing a record-less constructor into a list getTupleType constructorFields = case length constructorFields of From 8bc49448da50f1e7188a2f5aab437b7cfdd12266 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 05:41:12 -0800 Subject: [PATCH 035/208] Making progress, need to change the order things happen --- src/Data/Aeson/TypeScript/TH.hs | 61 +++++++++++++++++++++++++----- src/Data/Aeson/TypeScript/Types.hs | 1 + src/Data/Aeson/TypeScript/Util.hs | 4 +- test/Live.hs | 10 ++--- 4 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index a5643cf..0ebca13 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -153,6 +153,7 @@ import Data.Maybe import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString +import Data.Typeable import Language.Haskell.TH hiding (stringE) import Language.Haskell.TH.Datatype import qualified Language.Haskell.TH.Lib as TH @@ -188,24 +189,28 @@ deriveTypeScript' options name extraOptions = do return (var, unifyGenericVariable genericInfos) -- Build the declarations - (types, extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options datatypeInfo genericVariablesAndSuffixes) datatypeCons + (types, extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options extraOptions datatypeInfo genericVariablesAndSuffixes) datatypeCons typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName datatypeName) $(genericVariablesListExpr True genericVariablesAndSuffixes) $(listE $ fmap return types)|] let extraDecls = [x | ExtraDecl x <- extraDeclsOrGenericInfos] + let extraTopLevelDecls = mconcat [x | ExtraTopLevelDecs x <- extraDeclsOrGenericInfos] declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] - [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where - getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $(getBracketsExpressionAllTypesNoSuffix genericVariablesAndSuffixes) - getTypeScriptDeclarations _ = $(return declarationsFunctionBody) - getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] - | t <- mconcat $ fmap constructorFields datatypeCons]) - |] + inst <- [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where + getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $(getBracketsExpressionAllTypesNoSuffix genericVariablesAndSuffixes) + getTypeScriptDeclarations _ = $(return declarationsFunctionBody) + getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] + | t <- mconcat $ fmap constructorFields datatypeCons]) + |] + reportWarning [i|extraTopLevelDecls: #{extraTopLevelDecls}|] + + return (extraTopLevelDecls <> inst) -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration -handleConstructor :: Options -> DatatypeInfo -> [(Name, String)] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp -handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = +handleConstructor :: Options -> ExtraTypeScriptOptions -> DatatypeInfo -> [(Name, String)] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp +handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> do writeSingleConstructorEncoding brackets <- lift $ getBracketsExpression False genericVariables @@ -263,13 +268,49 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci@(ConstructorIn $(return members)|] getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] - getTSFields = forM (namesAndTypes options ci) $ \(nameString, typ) -> do + getTSFields = forM (namesAndTypes options ci) $ \(nameString, typ') -> do + typ <- transformTypeFamilies extraOptions typ' + (fieldTyp, optAsBool) <- lift $ case typ of (AppT (ConT name) t) | name == ''Maybe && not (omitNothingFields options) -> ( , ) <$> [|$(getTypeAsStringExp t) <> " | null"|] <*> getOptionalAsBoolExp t x -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) |] +transformTypeFamilies :: ExtraTypeScriptOptions -> Type -> WriterT [ExtraDeclOrGenericInfo] Q Type +transformTypeFamilies eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) + | name `L.elem` typeFamiliesToMapToTypeScript = lift (reify name) >>= \case + FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) _) _ -> do + name' <- lift $ newName (nameBase typeFamilyName <> "'") + -- name' <- lift $ newName "Foo" + lift $ reportWarning [i|Made new name based on #{nameBase typeFamilyName}, need to export an instance for it: #{name'}|] + + f <- lift $ newName "f" + let inst1 = DataD [] name' [PlainTV f] Nothing [] [] + tell [ExtraTopLevelDecs [inst1]] + + g <- lift $ newName "g" + inst2 <- lift $ [d|instance (Typeable g) => TypeScript ($(conT name') g) where + getTypeScriptType _ = "hi" + -- getTypeScriptDeclarations _ = $(return declarationsFunctionBody) + -- getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] + -- | t <- mconcat $ fmap constructorFields datatypeCons]) + |] + tell [ExtraTopLevelDecs inst2] + + transformTypeFamilies eo (AppT (ConT name') typ) + _ -> AppT (ConT name) <$> transformTypeFamilies eo typ + | otherwise = AppT (ConT name) <$> transformTypeFamilies eo typ +transformTypeFamilies eo (AppT typ1 typ2) = AppT <$> transformTypeFamilies eo typ1 <*> transformTypeFamilies eo typ2 +transformTypeFamilies eo (AppKindT typ kind) = flip AppKindT kind <$> transformTypeFamilies eo typ +transformTypeFamilies eo (SigT typ kind) = flip SigT kind <$> transformTypeFamilies eo typ +transformTypeFamilies eo (InfixT typ1 n typ2) = InfixT <$> transformTypeFamilies eo typ1 <*> pure n <*> transformTypeFamilies eo typ2 +transformTypeFamilies eo (UInfixT typ1 n typ2) = UInfixT <$> transformTypeFamilies eo typ1 <*> pure n <*> transformTypeFamilies eo typ2 +transformTypeFamilies eo (ParensT typ) = ParensT <$> transformTypeFamilies eo typ +transformTypeFamilies eo (ImplicitParamT s typ) = ImplicitParamT s <$> transformTypeFamilies eo typ +transformTypeFamilies eo typ = return typ + + searchForConstraints :: ExtraTypeScriptOptions -> Type -> (Name, Name) -> WriterT [GenericInfo] Q () searchForConstraints eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) tup@(varName, genericName) | typ == VarT varName && (name `L.elem` typeFamiliesToMapToTypeScript) = lift (reify name) >>= \case diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 26064a0..26269c1 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -152,6 +152,7 @@ defaultExtraTypeScriptOptions = ExtraTypeScriptOptions [] data ExtraDeclOrGenericInfo = ExtraDecl Exp | ExtraGeneric GenericInfo + | ExtraTopLevelDecs [Dec] deriving Show data GenericInfo = GenericInfo Name Name GenericInfoExtra diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index fb23463..fa9415e 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -151,8 +151,8 @@ getBracketsExpressionAllTypesNoSuffix names = [|"<" <> L.intercalate ", " $(list genericVariablesListExpr :: Bool -> [(Name, String)] -> Q Exp genericVariablesListExpr True genericVariables = [|catMaybes $(listE (fmap (\(x, suffix) -> - [|if isGenericVariable (Proxy :: Proxy $(varT x)) then Just ((getTypeScriptType (Proxy :: Proxy $(varT x))) <> suffix) else Nothing|]) + [|if isGenericVariable (Proxy :: Proxy $(varT x)) then Just ((getTypeScriptType (Proxy :: Proxy $(varT x))) <> $(TH.stringE suffix)) else Nothing|]) genericVariables))|] -genericVariablesListExpr False genericVariables = [|catMaybes $(listE (fmap (\(x, suffix) -> +genericVariablesListExpr False genericVariables = [|catMaybes $(listE (fmap (\(x, _suffix) -> [|if isGenericVariable (Proxy :: Proxy $(varT x)) then Just (getTypeScriptType (Proxy :: Proxy $(varT x))) else Nothing|]) genericVariables))|] diff --git a/test/Live.hs b/test/Live.hs index c9d0df9..e1aa6e1 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -31,11 +31,11 @@ import Prelude hiding (Double) import Database.Beam -data TestT a = TestT { - listOfA :: [a] - , maybeA :: Maybe a - } -$(deriveTypeScript A.defaultOptions ''TestT) +-- data TestT a = TestT { +-- listOfA :: [a] +-- , maybeA :: Maybe a +-- } +-- $(deriveTypeScript A.defaultOptions ''TestT) instance TypeScript UTCTime where getTypeScriptType _ = "DateTime" From d8d1e34d274dea9d9b4dc78445ae27a43d107369 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 05:46:49 -0800 Subject: [PATCH 036/208] Remove some unnecessary variables --- src/Data/Aeson/TypeScript/TH.hs | 34 +++++++++++++++--------------- src/Data/Aeson/TypeScript/Types.hs | 2 +- test/Live.hs | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 0ebca13..f5761fd 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -182,10 +182,10 @@ deriveTypeScript' options name extraOptions = do let eligibleGenericVars = catMaybes $ flip fmap (getDataTypeVars datatypeInfo) $ \case SigT (VarT n) StarT -> Just n _ -> Nothing - genericVariablesAndSuffixes <- forM (zip eligibleGenericVars allStarConstructors') $ \tuple@(var, genericVar) -> do + genericVariablesAndSuffixes <- forM eligibleGenericVars $ \var -> do (_, genericInfos) <- runWriterT $ forM_ datatypeCons $ \ci -> forM_ (namesAndTypes options ci) $ \(_, typ) -> do - searchForConstraints extraOptions typ tuple + searchForConstraints extraOptions typ var return (var, unifyGenericVariable genericInfos) -- Build the declarations @@ -311,25 +311,25 @@ transformTypeFamilies eo (ImplicitParamT s typ) = ImplicitParamT s <$> transform transformTypeFamilies eo typ = return typ -searchForConstraints :: ExtraTypeScriptOptions -> Type -> (Name, Name) -> WriterT [GenericInfo] Q () -searchForConstraints eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) tup@(varName, genericName) - | typ == VarT varName && (name `L.elem` typeFamiliesToMapToTypeScript) = lift (reify name) >>= \case +searchForConstraints :: ExtraTypeScriptOptions -> Type -> Name -> WriterT [GenericInfo] Q () +searchForConstraints eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) var + | typ == VarT var && (name `L.elem` typeFamiliesToMapToTypeScript) = lift (reify name) >>= \case FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) _) _ -> do - tell [GenericInfo varName genericName (TypeFamilyKey typeFamilyName)] - searchForConstraints eo typ tup - _ -> searchForConstraints eo typ tup - | otherwise = searchForConstraints eo typ tup -searchForConstraints eo (AppT typ1 typ2) tup = searchForConstraints eo typ1 tup >> searchForConstraints eo typ2 tup -searchForConstraints eo (AppKindT typ _) tup = searchForConstraints eo typ tup -searchForConstraints eo (SigT typ _) tup = searchForConstraints eo typ tup -searchForConstraints eo (InfixT typ1 _ typ2) tup = searchForConstraints eo typ1 tup >> searchForConstraints eo typ2 tup -searchForConstraints eo (UInfixT typ1 _ typ2) tup = searchForConstraints eo typ1 tup >> searchForConstraints eo typ2 tup -searchForConstraints eo (ParensT typ) tup = searchForConstraints eo typ tup -searchForConstraints eo (ImplicitParamT _ typ) tup = searchForConstraints eo typ tup + tell [GenericInfo var (TypeFamilyKey typeFamilyName)] + searchForConstraints eo typ var + _ -> searchForConstraints eo typ var + | otherwise = searchForConstraints eo typ var +searchForConstraints eo (AppT typ1 typ2) var = searchForConstraints eo typ1 var >> searchForConstraints eo typ2 var +searchForConstraints eo (AppKindT typ _) var = searchForConstraints eo typ var +searchForConstraints eo (SigT typ _) var = searchForConstraints eo typ var +searchForConstraints eo (InfixT typ1 _ typ2) var = searchForConstraints eo typ1 var >> searchForConstraints eo typ2 var +searchForConstraints eo (UInfixT typ1 _ typ2) var = searchForConstraints eo typ1 var >> searchForConstraints eo typ2 var +searchForConstraints eo (ParensT typ) var = searchForConstraints eo typ var +searchForConstraints eo (ImplicitParamT _ typ) var = searchForConstraints eo typ var searchForConstraints eo _ _ = return () unifyGenericVariable :: [GenericInfo] -> String -unifyGenericVariable genericInfos = case [nameBase name | GenericInfo _ _ (TypeFamilyKey name) <- genericInfos] of +unifyGenericVariable genericInfos = case [nameBase name | GenericInfo _ (TypeFamilyKey name) <- genericInfos] of [] -> "" names -> " extends keyof " <> (L.intercalate " & " names) diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 26269c1..2e57824 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -155,7 +155,7 @@ data ExtraDeclOrGenericInfo = ExtraDecl Exp | ExtraTopLevelDecs [Dec] deriving Show -data GenericInfo = GenericInfo Name Name GenericInfoExtra +data GenericInfo = GenericInfo Name GenericInfoExtra deriving Show data GenericInfoExtra = NormalStar diff --git a/test/Live.hs b/test/Live.hs index e1aa6e1..ada2002 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -73,7 +73,7 @@ data UserT env f = User { -- $(deriveTypeScriptLookupType ''DeployEnvironment "deployEnvDecl") -$(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) +-- $(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) -- data HigherKind a = HigherKind { higherKindList :: [a] } From 10a130edda77896352a0e20431167e74ac3185c9 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 17:18:12 -0800 Subject: [PATCH 037/208] About to try adding the needed constraint --- src/Data/Aeson/TypeScript/TH.hs | 10 +++++++--- test/Live.hs | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index f5761fd..d942d8c 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -270,11 +270,17 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@( getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] getTSFields = forM (namesAndTypes options ci) $ \(nameString, typ') -> do typ <- transformTypeFamilies extraOptions typ' + -- TODO: emit another constraint here to add to the main TypeScript instance + -- when (typ /= typ') $ do + -- inst3 <- lift $ [d|instance (TypeScript $(return typ')) => TypeScript $(return typ) where + -- getTypeScriptType _ = "hiiii" + -- |] + -- tell [ExtraTopLevelDecs inst3] (fieldTyp, optAsBool) <- lift $ case typ of (AppT (ConT name) t) | name == ''Maybe && not (omitNothingFields options) -> ( , ) <$> [|$(getTypeAsStringExp t) <> " | null"|] <*> getOptionalAsBoolExp t - x -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ + x -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ' lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) |] transformTypeFamilies :: ExtraTypeScriptOptions -> Type -> WriterT [ExtraDeclOrGenericInfo] Q Type @@ -282,8 +288,6 @@ transformTypeFamilies eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) | name `L.elem` typeFamiliesToMapToTypeScript = lift (reify name) >>= \case FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) _) _ -> do name' <- lift $ newName (nameBase typeFamilyName <> "'") - -- name' <- lift $ newName "Foo" - lift $ reportWarning [i|Made new name based on #{nameBase typeFamilyName}, need to export an instance for it: #{name'}|] f <- lift $ newName "f" let inst1 = DataD [] name' [PlainTV f] Nothing [] [] diff --git a/test/Live.hs b/test/Live.hs index ada2002..979aa96 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE OverloadedStrings #-} @@ -73,7 +74,7 @@ data UserT env f = User { -- $(deriveTypeScriptLookupType ''DeployEnvironment "deployEnvDecl") --- $(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) +$(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) -- data HigherKind a = HigherKind { higherKindList :: [a] } From 4d8391dbfc14feedef01a78361b731b2e6aab85e Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 17:56:10 -0800 Subject: [PATCH 038/208] Type family thing is working --- aeson-typescript.cabal | 3 +- src/Data/Aeson/TypeScript/Instances.hs | 7 ++-- src/Data/Aeson/TypeScript/Lookup.hs | 22 +++++++------ src/Data/Aeson/TypeScript/Recursive.hs | 8 +++++ src/Data/Aeson/TypeScript/TH.hs | 30 ++++++++--------- src/Data/Aeson/TypeScript/Types.hs | 2 ++ src/Data/Aeson/TypeScript/Util.hs | 6 +++- test/Live.hs | 20 +++--------- test/Live2.hs | 45 ++++++++++++++++++++++++++ 9 files changed, 100 insertions(+), 43 deletions(-) create mode 100644 test/Live2.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 33236be..706a87e 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -4,7 +4,7 @@ cabal-version: 1.12 -- -- see: https://github.com/sol/hpack -- --- hash: a8a23c594d80e27e34cb591cabb2a8ed3849b7bb2e7fe79c05fe526992ad6898 +-- hash: b3411bc5359cf53933561d5ecc5dda2e3ab245d676bc521f8f74fa5970700895 name: aeson-typescript version: 0.2.0.0 @@ -65,6 +65,7 @@ test-suite aeson-typescript-test other-modules: HigherKind Live + Live2 LiveLogging NoOmitNothingFields ObjectWithSingleFieldNoTagSingleConstructors diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index 5fe901c..38d71fc 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -1,4 +1,5 @@ -{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, ExistentialQuantification, FlexibleInstances, OverlappingInstances #-} +{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, ExistentialQuantification, FlexibleInstances, OverlappingInstances, CPP #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} -- Note: the OverlappingInstances pragma is only here so the overlapping instances in this file -- will work on older GHCs, like GHC 7.8.4 @@ -10,13 +11,15 @@ import Data.Aeson.TypeScript.Types import Data.Data import Data.HashMap.Strict import qualified Data.List as L -import Data.Monoid import Data.Set import Data.String.Interpolate.IsString import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Data.Void +#if !MIN_VERSION_base(4,11,0) +import Data.Monoid +#endif instance TypeScript () where getTypeScriptType _ = "void" diff --git a/src/Data/Aeson/TypeScript/Lookup.hs b/src/Data/Aeson/TypeScript/Lookup.hs index 9e7868b..ee0440c 100644 --- a/src/Data/Aeson/TypeScript/Lookup.hs +++ b/src/Data/Aeson/TypeScript/Lookup.hs @@ -18,7 +18,6 @@ module Data.Aeson.TypeScript.Lookup where import Control.Monad import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Types -import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString import Language.Haskell.TH hiding (stringE) @@ -34,14 +33,19 @@ deriveTypeScriptLookupType :: Name deriveTypeScriptLookupType name declNameStr = do info <- reify name case info of - FamilyI (ClosedTypeFamilyD (TypeFamilyHead name vars sig maybeInject) eqns) decs -> do - fields <- forM eqns $ \case - TySynEqn Nothing (AppT (ConT _) (ConT arg)) (ConT result) -> - [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(conT result))) |] - x -> fail [i|Don't know how to handle type family equation: '#{x}'|] + FamilyI (ClosedTypeFamilyD (TypeFamilyHead _name _vars _sig _maybeInject) eqns) _decs -> do + interfaceDecl <- getClosedTypeFamilyInterfaceDecl name eqns + return [FunD (mkName declNameStr) [Clause [] (NormalB (ListE [interfaceDecl])) []]] - expr <- [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] $(listE $ fmap return fields) |] + _ -> fail [i|Expected a close type family; got #{info}|] - return [FunD (mkName declNameStr) [Clause [] (NormalB (ListE [expr])) []]] +getClosedTypeFamilyInterfaceDecl :: Name -> [TySynEqn] -> Q Exp +getClosedTypeFamilyInterfaceDecl name eqns = do + fields <- forM eqns $ \case + TySynEqn Nothing (AppT (ConT _) (ConT arg)) result -> do + reportWarning [i|arg: #{arg}|] + reportWarning [i|result: #{result}|] + [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) |] + x -> fail [i|Don't know how to handle type family equation: '#{x}'|] - _ -> fail [i|Expected a close type family; got #{info}|] + [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] $(listE $ fmap return fields) |] diff --git a/src/Data/Aeson/TypeScript/Recursive.hs b/src/Data/Aeson/TypeScript/Recursive.hs index 4bcc546..a67ecee 100755 --- a/src/Data/Aeson/TypeScript/Recursive.hs +++ b/src/Data/Aeson/TypeScript/Recursive.hs @@ -2,11 +2,13 @@ module Data.Aeson.TypeScript.Recursive ( getTransitiveClosure + , getTypeScriptDeclarationsRecursively ) where import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.TH import Data.Function +import Data.Proxy import qualified Data.Set as S @@ -17,3 +19,9 @@ getTransitiveClosure initialTypes = fix (\loop items -> let items' = S.unions (i ) initialTypes where getMore :: TSType -> S.Set TSType getMore (TSType x) = S.fromList $ getParentTypes x + +getTypeScriptDeclarationsRecursively :: (TypeScript a) => Proxy a -> [TSDeclaration] +getTypeScriptDeclarationsRecursively initialType = S.toList $ S.fromList declarations + where + closure = getTransitiveClosure (S.fromList [TSType initialType]) + declarations = mconcat [getTypeScriptDeclarations x | TSType x <- S.toList closure] diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index d942d8c..2f108c9 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -176,8 +176,6 @@ deriveTypeScript' options name extraOptions = do -- Probably overkill/not exactly right, but it's a start. let constructorPreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- mconcat $ fmap constructorFields datatypeCons] let typeVariablePreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- getDataTypeVars datatypeInfo] - let predicates = constructorPreds <> typeVariablePreds - let constraints = foldl AppT (TupleT (length predicates)) predicates let eligibleGenericVars = catMaybes $ flip fmap (getDataTypeVars datatypeInfo) $ \case SigT (VarT n) StarT -> Just n @@ -195,13 +193,18 @@ deriveTypeScript' options name extraOptions = do $(listE $ fmap return types)|] let extraDecls = [x | ExtraDecl x <- extraDeclsOrGenericInfos] let extraTopLevelDecls = mconcat [x | ExtraTopLevelDecs x <- extraDeclsOrGenericInfos] + let predicates = constructorPreds <> typeVariablePreds <> [x | ExtraConstraint x <- extraDeclsOrGenericInfos] + let constraints = foldl AppT (TupleT (length predicates)) predicates + declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] + let extraParentTypes = [x | ExtraParentType x <- extraDeclsOrGenericInfos] + reportWarning [i|Extra parent types: #{extraParentTypes}|] inst <- [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $(getBracketsExpressionAllTypesNoSuffix genericVariablesAndSuffixes) getTypeScriptDeclarations _ = $(return declarationsFunctionBody) getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] - | t <- mconcat $ fmap constructorFields datatypeCons]) + | t <- (mconcat $ fmap constructorFields datatypeCons) <> extraParentTypes]) |] reportWarning [i|extraTopLevelDecls: #{extraTopLevelDecls}|] @@ -270,12 +273,9 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@( getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] getTSFields = forM (namesAndTypes options ci) $ \(nameString, typ') -> do typ <- transformTypeFamilies extraOptions typ' - -- TODO: emit another constraint here to add to the main TypeScript instance - -- when (typ /= typ') $ do - -- inst3 <- lift $ [d|instance (TypeScript $(return typ')) => TypeScript $(return typ) where - -- getTypeScriptType _ = "hiiii" - -- |] - -- tell [ExtraTopLevelDecs inst3] + when (typ /= typ') $ do + let constraint = AppT (ConT ''TypeScript) typ + tell [ExtraConstraint constraint] (fieldTyp, optAsBool) <- lift $ case typ of (AppT (ConT name) t) | name == ''Maybe && not (omitNothingFields options) -> @@ -286,7 +286,7 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@( transformTypeFamilies :: ExtraTypeScriptOptions -> Type -> WriterT [ExtraDeclOrGenericInfo] Q Type transformTypeFamilies eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) | name `L.elem` typeFamiliesToMapToTypeScript = lift (reify name) >>= \case - FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) _) _ -> do + FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) eqns) _ -> do name' <- lift $ newName (nameBase typeFamilyName <> "'") f <- lift $ newName "f" @@ -294,14 +294,14 @@ transformTypeFamilies eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) tell [ExtraTopLevelDecs [inst1]] g <- lift $ newName "g" - inst2 <- lift $ [d|instance (Typeable g) => TypeScript ($(conT name') g) where - getTypeScriptType _ = "hi" - -- getTypeScriptDeclarations _ = $(return declarationsFunctionBody) - -- getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] - -- | t <- mconcat $ fmap constructorFields datatypeCons]) + inst2 <- lift $ [d|instance (Typeable g, TypeScript g) => TypeScript ($(conT name') g) where + getTypeScriptType _ = $(TH.stringE $ nameBase name) <> "[" <> (getTypeScriptType (Proxy :: Proxy g)) <> "]" + getTypeScriptDeclarations _ = [$(getClosedTypeFamilyInterfaceDecl name eqns)] |] tell [ExtraTopLevelDecs inst2] + tell [ExtraParentType (AppT (ConT name') (ConT ''T))] + transformTypeFamilies eo (AppT (ConT name') typ) _ -> AppT (ConT name) <$> transformTypeFamilies eo typ | otherwise = AppT (ConT name) <$> transformTypeFamilies eo typ diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 2e57824..1127be7 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -153,6 +153,8 @@ defaultExtraTypeScriptOptions = ExtraTypeScriptOptions [] data ExtraDeclOrGenericInfo = ExtraDecl Exp | ExtraGeneric GenericInfo | ExtraTopLevelDecs [Dec] + | ExtraConstraint Type + | ExtraParentType Type deriving Show data GenericInfo = GenericInfo Name GenericInfoExtra diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index fa9415e..994e4d9 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -8,7 +8,6 @@ import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Types import qualified Data.List as L import Data.Maybe -import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString import qualified Data.Text as T @@ -16,13 +15,18 @@ import Language.Haskell.TH hiding (stringE) import Language.Haskell.TH.Datatype import qualified Language.Haskell.TH.Lib as TH +#if !MIN_VERSION_base(4,11,0) +import Data.Monoid +#endif +getDataTypeVars :: DatatypeInfo -> [Type] #if MIN_VERSION_th_abstraction(0,3,0) getDataTypeVars (DatatypeInfo {datatypeInstTypes}) = datatypeInstTypes #else getDataTypeVars (DatatypeInfo {datatypeVars}) = datatypeVars #endif +setDataTypeVars :: DatatypeInfo -> [Type] -> DatatypeInfo #if MIN_VERSION_th_abstraction(0,3,0) setDataTypeVars dti@(DatatypeInfo {}) vars = dti { datatypeInstTypes = vars } #else diff --git a/test/Live.hs b/test/Live.hs index 979aa96..5aceb2e 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -18,9 +18,11 @@ module Live where import Data.Aeson as A import Data.Aeson.TH as A +import Data.Aeson.TypeScript.Recursive import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types import Data.Kind +import Data.Function import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString @@ -32,18 +34,9 @@ import Prelude hiding (Double) import Database.Beam --- data TestT a = TestT { --- listOfA :: [a] --- , maybeA :: Maybe a --- } --- $(deriveTypeScript A.defaultOptions ''TestT) - instance TypeScript UTCTime where getTypeScriptType _ = "DateTime" -instance (Typeable a) => TypeScript (Identity a) where - getTypeScriptType x = getTypeScriptType x - instance TypeScript Identity where getTypeScriptType _ = "any" @@ -72,11 +65,8 @@ data UserT env f = User { , _userDeployEnvironment :: Columnar f (DeployEnvironment env) } --- $(deriveTypeScriptLookupType ''DeployEnvironment "deployEnvDecl") - $(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) - --- data HigherKind a = HigherKind { higherKindList :: [a] } --- $(deriveTypeScript A.defaultOptions ''HigherKind) - +main = getTypeScriptDeclarationsRecursively (Proxy :: Proxy (UserT T Identity)) + & formatTSDeclarations + & putStrLn diff --git a/test/Live2.hs b/test/Live2.hs new file mode 100644 index 0000000..5873d82 --- /dev/null +++ b/test/Live2.hs @@ -0,0 +1,45 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeFamilyDependencies #-} + +module Live2 where + +import Data.Aeson as A +import Data.Aeson.TH as A +import Data.Aeson.TypeScript.Recursive +import Data.Aeson.TypeScript.TH +import Data.Aeson.TypeScript.Types +import Data.Kind +import Data.Function +import Data.Monoid +import Data.Proxy +import Data.String.Interpolate.IsString +import qualified Data.Text as T +import Data.Time +import Database.Beam +import Language.Haskell.TH +import Prelude hiding (Double) +import Database.Beam + + +data TestT a = TestT { + listOfA :: [a] + , maybeA :: Maybe a + } +$(deriveTypeScript A.defaultOptions ''TestT) + +data HigherKind a = HigherKind { higherKindList :: [a] } +$(deriveTypeScript A.defaultOptions ''HigherKind) + From 6c6e9c8f430a197f93a71886c3ef236e495b2f76 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 18:17:13 -0800 Subject: [PATCH 039/208] Fix all warnings --- src/Data/Aeson/TypeScript/Formatting.hs | 16 +++++++----- src/Data/Aeson/TypeScript/Lookup.hs | 2 -- src/Data/Aeson/TypeScript/TH.hs | 14 +++++------ src/Data/Aeson/TypeScript/Util.hs | 20 ++++++--------- test/HigherKind.hs | 6 +++-- test/Live.hs | 7 ++---- test/Live2.hs | 14 ----------- test/LiveLogging.hs | 13 ---------- test/NoOmitNothingFields.hs | 5 +++- ...tWithSingleFieldNoTagSingleConstructors.hs | 7 +++--- ...ectWithSingleFieldTagSingleConstructors.hs | 6 +++-- test/OmitNothingFields.hs | 5 +++- test/Spec.hs | 2 ++ test/TaggedObjectNoTagSingleConstructors.hs | 7 +++--- test/TaggedObjectTagSingleConstructors.hs | 6 +++-- test/TestBoilerplate.hs | 3 ++- test/TwoElemArrayNoTagSingleConstructors.hs | 7 +++--- test/TwoElemArrayTagSingleConstructors.hs | 6 +++-- test/UntaggedNoTagSingleConstructors.hs | 7 +++--- test/UntaggedTagSingleConstructors.hs | 7 ++++-- test/Util.hs | 25 +++++++++---------- 21 files changed, 86 insertions(+), 99 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index ec9e26b..909a8ba 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -1,12 +1,16 @@ -{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns #-} +{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, CPP #-} module Data.Aeson.TypeScript.Formatting where import Data.Aeson.TypeScript.Types -import Data.Monoid import Data.String.Interpolate.IsString import qualified Data.Text as T +#if !MIN_VERSION_base(4,11,0) +import Data.Monoid +#endif + + -- | Same as 'formatTSDeclarations'', but uses default formatting options. formatTSDeclarations :: [TSDeclaration] -> String formatTSDeclarations = formatTSDeclarations' defaultFormattingOptions @@ -19,9 +23,9 @@ formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVar formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceName genericVariables members) = [i|interface #{modifiedInterfaceName}#{getGenericBrackets genericVariables} { -#{lines} -}|] where lines = T.intercalate "\n" $ fmap T.pack [(replicate numIndentSpaces ' ') <> formatTSField member <> ";"| member <- members] - modifiedInterfaceName = (\(i, name) -> i <> interfaceNameModifier name) . splitAt 1 $ interfaceName +#{ls} +}|] where ls = T.intercalate "\n" $ fmap T.pack [(replicate numIndentSpaces ' ') <> formatTSField member <> ";"| member <- members] + modifiedInterfaceName = (\(li, name) -> li <> interfaceNameModifier name) . splitAt 1 $ interfaceName formatTSDeclaration (FormattingOptions {..}) (TSRawDeclaration text) = text @@ -30,7 +34,7 @@ formatTSDeclarations' :: FormattingOptions -> [TSDeclaration] -> String formatTSDeclarations' options declarations = T.unpack $ T.intercalate "\n\n" (fmap (T.pack . formatTSDeclaration options) declarations) formatTSField :: TSField -> String -formatTSField (TSField optional name typ) = [i|#{name}#{if optional then "?" else ""}: #{typ}|] +formatTSField (TSField optional name typ) = [i|#{name}#{if optional then ("?" :: String) else ""}: #{typ}|] getGenericBrackets :: [String] -> String getGenericBrackets [] = "" diff --git a/src/Data/Aeson/TypeScript/Lookup.hs b/src/Data/Aeson/TypeScript/Lookup.hs index ee0440c..046a670 100644 --- a/src/Data/Aeson/TypeScript/Lookup.hs +++ b/src/Data/Aeson/TypeScript/Lookup.hs @@ -43,8 +43,6 @@ getClosedTypeFamilyInterfaceDecl :: Name -> [TySynEqn] -> Q Exp getClosedTypeFamilyInterfaceDecl name eqns = do fields <- forM eqns $ \case TySynEqn Nothing (AppT (ConT _) (ConT arg)) result -> do - reportWarning [i|arg: #{arg}|] - reportWarning [i|result: #{result}|] [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) |] x -> fail [i|Don't know how to handle type family equation: '#{x}'|] diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 2f108c9..e63818f 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -150,7 +150,6 @@ import Data.Aeson.TypeScript.Types import Data.Aeson.TypeScript.Util import qualified Data.List as L import Data.Maybe -import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString import Data.Typeable @@ -158,6 +157,9 @@ import Language.Haskell.TH hiding (stringE) import Language.Haskell.TH.Datatype import qualified Language.Haskell.TH.Lib as TH +#if !MIN_VERSION_base(4,11,0) +import Data.Monoid +#endif -- | Generates a 'TypeScript' instance declaration for the given data type. deriveTypeScript' :: Options @@ -199,7 +201,6 @@ deriveTypeScript' options name extraOptions = do declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] let extraParentTypes = [x | ExtraParentType x <- extraDeclsOrGenericInfos] - reportWarning [i|Extra parent types: #{extraParentTypes}|] inst <- [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $(getBracketsExpressionAllTypesNoSuffix genericVariablesAndSuffixes) getTypeScriptDeclarations _ = $(return declarationsFunctionBody) @@ -207,8 +208,6 @@ deriveTypeScript' options name extraOptions = do | t <- (mconcat $ fmap constructorFields datatypeCons) <> extraParentTypes]) |] - reportWarning [i|extraTopLevelDecls: #{extraTopLevelDecls}|] - return (extraTopLevelDecls <> inst) -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration @@ -280,7 +279,7 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@( (fieldTyp, optAsBool) <- lift $ case typ of (AppT (ConT name) t) | name == ''Maybe && not (omitNothingFields options) -> ( , ) <$> [|$(getTypeAsStringExp t) <> " | null"|] <*> getOptionalAsBoolExp t - x -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ' + _ -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ' lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) |] transformTypeFamilies :: ExtraTypeScriptOptions -> Type -> WriterT [ExtraDeclOrGenericInfo] Q Type @@ -293,7 +292,6 @@ transformTypeFamilies eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) let inst1 = DataD [] name' [PlainTV f] Nothing [] [] tell [ExtraTopLevelDecs [inst1]] - g <- lift $ newName "g" inst2 <- lift $ [d|instance (Typeable g, TypeScript g) => TypeScript ($(conT name') g) where getTypeScriptType _ = $(TH.stringE $ nameBase name) <> "[" <> (getTypeScriptType (Proxy :: Proxy g)) <> "]" getTypeScriptDeclarations _ = [$(getClosedTypeFamilyInterfaceDecl name eqns)] @@ -312,7 +310,7 @@ transformTypeFamilies eo (InfixT typ1 n typ2) = InfixT <$> transformTypeFamilies transformTypeFamilies eo (UInfixT typ1 n typ2) = UInfixT <$> transformTypeFamilies eo typ1 <*> pure n <*> transformTypeFamilies eo typ2 transformTypeFamilies eo (ParensT typ) = ParensT <$> transformTypeFamilies eo typ transformTypeFamilies eo (ImplicitParamT s typ) = ImplicitParamT s <$> transformTypeFamilies eo typ -transformTypeFamilies eo typ = return typ +transformTypeFamilies _ typ = return typ searchForConstraints :: ExtraTypeScriptOptions -> Type -> Name -> WriterT [GenericInfo] Q () @@ -330,7 +328,7 @@ searchForConstraints eo (InfixT typ1 _ typ2) var = searchForConstraints eo typ1 searchForConstraints eo (UInfixT typ1 _ typ2) var = searchForConstraints eo typ1 var >> searchForConstraints eo typ2 var searchForConstraints eo (ParensT typ) var = searchForConstraints eo typ var searchForConstraints eo (ImplicitParamT _ typ) var = searchForConstraints eo typ var -searchForConstraints eo _ _ = return () +searchForConstraints _ _ _ = return () unifyGenericVariable :: [GenericInfo] -> String unifyGenericVariable genericInfos = case [nameBase name | GenericInfo _ (TypeFamilyKey name) <- genericInfos] of diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index 994e4d9..58bd96a 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -69,6 +69,7 @@ getOptionalAsBoolExp :: Type -> Q Exp getOptionalAsBoolExp typ = [|getTypeScriptOptional (Proxy :: Proxy $(return typ))|] -- | Get the type of a tuple of constructor fields, as when we're packing a record-less constructor into a list +getTupleType :: [Type] -> Type getTupleType constructorFields = case length constructorFields of 0 -> AppT ListT (ConT ''()) 1 -> head constructorFields @@ -84,15 +85,6 @@ applyToArgsE :: Exp -> [Exp] -> Exp applyToArgsE f [] = f applyToArgsE f (x:xs) = applyToArgsE (AppE f x) xs -stringE = LitE . StringL - --- Between Template Haskell 2.10 and 2.11, InstanceD got an additional argument -#if MIN_VERSION_template_haskell(2,11,0) -mkInstance context typ decs = InstanceD Nothing context typ decs -#else -mkInstance context typ decs = InstanceD context typ decs -#endif - -- Between Aeson 1.1.2.0 and 1.2.0.0, tagSingleConstructors was added getTagSingleConstructors :: Options -> Bool #if MIN_VERSION_aeson(1,2,0) @@ -115,34 +107,36 @@ assertExtensionsTurnedOn _ = return () #endif -- Older versions of Aeson don't have an Eq instance for SumEncoding so we do this +isObjectWithSingleField :: SumEncoding -> Bool isObjectWithSingleField ObjectWithSingleField = True isObjectWithSingleField _ = False -- Older versions of Aeson don't have an Eq instance for SumEncoding so we do this +isTwoElemArray :: SumEncoding -> Bool isTwoElemArray TwoElemArray = True isTwoElemArray _ = False -- Older versions of Aeson don't have an Eq instance for SumEncoding so we do this -- UntaggedValue was added between Aeson 0.11.3.0 and 1.0.0.0 +isUntaggedValue :: SumEncoding -> Bool #if MIN_VERSION_aeson(1,0,0) isUntaggedValue UntaggedValue = True #endif isUntaggedValue _ = False -fst3 (x, _, _) = x -snd3 (_, y, _) = y - namesAndTypes :: Options -> ConstructorInfo -> [(String, Type)] namesAndTypes options ci = case constructorVariant ci of RecordConstructor names -> zip (fmap ((fieldLabelModifier options) . lastNameComponent') names) (constructorFields ci) - NormalConstructor -> case sumEncoding options of + _ -> case sumEncoding options of TaggedObject _ contentsFieldName | isConstructorNullary ci -> [] | otherwise -> [(contentsFieldName, contentsTupleType ci)] _ -> [(constructorNameToUse options ci, contentsTupleType ci)] +constructorNameToUse :: Options -> ConstructorInfo -> String constructorNameToUse options ci = (constructorTagModifier options) $ lastNameComponent' (constructorName ci) +contentsTupleType :: ConstructorInfo -> Type contentsTupleType ci = getTupleType (constructorFields ci) getBracketsExpression :: Bool -> [(Name, String)] -> Q Exp diff --git a/test/HigherKind.hs b/test/HigherKind.hs index 4a661dc..a2f82d5 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -11,6 +11,7 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} module HigherKind (tests) where @@ -18,7 +19,6 @@ import Data.Aeson as A import Data.Aeson.TH as A import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types -import Data.Kind import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString @@ -45,6 +45,7 @@ $(deriveTypeScript A.defaultOptions ''HigherKindWithUnary) $(deriveJSON A.defaultOptions ''HigherKindWithUnary) +tests :: SpecWith () tests = describe "Higher kinds" $ do describe "Kind * -> *" $ do it [i|makes the declaration and types correctly|] $ do @@ -99,9 +100,10 @@ tests = describe "Higher kinds" $ do testTypeCheckDeclarations declarations typesAndValues +main :: IO () main = hspec tests - +main' :: IO () main' = putStrLn $ formatTSDeclarations ( (getTypeScriptDeclarations (Proxy :: Proxy (HigherKind T))) <> (getTypeScriptDeclarations (Proxy :: Proxy (DoubleHigherKind T1 T2))) <> diff --git a/test/Live.hs b/test/Live.hs index 5aceb2e..56ffc4a 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -13,25 +13,21 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilyDependencies #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} module Live where import Data.Aeson as A -import Data.Aeson.TH as A import Data.Aeson.TypeScript.Recursive import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types -import Data.Kind import Data.Function -import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString import qualified Data.Text as T import Data.Time import Database.Beam -import Language.Haskell.TH import Prelude hiding (Double) -import Database.Beam instance TypeScript UTCTime where @@ -67,6 +63,7 @@ data UserT env f = User { $(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) +main :: IO () main = getTypeScriptDeclarationsRecursively (Proxy :: Proxy (UserT T Identity)) & formatTSDeclarations & putStrLn diff --git a/test/Live2.hs b/test/Live2.hs index 5873d82..9033e7f 100644 --- a/test/Live2.hs +++ b/test/Live2.hs @@ -17,21 +17,7 @@ module Live2 where import Data.Aeson as A -import Data.Aeson.TH as A -import Data.Aeson.TypeScript.Recursive import Data.Aeson.TypeScript.TH -import Data.Aeson.TypeScript.Types -import Data.Kind -import Data.Function -import Data.Monoid -import Data.Proxy -import Data.String.Interpolate.IsString -import qualified Data.Text as T -import Data.Time -import Database.Beam -import Language.Haskell.TH -import Prelude hiding (Double) -import Database.Beam data TestT a = TestT { diff --git a/test/LiveLogging.hs b/test/LiveLogging.hs index 90c3994..44f9ae5 100644 --- a/test/LiveLogging.hs +++ b/test/LiveLogging.hs @@ -13,21 +13,8 @@ module LiveLogging where -import Data.Aeson as A -import Data.Aeson.TH as A -import Data.Aeson.TypeScript.TH -import Data.Aeson.TypeScript.Types import Data.Kind -import Data.Monoid -import Data.Proxy -import Data.String.Interpolate.IsString -import qualified Data.Text as T -import Data.Time -import Database.Beam -import Language.Haskell.TH hiding (Type) import Prelude hiding (Double) -import Database.Beam - data LoggingSource = SGeneral diff --git a/test/NoOmitNothingFields.hs b/test/NoOmitNothingFields.hs index bfa55de..fbd5636 100644 --- a/test/NoOmitNothingFields.hs +++ b/test/NoOmitNothingFields.hs @@ -8,8 +8,10 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} -module NoOmitNothingFields (tests) where +module NoOmitNothingFields (main, tests) where import Data.Aeson as A import Data.Aeson.TypeScript.TH @@ -20,6 +22,7 @@ import TestBoilerplate $(testDeclarations "NoOmitNothingFields" (A.defaultOptions {omitNothingFields=False})) +main :: IO () main = hspec $ describe "NoOmitNothingFields" $ do it "encodes as expected" $ do let decls = getTypeScriptDeclarations (Proxy :: Proxy Optional) diff --git a/test/ObjectWithSingleFieldNoTagSingleConstructors.hs b/test/ObjectWithSingleFieldNoTagSingleConstructors.hs index 4dcd75d..310b5d0 100644 --- a/test/ObjectWithSingleFieldNoTagSingleConstructors.hs +++ b/test/ObjectWithSingleFieldNoTagSingleConstructors.hs @@ -8,15 +8,16 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} -module ObjectWithSingleFieldNoTagSingleConstructors (tests) where +module ObjectWithSingleFieldNoTagSingleConstructors (main, tests) where import Data.Aeson as A -import Data.Aeson.TH as A import Test.Hspec import TestBoilerplate -import Util $(testDeclarations "ObjectWithSingleField with tagSingleConstructors=False" (A.defaultOptions {sumEncoding=ObjectWithSingleField})) +main :: IO () main = hspec tests diff --git a/test/ObjectWithSingleFieldTagSingleConstructors.hs b/test/ObjectWithSingleFieldTagSingleConstructors.hs index 027d92d..7251aac 100644 --- a/test/ObjectWithSingleFieldTagSingleConstructors.hs +++ b/test/ObjectWithSingleFieldTagSingleConstructors.hs @@ -8,15 +8,17 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} -module ObjectWithSingleFieldTagSingleConstructors (tests) where +module ObjectWithSingleFieldTagSingleConstructors (main, tests) where import Data.Aeson as A -import Data.Aeson.TH as A import Test.Hspec import TestBoilerplate import Util $(testDeclarations "ObjectWithSingleField with tagSingleConstructors=True" (setTagSingleConstructors $ A.defaultOptions {sumEncoding=ObjectWithSingleField})) +main :: IO () main = hspec tests diff --git a/test/OmitNothingFields.hs b/test/OmitNothingFields.hs index 77b53d5..39886ea 100644 --- a/test/OmitNothingFields.hs +++ b/test/OmitNothingFields.hs @@ -8,8 +8,10 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} -module OmitNothingFields (tests) where +module OmitNothingFields (main, tests) where import Data.Aeson as A import Data.Aeson.TypeScript.TH @@ -20,6 +22,7 @@ import TestBoilerplate $(testDeclarations "OmitNothingFields" (A.defaultOptions {omitNothingFields=True})) +main :: IO () main = hspec $ describe "OmitNothingFields" $ do it "encodes as expected" $ do let decls = getTypeScriptDeclarations (Proxy :: Proxy Optional) diff --git a/test/Spec.hs b/test/Spec.hs index 4ffa194..c11e373 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -15,6 +15,8 @@ import qualified UntaggedTagSingleConstructors import qualified OmitNothingFields import qualified NoOmitNothingFields + +main :: IO () main = hspec $ do ObjectWithSingleFieldTagSingleConstructors.tests ObjectWithSingleFieldNoTagSingleConstructors.tests diff --git a/test/TaggedObjectNoTagSingleConstructors.hs b/test/TaggedObjectNoTagSingleConstructors.hs index a6f5ada..8a942a5 100644 --- a/test/TaggedObjectNoTagSingleConstructors.hs +++ b/test/TaggedObjectNoTagSingleConstructors.hs @@ -8,15 +8,16 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} -module TaggedObjectNoTagSingleConstructors (tests) where +module TaggedObjectNoTagSingleConstructors (main, tests) where import Data.Aeson as A -import Data.Aeson.TH as A import Test.Hspec import TestBoilerplate -import Util $(testDeclarations "TaggedObject with tagSingleConstructors=False" A.defaultOptions) +main :: IO () main = hspec tests diff --git a/test/TaggedObjectTagSingleConstructors.hs b/test/TaggedObjectTagSingleConstructors.hs index a0d9b1c..f86621d 100644 --- a/test/TaggedObjectTagSingleConstructors.hs +++ b/test/TaggedObjectTagSingleConstructors.hs @@ -8,15 +8,17 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} -module TaggedObjectTagSingleConstructors (tests) where +module TaggedObjectTagSingleConstructors (main, tests) where import Data.Aeson as A -import Data.Aeson.TH as A import Test.Hspec import TestBoilerplate import Util $(testDeclarations "TaggedObject with tagSingleConstructors=True" (setTagSingleConstructors A.defaultOptions)) +main :: IO () main = hspec tests diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index 3b24e09..bc7dd24 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -79,7 +79,8 @@ testDeclarations testName aesonOptions = do <> getTypeScriptDeclarations (Proxy :: Proxy Optional) |] - tests <- [d|tests = describe $(return $ LitE $ StringL testName) $ it "type checks everything with tsc" $ testTypeCheckDeclarations $(return declarations) $(return typesAndValues)|] + tests <- [d|tests :: SpecWith () + tests = describe $(return $ LitE $ StringL testName) $ it "type checks everything with tsc" $ testTypeCheckDeclarations $(return declarations) $(return typesAndValues)|] return $ decls ++ tests diff --git a/test/TwoElemArrayNoTagSingleConstructors.hs b/test/TwoElemArrayNoTagSingleConstructors.hs index 34381b4..f25b3b8 100644 --- a/test/TwoElemArrayNoTagSingleConstructors.hs +++ b/test/TwoElemArrayNoTagSingleConstructors.hs @@ -8,15 +8,16 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} -module TwoElemArrayNoTagSingleConstructors (tests) where +module TwoElemArrayNoTagSingleConstructors (main, tests) where import Data.Aeson as A -import Data.Aeson.TH as A import Test.Hspec import TestBoilerplate -import Util $(testDeclarations "TwoElemArray with tagSingleConstructors=False" (A.defaultOptions {sumEncoding=TwoElemArray})) +main :: IO () main = hspec tests diff --git a/test/TwoElemArrayTagSingleConstructors.hs b/test/TwoElemArrayTagSingleConstructors.hs index 7f03dd8..6b0d4b7 100644 --- a/test/TwoElemArrayTagSingleConstructors.hs +++ b/test/TwoElemArrayTagSingleConstructors.hs @@ -8,15 +8,17 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} -module TwoElemArrayTagSingleConstructors (tests) where +module TwoElemArrayTagSingleConstructors (main, tests) where import Data.Aeson as A -import Data.Aeson.TH as A import Test.Hspec import TestBoilerplate import Util $(testDeclarations "TwoElemArray with tagSingleConstructors=True" (setTagSingleConstructors $ A.defaultOptions {sumEncoding=TwoElemArray})) +main :: IO () main = hspec tests diff --git a/test/UntaggedNoTagSingleConstructors.hs b/test/UntaggedNoTagSingleConstructors.hs index 3c8acd2..8ca4738 100644 --- a/test/UntaggedNoTagSingleConstructors.hs +++ b/test/UntaggedNoTagSingleConstructors.hs @@ -8,14 +8,14 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} -module UntaggedNoTagSingleConstructors (tests) where +module UntaggedNoTagSingleConstructors (main, tests) where import Data.Aeson as A -import Data.Aeson.TH as A import Test.Hspec import TestBoilerplate -import Util -- Between Aeson 0.11.3.0 and 1.0.0.0, UntaggedValue was added -- Disable these tests if it's not present @@ -25,4 +25,5 @@ $(testDeclarations "UntaggedNoTagSingleConstructors" (A.defaultOptions {sumEncod tests = describe "UntaggedNoTagSingleConstructors" $ it "tests are disabled for this Aeson version" $ 2 `shouldBe` 2 #endif +main :: IO () main = hspec tests diff --git a/test/UntaggedTagSingleConstructors.hs b/test/UntaggedTagSingleConstructors.hs index 7eec0c8..5970c5e 100644 --- a/test/UntaggedTagSingleConstructors.hs +++ b/test/UntaggedTagSingleConstructors.hs @@ -8,11 +8,12 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} -module UntaggedTagSingleConstructors (tests) where +module UntaggedTagSingleConstructors (main, tests) where import Data.Aeson as A -import Data.Aeson.TH as A import Test.Hspec import TestBoilerplate import Util @@ -22,7 +23,9 @@ import Util #if MIN_VERSION_aeson(1,0,0) $(testDeclarations "UntaggedTagSingleConstructors" (setTagSingleConstructors $ A.defaultOptions {sumEncoding=UntaggedValue})) #else +tests :: SpecWith () tests = describe "UntaggedTagSingleConstructors" $ it "tests are disabled for this Aeson version" $ 2 `shouldBe` 2 #endif +main :: IO () main = hspec tests diff --git a/test/Util.hs b/test/Util.hs index 46cd051..17f171c 100644 --- a/test/Util.hs +++ b/test/Util.hs @@ -1,10 +1,9 @@ -{-# LANGUAGE CPP, QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns #-} +{-# LANGUAGE CPP, QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, LambdaCase #-} module Util where import Control.Monad import Data.Aeson as A -import Data.Aeson.TH as A import Data.Aeson.TypeScript.TH import qualified Data.ByteString.Lazy as B import Data.Proxy @@ -17,6 +16,7 @@ import System.FilePath import System.IO.Temp import System.Process +npmInstallScript, yarnInstallScript, localTSC :: String npmInstallScript = "test/assets/npm_install.sh" yarnInstallScript = "test/assets/yarn_install.sh" localTSC = "test/assets/node_modules/.bin/tsc" @@ -25,14 +25,12 @@ isCI :: IO Bool isCI = lookupEnv "CI" >>= (return . (== (Just "true"))) getTSC :: IO FilePath -getTSC = do - isCI <- isCI - case isCI of - True -> do - return "tsc" -- Assume it's set up on the path - False -> do - ensureTSCExists - return localTSC +getTSC = isCI >>= \case + True -> do + return "tsc" -- Assume it's set up on the path + False -> do + ensureTSCExists + return localTSC testTypeCheck :: forall a. (TypeScript a, ToJSON a) => a -> IO () testTypeCheck obj = withSystemTempDirectory "typescript_test" $ \folder -> do @@ -46,19 +44,20 @@ let x: #{tsType} = #{A.encode obj}; -- "--diagnostics", "--listFiles" tsc <- getTSC - readProcess tsc ["--noEmit", "--skipLibCheck", "--traceResolution", "--noResolve", tsFile] "" + void $ readProcess tsc ["--noEmit", "--skipLibCheck", "--traceResolution", "--noResolve", tsFile] "" return () where tsDeclarations :: [TSDeclaration] = getTypeScriptDeclarations (Proxy :: Proxy a) tsType :: String = getTypeScriptType (Proxy :: Proxy a) +getTSFile :: [TSDeclaration] -> [(String, B.ByteString)] -> String getTSFile tsDeclarations typesAndVals = [i| #{formatTSDeclarations tsDeclarations} #{T.unlines typeLines} |] - where typeLines = [[i|let x#{index}: #{typ} = #{val};|] | (index, (typ, val)) <- zip [1..] typesAndVals] + where typeLines = [[i|let x#{index}: #{typ} = #{val};|] | (index, (typ, val)) <- zip [(1 :: Int)..] typesAndVals] testTypeCheckDeclarations :: [TSDeclaration] -> [(String, B.ByteString)] -> IO () @@ -70,7 +69,7 @@ testTypeCheckDeclarations tsDeclarations typesAndVals = withSystemTempDirectory writeFile tsFile contents tsc <- getTSC - (code, output, err) <- readProcessWithExitCode tsc ["--strict", "--noEmit", "--skipLibCheck", "--traceResolution", "--noResolve", tsFile] "" + (code, output, _err) <- readProcessWithExitCode tsc ["--strict", "--noEmit", "--skipLibCheck", "--traceResolution", "--noResolve", tsFile] "" when (code /= ExitSuccess) $ do error [i|TSC check failed: #{output}. File contents were\n\n#{contents}|] From 02302ded26aa5a7d5b79e50809708ea50cf4b854 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 18:26:57 -0800 Subject: [PATCH 040/208] Remove beam dependencies that were added for testing --- aeson-typescript.cabal | 6 +----- package.yaml | 3 --- stack.yaml | 6 ------ stack.yaml.lock | 15 +-------------- test/Live.hs | 24 +++++++++++++++--------- test/Live2.hs | 19 ++++++++----------- 6 files changed, 25 insertions(+), 48 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 706a87e..0de521a 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -4,7 +4,7 @@ cabal-version: 1.12 -- -- see: https://github.com/sol/hpack -- --- hash: b3411bc5359cf53933561d5ecc5dda2e3ab245d676bc521f8f74fa5970700895 +-- hash: 85d96fd14a5fe287a9a28190585d9c9d6318007cb79b131fbf2965951b54ae8b name: aeson-typescript version: 0.2.0.0 @@ -48,14 +48,12 @@ library build-depends: aeson , base >=4.7 && <5 - , beam-core , containers , interpolate , mtl , template-haskell , text , th-abstraction <0.4 - , time , unordered-containers default-language: Haskell2010 @@ -95,7 +93,6 @@ test-suite aeson-typescript-test aeson , aeson-typescript , base >=4.7 && <5 - , beam-core , bytestring , containers , directory @@ -108,6 +105,5 @@ test-suite aeson-typescript-test , temporary , text , th-abstraction <0.4 - , time , unordered-containers default-language: Haskell2010 diff --git a/package.yaml b/package.yaml index e9b9e1e..89a2730 100644 --- a/package.yaml +++ b/package.yaml @@ -35,9 +35,6 @@ dependencies: - text - th-abstraction < 0.4 - unordered-containers -- beam-core -- time - library: source-dirs: src diff --git a/stack.yaml b/stack.yaml index fc099ee..11224c2 100644 --- a/stack.yaml +++ b/stack.yaml @@ -6,9 +6,3 @@ packages: # ghc-options: # "$locals": -fwrite-ide-info - -extra-deps: -- git: https://github.com/thomasjm/beam.git - commit: d4564da8625961b32a3aafacb358c00ebd3e5370 - subdirs: - - beam-core diff --git a/stack.yaml.lock b/stack.yaml.lock index 79bd9f9..201ec49 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -3,20 +3,7 @@ # For more information, please see the documentation at: # https://docs.haskellstack.org/en/stable/lock_files -packages: -- completed: - subdir: beam-core - name: beam-core - version: 0.9.0.0 - git: https://github.com/thomasjm/beam.git - pantry-tree: - size: 2704 - sha256: e54dabae556fcac9079587adcce1b2bb107c196688c476dabdd84ca86ad556af - commit: d4564da8625961b32a3aafacb358c00ebd3e5370 - original: - subdir: beam-core - git: https://github.com/thomasjm/beam.git - commit: d4564da8625961b32a3aafacb358c00ebd3e5370 +packages: [] snapshots: - completed: size: 532380 diff --git a/test/Live.hs b/test/Live.hs index 56ffc4a..d224793 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -22,19 +22,15 @@ import Data.Aeson.TypeScript.Recursive import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types import Data.Function +import Data.Functor.Identity +import Data.Kind import Data.Proxy import Data.String.Interpolate.IsString import qualified Data.Text as T -import Data.Time -import Database.Beam import Prelude hiding (Double) -instance TypeScript UTCTime where - getTypeScriptType _ = "DateTime" - -instance TypeScript Identity where - getTypeScriptType _ = "any" +instance TypeScript Identity where getTypeScriptType _ = "any" data SingleDE = SingleDE instance TypeScript SingleDE where getTypeScriptType _ = [i|"single"|] @@ -50,14 +46,24 @@ data K8SEnvironment = K8SEnvironment deriving (Eq, Show) instance TypeScript K8SEnvironment where getTypeScriptType _ = [i|"k8s_env"|] +data Nullable (c :: Type -> Type) x +data Exposed x +type family Columnar (f :: Type -> Type) x where + Columnar Exposed x = Exposed x + Columnar Identity x = x + Columnar (Nullable c) x = Columnar c (Maybe x) + Columnar f x = f x + type family DeployEnvironment env = result | result -> env where DeployEnvironment SingleNodeEnvironment = SingleDE DeployEnvironment K8SEnvironment = K8SDE DeployEnvironment T = () - + +-- * The main type + data UserT env f = User { _userUsername :: Columnar f T.Text - , _userCreatedAt :: Columnar f UTCTime + , _userCreatedAt :: Columnar f Int , _userDeployEnvironment :: Columnar f (DeployEnvironment env) } diff --git a/test/Live2.hs b/test/Live2.hs index 9033e7f..e8f4741 100644 --- a/test/Live2.hs +++ b/test/Live2.hs @@ -1,23 +1,19 @@ -{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilyDependencies #-} module Live2 where import Data.Aeson as A import Data.Aeson.TypeScript.TH +import Data.Function +import Data.Proxy data TestT a = TestT { @@ -26,6 +22,7 @@ data TestT a = TestT { } $(deriveTypeScript A.defaultOptions ''TestT) -data HigherKind a = HigherKind { higherKindList :: [a] } -$(deriveTypeScript A.defaultOptions ''HigherKind) - +main :: IO () +main = getTypeScriptDeclarations (Proxy :: Proxy (TestT Int)) + & formatTSDeclarations + & putStrLn From 2319778208ef34abf5a3ae1dd57a9110bf45be16 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 19:55:03 -0800 Subject: [PATCH 041/208] Don't add constraints for constructor types that don't have free type variables --- src/Data/Aeson/TypeScript/TH.hs | 15 ++++++++++++++- test/Live2.hs | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index e63818f..3c542b8 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -176,7 +176,8 @@ deriveTypeScript' options name extraOptions = do -- Build constraints: a TypeScript constraint for every constructor type and one for every type variable. -- Probably overkill/not exactly right, but it's a start. - let constructorPreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- mconcat $ fmap constructorFields datatypeCons] + let constructorPreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- mconcat $ fmap constructorFields datatypeCons + , hasFreeTypeVariable x] let typeVariablePreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- getDataTypeVars datatypeInfo] let eligibleGenericVars = catMaybes $ flip fmap (getDataTypeVars datatypeInfo) $ \case @@ -197,6 +198,7 @@ deriveTypeScript' options name extraOptions = do let extraTopLevelDecls = mconcat [x | ExtraTopLevelDecs x <- extraDeclsOrGenericInfos] let predicates = constructorPreds <> typeVariablePreds <> [x | ExtraConstraint x <- extraDeclsOrGenericInfos] let constraints = foldl AppT (TupleT (length predicates)) predicates + -- let constraints = predicates declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] @@ -330,6 +332,17 @@ searchForConstraints eo (ParensT typ) var = searchForConstraints eo typ var searchForConstraints eo (ImplicitParamT _ typ) var = searchForConstraints eo typ var searchForConstraints _ _ _ = return () +hasFreeTypeVariable :: Type -> Bool +hasFreeTypeVariable (VarT _) = True +hasFreeTypeVariable (AppT typ1 typ2) = hasFreeTypeVariable typ1 || hasFreeTypeVariable typ2 +hasFreeTypeVariable (AppKindT typ _) = hasFreeTypeVariable typ +hasFreeTypeVariable (SigT typ _) = hasFreeTypeVariable typ +hasFreeTypeVariable (InfixT typ1 _ typ2) = hasFreeTypeVariable typ1 || hasFreeTypeVariable typ2 +hasFreeTypeVariable (UInfixT typ1 _ typ2) = hasFreeTypeVariable typ1 || hasFreeTypeVariable typ2 +hasFreeTypeVariable (ParensT typ) = hasFreeTypeVariable typ +hasFreeTypeVariable (ImplicitParamT _ typ) = hasFreeTypeVariable typ +hasFreeTypeVariable _ = False + unifyGenericVariable :: [GenericInfo] -> String unifyGenericVariable genericInfos = case [nameBase name | GenericInfo _ (TypeFamilyKey name) <- genericInfos] of [] -> "" diff --git a/test/Live2.hs b/test/Live2.hs index e8f4741..7f1a82e 100644 --- a/test/Live2.hs +++ b/test/Live2.hs @@ -23,6 +23,6 @@ data TestT a = TestT { $(deriveTypeScript A.defaultOptions ''TestT) main :: IO () -main = getTypeScriptDeclarations (Proxy :: Proxy (TestT Int)) +main = getTypeScriptDeclarations (Proxy :: Proxy (TestT T)) & formatTSDeclarations & putStrLn From 7279dac788662deb19fd8c0d9ab5a9bd61a33a79 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 20:23:02 -0800 Subject: [PATCH 042/208] Remove unnecessary dollar signs --- src/Data/Aeson/TypeScript/TH.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 3c542b8..3a38839 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -218,7 +218,7 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@( if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> do writeSingleConstructorEncoding brackets <- lift $ getBracketsExpression False genericVariables - lift $ [|$(TH.stringE interfaceName) <> $(return brackets)|] + lift [|$(TH.stringE interfaceName) <> $(return brackets)|] | allConstructorsAreNullary datatypeCons && allNullaryToStringTag options -> stringEncoding -- With UntaggedValue, nullary constructors are encoded as strings @@ -228,15 +228,15 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@( | isObjectWithSingleField $ sumEncoding options -> do writeSingleConstructorEncoding brackets <- lift $ getBracketsExpression False genericVariables - lift $ [|"{" <> $(TH.stringE $ show $ constructorNameToUse options ci) <> ": " <> $(TH.stringE interfaceName) <> $(return brackets) <> "}"|] + lift [|"{" <> $(TH.stringE $ show $ constructorNameToUse options ci) <> ": " <> $(TH.stringE interfaceName) <> $(return brackets) <> "}"|] | isTwoElemArray $ sumEncoding options -> do writeSingleConstructorEncoding brackets <- lift $ getBracketsExpression False genericVariables - lift $ [|"[" <> $(TH.stringE $ show $ constructorNameToUse options ci) <> ", " <> $(TH.stringE interfaceName) <> $(return brackets) <> "]"|] + lift [|"[" <> $(TH.stringE $ show $ constructorNameToUse options ci) <> ", " <> $(TH.stringE interfaceName) <> $(return brackets) <> "]"|] | isUntaggedValue $ sumEncoding options -> do writeSingleConstructorEncoding brackets <- lift $ getBracketsExpression False genericVariables - lift $ [|$(TH.stringE interfaceName) <> $(return brackets)|] + lift [|$(TH.stringE interfaceName) <> $(return brackets)|] | otherwise -> do tagField :: [Exp] <- lift $ case sumEncoding options of TaggedObject tagFieldName _ -> (: []) <$> [|TSField False $(TH.stringE tagFieldName) $(TH.stringE [i|"#{constructorNameToUse options ci}"|])|] @@ -246,7 +246,7 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@( decl <- lift $ assembleInterfaceDeclaration (ListE (tagField ++ tsFields)) tell [ExtraDecl decl] brackets <- lift $ getBracketsExpression False genericVariables - lift $ [|$(TH.stringE interfaceName) <> $(return brackets)|] + lift [|$(TH.stringE interfaceName) <> $(return brackets)|] where stringEncoding = lift $ TH.stringE [i|"#{(constructorTagModifier options) $ getTypeName (constructorName ci)}"|] From edf92498f42b84fe3b8fbe778490674bbc1cac2e Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 20:26:44 -0800 Subject: [PATCH 043/208] Fix illegal tuple constraint issue hopefully --- src/Data/Aeson/TypeScript/TH.hs | 19 +++++++++++-------- src/Data/Aeson/TypeScript/Util.hs | 8 ++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 3a38839..2aab8fb 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -197,19 +197,22 @@ deriveTypeScript' options name extraOptions = do let extraDecls = [x | ExtraDecl x <- extraDeclsOrGenericInfos] let extraTopLevelDecls = mconcat [x | ExtraTopLevelDecs x <- extraDeclsOrGenericInfos] let predicates = constructorPreds <> typeVariablePreds <> [x | ExtraConstraint x <- extraDeclsOrGenericInfos] - let constraints = foldl AppT (TupleT (length predicates)) predicates - -- let constraints = predicates declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] let extraParentTypes = [x | ExtraParentType x <- extraDeclsOrGenericInfos] - inst <- [d|instance $(return constraints) => TypeScript $(return $ foldl AppT (ConT name) (getDataTypeVars datatypeInfo)) where - getTypeScriptType _ = $(TH.stringE $ getTypeName datatypeName) <> $(getBracketsExpressionAllTypesNoSuffix genericVariablesAndSuffixes) - getTypeScriptDeclarations _ = $(return declarationsFunctionBody) - getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return t))|] - | t <- (mconcat $ fmap constructorFields datatypeCons) <> extraParentTypes]) - |] + -- Couldn't figure out how to put the constraints for "instance TypeScript..." in the quasiquote above without + -- introducing () when the constraints are empty, which causes "illegal tuple constraint" unless the user enables ConstraintKinds. + -- So, just use our mkInstance function + getTypeScriptTypeExp <- [|$(TH.stringE $ getTypeName datatypeName) <> $(getBracketsExpressionAllTypesNoSuffix genericVariablesAndSuffixes)|] + getParentTypesExp <- listE [ [|TSType (Proxy :: Proxy $(return t))|] + | t <- (mconcat $ fmap constructorFields datatypeCons) <> extraParentTypes] + let inst = [mkInstance predicates (AppT (ConT ''TypeScript) (foldl AppT (ConT name) (getDataTypeVars datatypeInfo))) [ + FunD 'getTypeScriptType [Clause [WildP] (NormalB getTypeScriptTypeExp) []] + , FunD 'getTypeScriptDeclarations [Clause [WildP] (NormalB declarationsFunctionBody) []] + , FunD 'getParentTypes [Clause [WildP] (NormalB getParentTypesExp) []] + ]] return (extraTopLevelDecls <> inst) -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index 58bd96a..47f5e1f 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -124,6 +124,14 @@ isUntaggedValue UntaggedValue = True #endif isUntaggedValue _ = False +-- Between Template Haskell 2.10 and 2.11, InstanceD got an additional argument +mkInstance :: Cxt -> Type -> [Dec] -> Dec +#if MIN_VERSION_template_haskell(2,11,0) +mkInstance context typ decs = InstanceD Nothing context typ decs +#else +mkInstance context typ decs = InstanceD context typ decs +#endif + namesAndTypes :: Options -> ConstructorInfo -> [(String, Type)] namesAndTypes options ci = case constructorVariant ci of RecordConstructor names -> zip (fmap ((fieldLabelModifier options) . lastNameComponent') names) (constructorFields ci) From 06c9d8a7629812eff536762fb6ac312cfa195d59 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 21:17:01 -0800 Subject: [PATCH 044/208] Export ExtraTypeScriptOptions --- src/Data/Aeson/TypeScript/TH.hs | 37 ++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 2aab8fb..16e5dac 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -111,32 +111,35 @@ main = putStrLn $ 'formatTSDeclarations' ( -} module Data.Aeson.TypeScript.TH ( - deriveTypeScript, - deriveTypeScript', - deriveTypeScriptLookupType, + deriveTypeScript + , deriveTypeScript' + , deriveTypeScriptLookupType -- * The main typeclass - TypeScript(..), - TSType(..), + , TypeScript(..) + , TSType(..) - TSDeclaration(TSRawDeclaration), + , TSDeclaration(TSRawDeclaration) -- * Formatting declarations - formatTSDeclarations, - formatTSDeclarations', - formatTSDeclaration, - FormattingOptions(..), + , formatTSDeclarations + , formatTSDeclarations' + , formatTSDeclaration + , FormattingOptions(..) + -- * Advanced options + , ExtraTypeScriptOptions + -- * Convenience tools - HasJSONOptions(..), - deriveJSONAndTypeScript, + , HasJSONOptions(..) + , deriveJSONAndTypeScript - T(..), - T1(..), - T2(..), - T3(..), + , T(..) + , T1(..) + , T2(..) + , T3(..) - module Data.Aeson.TypeScript.Instances + , module Data.Aeson.TypeScript.Instances ) where import Control.Monad From 52fb71148579a95db2075427e6f2e281dee02f7f Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 21:27:57 -0800 Subject: [PATCH 045/208] Actually export ExtraTypeScriptOptions --- src/Data/Aeson/TypeScript/TH.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 16e5dac..5f4d55d 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -128,7 +128,7 @@ module Data.Aeson.TypeScript.TH ( , FormattingOptions(..) -- * Advanced options - , ExtraTypeScriptOptions + , ExtraTypeScriptOptions(..) -- * Convenience tools , HasJSONOptions(..) From 0e9273d7e67dc9651795f81fe9d01e460eb633ae Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 21:44:31 -0800 Subject: [PATCH 046/208] Add parent types to type family things --- src/Data/Aeson/TypeScript/Lookup.hs | 6 ++++++ src/Data/Aeson/TypeScript/TH.hs | 2 ++ test/Live.hs | 1 - 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/Lookup.hs b/src/Data/Aeson/TypeScript/Lookup.hs index 046a670..de4149c 100644 --- a/src/Data/Aeson/TypeScript/Lookup.hs +++ b/src/Data/Aeson/TypeScript/Lookup.hs @@ -47,3 +47,9 @@ getClosedTypeFamilyInterfaceDecl name eqns = do x -> fail [i|Don't know how to handle type family equation: '#{x}'|] [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] $(listE $ fmap return fields) |] + +getClosedTypeFamilyImage :: [TySynEqn] -> Q [Type] +getClosedTypeFamilyImage eqns = do + forM eqns $ \case + TySynEqn Nothing (AppT (ConT _) (ConT _)) result -> return result + x -> fail [i|Don't know how to handle type family equation: '#{x}'|] diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 5f4d55d..d574c19 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -300,9 +300,11 @@ transformTypeFamilies eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) let inst1 = DataD [] name' [PlainTV f] Nothing [] [] tell [ExtraTopLevelDecs [inst1]] + imageTypes <- lift $ getClosedTypeFamilyImage eqns inst2 <- lift $ [d|instance (Typeable g, TypeScript g) => TypeScript ($(conT name') g) where getTypeScriptType _ = $(TH.stringE $ nameBase name) <> "[" <> (getTypeScriptType (Proxy :: Proxy g)) <> "]" getTypeScriptDeclarations _ = [$(getClosedTypeFamilyInterfaceDecl name eqns)] + getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return x))|] | x <- imageTypes]) |] tell [ExtraTopLevelDecs inst2] diff --git a/test/Live.hs b/test/Live.hs index d224793..5727081 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -20,7 +20,6 @@ module Live where import Data.Aeson as A import Data.Aeson.TypeScript.Recursive import Data.Aeson.TypeScript.TH -import Data.Aeson.TypeScript.Types import Data.Function import Data.Functor.Identity import Data.Kind From 54ffe069bd57605f01053d5e1a216e5c641664fd Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 21:44:46 -0800 Subject: [PATCH 047/208] Improve error messages when type family equation not recognized --- src/Data/Aeson/TypeScript/Lookup.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Lookup.hs b/src/Data/Aeson/TypeScript/Lookup.hs index de4149c..acc0c17 100644 --- a/src/Data/Aeson/TypeScript/Lookup.hs +++ b/src/Data/Aeson/TypeScript/Lookup.hs @@ -44,7 +44,7 @@ getClosedTypeFamilyInterfaceDecl name eqns = do fields <- forM eqns $ \case TySynEqn Nothing (AppT (ConT _) (ConT arg)) result -> do [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) |] - x -> fail [i|Don't know how to handle type family equation: '#{x}'|] + x -> fail [i|aeson-typescript doesn't know yet how to handle this type family equation: '#{x}'|] [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] $(listE $ fmap return fields) |] @@ -52,4 +52,4 @@ getClosedTypeFamilyImage :: [TySynEqn] -> Q [Type] getClosedTypeFamilyImage eqns = do forM eqns $ \case TySynEqn Nothing (AppT (ConT _) (ConT _)) result -> return result - x -> fail [i|Don't know how to handle type family equation: '#{x}'|] + x -> fail [i|aeson-typescript doesn't know yet how to handle this type family equation: '#{x}'|] From 8da9b5d4e2770daa4099c621fcc3745605927223 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 18 Jan 2021 23:08:59 -0800 Subject: [PATCH 048/208] Add some test of type families --- aeson-typescript.cabal | 3 +- src/Data/Aeson/TypeScript/TH.hs | 10 ++-- src/Data/Aeson/TypeScript/Util.hs | 14 ++--- test/Live.hs | 1 - test/Spec.hs | 5 +- test/TestBoilerplate.hs | 33 ++++++++++- test/TypeFamilies.hs | 91 +++++++++++++++++++++++++++++++ 7 files changed, 141 insertions(+), 16 deletions(-) create mode 100644 test/TypeFamilies.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 0de521a..74b30ad 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -4,7 +4,7 @@ cabal-version: 1.12 -- -- see: https://github.com/sol/hpack -- --- hash: 85d96fd14a5fe287a9a28190585d9c9d6318007cb79b131fbf2965951b54ae8b +-- hash: 948862e20e0fbcabe4b159708168aacdb2987aa650871b8c181025d99e7370d4 name: aeson-typescript version: 0.2.0.0 @@ -74,6 +74,7 @@ test-suite aeson-typescript-test TestBoilerplate TwoElemArrayNoTagSingleConstructors TwoElemArrayTagSingleConstructors + TypeFamilies UntaggedNoTagSingleConstructors UntaggedTagSingleConstructors Util diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index d574c19..0758c8e 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -259,7 +259,7 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@( writeSingleConstructorEncoding = if | constructorVariant ci == NormalConstructor -> do - encoding <- lift tupleEncoding + encoding <- tupleEncoding tell [ExtraDecl encoding] | otherwise -> do tsFields <- getTSFields @@ -269,9 +269,11 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@( -- * Type declaration to use interfaceName = "I" <> (lastNameComponent' $ constructorName ci) - tupleEncoding = [|TSTypeAlternatives $(TH.stringE interfaceName) - $(genericVariablesListExpr True genericVariables) - [getTypeScriptType (Proxy :: Proxy $(return $ contentsTupleType ci))]|] + tupleEncoding = do + tupleType <- transformTypeFamilies extraOptions (contentsTupleType ci) + lift $ [|TSTypeAlternatives $(TH.stringE interfaceName) + $(genericVariablesListExpr True genericVariables) + [getTypeScriptType (Proxy :: Proxy $(return tupleType))]|] assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) $(genericVariablesListExpr True genericVariables) diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index 47f5e1f..e21e093 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -68,13 +68,6 @@ getTypeAsStringExp typ = [|getTypeScriptType (Proxy :: Proxy $(return typ))|] getOptionalAsBoolExp :: Type -> Q Exp getOptionalAsBoolExp typ = [|getTypeScriptOptional (Proxy :: Proxy $(return typ))|] --- | Get the type of a tuple of constructor fields, as when we're packing a record-less constructor into a list -getTupleType :: [Type] -> Type -getTupleType constructorFields = case length constructorFields of - 0 -> AppT ListT (ConT ''()) - 1 -> head constructorFields - x -> applyToArgsT (ConT $ tupleTypeName x) constructorFields - -- | Helper to apply a type constructor to a list of type args applyToArgsT :: Type -> [Type] -> Type applyToArgsT constructor [] = constructor @@ -144,8 +137,13 @@ namesAndTypes options ci = case constructorVariant ci of constructorNameToUse :: Options -> ConstructorInfo -> String constructorNameToUse options ci = (constructorTagModifier options) $ lastNameComponent' (constructorName ci) +-- | Get the type of a tuple of constructor fields, as when we're packing a record-less constructor into a list contentsTupleType :: ConstructorInfo -> Type -contentsTupleType ci = getTupleType (constructorFields ci) +contentsTupleType ci = let fields = constructorFields ci in + case length fields of + 0 -> AppT ListT (ConT ''()) + 1 -> head fields + x -> applyToArgsT (ConT $ tupleTypeName x) fields getBracketsExpression :: Bool -> [(Name, String)] -> Q Exp getBracketsExpression _ [] = [|""|] diff --git a/test/Live.hs b/test/Live.hs index 5727081..978a1a1 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -11,7 +11,6 @@ {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilyDependencies #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/Spec.hs b/test/Spec.hs index c11e373..3d26c48 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -4,6 +4,7 @@ module Main where import Test.Hspec import qualified HigherKind +import qualified TypeFamilies import qualified ObjectWithSingleFieldNoTagSingleConstructors import qualified ObjectWithSingleFieldTagSingleConstructors import qualified TaggedObjectNoTagSingleConstructors @@ -18,6 +19,9 @@ import qualified NoOmitNothingFields main :: IO () main = hspec $ do + HigherKind.tests + TypeFamilies.tests + ObjectWithSingleFieldTagSingleConstructors.tests ObjectWithSingleFieldNoTagSingleConstructors.tests TaggedObjectTagSingleConstructors.tests @@ -26,6 +30,5 @@ main = hspec $ do TwoElemArrayNoTagSingleConstructors.tests UntaggedTagSingleConstructors.tests UntaggedNoTagSingleConstructors.tests - HigherKind.tests OmitNothingFields.tests NoOmitNothingFields.tests diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index bc7dd24..d5d3c90 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -8,6 +8,9 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} module TestBoilerplate where @@ -15,8 +18,11 @@ import Control.Monad.Writer.Lazy hiding (Product) import qualified Data.Aeson as A import Data.Aeson.TH as A import Data.Aeson.TypeScript.TH +import Data.Functor.Identity +import Data.Kind import Data.Proxy -import Language.Haskell.TH +import Data.String.Interpolate.IsString +import Language.Haskell.TH hiding (Type) import Test.Hspec import Util @@ -30,6 +36,31 @@ data TwoConstructor = Con1 { con1String :: String } | Con2 { con2String :: Strin data Complex a = Nullary | Unary Int | Product String Char a | Record { testOne :: Int, testTwo :: Bool, testThree :: Complex a} deriving Eq data Optional = Optional {optionalInt :: Maybe Int} +-- * For testing type families + +instance TypeScript Identity where getTypeScriptType _ = "any" + +data SingleDE = SingleDE +instance TypeScript SingleDE where getTypeScriptType _ = [i|"single"|] + +data K8SDE = K8SDE +instance TypeScript K8SDE where getTypeScriptType _ = [i|"k8s"|] + +data SingleNodeEnvironment = SingleNodeEnvironment deriving (Eq, Show) +instance TypeScript SingleNodeEnvironment where getTypeScriptType _ = [i|"single_node_env"|] + +data K8SEnvironment = K8SEnvironment deriving (Eq, Show) +instance TypeScript K8SEnvironment where getTypeScriptType _ = [i|"k8s_env"|] + +data Nullable (c :: Type -> Type) x +data Exposed x +type family Columnar (f :: Type -> Type) x where + Columnar Exposed x = Exposed x + Columnar Identity x = x + Columnar (Nullable c) x = Columnar c (Maybe x) + Columnar f x = f x + +-- * Declarations testDeclarations :: String -> A.Options -> Q [Dec] testDeclarations testName aesonOptions = do diff --git a/test/TypeFamilies.hs b/test/TypeFamilies.hs new file mode 100644 index 0000000..8847334 --- /dev/null +++ b/test/TypeFamilies.hs @@ -0,0 +1,91 @@ +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeFamilyDependencies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} + +module TypeFamilies (tests) where + +import Data.Aeson as A +import Data.Aeson.TypeScript.Recursive +import Data.Aeson.TypeScript.TH +import Data.Aeson.TypeScript.Types +import Data.Functor.Identity +import Data.Proxy +import Data.String.Interpolate.IsString +import qualified Data.Text as T +import Prelude hiding (Double) +import Test.Hspec +import TestBoilerplate + + +type family DeployEnvironment env = result | result -> env where + DeployEnvironment SingleNodeEnvironment = SingleDE + DeployEnvironment K8SEnvironment = K8SDE + DeployEnvironment T = () +data UserT env f = User { + _userUsername :: Columnar f T.Text + , _userCreatedAt :: Columnar f Int + , _userDeployEnvironment :: Columnar f (DeployEnvironment env) + } +$(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) + +type family DeployEnvironment2 env = result | result -> env where + DeployEnvironment2 SingleNodeEnvironment = SingleDE + DeployEnvironment2 K8SEnvironment = K8SDE + DeployEnvironment2 T = () +newtype Simple env = Simple (DeployEnvironment2 env) +$(deriveTypeScript' A.defaultOptions ''Simple (ExtraTypeScriptOptions [''DeployEnvironment2])) + +tests :: SpecWith () +tests = describe "Type families" $ do + describe "simple newtype" $ do + it [i|makes the declaration and types correctly|] $ do + (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Simple T))) `shouldBe` ([ + TSInterfaceDeclaration "DeployEnvironment2" [] [ + TSField False "\"single_node_env\"" "\"single\"" + , TSField False "\"k8s_env\"" "\"k8s\"" + , TSField False "T" "void" + ] + , TSTypeAlternatives "ISimple" ["T extends keyof DeployEnvironment2"] ["DeployEnvironment2[T]"] + , TSTypeAlternatives "Simple" ["T extends keyof DeployEnvironment2"] ["ISimple"] + ]) + + describe "Complicated Beam-like user type" $ do + it [i|makes the declaration and types correctly|] $ do + (getTypeScriptDeclarations (Proxy :: Proxy (UserT T Identity))) `shouldBe` ([ + TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] + , TSInterfaceDeclaration "IUser" ["T extends keyof DeployEnvironment"] [ + TSField False "_userUsername" "string" + , TSField False "_userCreatedAt" "number" + , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" + ] + ]) + + it [i|get the declarations recursively|] $ do + (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (UserT T Identity))) `shouldBe` ([ + TSInterfaceDeclaration "DeployEnvironment" [] [ + TSField False "\"single_node_env\"" "\"single\"" + , TSField False "\"k8s_env\"" "\"k8s\"" + , TSField False "T" "void" + ] + , TSInterfaceDeclaration "IUser" ["T extends keyof DeployEnvironment"] [ + TSField False "_userUsername" "string" + , TSField False "_userCreatedAt" "number" + , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" + ] + , TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] + ]) + +main :: IO () +main = hspec tests From 70bfd32f0d51d10f73b77546217fffe83cc5e92c Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 19 Jan 2021 00:01:47 -0800 Subject: [PATCH 049/208] Add deriveJSONAndTypeScript' --- src/Data/Aeson/TypeScript/TH.hs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 0758c8e..cd62541 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -133,6 +133,7 @@ module Data.Aeson.TypeScript.TH ( -- * Convenience tools , HasJSONOptions(..) , deriveJSONAndTypeScript + , deriveJSONAndTypeScript' , T(..) , T1(..) @@ -373,6 +374,14 @@ deriveJSONAndTypeScript :: Options -> Q [Dec] deriveJSONAndTypeScript options name = (<>) <$> (deriveTypeScript options name) <*> (A.deriveJSON options name) +deriveJSONAndTypeScript' :: Options + -- ^ Encoding options. + -> Name + -- ^ Name of the type for which to generate 'A.ToJSON', 'A.FromJSON', and 'TypeScript' instance declarations. + -> ExtraTypeScriptOptions + -- ^ Extra options to control advanced features. + -> Q [Dec] +deriveJSONAndTypeScript' options name extraOptions = (<>) <$> (deriveTypeScript' options name extraOptions) <*> (A.deriveJSON options name) -- | Generates a 'TypeScript' instance declaration for the given data type. deriveTypeScript :: Options From 5c1957dd7bd1818d51d4c7e4db4acefaca511bfc Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 19 Jan 2021 05:19:45 -0800 Subject: [PATCH 050/208] Always emit generics --- src/Data/Aeson/TypeScript/TH.hs | 15 ++++++++++++--- src/Data/Aeson/TypeScript/Util.hs | 17 ++++++++++------- test/Live2.hs | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index cd62541..a7b1000 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -153,6 +153,7 @@ import Data.Aeson.TypeScript.Lookup import Data.Aeson.TypeScript.Types import Data.Aeson.TypeScript.Util import qualified Data.List as L +import qualified Data.Map as M import Data.Maybe import Data.Proxy import Data.String.Interpolate.IsString @@ -174,9 +175,17 @@ deriveTypeScript' :: Options -- ^ Extra options to control advanced features. -> Q [Dec] deriveTypeScript' options name extraOptions = do - datatypeInfo@(DatatypeInfo {..}) <- reifyDatatype name - - assertExtensionsTurnedOn datatypeInfo + datatypeInfo' <- reifyDatatype name + assertExtensionsTurnedOn datatypeInfo' + + -- Plug in generic variables for all star free variables + let starVars = [name | (isStarType -> Just name) <- getDataTypeVars datatypeInfo'] + let templateVarsToUse = case length starVars of + 1 -> [ConT ''T] + _ -> take (length starVars) allStarConstructors + let subMap = M.fromList $ zip starVars templateVarsToUse + let datatypeInfo = datatypeInfo' { datatypeCons = fmap (applySubstitution subMap) (datatypeCons datatypeInfo')} + let (DatatypeInfo {..}) = datatypeInfo -- Build constraints: a TypeScript constraint for every constructor type and one for every type variable. -- Probably overkill/not exactly right, but it's a start. diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index e21e093..2182ff2 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -7,7 +7,6 @@ import Data.Aeson as A import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Types import qualified Data.List as L -import Data.Maybe import Data.Proxy import Data.String.Interpolate.IsString import qualified Data.Text as T @@ -154,9 +153,13 @@ getBracketsExpressionAllTypesNoSuffix [] = [|""|] getBracketsExpressionAllTypesNoSuffix names = [|"<" <> L.intercalate ", " $(listE [ [|(getTypeScriptType (Proxy :: Proxy $(varT x)))|] | (x, _suffix) <- names]) <> ">"|] genericVariablesListExpr :: Bool -> [(Name, String)] -> Q Exp -genericVariablesListExpr True genericVariables = [|catMaybes $(listE (fmap (\(x, suffix) -> - [|if isGenericVariable (Proxy :: Proxy $(varT x)) then Just ((getTypeScriptType (Proxy :: Proxy $(varT x))) <> $(TH.stringE suffix)) else Nothing|]) - genericVariables))|] -genericVariablesListExpr False genericVariables = [|catMaybes $(listE (fmap (\(x, _suffix) -> - [|if isGenericVariable (Proxy :: Proxy $(varT x)) then Just (getTypeScriptType (Proxy :: Proxy $(varT x))) else Nothing|]) - genericVariables))|] +genericVariablesListExpr includeSuffix genericVariables = listE (fmap (\((_, suffix), correspondingGeneric) -> + [|(getTypeScriptType (Proxy :: Proxy $(return correspondingGeneric))) <> $(TH.stringE (if includeSuffix then suffix else ""))|]) + (case genericVariables of + [x] -> [(x, ConT ''T)] + xs -> zip xs allStarConstructors) + ) + +isStarType :: Type -> Maybe Name +isStarType (SigT (VarT n) StarT) = Just n +isStarType _ = Nothing diff --git a/test/Live2.hs b/test/Live2.hs index 7f1a82e..e8f4741 100644 --- a/test/Live2.hs +++ b/test/Live2.hs @@ -23,6 +23,6 @@ data TestT a = TestT { $(deriveTypeScript A.defaultOptions ''TestT) main :: IO () -main = getTypeScriptDeclarations (Proxy :: Proxy (TestT T)) +main = getTypeScriptDeclarations (Proxy :: Proxy (TestT Int)) & formatTSDeclarations & putStrLn From d38346ba85ac7d8c5d1efc7df828a2daf06642ec Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 19 Jan 2021 05:54:35 -0800 Subject: [PATCH 051/208] Try fixing parent types expression --- src/Data/Aeson/TypeScript/TH.hs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index a7b1000..b049e32 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -184,27 +184,26 @@ deriveTypeScript' options name extraOptions = do 1 -> [ConT ''T] _ -> take (length starVars) allStarConstructors let subMap = M.fromList $ zip starVars templateVarsToUse - let datatypeInfo = datatypeInfo' { datatypeCons = fmap (applySubstitution subMap) (datatypeCons datatypeInfo')} - let (DatatypeInfo {..}) = datatypeInfo + let dti = datatypeInfo' { datatypeCons = fmap (applySubstitution subMap) (datatypeCons datatypeInfo')} -- Build constraints: a TypeScript constraint for every constructor type and one for every type variable. -- Probably overkill/not exactly right, but it's a start. - let constructorPreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- mconcat $ fmap constructorFields datatypeCons + let constructorPreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- mconcat $ fmap constructorFields (datatypeCons dti) , hasFreeTypeVariable x] - let typeVariablePreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- getDataTypeVars datatypeInfo] + let typeVariablePreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- getDataTypeVars dti] - let eligibleGenericVars = catMaybes $ flip fmap (getDataTypeVars datatypeInfo) $ \case + let eligibleGenericVars = catMaybes $ flip fmap (getDataTypeVars dti) $ \case SigT (VarT n) StarT -> Just n _ -> Nothing genericVariablesAndSuffixes <- forM eligibleGenericVars $ \var -> do - (_, genericInfos) <- runWriterT $ forM_ datatypeCons $ \ci -> + (_, genericInfos) <- runWriterT $ forM_ (datatypeCons dti) $ \ci -> forM_ (namesAndTypes options ci) $ \(_, typ) -> do searchForConstraints extraOptions typ var return (var, unifyGenericVariable genericInfos) -- Build the declarations - (types, extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options extraOptions datatypeInfo genericVariablesAndSuffixes) datatypeCons - typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName datatypeName) + (types, extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options extraOptions dti genericVariablesAndSuffixes) (datatypeCons dti) + typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName (datatypeName dti)) $(genericVariablesListExpr True genericVariablesAndSuffixes) $(listE $ fmap return types)|] let extraDecls = [x | ExtraDecl x <- extraDeclsOrGenericInfos] @@ -218,10 +217,10 @@ deriveTypeScript' options name extraOptions = do -- Couldn't figure out how to put the constraints for "instance TypeScript..." in the quasiquote above without -- introducing () when the constraints are empty, which causes "illegal tuple constraint" unless the user enables ConstraintKinds. -- So, just use our mkInstance function - getTypeScriptTypeExp <- [|$(TH.stringE $ getTypeName datatypeName) <> $(getBracketsExpressionAllTypesNoSuffix genericVariablesAndSuffixes)|] + getTypeScriptTypeExp <- [|$(TH.stringE $ getTypeName (datatypeName dti)) <> $(getBracketsExpressionAllTypesNoSuffix genericVariablesAndSuffixes)|] getParentTypesExp <- listE [ [|TSType (Proxy :: Proxy $(return t))|] - | t <- (mconcat $ fmap constructorFields datatypeCons) <> extraParentTypes] - let inst = [mkInstance predicates (AppT (ConT ''TypeScript) (foldl AppT (ConT name) (getDataTypeVars datatypeInfo))) [ + | t <- (mconcat $ fmap constructorFields (datatypeCons datatypeInfo')) <> extraParentTypes] + let inst = [mkInstance predicates (AppT (ConT ''TypeScript) (foldl AppT (ConT name) (getDataTypeVars dti))) [ FunD 'getTypeScriptType [Clause [WildP] (NormalB getTypeScriptTypeExp) []] , FunD 'getTypeScriptDeclarations [Clause [WildP] (NormalB declarationsFunctionBody) []] , FunD 'getParentTypes [Clause [WildP] (NormalB getParentTypesExp) []] From 4cc50a926c049e7d3c1a34cfee16efd98532b90a Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 19 Jan 2021 06:01:40 -0800 Subject: [PATCH 052/208] Try adding even more constructor preds --- src/Data/Aeson/TypeScript/TH.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index b049e32..ff3f48c 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -190,6 +190,8 @@ deriveTypeScript' options name extraOptions = do -- Probably overkill/not exactly right, but it's a start. let constructorPreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- mconcat $ fmap constructorFields (datatypeCons dti) , hasFreeTypeVariable x] + let constructorPreds' :: [Pred] = [AppT (ConT ''TypeScript) x | x <- mconcat $ fmap constructorFields (datatypeCons datatypeInfo') + , hasFreeTypeVariable x] let typeVariablePreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- getDataTypeVars dti] let eligibleGenericVars = catMaybes $ flip fmap (getDataTypeVars dti) $ \case @@ -208,7 +210,7 @@ deriveTypeScript' options name extraOptions = do $(listE $ fmap return types)|] let extraDecls = [x | ExtraDecl x <- extraDeclsOrGenericInfos] let extraTopLevelDecls = mconcat [x | ExtraTopLevelDecs x <- extraDeclsOrGenericInfos] - let predicates = constructorPreds <> typeVariablePreds <> [x | ExtraConstraint x <- extraDeclsOrGenericInfos] + let predicates = constructorPreds <> constructorPreds' <> typeVariablePreds <> [x | ExtraConstraint x <- extraDeclsOrGenericInfos] declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] From 6bd33043ec24b09059b3a40bbf93805de211e1fc Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 19 Jan 2021 06:17:39 -0800 Subject: [PATCH 053/208] Add WIP Basic.hs test file --- aeson-typescript.cabal | 3 ++- test/Basic.hs | 51 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 test/Basic.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 74b30ad..b5a7768 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -4,7 +4,7 @@ cabal-version: 1.12 -- -- see: https://github.com/sol/hpack -- --- hash: 948862e20e0fbcabe4b159708168aacdb2987aa650871b8c181025d99e7370d4 +-- hash: c5f3990cf145c3739268d2bf2869bd68dbb2c4272a47ae4d8d61dd16d51a37e4 name: aeson-typescript version: 0.2.0.0 @@ -61,6 +61,7 @@ test-suite aeson-typescript-test type: exitcode-stdio-1.0 main-is: Spec.hs other-modules: + Basic HigherKind Live Live2 diff --git a/test/Basic.hs b/test/Basic.hs new file mode 100644 index 0000000..560c552 --- /dev/null +++ b/test/Basic.hs @@ -0,0 +1,51 @@ +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} + +module Basic (tests) where + +import Data.Aeson as A +import Data.Aeson.TH as A +import Data.Aeson.TypeScript.TH +import Data.Aeson.TypeScript.Types +import Data.Monoid +import Data.Proxy +import Data.String.Interpolate.IsString +import Prelude hiding (Double) +import Test.Hspec +import Util + + +data Unit1 = Unit1 +$(deriveTypeScript A.defaultOptions ''Unit1) + +data Unit2 = Unit2 +$(deriveTypeScript (A.defaultOptions { A.tagSingleConstructors = True + , A.constructorTagModifier = const "foo" }) ''Unit2) + +tests :: SpecWith () +tests = describe "Basic tests" $ do + describe "tagSingleConstructors and constructorTagModifier" $ do + it [i|Works with a normal unit|] $ do + (getTypeScriptDeclarations (Proxy :: Proxy Unit1)) `shouldBe` ([ + TSTypeAlternatives "Unit1" [] ["IUnit1"] + , TSTypeAlternatives "IUnit1" [] ["void[]"] + ]) + + it [i|Works with a unit with constructorTagModifier|] $ do + (getTypeScriptDeclarations (Proxy :: Proxy Unit2)) `shouldBe` ([]) + + +main :: IO () +main = hspec tests From 17908ced8da1fc7ca87df619c6b3503c37bf436c Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 20 Jan 2021 03:35:49 -0800 Subject: [PATCH 054/208] Fix a redundant case expression warning --- src/Data/Aeson/TypeScript/Util.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index 2182ff2..f8a1a26 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -146,7 +146,8 @@ contentsTupleType ci = let fields = constructorFields ci in getBracketsExpression :: Bool -> [(Name, String)] -> Q Exp getBracketsExpression _ [] = [|""|] -getBracketsExpression includeSuffix names = [|case $(genericVariablesListExpr includeSuffix names) of [] -> ""; vars -> "<" <> L.intercalate ", " vars <> ">"|] +getBracketsExpression includeSuffix names = + [|let vars = $(genericVariablesListExpr includeSuffix names) in "<" <> L.intercalate ", " vars <> ">"|] getBracketsExpressionAllTypesNoSuffix :: [(Name, String)] -> Q Exp getBracketsExpressionAllTypesNoSuffix [] = [|""|] From f86258e099f6b1d80b3fb58724e822c745d356ba Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 20 Jan 2021 03:35:59 -0800 Subject: [PATCH 055/208] Fix type families tests --- src/Data/Aeson/TypeScript/TH.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index ff3f48c..040de04 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -198,7 +198,7 @@ deriveTypeScript' options name extraOptions = do SigT (VarT n) StarT -> Just n _ -> Nothing genericVariablesAndSuffixes <- forM eligibleGenericVars $ \var -> do - (_, genericInfos) <- runWriterT $ forM_ (datatypeCons dti) $ \ci -> + (_, genericInfos) <- runWriterT $ forM_ (datatypeCons datatypeInfo') $ \ci -> forM_ (namesAndTypes options ci) $ \(_, typ) -> do searchForConstraints extraOptions typ var return (var, unifyGenericVariable genericInfos) From 74eb21e266024c0ae943783606b3fdae988c25d6 Mon Sep 17 00:00:00 2001 From: Ghais Date: Wed, 3 Feb 2021 15:28:01 -0500 Subject: [PATCH 056/208] Update th-abstraction dependency version to <0.5 Stack lts-17.0 upgraded th-abstraction from 0.3.2.0 to 0.4.2.0 This commit updates the package dependency on th-abstraction to be less than 0.5. --- ChangeLog.md | 1 + aeson-typescript.cabal | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 335a5a9..fe80204 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,4 @@ # Changelog for aeson-typescript ## Unreleased changes +* Update th-abstraction dependency to < 0.5 to support working with newer Stack lts. diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index b5a7768..7c79360 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -7,7 +7,7 @@ cabal-version: 1.12 -- hash: c5f3990cf145c3739268d2bf2869bd68dbb2c4272a47ae4d8d61dd16d51a37e4 name: aeson-typescript -version: 0.2.0.0 +version: 0.2.0.1 synopsis: Generate TypeScript definition files from your ADTs description: Please see the README on Github at category: Text, Web, JSON @@ -53,7 +53,7 @@ library , mtl , template-haskell , text - , th-abstraction <0.4 + , th-abstraction <0.5 , unordered-containers default-language: Haskell2010 @@ -106,6 +106,6 @@ test-suite aeson-typescript-test , template-haskell , temporary , text - , th-abstraction <0.4 + , th-abstraction <0.5 , unordered-containers default-language: Haskell2010 From e0b530419fb464ad1e96a761968e85347a13d0c6 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 5 Feb 2021 11:34:52 -0800 Subject: [PATCH 057/208] Improve changelog --- CHANGELOG.md | 19 +++++++++++++++++++ ChangeLog.md | 4 ---- 2 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 ChangeLog.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d0f569e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,19 @@ +# Change log + +## Unreleased changes + +* Update th-abstraction dependency to < 0.5 to support working with newer Stack LTS. +* Major refactors to improve TH quality. +* Tracking of parent types to allow recursive deriving + * The `getParentTypes` function was added to the main typeclass. + * The new `Data.Aeson.TypeScript.Recursive` module for working with recursive definitions. +* New support for mapping Haskell closed type families to TypeScript lookup types. + +## 0.2.0.0 + +* New formatting option `interfaceNameModifier`. + +## 0.1.0.0 + +* Initial release. + diff --git a/ChangeLog.md b/ChangeLog.md deleted file mode 100644 index fe80204..0000000 --- a/ChangeLog.md +++ /dev/null @@ -1,4 +0,0 @@ -# Changelog for aeson-typescript - -## Unreleased changes -* Update th-abstraction dependency to < 0.5 to support working with newer Stack lts. From c3279250e7b98a0ff267012cc2fa611d26192825 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 5 Feb 2021 12:05:58 -0800 Subject: [PATCH 058/208] Try using github workflow --- .github/workflows/aeson-typescript.yml | 88 ++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 .github/workflows/aeson-typescript.yml diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml new file mode 100644 index 0000000..f3404b7 --- /dev/null +++ b/.github/workflows/aeson-typescript.yml @@ -0,0 +1,88 @@ +name: aeson-typescript + +on: + pull_request: + push: + branches: [master] + +jobs: + cabal: + name: ${{ matrix.os }} / ghc ${{ matrix.ghc }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macOS-latest] + cabal: ["3.2"] + ghc: + - "8.6.5" + - "8.8.4" + - "8.10.1" + # exclude: + # - os: macOS-latest + # ghc: 8.8.3 + + steps: + - uses: actions/checkout@v2 + if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master' + + - uses: actions/setup-haskell@v1.1.1 + id: setup-haskell-cabal + name: Setup Haskell + with: + ghc-version: ${{ matrix.ghc }} + cabal-version: ${{ matrix.cabal }} + + - name: Freeze + run: | + cd sandwich + cabal freeze + + - uses: actions/cache@v1 + name: Cache ~/.cabal/store + with: + path: ${{ steps.setup-haskell-cabal.outputs.cabal-store }} + key: ${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }} + + - name: Build + run: | + cd sandwich + cabal configure --enable-tests --enable-benchmarks --test-show-details=direct + cabal build all + + - name: Test + run: | + cd sandwich + cabal test all + + stack: + name: stack / ghc ${{ matrix.ghc }} + runs-on: ubuntu-latest + strategy: + matrix: + stack: ["2.5.1"] + ghc: + - "8.8.4" + + steps: + - uses: actions/checkout@v2 + if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master' + + - uses: actions/setup-haskell@v1.1 + name: Setup Haskell Stack + with: + ghc-version: ${{ matrix.ghc }} + stack-version: ${{ matrix.stack }} + + - uses: actions/cache@v1 + name: Cache ~/.stack + with: + path: ~/.stack + key: ${{ runner.os }}-${{ matrix.ghc }}-stack + + - name: Build + run: | + stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks + + - name: Test + run: | + stack test --system-ghc From abba73a381b043764818570cc03c6c9e523714cd Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 5 Feb 2021 12:12:42 -0800 Subject: [PATCH 059/208] Set fail-fast: false --- .github/workflows/aeson-typescript.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index f3404b7..735d01f 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -10,6 +10,7 @@ jobs: name: ${{ matrix.os }} / ghc ${{ matrix.ghc }} runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [ubuntu-latest, macOS-latest] cabal: ["3.2"] @@ -58,6 +59,7 @@ jobs: name: stack / ghc ${{ matrix.ghc }} runs-on: ubuntu-latest strategy: + fail-fast: false matrix: stack: ["2.5.1"] ghc: From fa61a11514a134c26add4c1fb2bdcf8c61360477 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 5 Feb 2021 12:20:25 -0800 Subject: [PATCH 060/208] Try bumping setup-haskell --- .github/workflows/aeson-typescript.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 735d01f..3dc230c 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@v2 if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master' - - uses: actions/setup-haskell@v1.1.1 + - uses: actions/setup-haskell@v1.1.4 id: setup-haskell-cabal name: Setup Haskell with: @@ -69,7 +69,7 @@ jobs: - uses: actions/checkout@v2 if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master' - - uses: actions/setup-haskell@v1.1 + - uses: actions/setup-haskell@v1.1.4 name: Setup Haskell Stack with: ghc-version: ${{ matrix.ghc }} From c9d263ef4c738d83ae96136b32fd96715d051290 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 5 Feb 2021 12:30:49 -0800 Subject: [PATCH 061/208] Remove old references to sandwich --- .github/workflows/aeson-typescript.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 3dc230c..6bed3e7 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -35,7 +35,6 @@ jobs: - name: Freeze run: | - cd sandwich cabal freeze - uses: actions/cache@v1 @@ -46,13 +45,11 @@ jobs: - name: Build run: | - cd sandwich cabal configure --enable-tests --enable-benchmarks --test-show-details=direct cabal build all - name: Test run: | - cd sandwich cabal test all stack: From c496c072a7bd09306d8cbe98e966ccd0ddf0460f Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 5 Feb 2021 12:36:10 -0800 Subject: [PATCH 062/208] Add badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ee2a6a..7eb4620 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# Welcome to `aeson-typescript` [![Hackage](https://img.shields.io/hackage/v/aeson-typescript.svg)](https://hackage.haskell.org/package/aeson-typescript) [![Build Status](https://travis-ci.org/codedownio/aeson-typescript.svg)](https://travis-ci.org/codedownio/aeson-typescript) +# Welcome to `aeson-typescript` [![Hackage](https://img.shields.io/hackage/v/aeson-typescript.svg)](https://hackage.haskell.org/package/aeson-typescript) ![aeson-typescript](https://github.com/codedownio/aeson-typescript/workflows/aeson-typescript/badge.svg) This library provides a way to generate TypeScript `.d.ts` files that match your existing Aeson `ToJSON` instances. If you already use Aeson's Template Haskell support to derive your instances, then deriving TypeScript is as simple as From 1116dd4139ad9cb87697334a3f049e57647bc30c Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 5 Feb 2021 13:26:29 -0800 Subject: [PATCH 063/208] Fix on GHC 8.6 --- src/Data/Aeson/TypeScript/Lookup.hs | 10 +++++++++- src/Data/Aeson/TypeScript/TH.hs | 22 ++++++++++++++-------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Lookup.hs b/src/Data/Aeson/TypeScript/Lookup.hs index acc0c17..ff24e4b 100644 --- a/src/Data/Aeson/TypeScript/Lookup.hs +++ b/src/Data/Aeson/TypeScript/Lookup.hs @@ -42,14 +42,22 @@ deriveTypeScriptLookupType name declNameStr = do getClosedTypeFamilyInterfaceDecl :: Name -> [TySynEqn] -> Q Exp getClosedTypeFamilyInterfaceDecl name eqns = do fields <- forM eqns $ \case +#if MIN_VERSION_template_haskell(2,15,0) TySynEqn Nothing (AppT (ConT _) (ConT arg)) result -> do +#else + TySynEqn [ConT arg] result -> do +#endif [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) |] x -> fail [i|aeson-typescript doesn't know yet how to handle this type family equation: '#{x}'|] - [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] $(listE $ fmap return fields) |] + [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] $(listE $ fmap return fields) |] getClosedTypeFamilyImage :: [TySynEqn] -> Q [Type] getClosedTypeFamilyImage eqns = do forM eqns $ \case +#if MIN_VERSION_template_haskell(2,15,0) TySynEqn Nothing (AppT (ConT _) (ConT _)) result -> return result +#else + TySynEqn [ConT _] result -> return result +#endif x -> fail [i|aeson-typescript doesn't know yet how to handle this type family equation: '#{x}'|] diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 040de04..1f151bb 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -129,7 +129,7 @@ module Data.Aeson.TypeScript.TH ( -- * Advanced options , ExtraTypeScriptOptions(..) - + -- * Convenience tools , HasJSONOptions(..) , deriveJSONAndTypeScript @@ -139,7 +139,7 @@ module Data.Aeson.TypeScript.TH ( , T1(..) , T2(..) , T3(..) - + , module Data.Aeson.TypeScript.Instances ) where @@ -231,7 +231,7 @@ deriveTypeScript' options name extraOptions = do -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration handleConstructor :: Options -> ExtraTypeScriptOptions -> DatatypeInfo -> [(Name, String)] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp -handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = +handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> do writeSingleConstructorEncoding brackets <- lift $ getBracketsExpression False genericVariables @@ -298,7 +298,7 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@( tell [ExtraConstraint constraint] (fieldTyp, optAsBool) <- lift $ case typ of - (AppT (ConT name) t) | name == ''Maybe && not (omitNothingFields options) -> + (AppT (ConT name) t) | name == ''Maybe && not (omitNothingFields options) -> ( , ) <$> [|$(getTypeAsStringExp t) <> " | null"|] <*> getOptionalAsBoolExp t _ -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ' lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) |] @@ -323,16 +323,18 @@ transformTypeFamilies eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) tell [ExtraParentType (AppT (ConT name') (ConT ''T))] - transformTypeFamilies eo (AppT (ConT name') typ) + transformTypeFamilies eo (AppT (ConT name') typ) _ -> AppT (ConT name) <$> transformTypeFamilies eo typ | otherwise = AppT (ConT name) <$> transformTypeFamilies eo typ transformTypeFamilies eo (AppT typ1 typ2) = AppT <$> transformTypeFamilies eo typ1 <*> transformTypeFamilies eo typ2 -transformTypeFamilies eo (AppKindT typ kind) = flip AppKindT kind <$> transformTypeFamilies eo typ transformTypeFamilies eo (SigT typ kind) = flip SigT kind <$> transformTypeFamilies eo typ transformTypeFamilies eo (InfixT typ1 n typ2) = InfixT <$> transformTypeFamilies eo typ1 <*> pure n <*> transformTypeFamilies eo typ2 transformTypeFamilies eo (UInfixT typ1 n typ2) = UInfixT <$> transformTypeFamilies eo typ1 <*> pure n <*> transformTypeFamilies eo typ2 transformTypeFamilies eo (ParensT typ) = ParensT <$> transformTypeFamilies eo typ +#if MIN_VERSION_template_haskell(2,15,0) +transformTypeFamilies eo (AppKindT typ kind) = flip AppKindT kind <$> transformTypeFamilies eo typ transformTypeFamilies eo (ImplicitParamT s typ) = ImplicitParamT s <$> transformTypeFamilies eo typ +#endif transformTypeFamilies _ typ = return typ @@ -345,23 +347,27 @@ searchForConstraints eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) var _ -> searchForConstraints eo typ var | otherwise = searchForConstraints eo typ var searchForConstraints eo (AppT typ1 typ2) var = searchForConstraints eo typ1 var >> searchForConstraints eo typ2 var -searchForConstraints eo (AppKindT typ _) var = searchForConstraints eo typ var searchForConstraints eo (SigT typ _) var = searchForConstraints eo typ var searchForConstraints eo (InfixT typ1 _ typ2) var = searchForConstraints eo typ1 var >> searchForConstraints eo typ2 var searchForConstraints eo (UInfixT typ1 _ typ2) var = searchForConstraints eo typ1 var >> searchForConstraints eo typ2 var searchForConstraints eo (ParensT typ) var = searchForConstraints eo typ var +#if MIN_VERSION_template_haskell(2,15,0) +searchForConstraints eo (AppKindT typ _) var = searchForConstraints eo typ var searchForConstraints eo (ImplicitParamT _ typ) var = searchForConstraints eo typ var +#endif searchForConstraints _ _ _ = return () hasFreeTypeVariable :: Type -> Bool hasFreeTypeVariable (VarT _) = True hasFreeTypeVariable (AppT typ1 typ2) = hasFreeTypeVariable typ1 || hasFreeTypeVariable typ2 -hasFreeTypeVariable (AppKindT typ _) = hasFreeTypeVariable typ hasFreeTypeVariable (SigT typ _) = hasFreeTypeVariable typ hasFreeTypeVariable (InfixT typ1 _ typ2) = hasFreeTypeVariable typ1 || hasFreeTypeVariable typ2 hasFreeTypeVariable (UInfixT typ1 _ typ2) = hasFreeTypeVariable typ1 || hasFreeTypeVariable typ2 hasFreeTypeVariable (ParensT typ) = hasFreeTypeVariable typ +#if MIN_VERSION_template_haskell(2,15,0) +hasFreeTypeVariable (AppKindT typ _) = hasFreeTypeVariable typ hasFreeTypeVariable (ImplicitParamT _ typ) = hasFreeTypeVariable typ +#endif hasFreeTypeVariable _ = False unifyGenericVariable :: [GenericInfo] -> String From 9d174cd91612a8c4a883979f10c7a4634776f645 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 5 Feb 2021 14:37:01 -0800 Subject: [PATCH 064/208] Try adding TSC install step --- .github/workflows/aeson-typescript.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 6bed3e7..d94b603 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -43,6 +43,14 @@ jobs: path: ${{ steps.setup-haskell-cabal.outputs.cabal-store }} key: ${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }} + # Install TSC + - uses: actions/setup-node@v2 + with: + node-version: '12' + - name: Install TSC + run: | + npm install -g tsc + - name: Build run: | cabal configure --enable-tests --enable-benchmarks --test-show-details=direct @@ -78,6 +86,14 @@ jobs: path: ~/.stack key: ${{ runner.os }}-${{ matrix.ghc }}-stack + # Install TSC + - uses: actions/setup-node@v2 + with: + node-version: '12' + - name: Install TSC + run: | + npm install -g tsc + - name: Build run: | stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks From 53555f6d977b238c55fb87501a81598555a330e5 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 5 Feb 2021 15:06:45 -0800 Subject: [PATCH 065/208] tsc -> typescript --- .github/workflows/aeson-typescript.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index d94b603..c9a606b 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -49,7 +49,7 @@ jobs: node-version: '12' - name: Install TSC run: | - npm install -g tsc + npm install -g typescript - name: Build run: | @@ -92,7 +92,7 @@ jobs: node-version: '12' - name: Install TSC run: | - npm install -g tsc + npm install -g typescript - name: Build run: | From 8fd3a7181f84a54ff27ef2013304e58d448f6e95 Mon Sep 17 00:00:00 2001 From: Ninjatrappeur Date: Mon, 15 Mar 2021 17:56:07 +0100 Subject: [PATCH 066/208] Allow to export types easily (#22) In some cases, we'd like to use the generated types in a typescript module instead of a declaration file. When used in a module, the types need to be explicitely exported to be re-used elsewhere. Adding a new exportTypes FormattingOption in charge of prefixing the generated types with "export". Co-authored-by: Francesco Mazzoli --- src/Data/Aeson/TypeScript/Formatting.hs | 11 ++++++++--- src/Data/Aeson/TypeScript/Types.hs | 13 +++++++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index 909a8ba..e61339e 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -10,7 +10,7 @@ import qualified Data.Text as T import Data.Monoid #endif - + -- | Same as 'formatTSDeclarations'', but uses default formatting options. formatTSDeclarations :: [TSDeclaration] -> String formatTSDeclarations = formatTSDeclarations' defaultFormattingOptions @@ -18,17 +18,22 @@ formatTSDeclarations = formatTSDeclarations' defaultFormattingOptions -- | Format a single TypeScript declaration. This version accepts a FormattingOptions object in case you want more control over the output. formatTSDeclaration :: FormattingOptions -> TSDeclaration -> String formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVariables names) = - [i|type #{typeNameModifier name}#{getGenericBrackets genericVariables} = #{alternatives};|] + [i|#{exportPrefix exportMode}type #{typeNameModifier name}#{getGenericBrackets genericVariables} = #{alternatives};|] where alternatives = T.intercalate " | " (fmap T.pack names) + formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceName genericVariables members) = - [i|interface #{modifiedInterfaceName}#{getGenericBrackets genericVariables} { + [i|#{exportPrefix exportMode}interface #{modifiedInterfaceName}#{getGenericBrackets genericVariables} { #{ls} }|] where ls = T.intercalate "\n" $ fmap T.pack [(replicate numIndentSpaces ' ') <> formatTSField member <> ";"| member <- members] modifiedInterfaceName = (\(li, name) -> li <> interfaceNameModifier name) . splitAt 1 $ interfaceName formatTSDeclaration (FormattingOptions {..}) (TSRawDeclaration text) = text +exportPrefix :: ExportMode -> String +exportPrefix ExportEach = "export " +exportPrefix ExportNone = "" + -- | Format a list of TypeScript declarations into a string, suitable for putting directly into a @.d.ts@ file. formatTSDeclarations' :: FormattingOptions -> [TSDeclaration] -> String formatTSDeclarations' options declarations = T.unpack $ T.intercalate "\n\n" (fmap (T.pack . formatTSDeclaration options) declarations) diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 1127be7..e74dcb6 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -58,7 +58,7 @@ class (Typeable a) => TypeScript a where isGenericVariable :: Proxy a -> Bool -- ^ Special flag to indicate whether this type corresponds to a template variable. isGenericVariable _ = False - + -- | An existential wrapper for any TypeScript instance. data TSType = forall a. (Typeable a, TypeScript a) => TSType { unTSType :: Proxy a } @@ -91,6 +91,12 @@ instance IsString (TSString a) where -- * Formatting options +data ExportMode = + ExportEach + -- ^ Prefix every declaration with the "export" keyword (suitable for putting in a TypeScripe module) + | ExportNone + -- ^ No exporting (suitable for putting in a .d.ts file) + data FormattingOptions = FormattingOptions { numIndentSpaces :: Int -- ^ How many spaces to indent TypeScript blocks @@ -98,6 +104,8 @@ data FormattingOptions = FormattingOptions -- ^ Function applied to generated interface names , typeNameModifier :: String -> String -- ^ Function applied to generated type names + , exportMode :: ExportMode + -- ^ Prefix the generated types with "export" if set to 'True'. } defaultFormattingOptions :: FormattingOptions @@ -105,6 +113,7 @@ defaultFormattingOptions = FormattingOptions { numIndentSpaces = 2 , interfaceNameModifier = id , typeNameModifier = id + , exportMode = ExportNone } -- | Convenience typeclass class you can use to "attach" a set of Aeson encoding options to a type. @@ -136,7 +145,7 @@ instance TypeScript T9 where getTypeScriptType _ = "T9"; isGenericVariable _ = T instance TypeScript T10 where getTypeScriptType _ = "T10"; isGenericVariable _ = True allStarConstructors :: [Type] -allStarConstructors = [ConT ''T1, ConT ''T2, ConT ''T3, ConT ''T4, ConT ''T5, ConT ''T6, ConT ''T7, ConT ''T8, ConT ''T9, ConT ''T10] +allStarConstructors = [ConT ''T1, ConT ''T2, ConT ''T3, ConT ''T4, ConT ''T5, ConT ''T6, ConT ''T7, ConT ''T8, ConT ''T9, ConT ''T10] allStarConstructors' :: [Name] allStarConstructors' = [''T1, ''T2, ''T3, ''T4, ''T5, ''T6, ''T7, ''T8, ''T9, ''T10] From 8023174a46317fb575df833992dff659cdb8df5e Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 25 Mar 2021 00:29:01 -0700 Subject: [PATCH 067/208] Add Data.Aeson.TypeScript.Internal module --- package.yaml | 1 + src/Data/Aeson/TypeScript/Internal.hs | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 src/Data/Aeson/TypeScript/Internal.hs diff --git a/package.yaml b/package.yaml index 89a2730..d2f1b29 100644 --- a/package.yaml +++ b/package.yaml @@ -40,6 +40,7 @@ library: source-dirs: src exposed-modules: - Data.Aeson.TypeScript.TH + - Data.Aeson.TypeScript.Internal - Data.Aeson.TypeScript.Recursive tests: diff --git a/src/Data/Aeson/TypeScript/Internal.hs b/src/Data/Aeson/TypeScript/Internal.hs new file mode 100644 index 0000000..eebfbb7 --- /dev/null +++ b/src/Data/Aeson/TypeScript/Internal.hs @@ -0,0 +1,8 @@ +-- | Internal details. For now, this module just exports the full TSDeclaration constructors. +-- These are subject to breaking changes but are exported here in case you want to live dangerously. + +module Data.Aeson.TypeScript.Internal ( + TSDeclaration(..) + ) where + +import Data.Aeson.TypeScript.Types From 97d116663a518bd593131f9cbc36697173135768 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 25 Mar 2021 00:40:18 -0700 Subject: [PATCH 068/208] Merge mauriciofierrom/format-sum-types --- aeson-typescript.cabal | 5 +++- package.yaml | 4 +-- src/Data/Aeson/TypeScript/Formatting.hs | 31 ++++++++++++++++++++--- src/Data/Aeson/TypeScript/TH.hs | 1 + src/Data/Aeson/TypeScript/Types.hs | 22 ++++++++++++----- test/Basic.hs | 7 ++---- test/Formatting.hs | 33 +++++++++++++++++++++++++ test/Spec.hs | 3 +++ 8 files changed, 88 insertions(+), 18 deletions(-) create mode 100644 test/Formatting.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 7c79360..29213f0 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -4,7 +4,7 @@ cabal-version: 1.12 -- -- see: https://github.com/sol/hpack -- --- hash: c5f3990cf145c3739268d2bf2869bd68dbb2c4272a47ae4d8d61dd16d51a37e4 +-- hash: 16f81138b5a26b66ca80560ddcb787ab65a090a9f77906fef1236ac6309141be name: aeson-typescript version: 0.2.0.1 @@ -35,6 +35,7 @@ source-repository head library exposed-modules: Data.Aeson.TypeScript.TH + Data.Aeson.TypeScript.Internal Data.Aeson.TypeScript.Recursive other-modules: Data.Aeson.TypeScript.Formatting @@ -62,6 +63,7 @@ test-suite aeson-typescript-test main-is: Spec.hs other-modules: Basic + Formatting HigherKind Live Live2 @@ -81,6 +83,7 @@ test-suite aeson-typescript-test Util Data.Aeson.TypeScript.Formatting Data.Aeson.TypeScript.Instances + Data.Aeson.TypeScript.Internal Data.Aeson.TypeScript.Lookup Data.Aeson.TypeScript.Recursive Data.Aeson.TypeScript.TH diff --git a/package.yaml b/package.yaml index d2f1b29..3a22c19 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: aeson-typescript -version: 0.2.0.0 +version: 0.2.0.1 github: "codedownio/aeson-typescript" license: BSD3 category: Text, Web, JSON @@ -33,7 +33,7 @@ dependencies: - mtl - template-haskell - text -- th-abstraction < 0.4 +- th-abstraction < 0.5 - unordered-containers library: diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index e61339e..76fbac5 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -18,9 +18,18 @@ formatTSDeclarations = formatTSDeclarations' defaultFormattingOptions -- | Format a single TypeScript declaration. This version accepts a FormattingOptions object in case you want more control over the output. formatTSDeclaration :: FormattingOptions -> TSDeclaration -> String formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVariables names) = - [i|#{exportPrefix exportMode}type #{typeNameModifier name}#{getGenericBrackets genericVariables} = #{alternatives};|] - where alternatives = T.intercalate " | " (fmap T.pack names) - + case sumTypeFormat of + Enum -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnum} }|] + EnumWithType -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnumWithType} }#{enumType}|] + _ -> [i|#{exportPrefix exportMode}type #{typeNameModifier name}#{getGenericBrackets genericVariables} = #{alternatives};|] + where + alternatives = T.intercalate " | " (fmap T.pack names) + alternativesEnum = T.intercalate ", " $ + [T.pack (replicate numIndentSpaces ' ') <> toEnumName entry | entry <- T.pack <$> names] + alternativesEnumWithType = T.intercalate ", " $ + [T.pack (replicate numIndentSpaces ' ') <> toEnumName entry <> "=" <> entry | entry <- T.pack <$> names] + enumType = [i|\n\ntype #{name} = keyof typeof #{typeNameModifier name};|] + toEnumName = T.replace "\"" "" formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceName genericVariables members) = [i|#{exportPrefix exportMode}interface #{modifiedInterfaceName}#{getGenericBrackets genericVariables} { @@ -36,7 +45,21 @@ exportPrefix ExportNone = "" -- | Format a list of TypeScript declarations into a string, suitable for putting directly into a @.d.ts@ file. formatTSDeclarations' :: FormattingOptions -> [TSDeclaration] -> String -formatTSDeclarations' options declarations = T.unpack $ T.intercalate "\n\n" (fmap (T.pack . formatTSDeclaration options) declarations) +formatTSDeclarations' options declarations = T.unpack $ T.intercalate "\n\n" (fmap (T.pack . formatTSDeclaration (validateFormattingOptions options declarations)) declarations) + +validateFormattingOptions :: FormattingOptions -> [TSDeclaration] -> FormattingOptions +validateFormattingOptions options@FormattingOptions{..} decls + | sumTypeFormat == Enum && isPlainSumType decls = options + | sumTypeFormat == EnumWithType && isPlainSumType decls = options { typeNameModifier = flip (<>) "Enum" } + | otherwise = options { sumTypeFormat = StringLiteralType } + 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) = [i|#{name}#{if optional then ("?" :: String) else ""}: #{typ}|] diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 1f151bb..2bbdf42 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -126,6 +126,7 @@ module Data.Aeson.TypeScript.TH ( , formatTSDeclarations' , formatTSDeclaration , FormattingOptions(..) + , SumTypeFormat(..) -- * Advanced options , ExtraTypeScriptOptions(..) diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index e74dcb6..caa37b3 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -91,12 +91,6 @@ instance IsString (TSString a) where -- * Formatting options -data ExportMode = - ExportEach - -- ^ Prefix every declaration with the "export" keyword (suitable for putting in a TypeScripe module) - | ExportNone - -- ^ No exporting (suitable for putting in a .d.ts file) - data FormattingOptions = FormattingOptions { numIndentSpaces :: Int -- ^ How many spaces to indent TypeScript blocks @@ -106,14 +100,30 @@ data FormattingOptions = FormattingOptions -- ^ Function applied to generated type names , exportMode :: ExportMode -- ^ Prefix the generated types with "export" if set to 'True'. + , sumTypeFormat :: SumTypeFormat + -- ^ How to format sum types } +data ExportMode = + ExportEach + -- ^ Prefix every declaration with the "export" keyword (suitable for putting in a TypeScripe module) + | ExportNone + -- ^ No exporting (suitable for putting in a .d.ts file) + +-- | TODO: docstrings here +data SumTypeFormat = + StringLiteralType + | Enum + | EnumWithType + deriving (Eq, Show) + defaultFormattingOptions :: FormattingOptions defaultFormattingOptions = FormattingOptions { numIndentSpaces = 2 , interfaceNameModifier = id , typeNameModifier = id , exportMode = ExportNone + , sumTypeFormat = StringLiteralType } -- | Convenience typeclass class you can use to "attach" a set of Aeson encoding options to a type. diff --git a/test/Basic.hs b/test/Basic.hs index 560c552..3603210 100644 --- a/test/Basic.hs +++ b/test/Basic.hs @@ -16,15 +16,12 @@ module Basic (tests) where import Data.Aeson as A -import Data.Aeson.TH as A import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types -import Data.Monoid import Data.Proxy import Data.String.Interpolate.IsString import Prelude hiding (Double) import Test.Hspec -import Util data Unit1 = Unit1 @@ -32,7 +29,7 @@ $(deriveTypeScript A.defaultOptions ''Unit1) data Unit2 = Unit2 $(deriveTypeScript (A.defaultOptions { A.tagSingleConstructors = True - , A.constructorTagModifier = const "foo" }) ''Unit2) + , A.constructorTagModifier = const "foo" }) ''Unit2) tests :: SpecWith () tests = describe "Basic tests" $ do @@ -45,7 +42,7 @@ tests = describe "Basic tests" $ do it [i|Works with a unit with constructorTagModifier|] $ do (getTypeScriptDeclarations (Proxy :: Proxy Unit2)) `shouldBe` ([]) - + main :: IO () main = hspec tests diff --git a/test/Formatting.hs b/test/Formatting.hs new file mode 100644 index 0000000..271ad7b --- /dev/null +++ b/test/Formatting.hs @@ -0,0 +1,33 @@ +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} + +module Formatting (tests) where + +import Data.Aeson (defaultOptions) +import Data.Aeson.TypeScript.TH +import Data.Aeson.TypeScript.Types +import Data.Proxy +import Test.Hspec + +data D = S | F deriving (Eq, Show) + +$(deriveTypeScript defaultOptions ''D) + +tests :: Spec +tests = do + let stringTypeLiteralType = "type D = \"S\" | \"F\";" + enum = "enum D { S, F }" + enumWithType = "enum DEnum { S=\"S\", F=\"F\" }\n\ntype D = keyof typeof DEnum;" + + describe "Formatting" $ + describe "when given a Sum Type" $ do + describe "and the StringLiteralType format option is set" $ + it "should generate a TS string literal type" $ + formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @D Proxy) `shouldBe` stringTypeLiteralType + describe "and the Enum format option is set" $ + it "should generate a TS Enum" $ + formatTSDeclarations' (defaultFormattingOptions { sumTypeFormat = Enum }) (getTypeScriptDeclarations @D Proxy) `shouldBe` enum + describe "and the EnumWithType format option is set" $ + it "should generate a TS Enum with a type declaration" $ + formatTSDeclarations' (defaultFormattingOptions { sumTypeFormat = EnumWithType }) (getTypeScriptDeclarations @D Proxy) `shouldBe` enumWithType diff --git a/test/Spec.hs b/test/Spec.hs index 3d26c48..fb862b7 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -3,8 +3,10 @@ module Main where import Test.Hspec +import qualified Formatting import qualified HigherKind import qualified TypeFamilies + import qualified ObjectWithSingleFieldNoTagSingleConstructors import qualified ObjectWithSingleFieldTagSingleConstructors import qualified TaggedObjectNoTagSingleConstructors @@ -19,6 +21,7 @@ import qualified NoOmitNothingFields main :: IO () main = hspec $ do + Formatting.tests HigherKind.tests TypeFamilies.tests From baa74a15d46f0a46c892df3b9dcb2a3e8b65b994 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 25 Mar 2021 00:52:39 -0700 Subject: [PATCH 069/208] Change some names --- src/Data/Aeson/TypeScript/Formatting.hs | 10 +++++----- src/Data/Aeson/TypeScript/Types.hs | 10 +++++----- test/Formatting.hs | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index 76fbac5..3453938 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -18,10 +18,10 @@ formatTSDeclarations = formatTSDeclarations' defaultFormattingOptions -- | Format a single TypeScript declaration. This version accepts a FormattingOptions object in case you want more control over the output. formatTSDeclaration :: FormattingOptions -> TSDeclaration -> String formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVariables names) = - case sumTypeFormat of + case typeAlternativesFormat of Enum -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnum} }|] EnumWithType -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnumWithType} }#{enumType}|] - _ -> [i|#{exportPrefix exportMode}type #{typeNameModifier name}#{getGenericBrackets genericVariables} = #{alternatives};|] + TypeAlias -> [i|#{exportPrefix exportMode}type #{typeNameModifier name}#{getGenericBrackets genericVariables} = #{alternatives};|] where alternatives = T.intercalate " | " (fmap T.pack names) alternativesEnum = T.intercalate ", " $ @@ -49,9 +49,9 @@ formatTSDeclarations' options declarations = T.unpack $ T.intercalate "\n\n" (fm validateFormattingOptions :: FormattingOptions -> [TSDeclaration] -> FormattingOptions validateFormattingOptions options@FormattingOptions{..} decls - | sumTypeFormat == Enum && isPlainSumType decls = options - | sumTypeFormat == EnumWithType && isPlainSumType decls = options { typeNameModifier = flip (<>) "Enum" } - | otherwise = options { sumTypeFormat = StringLiteralType } + | typeAlternativesFormat == Enum && isPlainSumType decls = options + | typeAlternativesFormat == EnumWithType && isPlainSumType decls = options { typeNameModifier = flip (<>) "Enum" } + | otherwise = options { typeAlternativesFormat = TypeAlias } where isInterface :: TSDeclaration -> Bool isInterface TSInterfaceDeclaration{} = True diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index caa37b3..154eea2 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -99,9 +99,9 @@ data FormattingOptions = FormattingOptions , typeNameModifier :: String -> String -- ^ Function applied to generated type names , exportMode :: ExportMode - -- ^ Prefix the generated types with "export" if set to 'True'. - , sumTypeFormat :: SumTypeFormat - -- ^ How to format sum types + -- ^ Whether to include the export keyword in declarations + , typeAlternativesFormat :: SumTypeFormat + -- ^ How to format the declaration of the alternatives when multiple constructors exist } data ExportMode = @@ -112,7 +112,7 @@ data ExportMode = -- | TODO: docstrings here data SumTypeFormat = - StringLiteralType + TypeAlias | Enum | EnumWithType deriving (Eq, Show) @@ -123,7 +123,7 @@ defaultFormattingOptions = FormattingOptions , interfaceNameModifier = id , typeNameModifier = id , exportMode = ExportNone - , sumTypeFormat = StringLiteralType + , typeAlternativesFormat = TypeAlias } -- | Convenience typeclass class you can use to "attach" a set of Aeson encoding options to a type. diff --git a/test/Formatting.hs b/test/Formatting.hs index 271ad7b..26e9c1f 100644 --- a/test/Formatting.hs +++ b/test/Formatting.hs @@ -17,17 +17,17 @@ $(deriveTypeScript defaultOptions ''D) tests :: Spec tests = do let stringTypeLiteralType = "type D = \"S\" | \"F\";" - enum = "enum D { S, F }" - enumWithType = "enum DEnum { S=\"S\", F=\"F\" }\n\ntype D = keyof typeof DEnum;" + let enum = "enum D { S, F }" + let enumWithType = "enum DEnum { S=\"S\", F=\"F\" }\n\ntype D = keyof typeof DEnum;" describe "Formatting" $ describe "when given a Sum Type" $ do - describe "and the StringLiteralType format option is set" $ + describe "and the TypeAlias format option is set" $ it "should generate a TS string literal type" $ formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @D Proxy) `shouldBe` stringTypeLiteralType describe "and the Enum format option is set" $ it "should generate a TS Enum" $ - formatTSDeclarations' (defaultFormattingOptions { sumTypeFormat = Enum }) (getTypeScriptDeclarations @D Proxy) `shouldBe` enum + formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @D Proxy) `shouldBe` enum describe "and the EnumWithType format option is set" $ it "should generate a TS Enum with a type declaration" $ - formatTSDeclarations' (defaultFormattingOptions { sumTypeFormat = EnumWithType }) (getTypeScriptDeclarations @D Proxy) `shouldBe` enumWithType + formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = EnumWithType }) (getTypeScriptDeclarations @D Proxy) `shouldBe` enumWithType From f31170cc261a7d131ed1024b6e8918f1b93324d5 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 25 Mar 2021 01:06:54 -0700 Subject: [PATCH 070/208] Fix weird indentation --- src/Data/Aeson/TypeScript/Formatting.hs | 6 ++---- test/Formatting.hs | 15 ++++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index 3453938..d70cc7e 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -24,10 +24,8 @@ formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVar TypeAlias -> [i|#{exportPrefix exportMode}type #{typeNameModifier name}#{getGenericBrackets genericVariables} = #{alternatives};|] where alternatives = T.intercalate " | " (fmap T.pack names) - alternativesEnum = T.intercalate ", " $ - [T.pack (replicate numIndentSpaces ' ') <> toEnumName entry | entry <- T.pack <$> names] - alternativesEnumWithType = T.intercalate ", " $ - [T.pack (replicate numIndentSpaces ' ') <> toEnumName entry <> "=" <> entry | entry <- 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};|] toEnumName = T.replace "\"" "" diff --git a/test/Formatting.hs b/test/Formatting.hs index 26e9c1f..4c21654 100644 --- a/test/Formatting.hs +++ b/test/Formatting.hs @@ -1,6 +1,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE QuasiQuotes #-} module Formatting (tests) where @@ -8,6 +9,7 @@ import Data.Aeson (defaultOptions) import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types import Data.Proxy +import Data.String.Interpolate.IsString import Test.Hspec data D = S | F deriving (Eq, Show) @@ -16,18 +18,17 @@ $(deriveTypeScript defaultOptions ''D) tests :: Spec tests = do - let stringTypeLiteralType = "type D = \"S\" | \"F\";" - let enum = "enum D { S, F }" - let enumWithType = "enum DEnum { S=\"S\", F=\"F\" }\n\ntype D = keyof typeof DEnum;" - describe "Formatting" $ describe "when given a Sum Type" $ do describe "and the TypeAlias format option is set" $ it "should generate a TS string literal type" $ - formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @D Proxy) `shouldBe` stringTypeLiteralType + formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @D Proxy) `shouldBe` + [i|type D = "S" | "F";|] describe "and the Enum format option is set" $ it "should generate a TS Enum" $ - formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @D Proxy) `shouldBe` enum + formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @D Proxy) `shouldBe` + [i|enum D { S, F }|] describe "and the EnumWithType format option is set" $ it "should generate a TS Enum with a type declaration" $ - formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = EnumWithType }) (getTypeScriptDeclarations @D Proxy) `shouldBe` enumWithType + formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = EnumWithType }) (getTypeScriptDeclarations @D Proxy) `shouldBe` + [i|enum DEnum { S="S", F="F" }\n\ntype D = keyof typeof DEnum;|] From 6dcc8c82439b881a32544f8a1d4081126339870f Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 25 Mar 2021 01:31:36 -0700 Subject: [PATCH 071/208] Update GHC version in CI (8.10.1->8.10.4) --- .github/workflows/aeson-typescript.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index c9a606b..25e4c9c 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -17,7 +17,7 @@ jobs: ghc: - "8.6.5" - "8.8.4" - - "8.10.1" + - "8.10.4" # exclude: # - os: macOS-latest # ghc: 8.8.3 From 50ad3d41eaea1f49edbd62699440c99b458b4d54 Mon Sep 17 00:00:00 2001 From: Rhys Date: Thu, 13 May 2021 13:44:15 +1000 Subject: [PATCH 072/208] Export ExportMode(..) (#23) --- src/Data/Aeson/TypeScript/TH.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 2bbdf42..159331b 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -127,6 +127,7 @@ module Data.Aeson.TypeScript.TH ( , formatTSDeclaration , FormattingOptions(..) , SumTypeFormat(..) + , ExportMode(..) -- * Advanced options , ExtraTypeScriptOptions(..) From cb6c492f0d9ec3bea62575e1dc6310b5e5b106ac Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 12 May 2021 21:48:55 -0600 Subject: [PATCH 073/208] Getting ready to upload new major version --- CHANGELOG.md | 2 +- aeson-typescript.cabal | 11 ++++++----- package.yaml | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0f569e..d5bcd6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change log -## Unreleased changes +## 0.3.0.0 * Update th-abstraction dependency to < 0.5 to support working with newer Stack LTS. * Major refactors to improve TH quality. diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 29213f0..38d6d9f 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -1,13 +1,13 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.33.0. +-- This file has been generated from package.yaml by hpack version 0.34.4. -- -- see: https://github.com/sol/hpack -- --- hash: 16f81138b5a26b66ca80560ddcb787ab65a090a9f77906fef1236ac6309141be +-- hash: 19d5c6fbe324603190ea2987b9bacb04c42f5832447d310d8f7c33f344910511 name: aeson-typescript -version: 0.2.0.1 +version: 0.3.0.0 synopsis: Generate TypeScript definition files from your ADTs description: Please see the README on Github at category: Text, Web, JSON @@ -15,10 +15,11 @@ homepage: https://github.com/codedownio/aeson-typescript#readme bug-reports: https://github.com/codedownio/aeson-typescript/issues author: Tom McLaughlin maintainer: tom@codedown.io -copyright: 2018 CodeDown +copyright: 2021 CodeDown license: BSD3 license-file: LICENSE -tested-with: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.2 +tested-with: + GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.2 build-type: Simple extra-source-files: README.md diff --git a/package.yaml b/package.yaml index 3a22c19..084eb74 100644 --- a/package.yaml +++ b/package.yaml @@ -1,11 +1,11 @@ name: aeson-typescript -version: 0.2.0.1 +version: 0.3.0.0 github: "codedownio/aeson-typescript" license: BSD3 category: Text, Web, JSON author: "Tom McLaughlin" maintainer: "tom@codedown.io" -copyright: "2018 CodeDown" +copyright: "2021 CodeDown" extra-source-files: - README.md From 5b20f36ecf7cae05fd1df70281d528ae67921174 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 12 May 2021 21:54:23 -0600 Subject: [PATCH 074/208] Fix CHANGELOG.md path --- package.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.yaml b/package.yaml index 084eb74..45a746f 100644 --- a/package.yaml +++ b/package.yaml @@ -9,7 +9,7 @@ copyright: "2021 CodeDown" extra-source-files: - README.md -- ChangeLog.md +- CHANGELOG.md - test/assets/package.json - test/assets/npm_install.sh - test/assets/yarn_install.sh From 2eedb09b874c23031ea48e58ca58402fd9cab313 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 12 May 2021 21:56:45 -0600 Subject: [PATCH 075/208] Update cabal file and add GHC 8.10.4 to stack build matrix --- .github/workflows/aeson-typescript.yml | 1 + aeson-typescript.cabal | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 25e4c9c..afc0442 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -69,6 +69,7 @@ jobs: stack: ["2.5.1"] ghc: - "8.8.4" + - "8.10.4" steps: - uses: actions/checkout@v2 diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 38d6d9f..7e24a47 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -4,7 +4,7 @@ cabal-version: 1.12 -- -- see: https://github.com/sol/hpack -- --- hash: 19d5c6fbe324603190ea2987b9bacb04c42f5832447d310d8f7c33f344910511 +-- hash: 73021b1b79e95ae7e4b67a45fafb5d452d8ab05ae59d4071098e8628cf8f29e4 name: aeson-typescript version: 0.3.0.0 @@ -23,7 +23,7 @@ tested-with: build-type: Simple extra-source-files: README.md - ChangeLog.md + CHANGELOG.md test/assets/package.json test/assets/npm_install.sh test/assets/yarn_install.sh From 32d063ff2bc1f9187ca1faa0c34737929c86ffbd Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 27 May 2021 19:58:01 -0600 Subject: [PATCH 076/208] Update CI --- .github/workflows/aeson-typescript.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index afc0442..f3002a9 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -13,11 +13,11 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macOS-latest] - cabal: ["3.2"] ghc: - "8.6.5" - "8.8.4" - "8.10.4" + - "9.0.1" # exclude: # - os: macOS-latest # ghc: 8.8.3 @@ -26,12 +26,12 @@ jobs: - uses: actions/checkout@v2 if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master' - - uses: actions/setup-haskell@v1.1.4 + - uses: haskell/actions/setup@v1 id: setup-haskell-cabal name: Setup Haskell with: ghc-version: ${{ matrix.ghc }} - cabal-version: ${{ matrix.cabal }} + cabal-version: "latest" - name: Freeze run: | @@ -66,20 +66,20 @@ jobs: strategy: fail-fast: false matrix: - stack: ["2.5.1"] ghc: - "8.8.4" - "8.10.4" + - "9.0.1" steps: - uses: actions/checkout@v2 if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master' - - uses: actions/setup-haskell@v1.1.4 + - uses: haskell/actions/setup@v1 name: Setup Haskell Stack with: ghc-version: ${{ matrix.ghc }} - stack-version: ${{ matrix.stack }} + stack-version: "latest" - uses: actions/cache@v1 name: Cache ~/.stack From e4700fcf420a7f6ff418012bd51a3de73fb4e294 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 27 May 2021 19:59:23 -0600 Subject: [PATCH 077/208] Update docs to include type parameter --- README.md | 2 +- src/Data/Aeson/TypeScript/TH.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7eb4620..cba6b71 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ $(deriveTypeScript (defaultOptions {fieldLabelModifier = drop 4, constructorTagM Now we can use the newly created instances. ```haskell ->>> putStrLn $ formatTSDeclarations $ getTypeScriptDeclaration (Proxy :: Proxy D) +>>> putStrLn $ formatTSDeclarations $ getTypeScriptDeclaration (Proxy :: Proxy (D T)) type D = "nullary" | IUnary | IProduct | IRecord; diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 159331b..182ab64 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -48,7 +48,7 @@ $('deriveTypeScript' ('defaultOptions' {'fieldLabelModifier' = 'drop' 4, 'constr Now we can use the newly created instances. @ ->>> putStrLn $ 'formatTSDeclarations' $ 'getTypeScriptDeclarations' (Proxy :: Proxy D) +>>> putStrLn $ 'formatTSDeclarations' $ 'getTypeScriptDeclarations' (Proxy :: Proxy (D T)) type D\ = INullary\ | IUnary\ | IProduct\ | IRecord\; From 2c8bb13c2f46eba912ec8bb0cb67f553bc4a986c Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 27 May 2021 20:25:44 -0600 Subject: [PATCH 078/208] Switch to string-interpolate and fix build on GHC 9.0.1 --- aeson-typescript.cabal | 14 ++++++-------- package.yaml | 6 +++--- src/Data/Aeson/TypeScript/Formatting.hs | 4 ++-- src/Data/Aeson/TypeScript/Instances.hs | 2 +- src/Data/Aeson/TypeScript/Lookup.hs | 2 +- src/Data/Aeson/TypeScript/TH.hs | 8 ++++++-- src/Data/Aeson/TypeScript/Util.hs | 8 ++++---- test/Basic.hs | 2 +- test/Formatting.hs | 2 +- test/HigherKind.hs | 2 +- test/Live.hs | 2 +- test/TestBoilerplate.hs | 2 +- test/TypeFamilies.hs | 2 +- test/Util.hs | 2 +- 14 files changed, 30 insertions(+), 28 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 7e24a47..a2b2edc 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -3,8 +3,6 @@ cabal-version: 1.12 -- This file has been generated from package.yaml by hpack version 0.34.4. -- -- see: https://github.com/sol/hpack --- --- hash: 73021b1b79e95ae7e4b67a45fafb5d452d8ab05ae59d4071098e8628cf8f29e4 name: aeson-typescript version: 0.3.0.0 @@ -18,9 +16,9 @@ maintainer: tom@codedown.io copyright: 2021 CodeDown license: BSD3 license-file: LICENSE -tested-with: - GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.2 build-type: Simple +tested-with: + GHC == 8.10.4, GHC == 8.10.3, GHC == 8.8.4, GHC == 8.8.3 extra-source-files: README.md CHANGELOG.md @@ -51,11 +49,11 @@ library aeson , base >=4.7 && <5 , containers - , interpolate , mtl + , string-interpolate , template-haskell , text - , th-abstraction <0.5 + , th-abstraction , unordered-containers default-language: Haskell2010 @@ -104,12 +102,12 @@ test-suite aeson-typescript-test , directory , filepath , hspec - , interpolate , mtl , process + , string-interpolate , template-haskell , temporary , text - , th-abstraction <0.5 + , th-abstraction , unordered-containers default-language: Haskell2010 diff --git a/package.yaml b/package.yaml index 45a746f..268ea54 100644 --- a/package.yaml +++ b/package.yaml @@ -23,17 +23,17 @@ synopsis: Generate TypeScript definition files from your ADTs # common to point users to the README.md file. description: Please see the README on Github at -tested-with: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.2 +tested-with: GHC == 8.10.4, GHC == 8.10.3, GHC == 8.8.4, GHC == 8.8.3 dependencies: - aeson - base >= 4.7 && < 5 - containers -- interpolate - mtl +- string-interpolate - template-haskell - text -- th-abstraction < 0.5 +- th-abstraction - unordered-containers library: diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index d70cc7e..d9e892f 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -3,7 +3,7 @@ module Data.Aeson.TypeScript.Formatting where import Data.Aeson.TypeScript.Types -import Data.String.Interpolate.IsString +import Data.String.Interpolate import qualified Data.Text as T #if !MIN_VERSION_base(4,11,0) @@ -26,7 +26,7 @@ formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVar 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};|] + enumType = [i|\n\ntype #{name} = keyof typeof #{typeNameModifier name};|] :: T.Text toEnumName = T.replace "\"" "" formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceName genericVariables members) = diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index 38d71fc..73cfc96 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -12,7 +12,7 @@ import Data.Data import Data.HashMap.Strict import qualified Data.List as L import Data.Set -import Data.String.Interpolate.IsString +import Data.String.Interpolate import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Data.Void diff --git a/src/Data/Aeson/TypeScript/Lookup.hs b/src/Data/Aeson/TypeScript/Lookup.hs index ff24e4b..151a4ca 100644 --- a/src/Data/Aeson/TypeScript/Lookup.hs +++ b/src/Data/Aeson/TypeScript/Lookup.hs @@ -19,7 +19,7 @@ import Control.Monad import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Types import Data.Proxy -import Data.String.Interpolate.IsString +import Data.String.Interpolate import Language.Haskell.TH hiding (stringE) import qualified Language.Haskell.TH.Lib as TH diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 182ab64..6a8496d 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -158,7 +158,7 @@ import qualified Data.List as L import qualified Data.Map as M import Data.Maybe import Data.Proxy -import Data.String.Interpolate.IsString +import Data.String.Interpolate import Data.Typeable import Language.Haskell.TH hiding (stringE) import Language.Haskell.TH.Datatype @@ -181,7 +181,7 @@ deriveTypeScript' options name extraOptions = do assertExtensionsTurnedOn datatypeInfo' -- Plug in generic variables for all star free variables - let starVars = [name | (isStarType -> Just name) <- getDataTypeVars datatypeInfo'] + let starVars = [name | (isStarType -> Just _) <- getDataTypeVars datatypeInfo'] let templateVarsToUse = case length starVars of 1 -> [ConT ''T] _ -> take (length starVars) allStarConstructors @@ -312,7 +312,11 @@ transformTypeFamilies eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) name' <- lift $ newName (nameBase typeFamilyName <> "'") f <- lift $ newName "f" +#if MIN_VERSION_template_haskell(2,17,0) + let inst1 = DataD [] name' [PlainTV f ()] Nothing [] [] +#else let inst1 = DataD [] name' [PlainTV f] Nothing [] [] +#endif tell [ExtraTopLevelDecs [inst1]] imageTypes <- lift $ getClosedTypeFamilyImage eqns diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index f8a1a26..46f9213 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -8,7 +8,7 @@ import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Types import qualified Data.List as L import Data.Proxy -import Data.String.Interpolate.IsString +import Data.String.Interpolate import qualified Data.Text as T import Language.Haskell.TH hiding (stringE) import Language.Haskell.TH.Datatype @@ -35,7 +35,7 @@ setDataTypeVars dti@(DatatypeInfo {}) vars = dti { datatypeVars = vars } dropLeadingIFromInterfaceName :: TSDeclaration -> TSDeclaration dropLeadingIFromInterfaceName decl@(TSInterfaceDeclaration {interfaceName=('I':xs)}) = decl { interfaceName = xs } dropLeadingIFromInterfaceName decl@(TSTypeAlternatives {typeName=('I':xs)}) = decl { typeName = xs } -dropLeadingIFromInterfaceName x = x +dropLeadingIFromInterfaceName x = x lastNameComponent :: String -> String lastNameComponent x = T.unpack $ last $ T.splitOn "." (T.pack x) @@ -92,8 +92,8 @@ assertExtensionsTurnedOn (DatatypeInfo {..}) = do -- Check that necessary language extensions are turned on scopedTypeVariablesEnabled <- isExtEnabled ScopedTypeVariables kindSignaturesEnabled <- isExtEnabled KindSignatures - when (not scopedTypeVariablesEnabled) $ error [i|The ScopedTypeVariables extension is required; please enable it before calling deriveTypeScript. (For example: put {-# LANGUAGE ScopedTypeVariables #-} at the top of the file.)|] - when ((not kindSignaturesEnabled) && (length datatypeVars > 0)) $ error [i|The KindSignatures extension is required since type #{datatypeName} is a higher order type; please enable it before calling deriveTypeScript. (For example: put {-# LANGUAGE KindSignatures #-} at the top of the file.)|] + when (not scopedTypeVariablesEnabled) $ error [i|The ScopedTypeVariables extension is required; please enable it before calling deriveTypeScript. (For example: put {-\# LANGUAGE ScopedTypeVariables \#-} at the top of the file.)|] + when ((not kindSignaturesEnabled) && (length datatypeVars > 0)) $ error [i|The KindSignatures extension is required since type #{datatypeName} is a higher order type; please enable it before calling deriveTypeScript. (For example: put {-\# LANGUAGE KindSignatures \#-} at the top of the file.)|] #else assertExtensionsTurnedOn _ = return () #endif diff --git a/test/Basic.hs b/test/Basic.hs index 3603210..d018fb0 100644 --- a/test/Basic.hs +++ b/test/Basic.hs @@ -19,7 +19,7 @@ import Data.Aeson as A import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types import Data.Proxy -import Data.String.Interpolate.IsString +import Data.String.Interpolate import Prelude hiding (Double) import Test.Hspec diff --git a/test/Formatting.hs b/test/Formatting.hs index 4c21654..136751d 100644 --- a/test/Formatting.hs +++ b/test/Formatting.hs @@ -9,7 +9,7 @@ import Data.Aeson (defaultOptions) import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types import Data.Proxy -import Data.String.Interpolate.IsString +import Data.String.Interpolate import Test.Hspec data D = S | F deriving (Eq, Show) diff --git a/test/HigherKind.hs b/test/HigherKind.hs index a2f82d5..63fe4e8 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -21,7 +21,7 @@ import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types import Data.Monoid import Data.Proxy -import Data.String.Interpolate.IsString +import Data.String.Interpolate import Prelude hiding (Double) import Test.Hspec import Util diff --git a/test/Live.hs b/test/Live.hs index 978a1a1..592cdec 100644 --- a/test/Live.hs +++ b/test/Live.hs @@ -23,7 +23,7 @@ import Data.Function import Data.Functor.Identity import Data.Kind import Data.Proxy -import Data.String.Interpolate.IsString +import Data.String.Interpolate import qualified Data.Text as T import Prelude hiding (Double) diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index d5d3c90..fc99e87 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -21,7 +21,7 @@ import Data.Aeson.TypeScript.TH import Data.Functor.Identity import Data.Kind import Data.Proxy -import Data.String.Interpolate.IsString +import Data.String.Interpolate import Language.Haskell.TH hiding (Type) import Test.Hspec import Util diff --git a/test/TypeFamilies.hs b/test/TypeFamilies.hs index 8847334..c9c483c 100644 --- a/test/TypeFamilies.hs +++ b/test/TypeFamilies.hs @@ -22,7 +22,7 @@ import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types import Data.Functor.Identity import Data.Proxy -import Data.String.Interpolate.IsString +import Data.String.Interpolate import qualified Data.Text as T import Prelude hiding (Double) import Test.Hspec diff --git a/test/Util.hs b/test/Util.hs index 17f171c..ea6dfe4 100644 --- a/test/Util.hs +++ b/test/Util.hs @@ -7,7 +7,7 @@ import Data.Aeson as A import Data.Aeson.TypeScript.TH import qualified Data.ByteString.Lazy as B import Data.Proxy -import Data.String.Interpolate.IsString +import Data.String.Interpolate import qualified Data.Text as T import System.Directory import System.Environment From 87afddecd6d1adf8eb5587078b57f4e74e326341 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 27 May 2021 20:41:44 -0600 Subject: [PATCH 079/208] Bump version to 0.3.0.1 --- CHANGELOG.md | 5 ++++- aeson-typescript.cabal | 4 ++-- package.yaml | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5bcd6f..e9dcff1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change log +## 0.3.0.1 + +* Support GHC 9.0.1 + ## 0.3.0.0 * Update th-abstraction dependency to < 0.5 to support working with newer Stack LTS. @@ -16,4 +20,3 @@ ## 0.1.0.0 * Initial release. - diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index a2b2edc..a6d4495 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: aeson-typescript -version: 0.3.0.0 +version: 0.3.0.1 synopsis: Generate TypeScript definition files from your ADTs description: Please see the README on Github at category: Text, Web, JSON @@ -18,7 +18,7 @@ license: BSD3 license-file: LICENSE build-type: Simple tested-with: - GHC == 8.10.4, GHC == 8.10.3, GHC == 8.8.4, GHC == 8.8.3 + GHC == 9.0.1, GHC == 8.10.4, GHC == 8.10.3, GHC == 8.8.4, GHC == 8.8.3 extra-source-files: README.md CHANGELOG.md diff --git a/package.yaml b/package.yaml index 268ea54..6d8bca2 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: aeson-typescript -version: 0.3.0.0 +version: 0.3.0.1 github: "codedownio/aeson-typescript" license: BSD3 category: Text, Web, JSON @@ -23,7 +23,7 @@ synopsis: Generate TypeScript definition files from your ADTs # common to point users to the README.md file. description: Please see the README on Github at -tested-with: GHC == 8.10.4, GHC == 8.10.3, GHC == 8.8.4, GHC == 8.8.3 +tested-with: GHC == 9.0.1, GHC == 8.10.4, GHC == 8.10.3, GHC == 8.8.4, GHC == 8.8.3 dependencies: - aeson From ee1a87fcab8a548c69e46685ce91465a7462be89 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 4 Aug 2021 01:34:15 -0700 Subject: [PATCH 080/208] Dedup predicates --- src/Data/Aeson/TypeScript/TH.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 6a8496d..c5626c8 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -212,7 +212,7 @@ deriveTypeScript' options name extraOptions = do $(listE $ fmap return types)|] let extraDecls = [x | ExtraDecl x <- extraDeclsOrGenericInfos] let extraTopLevelDecls = mconcat [x | ExtraTopLevelDecs x <- extraDeclsOrGenericInfos] - let predicates = constructorPreds <> constructorPreds' <> typeVariablePreds <> [x | ExtraConstraint x <- extraDeclsOrGenericInfos] + let predicates = L.nub (constructorPreds <> constructorPreds' <> typeVariablePreds <> [x | ExtraConstraint x <- extraDeclsOrGenericInfos]) declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] From 005609f3b617eded3232244668750027ad0abbfb Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 4 Aug 2021 18:35:19 -0700 Subject: [PATCH 081/208] More reducing redundant constraints --- src/Data/Aeson/TypeScript/TH.hs | 8 ++++++-- src/Data/Aeson/TypeScript/Util.hs | 7 +++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index c5626c8..15f0a44 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -191,9 +191,13 @@ deriveTypeScript' options name extraOptions = do -- Build constraints: a TypeScript constraint for every constructor type and one for every type variable. -- Probably overkill/not exactly right, but it's a start. let constructorPreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- mconcat $ fmap constructorFields (datatypeCons dti) - , hasFreeTypeVariable x] + , hasFreeTypeVariable x + , not $ coveredByDataTypeVars (getDataTypeVars dti) x + ] let constructorPreds' :: [Pred] = [AppT (ConT ''TypeScript) x | x <- mconcat $ fmap constructorFields (datatypeCons datatypeInfo') - , hasFreeTypeVariable x] + , hasFreeTypeVariable x + , not $ coveredByDataTypeVars (getDataTypeVars dti) x + ] let typeVariablePreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- getDataTypeVars dti] let eligibleGenericVars = catMaybes $ flip fmap (getDataTypeVars dti) $ \case diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index 46f9213..1fffb69 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -25,6 +25,13 @@ getDataTypeVars (DatatypeInfo {datatypeInstTypes}) = datatypeInstTypes getDataTypeVars (DatatypeInfo {datatypeVars}) = datatypeVars #endif +coveredByDataTypeVars :: [Type] -> Type -> Bool +-- Don't include a type found in a constructor if it's already found as a datatype var +coveredByDataTypeVars dataTypeVars candidate | candidate `L.elem` dataTypeVars = True +-- Don't include a type found in a constructor if the version with a simple star kind signature is already present +coveredByDataTypeVars dataTypeVars candidate | (SigT candidate StarT) `L.elem` dataTypeVars = True +coveredByDataTypeVars _ _ = False + setDataTypeVars :: DatatypeInfo -> [Type] -> DatatypeInfo #if MIN_VERSION_th_abstraction(0,3,0) setDataTypeVars dti@(DatatypeInfo {}) vars = dti { datatypeInstTypes = vars } From 70de71de15e658e779830f8c418a9a7c6a46c975 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 4 Aug 2021 18:55:35 -0700 Subject: [PATCH 082/208] Add some more basic instances --- src/Data/Aeson/TypeScript/Instances.hs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index 73cfc96..6c0b840 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -16,11 +16,14 @@ import Data.String.Interpolate import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Data.Void +import Data.Word +import GHC.Int #if !MIN_VERSION_base(4,11,0) import Data.Monoid #endif + instance TypeScript () where getTypeScriptType _ = "void" @@ -48,9 +51,18 @@ instance TypeScript Bool where instance TypeScript Int where getTypeScriptType _ = "number" +instance TypeScript Int32 where + getTypeScriptType _ = "number" + +instance TypeScript Int64 where + getTypeScriptType _ = "number" + instance TypeScript Char where getTypeScriptType _ = "string" +instance TypeScript Word8 where + getTypeScriptType _ = "number" + instance {-# OVERLAPPABLE #-} (TypeScript a) => TypeScript [a] where getTypeScriptType _ = (getTypeScriptType (Proxy :: Proxy a)) ++ "[]" getParentTypes _ = (TSType (Proxy :: Proxy a)) : (getParentTypes (Proxy :: Proxy a)) From b6137d9ef532fc12aa59decd163937925e3c14ac Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 4 Aug 2021 18:57:34 -0700 Subject: [PATCH 083/208] Export TSField in internal module --- src/Data/Aeson/TypeScript/Internal.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Data/Aeson/TypeScript/Internal.hs b/src/Data/Aeson/TypeScript/Internal.hs index eebfbb7..16a3cab 100644 --- a/src/Data/Aeson/TypeScript/Internal.hs +++ b/src/Data/Aeson/TypeScript/Internal.hs @@ -3,6 +3,7 @@ module Data.Aeson.TypeScript.Internal ( TSDeclaration(..) + , TSField(..) ) where import Data.Aeson.TypeScript.Types From 85ec32e3ad84dc2688526e7f0dfd855f3e6d9904 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 4 Aug 2021 19:01:38 -0700 Subject: [PATCH 084/208] Add Map and HashSet instances --- src/Data/Aeson/TypeScript/Instances.hs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index 6c0b840..04485ef 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -10,7 +10,9 @@ import qualified Data.Aeson as A import Data.Aeson.TypeScript.Types import Data.Data import Data.HashMap.Strict +import Data.HashSet import qualified Data.List as L +import Data.Map.Strict import Data.Set import Data.String.Interpolate import qualified Data.Text as T @@ -116,6 +118,11 @@ instance (TypeScript a) => TypeScript (Maybe a) where instance TypeScript A.Value where getTypeScriptType _ = "any"; +instance (TypeScript a, TypeScript b) => TypeScript (Map a b) where + getTypeScriptType _ = + "{[k: " ++ getTypeScriptType (Proxy :: Proxy a) ++ "]: " ++ getTypeScriptType (Proxy :: Proxy b) ++ "}" + getParentTypes _ = [TSType (Proxy :: Proxy a), TSType (Proxy :: Proxy b)] + instance (TypeScript a, TypeScript b) => TypeScript (HashMap a b) where getTypeScriptType _ = [i|{[k: #{getTypeScriptType (Proxy :: Proxy a)}]: #{getTypeScriptType (Proxy :: Proxy b)}}|] getParentTypes _ = L.nub ((getParentTypes (Proxy :: Proxy a)) @@ -124,3 +131,7 @@ instance (TypeScript a, TypeScript b) => TypeScript (HashMap a b) where instance (TypeScript a) => TypeScript (Set a) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) <> "[]"; getParentTypes _ = L.nub (getParentTypes (Proxy :: Proxy a)) + +instance (TypeScript a) => TypeScript (HashSet a) where + getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) ++ "[]" + getParentTypes _ = [TSType (Proxy :: Proxy a)] From 7e363e13d213f81481306cc7ea0223896d7d725b Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 4 Aug 2021 19:02:09 -0700 Subject: [PATCH 085/208] Fix warning --- src/Data/Aeson/TypeScript/Formatting.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index d9e892f..1edb6bf 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -35,7 +35,7 @@ formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceNa }|] where ls = T.intercalate "\n" $ fmap T.pack [(replicate numIndentSpaces ' ') <> formatTSField member <> ";"| member <- members] modifiedInterfaceName = (\(li, name) -> li <> interfaceNameModifier name) . splitAt 1 $ interfaceName -formatTSDeclaration (FormattingOptions {..}) (TSRawDeclaration text) = text +formatTSDeclaration _ (TSRawDeclaration text) = text exportPrefix :: ExportMode -> String exportPrefix ExportEach = "export " From ccf9d57f92a7b4eb5229a48f7988fcb3789011d2 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 8 Apr 2020 15:43:34 +0200 Subject: [PATCH 086/208] include immediate parents for containers... ...and exclude grandparents. The immediate parents are needed to make sure that we don't miss them when computing closures. The grandparents are not needed -- we'll get them through the immediate parents. --- src/Data/Aeson/TypeScript/Instances.hs | 44 +++++++++++--------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index 04485ef..fcfbeb6 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -67,7 +67,7 @@ instance TypeScript Word8 where instance {-# OVERLAPPABLE #-} (TypeScript a) => TypeScript [a] where getTypeScriptType _ = (getTypeScriptType (Proxy :: Proxy a)) ++ "[]" - getParentTypes _ = (TSType (Proxy :: Proxy a)) : (getParentTypes (Proxy :: Proxy a)) + getParentTypes _ = [TSType (Proxy :: Proxy a)] instance {-# OVERLAPPING #-} TypeScript [Char] where getTypeScriptType _ = "string" @@ -78,37 +78,30 @@ instance (TypeScript a, TypeScript b) => TypeScript (Either a b) where , TSInterfaceDeclaration "Left" ["T"] [TSField False "Left" "T"] , TSInterfaceDeclaration "Right" ["T"] [TSField False "Right" "T"] ] - getParentTypes _ = L.nub ((TSType (Proxy :: Proxy a)) - : (TSType (Proxy :: Proxy b)) - : (getParentTypes (Proxy :: Proxy a)) - <> (getParentTypes (Proxy :: Proxy b))) + getParentTypes _ = L.nub [ (TSType (Proxy :: Proxy a)) + , (TSType (Proxy :: Proxy b)) + ] instance (TypeScript a, TypeScript b) => TypeScript (a, b) where getTypeScriptType _ = [i|[#{getTypeScriptType (Proxy :: Proxy a)}, #{getTypeScriptType (Proxy :: Proxy b)}]|] - getParentTypes _ = L.nub ((TSType (Proxy :: Proxy a)) - : (TSType (Proxy :: Proxy b)) - : (getParentTypes (Proxy :: Proxy a)) - <> (getParentTypes (Proxy :: Proxy b))) + getParentTypes _ = L.nub [ (TSType (Proxy :: Proxy a)) + , (TSType (Proxy :: Proxy b)) + ] instance (TypeScript a, TypeScript b, TypeScript c) => TypeScript (a, b, c) where getTypeScriptType _ = [i|[#{getTypeScriptType (Proxy :: Proxy a)}, #{getTypeScriptType (Proxy :: Proxy b)}, #{getTypeScriptType (Proxy :: Proxy c)}]|] - getParentTypes _ = L.nub ((TSType (Proxy :: Proxy a)) - : (TSType (Proxy :: Proxy b)) - : (TSType (Proxy :: Proxy c)) - : (getParentTypes (Proxy :: Proxy a)) - <> (getParentTypes (Proxy :: Proxy b)) - <> (getParentTypes (Proxy :: Proxy c))) + getParentTypes _ = L.nub [ (TSType (Proxy :: Proxy a)) + , (TSType (Proxy :: Proxy b)) + , (TSType (Proxy :: Proxy c)) + ] instance (TypeScript a, TypeScript b, TypeScript c, TypeScript d) => TypeScript (a, b, c, d) where getTypeScriptType _ = [i|[#{getTypeScriptType (Proxy :: Proxy a)}, #{getTypeScriptType (Proxy :: Proxy b)}, #{getTypeScriptType (Proxy :: Proxy c)}, #{getTypeScriptType (Proxy :: Proxy d)}]|] - getParentTypes _ = L.nub ((TSType (Proxy :: Proxy a)) - : (TSType (Proxy :: Proxy b)) - : (TSType (Proxy :: Proxy c)) - : (TSType (Proxy :: Proxy d)) - : (getParentTypes (Proxy :: Proxy a)) - <> (getParentTypes (Proxy :: Proxy b)) - <> (getParentTypes (Proxy :: Proxy c)) - <> (getParentTypes (Proxy :: Proxy d))) + getParentTypes _ = L.nub [ (TSType (Proxy :: Proxy a)) + , (TSType (Proxy :: Proxy b)) + , (TSType (Proxy :: Proxy c)) + , (TSType (Proxy :: Proxy d)) + ] instance (TypeScript a) => TypeScript (Maybe a) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) @@ -125,12 +118,11 @@ instance (TypeScript a, TypeScript b) => TypeScript (Map a b) where instance (TypeScript a, TypeScript b) => TypeScript (HashMap a b) where getTypeScriptType _ = [i|{[k: #{getTypeScriptType (Proxy :: Proxy a)}]: #{getTypeScriptType (Proxy :: Proxy b)}}|] - getParentTypes _ = L.nub ((getParentTypes (Proxy :: Proxy a)) - <> (getParentTypes (Proxy :: Proxy b))) + getParentTypes _ = L.nub [TSType (Proxy :: Proxy a), TSType (Proxy :: Proxy b)] instance (TypeScript a) => TypeScript (Set a) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) <> "[]"; - getParentTypes _ = L.nub (getParentTypes (Proxy :: Proxy a)) + getParentTypes _ = [TSType (Proxy :: Proxy a)] instance (TypeScript a) => TypeScript (HashSet a) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) ++ "[]" From 64c03f3b8ffe15180f60d6cc72795f9ac8639798 Mon Sep 17 00:00:00 2001 From: Francesco Mazzoli Date: Wed, 8 Apr 2020 16:07:35 +0200 Subject: [PATCH 087/208] use mapped types to represent maps This allows to have unions as keys, which often happens when encoding variants made out of nullary constructors. --- src/Data/Aeson/TypeScript/Instances.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index fcfbeb6..7e50b51 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -112,12 +112,11 @@ instance TypeScript A.Value where getTypeScriptType _ = "any"; instance (TypeScript a, TypeScript b) => TypeScript (Map a b) where - getTypeScriptType _ = - "{[k: " ++ getTypeScriptType (Proxy :: Proxy a) ++ "]: " ++ getTypeScriptType (Proxy :: Proxy b) ++ "}" + getTypeScriptType _ = "{[k in " ++ getTypeScriptType (Proxy :: Proxy a) ++ "]?: " ++ getTypeScriptType (Proxy :: Proxy b) ++ "}" getParentTypes _ = [TSType (Proxy :: Proxy a), TSType (Proxy :: Proxy b)] instance (TypeScript a, TypeScript b) => TypeScript (HashMap a b) where - getTypeScriptType _ = [i|{[k: #{getTypeScriptType (Proxy :: Proxy a)}]: #{getTypeScriptType (Proxy :: Proxy b)}}|] + getTypeScriptType _ = [i|{[k in #{getTypeScriptType (Proxy :: Proxy a)}]?: #{getTypeScriptType (Proxy :: Proxy b)}}|] getParentTypes _ = L.nub [TSType (Proxy :: Proxy a), TSType (Proxy :: Proxy b)] instance (TypeScript a) => TypeScript (Set a) where From 3fb5d50e24f071875e48e5e9e43b6082774c50a9 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 10 Aug 2021 19:51:00 -0700 Subject: [PATCH 088/208] Support open type families --- aeson-typescript.cabal | 7 +- package.yaml | 2 +- src/Data/Aeson/TypeScript/TH.hs | 42 +++++---- ...{TypeFamilies.hs => ClosedTypeFamilies.hs} | 2 +- test/Live3.hs | 27 ++++++ test/Live4.hs | 33 +++++++ test/OpenTypeFamilies.hs | 91 +++++++++++++++++++ test/Spec.hs | 4 +- 8 files changed, 185 insertions(+), 23 deletions(-) rename test/{TypeFamilies.hs => ClosedTypeFamilies.hs} (98%) create mode 100644 test/Live3.hs create mode 100644 test/Live4.hs create mode 100644 test/OpenTypeFamilies.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index a6d4495..9b50c56 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -57,26 +57,29 @@ library , unordered-containers default-language: Haskell2010 -test-suite aeson-typescript-test +test-suite aeson-typescript-tests type: exitcode-stdio-1.0 main-is: Spec.hs other-modules: Basic + ClosedTypeFamilies Formatting HigherKind Live Live2 + Live3 + Live4 LiveLogging NoOmitNothingFields ObjectWithSingleFieldNoTagSingleConstructors ObjectWithSingleFieldTagSingleConstructors OmitNothingFields + OpenTypeFamilies TaggedObjectNoTagSingleConstructors TaggedObjectTagSingleConstructors TestBoilerplate TwoElemArrayNoTagSingleConstructors TwoElemArrayTagSingleConstructors - TypeFamilies UntaggedNoTagSingleConstructors UntaggedTagSingleConstructors Util diff --git a/package.yaml b/package.yaml index 6d8bca2..3788e3c 100644 --- a/package.yaml +++ b/package.yaml @@ -44,7 +44,7 @@ library: - Data.Aeson.TypeScript.Recursive tests: - aeson-typescript-test: + aeson-typescript-tests: main: Spec.hs source-dirs: - test diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 15f0a44..ebf66b6 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -312,30 +312,35 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@( transformTypeFamilies :: ExtraTypeScriptOptions -> Type -> WriterT [ExtraDeclOrGenericInfo] Q Type transformTypeFamilies eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) | name `L.elem` typeFamiliesToMapToTypeScript = lift (reify name) >>= \case - FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) eqns) _ -> do - name' <- lift $ newName (nameBase typeFamilyName <> "'") + FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) eqns) _ -> handle typeFamilyName eqns + FamilyI (OpenTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _)) decs -> handle typeFamilyName [eqn | TySynInstD eqn <- decs] + _ -> AppT (ConT name) <$> transformTypeFamilies eo typ + | otherwise = AppT (ConT name) <$> transformTypeFamilies eo typ + where + handle :: Name -> [TySynEqn] -> WriterT [ExtraDeclOrGenericInfo] Q Type + handle typeFamilyName eqns = do + name' <- lift $ newName (nameBase typeFamilyName <> "'") - f <- lift $ newName "f" + f <- lift $ newName "f" #if MIN_VERSION_template_haskell(2,17,0) - let inst1 = DataD [] name' [PlainTV f ()] Nothing [] [] + let inst1 = DataD [] name' [PlainTV f ()] Nothing [] [] #else - let inst1 = DataD [] name' [PlainTV f] Nothing [] [] + let inst1 = DataD [] name' [PlainTV f] Nothing [] [] #endif - tell [ExtraTopLevelDecs [inst1]] + tell [ExtraTopLevelDecs [inst1]] - imageTypes <- lift $ getClosedTypeFamilyImage eqns - inst2 <- lift $ [d|instance (Typeable g, TypeScript g) => TypeScript ($(conT name') g) where - getTypeScriptType _ = $(TH.stringE $ nameBase name) <> "[" <> (getTypeScriptType (Proxy :: Proxy g)) <> "]" - getTypeScriptDeclarations _ = [$(getClosedTypeFamilyInterfaceDecl name eqns)] - getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return x))|] | x <- imageTypes]) - |] - tell [ExtraTopLevelDecs inst2] + imageTypes <- lift $ getClosedTypeFamilyImage eqns + inst2 <- lift $ [d|instance (Typeable g, TypeScript g) => TypeScript ($(conT name') g) where + getTypeScriptType _ = $(TH.stringE $ nameBase name) <> "[" <> (getTypeScriptType (Proxy :: Proxy g)) <> "]" + getTypeScriptDeclarations _ = [$(getClosedTypeFamilyInterfaceDecl name eqns)] + getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return x))|] | x <- imageTypes]) + |] + tell [ExtraTopLevelDecs inst2] - tell [ExtraParentType (AppT (ConT name') (ConT ''T))] + tell [ExtraParentType (AppT (ConT name') (ConT ''T))] + + transformTypeFamilies eo (AppT (ConT name') typ) - transformTypeFamilies eo (AppT (ConT name') typ) - _ -> AppT (ConT name) <$> transformTypeFamilies eo typ - | otherwise = AppT (ConT name) <$> transformTypeFamilies eo typ transformTypeFamilies eo (AppT typ1 typ2) = AppT <$> transformTypeFamilies eo typ1 <*> transformTypeFamilies eo typ2 transformTypeFamilies eo (SigT typ kind) = flip SigT kind <$> transformTypeFamilies eo typ transformTypeFamilies eo (InfixT typ1 n typ2) = InfixT <$> transformTypeFamilies eo typ1 <*> pure n <*> transformTypeFamilies eo typ2 @@ -354,6 +359,9 @@ searchForConstraints eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) var FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) _) _ -> do tell [GenericInfo var (TypeFamilyKey typeFamilyName)] searchForConstraints eo typ var + FamilyI (OpenTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _)) _ -> do + tell [GenericInfo var (TypeFamilyKey typeFamilyName)] + searchForConstraints eo typ var _ -> searchForConstraints eo typ var | otherwise = searchForConstraints eo typ var searchForConstraints eo (AppT typ1 typ2) var = searchForConstraints eo typ1 var >> searchForConstraints eo typ2 var diff --git a/test/TypeFamilies.hs b/test/ClosedTypeFamilies.hs similarity index 98% rename from test/TypeFamilies.hs rename to test/ClosedTypeFamilies.hs index c9c483c..122f94f 100644 --- a/test/TypeFamilies.hs +++ b/test/ClosedTypeFamilies.hs @@ -14,7 +14,7 @@ {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-unused-top-binds #-} -module TypeFamilies (tests) where +module ClosedTypeFamilies (tests) where import Data.Aeson as A import Data.Aeson.TypeScript.Recursive diff --git a/test/Live3.hs b/test/Live3.hs new file mode 100644 index 0000000..d8673df --- /dev/null +++ b/test/Live3.hs @@ -0,0 +1,27 @@ +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilyDependencies #-} + +module Live3 where + +import Data.Aeson as A +import Data.Aeson.TypeScript.TH +import Data.Function +import Data.Proxy + + +data Test = TestBlah {x :: Int, y :: Bool} + +$(deriveTypeScript (A.defaultOptions { A.tagSingleConstructors = True }) ''Test) + +main :: IO () +main = getTypeScriptDeclarations (Proxy @Test) + & formatTSDeclarations + & putStrLn diff --git a/test/Live4.hs b/test/Live4.hs new file mode 100644 index 0000000..294f0e8 --- /dev/null +++ b/test/Live4.hs @@ -0,0 +1,33 @@ +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilyDependencies #-} + +module Live4 where + +import Data.Aeson as A +import Data.Aeson.TypeScript.Recursive +import Data.Aeson.TypeScript.TH +import Data.Function +import Data.Proxy +import TestBoilerplate + + +type family DeployEnvironment2 env = result | result -> env +type instance DeployEnvironment2 SingleNodeEnvironment = SingleDE +type instance DeployEnvironment2 K8SEnvironment = K8SDE +type instance DeployEnvironment2 T = () +newtype Simple env = Simple (DeployEnvironment2 env) +$(deriveTypeScript' A.defaultOptions ''Simple (ExtraTypeScriptOptions [''DeployEnvironment2])) + +main :: IO () +main = getTypeScriptDeclarationsRecursively (Proxy @(Simple T)) + & formatTSDeclarations + & putStrLn diff --git a/test/OpenTypeFamilies.hs b/test/OpenTypeFamilies.hs new file mode 100644 index 0000000..222ef15 --- /dev/null +++ b/test/OpenTypeFamilies.hs @@ -0,0 +1,91 @@ +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeFamilyDependencies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} + +module OpenTypeFamilies (tests) where + +import Data.Aeson as A +import Data.Aeson.TypeScript.Recursive +import Data.Aeson.TypeScript.TH +import Data.Aeson.TypeScript.Types +import Data.Functor.Identity +import Data.Proxy +import Data.String.Interpolate +import qualified Data.Text as T +import Prelude hiding (Double) +import Test.Hspec +import TestBoilerplate + + +type family DeployEnvironment env = result | result -> env +type instance DeployEnvironment SingleNodeEnvironment = SingleDE +type instance DeployEnvironment K8SEnvironment = K8SDE +type instance DeployEnvironment T = () +data UserT env f = User { + _userUsername :: Columnar f T.Text + , _userCreatedAt :: Columnar f Int + , _userDeployEnvironment :: Columnar f (DeployEnvironment env) + } +$(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) + +type family DeployEnvironment2 env = result | result -> env +type instance DeployEnvironment2 SingleNodeEnvironment = SingleDE +type instance DeployEnvironment2 K8SEnvironment = K8SDE +type instance DeployEnvironment2 T = () +newtype Simple env = Simple (DeployEnvironment2 env) +$(deriveTypeScript' A.defaultOptions ''Simple (ExtraTypeScriptOptions [''DeployEnvironment2])) + +tests :: SpecWith () +tests = describe "Type families" $ do + describe "simple newtype" $ do + it [i|makes the declaration and types correctly|] $ do + (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Simple T))) `shouldBe` ([ + TSInterfaceDeclaration "DeployEnvironment2" [] [ + TSField False "\"single_node_env\"" "\"single\"" + , TSField False "\"k8s_env\"" "\"k8s\"" + , TSField False "T" "void" + ] + , TSTypeAlternatives "ISimple" ["T extends keyof DeployEnvironment2"] ["DeployEnvironment2[T]"] + , TSTypeAlternatives "Simple" ["T extends keyof DeployEnvironment2"] ["ISimple"] + ]) + + describe "Complicated Beam-like user type" $ do + it [i|makes the declaration and types correctly|] $ do + (getTypeScriptDeclarations (Proxy :: Proxy (UserT T Identity))) `shouldBe` ([ + TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] + , TSInterfaceDeclaration "IUser" ["T extends keyof DeployEnvironment"] [ + TSField False "_userUsername" "string" + , TSField False "_userCreatedAt" "number" + , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" + ] + ]) + + it [i|get the declarations recursively|] $ do + (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (UserT T Identity))) `shouldBe` ([ + TSInterfaceDeclaration "DeployEnvironment" [] [ + TSField False "\"single_node_env\"" "\"single\"" + , TSField False "\"k8s_env\"" "\"k8s\"" + , TSField False "T" "void" + ] + , TSInterfaceDeclaration "IUser" ["T extends keyof DeployEnvironment"] [ + TSField False "_userUsername" "string" + , TSField False "_userCreatedAt" "number" + , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" + ] + , TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] + ]) + +main :: IO () +main = hspec tests diff --git a/test/Spec.hs b/test/Spec.hs index fb862b7..9fbee20 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -5,7 +5,7 @@ import Test.Hspec import qualified Formatting import qualified HigherKind -import qualified TypeFamilies +import qualified ClosedTypeFamilies import qualified ObjectWithSingleFieldNoTagSingleConstructors import qualified ObjectWithSingleFieldTagSingleConstructors @@ -23,7 +23,7 @@ main :: IO () main = hspec $ do Formatting.tests HigherKind.tests - TypeFamilies.tests + ClosedTypeFamilies.tests ObjectWithSingleFieldTagSingleConstructors.tests ObjectWithSingleFieldNoTagSingleConstructors.tests From bfd68abdbc507c802d5fd5ec797673ac4f94d349 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 10 Aug 2021 20:10:21 -0700 Subject: [PATCH 089/208] Add CPP for another TH breaking change --- src/Data/Aeson/TypeScript/TH.hs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index ebf66b6..5f71db2 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -313,7 +313,14 @@ transformTypeFamilies :: ExtraTypeScriptOptions -> Type -> WriterT [ExtraDeclOrG transformTypeFamilies eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) | name `L.elem` typeFamiliesToMapToTypeScript = lift (reify name) >>= \case FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) eqns) _ -> handle typeFamilyName eqns + + +#if MIN_VERSION_template_haskell(2,15,0) FamilyI (OpenTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _)) decs -> handle typeFamilyName [eqn | TySynInstD eqn <- decs] +#else + FamilyI (OpenTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _)) decs -> handle typeFamilyName [eqn | TySynInstD _name eqn <- decs] +#endif + _ -> AppT (ConT name) <$> transformTypeFamilies eo typ | otherwise = AppT (ConT name) <$> transformTypeFamilies eo typ where From d6b3b10550f7bb1c7b4072beb4e6ac03a750da89 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 10 Aug 2021 20:10:41 -0700 Subject: [PATCH 090/208] Try handling promoted types --- src/Data/Aeson/TypeScript/Lookup.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/Lookup.hs b/src/Data/Aeson/TypeScript/Lookup.hs index 151a4ca..5c9dd1f 100644 --- a/src/Data/Aeson/TypeScript/Lookup.hs +++ b/src/Data/Aeson/TypeScript/Lookup.hs @@ -44,10 +44,13 @@ getClosedTypeFamilyInterfaceDecl name eqns = do fields <- forM eqns $ \case #if MIN_VERSION_template_haskell(2,15,0) TySynEqn Nothing (AppT (ConT _) (ConT arg)) result -> do + [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) |] + TySynEqn Nothing (AppT (ConT _) (PromotedT arg)) result -> do + [| TSField False (getTypeScriptType (Proxy :: Proxy $(promotedT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) |] #else TySynEqn [ConT arg] result -> do -#endif [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) |] +#endif x -> fail [i|aeson-typescript doesn't know yet how to handle this type family equation: '#{x}'|] [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] $(listE $ fmap return fields) |] From 2196c84a01141c1ad0ca5f3cf8d4db14d1bd26b5 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 11 Aug 2021 09:50:59 -0700 Subject: [PATCH 091/208] Last few changes --- aeson-typescript.cabal | 4 ---- {test => dev}/Live.hs | 0 {test => dev}/Live2.hs | 0 {test => dev}/Live3.hs | 0 {test => dev}/Live4.hs | 0 src/Data/Aeson/TypeScript/Lookup.hs | 2 +- 6 files changed, 1 insertion(+), 5 deletions(-) rename {test => dev}/Live.hs (100%) rename {test => dev}/Live2.hs (100%) rename {test => dev}/Live3.hs (100%) rename {test => dev}/Live4.hs (100%) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 9b50c56..f43507e 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -65,10 +65,6 @@ test-suite aeson-typescript-tests ClosedTypeFamilies Formatting HigherKind - Live - Live2 - Live3 - Live4 LiveLogging NoOmitNothingFields ObjectWithSingleFieldNoTagSingleConstructors diff --git a/test/Live.hs b/dev/Live.hs similarity index 100% rename from test/Live.hs rename to dev/Live.hs diff --git a/test/Live2.hs b/dev/Live2.hs similarity index 100% rename from test/Live2.hs rename to dev/Live2.hs diff --git a/test/Live3.hs b/dev/Live3.hs similarity index 100% rename from test/Live3.hs rename to dev/Live3.hs diff --git a/test/Live4.hs b/dev/Live4.hs similarity index 100% rename from test/Live4.hs rename to dev/Live4.hs diff --git a/src/Data/Aeson/TypeScript/Lookup.hs b/src/Data/Aeson/TypeScript/Lookup.hs index 5c9dd1f..ccb1f32 100644 --- a/src/Data/Aeson/TypeScript/Lookup.hs +++ b/src/Data/Aeson/TypeScript/Lookup.hs @@ -59,7 +59,7 @@ getClosedTypeFamilyImage :: [TySynEqn] -> Q [Type] getClosedTypeFamilyImage eqns = do forM eqns $ \case #if MIN_VERSION_template_haskell(2,15,0) - TySynEqn Nothing (AppT (ConT _) (ConT _)) result -> return result + TySynEqn Nothing (AppT (ConT _) _) result -> return result #else TySynEqn [ConT _] result -> return result #endif From 998250343c19dc551342786637f2512bb5ea9797 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 11 Aug 2021 09:58:53 -0700 Subject: [PATCH 092/208] Move dev files --- aeson-typescript.cabal | 7 ++++ dev/Live5.hs | 80 ++++++++++++++++++++++++++++++++++++++++++ dev/Live6.hs | 32 +++++++++++++++++ package.yaml | 1 + 4 files changed, 120 insertions(+) create mode 100644 dev/Live5.hs create mode 100644 dev/Live6.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index f43507e..9f74c31 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -87,10 +87,17 @@ test-suite aeson-typescript-tests Data.Aeson.TypeScript.TH Data.Aeson.TypeScript.Types Data.Aeson.TypeScript.Util + Live + Live2 + Live3 + Live4 + Live5 + Live6 Paths_aeson_typescript hs-source-dirs: test src + dev ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N build-depends: aeson diff --git a/dev/Live5.hs b/dev/Live5.hs new file mode 100644 index 0000000..31082d3 --- /dev/null +++ b/dev/Live5.hs @@ -0,0 +1,80 @@ +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeInType #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilyDependencies #-} +{-# LANGUAGE DeriveGeneric #-} + +module Live5 where + +import Data.Aeson as A +import Data.Aeson.TypeScript.Recursive +import Data.Aeson.TypeScript.TH +import Data.Function +import Data.Kind as Kind +import Data.Proxy +import qualified Data.Text as T +import Data.Typeable +import Data.Void +import GHC.Generics +import TestBoilerplate + + +-- data From = FromServer | FromClient +-- data MethodType = Notification | Request + +-- data Method (f :: From) (t :: MethodType) where +-- Login :: Method 'FromClient 'Request +-- ReportClick :: Method 'FromClient 'Notification + +-- instance TypeScript Login where getTypeScriptType _ = "asdf" +-- instance TypeScript ReportClick where getTypeScriptType _ = "fdsa" + +-- data LoginParams = LoginParams { +-- loginUsername :: T.Text +-- , loginPassword :: T.Text +-- } +-- $(deriveJSONAndTypeScript A.defaultOptions ''LoginParams) + + +-- data ReportClickParams = ReportClickParams { +-- reportClickX :: Int +-- , reportClickY :: Int +-- } +-- $(deriveJSONAndTypeScript A.defaultOptions ''ReportClickParams) + +-- type family MessageParams (m :: Method f t) :: Kind.Type where +-- MessageParams 'Login = LoginParams +-- MessageParams 'ReportClick = ReportClickParams + +-- data SMethod (m :: Method f t) where +-- SLogin :: SMethod 'Login +-- SReportClick :: SMethod 'ReportClick + +-- data RequestMessage (m :: Method f 'Request) = +-- RequestMessage { +-- _id :: T.Text +-- , _method :: SMethod m +-- , _params :: MessageParams m +-- } + +-- data LoginResult = LoginResult { profilePicture :: T.Text } +-- $(deriveJSONAndTypeScript A.defaultOptions ''LoginResult) + +-- type family ResponseResult (m :: Method f 'Request) :: Kind.Type where +-- ResponseResult 'Login = LoginResult +-- ResponseResult _ = Void + +-- deriveTypeScript' A.defaultOptions ''RequestMessage (ExtraTypeScriptOptions [''MessageParams]) + +-- -- main :: IO () +-- -- main = getTypeScriptDeclarationsRecursively (Proxy @(RequestMessage (Method FromClient Request))) +-- -- & formatTSDeclarations +-- -- & putStrLn diff --git a/dev/Live6.hs b/dev/Live6.hs new file mode 100644 index 0000000..546e58f --- /dev/null +++ b/dev/Live6.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeInType #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilyDependencies #-} +{-# LANGUAGE DeriveGeneric #-} + +module Live6 where + +import Data.Aeson as A +import Data.Aeson.TypeScript.Recursive +import Data.Aeson.TypeScript.TH +import Data.Function +import Data.Proxy + + +data BulkCommandNoArg k = BulkCommandNoArg { + bulkCommandNoArgKeys :: [k] + } deriving (Show) +$(deriveJSONAndTypeScript defaultOptions ''BulkCommandNoArg) + + +main :: IO () +main = getTypeScriptDeclarations (Proxy @(BulkCommandNoArg Int)) + & formatTSDeclarations + & putStrLn diff --git a/package.yaml b/package.yaml index 3788e3c..f08c29a 100644 --- a/package.yaml +++ b/package.yaml @@ -49,6 +49,7 @@ tests: source-dirs: - test - src + - dev ghc-options: - -Wall - -threaded From 575282406712b3878f1f482a2a72357e2d02ebc3 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 11 Aug 2021 10:20:55 -0700 Subject: [PATCH 093/208] Add failing test for generic instance in interface --- aeson-typescript.cabal | 1 + test/Generic.hs | 44 ++++++++++++++++++++++++++++++++++++++++++ test/Spec.hs | 2 ++ 3 files changed, 47 insertions(+) create mode 100644 test/Generic.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 9f74c31..52b9c70 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -64,6 +64,7 @@ test-suite aeson-typescript-tests Basic ClosedTypeFamilies Formatting + Generic HigherKind LiveLogging NoOmitNothingFields diff --git a/test/Generic.hs b/test/Generic.hs new file mode 100644 index 0000000..c165b48 --- /dev/null +++ b/test/Generic.hs @@ -0,0 +1,44 @@ +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeFamilyDependencies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} + +module Generic (tests) where + +import Data.Aeson as A +import Data.Aeson.TypeScript.Recursive +import Data.Aeson.TypeScript.TH +import Data.Aeson.TypeScript.Types +import Data.Functor.Identity +import Data.Proxy +import Data.String.Interpolate +import qualified Data.Text as T +import Prelude hiding (Double) +import Test.Hspec + + +data Complex a = Product Int a | Unary Int deriving Eq +$(deriveTypeScript defaultOptions ''Complex) + +tests :: SpecWith () +tests = describe "Generic instances" $ do + it [i|makes the declaration and types correctly|] $ do + (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex String))) `shouldBe` [ + TSInterfaceDeclaration {interfaceName = "IProduct", interfaceGenericVariables = ["T"], interfaceMembers = [TSField {fieldOptional = False, fieldName = "tag", fieldType = "\"Product\""},TSField {fieldOptional = False, fieldName = "contents", fieldType = "[number, T]"}]} + ,TSInterfaceDeclaration {interfaceName = "IUnary", interfaceGenericVariables = ["T"], interfaceMembers = [TSField {fieldOptional = False, fieldName = "tag", fieldType = "\"Unary\""},TSField {fieldOptional = False, fieldName = "contents", fieldType = "number"}]} + ,TSTypeAlternatives {typeName = "Complex", typeGenericVariables = ["T"], alternativeTypes = ["IProduct","IUnary"]} + ] + +main :: IO () +main = hspec tests diff --git a/test/Spec.hs b/test/Spec.hs index 9fbee20..cdff2a1 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -4,6 +4,7 @@ module Main where import Test.Hspec import qualified Formatting +import qualified Generic import qualified HigherKind import qualified ClosedTypeFamilies @@ -22,6 +23,7 @@ import qualified NoOmitNothingFields main :: IO () main = hspec $ do Formatting.tests + Generic.tests HigherKind.tests ClosedTypeFamilies.tests From 5cb8c2569bdc88e839451762a530bd67e7e27bef Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 11 Aug 2021 10:23:52 -0700 Subject: [PATCH 094/208] Bump copyright years --- LICENSE | 2 +- src/Data/Aeson/TypeScript/TH.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 1d753c9..5dfaa25 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright Tom McLaughlin (c) 2017 +Copyright Tom McLaughlin (c) 2021 All rights reserved. diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 5f71db2..bf1e86c 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -15,7 +15,7 @@ {-| Module: Data.Aeson.TypeScript.TH -Copyright: (c) 2018 Tom McLaughlin +Copyright: (c) 2021 Tom McLaughlin License: BSD3 Stability: experimental Portability: portable From ff7ce983a1f25ce262299e050e2fb28fae1cefd4 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 11 Aug 2021 12:18:41 -0700 Subject: [PATCH 095/208] Propagate T variables in declarations correctly --- dev/Live6.hs | 25 ++++++++--- src/Data/Aeson/TypeScript/TH.hs | 14 +++--- src/Data/Aeson/TypeScript/Types.hs | 9 +++- src/Data/Aeson/TypeScript/Util.hs | 71 +++++++++++++++++++++++------- test/Generic.hs | 13 ++++-- 5 files changed, 99 insertions(+), 33 deletions(-) diff --git a/dev/Live6.hs b/dev/Live6.hs index 546e58f..10215aa 100644 --- a/dev/Live6.hs +++ b/dev/Live6.hs @@ -20,13 +20,24 @@ import Data.Function import Data.Proxy -data BulkCommandNoArg k = BulkCommandNoArg { - bulkCommandNoArgKeys :: [k] - } deriving (Show) -$(deriveJSONAndTypeScript defaultOptions ''BulkCommandNoArg) +data Complex a = Product Int a | Unary Int deriving Eq + +data Complex2 a = Product2 Int a + +-- data BulkCommandNoArg k = BulkCommandNoArg { +-- bulkCommandNoArgKeys :: [k] +-- } deriving (Show) +-- $(deriveTypeScript defaultOptions ''BulkCommandNoArg) + +$(deriveTypeScript defaultOptions ''Complex) + +$(deriveTypeScript (defaultOptions { sumEncoding = UntaggedValue }) ''Complex2) main :: IO () -main = getTypeScriptDeclarations (Proxy @(BulkCommandNoArg Int)) - & formatTSDeclarations - & putStrLn +-- main = printThing (Proxy @(BulkCommandNoArg Int)) +main = printThing (Proxy @(Complex2 String)) + +printThing x = getTypeScriptDeclarations x + & formatTSDeclarations + & putStrLn diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index bf1e86c..fe997de 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -205,7 +205,7 @@ deriveTypeScript' options name extraOptions = do _ -> Nothing genericVariablesAndSuffixes <- forM eligibleGenericVars $ \var -> do (_, genericInfos) <- runWriterT $ forM_ (datatypeCons datatypeInfo') $ \ci -> - forM_ (namesAndTypes options ci) $ \(_, typ) -> do + forM_ (namesAndTypes options [] ci) $ \(_, typ) -> do searchForConstraints extraOptions typ var return (var, unifyGenericVariable genericInfos) @@ -237,7 +237,7 @@ deriveTypeScript' options name extraOptions = do -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration handleConstructor :: Options -> ExtraTypeScriptOptions -> DatatypeInfo -> [(Name, String)] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp -handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = +handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = do if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> do writeSingleConstructorEncoding brackets <- lift $ getBracketsExpression False genericVariables @@ -287,17 +287,17 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@( interfaceName = "I" <> (lastNameComponent' $ constructorName ci) tupleEncoding = do - tupleType <- transformTypeFamilies extraOptions (contentsTupleType ci) - lift $ [|TSTypeAlternatives $(TH.stringE interfaceName) - $(genericVariablesListExpr True genericVariables) - [getTypeScriptType (Proxy :: Proxy $(return tupleType))]|] + tupleType <- transformTypeFamilies extraOptions (contentsTupleTypeSubstituted genericVariables ci) + lift [|TSTypeAlternatives $(TH.stringE interfaceName) + $(genericVariablesListExpr True genericVariables) + [getTypeScriptType (Proxy :: Proxy $(return tupleType))]|] assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) $(genericVariablesListExpr True genericVariables) $(return members)|] getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] - getTSFields = forM (namesAndTypes options ci) $ \(nameString, typ') -> do + getTSFields = forM (namesAndTypes options genericVariables ci) $ \(nameString, typ') -> do typ <- transformTypeFamilies extraOptions typ' when (typ /= typ') $ do let constraint = AppT (ConT ''TypeScript) typ diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 154eea2..6e240ee 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -1,4 +1,11 @@ -{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, ExistentialQuantification, PolyKinds, StandaloneDeriving #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE StandaloneDeriving #-} module Data.Aeson.TypeScript.Types where diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index 1fffb69..39b6420 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -1,4 +1,15 @@ -{-# LANGUAGE CPP, QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, ExistentialQuantification, FlexibleInstances, NamedFieldPuns, MultiWayIf, ViewPatterns, PolyKinds #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE PolyKinds #-} module Data.Aeson.TypeScript.Util where @@ -18,6 +29,7 @@ import qualified Language.Haskell.TH.Lib as TH import Data.Monoid #endif + getDataTypeVars :: DatatypeInfo -> [Type] #if MIN_VERSION_th_abstraction(0,3,0) getDataTypeVars (DatatypeInfo {datatypeInstTypes}) = datatypeInstTypes @@ -63,7 +75,7 @@ isConstructorNullary (ConstructorInfo {constructorVariant, constructorFields}) = -- In earlier versions, it has constructors getDatatypePredicate :: Type -> Pred #if MIN_VERSION_template_haskell(2,10,0) -getDatatypePredicate typ = AppT (ConT ''TypeScript) typ +getDatatypePredicate = AppT (ConT ''TypeScript) #else getDatatypePredicate typ = ClassP ''TypeScript [typ] #endif @@ -87,7 +99,7 @@ applyToArgsE f (x:xs) = applyToArgsE (AppE f x) xs -- Between Aeson 1.1.2.0 and 1.2.0.0, tagSingleConstructors was added getTagSingleConstructors :: Options -> Bool #if MIN_VERSION_aeson(1,2,0) -getTagSingleConstructors options = tagSingleConstructors options +getTagSingleConstructors = tagSingleConstructors #else getTagSingleConstructors _ = False #endif @@ -99,8 +111,10 @@ assertExtensionsTurnedOn (DatatypeInfo {..}) = do -- Check that necessary language extensions are turned on scopedTypeVariablesEnabled <- isExtEnabled ScopedTypeVariables kindSignaturesEnabled <- isExtEnabled KindSignatures - when (not scopedTypeVariablesEnabled) $ error [i|The ScopedTypeVariables extension is required; please enable it before calling deriveTypeScript. (For example: put {-\# LANGUAGE ScopedTypeVariables \#-} at the top of the file.)|] - when ((not kindSignaturesEnabled) && (length datatypeVars > 0)) $ error [i|The KindSignatures extension is required since type #{datatypeName} is a higher order type; please enable it before calling deriveTypeScript. (For example: put {-\# LANGUAGE KindSignatures \#-} at the top of the file.)|] + unless scopedTypeVariablesEnabled $ + error [i|The ScopedTypeVariables extension is required; please enable it before calling deriveTypeScript. (For example: put {-\# LANGUAGE ScopedTypeVariables \#-} at the top of the file.)|] + unless (kindSignaturesEnabled || (L.null datatypeVars)) $ + error [i|The KindSignatures extension is required since type #{datatypeName} is a higher order type; please enable it before calling deriveTypeScript. (For example: put {-\# LANGUAGE KindSignatures \#-} at the top of the file.)|] #else assertExtensionsTurnedOn _ = return () #endif @@ -126,19 +140,19 @@ isUntaggedValue _ = False -- Between Template Haskell 2.10 and 2.11, InstanceD got an additional argument mkInstance :: Cxt -> Type -> [Dec] -> Dec #if MIN_VERSION_template_haskell(2,11,0) -mkInstance context typ decs = InstanceD Nothing context typ decs +mkInstance = InstanceD Nothing #else -mkInstance context typ decs = InstanceD context typ decs +mkInstance = InstanceD #endif -namesAndTypes :: Options -> ConstructorInfo -> [(String, Type)] -namesAndTypes options ci = case constructorVariant ci of +namesAndTypes :: Options -> [(Name, String)] -> ConstructorInfo -> [(String, Type)] +namesAndTypes options genericVariables ci = case constructorVariant ci of RecordConstructor names -> zip (fmap ((fieldLabelModifier options) . lastNameComponent') names) (constructorFields ci) _ -> case sumEncoding options of TaggedObject _ contentsFieldName | isConstructorNullary ci -> [] - | otherwise -> [(contentsFieldName, contentsTupleType ci)] - _ -> [(constructorNameToUse options ci, contentsTupleType ci)] + | otherwise -> [(contentsFieldName, contentsTupleTypeSubstituted genericVariables ci)] + _ -> [(constructorNameToUse options ci, contentsTupleTypeSubstituted genericVariables ci)] constructorNameToUse :: Options -> ConstructorInfo -> String constructorNameToUse options ci = (constructorTagModifier options) $ lastNameComponent' (constructorName ci) @@ -146,10 +160,37 @@ constructorNameToUse options ci = (constructorTagModifier options) $ lastNameCom -- | Get the type of a tuple of constructor fields, as when we're packing a record-less constructor into a list contentsTupleType :: ConstructorInfo -> Type contentsTupleType ci = let fields = constructorFields ci in - case length fields of - 0 -> AppT ListT (ConT ''()) - 1 -> head fields - x -> applyToArgsT (ConT $ tupleTypeName x) fields + case fields of + [] -> AppT ListT (ConT ''()) + [x] -> x + xs-> applyToArgsT (ConT $ tupleTypeName (L.length xs)) fields + +contentsTupleTypeSubstituted :: [(Name, String)] -> ConstructorInfo -> Type +contentsTupleTypeSubstituted genericVariables ci = let fields = constructorFields ci in + case fields of + [] -> AppT ListT (ConT ''()) + [x] -> mapType x + xs -> applyToArgsT (ConT $ tupleTypeName (L.length xs)) (fmap mapType xs) + where + mapType x@(VarT name) = tryPromote x name + mapType x@(ConT name) = tryPromote x name + mapType x@(PromotedT name) = tryPromote x name + mapType x = x + + tryPromote _ (flip L.lookup genericVariables -> Just "") = ConT ''T + tryPromote _ (flip L.lookup genericVariables -> Just "T") = ConT ''T + tryPromote _ (flip L.lookup genericVariables -> Just "T1") = ConT ''T1 + tryPromote _ (flip L.lookup genericVariables -> Just "T2") = ConT ''T2 + tryPromote _ (flip L.lookup genericVariables -> Just "T3") = ConT ''T3 + tryPromote _ (flip L.lookup genericVariables -> Just "T4") = ConT ''T4 + tryPromote _ (flip L.lookup genericVariables -> Just "T5") = ConT ''T5 + tryPromote _ (flip L.lookup genericVariables -> Just "T6") = ConT ''T6 + tryPromote _ (flip L.lookup genericVariables -> Just "T7") = ConT ''T7 + tryPromote _ (flip L.lookup genericVariables -> Just "T8") = ConT ''T8 + tryPromote _ (flip L.lookup genericVariables -> Just "T9") = ConT ''T9 + tryPromote _ (flip L.lookup genericVariables -> Just "T10") = ConT ''T10 + tryPromote x _ = x + getBracketsExpression :: Bool -> [(Name, String)] -> Q Exp getBracketsExpression _ [] = [|""|] diff --git a/test/Generic.hs b/test/Generic.hs index c165b48..e2f9532 100644 --- a/test/Generic.hs +++ b/test/Generic.hs @@ -20,10 +20,8 @@ import Data.Aeson as A import Data.Aeson.TypeScript.Recursive import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types -import Data.Functor.Identity import Data.Proxy import Data.String.Interpolate -import qualified Data.Text as T import Prelude hiding (Double) import Test.Hspec @@ -31,14 +29,23 @@ import Test.Hspec data Complex a = Product Int a | Unary Int deriving Eq $(deriveTypeScript defaultOptions ''Complex) +data Complex2 a = Product2 Int a +$(deriveTypeScript (defaultOptions { sumEncoding = UntaggedValue }) ''Complex2) + tests :: SpecWith () tests = describe "Generic instances" $ do - it [i|makes the declaration and types correctly|] $ do + it [i|Complex makes the declaration and types correctly|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex String))) `shouldBe` [ TSInterfaceDeclaration {interfaceName = "IProduct", interfaceGenericVariables = ["T"], interfaceMembers = [TSField {fieldOptional = False, fieldName = "tag", fieldType = "\"Product\""},TSField {fieldOptional = False, fieldName = "contents", fieldType = "[number, T]"}]} ,TSInterfaceDeclaration {interfaceName = "IUnary", interfaceGenericVariables = ["T"], interfaceMembers = [TSField {fieldOptional = False, fieldName = "tag", fieldType = "\"Unary\""},TSField {fieldOptional = False, fieldName = "contents", fieldType = "number"}]} ,TSTypeAlternatives {typeName = "Complex", typeGenericVariables = ["T"], alternativeTypes = ["IProduct","IUnary"]} ] + it [i|Complex2 makes the declaration and types correctly|] $ do + (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex2 String))) `shouldBe` [ + TSTypeAlternatives {typeName = "Complex2", typeGenericVariables = ["T"], alternativeTypes = ["IProduct2"]} + ,TSTypeAlternatives {typeName = "IProduct2", typeGenericVariables = ["T"], alternativeTypes = ["[number, T]"]} + ] + main :: IO () main = hspec tests From 0819fbe6dfefd54fd04e459df6e4de11da69676e Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 11 Aug 2021 12:19:05 -0700 Subject: [PATCH 096/208] Switch resolver to 18.3 --- stack.yaml | 3 ++- stack.yaml.lock | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/stack.yaml b/stack.yaml index 11224c2..2b993a4 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,5 +1,6 @@ -resolver: lts-16.16 +resolver: lts-18.3 +compiler: ghc-8.10.5 packages: - . diff --git a/stack.yaml.lock b/stack.yaml.lock index 201ec49..7940767 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -6,7 +6,7 @@ packages: [] snapshots: - completed: - size: 532380 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/16/16.yaml - sha256: d6b004b095fe2a0b8b14fbc30014ee97e58843b9c9362ddb9244273dda62649e - original: lts-16.16 + size: 585603 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/3.yaml + sha256: 694573e96dca34db5636edb1fe6c96bb233ca0f9fb96c1ead1671cdfa9bd73e9 + original: lts-18.3 From 8c75c944b8fc066feb079d23b5e0654a38d3d044 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 11 Aug 2021 12:23:50 -0700 Subject: [PATCH 097/208] Add notes to CHANGELOG under 0.4.0.0 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9dcff1..1ec9a7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Change log +## 0.4.0.0 + +* Add new built-in instances (Word8, Int32, Int64, Map, HashSet) +* Export TSField in the Internal module +* Avoid producing redundant constraints (for fewer warnings when using -Wredundant-constraints) +* Encode maps as mapped types (allows you to have unions as keys) +* Support mapping open type families to lookup types (+ progress on handling promoted types) +* Improve propagation of T variables in declarations + ## 0.3.0.1 * Support GHC 9.0.1 From 18e7e0da17fab78120217dac3a0e9081dc440a5b Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 13 Aug 2021 04:11:50 -0700 Subject: [PATCH 098/208] Check in .dir-locals.el --- .dir-locals.el | 7 +++++++ .gitignore | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100755 .dir-locals.el diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100755 index 0000000..4f9ce36 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,7 @@ +((haskell-mode + . ( + (haskell-process-args-stack-ghci . ("--ghci-options=-ferror-spans" "--no-build" "--no-load" + "aeson-typescript:lib" + "aeson-typescript:aeson-typescript-tests" + )) + ))) diff --git a/.gitignore b/.gitignore index 781cfa4..70115df 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .stack-work/ *~ -.dir-locals.el dist-newstyle *.hie From 9b8a373b09ccfe82eb5597257362df38c25faa16 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 13 Aug 2021 04:20:02 -0700 Subject: [PATCH 099/208] Trying to fix another generic case --- src/Data/Aeson/TypeScript/TH.hs | 5 ++-- src/Data/Aeson/TypeScript/Util.hs | 45 ++++++++++++++++--------------- test/Generic.hs | 13 ++++++++- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index fe997de..7f42d84 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -160,6 +160,7 @@ import Data.Maybe import Data.Proxy import Data.String.Interpolate import Data.Typeable +import Debug.Trace import Language.Haskell.TH hiding (stringE) import Language.Haskell.TH.Datatype import qualified Language.Haskell.TH.Lib as TH @@ -304,9 +305,9 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@( tell [ExtraConstraint constraint] (fieldTyp, optAsBool) <- lift $ case typ of - (AppT (ConT name) t) | name == ''Maybe && not (omitNothingFields options) -> + (AppT (ConT name) (mapType genericVariables -> t)) | name == ''Maybe && not (omitNothingFields options) -> ( , ) <$> [|$(getTypeAsStringExp t) <> " | null"|] <*> getOptionalAsBoolExp t - _ -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ' + _ -> ( , ) <$> getTypeAsStringExp (mapType genericVariables typ) <*> getOptionalAsBoolExp (mapType genericVariables typ') lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) |] transformTypeFamilies :: ExtraTypeScriptOptions -> Type -> WriterT [ExtraDeclOrGenericInfo] Q Type diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index 39b6420..e86cba0 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -169,28 +169,29 @@ contentsTupleTypeSubstituted :: [(Name, String)] -> ConstructorInfo -> Type contentsTupleTypeSubstituted genericVariables ci = let fields = constructorFields ci in case fields of [] -> AppT ListT (ConT ''()) - [x] -> mapType x - xs -> applyToArgsT (ConT $ tupleTypeName (L.length xs)) (fmap mapType xs) - where - mapType x@(VarT name) = tryPromote x name - mapType x@(ConT name) = tryPromote x name - mapType x@(PromotedT name) = tryPromote x name - mapType x = x - - tryPromote _ (flip L.lookup genericVariables -> Just "") = ConT ''T - tryPromote _ (flip L.lookup genericVariables -> Just "T") = ConT ''T - tryPromote _ (flip L.lookup genericVariables -> Just "T1") = ConT ''T1 - tryPromote _ (flip L.lookup genericVariables -> Just "T2") = ConT ''T2 - tryPromote _ (flip L.lookup genericVariables -> Just "T3") = ConT ''T3 - tryPromote _ (flip L.lookup genericVariables -> Just "T4") = ConT ''T4 - tryPromote _ (flip L.lookup genericVariables -> Just "T5") = ConT ''T5 - tryPromote _ (flip L.lookup genericVariables -> Just "T6") = ConT ''T6 - tryPromote _ (flip L.lookup genericVariables -> Just "T7") = ConT ''T7 - tryPromote _ (flip L.lookup genericVariables -> Just "T8") = ConT ''T8 - tryPromote _ (flip L.lookup genericVariables -> Just "T9") = ConT ''T9 - tryPromote _ (flip L.lookup genericVariables -> Just "T10") = ConT ''T10 - tryPromote x _ = x - + [x] -> mapType genericVariables x + xs -> applyToArgsT (ConT $ tupleTypeName (L.length xs)) (fmap (mapType genericVariables) xs) + +mapType :: [(Name, String)] -> Type -> Type +mapType genericVariables x@(VarT name) = tryPromote x genericVariables name +mapType genericVariables x@(ConT name) = tryPromote x genericVariables name +mapType genericVariables x@(PromotedT name) = tryPromote x genericVariables name +mapType genericVariables (AppT ListT val) = AppT ListT $ mapType genericVariables val +mapType _ x = x + +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "") = ConT ''T +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T") = ConT ''T +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T1") = ConT ''T1 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T2") = ConT ''T2 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T3") = ConT ''T3 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T4") = ConT ''T4 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T5") = ConT ''T5 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T6") = ConT ''T6 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T7") = ConT ''T7 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T8") = ConT ''T8 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T9") = ConT ''T9 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T10") = ConT ''T10 +tryPromote x _ _ = x getBracketsExpression :: Bool -> [(Name, String)] -> Q Exp getBracketsExpression _ [] = [|""|] diff --git a/test/Generic.hs b/test/Generic.hs index e2f9532..3fa3416 100644 --- a/test/Generic.hs +++ b/test/Generic.hs @@ -26,12 +26,15 @@ import Prelude hiding (Double) import Test.Hspec -data Complex a = Product Int a | Unary Int deriving Eq +data Complex a = Product Int a | Unary Int $(deriveTypeScript defaultOptions ''Complex) data Complex2 a = Product2 Int a $(deriveTypeScript (defaultOptions { sumEncoding = UntaggedValue }) ''Complex2) +data Complex3 k = Product3 { record3 :: [k] } +$(deriveTypeScript defaultOptions ''Complex3) + tests :: SpecWith () tests = describe "Generic instances" $ do it [i|Complex makes the declaration and types correctly|] $ do @@ -47,5 +50,13 @@ tests = describe "Generic instances" $ do ,TSTypeAlternatives {typeName = "IProduct2", typeGenericVariables = ["T"], alternativeTypes = ["[number, T]"]} ] + it [i|Complex3 makes the declaration and types correctly|] $ do + (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex3 String))) `shouldBe` [ + TSInterfaceDeclaration {interfaceName = "IProduct3", interfaceGenericVariables = ["T"], interfaceMembers = [ + TSField {fieldOptional = False, fieldName = "record3", fieldType = "T[]"} + ]} + ,TSTypeAlternatives {typeName = "Complex3", typeGenericVariables = ["T"], alternativeTypes = ["IProduct3"]} + ] + main :: IO () main = hspec tests From 3fe26a158ef1a478d6bc9b4251dfeb5122d94308 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 13 Aug 2021 04:50:59 -0700 Subject: [PATCH 100/208] More generic cases working --- dev/Live6.hs | 15 ++++------- src/Data/Aeson/TypeScript/TH.hs | 13 +++++---- src/Data/Aeson/TypeScript/Types.hs | 3 +++ src/Data/Aeson/TypeScript/Util.hs | 43 ++++++++++++++++-------------- test/Generic.hs | 7 +++++ 5 files changed, 46 insertions(+), 35 deletions(-) diff --git a/dev/Live6.hs b/dev/Live6.hs index 10215aa..3738874 100644 --- a/dev/Live6.hs +++ b/dev/Live6.hs @@ -24,19 +24,14 @@ data Complex a = Product Int a | Unary Int deriving Eq data Complex2 a = Product2 Int a --- data BulkCommandNoArg k = BulkCommandNoArg { --- bulkCommandNoArgKeys :: [k] --- } deriving (Show) --- $(deriveTypeScript defaultOptions ''BulkCommandNoArg) - -$(deriveTypeScript defaultOptions ''Complex) - -$(deriveTypeScript (defaultOptions { sumEncoding = UntaggedValue }) ''Complex2) - +data BulkCommandNoArg k = BulkCommandNoArg { + bulkCommandNoArgKeys :: [k] + } deriving (Show) +$(deriveTypeScript defaultOptions ''BulkCommandNoArg) main :: IO () -- main = printThing (Proxy @(BulkCommandNoArg Int)) -main = printThing (Proxy @(Complex2 String)) +main = printThing (Proxy @(BulkCommandNoArg String)) printThing x = getTypeScriptDeclarations x & formatTSDeclarations diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 7f42d84..08c30aa 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -160,7 +160,6 @@ import Data.Maybe import Data.Proxy import Data.String.Interpolate import Data.Typeable -import Debug.Trace import Language.Haskell.TH hiding (stringE) import Language.Haskell.TH.Datatype import qualified Language.Haskell.TH.Lib as TH @@ -204,11 +203,15 @@ deriveTypeScript' options name extraOptions = do let eligibleGenericVars = catMaybes $ flip fmap (getDataTypeVars dti) $ \case SigT (VarT n) StarT -> Just n _ -> Nothing - genericVariablesAndSuffixes <- forM eligibleGenericVars $ \var -> do + let varsAndTVars = case eligibleGenericVars of + [] -> [] + [x] -> [(x, "T")] + xs -> zip xs allStarConstructors'' + genericVariablesAndSuffixes <- forM varsAndTVars $ \(var, tvar) -> do (_, genericInfos) <- runWriterT $ forM_ (datatypeCons datatypeInfo') $ \ci -> forM_ (namesAndTypes options [] ci) $ \(_, typ) -> do searchForConstraints extraOptions typ var - return (var, unifyGenericVariable genericInfos) + return (var, (unifyGenericVariable genericInfos, tvar)) -- Build the declarations (types, extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options extraOptions dti genericVariablesAndSuffixes) (datatypeCons dti) @@ -237,8 +240,8 @@ deriveTypeScript' options name extraOptions = do return (extraTopLevelDecls <> inst) -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration -handleConstructor :: Options -> ExtraTypeScriptOptions -> DatatypeInfo -> [(Name, String)] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp -handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = do +handleConstructor :: Options -> ExtraTypeScriptOptions -> DatatypeInfo -> [(Name, (Suffix, Var))] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp +handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = do if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> do writeSingleConstructorEncoding brackets <- lift $ getBracketsExpression False genericVariables diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 6e240ee..414acff 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -167,6 +167,9 @@ allStarConstructors = [ConT ''T1, ConT ''T2, ConT ''T3, ConT ''T4, ConT ''T5, Co allStarConstructors' :: [Name] allStarConstructors' = [''T1, ''T2, ''T3, ''T4, ''T5, ''T6, ''T7, ''T8, ''T9, ''T10] +allStarConstructors'' :: [String] +allStarConstructors'' = ["T1", "T2", "T3", "T4", "T5", "T6", "T7", "T8", "T9", "T10"] + -- | Type variable gathering data ExtraTypeScriptOptions = ExtraTypeScriptOptions { diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index e86cba0..9d3193a 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -30,6 +30,9 @@ import Data.Monoid #endif +type Suffix = String +type Var = String + getDataTypeVars :: DatatypeInfo -> [Type] #if MIN_VERSION_th_abstraction(0,3,0) getDataTypeVars (DatatypeInfo {datatypeInstTypes}) = datatypeInstTypes @@ -145,7 +148,7 @@ mkInstance = InstanceD Nothing mkInstance = InstanceD #endif -namesAndTypes :: Options -> [(Name, String)] -> ConstructorInfo -> [(String, Type)] +namesAndTypes :: Options -> [(Name, (Suffix, Var))] -> ConstructorInfo -> [(String, Type)] namesAndTypes options genericVariables ci = case constructorVariant ci of RecordConstructor names -> zip (fmap ((fieldLabelModifier options) . lastNameComponent') names) (constructorFields ci) _ -> case sumEncoding options of @@ -165,45 +168,45 @@ contentsTupleType ci = let fields = constructorFields ci in [x] -> x xs-> applyToArgsT (ConT $ tupleTypeName (L.length xs)) fields -contentsTupleTypeSubstituted :: [(Name, String)] -> ConstructorInfo -> Type +contentsTupleTypeSubstituted :: [(Name, (Suffix, Var))] -> ConstructorInfo -> Type contentsTupleTypeSubstituted genericVariables ci = let fields = constructorFields ci in case fields of [] -> AppT ListT (ConT ''()) [x] -> mapType genericVariables x xs -> applyToArgsT (ConT $ tupleTypeName (L.length xs)) (fmap (mapType genericVariables) xs) -mapType :: [(Name, String)] -> Type -> Type +mapType :: [(Name, (Suffix, Var))] -> Type -> Type mapType genericVariables x@(VarT name) = tryPromote x genericVariables name mapType genericVariables x@(ConT name) = tryPromote x genericVariables name mapType genericVariables x@(PromotedT name) = tryPromote x genericVariables name mapType genericVariables (AppT ListT val) = AppT ListT $ mapType genericVariables val mapType _ x = x -tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "") = ConT ''T -tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T") = ConT ''T -tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T1") = ConT ''T1 -tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T2") = ConT ''T2 -tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T3") = ConT ''T3 -tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T4") = ConT ''T4 -tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T5") = ConT ''T5 -tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T6") = ConT ''T6 -tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T7") = ConT ''T7 -tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T8") = ConT ''T8 -tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T9") = ConT ''T9 -tryPromote _ genericVariables (flip L.lookup genericVariables -> Just "T10") = ConT ''T10 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just (_, "")) = ConT ''T +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just (_, "T")) = ConT ''T +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just (_, "T1")) = ConT ''T1 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just (_, "T2")) = ConT ''T2 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just (_, "T3")) = ConT ''T3 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just (_, "T4")) = ConT ''T4 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just (_, "T5")) = ConT ''T5 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just (_, "T6")) = ConT ''T6 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just (_, "T7")) = ConT ''T7 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just (_, "T8")) = ConT ''T8 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just (_, "T9")) = ConT ''T9 +tryPromote _ genericVariables (flip L.lookup genericVariables -> Just (_, "T10")) = ConT ''T10 tryPromote x _ _ = x -getBracketsExpression :: Bool -> [(Name, String)] -> Q Exp +getBracketsExpression :: Bool -> [(Name, (Suffix, Var))] -> Q Exp getBracketsExpression _ [] = [|""|] getBracketsExpression includeSuffix names = [|let vars = $(genericVariablesListExpr includeSuffix names) in "<" <> L.intercalate ", " vars <> ">"|] -getBracketsExpressionAllTypesNoSuffix :: [(Name, String)] -> Q Exp +getBracketsExpressionAllTypesNoSuffix :: [(Name, (Suffix, Var))] -> Q Exp getBracketsExpressionAllTypesNoSuffix [] = [|""|] -getBracketsExpressionAllTypesNoSuffix names = [|"<" <> L.intercalate ", " $(listE [ [|(getTypeScriptType (Proxy :: Proxy $(varT x)))|] | (x, _suffix) <- names]) <> ">"|] +getBracketsExpressionAllTypesNoSuffix names = [|"<" <> L.intercalate ", " $(listE [ [|(getTypeScriptType (Proxy :: Proxy $(varT x)))|] | (x, (_suffix, _)) <- names]) <> ">"|] -genericVariablesListExpr :: Bool -> [(Name, String)] -> Q Exp -genericVariablesListExpr includeSuffix genericVariables = listE (fmap (\((_, suffix), correspondingGeneric) -> +genericVariablesListExpr :: Bool -> [(Name, (Suffix, Var))] -> Q Exp +genericVariablesListExpr includeSuffix genericVariables = listE (fmap (\((_, (suffix, _)), correspondingGeneric) -> [|(getTypeScriptType (Proxy :: Proxy $(return correspondingGeneric))) <> $(TH.stringE (if includeSuffix then suffix else ""))|]) (case genericVariables of [x] -> [(x, ConT ''T)] diff --git a/test/Generic.hs b/test/Generic.hs index 3fa3416..bb6d6f1 100644 --- a/test/Generic.hs +++ b/test/Generic.hs @@ -58,5 +58,12 @@ tests = describe "Generic instances" $ do ,TSTypeAlternatives {typeName = "Complex3", typeGenericVariables = ["T"], alternativeTypes = ["IProduct3"]} ] + (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex3 Int))) `shouldBe` [ + TSInterfaceDeclaration {interfaceName = "IProduct3", interfaceGenericVariables = ["T"], interfaceMembers = [ + TSField {fieldOptional = False, fieldName = "record3", fieldType = "T[]"} + ]} + ,TSTypeAlternatives {typeName = "Complex3", typeGenericVariables = ["T"], alternativeTypes = ["IProduct3"]} + ] + main :: IO () main = hspec tests From 906decc7a3299074d2e05cfe3a9786dff2616e2f Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 13 Aug 2021 06:17:56 -0700 Subject: [PATCH 101/208] More improvement to mapType + another generic case --- dev/Live6.hs | 10 ++++++---- src/Data/Aeson/TypeScript/Util.hs | 16 ++++++++++++---- test/Generic.hs | 13 +++++++++++++ 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/dev/Live6.hs b/dev/Live6.hs index 3738874..e856e7a 100644 --- a/dev/Live6.hs +++ b/dev/Live6.hs @@ -17,21 +17,23 @@ import Data.Aeson as A import Data.Aeson.TypeScript.Recursive import Data.Aeson.TypeScript.TH import Data.Function +import Data.Map import Data.Proxy +import Data.Text data Complex a = Product Int a | Unary Int deriving Eq data Complex2 a = Product2 Int a -data BulkCommandNoArg k = BulkCommandNoArg { - bulkCommandNoArgKeys :: [k] +data Complex3 k = Complex3 { + bulkCommandNoArgKeys :: Map Text k } deriving (Show) -$(deriveTypeScript defaultOptions ''BulkCommandNoArg) +$(deriveTypeScript defaultOptions ''Complex3) main :: IO () -- main = printThing (Proxy @(BulkCommandNoArg Int)) -main = printThing (Proxy @(BulkCommandNoArg String)) +main = printThing (Proxy @(Complex3 Double)) printThing x = getTypeScriptDeclarations x & formatTSDeclarations diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index 9d3193a..89c771b 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -176,10 +176,18 @@ contentsTupleTypeSubstituted genericVariables ci = let fields = constructorField xs -> applyToArgsT (ConT $ tupleTypeName (L.length xs)) (fmap (mapType genericVariables) xs) mapType :: [(Name, (Suffix, Var))] -> Type -> Type -mapType genericVariables x@(VarT name) = tryPromote x genericVariables name -mapType genericVariables x@(ConT name) = tryPromote x genericVariables name -mapType genericVariables x@(PromotedT name) = tryPromote x genericVariables name -mapType genericVariables (AppT ListT val) = AppT ListT $ mapType genericVariables val +mapType g x@(VarT name) = tryPromote x g name +mapType g x@(ConT name) = tryPromote x g name +mapType g x@(PromotedT name) = tryPromote x g name +mapType g (AppT typ1 typ2) = AppT (mapType g typ1) (mapType g typ2) +mapType g (SigT typ x) = SigT (mapType g typ) x +mapType g (InfixT typ1 x typ2) = InfixT (mapType g typ1) x (mapType g typ2) +mapType g (UInfixT typ1 x typ2) = UInfixT (mapType g typ1) x (mapType g typ2) +mapType g (ParensT typ) = ParensT (mapType g typ) +#if MIN_VERSION_template_haskell(2,15,0) +mapType g (AppKindT typ x) = AppKindT (mapType g typ) x +mapType g (ImplicitParamT x typ) = ImplicitParamT x (mapType g typ) +#endif mapType _ x = x tryPromote _ genericVariables (flip L.lookup genericVariables -> Just (_, "")) = ConT ''T diff --git a/test/Generic.hs b/test/Generic.hs index bb6d6f1..60e1a20 100644 --- a/test/Generic.hs +++ b/test/Generic.hs @@ -20,8 +20,10 @@ import Data.Aeson as A import Data.Aeson.TypeScript.Recursive import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types +import Data.Map import Data.Proxy import Data.String.Interpolate +import Data.Text import Prelude hiding (Double) import Test.Hspec @@ -35,6 +37,9 @@ $(deriveTypeScript (defaultOptions { sumEncoding = UntaggedValue }) ''Complex2) data Complex3 k = Product3 { record3 :: [k] } $(deriveTypeScript defaultOptions ''Complex3) +data Complex4 k = Product4 { record4 :: Map Text k } +$(deriveTypeScript defaultOptions ''Complex4) + tests :: SpecWith () tests = describe "Generic instances" $ do it [i|Complex makes the declaration and types correctly|] $ do @@ -65,5 +70,13 @@ tests = describe "Generic instances" $ do ,TSTypeAlternatives {typeName = "Complex3", typeGenericVariables = ["T"], alternativeTypes = ["IProduct3"]} ] + it [i|Complex4 makes the declaration and types correctly|] $ do + (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex4 String))) `shouldBe` [ + TSInterfaceDeclaration {interfaceName = "IProduct4", interfaceGenericVariables = ["T"], interfaceMembers = [ + TSField {fieldOptional = False, fieldName = "record4", fieldType = "{[k in string]?: T}"} + ]} + ,TSTypeAlternatives {typeName = "Complex4", typeGenericVariables = ["T"], alternativeTypes = ["IProduct4"]} + ] + main :: IO () main = hspec tests From 473cd886334b0eb94d85a9f27a375002746d26c6 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 13 Aug 2021 06:22:21 -0700 Subject: [PATCH 102/208] Comment some currently errored live stuff --- dev/Live.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/Live.hs b/dev/Live.hs index 592cdec..fd3392f 100644 --- a/dev/Live.hs +++ b/dev/Live.hs @@ -39,7 +39,7 @@ instance TypeScript K8SDE where getTypeScriptType _ = [i|"k8s"|] data SingleNodeEnvironment = SingleNodeEnvironment deriving (Eq, Show) instance TypeScript SingleNodeEnvironment where getTypeScriptType _ = [i|"single_node_env"|] - + data K8SEnvironment = K8SEnvironment deriving (Eq, Show) instance TypeScript K8SEnvironment where getTypeScriptType _ = [i|"k8s_env"|] @@ -65,9 +65,9 @@ data UserT env f = User { , _userDeployEnvironment :: Columnar f (DeployEnvironment env) } -$(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) +-- $(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) -main :: IO () -main = getTypeScriptDeclarationsRecursively (Proxy :: Proxy (UserT T Identity)) - & formatTSDeclarations - & putStrLn +-- main :: IO () +-- main = getTypeScriptDeclarationsRecursively (Proxy :: Proxy (UserT T Identity)) +-- & formatTSDeclarations +-- & putStrLn From 2223f2cbda379b83e21c7451e3553dd7a453bbef Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 13 Aug 2021 17:47:05 -0700 Subject: [PATCH 103/208] Tidy where a constraint is added --- src/Data/Aeson/TypeScript/TH.hs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 08c30aa..fea185a 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -241,7 +241,7 @@ deriveTypeScript' options name extraOptions = do -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration handleConstructor :: Options -> ExtraTypeScriptOptions -> DatatypeInfo -> [(Name, (Suffix, Var))] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp -handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@(ConstructorInfo {}) = do +handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci = do if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> do writeSingleConstructorEncoding brackets <- lift $ getBracketsExpression False genericVariables @@ -303,9 +303,6 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci@ getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] getTSFields = forM (namesAndTypes options genericVariables ci) $ \(nameString, typ') -> do typ <- transformTypeFamilies extraOptions typ' - when (typ /= typ') $ do - let constraint = AppT (ConT ''TypeScript) typ - tell [ExtraConstraint constraint] (fieldTyp, optAsBool) <- lift $ case typ of (AppT (ConT name) (mapType genericVariables -> t)) | name == ''Maybe && not (omitNothingFields options) -> @@ -318,7 +315,6 @@ transformTypeFamilies eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) | name `L.elem` typeFamiliesToMapToTypeScript = lift (reify name) >>= \case FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) eqns) _ -> handle typeFamilyName eqns - #if MIN_VERSION_template_haskell(2,15,0) FamilyI (OpenTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _)) decs -> handle typeFamilyName [eqn | TySynInstD eqn <- decs] #else @@ -350,8 +346,9 @@ transformTypeFamilies eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) tell [ExtraParentType (AppT (ConT name') (ConT ''T))] - transformTypeFamilies eo (AppT (ConT name') typ) - + ret <- transformTypeFamilies eo (AppT (ConT name') typ) + tell [ExtraConstraint (AppT (ConT ''TypeScript) ret)] + return ret transformTypeFamilies eo (AppT typ1 typ2) = AppT <$> transformTypeFamilies eo typ1 <*> transformTypeFamilies eo typ2 transformTypeFamilies eo (SigT typ kind) = flip SigT kind <$> transformTypeFamilies eo typ transformTypeFamilies eo (InfixT typ1 n typ2) = InfixT <$> transformTypeFamilies eo typ1 <*> pure n <*> transformTypeFamilies eo typ2 From 56344a26cce1b41d0a6d00d751771f20fe4c01f4 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 13 Aug 2021 18:37:21 -0700 Subject: [PATCH 104/208] Crazy refactor that seems to actually work --- dev/Live.hs | 23 +++++++++----- src/Data/Aeson/TypeScript/TH.hs | 53 ++++++++++++++++----------------- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/dev/Live.hs b/dev/Live.hs index fd3392f..0fff140 100644 --- a/dev/Live.hs +++ b/dev/Live.hs @@ -19,12 +19,16 @@ module Live where import Data.Aeson as A import Data.Aeson.TypeScript.Recursive import Data.Aeson.TypeScript.TH +import Data.Aeson.TypeScript.Types import Data.Function import Data.Functor.Identity import Data.Kind +import Data.List +import Data.Map import Data.Proxy import Data.String.Interpolate import qualified Data.Text as T +import Data.Typeable import Prelude hiding (Double) @@ -60,14 +64,17 @@ type family DeployEnvironment env = result | result -> env where -- * The main type data UserT env f = User { - _userUsername :: Columnar f T.Text - , _userCreatedAt :: Columnar f Int - , _userDeployEnvironment :: Columnar f (DeployEnvironment env) + _userDeployEnv :: Columnar f (DeployEnvironment env) } --- $(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) +data Complex3 k = Complex3 { + bulkCommandNoArgKeys :: Map T.Text k + } deriving (Show) --- main :: IO () --- main = getTypeScriptDeclarationsRecursively (Proxy :: Proxy (UserT T Identity)) --- & formatTSDeclarations --- & putStrLn + +deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment]) + +main :: IO () +main = getTypeScriptDeclarationsRecursively (Proxy :: Proxy (UserT T Identity)) + & formatTSDeclarations + & putStrLn diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index fea185a..497d94f 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -155,7 +155,6 @@ import Data.Aeson.TypeScript.Lookup import Data.Aeson.TypeScript.Types import Data.Aeson.TypeScript.Util import qualified Data.List as L -import qualified Data.Map as M import Data.Maybe import Data.Proxy import Data.String.Interpolate @@ -180,13 +179,26 @@ deriveTypeScript' options name extraOptions = do datatypeInfo' <- reifyDatatype name assertExtensionsTurnedOn datatypeInfo' - -- Plug in generic variables for all star free variables - let starVars = [name | (isStarType -> Just _) <- getDataTypeVars datatypeInfo'] - let templateVarsToUse = case length starVars of - 1 -> [ConT ''T] - _ -> take (length starVars) allStarConstructors - let subMap = M.fromList $ zip starVars templateVarsToUse - let dti = datatypeInfo' { datatypeCons = fmap (applySubstitution subMap) (datatypeCons datatypeInfo')} + -- Figure out what the generic variables are + let eligibleGenericVars = catMaybes $ flip fmap (getDataTypeVars datatypeInfo') $ \case + SigT (VarT n) StarT -> Just n + _ -> Nothing + let varsAndTVars = case eligibleGenericVars of + [] -> [] + [x] -> [(x, "T")] + xs -> zip xs allStarConstructors'' + genericVariablesAndSuffixes <- forM varsAndTVars $ \(var, tvar) -> do + (_, genericInfos) <- runWriterT $ forM_ (datatypeCons datatypeInfo') $ \ci -> + forM_ (namesAndTypes options [] ci) $ \(_, typ) -> do + searchForConstraints extraOptions typ var + return (var, (unifyGenericVariable genericInfos, tvar)) + + -- Plug in generic variables and de-family-ify + (newConstructorInfos, extraDeclsOrGenericInfosInitial) <- runWriterT $ forM (datatypeCons datatypeInfo') $ \ci@(ConstructorInfo {..}) -> do + newFields <- forM constructorFields $ \t -> + transformTypeFamilies extraOptions $ mapType genericVariablesAndSuffixes t + return (ci { constructorFields = newFields }) + let dti = datatypeInfo' { datatypeCons = newConstructorInfos } -- Build constraints: a TypeScript constraint for every constructor type and one for every type variable. -- Probably overkill/not exactly right, but it's a start. @@ -200,21 +212,8 @@ deriveTypeScript' options name extraOptions = do ] let typeVariablePreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- getDataTypeVars dti] - let eligibleGenericVars = catMaybes $ flip fmap (getDataTypeVars dti) $ \case - SigT (VarT n) StarT -> Just n - _ -> Nothing - let varsAndTVars = case eligibleGenericVars of - [] -> [] - [x] -> [(x, "T")] - xs -> zip xs allStarConstructors'' - genericVariablesAndSuffixes <- forM varsAndTVars $ \(var, tvar) -> do - (_, genericInfos) <- runWriterT $ forM_ (datatypeCons datatypeInfo') $ \ci -> - forM_ (namesAndTypes options [] ci) $ \(_, typ) -> do - searchForConstraints extraOptions typ var - return (var, (unifyGenericVariable genericInfos, tvar)) - -- Build the declarations - (types, extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options extraOptions dti genericVariablesAndSuffixes) (datatypeCons dti) + (types, (extraDeclsOrGenericInfosInitial <>) -> extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options extraOptions dti genericVariablesAndSuffixes) (datatypeCons dti) typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName (datatypeName dti)) $(genericVariablesListExpr True genericVariablesAndSuffixes) $(listE $ fmap return types)|] @@ -291,7 +290,7 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci = interfaceName = "I" <> (lastNameComponent' $ constructorName ci) tupleEncoding = do - tupleType <- transformTypeFamilies extraOptions (contentsTupleTypeSubstituted genericVariables ci) + let tupleType = contentsTupleTypeSubstituted genericVariables ci lift [|TSTypeAlternatives $(TH.stringE interfaceName) $(genericVariablesListExpr True genericVariables) [getTypeScriptType (Proxy :: Proxy $(return tupleType))]|] @@ -301,13 +300,11 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci = $(return members)|] getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] - getTSFields = forM (namesAndTypes options genericVariables ci) $ \(nameString, typ') -> do - typ <- transformTypeFamilies extraOptions typ' - + getTSFields = forM (namesAndTypes options genericVariables ci) $ \(nameString, typ) -> do (fieldTyp, optAsBool) <- lift $ case typ of - (AppT (ConT name) (mapType genericVariables -> t)) | name == ''Maybe && not (omitNothingFields options) -> + (AppT (ConT name) t) | name == ''Maybe && not (omitNothingFields options) -> ( , ) <$> [|$(getTypeAsStringExp t) <> " | null"|] <*> getOptionalAsBoolExp t - _ -> ( , ) <$> getTypeAsStringExp (mapType genericVariables typ) <*> getOptionalAsBoolExp (mapType genericVariables typ') + _ -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) |] transformTypeFamilies :: ExtraTypeScriptOptions -> Type -> WriterT [ExtraDeclOrGenericInfo] Q Type From eb8a0e5a89203a25724b6d4a535145fa2d26da39 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 13 Aug 2021 18:46:00 -0700 Subject: [PATCH 105/208] Refactoring --- aeson-typescript.cabal | 2 + dev/Live.hs | 3 - src/Data/Aeson/TypeScript/TH.hs | 62 ++----------------- src/Data/Aeson/TypeScript/Transform.hs | 86 ++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 59 deletions(-) create mode 100644 src/Data/Aeson/TypeScript/Transform.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 52b9c70..ce41fab 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -40,6 +40,7 @@ library Data.Aeson.TypeScript.Formatting Data.Aeson.TypeScript.Instances Data.Aeson.TypeScript.Lookup + Data.Aeson.TypeScript.Transform Data.Aeson.TypeScript.Types Data.Aeson.TypeScript.Util Paths_aeson_typescript @@ -86,6 +87,7 @@ test-suite aeson-typescript-tests Data.Aeson.TypeScript.Lookup Data.Aeson.TypeScript.Recursive Data.Aeson.TypeScript.TH + Data.Aeson.TypeScript.Transform Data.Aeson.TypeScript.Types Data.Aeson.TypeScript.Util Live diff --git a/dev/Live.hs b/dev/Live.hs index 0fff140..3a61420 100644 --- a/dev/Live.hs +++ b/dev/Live.hs @@ -19,16 +19,13 @@ module Live where import Data.Aeson as A import Data.Aeson.TypeScript.Recursive import Data.Aeson.TypeScript.TH -import Data.Aeson.TypeScript.Types import Data.Function import Data.Functor.Identity import Data.Kind -import Data.List import Data.Map import Data.Proxy import Data.String.Interpolate import qualified Data.Text as T -import Data.Typeable import Prelude hiding (Double) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 497d94f..70a3bab 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -152,6 +152,7 @@ import Data.Aeson.TH as A import Data.Aeson.TypeScript.Formatting import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Lookup +import Data.Aeson.TypeScript.Transform import Data.Aeson.TypeScript.Types import Data.Aeson.TypeScript.Util import qualified Data.List as L @@ -213,7 +214,7 @@ deriveTypeScript' options name extraOptions = do let typeVariablePreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- getDataTypeVars dti] -- Build the declarations - (types, (extraDeclsOrGenericInfosInitial <>) -> extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options extraOptions dti genericVariablesAndSuffixes) (datatypeCons dti) + (types, (extraDeclsOrGenericInfosInitial <>) -> extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options dti genericVariablesAndSuffixes) (datatypeCons dti) typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName (datatypeName dti)) $(genericVariablesListExpr True genericVariablesAndSuffixes) $(listE $ fmap return types)|] @@ -239,8 +240,8 @@ deriveTypeScript' options name extraOptions = do return (extraTopLevelDecls <> inst) -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration -handleConstructor :: Options -> ExtraTypeScriptOptions -> DatatypeInfo -> [(Name, (Suffix, Var))] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp -handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci = do +handleConstructor :: Options -> DatatypeInfo -> [(Name, (Suffix, Var))] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp +handleConstructor options (DatatypeInfo {..}) genericVariables ci = do if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> do writeSingleConstructorEncoding brackets <- lift $ getBracketsExpression False genericVariables @@ -289,11 +290,10 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci = -- * Type declaration to use interfaceName = "I" <> (lastNameComponent' $ constructorName ci) - tupleEncoding = do - let tupleType = contentsTupleTypeSubstituted genericVariables ci + tupleEncoding = lift [|TSTypeAlternatives $(TH.stringE interfaceName) $(genericVariablesListExpr True genericVariables) - [getTypeScriptType (Proxy :: Proxy $(return tupleType))]|] + [getTypeScriptType (Proxy :: Proxy $(return (contentsTupleTypeSubstituted genericVariables ci)))]|] assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) $(genericVariablesListExpr True genericVariables) @@ -307,56 +307,6 @@ handleConstructor options extraOptions (DatatypeInfo {..}) genericVariables ci = _ -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) |] -transformTypeFamilies :: ExtraTypeScriptOptions -> Type -> WriterT [ExtraDeclOrGenericInfo] Q Type -transformTypeFamilies eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) - | name `L.elem` typeFamiliesToMapToTypeScript = lift (reify name) >>= \case - FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) eqns) _ -> handle typeFamilyName eqns - -#if MIN_VERSION_template_haskell(2,15,0) - FamilyI (OpenTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _)) decs -> handle typeFamilyName [eqn | TySynInstD eqn <- decs] -#else - FamilyI (OpenTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _)) decs -> handle typeFamilyName [eqn | TySynInstD _name eqn <- decs] -#endif - - _ -> AppT (ConT name) <$> transformTypeFamilies eo typ - | otherwise = AppT (ConT name) <$> transformTypeFamilies eo typ - where - handle :: Name -> [TySynEqn] -> WriterT [ExtraDeclOrGenericInfo] Q Type - handle typeFamilyName eqns = do - name' <- lift $ newName (nameBase typeFamilyName <> "'") - - f <- lift $ newName "f" -#if MIN_VERSION_template_haskell(2,17,0) - let inst1 = DataD [] name' [PlainTV f ()] Nothing [] [] -#else - let inst1 = DataD [] name' [PlainTV f] Nothing [] [] -#endif - tell [ExtraTopLevelDecs [inst1]] - - imageTypes <- lift $ getClosedTypeFamilyImage eqns - inst2 <- lift $ [d|instance (Typeable g, TypeScript g) => TypeScript ($(conT name') g) where - getTypeScriptType _ = $(TH.stringE $ nameBase name) <> "[" <> (getTypeScriptType (Proxy :: Proxy g)) <> "]" - getTypeScriptDeclarations _ = [$(getClosedTypeFamilyInterfaceDecl name eqns)] - getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return x))|] | x <- imageTypes]) - |] - tell [ExtraTopLevelDecs inst2] - - tell [ExtraParentType (AppT (ConT name') (ConT ''T))] - - ret <- transformTypeFamilies eo (AppT (ConT name') typ) - tell [ExtraConstraint (AppT (ConT ''TypeScript) ret)] - return ret -transformTypeFamilies eo (AppT typ1 typ2) = AppT <$> transformTypeFamilies eo typ1 <*> transformTypeFamilies eo typ2 -transformTypeFamilies eo (SigT typ kind) = flip SigT kind <$> transformTypeFamilies eo typ -transformTypeFamilies eo (InfixT typ1 n typ2) = InfixT <$> transformTypeFamilies eo typ1 <*> pure n <*> transformTypeFamilies eo typ2 -transformTypeFamilies eo (UInfixT typ1 n typ2) = UInfixT <$> transformTypeFamilies eo typ1 <*> pure n <*> transformTypeFamilies eo typ2 -transformTypeFamilies eo (ParensT typ) = ParensT <$> transformTypeFamilies eo typ -#if MIN_VERSION_template_haskell(2,15,0) -transformTypeFamilies eo (AppKindT typ kind) = flip AppKindT kind <$> transformTypeFamilies eo typ -transformTypeFamilies eo (ImplicitParamT s typ) = ImplicitParamT s <$> transformTypeFamilies eo typ -#endif -transformTypeFamilies _ typ = return typ - searchForConstraints :: ExtraTypeScriptOptions -> Type -> Name -> WriterT [GenericInfo] Q () searchForConstraints eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) var diff --git a/src/Data/Aeson/TypeScript/Transform.hs b/src/Data/Aeson/TypeScript/Transform.hs new file mode 100644 index 0000000..259267a --- /dev/null +++ b/src/Data/Aeson/TypeScript/Transform.hs @@ -0,0 +1,86 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE LambdaCase #-} + + +module Data.Aeson.TypeScript.Transform ( + transformTypeFamilies + ) where + +import Control.Monad.Writer +import Data.Aeson.TypeScript.Lookup +import Data.Aeson.TypeScript.Types +import qualified Data.List as L +import Data.Typeable +import Language.Haskell.TH hiding (stringE) +import qualified Language.Haskell.TH.Lib as TH + +#if !MIN_VERSION_base(4,11,0) +import Data.Monoid +#endif + + +-- | Search the given type for type families. For each one found, emit a declaration for a new +-- corresponding concrete type and a TypeScript instance for it which emits a lookup type. +-- Then, replace all occurrences of the given type family with the concrete type in the return value. +-- Thus the type becomes "de-family-ified". +transformTypeFamilies :: ExtraTypeScriptOptions -> Type -> WriterT [ExtraDeclOrGenericInfo] Q Type +transformTypeFamilies eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) + | name `L.elem` typeFamiliesToMapToTypeScript = lift (reify name) >>= \case + FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) eqns) _ -> handle typeFamilyName eqns + +#if MIN_VERSION_template_haskell(2,15,0) + FamilyI (OpenTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _)) decs -> handle typeFamilyName [eqn | TySynInstD eqn <- decs] +#else + FamilyI (OpenTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _)) decs -> handle typeFamilyName [eqn | TySynInstD _name eqn <- decs] +#endif + + _ -> AppT (ConT name) <$> transformTypeFamilies eo typ + | otherwise = AppT (ConT name) <$> transformTypeFamilies eo typ + where + handle :: Name -> [TySynEqn] -> WriterT [ExtraDeclOrGenericInfo] Q Type + handle typeFamilyName eqns = do + name' <- lift $ newName (nameBase typeFamilyName <> "'") + + f <- lift $ newName "f" +#if MIN_VERSION_template_haskell(2,17,0) + let inst1 = DataD [] name' [PlainTV f ()] Nothing [] [] +#else + let inst1 = DataD [] name' [PlainTV f] Nothing [] [] +#endif + tell [ExtraTopLevelDecs [inst1]] + + imageTypes <- lift $ getClosedTypeFamilyImage eqns + inst2 <- lift $ [d|instance (Typeable g, TypeScript g) => TypeScript ($(conT name') g) where + getTypeScriptType _ = $(TH.stringE $ nameBase name) <> "[" <> (getTypeScriptType (Proxy :: Proxy g)) <> "]" + getTypeScriptDeclarations _ = [$(getClosedTypeFamilyInterfaceDecl name eqns)] + getParentTypes _ = $(listE [ [|TSType (Proxy :: Proxy $(return x))|] | x <- imageTypes]) + |] + tell [ExtraTopLevelDecs inst2] + + tell [ExtraParentType (AppT (ConT name') (ConT ''T))] + + ret <- transformTypeFamilies eo (AppT (ConT name') typ) + tell [ExtraConstraint (AppT (ConT ''TypeScript) ret)] + return ret +transformTypeFamilies eo (AppT typ1 typ2) = AppT <$> transformTypeFamilies eo typ1 <*> transformTypeFamilies eo typ2 +transformTypeFamilies eo (SigT typ kind) = flip SigT kind <$> transformTypeFamilies eo typ +transformTypeFamilies eo (InfixT typ1 n typ2) = InfixT <$> transformTypeFamilies eo typ1 <*> pure n <*> transformTypeFamilies eo typ2 +transformTypeFamilies eo (UInfixT typ1 n typ2) = UInfixT <$> transformTypeFamilies eo typ1 <*> pure n <*> transformTypeFamilies eo typ2 +transformTypeFamilies eo (ParensT typ) = ParensT <$> transformTypeFamilies eo typ +#if MIN_VERSION_template_haskell(2,15,0) +transformTypeFamilies eo (AppKindT typ kind) = flip AppKindT kind <$> transformTypeFamilies eo typ +transformTypeFamilies eo (ImplicitParamT s typ) = ImplicitParamT s <$> transformTypeFamilies eo typ +#endif +transformTypeFamilies _ typ = return typ From 29e227179239a0d96f6c45203f53ba8d79aedf0f Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 13 Aug 2021 18:53:07 -0700 Subject: [PATCH 106/208] More refactoring --- aeson-typescript.cabal | 2 + dev/Live6.hs | 1 - src/Data/Aeson/TypeScript/TH.hs | 42 +---------- src/Data/Aeson/TypeScript/TypeManipulation.hs | 70 +++++++++++++++++++ 4 files changed, 73 insertions(+), 42 deletions(-) create mode 100644 src/Data/Aeson/TypeScript/TypeManipulation.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index ce41fab..3354443 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -41,6 +41,7 @@ library Data.Aeson.TypeScript.Instances Data.Aeson.TypeScript.Lookup Data.Aeson.TypeScript.Transform + Data.Aeson.TypeScript.TypeManipulation Data.Aeson.TypeScript.Types Data.Aeson.TypeScript.Util Paths_aeson_typescript @@ -88,6 +89,7 @@ test-suite aeson-typescript-tests Data.Aeson.TypeScript.Recursive Data.Aeson.TypeScript.TH Data.Aeson.TypeScript.Transform + Data.Aeson.TypeScript.TypeManipulation Data.Aeson.TypeScript.Types Data.Aeson.TypeScript.Util Live diff --git a/dev/Live6.hs b/dev/Live6.hs index e856e7a..c0fdf90 100644 --- a/dev/Live6.hs +++ b/dev/Live6.hs @@ -14,7 +14,6 @@ module Live6 where import Data.Aeson as A -import Data.Aeson.TypeScript.Recursive import Data.Aeson.TypeScript.TH import Data.Function import Data.Map diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 70a3bab..3c39dc4 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -153,13 +153,13 @@ import Data.Aeson.TypeScript.Formatting import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Lookup import Data.Aeson.TypeScript.Transform +import Data.Aeson.TypeScript.TypeManipulation import Data.Aeson.TypeScript.Types import Data.Aeson.TypeScript.Util import qualified Data.List as L import Data.Maybe import Data.Proxy import Data.String.Interpolate -import Data.Typeable import Language.Haskell.TH hiding (stringE) import Language.Haskell.TH.Datatype import qualified Language.Haskell.TH.Lib as TH @@ -308,46 +308,6 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci = do lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) |] -searchForConstraints :: ExtraTypeScriptOptions -> Type -> Name -> WriterT [GenericInfo] Q () -searchForConstraints eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) var - | typ == VarT var && (name `L.elem` typeFamiliesToMapToTypeScript) = lift (reify name) >>= \case - FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) _) _ -> do - tell [GenericInfo var (TypeFamilyKey typeFamilyName)] - searchForConstraints eo typ var - FamilyI (OpenTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _)) _ -> do - tell [GenericInfo var (TypeFamilyKey typeFamilyName)] - searchForConstraints eo typ var - _ -> searchForConstraints eo typ var - | otherwise = searchForConstraints eo typ var -searchForConstraints eo (AppT typ1 typ2) var = searchForConstraints eo typ1 var >> searchForConstraints eo typ2 var -searchForConstraints eo (SigT typ _) var = searchForConstraints eo typ var -searchForConstraints eo (InfixT typ1 _ typ2) var = searchForConstraints eo typ1 var >> searchForConstraints eo typ2 var -searchForConstraints eo (UInfixT typ1 _ typ2) var = searchForConstraints eo typ1 var >> searchForConstraints eo typ2 var -searchForConstraints eo (ParensT typ) var = searchForConstraints eo typ var -#if MIN_VERSION_template_haskell(2,15,0) -searchForConstraints eo (AppKindT typ _) var = searchForConstraints eo typ var -searchForConstraints eo (ImplicitParamT _ typ) var = searchForConstraints eo typ var -#endif -searchForConstraints _ _ _ = return () - -hasFreeTypeVariable :: Type -> Bool -hasFreeTypeVariable (VarT _) = True -hasFreeTypeVariable (AppT typ1 typ2) = hasFreeTypeVariable typ1 || hasFreeTypeVariable typ2 -hasFreeTypeVariable (SigT typ _) = hasFreeTypeVariable typ -hasFreeTypeVariable (InfixT typ1 _ typ2) = hasFreeTypeVariable typ1 || hasFreeTypeVariable typ2 -hasFreeTypeVariable (UInfixT typ1 _ typ2) = hasFreeTypeVariable typ1 || hasFreeTypeVariable typ2 -hasFreeTypeVariable (ParensT typ) = hasFreeTypeVariable typ -#if MIN_VERSION_template_haskell(2,15,0) -hasFreeTypeVariable (AppKindT typ _) = hasFreeTypeVariable typ -hasFreeTypeVariable (ImplicitParamT _ typ) = hasFreeTypeVariable typ -#endif -hasFreeTypeVariable _ = False - -unifyGenericVariable :: [GenericInfo] -> String -unifyGenericVariable genericInfos = case [nameBase name | GenericInfo _ (TypeFamilyKey name) <- genericInfos] of - [] -> "" - names -> " extends keyof " <> (L.intercalate " & " names) - -- * Convenience functions -- | Convenience function to generate 'A.ToJSON', 'A.FromJSON', and 'TypeScript' instances simultaneously, so the instances are guaranteed to be in sync. diff --git a/src/Data/Aeson/TypeScript/TypeManipulation.hs b/src/Data/Aeson/TypeScript/TypeManipulation.hs new file mode 100644 index 0000000..dc36c11 --- /dev/null +++ b/src/Data/Aeson/TypeScript/TypeManipulation.hs @@ -0,0 +1,70 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE LambdaCase #-} + +module Data.Aeson.TypeScript.TypeManipulation ( + searchForConstraints + , hasFreeTypeVariable + , unifyGenericVariable + ) where + +import Control.Monad.Writer +import Data.Aeson.TypeScript.Types +import qualified Data.List as L +import Language.Haskell.TH + +#if !MIN_VERSION_base(4,11,0) +import Data.Monoid +#endif + + +searchForConstraints :: ExtraTypeScriptOptions -> Type -> Name -> WriterT [GenericInfo] Q () +searchForConstraints eo@(ExtraTypeScriptOptions {..}) (AppT (ConT name) typ) var + | typ == VarT var && (name `L.elem` typeFamiliesToMapToTypeScript) = lift (reify name) >>= \case + FamilyI (ClosedTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _) _) _ -> do + tell [GenericInfo var (TypeFamilyKey typeFamilyName)] + searchForConstraints eo typ var + FamilyI (OpenTypeFamilyD (TypeFamilyHead typeFamilyName _ _ _)) _ -> do + tell [GenericInfo var (TypeFamilyKey typeFamilyName)] + searchForConstraints eo typ var + _ -> searchForConstraints eo typ var + | otherwise = searchForConstraints eo typ var +searchForConstraints eo (AppT typ1 typ2) var = searchForConstraints eo typ1 var >> searchForConstraints eo typ2 var +searchForConstraints eo (SigT typ _) var = searchForConstraints eo typ var +searchForConstraints eo (InfixT typ1 _ typ2) var = searchForConstraints eo typ1 var >> searchForConstraints eo typ2 var +searchForConstraints eo (UInfixT typ1 _ typ2) var = searchForConstraints eo typ1 var >> searchForConstraints eo typ2 var +searchForConstraints eo (ParensT typ) var = searchForConstraints eo typ var +#if MIN_VERSION_template_haskell(2,15,0) +searchForConstraints eo (AppKindT typ _) var = searchForConstraints eo typ var +searchForConstraints eo (ImplicitParamT _ typ) var = searchForConstraints eo typ var +#endif +searchForConstraints _ _ _ = return () + +hasFreeTypeVariable :: Type -> Bool +hasFreeTypeVariable (VarT _) = True +hasFreeTypeVariable (AppT typ1 typ2) = hasFreeTypeVariable typ1 || hasFreeTypeVariable typ2 +hasFreeTypeVariable (SigT typ _) = hasFreeTypeVariable typ +hasFreeTypeVariable (InfixT typ1 _ typ2) = hasFreeTypeVariable typ1 || hasFreeTypeVariable typ2 +hasFreeTypeVariable (UInfixT typ1 _ typ2) = hasFreeTypeVariable typ1 || hasFreeTypeVariable typ2 +hasFreeTypeVariable (ParensT typ) = hasFreeTypeVariable typ +#if MIN_VERSION_template_haskell(2,15,0) +hasFreeTypeVariable (AppKindT typ _) = hasFreeTypeVariable typ +hasFreeTypeVariable (ImplicitParamT _ typ) = hasFreeTypeVariable typ +#endif +hasFreeTypeVariable _ = False + +unifyGenericVariable :: [GenericInfo] -> String +unifyGenericVariable genericInfos = case [nameBase name | GenericInfo _ (TypeFamilyKey name) <- genericInfos] of + [] -> "" + names -> " extends keyof " <> (L.intercalate " & " names) From c1ab39c0a8ca5ed2ef07915f6d6e8e9c8dcc0cdc Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 13 Aug 2021 19:07:24 -0700 Subject: [PATCH 107/208] A bit more code golf --- src/Data/Aeson/TypeScript/TH.hs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 3c39dc4..e021a45 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -195,10 +195,9 @@ deriveTypeScript' options name extraOptions = do return (var, (unifyGenericVariable genericInfos, tvar)) -- Plug in generic variables and de-family-ify - (newConstructorInfos, extraDeclsOrGenericInfosInitial) <- runWriterT $ forM (datatypeCons datatypeInfo') $ \ci@(ConstructorInfo {..}) -> do - newFields <- forM constructorFields $ \t -> - transformTypeFamilies extraOptions $ mapType genericVariablesAndSuffixes t - return (ci { constructorFields = newFields }) + (newConstructorInfos, extraDeclsOrGenericInfosInitial) <- runWriterT $ forM (datatypeCons datatypeInfo') $ \ci -> + ((\x -> ci { constructorFields = x }) <$>) $ forM (constructorFields ci) $ + transformTypeFamilies extraOptions . mapType genericVariablesAndSuffixes let dti = datatypeInfo' { datatypeCons = newConstructorInfos } -- Build constraints: a TypeScript constraint for every constructor type and one for every type variable. @@ -218,26 +217,22 @@ deriveTypeScript' options name extraOptions = do typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName (datatypeName dti)) $(genericVariablesListExpr True genericVariablesAndSuffixes) $(listE $ fmap return types)|] - let extraDecls = [x | ExtraDecl x <- extraDeclsOrGenericInfos] - let extraTopLevelDecls = mconcat [x | ExtraTopLevelDecs x <- extraDeclsOrGenericInfos] - let predicates = L.nub (constructorPreds <> constructorPreds' <> typeVariablePreds <> [x | ExtraConstraint x <- extraDeclsOrGenericInfos]) - - declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return $ extraDecls)) |] - let extraParentTypes = [x | ExtraParentType x <- extraDeclsOrGenericInfos] + declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return [x | ExtraDecl x <- extraDeclsOrGenericInfos])) |] -- Couldn't figure out how to put the constraints for "instance TypeScript..." in the quasiquote above without -- introducing () when the constraints are empty, which causes "illegal tuple constraint" unless the user enables ConstraintKinds. -- So, just use our mkInstance function getTypeScriptTypeExp <- [|$(TH.stringE $ getTypeName (datatypeName dti)) <> $(getBracketsExpressionAllTypesNoSuffix genericVariablesAndSuffixes)|] getParentTypesExp <- listE [ [|TSType (Proxy :: Proxy $(return t))|] - | t <- (mconcat $ fmap constructorFields (datatypeCons datatypeInfo')) <> extraParentTypes] + | t <- (mconcat $ fmap constructorFields (datatypeCons datatypeInfo')) <> [x | ExtraParentType x <- extraDeclsOrGenericInfos]] + let predicates = L.nub (constructorPreds <> constructorPreds' <> typeVariablePreds <> [x | ExtraConstraint x <- extraDeclsOrGenericInfos]) let inst = [mkInstance predicates (AppT (ConT ''TypeScript) (foldl AppT (ConT name) (getDataTypeVars dti))) [ FunD 'getTypeScriptType [Clause [WildP] (NormalB getTypeScriptTypeExp) []] , FunD 'getTypeScriptDeclarations [Clause [WildP] (NormalB declarationsFunctionBody) []] , FunD 'getParentTypes [Clause [WildP] (NormalB getParentTypesExp) []] ]] - return (extraTopLevelDecls <> inst) + return (mconcat [x | ExtraTopLevelDecs x <- extraDeclsOrGenericInfos] <> inst) -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration handleConstructor :: Options -> DatatypeInfo -> [(Name, (Suffix, Var))] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp From 2a161123dbb115e3b5167dfb13e0c16ca2fe33ee Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 13 Aug 2021 19:11:15 -0700 Subject: [PATCH 108/208] Final code golf for now --- src/Data/Aeson/TypeScript/TH.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index e021a45..710b341 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -195,10 +195,9 @@ deriveTypeScript' options name extraOptions = do return (var, (unifyGenericVariable genericInfos, tvar)) -- Plug in generic variables and de-family-ify - (newConstructorInfos, extraDeclsOrGenericInfosInitial) <- runWriterT $ forM (datatypeCons datatypeInfo') $ \ci -> + ((\x -> (datatypeInfo' { datatypeCons = x })) -> dti, extraDeclsOrGenericInfosInitial) <- runWriterT $ forM (datatypeCons datatypeInfo') $ \ci -> ((\x -> ci { constructorFields = x }) <$>) $ forM (constructorFields ci) $ transformTypeFamilies extraOptions . mapType genericVariablesAndSuffixes - let dti = datatypeInfo' { datatypeCons = newConstructorInfos } -- Build constraints: a TypeScript constraint for every constructor type and one for every type variable. -- Probably overkill/not exactly right, but it's a start. From 1224ab77afe6f3f3263d50a8bb7013fc48fa7bde Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 13 Aug 2021 16:15:32 -0700 Subject: [PATCH 109/208] Considering separate key types --- src/Data/Aeson/TypeScript/Instances.hs | 4 ++-- src/Data/Aeson/TypeScript/Types.hs | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index 7e50b51..8310c54 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -112,11 +112,11 @@ instance TypeScript A.Value where getTypeScriptType _ = "any"; instance (TypeScript a, TypeScript b) => TypeScript (Map a b) where - getTypeScriptType _ = "{[k in " ++ getTypeScriptType (Proxy :: Proxy a) ++ "]?: " ++ getTypeScriptType (Proxy :: Proxy b) ++ "}" + getTypeScriptType _ = "{[k in " ++ getTypeScriptKeyType (Proxy :: Proxy a) ++ "]?: " ++ getTypeScriptType (Proxy :: Proxy b) ++ "}" getParentTypes _ = [TSType (Proxy :: Proxy a), TSType (Proxy :: Proxy b)] instance (TypeScript a, TypeScript b) => TypeScript (HashMap a b) where - getTypeScriptType _ = [i|{[k in #{getTypeScriptType (Proxy :: Proxy a)}]?: #{getTypeScriptType (Proxy :: Proxy b)}}|] + getTypeScriptType _ = [i|{[k in #{getTypeScriptKeyType (Proxy :: Proxy a)}]?: #{getTypeScriptType (Proxy :: Proxy b)}}|] getParentTypes _ = L.nub [TSType (Proxy :: Proxy a), TSType (Proxy :: Proxy b)] instance (TypeScript a) => TypeScript (Set a) where diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 414acff..5cbda3f 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -54,6 +54,10 @@ class (Typeable a) => TypeScript a where getTypeScriptType :: Proxy a -> String -- ^ Get the type as a string. + getTypeScriptKeyType :: Proxy a -> String + getTypeScriptKeyType proxy = getTypeScriptType proxy + -- ^ Get the key type as a string. + getTypeScriptOptional :: Proxy a -> Bool -- ^ Get a flag representing whether this type is optional. getTypeScriptOptional _ = False From b651e53be5942c28550cfd2a177e49a0a786d2a1 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 13 Aug 2021 19:48:35 -0700 Subject: [PATCH 110/208] Be able to set key type --- src/Data/Aeson/TypeScript/TH.hs | 9 +++++++-- src/Data/Aeson/TypeScript/Types.hs | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 710b341..98e2850 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -226,11 +226,16 @@ deriveTypeScript' options name extraOptions = do getParentTypesExp <- listE [ [|TSType (Proxy :: Proxy $(return t))|] | t <- (mconcat $ fmap constructorFields (datatypeCons datatypeInfo')) <> [x | ExtraParentType x <- extraDeclsOrGenericInfos]] let predicates = L.nub (constructorPreds <> constructorPreds' <> typeVariablePreds <> [x | ExtraConstraint x <- extraDeclsOrGenericInfos]) - let inst = [mkInstance predicates (AppT (ConT ''TypeScript) (foldl AppT (ConT name) (getDataTypeVars dti))) [ + keyTypeDecl <- case keyType extraOptions of + Nothing -> return [] + Just kt -> do + keyTypeExp <- [|$(TH.stringE kt)|] + return $ [FunD 'getTypeScriptKeyType [Clause [WildP] (NormalB keyTypeExp) []]] + let inst = [mkInstance predicates (AppT (ConT ''TypeScript) (foldl AppT (ConT name) (getDataTypeVars dti))) ([ FunD 'getTypeScriptType [Clause [WildP] (NormalB getTypeScriptTypeExp) []] , FunD 'getTypeScriptDeclarations [Clause [WildP] (NormalB declarationsFunctionBody) []] , FunD 'getParentTypes [Clause [WildP] (NormalB getParentTypesExp) []] - ]] + ] <> keyTypeDecl)] return (mconcat [x | ExtraTopLevelDecs x <- extraDeclsOrGenericInfos] <> inst) -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 5cbda3f..e3c7c6c 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -178,10 +178,11 @@ allStarConstructors'' = ["T1", "T2", "T3", "T4", "T5", "T6", "T7", "T8", "T9", " data ExtraTypeScriptOptions = ExtraTypeScriptOptions { typeFamiliesToMapToTypeScript :: [Name] + , keyType :: Maybe String } defaultExtraTypeScriptOptions :: ExtraTypeScriptOptions -defaultExtraTypeScriptOptions = ExtraTypeScriptOptions [] +defaultExtraTypeScriptOptions = ExtraTypeScriptOptions [] Nothing data ExtraDeclOrGenericInfo = ExtraDecl Exp | ExtraGeneric GenericInfo From 1db89b96ebf004d5d140293ec465c82afd4abae5 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 13 Aug 2021 19:59:23 -0700 Subject: [PATCH 111/208] Fix up exports for ExtraTypeScriptOptions --- src/Data/Aeson/TypeScript/TH.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 98e2850..273e950 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -130,7 +130,10 @@ module Data.Aeson.TypeScript.TH ( , ExportMode(..) -- * Advanced options - , ExtraTypeScriptOptions(..) + , defaultExtraTypeScriptOptions + , keyType + , typeFamiliesToMapToTypeScript + , ExtraTypeScriptOptions -- * Convenience tools , HasJSONOptions(..) From cbfa1e1fcce21a2449ab9de4bc48eb418ba95ebb Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sat, 14 Aug 2021 17:52:23 -0700 Subject: [PATCH 112/208] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ec9a7f..a44f577 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Encode maps as mapped types (allows you to have unions as keys) * Support mapping open type families to lookup types (+ progress on handling promoted types) * Improve propagation of T variables in declarations +* Add support for "key types", in case you have custom implementations of FromJSONKey/ToJSONKey ## 0.3.0.1 From f9c3d801870f51dd2c49f94b8179737014e96cc5 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sat, 21 Aug 2021 18:34:54 -0700 Subject: [PATCH 113/208] Fix deriveTypeScript' calls --- dev/Live.hs | 2 +- dev/Live4.hs | 2 +- test/ClosedTypeFamilies.hs | 4 ++-- test/OpenTypeFamilies.hs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/Live.hs b/dev/Live.hs index 3a61420..496486b 100644 --- a/dev/Live.hs +++ b/dev/Live.hs @@ -69,7 +69,7 @@ data Complex3 k = Complex3 { } deriving (Show) -deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment]) +deriveTypeScript' A.defaultOptions ''UserT (defaultExtraTypeScriptOptions { typeFamiliesToMapToTypeScript = [''DeployEnvironment] }) main :: IO () main = getTypeScriptDeclarationsRecursively (Proxy :: Proxy (UserT T Identity)) diff --git a/dev/Live4.hs b/dev/Live4.hs index 294f0e8..3a8e354 100644 --- a/dev/Live4.hs +++ b/dev/Live4.hs @@ -25,7 +25,7 @@ type instance DeployEnvironment2 SingleNodeEnvironment = SingleDE type instance DeployEnvironment2 K8SEnvironment = K8SDE type instance DeployEnvironment2 T = () newtype Simple env = Simple (DeployEnvironment2 env) -$(deriveTypeScript' A.defaultOptions ''Simple (ExtraTypeScriptOptions [''DeployEnvironment2])) +$(deriveTypeScript' A.defaultOptions ''Simple (defaultExtraTypeScriptOptions { typeFamiliesToMapToTypeScript = [''DeployEnvironment2] })) main :: IO () main = getTypeScriptDeclarationsRecursively (Proxy @(Simple T)) diff --git a/test/ClosedTypeFamilies.hs b/test/ClosedTypeFamilies.hs index 122f94f..9038c21 100644 --- a/test/ClosedTypeFamilies.hs +++ b/test/ClosedTypeFamilies.hs @@ -38,14 +38,14 @@ data UserT env f = User { , _userCreatedAt :: Columnar f Int , _userDeployEnvironment :: Columnar f (DeployEnvironment env) } -$(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) +$(deriveTypeScript' A.defaultOptions ''UserT (defaultExtraTypeScriptOptions { typeFamiliesToMapToTypeScript = [''DeployEnvironment] })) type family DeployEnvironment2 env = result | result -> env where DeployEnvironment2 SingleNodeEnvironment = SingleDE DeployEnvironment2 K8SEnvironment = K8SDE DeployEnvironment2 T = () newtype Simple env = Simple (DeployEnvironment2 env) -$(deriveTypeScript' A.defaultOptions ''Simple (ExtraTypeScriptOptions [''DeployEnvironment2])) +$(deriveTypeScript' A.defaultOptions ''Simple (defaultExtraTypeScriptOptions { typeFamiliesToMapToTypeScript = [''DeployEnvironment2] })) tests :: SpecWith () tests = describe "Type families" $ do diff --git a/test/OpenTypeFamilies.hs b/test/OpenTypeFamilies.hs index 222ef15..d0c1c74 100644 --- a/test/OpenTypeFamilies.hs +++ b/test/OpenTypeFamilies.hs @@ -38,14 +38,14 @@ data UserT env f = User { , _userCreatedAt :: Columnar f Int , _userDeployEnvironment :: Columnar f (DeployEnvironment env) } -$(deriveTypeScript' A.defaultOptions ''UserT (ExtraTypeScriptOptions [''DeployEnvironment])) +$(deriveTypeScript' A.defaultOptions ''UserT (defaultExtraTypeScriptOptions { typeFamiliesToMapToTypeScript = [''DeployEnvironment] })) type family DeployEnvironment2 env = result | result -> env type instance DeployEnvironment2 SingleNodeEnvironment = SingleDE type instance DeployEnvironment2 K8SEnvironment = K8SDE type instance DeployEnvironment2 T = () newtype Simple env = Simple (DeployEnvironment2 env) -$(deriveTypeScript' A.defaultOptions ''Simple (ExtraTypeScriptOptions [''DeployEnvironment2])) +$(deriveTypeScript' A.defaultOptions ''Simple (defaultExtraTypeScriptOptions { typeFamiliesToMapToTypeScript = [''DeployEnvironment2] })) tests :: SpecWith () tests = describe "Type families" $ do From 29db9bf4d8e3d2957261b0104f8c0852060dbfbb Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sat, 21 Aug 2021 18:44:33 -0700 Subject: [PATCH 114/208] Add failing test for unwrapUnaryRecords --- aeson-typescript.cabal | 2 +- src/Data/Aeson/TypeScript/TH.hs | 8 ++++++ test/LiveLogging.hs | 32 ----------------------- test/NoOmitNothingFields.hs | 39 +++++++++++++++------------- test/Spec.hs | 4 ++- test/UnwrapUnaryRecords.hs | 45 +++++++++++++++++++++++++++++++++ 6 files changed, 78 insertions(+), 52 deletions(-) delete mode 100644 test/LiveLogging.hs create mode 100644 test/UnwrapUnaryRecords.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 3354443..a93c213 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -68,7 +68,6 @@ test-suite aeson-typescript-tests Formatting Generic HigherKind - LiveLogging NoOmitNothingFields ObjectWithSingleFieldNoTagSingleConstructors ObjectWithSingleFieldTagSingleConstructors @@ -81,6 +80,7 @@ test-suite aeson-typescript-tests TwoElemArrayTagSingleConstructors UntaggedNoTagSingleConstructors UntaggedTagSingleConstructors + UnwrapUnaryRecords Util Data.Aeson.TypeScript.Formatting Data.Aeson.TypeScript.Instances diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 273e950..4209d04 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -284,6 +284,12 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci = do | constructorVariant ci == NormalConstructor -> do encoding <- tupleEncoding tell [ExtraDecl encoding] + +#if MIN_VERSION_aeson(0,10,0) + | unwrapUnaryRecords options && (isSingleRecordConstructor ci) -> do + undefined +#endif + | otherwise -> do tsFields <- getTSFields decl <- lift $ assembleInterfaceDeclaration (ListE tsFields) @@ -309,6 +315,8 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci = do _ -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) |] + isSingleRecordConstructor (constructorVariant -> RecordConstructor [x]) = True + isSingleRecordConstructor _ = False -- * Convenience functions diff --git a/test/LiveLogging.hs b/test/LiveLogging.hs deleted file mode 100644 index 44f9ae5..0000000 --- a/test/LiveLogging.hs +++ /dev/null @@ -1,32 +0,0 @@ -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilies #-} - -module LiveLogging where - -import Data.Kind -import Prelude hiding (Double) - - -data LoggingSource = SGeneral - -data LoggingSourceTagged s where - General :: LoggingSourceTagged 'SGeneral - -type family ParamsFamily (q :: LoggingSource) :: Type where - ParamsFamily 'SGeneral = String - -data HigherKindWithTypeFamily s = TapMessageParams { params :: ParamsFamily s } --- $(deriveTypeScript A.defaultOptions ''HigherKindWithTypeFamily) - --- main = do --- putStrLn $(stringE . pprint =<< (deriveTypeScript A.defaultOptions ''TestT)) diff --git a/test/NoOmitNothingFields.hs b/test/NoOmitNothingFields.hs index fbd5636..fa03d99 100644 --- a/test/NoOmitNothingFields.hs +++ b/test/NoOmitNothingFields.hs @@ -1,17 +1,17 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE MonoLocalBinds #-} +{-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -module NoOmitNothingFields (main, tests) where +module NoOmitNothingFields (allTests) where import Data.Aeson as A import Data.Aeson.TypeScript.TH @@ -20,21 +20,24 @@ import Data.Proxy import Test.Hspec import TestBoilerplate -$(testDeclarations "NoOmitNothingFields" (A.defaultOptions {omitNothingFields=False})) +$(testDeclarations "NoOmitNothingFields" (A.defaultOptions {omitNothingFields = False})) -main :: IO () -main = hspec $ describe "NoOmitNothingFields" $ do +allTests :: SpecWith () +allTests = describe "NoOmitNothingFields" $ do it "encodes as expected" $ do let decls = getTypeScriptDeclarations (Proxy :: Proxy Optional) - decls `shouldBe` [TSInterfaceDeclaration { - interfaceName = "Optional" + decls `shouldBe` [TSTypeAlternatives { + typeName = "Optional" + , typeGenericVariables = [] + , alternativeTypes = ["IOptional"] + } + , TSInterfaceDeclaration { + interfaceName = "IOptional" , interfaceGenericVariables = [] - , interfaceMembers = [ - TSField {fieldOptional = False - , fieldName = "optionalInt" - , fieldType = "number | null"} - ] + , interfaceMembers = [TSField {fieldOptional = False + , fieldName = "optionalInt" + , fieldType = "number | null"}] }] tests diff --git a/test/Spec.hs b/test/Spec.hs index cdff2a1..611fc36 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -18,6 +18,7 @@ import qualified UntaggedNoTagSingleConstructors import qualified UntaggedTagSingleConstructors import qualified OmitNothingFields import qualified NoOmitNothingFields +import qualified UnwrapUnaryRecords main :: IO () @@ -36,4 +37,5 @@ main = hspec $ do UntaggedTagSingleConstructors.tests UntaggedNoTagSingleConstructors.tests OmitNothingFields.tests - NoOmitNothingFields.tests + NoOmitNothingFields.allTests + UnwrapUnaryRecords.allTests diff --git a/test/UnwrapUnaryRecords.hs b/test/UnwrapUnaryRecords.hs new file mode 100644 index 0000000..e4cbbde --- /dev/null +++ b/test/UnwrapUnaryRecords.hs @@ -0,0 +1,45 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module UnwrapUnaryRecords (allTests) where + +import Data.Aeson as A +import Data.Aeson.TypeScript.TH +import Data.Proxy +import Test.Hspec +import TestBoilerplate +import Util + +-- Between Aeson 0.11.3.0 and 1.0.0.0, UntaggedValue was added +-- Disable these tests if it's not present +#if MIN_VERSION_aeson(0,10,0) +$(testDeclarations "UnwrapUnaryRecords" (setTagSingleConstructors $ A.defaultOptions {unwrapUnaryRecords = True})) + +allTests = describe "NoOmitNothingFields" $ do + it "encodes as expected" $ do + let decls = getTypeScriptDeclarations (Proxy :: Proxy OneField) + + decls `shouldBe` [] + + tests + + +#else +tests :: SpecWith () +tests = describe "UnwrapUnaryRecords" $ it "tests are disabled for this Aeson version" $ 2 `shouldBe` 2 + +allTests = tests +#endif + +main :: IO () +main = hspec allTests From 7e4c7b1d108280147a6eb5fdfae94e18fcdcb60b Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sat, 21 Aug 2021 19:35:03 -0700 Subject: [PATCH 115/208] Working on unwrapUnaryRecords --- src/Data/Aeson/TypeScript/TH.hs | 9 ++++++++- test/UnwrapUnaryRecords.hs | 21 +++++++++++---------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 4209d04..6dbfe92 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -287,7 +287,14 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci = do #if MIN_VERSION_aeson(0,10,0) | unwrapUnaryRecords options && (isSingleRecordConstructor ci) -> do - undefined + let [typ] = constructorFields ci + stringExp <- lift $ case typ of + (AppT (ConT name) t) | name == ''Maybe && not (omitNothingFields options) -> [|$(getTypeAsStringExp t) <> " | null"|] + _ -> getTypeAsStringExp typ + alternatives <- lift [|TSTypeAlternatives $(TH.stringE interfaceName) + $(genericVariablesListExpr True genericVariables) + [$(return stringExp)]|] + tell [ExtraDecl alternatives] #endif | otherwise -> do diff --git a/test/UnwrapUnaryRecords.hs b/test/UnwrapUnaryRecords.hs index e4cbbde..2b93486 100644 --- a/test/UnwrapUnaryRecords.hs +++ b/test/UnwrapUnaryRecords.hs @@ -15,25 +15,26 @@ module UnwrapUnaryRecords (allTests) where import Data.Aeson as A import Data.Aeson.TypeScript.TH +import Data.Aeson.TypeScript.Types import Data.Proxy import Test.Hspec import TestBoilerplate -import Util --- Between Aeson 0.11.3.0 and 1.0.0.0, UntaggedValue was added --- Disable these tests if it's not present + #if MIN_VERSION_aeson(0,10,0) -$(testDeclarations "UnwrapUnaryRecords" (setTagSingleConstructors $ A.defaultOptions {unwrapUnaryRecords = True})) +$(testDeclarations "UnwrapUnaryRecords" (A.defaultOptions {unwrapUnaryRecords = True})) -allTests = describe "NoOmitNothingFields" $ do +allTests :: SpecWith () +allTests = describe "UnwrapUnaryRecords" $ do it "encodes as expected" $ do let decls = getTypeScriptDeclarations (Proxy :: Proxy OneField) - decls `shouldBe` [] + decls `shouldBe` [ + TSTypeAlternatives {typeName = "OneField", typeGenericVariables = [], alternativeTypes = ["IOneField"]} + ,TSTypeAlternatives {typeName = "IOneField", typeGenericVariables = [], alternativeTypes = ["string"]} + ] tests - - #else tests :: SpecWith () tests = describe "UnwrapUnaryRecords" $ it "tests are disabled for this Aeson version" $ 2 `shouldBe` 2 @@ -41,5 +42,5 @@ tests = describe "UnwrapUnaryRecords" $ it "tests are disabled for this Aeson ve allTests = tests #endif -main :: IO () -main = hspec allTests +-- main :: IO () +-- main = hspec allTests From 5cea17855d0142be2527f363d3948dfcaa2ca100 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 12 Sep 2021 19:40:13 -0700 Subject: [PATCH 116/208] Add recursivelyDeriveMissingTypeScriptInstancesFor etc. --- CHANGELOG.md | 3 +- aeson-typescript.cabal | 2 + package.yaml | 1 + src/Data/Aeson/TypeScript/Recursive.hs | 123 ++++++++++++++++++++++++- 4 files changed, 123 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a44f577..a6093d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change log -## 0.4.0.0 +## 0.4.0.0 (unreleased) * Add new built-in instances (Word8, Int32, Int64, Map, HashSet) * Export TSField in the Internal module @@ -9,6 +9,7 @@ * Support mapping open type families to lookup types (+ progress on handling promoted types) * Improve propagation of T variables in declarations * Add support for "key types", in case you have custom implementations of FromJSONKey/ToJSONKey +* Add ability to recursively derive missing instances (fragile) ## 0.3.0.1 diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index a93c213..291198b 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -56,6 +56,7 @@ library , template-haskell , text , th-abstraction + , transformers , unordered-containers default-language: Haskell2010 @@ -120,5 +121,6 @@ test-suite aeson-typescript-tests , temporary , text , th-abstraction + , transformers , unordered-containers default-language: Haskell2010 diff --git a/package.yaml b/package.yaml index f08c29a..d6aa63f 100644 --- a/package.yaml +++ b/package.yaml @@ -34,6 +34,7 @@ dependencies: - template-haskell - text - th-abstraction +- transformers - unordered-containers library: diff --git a/src/Data/Aeson/TypeScript/Recursive.hs b/src/Data/Aeson/TypeScript/Recursive.hs index a67ecee..9b724db 100755 --- a/src/Data/Aeson/TypeScript/Recursive.hs +++ b/src/Data/Aeson/TypeScript/Recursive.hs @@ -1,22 +1,53 @@ -{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, ExistentialQuantification, FlexibleInstances, NamedFieldPuns, MultiWayIf, ViewPatterns, LambdaCase, PolyKinds #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE PolyKinds #-} module Data.Aeson.TypeScript.Recursive ( + -- * Getting declarations recursively getTransitiveClosure , getTypeScriptDeclarationsRecursively + + -- * Deriving missing instances recursively + , recursivelyDeriveMissingTypeScriptInstancesFor + , recursivelyDeriveMissingInstancesFor + , deriveInstanceIfNecessary + , doesTypeScriptInstanceExist + , getAllParentTypes ) where +import Control.Monad.State +import Control.Monad.Trans.Maybe +import Control.Monad.Writer import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.TH +import Data.Bifunctor import Data.Function +import qualified Data.List as L +import Data.Maybe import Data.Proxy import qualified Data.Set as S +import Data.String.Interpolate +import Language.Haskell.TH as TH +import Language.Haskell.TH.Datatype +import Language.Haskell.TH.Syntax hiding (lift) getTransitiveClosure :: S.Set TSType -> S.Set TSType -getTransitiveClosure initialTypes = fix (\loop items -> let items' = S.unions (items : [getMore x | x <- S.toList items]) in - if | items' == items -> items - | otherwise -> loop items' - ) initialTypes +getTransitiveClosure = fix $ \loop items -> + let items' = S.unions (items : [getMore x | x <- S.toList items]) + in if + | items' == items -> items + | otherwise -> loop items' + where getMore :: TSType -> S.Set TSType getMore (TSType x) = S.fromList $ getParentTypes x @@ -25,3 +56,85 @@ getTypeScriptDeclarationsRecursively initialType = S.toList $ S.fromList declara where closure = getTransitiveClosure (S.fromList [TSType initialType]) declarations = mconcat [getTypeScriptDeclarations x | TSType x <- S.toList closure] + + +-- * Recursively deriving missing TypeScript interfaces + +recursivelyDeriveMissingTypeScriptInstancesFor :: (Monoid w) => Name -> (Name -> Q w) -> Q w +recursivelyDeriveMissingTypeScriptInstancesFor = recursivelyDeriveMissingInstancesFor doesTypeScriptInstanceExist + +recursivelyDeriveMissingInstancesFor :: (Monoid w) => (Name -> Q Bool) -> Name -> (Name -> Q w) -> Q w +recursivelyDeriveMissingInstancesFor doesInstanceExist name deriveFn = execWriterT $ do + deriveInstanceIfNecessary name deriveFn + + names <- lift $ getAllParentTypes name doesInstanceExist + forM_ names $ \n -> deriveInstanceIfNecessary n deriveFn + +deriveInstanceIfNecessary :: (Monoid w) => Name -> (Name -> Q w) -> WriterT w Q () +deriveInstanceIfNecessary name deriveFn = do + lift (doesTypeScriptInstanceExist name) >>= \case + True -> return () + False -> do + (lift $ nothingOnFail (deriveFn name)) >>= \case + Nothing -> lift $ reportWarning [i|Failed to derive decls for name '#{name}'|] + Just x -> tell x + +doesTypeScriptInstanceExist :: Name -> Q Bool +doesTypeScriptInstanceExist name = do + result :: Maybe Bool <- runMaybeT $ do + (DatatypeInfo {..}) <- MaybeT $ nothingOnFail $ reifyDatatype name + + -- Skip names with type parameters for now + when (datatypeVars /= []) $ fail "" + + MaybeT $ nothingOnFail $ isInstance ''TypeScript [ConT name] + + return $ fromMaybe True result + +getAllParentTypes :: Name -> (Name -> Q Bool) -> Q [Name] +getAllParentTypes name pruneFn = reverse <$> execStateT (getAllParentTypes' name pruneFn) [] + where + getAllParentTypes' :: Name -> (Name -> Q Bool) -> StateT [Name] Q () + getAllParentTypes' nm pfn = (lift $ nothingOnFail $ pfn nm) >>= \case + Nothing -> return () + Just True -> return () + Just False -> (lift $ nothingOnFail (reifyDatatype nm)) >>= \case + Nothing -> do + lift $ reportWarning [i|Failed to reify: '#{nm}'|] + Just (DatatypeInfo {..}) -> do + let parentTypes = mconcat $ fmap constructorFields datatypeCons + + let maybeRecurse n = do + st <- get + unless (n `L.elem` st) $ do + modify (n :) + getAllParentTypes' n pfn + + forM_ parentTypes $ \typ -> do + let names :: [Name] = fst $ execState (getNamesFromType typ) ([], [typ]) + forM_ names maybeRecurse + + getNamesFromType :: Type -> State ([Name], [Type]) () + getNamesFromType (ConT n) = modify (first $ addIfNotPresent n) + getNamesFromType (AppT t1 t2) = handleTwoTypes t1 t2 + getNamesFromType (InfixT t1 _ t2) = handleTwoTypes t1 t2 + getNamesFromType (UInfixT t1 _ t2) = handleTwoTypes t1 t2 + getNamesFromType _ = return () + + handleTwoTypes t1 t2 = do + (_, visitedTypes) <- get + unless (t1 `L.elem` visitedTypes) $ do + modify (second (t1 :)) + getNamesFromType t1 + + (_, visitedTypes') <- get + unless (t2 `L.elem` visitedTypes') $ do + modify (second (t2 :)) + getNamesFromType t2 + + addIfNotPresent :: (Eq a) => a -> [a] -> [a] + addIfNotPresent x xs | x `L.elem` xs = xs + addIfNotPresent x xs = x : xs + +nothingOnFail :: Q a -> Q (Maybe a) +nothingOnFail action = recover (return Nothing) (Just <$> action) From 8985dcfef73873382b5ee05137673053ada12b20 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 12 Sep 2021 19:46:12 -0700 Subject: [PATCH 117/208] Bump version number in preparation for next release --- aeson-typescript.cabal | 2 +- package.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 291198b..7617056 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: aeson-typescript -version: 0.3.0.1 +version: 0.4.0.0 synopsis: Generate TypeScript definition files from your ADTs description: Please see the README on Github at category: Text, Web, JSON diff --git a/package.yaml b/package.yaml index d6aa63f..3627dc5 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: aeson-typescript -version: 0.3.0.1 +version: 0.4.0.0 github: "codedownio/aeson-typescript" license: BSD3 category: Text, Web, JSON From 16d6c2a40730fdc72784ca70391b68c5086d78a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20Hamb=C3=BCchen?= Date: Mon, 4 Oct 2021 21:38:07 +0200 Subject: [PATCH 118/208] Export `defaultFormattingOptions`. No point having a default when the user cannot access to override it! This was forgotten in PR #22 when extracting it from PR #17. --- src/Data/Aeson/TypeScript/TH.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 6dbfe92..8731584 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -126,6 +126,7 @@ module Data.Aeson.TypeScript.TH ( , formatTSDeclarations' , formatTSDeclaration , FormattingOptions(..) + , defaultFormattingOptions , SumTypeFormat(..) , ExportMode(..) From 305cd581fce927ec3a5792ee2cf8b0e7208d901e Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 7 Feb 2022 22:02:40 -0800 Subject: [PATCH 119/208] Sort type family interface keys so they're deterministic --- src/Data/Aeson/TypeScript/Lookup.hs | 4 +++- stack.yaml | 3 +-- stack.yaml.lock | 8 ++++---- test/ClosedTypeFamilies.hs | 10 +++++----- test/OpenTypeFamilies.hs | 2 +- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Lookup.hs b/src/Data/Aeson/TypeScript/Lookup.hs index ccb1f32..29c254b 100644 --- a/src/Data/Aeson/TypeScript/Lookup.hs +++ b/src/Data/Aeson/TypeScript/Lookup.hs @@ -18,6 +18,8 @@ module Data.Aeson.TypeScript.Lookup where import Control.Monad import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.Types +import Data.Function +import qualified Data.List as L import Data.Proxy import Data.String.Interpolate import Language.Haskell.TH hiding (stringE) @@ -53,7 +55,7 @@ getClosedTypeFamilyInterfaceDecl name eqns = do #endif x -> fail [i|aeson-typescript doesn't know yet how to handle this type family equation: '#{x}'|] - [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] $(listE $ fmap return fields) |] + [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] (L.sortBy (compare `on` fieldName) $(listE $ fmap return fields)) |] getClosedTypeFamilyImage :: [TySynEqn] -> Q [Type] getClosedTypeFamilyImage eqns = do diff --git a/stack.yaml b/stack.yaml index 2b993a4..9da7a42 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,6 +1,5 @@ -resolver: lts-18.3 -compiler: ghc-8.10.5 +resolver: nightly-2022-01-15 packages: - . diff --git a/stack.yaml.lock b/stack.yaml.lock index 7940767..d224fde 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -6,7 +6,7 @@ packages: [] snapshots: - completed: - size: 585603 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/3.yaml - sha256: 694573e96dca34db5636edb1fe6c96bb233ca0f9fb96c1ead1671cdfa9bd73e9 - original: lts-18.3 + size: 621914 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/1/15.yaml + sha256: da9648bb5bf30143b3b45c0daeb2c06ee09fa2af08d362672acf318c6eac1724 + original: nightly-2022-01-15 diff --git a/test/ClosedTypeFamilies.hs b/test/ClosedTypeFamilies.hs index 9038c21..45548c2 100644 --- a/test/ClosedTypeFamilies.hs +++ b/test/ClosedTypeFamilies.hs @@ -48,13 +48,13 @@ newtype Simple env = Simple (DeployEnvironment2 env) $(deriveTypeScript' A.defaultOptions ''Simple (defaultExtraTypeScriptOptions { typeFamiliesToMapToTypeScript = [''DeployEnvironment2] })) tests :: SpecWith () -tests = describe "Type families" $ do +tests = describe "Closed type families" $ do describe "simple newtype" $ do it [i|makes the declaration and types correctly|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Simple T))) `shouldBe` ([ TSInterfaceDeclaration "DeployEnvironment2" [] [ - TSField False "\"single_node_env\"" "\"single\"" - , TSField False "\"k8s_env\"" "\"k8s\"" + TSField False "\"k8s_env\"" "\"k8s\"" + , TSField False "\"single_node_env\"" "\"single\"" , TSField False "T" "void" ] , TSTypeAlternatives "ISimple" ["T extends keyof DeployEnvironment2"] ["DeployEnvironment2[T]"] @@ -75,8 +75,8 @@ tests = describe "Type families" $ do it [i|get the declarations recursively|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (UserT T Identity))) `shouldBe` ([ TSInterfaceDeclaration "DeployEnvironment" [] [ - TSField False "\"single_node_env\"" "\"single\"" - , TSField False "\"k8s_env\"" "\"k8s\"" + TSField False "\"k8s_env\"" "\"k8s\"" + , TSField False "\"single_node_env\"" "\"single\"" , TSField False "T" "void" ] , TSInterfaceDeclaration "IUser" ["T extends keyof DeployEnvironment"] [ diff --git a/test/OpenTypeFamilies.hs b/test/OpenTypeFamilies.hs index d0c1c74..810154f 100644 --- a/test/OpenTypeFamilies.hs +++ b/test/OpenTypeFamilies.hs @@ -48,7 +48,7 @@ newtype Simple env = Simple (DeployEnvironment2 env) $(deriveTypeScript' A.defaultOptions ''Simple (defaultExtraTypeScriptOptions { typeFamiliesToMapToTypeScript = [''DeployEnvironment2] })) tests :: SpecWith () -tests = describe "Type families" $ do +tests = describe "Open type families" $ do describe "simple newtype" $ do it [i|makes the declaration and types correctly|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Simple T))) `shouldBe` ([ From c0cd1226aa5a8fc1ebc9f11b21ae25c8f1b8fc1e Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 17 Mar 2022 18:28:00 -0700 Subject: [PATCH 120/208] Prepare for 0.4.0.0 release --- CHANGELOG.md | 2 +- LICENSE | 2 +- aeson-typescript.cabal | 2 +- package.yaml | 2 +- src/Data/Aeson/TypeScript/TH.hs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6093d3..cb2be46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change log -## 0.4.0.0 (unreleased) +## 0.4.0.0 * Add new built-in instances (Word8, Int32, Int64, Map, HashSet) * Export TSField in the Internal module diff --git a/LICENSE b/LICENSE index 5dfaa25..9ff54f5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright Tom McLaughlin (c) 2021 +Copyright Tom McLaughlin (c) 2022 All rights reserved. diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 7617056..72f4945 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -13,7 +13,7 @@ homepage: https://github.com/codedownio/aeson-typescript#readme bug-reports: https://github.com/codedownio/aeson-typescript/issues author: Tom McLaughlin maintainer: tom@codedown.io -copyright: 2021 CodeDown +copyright: 2022 CodeDown license: BSD3 license-file: LICENSE build-type: Simple diff --git a/package.yaml b/package.yaml index 3627dc5..437c2dc 100644 --- a/package.yaml +++ b/package.yaml @@ -5,7 +5,7 @@ license: BSD3 category: Text, Web, JSON author: "Tom McLaughlin" maintainer: "tom@codedown.io" -copyright: "2021 CodeDown" +copyright: "2022 CodeDown" extra-source-files: - README.md diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 8731584..2925c16 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -15,7 +15,7 @@ {-| Module: Data.Aeson.TypeScript.TH -Copyright: (c) 2021 Tom McLaughlin +Copyright: (c) 2022 Tom McLaughlin License: BSD3 Stability: experimental Portability: portable From 6bff8f9e046a2ca4e1affd110d0a634d42ca004b Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 25 Apr 2022 03:28:18 -0700 Subject: [PATCH 121/208] Add TypeScript Int16 --- src/Data/Aeson/TypeScript/Instances.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index 8310c54..d4f650c 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -53,6 +53,9 @@ instance TypeScript Bool where instance TypeScript Int where getTypeScriptType _ = "number" +instance TypeScript Int16 where + getTypeScriptType _ = "number" + instance TypeScript Int32 where getTypeScriptType _ = "number" From 1066fb8d72e642bfd4e2317e3a1f31be049e11ab Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 20 May 2022 23:26:51 -0700 Subject: [PATCH 122/208] Add TypeScript (A.KeyMap a) instance for aeson 2 --- src/Data/Aeson/TypeScript/Instances.hs | 20 +++++++++++++++++++- stack.yaml | 4 +++- stack.yaml.lock | 8 ++++---- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index d4f650c..13dcbfe 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -1,4 +1,12 @@ -{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, ExistentialQuantification, FlexibleInstances, OverlappingInstances, CPP #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE OverlappingInstances #-} +{-# LANGUAGE CPP #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -- Note: the OverlappingInstances pragma is only here so the overlapping instances in this file @@ -25,6 +33,10 @@ import GHC.Int import Data.Monoid #endif +#if MIN_VERSION_aeson(2,0,0) +import qualified Data.Aeson.KeyMap as A +#endif + instance TypeScript () where getTypeScriptType _ = "void" @@ -122,6 +134,12 @@ instance (TypeScript a, TypeScript b) => TypeScript (HashMap a b) where getTypeScriptType _ = [i|{[k in #{getTypeScriptKeyType (Proxy :: Proxy a)}]?: #{getTypeScriptType (Proxy :: Proxy b)}}|] getParentTypes _ = L.nub [TSType (Proxy :: Proxy a), TSType (Proxy :: Proxy b)] +#if MIN_VERSION_aeson(2,0,0) +instance (TypeScript a) => TypeScript (A.KeyMap a) where + getTypeScriptType _ = [i|{[k]?: #{getTypeScriptType (Proxy :: Proxy a)}}|] + getParentTypes _ = L.nub [TSType (Proxy :: Proxy a)] +#endif + instance (TypeScript a) => TypeScript (Set a) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) <> "[]"; getParentTypes _ = [TSType (Proxy :: Proxy a)] diff --git a/stack.yaml b/stack.yaml index 9da7a42..78cc250 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,5 +1,7 @@ -resolver: nightly-2022-01-15 +resolver: lts-19.7 +# resolver: nightly-2022-02-07 +# resolver: nightly-2022-01-15 packages: - . diff --git a/stack.yaml.lock b/stack.yaml.lock index d224fde..8787e19 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -6,7 +6,7 @@ packages: [] snapshots: - completed: - size: 621914 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/1/15.yaml - sha256: da9648bb5bf30143b3b45c0daeb2c06ee09fa2af08d362672acf318c6eac1724 - original: nightly-2022-01-15 + size: 618884 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/19/7.yaml + sha256: 57d4ce67cc097fea2058446927987bc1f7408890e3a6df0da74e5e318f051c20 + original: lts-19.7 From 2f0d7a850f4d7f94f37d64213e470dc520434480 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Sun, 16 Oct 2022 21:10:42 -0600 Subject: [PATCH 123/208] Update CHANGELOG --- CHANGELOG.md | 5 +++++ aeson-typescript.cabal | 4 ++-- package.yaml | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb2be46..5b30661 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change log +## 0.4.1.0 + +* Add TypeScript Int16 +* Add TypeScript (A.KeyMap a) instance for aeson 2 + ## 0.4.0.0 * Add new built-in instances (Word8, Int32, Int64, Map, HashSet) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 72f4945..b6ad8bc 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.34.4. +-- This file has been generated from package.yaml by hpack version 0.35.0. -- -- see: https://github.com/sol/hpack name: aeson-typescript -version: 0.4.0.0 +version: 0.4.1.0 synopsis: Generate TypeScript definition files from your ADTs description: Please see the README on Github at category: Text, Web, JSON diff --git a/package.yaml b/package.yaml index 437c2dc..82cc5a7 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: aeson-typescript -version: 0.4.0.0 +version: 0.4.1.0 github: "codedownio/aeson-typescript" license: BSD3 category: Text, Web, JSON From d4ff690d9618ffa5e118e540ba1085a7119d0d96 Mon Sep 17 00:00:00 2001 From: Tanya Bouman Date: Tue, 18 Oct 2022 20:21:10 -0400 Subject: [PATCH 124/208] README: fix getTypeScriptDeclarations name --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cba6b71..d7c2fa3 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ $(deriveTypeScript (defaultOptions {fieldLabelModifier = drop 4, constructorTagM Now we can use the newly created instances. ```haskell ->>> putStrLn $ formatTSDeclarations $ getTypeScriptDeclaration (Proxy :: Proxy (D T)) +>>> putStrLn $ formatTSDeclarations $ getTypeScriptDeclarations (Proxy :: Proxy (D T)) type D = "nullary" | IUnary | IProduct | IRecord; From 1214f1ce30a4bb3b36d21ab454a6aedb620f1392 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 18 Oct 2022 17:31:07 -0700 Subject: [PATCH 125/208] Test GHC 9.2.4 in CI --- .github/workflows/aeson-typescript.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index f3002a9..9c2d5ad 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -16,8 +16,9 @@ jobs: ghc: - "8.6.5" - "8.8.4" - - "8.10.4" - - "9.0.1" + - "8.10.7" + - "9.0.2" + - "9.2.4" # exclude: # - os: macOS-latest # ghc: 8.8.3 @@ -68,8 +69,9 @@ jobs: matrix: ghc: - "8.8.4" - - "8.10.4" - - "9.0.1" + - "8.10.7" + - "9.0.2" + - "9.2.4" steps: - uses: actions/checkout@v2 From 477a8834a1e0116f8076b8fe865710d133d6cd46 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 18 Oct 2022 17:35:10 -0700 Subject: [PATCH 126/208] Remove accidentally checked in dev folder --- .gitignore | 1 + dev/Live.hs | 77 -------------------------------------------------- dev/Live2.hs | 28 ------------------ dev/Live3.hs | 27 ------------------ dev/Live4.hs | 33 ---------------------- dev/Live5.hs | 80 ---------------------------------------------------- dev/Live6.hs | 39 ------------------------- 7 files changed, 1 insertion(+), 284 deletions(-) delete mode 100644 dev/Live.hs delete mode 100644 dev/Live2.hs delete mode 100644 dev/Live3.hs delete mode 100644 dev/Live4.hs delete mode 100644 dev/Live5.hs delete mode 100644 dev/Live6.hs diff --git a/.gitignore b/.gitignore index 70115df..938bf2c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *~ dist-newstyle *.hie +dev/ diff --git a/dev/Live.hs b/dev/Live.hs deleted file mode 100644 index 496486b..0000000 --- a/dev/Live.hs +++ /dev/null @@ -1,77 +0,0 @@ -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilyDependencies #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Live where - -import Data.Aeson as A -import Data.Aeson.TypeScript.Recursive -import Data.Aeson.TypeScript.TH -import Data.Function -import Data.Functor.Identity -import Data.Kind -import Data.Map -import Data.Proxy -import Data.String.Interpolate -import qualified Data.Text as T -import Prelude hiding (Double) - - -instance TypeScript Identity where getTypeScriptType _ = "any" - -data SingleDE = SingleDE -instance TypeScript SingleDE where getTypeScriptType _ = [i|"single"|] - -data K8SDE = K8SDE -instance TypeScript K8SDE where getTypeScriptType _ = [i|"k8s"|] - -data SingleNodeEnvironment = SingleNodeEnvironment - deriving (Eq, Show) -instance TypeScript SingleNodeEnvironment where getTypeScriptType _ = [i|"single_node_env"|] - -data K8SEnvironment = K8SEnvironment - deriving (Eq, Show) -instance TypeScript K8SEnvironment where getTypeScriptType _ = [i|"k8s_env"|] - -data Nullable (c :: Type -> Type) x -data Exposed x -type family Columnar (f :: Type -> Type) x where - Columnar Exposed x = Exposed x - Columnar Identity x = x - Columnar (Nullable c) x = Columnar c (Maybe x) - Columnar f x = f x - -type family DeployEnvironment env = result | result -> env where - DeployEnvironment SingleNodeEnvironment = SingleDE - DeployEnvironment K8SEnvironment = K8SDE - DeployEnvironment T = () - --- * The main type - -data UserT env f = User { - _userDeployEnv :: Columnar f (DeployEnvironment env) - } - -data Complex3 k = Complex3 { - bulkCommandNoArgKeys :: Map T.Text k - } deriving (Show) - - -deriveTypeScript' A.defaultOptions ''UserT (defaultExtraTypeScriptOptions { typeFamiliesToMapToTypeScript = [''DeployEnvironment] }) - -main :: IO () -main = getTypeScriptDeclarationsRecursively (Proxy :: Proxy (UserT T Identity)) - & formatTSDeclarations - & putStrLn diff --git a/dev/Live2.hs b/dev/Live2.hs deleted file mode 100644 index e8f4741..0000000 --- a/dev/Live2.hs +++ /dev/null @@ -1,28 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilyDependencies #-} - -module Live2 where - -import Data.Aeson as A -import Data.Aeson.TypeScript.TH -import Data.Function -import Data.Proxy - - -data TestT a = TestT { - listOfA :: [a] - , maybeA :: Maybe a - } -$(deriveTypeScript A.defaultOptions ''TestT) - -main :: IO () -main = getTypeScriptDeclarations (Proxy :: Proxy (TestT Int)) - & formatTSDeclarations - & putStrLn diff --git a/dev/Live3.hs b/dev/Live3.hs deleted file mode 100644 index d8673df..0000000 --- a/dev/Live3.hs +++ /dev/null @@ -1,27 +0,0 @@ -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilyDependencies #-} - -module Live3 where - -import Data.Aeson as A -import Data.Aeson.TypeScript.TH -import Data.Function -import Data.Proxy - - -data Test = TestBlah {x :: Int, y :: Bool} - -$(deriveTypeScript (A.defaultOptions { A.tagSingleConstructors = True }) ''Test) - -main :: IO () -main = getTypeScriptDeclarations (Proxy @Test) - & formatTSDeclarations - & putStrLn diff --git a/dev/Live4.hs b/dev/Live4.hs deleted file mode 100644 index 3a8e354..0000000 --- a/dev/Live4.hs +++ /dev/null @@ -1,33 +0,0 @@ -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilyDependencies #-} - -module Live4 where - -import Data.Aeson as A -import Data.Aeson.TypeScript.Recursive -import Data.Aeson.TypeScript.TH -import Data.Function -import Data.Proxy -import TestBoilerplate - - -type family DeployEnvironment2 env = result | result -> env -type instance DeployEnvironment2 SingleNodeEnvironment = SingleDE -type instance DeployEnvironment2 K8SEnvironment = K8SDE -type instance DeployEnvironment2 T = () -newtype Simple env = Simple (DeployEnvironment2 env) -$(deriveTypeScript' A.defaultOptions ''Simple (defaultExtraTypeScriptOptions { typeFamiliesToMapToTypeScript = [''DeployEnvironment2] })) - -main :: IO () -main = getTypeScriptDeclarationsRecursively (Proxy @(Simple T)) - & formatTSDeclarations - & putStrLn diff --git a/dev/Live5.hs b/dev/Live5.hs deleted file mode 100644 index 31082d3..0000000 --- a/dev/Live5.hs +++ /dev/null @@ -1,80 +0,0 @@ -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeInType #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilyDependencies #-} -{-# LANGUAGE DeriveGeneric #-} - -module Live5 where - -import Data.Aeson as A -import Data.Aeson.TypeScript.Recursive -import Data.Aeson.TypeScript.TH -import Data.Function -import Data.Kind as Kind -import Data.Proxy -import qualified Data.Text as T -import Data.Typeable -import Data.Void -import GHC.Generics -import TestBoilerplate - - --- data From = FromServer | FromClient --- data MethodType = Notification | Request - --- data Method (f :: From) (t :: MethodType) where --- Login :: Method 'FromClient 'Request --- ReportClick :: Method 'FromClient 'Notification - --- instance TypeScript Login where getTypeScriptType _ = "asdf" --- instance TypeScript ReportClick where getTypeScriptType _ = "fdsa" - --- data LoginParams = LoginParams { --- loginUsername :: T.Text --- , loginPassword :: T.Text --- } --- $(deriveJSONAndTypeScript A.defaultOptions ''LoginParams) - - --- data ReportClickParams = ReportClickParams { --- reportClickX :: Int --- , reportClickY :: Int --- } --- $(deriveJSONAndTypeScript A.defaultOptions ''ReportClickParams) - --- type family MessageParams (m :: Method f t) :: Kind.Type where --- MessageParams 'Login = LoginParams --- MessageParams 'ReportClick = ReportClickParams - --- data SMethod (m :: Method f t) where --- SLogin :: SMethod 'Login --- SReportClick :: SMethod 'ReportClick - --- data RequestMessage (m :: Method f 'Request) = --- RequestMessage { --- _id :: T.Text --- , _method :: SMethod m --- , _params :: MessageParams m --- } - --- data LoginResult = LoginResult { profilePicture :: T.Text } --- $(deriveJSONAndTypeScript A.defaultOptions ''LoginResult) - --- type family ResponseResult (m :: Method f 'Request) :: Kind.Type where --- ResponseResult 'Login = LoginResult --- ResponseResult _ = Void - --- deriveTypeScript' A.defaultOptions ''RequestMessage (ExtraTypeScriptOptions [''MessageParams]) - --- -- main :: IO () --- -- main = getTypeScriptDeclarationsRecursively (Proxy @(RequestMessage (Method FromClient Request))) --- -- & formatTSDeclarations --- -- & putStrLn diff --git a/dev/Live6.hs b/dev/Live6.hs deleted file mode 100644 index c0fdf90..0000000 --- a/dev/Live6.hs +++ /dev/null @@ -1,39 +0,0 @@ -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TypeInType #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilyDependencies #-} -{-# LANGUAGE DeriveGeneric #-} - -module Live6 where - -import Data.Aeson as A -import Data.Aeson.TypeScript.TH -import Data.Function -import Data.Map -import Data.Proxy -import Data.Text - - -data Complex a = Product Int a | Unary Int deriving Eq - -data Complex2 a = Product2 Int a - -data Complex3 k = Complex3 { - bulkCommandNoArgKeys :: Map Text k - } deriving (Show) -$(deriveTypeScript defaultOptions ''Complex3) - -main :: IO () --- main = printThing (Proxy @(BulkCommandNoArg Int)) -main = printThing (Proxy @(Complex3 Double)) - -printThing x = getTypeScriptDeclarations x - & formatTSDeclarations - & putStrLn From 85200a0c17e78fa6eddeedcea73d0afb53daadf2 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 18 Oct 2022 17:35:38 -0700 Subject: [PATCH 127/208] Bump Stackage in stack.yaml --- stack.yaml | 6 +++--- stack.yaml.lock | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/stack.yaml b/stack.yaml index 78cc250..e41ff87 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,7 +1,7 @@ -resolver: lts-19.7 -# resolver: nightly-2022-02-07 -# resolver: nightly-2022-01-15 +resolver: nightly-2022-10-18 +# resolver: lts-19.7 +# resolver: nightly-2022-02-07 # End of aeson 1 packages: - . diff --git a/stack.yaml.lock b/stack.yaml.lock index 8787e19..38eee5b 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -6,7 +6,7 @@ packages: [] snapshots: - completed: - size: 618884 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/19/7.yaml - sha256: 57d4ce67cc097fea2058446927987bc1f7408890e3a6df0da74e5e318f051c20 - original: lts-19.7 + sha256: 895204e9116cba1f32047525ec5bad7423216587706e5df044c4a7c191a5d8cb + size: 644482 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/10/18.yaml + original: nightly-2022-10-18 From 8311cdffa00b52506d6563ae2c5a281a06faef7b Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 18 Oct 2022 18:11:22 -0700 Subject: [PATCH 128/208] Add failing test for bad type of Aeson map --- aeson-typescript.cabal | 2 ++ package.yaml | 1 + test/TestBoilerplate.hs | 15 +++++++++++++-- test/Util/Aeson.hs | 13 +++++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 test/Util/Aeson.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index b6ad8bc..e3f2d62 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -83,6 +83,7 @@ test-suite aeson-typescript-tests UntaggedTagSingleConstructors UnwrapUnaryRecords Util + Util.Aeson Data.Aeson.TypeScript.Formatting Data.Aeson.TypeScript.Instances Data.Aeson.TypeScript.Internal @@ -123,4 +124,5 @@ test-suite aeson-typescript-tests , th-abstraction , transformers , unordered-containers + , vector default-language: Haskell2010 diff --git a/package.yaml b/package.yaml index 82cc5a7..75a5929 100644 --- a/package.yaml +++ b/package.yaml @@ -64,3 +64,4 @@ tests: - hspec - process - temporary + - vector diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index fc99e87..322f21f 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -22,9 +22,11 @@ import Data.Functor.Identity import Data.Kind import Data.Proxy import Data.String.Interpolate +import qualified Data.Vector as V import Language.Haskell.TH hiding (Type) import Test.Hspec import Util +import Util.Aeson data Unit = Unit data OneFieldRecordless = OneFieldRecordless Int @@ -35,6 +37,7 @@ data Hybrid = HybridSimple Int | HybridRecord { hybridString :: String } data TwoConstructor = Con1 { con1String :: String } | Con2 { con2String :: String, con2Int :: Int } data Complex a = Nullary | Unary Int | Product String Char a | Record { testOne :: Int, testTwo :: Bool, testThree :: Complex a} deriving Eq data Optional = Optional {optionalInt :: Maybe Int} +data AesonTypes = AesonTypes { aesonValue :: A.Value, aesonObject :: A.Object } -- * For testing type families @@ -74,6 +77,7 @@ testDeclarations testName aesonOptions = do deriveInstances ''TwoConstructor deriveInstances ''Complex deriveInstances ''Optional + deriveInstances ''AesonTypes typesAndValues :: Exp <- [e|[(getTypeScriptType (Proxy :: Proxy Unit), A.encode Unit) @@ -95,9 +99,15 @@ testDeclarations testName aesonOptions = do , (getTypeScriptType (Proxy :: Proxy (Complex Int)), A.encode (Unary 42 :: Complex Int)) , (getTypeScriptType (Proxy :: Proxy (Complex Int)), A.encode (Product "asdf" 'g' 42 :: Complex Int)) , (getTypeScriptType (Proxy :: Proxy (Complex Int)), A.encode ((Record { testOne = 3, testTwo = True, testThree = Product "test" 'A' 123}) :: Complex Int)) + , (getTypeScriptType (Proxy :: Proxy Optional), A.encode (Optional { optionalInt = Nothing })) - , (getTypeScriptType (Proxy :: Proxy Optional), A.encode (Optional { optionalInt = Just 1 }))] - |] + , (getTypeScriptType (Proxy :: Proxy Optional), A.encode (Optional { optionalInt = Just 1 })) + + , (getTypeScriptType (Proxy :: Proxy Optional), A.encode (AesonTypes { + aesonValue = A.object [("foo" :: A.Key, A.Number 42)] + , aesonObject = aesonFromList [("foo", A.Number 42)] + })) + ]|] declarations :: Exp <- [e|getTypeScriptDeclarations (Proxy :: Proxy Unit) <> getTypeScriptDeclarations (Proxy :: Proxy OneFieldRecordless) @@ -108,6 +118,7 @@ testDeclarations testName aesonOptions = do <> getTypeScriptDeclarations (Proxy :: Proxy TwoConstructor) <> getTypeScriptDeclarations (Proxy :: Proxy (Complex T)) <> getTypeScriptDeclarations (Proxy :: Proxy Optional) + <> getTypeScriptDeclarations (Proxy :: Proxy AesonTypes) |] tests <- [d|tests :: SpecWith () diff --git a/test/Util/Aeson.hs b/test/Util/Aeson.hs new file mode 100644 index 0000000..68cf681 --- /dev/null +++ b/test/Util/Aeson.hs @@ -0,0 +1,13 @@ +{-# LANGUAGE CPP #-} + +module Util.Aeson where + +#if MIN_VERSION_aeson(2,0,0) +import qualified Data.Aeson.KeyMap as KM + +aesonFromList = KM.fromList +#else +import Data.HashMap.Strict as HM + +aesonFromList = HM.fromList +#endif From a70a8c3889c12f2775ad0cab8a547c36fc186a70 Mon Sep 17 00:00:00 2001 From: Tanya Bouman Date: Tue, 18 Oct 2022 20:11:08 -0400 Subject: [PATCH 129/208] Fix Typescript instance for KeyMap This makes `KeyMap a` generate the same type as `HashMap Text a`, so that the old and new Aeson representations of an Object have the same typescript type. --- src/Data/Aeson/TypeScript/Instances.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index 13dcbfe..98c2e49 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -136,7 +136,7 @@ instance (TypeScript a, TypeScript b) => TypeScript (HashMap a b) where #if MIN_VERSION_aeson(2,0,0) instance (TypeScript a) => TypeScript (A.KeyMap a) where - getTypeScriptType _ = [i|{[k]?: #{getTypeScriptType (Proxy :: Proxy a)}}|] + getTypeScriptType _ = [i|{[k: string]: #{getTypeScriptType (Proxy :: Proxy a)}}|] getParentTypes _ = L.nub [TSType (Proxy :: Proxy a)] #endif From 3c7f78bdcaf7f99155ace833d11546abacf93058 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 18 Oct 2022 18:31:59 -0700 Subject: [PATCH 130/208] Fix TestBoilerplate --- aeson-typescript.cabal | 1 - package.yaml | 1 - test/TestBoilerplate.hs | 9 ++++----- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index e3f2d62..eb9cb3a 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -124,5 +124,4 @@ test-suite aeson-typescript-tests , th-abstraction , transformers , unordered-containers - , vector default-language: Haskell2010 diff --git a/package.yaml b/package.yaml index 75a5929..82cc5a7 100644 --- a/package.yaml +++ b/package.yaml @@ -64,4 +64,3 @@ tests: - hspec - process - temporary - - vector diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index 322f21f..062c0e0 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -22,7 +22,6 @@ import Data.Functor.Identity import Data.Kind import Data.Proxy import Data.String.Interpolate -import qualified Data.Vector as V import Language.Haskell.TH hiding (Type) import Test.Hspec import Util @@ -103,10 +102,10 @@ testDeclarations testName aesonOptions = do , (getTypeScriptType (Proxy :: Proxy Optional), A.encode (Optional { optionalInt = Nothing })) , (getTypeScriptType (Proxy :: Proxy Optional), A.encode (Optional { optionalInt = Just 1 })) - , (getTypeScriptType (Proxy :: Proxy Optional), A.encode (AesonTypes { - aesonValue = A.object [("foo" :: A.Key, A.Number 42)] - , aesonObject = aesonFromList [("foo", A.Number 42)] - })) + , (getTypeScriptType (Proxy :: Proxy AesonTypes), A.encode (AesonTypes { + aesonValue = A.object [("foo" :: A.Key, A.Number 42)] + , aesonObject = aesonFromList [("foo", A.Number 42)] + })) ]|] declarations :: Exp <- [e|getTypeScriptDeclarations (Proxy :: Proxy Unit) From c4958f9c4d94c0464472baba0d72eb50aa8af3a0 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 18 Oct 2022 18:35:29 -0700 Subject: [PATCH 131/208] Also remove dev as a source dir --- aeson-typescript.cabal | 7 ------- package.yaml | 1 - 2 files changed, 8 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index eb9cb3a..9e478dc 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -94,17 +94,10 @@ test-suite aeson-typescript-tests Data.Aeson.TypeScript.TypeManipulation Data.Aeson.TypeScript.Types Data.Aeson.TypeScript.Util - Live - Live2 - Live3 - Live4 - Live5 - Live6 Paths_aeson_typescript hs-source-dirs: test src - dev ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N build-depends: aeson diff --git a/package.yaml b/package.yaml index 82cc5a7..f8be48e 100644 --- a/package.yaml +++ b/package.yaml @@ -50,7 +50,6 @@ tests: source-dirs: - test - src - - dev ghc-options: - -Wall - -threaded From c982c7ccd7693769fcb2bcbd03fa7af84970fa4f Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 28 Oct 2022 20:24:23 -0700 Subject: [PATCH 132/208] Update CHANGELOG for 0.4.2.0 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b30661..455de03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change log +## 0.4.2.0 + +* Fix TypeScript (A.KeyMap a) instance + ## 0.4.1.0 * Add TypeScript Int16 From 5f71db93a30ddf102ff4336ed94a4a7544fdb9e6 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 10 Nov 2022 17:02:32 -0800 Subject: [PATCH 133/208] Release 0.4.2.0 --- aeson-typescript.cabal | 2 +- package.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 9e478dc..c03bc69 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: aeson-typescript -version: 0.4.1.0 +version: 0.4.2.0 synopsis: Generate TypeScript definition files from your ADTs description: Please see the README on Github at category: Text, Web, JSON diff --git a/package.yaml b/package.yaml index f8be48e..53c2e6a 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: aeson-typescript -version: 0.4.1.0 +version: 0.4.2.0 github: "codedownio/aeson-typescript" license: BSD3 category: Text, Web, JSON From ba5d99812384a361b3988e133d4763748e49752a Mon Sep 17 00:00:00 2001 From: parsonsmatt Date: Thu, 27 Oct 2022 12:49:43 -0600 Subject: [PATCH 134/208] check for illegal characters --- CHANGELOG.md | 7 ++++ aeson-typescript.cabal | 3 ++ package.yaml | 1 + src/Data/Aeson/TypeScript/LegalName.hs | 39 +++++++++++++++++++++ src/Data/Aeson/TypeScript/TH.hs | 1 + src/Data/Aeson/TypeScript/Types.hs | 24 +++++++++++-- test/Data/Aeson/TypeScript/LegalNameSpec.hs | 23 ++++++++++++ test/Formatting.hs | 23 +++++++++++- test/Spec.hs | 4 ++- 9 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 src/Data/Aeson/TypeScript/LegalName.hs create mode 100644 test/Data/Aeson/TypeScript/LegalNameSpec.hs diff --git a/CHANGELOG.md b/CHANGELOG.md index 455de03..f0610c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change log + +## (unreleased) + +* [#35](https://github.com/codedownio/aeson-typescript/pull/35) + * Add `Data.Aeson.TypeScript.LegalName` module for checking whether a name is a legal JavaScript name or not. + * The `defaultFormatter` will `error` if the name contains illegal characters. + ## 0.4.2.0 * Fix TypeScript (A.KeyMap a) instance diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index c03bc69..54e8557 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -36,6 +36,7 @@ library Data.Aeson.TypeScript.TH Data.Aeson.TypeScript.Internal Data.Aeson.TypeScript.Recursive + Data.Aeson.TypeScript.LegalName other-modules: Data.Aeson.TypeScript.Formatting Data.Aeson.TypeScript.Instances @@ -66,6 +67,7 @@ test-suite aeson-typescript-tests other-modules: Basic ClosedTypeFamilies + Data.Aeson.TypeScript.LegalNameSpec Formatting Generic HigherKind @@ -87,6 +89,7 @@ test-suite aeson-typescript-tests Data.Aeson.TypeScript.Formatting Data.Aeson.TypeScript.Instances Data.Aeson.TypeScript.Internal + Data.Aeson.TypeScript.LegalName Data.Aeson.TypeScript.Lookup Data.Aeson.TypeScript.Recursive Data.Aeson.TypeScript.TH diff --git a/package.yaml b/package.yaml index 53c2e6a..49a963e 100644 --- a/package.yaml +++ b/package.yaml @@ -43,6 +43,7 @@ library: - Data.Aeson.TypeScript.TH - Data.Aeson.TypeScript.Internal - Data.Aeson.TypeScript.Recursive + - Data.Aeson.TypeScript.LegalName tests: aeson-typescript-tests: diff --git a/src/Data/Aeson/TypeScript/LegalName.hs b/src/Data/Aeson/TypeScript/LegalName.hs new file mode 100644 index 0000000..06201ad --- /dev/null +++ b/src/Data/Aeson/TypeScript/LegalName.hs @@ -0,0 +1,39 @@ +-- | This module defines functions which are useful for determining if +-- a given name is a legal JavaScript name according to . +module Data.Aeson.TypeScript.LegalName where + +import qualified Data.Set as Set +import Language.Haskell.TH +import Data.List.NonEmpty (NonEmpty (..)) +import qualified Data.List.NonEmpty as NonEmpty +import Data.Char +import Data.Foldable + +-- | The return type is the illegal characters that are in the name. If the +-- input has no illegal characters, then you have 'Nothing'. +checkIllegalNameChars :: NonEmpty Char -> Maybe (NonEmpty Char) +checkIllegalNameChars (firstChar :| restChars) = NonEmpty.nonEmpty $ + let + legalFirstCategories = + Set.fromList + [ UppercaseLetter + , LowercaseLetter + , TitlecaseLetter + , ModifierLetter + , OtherLetter + , LetterNumber + ] + legalRestCategories = + Set.fromList + [ NonSpacingMark + , SpacingCombiningMark + , DecimalNumber + , ConnectorPunctuation + ] + `Set.union` legalFirstCategories + isIllegalFirstChar c = not $ + c `elem` ['$', '_'] || generalCategory c `Set.member` legalFirstCategories + isIllegalRestChar c = not $ + generalCategory c `Set.member` legalRestCategories + in + filter isIllegalFirstChar [firstChar] <> filter isIllegalRestChar restChars diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 2925c16..f7ec6bc 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -127,6 +127,7 @@ module Data.Aeson.TypeScript.TH ( , formatTSDeclaration , FormattingOptions(..) , defaultFormattingOptions + , defaultNameFormatter , SumTypeFormat(..) , ExportMode(..) diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index e3c7c6c..3398ffa 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -9,11 +9,13 @@ module Data.Aeson.TypeScript.Types where +import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Aeson as A import Data.Proxy import Data.String import Data.Typeable import Language.Haskell.TH +import Data.Aeson.TypeScript.LegalName -- | The typeclass that defines how a type is turned into TypeScript. -- @@ -131,12 +133,30 @@ data SumTypeFormat = defaultFormattingOptions :: FormattingOptions defaultFormattingOptions = FormattingOptions { numIndentSpaces = 2 - , interfaceNameModifier = id - , typeNameModifier = id + , interfaceNameModifier = defaultNameFormatter + , typeNameModifier = defaultNameFormatter , exportMode = ExportNone , typeAlternativesFormat = TypeAlias } +-- | The 'defaultNameFormatter' in the 'FormattingOptions' checks to see if +-- the name is a legal TypeScript name. If it is not, then it throws +-- a runtime error. +defaultNameFormatter :: String -> String +defaultNameFormatter str = + case NonEmpty.nonEmpty str of + Nothing -> + error "Name cannot be empty" + Just nameChars -> + case checkIllegalNameChars nameChars of + Just badChars -> + error $ concat + [ "The name ", str, " contains illegal characters: ", NonEmpty.toList badChars + , "\nConsider setting a default name formatter that replaces these characters, or renaming the type." + ] + Nothing -> + str + -- | Convenience typeclass class you can use to "attach" a set of Aeson encoding options to a type. class HasJSONOptions a where getJSONOptions :: (Proxy a) -> A.Options diff --git a/test/Data/Aeson/TypeScript/LegalNameSpec.hs b/test/Data/Aeson/TypeScript/LegalNameSpec.hs new file mode 100644 index 0000000..dda1429 --- /dev/null +++ b/test/Data/Aeson/TypeScript/LegalNameSpec.hs @@ -0,0 +1,23 @@ +module Data.Aeson.TypeScript.LegalNameSpec where + +import Test.Hspec +import Data.List.NonEmpty (NonEmpty (..)) +import Data.Aeson.TypeScript.LegalName + +tests :: Spec +tests = describe "Data.Aeson.TypeScript.LegalName" $ do + describe "checkIllegalNameChars" $ do + describe "legal Haskell names" $ do + it "allows an uppercase letter" $ do + checkIllegalNameChars ('A' :| []) + `shouldBe` Nothing + it "allows an underscore" $ do + checkIllegalNameChars ('_' :| "asdf") + `shouldBe` Nothing + it "reports that ' is illegal" $ do + checkIllegalNameChars ('F' :| "oo'") + `shouldBe` Just ('\'' :| []) + describe "illegal Haskell names" $ do + it "allows a $" $ do + checkIllegalNameChars ('$' :| "asdf") + `shouldBe` Nothing diff --git a/test/Formatting.hs b/test/Formatting.hs index 136751d..4f10865 100644 --- a/test/Formatting.hs +++ b/test/Formatting.hs @@ -5,6 +5,7 @@ module Formatting (tests) where +import Control.Exception import Data.Aeson (defaultOptions) import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types @@ -16,9 +17,17 @@ data D = S | F deriving (Eq, Show) $(deriveTypeScript defaultOptions ''D) +data PrimeInType' = PrimeInType + +$(deriveTypeScript defaultOptions ''PrimeInType') + +data PrimeInConstr = PrimeInConstr' + +$(deriveTypeScript defaultOptions ''PrimeInConstr) + tests :: Spec tests = do - describe "Formatting" $ + describe "Formatting" $ do describe "when given a Sum Type" $ do describe "and the TypeAlias format option is set" $ it "should generate a TS string literal type" $ @@ -32,3 +41,15 @@ tests = 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;|] + describe "when the name has an apostrophe" $ do + describe "in the type" $ do + it "throws an error" $ do + evaluate (formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @PrimeInType' Proxy)) + `shouldThrow` + anyErrorCall + describe "in the constructor" $ do + it "throws an error" $ do + evaluate (formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @PrimeInConstr Proxy)) + `shouldThrow` + anyErrorCall + diff --git a/test/Spec.hs b/test/Spec.hs index 611fc36..83ee5fa 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -19,14 +19,16 @@ import qualified UntaggedTagSingleConstructors import qualified OmitNothingFields import qualified NoOmitNothingFields import qualified UnwrapUnaryRecords +import qualified Data.Aeson.TypeScript.LegalNameSpec as LegalNameSpec main :: IO () -main = hspec $ do +main = hspec $ parallel $ do Formatting.tests Generic.tests HigherKind.tests ClosedTypeFamilies.tests + LegalNameSpec.tests ObjectWithSingleFieldTagSingleConstructors.tests ObjectWithSingleFieldNoTagSingleConstructors.tests From cc1545e706a3fa94cd8927b0e51d8deecf41044c Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 22 Dec 2022 19:44:42 -0700 Subject: [PATCH 135/208] Formatting and tweaks following pull #35 --- aeson-typescript.cabal | 2 +- src/Data/Aeson/TypeScript/LegalName.hs | 10 ++++----- src/Data/Aeson/TypeScript/Types.hs | 4 ++-- test/Formatting.hs | 6 +---- .../Aeson/TypeScript => }/LegalNameSpec.hs | 6 ++--- test/Spec.hs | 22 +++++++++---------- 6 files changed, 23 insertions(+), 27 deletions(-) rename test/{Data/Aeson/TypeScript => }/LegalNameSpec.hs (93%) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 54e8557..e3095bf 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -67,10 +67,10 @@ test-suite aeson-typescript-tests other-modules: Basic ClosedTypeFamilies - Data.Aeson.TypeScript.LegalNameSpec Formatting Generic HigherKind + LegalNameSpec NoOmitNothingFields ObjectWithSingleFieldNoTagSingleConstructors ObjectWithSingleFieldTagSingleConstructors diff --git a/src/Data/Aeson/TypeScript/LegalName.hs b/src/Data/Aeson/TypeScript/LegalName.hs index 06201ad..3f15900 100644 --- a/src/Data/Aeson/TypeScript/LegalName.hs +++ b/src/Data/Aeson/TypeScript/LegalName.hs @@ -1,13 +1,13 @@ -- | This module defines functions which are useful for determining if --- a given name is a legal JavaScript name according to . +-- a given name is a legal JavaScript name according to +-- . module Data.Aeson.TypeScript.LegalName where -import qualified Data.Set as Set -import Language.Haskell.TH +import Data.Char import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.List.NonEmpty as NonEmpty -import Data.Char -import Data.Foldable +import qualified Data.Set as Set + -- | The return type is the illegal characters that are in the name. If the -- input has no illegal characters, then you have 'Nothing'. diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 3398ffa..9af55a6 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -9,13 +9,13 @@ module Data.Aeson.TypeScript.Types where -import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Aeson as A +import Data.Aeson.TypeScript.LegalName +import qualified Data.List.NonEmpty as NonEmpty import Data.Proxy import Data.String import Data.Typeable import Language.Haskell.TH -import Data.Aeson.TypeScript.LegalName -- | The typeclass that defines how a type is turned into TypeScript. -- diff --git a/test/Formatting.hs b/test/Formatting.hs index 4f10865..0ad964b 100644 --- a/test/Formatting.hs +++ b/test/Formatting.hs @@ -8,21 +8,18 @@ module Formatting (tests) where import Control.Exception import Data.Aeson (defaultOptions) import Data.Aeson.TypeScript.TH -import Data.Aeson.TypeScript.Types import Data.Proxy import Data.String.Interpolate import Test.Hspec -data D = S | F deriving (Eq, Show) +data D = S | F deriving (Eq, Show) $(deriveTypeScript defaultOptions ''D) data PrimeInType' = PrimeInType - $(deriveTypeScript defaultOptions ''PrimeInType') data PrimeInConstr = PrimeInConstr' - $(deriveTypeScript defaultOptions ''PrimeInConstr) tests :: Spec @@ -52,4 +49,3 @@ tests = do evaluate (formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @PrimeInConstr Proxy)) `shouldThrow` anyErrorCall - diff --git a/test/Data/Aeson/TypeScript/LegalNameSpec.hs b/test/LegalNameSpec.hs similarity index 93% rename from test/Data/Aeson/TypeScript/LegalNameSpec.hs rename to test/LegalNameSpec.hs index dda1429..f657092 100644 --- a/test/Data/Aeson/TypeScript/LegalNameSpec.hs +++ b/test/LegalNameSpec.hs @@ -1,8 +1,8 @@ -module Data.Aeson.TypeScript.LegalNameSpec where +module LegalNameSpec where -import Test.Hspec -import Data.List.NonEmpty (NonEmpty (..)) import Data.Aeson.TypeScript.LegalName +import Data.List.NonEmpty (NonEmpty (..)) +import Test.Hspec tests :: Spec tests = describe "Data.Aeson.TypeScript.LegalName" $ do diff --git a/test/Spec.hs b/test/Spec.hs index 83ee5fa..f645327 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -8,36 +8,36 @@ import qualified Generic import qualified HigherKind import qualified ClosedTypeFamilies +import qualified LegalNameSpec +import qualified NoOmitNothingFields import qualified ObjectWithSingleFieldNoTagSingleConstructors import qualified ObjectWithSingleFieldTagSingleConstructors +import qualified OmitNothingFields import qualified TaggedObjectNoTagSingleConstructors import qualified TaggedObjectTagSingleConstructors import qualified TwoElemArrayNoTagSingleConstructors import qualified TwoElemArrayTagSingleConstructors import qualified UntaggedNoTagSingleConstructors import qualified UntaggedTagSingleConstructors -import qualified OmitNothingFields -import qualified NoOmitNothingFields import qualified UnwrapUnaryRecords -import qualified Data.Aeson.TypeScript.LegalNameSpec as LegalNameSpec main :: IO () main = hspec $ parallel $ do + ClosedTypeFamilies.tests Formatting.tests Generic.tests HigherKind.tests - ClosedTypeFamilies.tests - LegalNameSpec.tests - ObjectWithSingleFieldTagSingleConstructors.tests + LegalNameSpec.tests + NoOmitNothingFields.allTests ObjectWithSingleFieldNoTagSingleConstructors.tests - TaggedObjectTagSingleConstructors.tests + ObjectWithSingleFieldTagSingleConstructors.tests + OmitNothingFields.tests TaggedObjectNoTagSingleConstructors.tests - TwoElemArrayTagSingleConstructors.tests + TaggedObjectTagSingleConstructors.tests TwoElemArrayNoTagSingleConstructors.tests - UntaggedTagSingleConstructors.tests + TwoElemArrayTagSingleConstructors.tests UntaggedNoTagSingleConstructors.tests - OmitNothingFields.tests - NoOmitNothingFields.allTests + UntaggedTagSingleConstructors.tests UnwrapUnaryRecords.allTests From a30807d8f5e3b607992259574d26031c78e96e07 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 18:24:33 -0800 Subject: [PATCH 136/208] Bump stack.yaml to lts-20.12 --- stack.yaml | 4 +--- stack.yaml.lock | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/stack.yaml b/stack.yaml index e41ff87..812491d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,7 +1,5 @@ -resolver: nightly-2022-10-18 -# resolver: lts-19.7 -# resolver: nightly-2022-02-07 # End of aeson 1 +resolver: lts-20.12 packages: - . diff --git a/stack.yaml.lock b/stack.yaml.lock index 38eee5b..b1d5d3a 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -6,7 +6,7 @@ packages: [] snapshots: - completed: - sha256: 895204e9116cba1f32047525ec5bad7423216587706e5df044c4a7c191a5d8cb - size: 644482 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/10/18.yaml - original: nightly-2022-10-18 + sha256: af5d667f6096e535b9c725a72cffe0f6c060e0568d9f9eeda04caee70d0d9d2d + size: 649133 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/12.yaml + original: lts-20.12 From 5d10208ebfd099a83da4bf2e0fea8b58544e81af Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 18:24:13 -0800 Subject: [PATCH 137/208] Piping through haddocks using getDoc --- src/Data/Aeson/TypeScript/Formatting.hs | 15 ++++++++++++--- src/Data/Aeson/TypeScript/Instances.hs | 4 ++-- src/Data/Aeson/TypeScript/Lookup.hs | 6 +++--- src/Data/Aeson/TypeScript/TH.hs | 11 +++++++++-- src/Data/Aeson/TypeScript/Types.hs | 10 +++++++--- test/ClosedTypeFamilies.hs | 24 ++++++++++++------------ test/HigherKind.hs | 10 +++++----- test/OpenTypeFamilies.hs | 24 ++++++++++++------------ 8 files changed, 62 insertions(+), 42 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index 1edb6bf..f956b3b 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -32,11 +32,16 @@ formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVar formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceName genericVariables members) = [i|#{exportPrefix exportMode}interface #{modifiedInterfaceName}#{getGenericBrackets genericVariables} { #{ls} -}|] where ls = T.intercalate "\n" $ fmap T.pack [(replicate numIndentSpaces ' ') <> formatTSField member <> ";"| member <- members] - modifiedInterfaceName = (\(li, name) -> li <> interfaceNameModifier name) . splitAt 1 $ interfaceName +}|] where + ls = T.intercalate "\n" $ fmap T.pack [indentTo numIndentSpaces (formatTSField member <> ";") | member <- members] + modifiedInterfaceName = (\(li, name) -> li <> interfaceNameModifier name) . splitAt 1 $ interfaceName formatTSDeclaration _ (TSRawDeclaration text) = text +-- | TODO: handle multiple lines +indentTo :: Int -> String -> String +indentTo numIndentSpaces s = replicate numIndentSpaces ' ' <> s + exportPrefix :: ExportMode -> String exportPrefix ExportEach = "export " exportPrefix ExportNone = "" @@ -60,7 +65,11 @@ validateFormattingOptions options@FormattingOptions{..} decls isPlainSumType ds = (not . any isInterface $ ds) && length ds == 1 formatTSField :: TSField -> String -formatTSField (TSField optional name typ) = [i|#{name}#{if optional then ("?" :: String) else ""}: #{typ}|] +formatTSField (TSField optional name typ maybeDoc) = docPrefix <> [i|#{name}#{if optional then ("?" :: String) else ""}: #{typ}|] + where + docPrefix = case maybeDoc of + Nothing -> "" + Just doc -> "/* " <> doc <> " */\n" getGenericBrackets :: [String] -> String getGenericBrackets [] = "" diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index 98c2e49..ee566be 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -90,8 +90,8 @@ instance {-# OVERLAPPING #-} TypeScript [Char] where instance (TypeScript a, TypeScript b) => TypeScript (Either a b) where getTypeScriptType _ = [i|Either<#{getTypeScriptType (Proxy :: Proxy a)}, #{getTypeScriptType (Proxy :: Proxy b)}>|] getTypeScriptDeclarations _ = [TSTypeAlternatives "Either" ["T1", "T2"] ["Left", "Right"] - , TSInterfaceDeclaration "Left" ["T"] [TSField False "Left" "T"] - , TSInterfaceDeclaration "Right" ["T"] [TSField False "Right" "T"] + , TSInterfaceDeclaration "Left" ["T"] [TSField False "Left" "T" Nothing] + , TSInterfaceDeclaration "Right" ["T"] [TSField False "Right" "T" Nothing] ] getParentTypes _ = L.nub [ (TSType (Proxy :: Proxy a)) , (TSType (Proxy :: Proxy b)) diff --git a/src/Data/Aeson/TypeScript/Lookup.hs b/src/Data/Aeson/TypeScript/Lookup.hs index 29c254b..8c245c8 100644 --- a/src/Data/Aeson/TypeScript/Lookup.hs +++ b/src/Data/Aeson/TypeScript/Lookup.hs @@ -46,12 +46,12 @@ getClosedTypeFamilyInterfaceDecl name eqns = do fields <- forM eqns $ \case #if MIN_VERSION_template_haskell(2,15,0) TySynEqn Nothing (AppT (ConT _) (ConT arg)) result -> do - [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) |] + [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) Nothing |] TySynEqn Nothing (AppT (ConT _) (PromotedT arg)) result -> do - [| TSField False (getTypeScriptType (Proxy :: Proxy $(promotedT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) |] + [| TSField False (getTypeScriptType (Proxy :: Proxy $(promotedT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) Nothing |] #else TySynEqn [ConT arg] result -> do - [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) |] + [| TSField False (getTypeScriptType (Proxy :: Proxy $(conT arg))) (getTypeScriptType (Proxy :: Proxy $(return result))) Nothing |] #endif x -> fail [i|aeson-typescript doesn't know yet how to handle this type family equation: '#{x}'|] diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index f7ec6bc..33d6bc5 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -270,7 +270,7 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci = do lift [|$(TH.stringE interfaceName) <> $(return brackets)|] | otherwise -> do tagField :: [Exp] <- lift $ case sumEncoding options of - TaggedObject tagFieldName _ -> (: []) <$> [|TSField False $(TH.stringE tagFieldName) $(TH.stringE [i|"#{constructorNameToUse options ci}"|])|] + TaggedObject tagFieldName _ -> (: []) <$> [|TSField False $(TH.stringE tagFieldName) $(TH.stringE [i|"#{constructorNameToUse options ci}"|]) Nothing|] _ -> return [] tsFields <- getTSFields @@ -322,7 +322,14 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci = do (AppT (ConT name) t) | name == ''Maybe && not (omitNothingFields options) -> ( , ) <$> [|$(getTypeAsStringExp t) <> " | null"|] <*> getOptionalAsBoolExp t _ -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ - lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) |] + +#if MIN_VERSION_template_haskell(2,18,0) + maybeDoc <- lift $ getDoc (DeclDoc (mkName nameString)) +#else + let maybeDoc = Nothing +#endif + + lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) Nothing |] -- TODO isSingleRecordConstructor (constructorVariant -> RecordConstructor [x]) = True isSingleRecordConstructor _ = False diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 9af55a6..c42af91 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -93,9 +93,13 @@ data TSDeclaration = TSInterfaceDeclaration { interfaceName :: String | TSRawDeclaration { text :: String } deriving (Show, Eq, Ord) -data TSField = TSField { fieldOptional :: Bool - , fieldName :: String - , fieldType :: String } deriving (Show, Eq, Ord) +data TSField = TSField + { fieldOptional :: Bool + , fieldName :: String + , fieldType :: String + , fieldDoc :: Maybe String + -- ^ Haddock documentation for the field, if present + } deriving (Show, Eq, Ord) newtype TSString a = TSString { unpackTSString :: String } deriving Show diff --git a/test/ClosedTypeFamilies.hs b/test/ClosedTypeFamilies.hs index 45548c2..29fa686 100644 --- a/test/ClosedTypeFamilies.hs +++ b/test/ClosedTypeFamilies.hs @@ -53,9 +53,9 @@ tests = describe "Closed type families" $ do it [i|makes the declaration and types correctly|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Simple T))) `shouldBe` ([ TSInterfaceDeclaration "DeployEnvironment2" [] [ - TSField False "\"k8s_env\"" "\"k8s\"" - , TSField False "\"single_node_env\"" "\"single\"" - , TSField False "T" "void" + TSField False "\"k8s_env\"" "\"k8s\"" Nothing + , TSField False "\"single_node_env\"" "\"single\"" Nothing + , TSField False "T" "void" Nothing ] , TSTypeAlternatives "ISimple" ["T extends keyof DeployEnvironment2"] ["DeployEnvironment2[T]"] , TSTypeAlternatives "Simple" ["T extends keyof DeployEnvironment2"] ["ISimple"] @@ -66,23 +66,23 @@ tests = describe "Closed type families" $ do (getTypeScriptDeclarations (Proxy :: Proxy (UserT T Identity))) `shouldBe` ([ TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] , TSInterfaceDeclaration "IUser" ["T extends keyof DeployEnvironment"] [ - TSField False "_userUsername" "string" - , TSField False "_userCreatedAt" "number" - , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" + TSField False "_userUsername" "string" Nothing + , TSField False "_userCreatedAt" "number" Nothing + , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" Nothing ] ]) it [i|get the declarations recursively|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (UserT T Identity))) `shouldBe` ([ TSInterfaceDeclaration "DeployEnvironment" [] [ - TSField False "\"k8s_env\"" "\"k8s\"" - , TSField False "\"single_node_env\"" "\"single\"" - , TSField False "T" "void" + TSField False "\"k8s_env\"" "\"k8s\"" Nothing + , TSField False "\"single_node_env\"" "\"single\"" Nothing + , TSField False "T" "void" Nothing ] , TSInterfaceDeclaration "IUser" ["T extends keyof DeployEnvironment"] [ - TSField False "_userUsername" "string" - , TSField False "_userCreatedAt" "number" - , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" + TSField False "_userUsername" "string" Nothing + , TSField False "_userCreatedAt" "number" Nothing + , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" Nothing ] , TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] ]) diff --git a/test/HigherKind.hs b/test/HigherKind.hs index 63fe4e8..20afa6b 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -51,7 +51,7 @@ tests = describe "Higher kinds" $ do it [i|makes the declaration and types correctly|] $ do (getTypeScriptDeclarations (Proxy :: Proxy (HigherKind T))) `shouldBe` ([ TSTypeAlternatives "HigherKind" ["T"] ["IHigherKind"], - TSInterfaceDeclaration "IHigherKind" ["T"] [TSField False "higherKindList" "T[]"] + TSInterfaceDeclaration "IHigherKind" ["T"] [TSField False "higherKindList" "T[]" Nothing] ]) (getTypeScriptType (Proxy :: Proxy (HigherKind Int))) `shouldBe` "HigherKind" @@ -60,8 +60,8 @@ tests = describe "Higher kinds" $ do it [i|works when referenced in another type|] $ do (getTypeScriptDeclarations (Proxy :: Proxy Foo)) `shouldBe` ([ TSTypeAlternatives "Foo" [] ["IFoo"], - TSInterfaceDeclaration "IFoo" [] [TSField False "fooString" "string" - , TSField False "fooHigherKindReference" "HigherKind"] + TSInterfaceDeclaration "IFoo" [] [TSField False "fooString" "string" Nothing + , TSField False "fooHigherKindReference" "HigherKind" Nothing] ]) it [i|works with an interface inside|] $ do @@ -74,8 +74,8 @@ tests = describe "Higher kinds" $ do it [i|makes the declaration and type correctly|] $ do (getTypeScriptDeclarations (Proxy :: Proxy (DoubleHigherKind T1 T2))) `shouldBe` ([ TSTypeAlternatives "DoubleHigherKind" ["T1","T2"] ["IDoubleHigherKind"], - TSInterfaceDeclaration "IDoubleHigherKind" ["T1","T2"] [TSField False "someList" "T2[]" - , TSField False "higherKindThing" "HigherKind"] + TSInterfaceDeclaration "IDoubleHigherKind" ["T1","T2"] [TSField False "someList" "T2[]" Nothing + , TSField False "higherKindThing" "HigherKind" Nothing] ]) (getTypeScriptType (Proxy :: Proxy (DoubleHigherKind Int String))) `shouldBe` "DoubleHigherKind" diff --git a/test/OpenTypeFamilies.hs b/test/OpenTypeFamilies.hs index 810154f..e860277 100644 --- a/test/OpenTypeFamilies.hs +++ b/test/OpenTypeFamilies.hs @@ -53,9 +53,9 @@ tests = describe "Open type families" $ do it [i|makes the declaration and types correctly|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Simple T))) `shouldBe` ([ TSInterfaceDeclaration "DeployEnvironment2" [] [ - TSField False "\"single_node_env\"" "\"single\"" - , TSField False "\"k8s_env\"" "\"k8s\"" - , TSField False "T" "void" + TSField False "\"single_node_env\"" "\"single\"" Nothing + , TSField False "\"k8s_env\"" "\"k8s\"" Nothing + , TSField False "T" "void" Nothing ] , TSTypeAlternatives "ISimple" ["T extends keyof DeployEnvironment2"] ["DeployEnvironment2[T]"] , TSTypeAlternatives "Simple" ["T extends keyof DeployEnvironment2"] ["ISimple"] @@ -66,23 +66,23 @@ tests = describe "Open type families" $ do (getTypeScriptDeclarations (Proxy :: Proxy (UserT T Identity))) `shouldBe` ([ TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] , TSInterfaceDeclaration "IUser" ["T extends keyof DeployEnvironment"] [ - TSField False "_userUsername" "string" - , TSField False "_userCreatedAt" "number" - , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" + TSField False "_userUsername" "string" Nothing + , TSField False "_userCreatedAt" "number" Nothing + , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" Nothing ] ]) it [i|get the declarations recursively|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (UserT T Identity))) `shouldBe` ([ TSInterfaceDeclaration "DeployEnvironment" [] [ - TSField False "\"single_node_env\"" "\"single\"" - , TSField False "\"k8s_env\"" "\"k8s\"" - , TSField False "T" "void" + TSField False "\"single_node_env\"" "\"single\"" Nothing + , TSField False "\"k8s_env\"" "\"k8s\"" Nothing + , TSField False "T" "void" Nothing ] , TSInterfaceDeclaration "IUser" ["T extends keyof DeployEnvironment"] [ - TSField False "_userUsername" "string" - , TSField False "_userCreatedAt" "number" - , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" + TSField False "_userUsername" "string" Nothing + , TSField False "_userCreatedAt" "number" Nothing + , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" Nothing ] , TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] ]) From 8a5bb23dc79d9f18ee7462f9a38882acd37bbaae Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 19:04:05 -0800 Subject: [PATCH 138/208] Recover from getDoc failure --- src/Data/Aeson/TypeScript/Recursive.hs | 4 +--- src/Data/Aeson/TypeScript/TH.hs | 4 ++-- src/Data/Aeson/TypeScript/Util.hs | 3 +++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Recursive.hs b/src/Data/Aeson/TypeScript/Recursive.hs index 9b724db..e525bf6 100755 --- a/src/Data/Aeson/TypeScript/Recursive.hs +++ b/src/Data/Aeson/TypeScript/Recursive.hs @@ -29,6 +29,7 @@ import Control.Monad.Trans.Maybe import Control.Monad.Writer import Data.Aeson.TypeScript.Instances () import Data.Aeson.TypeScript.TH +import Data.Aeson.TypeScript.Util (nothingOnFail) import Data.Bifunctor import Data.Function import qualified Data.List as L @@ -135,6 +136,3 @@ getAllParentTypes name pruneFn = reverse <$> execStateT (getAllParentTypes' name addIfNotPresent :: (Eq a) => a -> [a] -> [a] addIfNotPresent x xs | x `L.elem` xs = xs addIfNotPresent x xs = x : xs - -nothingOnFail :: Q a -> Q (Maybe a) -nothingOnFail action = recover (return Nothing) (Just <$> action) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 33d6bc5..353180e 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -324,12 +324,12 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci = do _ -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ #if MIN_VERSION_template_haskell(2,18,0) - maybeDoc <- lift $ getDoc (DeclDoc (mkName nameString)) + maybeDoc <- lift $ nothingOnFail $ getDoc (DeclDoc (mkName nameString)) #else let maybeDoc = Nothing #endif - lift $ [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) Nothing |] -- TODO + lift [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) Nothing |] -- TODO isSingleRecordConstructor (constructorVariant -> RecordConstructor [x]) = True isSingleRecordConstructor _ = False diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index 89c771b..7e39892 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -224,3 +224,6 @@ genericVariablesListExpr includeSuffix genericVariables = listE (fmap (\((_, (su isStarType :: Type -> Maybe Name isStarType (SigT (VarT n) StarT) = Just n isStarType _ = Nothing + +nothingOnFail :: Q a -> Q (Maybe a) +nothingOnFail action = recover (return Nothing) (Just <$> action) From 5bfb857119f436f54b87d3b583177ac871122193 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 19:16:17 -0800 Subject: [PATCH 139/208] Add getDoc tests and fix Basic tests --- aeson-typescript.cabal | 3 ++- test/Basic.hs | 4 +++- test/Generic.hs | 10 +++++----- test/GetDoc.hs | 40 +++++++++++++++++++++++++++++++++++++ test/NoOmitNothingFields.hs | 4 +--- test/OmitNothingFields.hs | 4 +--- test/Spec.hs | 4 ++++ 7 files changed, 56 insertions(+), 13 deletions(-) create mode 100644 test/GetDoc.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index e3095bf..8c0a9b3 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -1,6 +1,6 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.0. +-- This file has been generated from package.yaml by hpack version 0.35.1. -- -- see: https://github.com/sol/hpack @@ -69,6 +69,7 @@ test-suite aeson-typescript-tests ClosedTypeFamilies Formatting Generic + GetDoc HigherKind LegalNameSpec NoOmitNothingFields diff --git a/test/Basic.hs b/test/Basic.hs index d018fb0..ec66cb5 100644 --- a/test/Basic.hs +++ b/test/Basic.hs @@ -41,7 +41,9 @@ tests = describe "Basic tests" $ do ]) it [i|Works with a unit with constructorTagModifier|] $ do - (getTypeScriptDeclarations (Proxy :: Proxy Unit2)) `shouldBe` ([]) + (getTypeScriptDeclarations (Proxy :: Proxy Unit2)) `shouldBe` ([ + TSTypeAlternatives "Unit2" [] ["\"foo\""] + ]) main :: IO () diff --git a/test/Generic.hs b/test/Generic.hs index 60e1a20..13ada57 100644 --- a/test/Generic.hs +++ b/test/Generic.hs @@ -44,8 +44,8 @@ tests :: SpecWith () tests = describe "Generic instances" $ do it [i|Complex makes the declaration and types correctly|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex String))) `shouldBe` [ - TSInterfaceDeclaration {interfaceName = "IProduct", interfaceGenericVariables = ["T"], interfaceMembers = [TSField {fieldOptional = False, fieldName = "tag", fieldType = "\"Product\""},TSField {fieldOptional = False, fieldName = "contents", fieldType = "[number, T]"}]} - ,TSInterfaceDeclaration {interfaceName = "IUnary", interfaceGenericVariables = ["T"], interfaceMembers = [TSField {fieldOptional = False, fieldName = "tag", fieldType = "\"Unary\""},TSField {fieldOptional = False, fieldName = "contents", fieldType = "number"}]} + TSInterfaceDeclaration {interfaceName = "IProduct", interfaceGenericVariables = ["T"], interfaceMembers = [TSField False "tag" "\"Product\"" Nothing, TSField False "contents" "[number, T]" Nothing]} + ,TSInterfaceDeclaration {interfaceName = "IUnary", interfaceGenericVariables = ["T"], interfaceMembers = [TSField False "tag" "\"Unary\"" Nothing, TSField False "contents" "number" Nothing]} ,TSTypeAlternatives {typeName = "Complex", typeGenericVariables = ["T"], alternativeTypes = ["IProduct","IUnary"]} ] @@ -58,14 +58,14 @@ tests = describe "Generic instances" $ do it [i|Complex3 makes the declaration and types correctly|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex3 String))) `shouldBe` [ TSInterfaceDeclaration {interfaceName = "IProduct3", interfaceGenericVariables = ["T"], interfaceMembers = [ - TSField {fieldOptional = False, fieldName = "record3", fieldType = "T[]"} + TSField False "record3" "T[]" Nothing ]} ,TSTypeAlternatives {typeName = "Complex3", typeGenericVariables = ["T"], alternativeTypes = ["IProduct3"]} ] (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex3 Int))) `shouldBe` [ TSInterfaceDeclaration {interfaceName = "IProduct3", interfaceGenericVariables = ["T"], interfaceMembers = [ - TSField {fieldOptional = False, fieldName = "record3", fieldType = "T[]"} + TSField False "record3" "T[]" Nothing ]} ,TSTypeAlternatives {typeName = "Complex3", typeGenericVariables = ["T"], alternativeTypes = ["IProduct3"]} ] @@ -73,7 +73,7 @@ tests = describe "Generic instances" $ do it [i|Complex4 makes the declaration and types correctly|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex4 String))) `shouldBe` [ TSInterfaceDeclaration {interfaceName = "IProduct4", interfaceGenericVariables = ["T"], interfaceMembers = [ - TSField {fieldOptional = False, fieldName = "record4", fieldType = "{[k in string]?: T}"} + TSField False "record4" "{[k in string]?: T}" Nothing ]} ,TSTypeAlternatives {typeName = "Complex4", typeGenericVariables = ["T"], alternativeTypes = ["IProduct4"]} ] diff --git a/test/GetDoc.hs b/test/GetDoc.hs new file mode 100644 index 0000000..bda2e5d --- /dev/null +++ b/test/GetDoc.hs @@ -0,0 +1,40 @@ +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} + +module GetDoc (tests) where + +import Data.Aeson as A +import Data.Aeson.TypeScript.TH +import Data.Aeson.TypeScript.Types +import Data.Proxy +import Data.String.Interpolate +import Prelude hiding (Double) +import Test.Hspec + + +data OneField = OneField { + -- | This is a simple string. + simpleString :: String + } +$(deriveTypeScript A.defaultOptions ''OneField) + +tests :: SpecWith () +tests = describe "getDoc tests" $ do + it [i|Works with a simple record type|] $ do + (getTypeScriptDeclarations (Proxy :: Proxy OneField)) `shouldBe` ([]) + + +main :: IO () +main = hspec tests diff --git a/test/NoOmitNothingFields.hs b/test/NoOmitNothingFields.hs index fa03d99..2a8158f 100644 --- a/test/NoOmitNothingFields.hs +++ b/test/NoOmitNothingFields.hs @@ -35,9 +35,7 @@ allTests = describe "NoOmitNothingFields" $ do , TSInterfaceDeclaration { interfaceName = "IOptional" , interfaceGenericVariables = [] - , interfaceMembers = [TSField {fieldOptional = False - , fieldName = "optionalInt" - , fieldType = "number | null"}] + , interfaceMembers = [TSField False "optionalInt" "number | null" Nothing] }] tests diff --git a/test/OmitNothingFields.hs b/test/OmitNothingFields.hs index 39886ea..55918fd 100644 --- a/test/OmitNothingFields.hs +++ b/test/OmitNothingFields.hs @@ -31,9 +31,7 @@ main = hspec $ describe "OmitNothingFields" $ do interfaceName = "Optional" , interfaceGenericVariables = [] , interfaceMembers = [ - TSField {fieldOptional = True - , fieldName = "optionalInt" - , fieldType = "number"} + TSField True "optionalInt" "number" Nothing ] }] diff --git a/test/Spec.hs b/test/Spec.hs index f645327..3424fef 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -3,8 +3,10 @@ module Main where import Test.Hspec +import qualified Basic import qualified Formatting import qualified Generic +import qualified GetDoc import qualified HigherKind import qualified ClosedTypeFamilies @@ -24,9 +26,11 @@ import qualified UnwrapUnaryRecords main :: IO () main = hspec $ parallel $ do + Basic.tests ClosedTypeFamilies.tests Formatting.tests Generic.tests + GetDoc.tests HigherKind.tests LegalNameSpec.tests From b284178928cf2e2c11d7fd6a7c68c1f5f76d35a7 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 19:48:09 -0800 Subject: [PATCH 140/208] Use the actual getDoc result; not working yet --- src/Data/Aeson/TypeScript/TH.hs | 2 +- test/GetDoc.hs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 353180e..603a85f 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -329,7 +329,7 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci = do let maybeDoc = Nothing #endif - lift [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) Nothing |] -- TODO + lift [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) $(case maybeDoc of Just (Just doc) -> [|Just $(TH.stringE doc)|]; _ -> [|Nothing|]) |] isSingleRecordConstructor (constructorVariant -> RecordConstructor [x]) = True isSingleRecordConstructor _ = False diff --git a/test/GetDoc.hs b/test/GetDoc.hs index bda2e5d..7cc231c 100644 --- a/test/GetDoc.hs +++ b/test/GetDoc.hs @@ -24,9 +24,10 @@ import Prelude hiding (Double) import Test.Hspec +-- | OneField is a type with a single field data OneField = OneField { - -- | This is a simple string. simpleString :: String + -- ^ This is a simple string. } $(deriveTypeScript A.defaultOptions ''OneField) From b773b8c9e12ff2de50e51225668dc045ad810649 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 21:47:09 -0800 Subject: [PATCH 141/208] Small tweaks --- src/Data/Aeson/TypeScript/TH.hs | 2 +- test/Util/Aeson.hs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 603a85f..e1495b0 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -331,7 +331,7 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci = do lift [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) $(case maybeDoc of Just (Just doc) -> [|Just $(TH.stringE doc)|]; _ -> [|Nothing|]) |] - isSingleRecordConstructor (constructorVariant -> RecordConstructor [x]) = True + isSingleRecordConstructor (constructorVariant -> RecordConstructor [_]) = True isSingleRecordConstructor _ = False -- * Convenience functions diff --git a/test/Util/Aeson.hs b/test/Util/Aeson.hs index 68cf681..b38b900 100644 --- a/test/Util/Aeson.hs +++ b/test/Util/Aeson.hs @@ -3,8 +3,10 @@ module Util.Aeson where #if MIN_VERSION_aeson(2,0,0) +import qualified Data.Aeson.Key as K import qualified Data.Aeson.KeyMap as KM +aesonFromList :: [(K.Key, v)] -> KM.KeyMap v aesonFromList = KM.fromList #else import Data.HashMap.Strict as HM From cbc75951e36c9a0f40e1feb8864903730fc727e4 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 21:47:38 -0800 Subject: [PATCH 142/208] GetDoc.hs test passing + start to clean up test extensions --- aeson-typescript.cabal | 2 +- package.yaml | 2 ++ test/Basic.hs | 1 - test/ClosedTypeFamilies.hs | 1 - test/Generic.hs | 1 - test/GetDoc.hs | 21 +++++++-------------- test/HigherKind.hs | 1 - test/OpenTypeFamilies.hs | 1 - test/UntaggedTagSingleConstructors.hs | 5 ----- test/UnwrapUnaryRecords.hs | 9 ++------- 10 files changed, 12 insertions(+), 32 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 8c0a9b3..5b91721 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -102,7 +102,7 @@ test-suite aeson-typescript-tests hs-source-dirs: test src - ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N + ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -haddock -fno-warn-unused-top-binds build-depends: aeson , aeson-typescript diff --git a/package.yaml b/package.yaml index 49a963e..3728830 100644 --- a/package.yaml +++ b/package.yaml @@ -56,6 +56,8 @@ tests: - -threaded - -rtsopts - -with-rtsopts=-N + - -haddock + - -fno-warn-unused-top-binds dependencies: - aeson-typescript - bytestring diff --git a/test/Basic.hs b/test/Basic.hs index ec66cb5..7ccfee9 100644 --- a/test/Basic.hs +++ b/test/Basic.hs @@ -11,7 +11,6 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} module Basic (tests) where diff --git a/test/ClosedTypeFamilies.hs b/test/ClosedTypeFamilies.hs index 29fa686..986cf0a 100644 --- a/test/ClosedTypeFamilies.hs +++ b/test/ClosedTypeFamilies.hs @@ -12,7 +12,6 @@ {-# LANGUAGE TypeFamilyDependencies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} module ClosedTypeFamilies (tests) where diff --git a/test/Generic.hs b/test/Generic.hs index 13ada57..1d2ea09 100644 --- a/test/Generic.hs +++ b/test/Generic.hs @@ -12,7 +12,6 @@ {-# LANGUAGE TypeFamilyDependencies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} module Generic (tests) where diff --git a/test/GetDoc.hs b/test/GetDoc.hs index 7cc231c..c953f80 100644 --- a/test/GetDoc.hs +++ b/test/GetDoc.hs @@ -1,17 +1,6 @@ {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} module GetDoc (tests) where @@ -26,16 +15,20 @@ import Test.Hspec -- | OneField is a type with a single field data OneField = OneField { + -- | This is a simple string simpleString :: String - -- ^ This is a simple string. } $(deriveTypeScript A.defaultOptions ''OneField) tests :: SpecWith () tests = describe "getDoc tests" $ do it [i|Works with a simple record type|] $ do - (getTypeScriptDeclarations (Proxy :: Proxy OneField)) `shouldBe` ([]) - + (getTypeScriptDeclarations (Proxy :: Proxy OneField)) `shouldBe` ([ + TSTypeAlternatives "OneField" [] ["IOneField"] + , TSInterfaceDeclaration "IOneField" [] [ + TSField False "simpleString" "string" (Just " This is a simple string") + ] + ]) main :: IO () main = hspec tests diff --git a/test/HigherKind.hs b/test/HigherKind.hs index 20afa6b..4f55473 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -11,7 +11,6 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} module HigherKind (tests) where diff --git a/test/OpenTypeFamilies.hs b/test/OpenTypeFamilies.hs index e860277..9f13e96 100644 --- a/test/OpenTypeFamilies.hs +++ b/test/OpenTypeFamilies.hs @@ -12,7 +12,6 @@ {-# LANGUAGE TypeFamilyDependencies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} module OpenTypeFamilies (tests) where diff --git a/test/UntaggedTagSingleConstructors.hs b/test/UntaggedTagSingleConstructors.hs index 5970c5e..da8e9bb 100644 --- a/test/UntaggedTagSingleConstructors.hs +++ b/test/UntaggedTagSingleConstructors.hs @@ -1,14 +1,9 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module UntaggedTagSingleConstructors (main, tests) where diff --git a/test/UnwrapUnaryRecords.hs b/test/UnwrapUnaryRecords.hs index 2b93486..7764fb8 100644 --- a/test/UnwrapUnaryRecords.hs +++ b/test/UnwrapUnaryRecords.hs @@ -1,14 +1,9 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module UnwrapUnaryRecords (allTests) where @@ -42,5 +37,5 @@ tests = describe "UnwrapUnaryRecords" $ it "tests are disabled for this Aeson ve allTests = tests #endif --- main :: IO () --- main = hspec allTests +main :: IO () +main = hspec allTests From 332ff83fd512dca9a81a69f0ec57ae33e04a7e4a Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 21:47:50 -0800 Subject: [PATCH 143/208] Update github CI --- .github/workflows/aeson-typescript.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 9c2d5ad..2fc03f6 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -18,7 +18,8 @@ jobs: - "8.8.4" - "8.10.7" - "9.0.2" - - "9.2.4" + - "9.2.6" + - "9.4.4" # exclude: # - os: macOS-latest # ghc: 8.8.3 @@ -71,7 +72,8 @@ jobs: - "8.8.4" - "8.10.7" - "9.0.2" - - "9.2.4" + - "9.2.6" + - "9.4.4" steps: - uses: actions/checkout@v2 From ba10af7287cd67e578b51f084bde3629ccee546a Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 21:50:56 -0800 Subject: [PATCH 144/208] Run CI on all branches --- .github/workflows/aeson-typescript.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 2fc03f6..55ce756 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -3,7 +3,6 @@ name: aeson-typescript on: pull_request: push: - branches: [master] jobs: cabal: From 68c56eaafa6b05ca4da6acc0da98cb55359f14b7 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 21:54:42 -0800 Subject: [PATCH 145/208] Cleaning up test extensions --- aeson-typescript.cabal | 5 +++++ package.yaml | 5 +++++ test/Basic.hs | 3 --- test/ClosedTypeFamilies.hs | 3 --- test/Generic.hs | 3 --- test/HigherKind.hs | 3 --- test/NoOmitNothingFields.hs | 3 --- test/ObjectWithSingleFieldNoTagSingleConstructors.hs | 3 --- test/ObjectWithSingleFieldTagSingleConstructors.hs | 3 --- test/OmitNothingFields.hs | 3 --- test/OpenTypeFamilies.hs | 3 --- test/TaggedObjectNoTagSingleConstructors.hs | 3 --- test/TaggedObjectTagSingleConstructors.hs | 3 --- test/TestBoilerplate.hs | 3 --- test/TwoElemArrayNoTagSingleConstructors.hs | 3 --- test/TwoElemArrayTagSingleConstructors.hs | 3 --- test/UntaggedNoTagSingleConstructors.hs | 3 --- test/UntaggedTagSingleConstructors.hs | 3 --- test/UnwrapUnaryRecords.hs | 3 --- 19 files changed, 10 insertions(+), 51 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 5b91721..4ed8e89 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -102,6 +102,11 @@ test-suite aeson-typescript-tests hs-source-dirs: test src + default-extensions: + OverloadedStrings + ScopedTypeVariables + KindSignatures + FlexibleContexts ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -haddock -fno-warn-unused-top-binds build-depends: aeson diff --git a/package.yaml b/package.yaml index 3728830..18fea5d 100644 --- a/package.yaml +++ b/package.yaml @@ -58,6 +58,11 @@ tests: - -with-rtsopts=-N - -haddock - -fno-warn-unused-top-binds + default-extensions: + - OverloadedStrings + - ScopedTypeVariables + - KindSignatures + - FlexibleContexts dependencies: - aeson-typescript - bytestring diff --git a/test/Basic.hs b/test/Basic.hs index 7ccfee9..edd589a 100644 --- a/test/Basic.hs +++ b/test/Basic.hs @@ -1,15 +1,12 @@ {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} module Basic (tests) where diff --git a/test/ClosedTypeFamilies.hs b/test/ClosedTypeFamilies.hs index 986cf0a..24c3008 100644 --- a/test/ClosedTypeFamilies.hs +++ b/test/ClosedTypeFamilies.hs @@ -1,16 +1,13 @@ {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilyDependencies #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} module ClosedTypeFamilies (tests) where diff --git a/test/Generic.hs b/test/Generic.hs index 1d2ea09..27494cb 100644 --- a/test/Generic.hs +++ b/test/Generic.hs @@ -1,16 +1,13 @@ {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilyDependencies #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} module Generic (tests) where diff --git a/test/HigherKind.hs b/test/HigherKind.hs index 4f55473..f076032 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -1,15 +1,12 @@ {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} module HigherKind (tests) where diff --git a/test/NoOmitNothingFields.hs b/test/NoOmitNothingFields.hs index 2a8158f..a65bb38 100644 --- a/test/NoOmitNothingFields.hs +++ b/test/NoOmitNothingFields.hs @@ -1,10 +1,7 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE KindSignatures #-} {-# LANGUAGE MonoLocalBinds #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} diff --git a/test/ObjectWithSingleFieldNoTagSingleConstructors.hs b/test/ObjectWithSingleFieldNoTagSingleConstructors.hs index 310b5d0..7e72a0b 100644 --- a/test/ObjectWithSingleFieldNoTagSingleConstructors.hs +++ b/test/ObjectWithSingleFieldNoTagSingleConstructors.hs @@ -1,12 +1,9 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/ObjectWithSingleFieldTagSingleConstructors.hs b/test/ObjectWithSingleFieldTagSingleConstructors.hs index 7251aac..fa15288 100644 --- a/test/ObjectWithSingleFieldTagSingleConstructors.hs +++ b/test/ObjectWithSingleFieldTagSingleConstructors.hs @@ -1,12 +1,9 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/OmitNothingFields.hs b/test/OmitNothingFields.hs index 55918fd..ea61e60 100644 --- a/test/OmitNothingFields.hs +++ b/test/OmitNothingFields.hs @@ -1,12 +1,9 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/OpenTypeFamilies.hs b/test/OpenTypeFamilies.hs index 9f13e96..2d4f1f7 100644 --- a/test/OpenTypeFamilies.hs +++ b/test/OpenTypeFamilies.hs @@ -1,16 +1,13 @@ {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilyDependencies #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} module OpenTypeFamilies (tests) where diff --git a/test/TaggedObjectNoTagSingleConstructors.hs b/test/TaggedObjectNoTagSingleConstructors.hs index 8a942a5..160a452 100644 --- a/test/TaggedObjectNoTagSingleConstructors.hs +++ b/test/TaggedObjectNoTagSingleConstructors.hs @@ -1,12 +1,9 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/TaggedObjectTagSingleConstructors.hs b/test/TaggedObjectTagSingleConstructors.hs index f86621d..8806672 100644 --- a/test/TaggedObjectTagSingleConstructors.hs +++ b/test/TaggedObjectTagSingleConstructors.hs @@ -1,12 +1,9 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index 062c0e0..0c0d7bd 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -1,12 +1,9 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} diff --git a/test/TwoElemArrayNoTagSingleConstructors.hs b/test/TwoElemArrayNoTagSingleConstructors.hs index f25b3b8..abcbb7c 100644 --- a/test/TwoElemArrayNoTagSingleConstructors.hs +++ b/test/TwoElemArrayNoTagSingleConstructors.hs @@ -1,12 +1,9 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/TwoElemArrayTagSingleConstructors.hs b/test/TwoElemArrayTagSingleConstructors.hs index 6b0d4b7..ca8d29d 100644 --- a/test/TwoElemArrayTagSingleConstructors.hs +++ b/test/TwoElemArrayTagSingleConstructors.hs @@ -1,12 +1,9 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/UntaggedNoTagSingleConstructors.hs b/test/UntaggedNoTagSingleConstructors.hs index 8ca4738..8a1d4f9 100644 --- a/test/UntaggedNoTagSingleConstructors.hs +++ b/test/UntaggedNoTagSingleConstructors.hs @@ -1,12 +1,9 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/UntaggedTagSingleConstructors.hs b/test/UntaggedTagSingleConstructors.hs index da8e9bb..c2a3004 100644 --- a/test/UntaggedTagSingleConstructors.hs +++ b/test/UntaggedTagSingleConstructors.hs @@ -1,9 +1,6 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE FlexibleContexts #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module UntaggedTagSingleConstructors (main, tests) where diff --git a/test/UnwrapUnaryRecords.hs b/test/UnwrapUnaryRecords.hs index 7764fb8..2221e68 100644 --- a/test/UnwrapUnaryRecords.hs +++ b/test/UnwrapUnaryRecords.hs @@ -1,9 +1,6 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE FlexibleContexts #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module UnwrapUnaryRecords (allTests) where From d17d4573fed9cb355b9e618572ac056b3cd2d77c Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 21:56:18 -0800 Subject: [PATCH 146/208] Bump haskell setup actions --- .github/workflows/aeson-typescript.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 55ce756..8325ada 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -78,7 +78,7 @@ jobs: - uses: actions/checkout@v2 if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master' - - uses: haskell/actions/setup@v1 + - uses: haskell/actions/setup@v2 name: Setup Haskell Stack with: ghc-version: ${{ matrix.ghc }} From 724db4dac237db522084bce59731471d71749933 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 21:59:50 -0800 Subject: [PATCH 147/208] More test extension cleanup --- aeson-typescript.cabal | 2 ++ package.yaml | 2 ++ test/Basic.hs | 5 ----- test/ClosedTypeFamilies.hs | 5 ----- test/Formatting.hs | 3 --- test/Generic.hs | 5 ----- test/GetDoc.hs | 3 --- test/HigherKind.hs | 5 ----- test/NoOmitNothingFields.hs | 5 ----- test/ObjectWithSingleFieldNoTagSingleConstructors.hs | 5 ----- test/ObjectWithSingleFieldTagSingleConstructors.hs | 5 ----- test/OmitNothingFields.hs | 5 ----- test/OpenTypeFamilies.hs | 5 ----- test/TaggedObjectNoTagSingleConstructors.hs | 5 ----- test/TaggedObjectTagSingleConstructors.hs | 5 ----- test/TestBoilerplate.hs | 5 ----- test/TwoElemArrayNoTagSingleConstructors.hs | 5 ----- test/TwoElemArrayTagSingleConstructors.hs | 5 ----- test/UntaggedNoTagSingleConstructors.hs | 5 ----- test/UntaggedTagSingleConstructors.hs | 2 -- test/UnwrapUnaryRecords.hs | 2 -- 21 files changed, 4 insertions(+), 85 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 4ed8e89..a1a7ea6 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -107,6 +107,8 @@ test-suite aeson-typescript-tests ScopedTypeVariables KindSignatures FlexibleContexts + QuasiQuotes + TemplateHaskell ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -haddock -fno-warn-unused-top-binds build-depends: aeson diff --git a/package.yaml b/package.yaml index 18fea5d..e9e9a28 100644 --- a/package.yaml +++ b/package.yaml @@ -63,6 +63,8 @@ tests: - ScopedTypeVariables - KindSignatures - FlexibleContexts + - QuasiQuotes + - TemplateHaskell dependencies: - aeson-typescript - bytestring diff --git a/test/Basic.hs b/test/Basic.hs index edd589a..ec98b84 100644 --- a/test/Basic.hs +++ b/test/Basic.hs @@ -1,8 +1,3 @@ -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} diff --git a/test/ClosedTypeFamilies.hs b/test/ClosedTypeFamilies.hs index 24c3008..d947ace 100644 --- a/test/ClosedTypeFamilies.hs +++ b/test/ClosedTypeFamilies.hs @@ -1,8 +1,3 @@ -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} diff --git a/test/Formatting.hs b/test/Formatting.hs index 0ad964b..c4fec32 100644 --- a/test/Formatting.hs +++ b/test/Formatting.hs @@ -1,7 +1,4 @@ -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} -{-# LANGUAGE QuasiQuotes #-} module Formatting (tests) where diff --git a/test/Generic.hs b/test/Generic.hs index 27494cb..e206bf0 100644 --- a/test/Generic.hs +++ b/test/Generic.hs @@ -1,8 +1,3 @@ -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} diff --git a/test/GetDoc.hs b/test/GetDoc.hs index c953f80..0f91f82 100644 --- a/test/GetDoc.hs +++ b/test/GetDoc.hs @@ -1,6 +1,3 @@ -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE ScopedTypeVariables #-} module GetDoc (tests) where diff --git a/test/HigherKind.hs b/test/HigherKind.hs index f076032..d0a588f 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -1,8 +1,3 @@ -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} diff --git a/test/NoOmitNothingFields.hs b/test/NoOmitNothingFields.hs index a65bb38..3d9a7f0 100644 --- a/test/NoOmitNothingFields.hs +++ b/test/NoOmitNothingFields.hs @@ -1,11 +1,6 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module NoOmitNothingFields (allTests) where diff --git a/test/ObjectWithSingleFieldNoTagSingleConstructors.hs b/test/ObjectWithSingleFieldNoTagSingleConstructors.hs index 7e72a0b..63ab976 100644 --- a/test/ObjectWithSingleFieldNoTagSingleConstructors.hs +++ b/test/ObjectWithSingleFieldNoTagSingleConstructors.hs @@ -1,9 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/ObjectWithSingleFieldTagSingleConstructors.hs b/test/ObjectWithSingleFieldTagSingleConstructors.hs index fa15288..014daab 100644 --- a/test/ObjectWithSingleFieldTagSingleConstructors.hs +++ b/test/ObjectWithSingleFieldTagSingleConstructors.hs @@ -1,9 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/OmitNothingFields.hs b/test/OmitNothingFields.hs index ea61e60..52f665b 100644 --- a/test/OmitNothingFields.hs +++ b/test/OmitNothingFields.hs @@ -1,9 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/OpenTypeFamilies.hs b/test/OpenTypeFamilies.hs index 2d4f1f7..8e96307 100644 --- a/test/OpenTypeFamilies.hs +++ b/test/OpenTypeFamilies.hs @@ -1,8 +1,3 @@ -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} diff --git a/test/TaggedObjectNoTagSingleConstructors.hs b/test/TaggedObjectNoTagSingleConstructors.hs index 160a452..f4181c0 100644 --- a/test/TaggedObjectNoTagSingleConstructors.hs +++ b/test/TaggedObjectNoTagSingleConstructors.hs @@ -1,9 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/TaggedObjectTagSingleConstructors.hs b/test/TaggedObjectTagSingleConstructors.hs index 8806672..65e6ec4 100644 --- a/test/TaggedObjectTagSingleConstructors.hs +++ b/test/TaggedObjectTagSingleConstructors.hs @@ -1,9 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index 0c0d7bd..5f0b438 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -1,9 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} diff --git a/test/TwoElemArrayNoTagSingleConstructors.hs b/test/TwoElemArrayNoTagSingleConstructors.hs index abcbb7c..d634a5e 100644 --- a/test/TwoElemArrayNoTagSingleConstructors.hs +++ b/test/TwoElemArrayNoTagSingleConstructors.hs @@ -1,9 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/TwoElemArrayTagSingleConstructors.hs b/test/TwoElemArrayTagSingleConstructors.hs index ca8d29d..b3943d3 100644 --- a/test/TwoElemArrayTagSingleConstructors.hs +++ b/test/TwoElemArrayTagSingleConstructors.hs @@ -1,9 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/UntaggedNoTagSingleConstructors.hs b/test/UntaggedNoTagSingleConstructors.hs index 8a1d4f9..76c39a9 100644 --- a/test/UntaggedNoTagSingleConstructors.hs +++ b/test/UntaggedNoTagSingleConstructors.hs @@ -1,9 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/UntaggedTagSingleConstructors.hs b/test/UntaggedTagSingleConstructors.hs index c2a3004..dc049b1 100644 --- a/test/UntaggedTagSingleConstructors.hs +++ b/test/UntaggedTagSingleConstructors.hs @@ -1,6 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module UntaggedTagSingleConstructors (main, tests) where diff --git a/test/UnwrapUnaryRecords.hs b/test/UnwrapUnaryRecords.hs index 2221e68..37f398d 100644 --- a/test/UnwrapUnaryRecords.hs +++ b/test/UnwrapUnaryRecords.hs @@ -1,6 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module UnwrapUnaryRecords (allTests) where From c7175f81444223d3215f490e8faff43b7a85b633 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 22:02:02 -0800 Subject: [PATCH 148/208] Always do checkout --- .github/workflows/aeson-typescript.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 8325ada..c215331 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -76,7 +76,6 @@ jobs: steps: - uses: actions/checkout@v2 - if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master' - uses: haskell/actions/setup@v2 name: Setup Haskell Stack From bf1f5821c853dc1791337eec84ac784688556696 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 22:02:13 -0800 Subject: [PATCH 149/208] More extension cleanup --- test/Basic.hs | 1 - test/ClosedTypeFamilies.hs | 1 - test/Generic.hs | 1 - test/HigherKind.hs | 1 - test/NoOmitNothingFields.hs | 1 - test/ObjectWithSingleFieldNoTagSingleConstructors.hs | 1 - test/ObjectWithSingleFieldTagSingleConstructors.hs | 1 - test/OmitNothingFields.hs | 1 - test/OpenTypeFamilies.hs | 1 - test/TaggedObjectNoTagSingleConstructors.hs | 1 - test/TaggedObjectTagSingleConstructors.hs | 1 - test/TestBoilerplate.hs | 1 - test/TwoElemArrayNoTagSingleConstructors.hs | 1 - test/TwoElemArrayTagSingleConstructors.hs | 1 - test/UntaggedNoTagSingleConstructors.hs | 1 - 15 files changed, 15 deletions(-) diff --git a/test/Basic.hs b/test/Basic.hs index ec98b84..1d82527 100644 --- a/test/Basic.hs +++ b/test/Basic.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} diff --git a/test/ClosedTypeFamilies.hs b/test/ClosedTypeFamilies.hs index d947ace..b9b2302 100644 --- a/test/ClosedTypeFamilies.hs +++ b/test/ClosedTypeFamilies.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} diff --git a/test/Generic.hs b/test/Generic.hs index e206bf0..c5c2081 100644 --- a/test/Generic.hs +++ b/test/Generic.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} diff --git a/test/HigherKind.hs b/test/HigherKind.hs index d0a588f..c26e6a1 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} diff --git a/test/NoOmitNothingFields.hs b/test/NoOmitNothingFields.hs index 3d9a7f0..ef42939 100644 --- a/test/NoOmitNothingFields.hs +++ b/test/NoOmitNothingFields.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/ObjectWithSingleFieldNoTagSingleConstructors.hs b/test/ObjectWithSingleFieldNoTagSingleConstructors.hs index 63ab976..77aeb6f 100644 --- a/test/ObjectWithSingleFieldNoTagSingleConstructors.hs +++ b/test/ObjectWithSingleFieldNoTagSingleConstructors.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/ObjectWithSingleFieldTagSingleConstructors.hs b/test/ObjectWithSingleFieldTagSingleConstructors.hs index 014daab..d05f416 100644 --- a/test/ObjectWithSingleFieldTagSingleConstructors.hs +++ b/test/ObjectWithSingleFieldTagSingleConstructors.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/OmitNothingFields.hs b/test/OmitNothingFields.hs index 52f665b..f58836d 100644 --- a/test/OmitNothingFields.hs +++ b/test/OmitNothingFields.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/OpenTypeFamilies.hs b/test/OpenTypeFamilies.hs index 8e96307..961ea5e 100644 --- a/test/OpenTypeFamilies.hs +++ b/test/OpenTypeFamilies.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} diff --git a/test/TaggedObjectNoTagSingleConstructors.hs b/test/TaggedObjectNoTagSingleConstructors.hs index f4181c0..a81f925 100644 --- a/test/TaggedObjectNoTagSingleConstructors.hs +++ b/test/TaggedObjectNoTagSingleConstructors.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/TaggedObjectTagSingleConstructors.hs b/test/TaggedObjectTagSingleConstructors.hs index 65e6ec4..d5e37c8 100644 --- a/test/TaggedObjectTagSingleConstructors.hs +++ b/test/TaggedObjectTagSingleConstructors.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index 5f0b438..591f44b 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/TwoElemArrayNoTagSingleConstructors.hs b/test/TwoElemArrayNoTagSingleConstructors.hs index d634a5e..974be7f 100644 --- a/test/TwoElemArrayNoTagSingleConstructors.hs +++ b/test/TwoElemArrayNoTagSingleConstructors.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/TwoElemArrayTagSingleConstructors.hs b/test/TwoElemArrayTagSingleConstructors.hs index b3943d3..b3fc9a1 100644 --- a/test/TwoElemArrayTagSingleConstructors.hs +++ b/test/TwoElemArrayTagSingleConstructors.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/UntaggedNoTagSingleConstructors.hs b/test/UntaggedNoTagSingleConstructors.hs index 76c39a9..39c308a 100644 --- a/test/UntaggedNoTagSingleConstructors.hs +++ b/test/UntaggedNoTagSingleConstructors.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} From 3117bfc66736d0d9e2b7e4a87f94509b58e60e00 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 22:05:10 -0800 Subject: [PATCH 150/208] More CI fixups --- .github/workflows/aeson-typescript.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index c215331..669806a 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -25,9 +25,8 @@ jobs: steps: - uses: actions/checkout@v2 - if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master' - - uses: haskell/actions/setup@v1 + - uses: haskell/actions/setup@v2 id: setup-haskell-cabal name: Setup Haskell with: From 0bb53c0e5cf6a78e5e714cf7be3247406f651037 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 22:06:12 -0800 Subject: [PATCH 151/208] More extension cleanup --- aeson-typescript.cabal | 1 + package.yaml | 1 + test/Basic.hs | 2 -- test/ClosedTypeFamilies.hs | 2 -- test/Generic.hs | 3 --- test/HigherKind.hs | 2 -- test/OpenTypeFamilies.hs | 2 -- test/TestBoilerplate.hs | 1 - 8 files changed, 2 insertions(+), 12 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index a1a7ea6..2b55ef7 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -109,6 +109,7 @@ test-suite aeson-typescript-tests FlexibleContexts QuasiQuotes TemplateHaskell + TypeFamilies ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -haddock -fno-warn-unused-top-binds build-depends: aeson diff --git a/package.yaml b/package.yaml index e9e9a28..982f957 100644 --- a/package.yaml +++ b/package.yaml @@ -65,6 +65,7 @@ tests: - FlexibleContexts - QuasiQuotes - TemplateHaskell + - TypeFamilies dependencies: - aeson-typescript - bytestring diff --git a/test/Basic.hs b/test/Basic.hs index 1d82527..6facfcb 100644 --- a/test/Basic.hs +++ b/test/Basic.hs @@ -1,6 +1,4 @@ {-# LANGUAGE DataKinds #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} module Basic (tests) where diff --git a/test/ClosedTypeFamilies.hs b/test/ClosedTypeFamilies.hs index b9b2302..50191d2 100644 --- a/test/ClosedTypeFamilies.hs +++ b/test/ClosedTypeFamilies.hs @@ -1,6 +1,4 @@ {-# LANGUAGE DataKinds #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilyDependencies #-} {-# LANGUAGE UndecidableInstances #-} diff --git a/test/Generic.hs b/test/Generic.hs index c5c2081..e1c33d3 100644 --- a/test/Generic.hs +++ b/test/Generic.hs @@ -1,7 +1,4 @@ {-# LANGUAGE DataKinds #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeFamilyDependencies #-} {-# LANGUAGE UndecidableInstances #-} module Generic (tests) where diff --git a/test/HigherKind.hs b/test/HigherKind.hs index c26e6a1..7c1430d 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -1,6 +1,4 @@ {-# LANGUAGE DataKinds #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} module HigherKind (tests) where diff --git a/test/OpenTypeFamilies.hs b/test/OpenTypeFamilies.hs index 961ea5e..b6bc6d5 100644 --- a/test/OpenTypeFamilies.hs +++ b/test/OpenTypeFamilies.hs @@ -1,6 +1,4 @@ {-# LANGUAGE DataKinds #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilyDependencies #-} {-# LANGUAGE UndecidableInstances #-} diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index 591f44b..3cf77b5 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-} From 85c11ec20b24ad7adac55f9063a01da99e3c21d1 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 22:08:25 -0800 Subject: [PATCH 152/208] More extension cleanup --- test/Basic.hs | 2 -- test/ClosedTypeFamilies.hs | 1 - test/Generic.hs | 2 -- test/HigherKind.hs | 2 -- test/OpenTypeFamilies.hs | 1 - 5 files changed, 8 deletions(-) diff --git a/test/Basic.hs b/test/Basic.hs index 6facfcb..eec08ea 100644 --- a/test/Basic.hs +++ b/test/Basic.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE UndecidableInstances #-} module Basic (tests) where diff --git a/test/ClosedTypeFamilies.hs b/test/ClosedTypeFamilies.hs index 50191d2..d8381db 100644 --- a/test/ClosedTypeFamilies.hs +++ b/test/ClosedTypeFamilies.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeFamilyDependencies #-} {-# LANGUAGE UndecidableInstances #-} diff --git a/test/Generic.hs b/test/Generic.hs index e1c33d3..889ac8d 100644 --- a/test/Generic.hs +++ b/test/Generic.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE UndecidableInstances #-} module Generic (tests) where diff --git a/test/HigherKind.hs b/test/HigherKind.hs index 7c1430d..b272ecc 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE UndecidableInstances #-} module HigherKind (tests) where diff --git a/test/OpenTypeFamilies.hs b/test/OpenTypeFamilies.hs index b6bc6d5..f668757 100644 --- a/test/OpenTypeFamilies.hs +++ b/test/OpenTypeFamilies.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeFamilyDependencies #-} {-# LANGUAGE UndecidableInstances #-} From 62882d05be682b8d5bca8d961b608a2e15eef5fb Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 22:11:44 -0800 Subject: [PATCH 153/208] More extension cleanup --- aeson-typescript.cabal | 1 + package.yaml | 1 + test/LegalNameSpec.hs | 1 + test/NoOmitNothingFields.hs | 2 -- test/ObjectWithSingleFieldNoTagSingleConstructors.hs | 2 -- test/ObjectWithSingleFieldTagSingleConstructors.hs | 2 -- test/OmitNothingFields.hs | 2 -- test/TaggedObjectNoTagSingleConstructors.hs | 2 -- test/TaggedObjectTagSingleConstructors.hs | 2 -- test/TestBoilerplate.hs | 1 - test/TwoElemArrayNoTagSingleConstructors.hs | 2 -- test/TwoElemArrayTagSingleConstructors.hs | 2 -- test/UntaggedNoTagSingleConstructors.hs | 1 - test/Util.hs | 2 +- 14 files changed, 4 insertions(+), 19 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 2b55ef7..71176a2 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -110,6 +110,7 @@ test-suite aeson-typescript-tests QuasiQuotes TemplateHaskell TypeFamilies + LambdaCase ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -haddock -fno-warn-unused-top-binds build-depends: aeson diff --git a/package.yaml b/package.yaml index 982f957..c86a44f 100644 --- a/package.yaml +++ b/package.yaml @@ -66,6 +66,7 @@ tests: - QuasiQuotes - TemplateHaskell - TypeFamilies + - LambdaCase dependencies: - aeson-typescript - bytestring diff --git a/test/LegalNameSpec.hs b/test/LegalNameSpec.hs index f657092..638f314 100644 --- a/test/LegalNameSpec.hs +++ b/test/LegalNameSpec.hs @@ -1,3 +1,4 @@ + module LegalNameSpec where import Data.Aeson.TypeScript.LegalName diff --git a/test/NoOmitNothingFields.hs b/test/NoOmitNothingFields.hs index ef42939..eb9d993 100644 --- a/test/NoOmitNothingFields.hs +++ b/test/NoOmitNothingFields.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module NoOmitNothingFields (allTests) where diff --git a/test/ObjectWithSingleFieldNoTagSingleConstructors.hs b/test/ObjectWithSingleFieldNoTagSingleConstructors.hs index 77aeb6f..f69464a 100644 --- a/test/ObjectWithSingleFieldNoTagSingleConstructors.hs +++ b/test/ObjectWithSingleFieldNoTagSingleConstructors.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module ObjectWithSingleFieldNoTagSingleConstructors (main, tests) where diff --git a/test/ObjectWithSingleFieldTagSingleConstructors.hs b/test/ObjectWithSingleFieldTagSingleConstructors.hs index d05f416..9920e0c 100644 --- a/test/ObjectWithSingleFieldTagSingleConstructors.hs +++ b/test/ObjectWithSingleFieldTagSingleConstructors.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module ObjectWithSingleFieldTagSingleConstructors (main, tests) where diff --git a/test/OmitNothingFields.hs b/test/OmitNothingFields.hs index f58836d..1107f70 100644 --- a/test/OmitNothingFields.hs +++ b/test/OmitNothingFields.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module OmitNothingFields (main, tests) where diff --git a/test/TaggedObjectNoTagSingleConstructors.hs b/test/TaggedObjectNoTagSingleConstructors.hs index a81f925..6993d5f 100644 --- a/test/TaggedObjectNoTagSingleConstructors.hs +++ b/test/TaggedObjectNoTagSingleConstructors.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module TaggedObjectNoTagSingleConstructors (main, tests) where diff --git a/test/TaggedObjectTagSingleConstructors.hs b/test/TaggedObjectTagSingleConstructors.hs index d5e37c8..3a1ee31 100644 --- a/test/TaggedObjectTagSingleConstructors.hs +++ b/test/TaggedObjectTagSingleConstructors.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module TaggedObjectTagSingleConstructors (main, tests) where diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index 3cf77b5..d8957a4 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE CPP #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/test/TwoElemArrayNoTagSingleConstructors.hs b/test/TwoElemArrayNoTagSingleConstructors.hs index 974be7f..35a2ae5 100644 --- a/test/TwoElemArrayNoTagSingleConstructors.hs +++ b/test/TwoElemArrayNoTagSingleConstructors.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module TwoElemArrayNoTagSingleConstructors (main, tests) where diff --git a/test/TwoElemArrayTagSingleConstructors.hs b/test/TwoElemArrayTagSingleConstructors.hs index b3fc9a1..e21ea3b 100644 --- a/test/TwoElemArrayTagSingleConstructors.hs +++ b/test/TwoElemArrayTagSingleConstructors.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module TwoElemArrayTagSingleConstructors (main, tests) where diff --git a/test/UntaggedNoTagSingleConstructors.hs b/test/UntaggedNoTagSingleConstructors.hs index 39c308a..8e3148d 100644 --- a/test/UntaggedNoTagSingleConstructors.hs +++ b/test/UntaggedNoTagSingleConstructors.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module UntaggedNoTagSingleConstructors (main, tests) where diff --git a/test/Util.hs b/test/Util.hs index ea6dfe4..180f657 100644 --- a/test/Util.hs +++ b/test/Util.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE CPP, QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, LambdaCase #-} +{-# LANGUAGE CPP #-} module Util where From d0aa202e07ae789b94b6cc1d6118e1f457409794 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 22:13:02 -0800 Subject: [PATCH 154/208] More extension cleanup --- aeson-typescript.cabal | 2 +- package.yaml | 1 + test/NoOmitNothingFields.hs | 1 - test/ObjectWithSingleFieldNoTagSingleConstructors.hs | 1 - test/ObjectWithSingleFieldTagSingleConstructors.hs | 1 - test/OmitNothingFields.hs | 1 - test/TaggedObjectNoTagSingleConstructors.hs | 1 - test/TaggedObjectTagSingleConstructors.hs | 1 - test/TestBoilerplate.hs | 1 - test/TwoElemArrayNoTagSingleConstructors.hs | 1 - test/TwoElemArrayTagSingleConstructors.hs | 1 - test/UntaggedNoTagSingleConstructors.hs | 1 - test/UntaggedTagSingleConstructors.hs | 1 - test/UnwrapUnaryRecords.hs | 1 - 14 files changed, 2 insertions(+), 13 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 71176a2..2837766 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -111,7 +111,7 @@ test-suite aeson-typescript-tests TemplateHaskell TypeFamilies LambdaCase - ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -haddock -fno-warn-unused-top-binds + ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -haddock -fno-warn-unused-top-binds -fno-warn-orphans build-depends: aeson , aeson-typescript diff --git a/package.yaml b/package.yaml index c86a44f..4626235 100644 --- a/package.yaml +++ b/package.yaml @@ -58,6 +58,7 @@ tests: - -with-rtsopts=-N - -haddock - -fno-warn-unused-top-binds + - -fno-warn-orphans default-extensions: - OverloadedStrings - ScopedTypeVariables diff --git a/test/NoOmitNothingFields.hs b/test/NoOmitNothingFields.hs index eb9d993..4f5da71 100644 --- a/test/NoOmitNothingFields.hs +++ b/test/NoOmitNothingFields.hs @@ -1,4 +1,3 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} module NoOmitNothingFields (allTests) where diff --git a/test/ObjectWithSingleFieldNoTagSingleConstructors.hs b/test/ObjectWithSingleFieldNoTagSingleConstructors.hs index f69464a..c075759 100644 --- a/test/ObjectWithSingleFieldNoTagSingleConstructors.hs +++ b/test/ObjectWithSingleFieldNoTagSingleConstructors.hs @@ -1,4 +1,3 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} module ObjectWithSingleFieldNoTagSingleConstructors (main, tests) where diff --git a/test/ObjectWithSingleFieldTagSingleConstructors.hs b/test/ObjectWithSingleFieldTagSingleConstructors.hs index 9920e0c..42807f0 100644 --- a/test/ObjectWithSingleFieldTagSingleConstructors.hs +++ b/test/ObjectWithSingleFieldTagSingleConstructors.hs @@ -1,4 +1,3 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} module ObjectWithSingleFieldTagSingleConstructors (main, tests) where diff --git a/test/OmitNothingFields.hs b/test/OmitNothingFields.hs index 1107f70..9993e3b 100644 --- a/test/OmitNothingFields.hs +++ b/test/OmitNothingFields.hs @@ -1,4 +1,3 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} module OmitNothingFields (main, tests) where diff --git a/test/TaggedObjectNoTagSingleConstructors.hs b/test/TaggedObjectNoTagSingleConstructors.hs index 6993d5f..91266e5 100644 --- a/test/TaggedObjectNoTagSingleConstructors.hs +++ b/test/TaggedObjectNoTagSingleConstructors.hs @@ -1,4 +1,3 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} module TaggedObjectNoTagSingleConstructors (main, tests) where diff --git a/test/TaggedObjectTagSingleConstructors.hs b/test/TaggedObjectTagSingleConstructors.hs index 3a1ee31..b5dba98 100644 --- a/test/TaggedObjectTagSingleConstructors.hs +++ b/test/TaggedObjectTagSingleConstructors.hs @@ -1,4 +1,3 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} module TaggedObjectTagSingleConstructors (main, tests) where diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index d8957a4..ac4afd0 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -1,5 +1,4 @@ {-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} module TestBoilerplate where diff --git a/test/TwoElemArrayNoTagSingleConstructors.hs b/test/TwoElemArrayNoTagSingleConstructors.hs index 35a2ae5..cc0bd2c 100644 --- a/test/TwoElemArrayNoTagSingleConstructors.hs +++ b/test/TwoElemArrayNoTagSingleConstructors.hs @@ -1,4 +1,3 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} module TwoElemArrayNoTagSingleConstructors (main, tests) where diff --git a/test/TwoElemArrayTagSingleConstructors.hs b/test/TwoElemArrayTagSingleConstructors.hs index e21ea3b..7bff5f1 100644 --- a/test/TwoElemArrayTagSingleConstructors.hs +++ b/test/TwoElemArrayTagSingleConstructors.hs @@ -1,4 +1,3 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} module TwoElemArrayTagSingleConstructors (main, tests) where diff --git a/test/UntaggedNoTagSingleConstructors.hs b/test/UntaggedNoTagSingleConstructors.hs index 8e3148d..d4862e6 100644 --- a/test/UntaggedNoTagSingleConstructors.hs +++ b/test/UntaggedNoTagSingleConstructors.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} module UntaggedNoTagSingleConstructors (main, tests) where diff --git a/test/UntaggedTagSingleConstructors.hs b/test/UntaggedTagSingleConstructors.hs index dc049b1..d94b811 100644 --- a/test/UntaggedTagSingleConstructors.hs +++ b/test/UntaggedTagSingleConstructors.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} module UntaggedTagSingleConstructors (main, tests) where diff --git a/test/UnwrapUnaryRecords.hs b/test/UnwrapUnaryRecords.hs index 37f398d..99c702d 100644 --- a/test/UnwrapUnaryRecords.hs +++ b/test/UnwrapUnaryRecords.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} module UnwrapUnaryRecords (allTests) where From 91b7205aa0ef931241595ead3a753751bd167fde Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 22:42:21 -0800 Subject: [PATCH 155/208] Fix getDoc call to use actual name, not transformed one --- src/Data/Aeson/TypeScript/TH.hs | 6 +++--- src/Data/Aeson/TypeScript/Util.hs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index e1495b0..73c764e 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -195,7 +195,7 @@ deriveTypeScript' options name extraOptions = do xs -> zip xs allStarConstructors'' genericVariablesAndSuffixes <- forM varsAndTVars $ \(var, tvar) -> do (_, genericInfos) <- runWriterT $ forM_ (datatypeCons datatypeInfo') $ \ci -> - forM_ (namesAndTypes options [] ci) $ \(_, typ) -> do + forM_ (namesAndTypes options [] ci) $ \(_, _, typ) -> do searchForConstraints extraOptions typ var return (var, (unifyGenericVariable genericInfos, tvar)) @@ -317,14 +317,14 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci = do $(return members)|] getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] - getTSFields = forM (namesAndTypes options genericVariables ci) $ \(nameString, typ) -> do + getTSFields = forM (namesAndTypes options genericVariables ci) $ \(name, nameString, typ) -> do (fieldTyp, optAsBool) <- lift $ case typ of (AppT (ConT name) t) | name == ''Maybe && not (omitNothingFields options) -> ( , ) <$> [|$(getTypeAsStringExp t) <> " | null"|] <*> getOptionalAsBoolExp t _ -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ #if MIN_VERSION_template_haskell(2,18,0) - maybeDoc <- lift $ nothingOnFail $ getDoc (DeclDoc (mkName nameString)) + maybeDoc <- lift $ nothingOnFail $ getDoc (DeclDoc name) #else let maybeDoc = Nothing #endif diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index 7e39892..74b2c29 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -148,14 +148,14 @@ mkInstance = InstanceD Nothing mkInstance = InstanceD #endif -namesAndTypes :: Options -> [(Name, (Suffix, Var))] -> ConstructorInfo -> [(String, Type)] +namesAndTypes :: Options -> [(Name, (Suffix, Var))] -> ConstructorInfo -> [(Name, String, Type)] namesAndTypes options genericVariables ci = case constructorVariant ci of - RecordConstructor names -> zip (fmap ((fieldLabelModifier options) . lastNameComponent') names) (constructorFields ci) + RecordConstructor names -> zip3 names (fmap ((fieldLabelModifier options) . lastNameComponent') names) (constructorFields ci) _ -> case sumEncoding options of TaggedObject _ contentsFieldName | isConstructorNullary ci -> [] - | otherwise -> [(contentsFieldName, contentsTupleTypeSubstituted genericVariables ci)] - _ -> [(constructorNameToUse options ci, contentsTupleTypeSubstituted genericVariables ci)] + | otherwise -> [(mkName "", contentsFieldName, contentsTupleTypeSubstituted genericVariables ci)] + _ -> [(constructorName ci, constructorNameToUse options ci, contentsTupleTypeSubstituted genericVariables ci)] constructorNameToUse :: Options -> ConstructorInfo -> String constructorNameToUse options ci = (constructorTagModifier options) $ lastNameComponent' (constructorName ci) From 40b5750af8495f9548ad2f538d786f39ead9590c Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 22:42:47 -0800 Subject: [PATCH 156/208] Add docstring example to README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d7c2fa3..4cdf469 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ data D a = Nullary | Product String Char a | Record { testOne :: Double , testTwo :: Bool + -- | This docstring will go into the generated TypeScript! , testThree :: D a } deriving Eq ``` @@ -41,6 +42,7 @@ interface IRecord { tag: "record"; One: number; Two: boolean; + // This docstring will go into the generated TypeScript! Three: D; } ``` From 02da0c6ddc2e20be0a424c2b0322e7327c08e194 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 22:42:58 -0800 Subject: [PATCH 157/208] Improve docstring formatting --- src/Data/Aeson/TypeScript/Formatting.hs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index f956b3b..eec3e7d 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -3,6 +3,7 @@ module Data.Aeson.TypeScript.Formatting where import Data.Aeson.TypeScript.Types +import qualified Data.List as L import Data.String.Interpolate import qualified Data.Text as T @@ -33,14 +34,14 @@ formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceNa [i|#{exportPrefix exportMode}interface #{modifiedInterfaceName}#{getGenericBrackets genericVariables} { #{ls} }|] where - ls = T.intercalate "\n" $ fmap T.pack [indentTo numIndentSpaces (formatTSField member <> ";") | member <- members] + ls = T.intercalate "\n" $ [indentTo numIndentSpaces (T.pack (formatTSField member <> ";")) | member <- members] modifiedInterfaceName = (\(li, name) -> li <> interfaceNameModifier name) . splitAt 1 $ interfaceName formatTSDeclaration _ (TSRawDeclaration text) = text --- | TODO: handle multiple lines -indentTo :: Int -> String -> String -indentTo numIndentSpaces s = replicate numIndentSpaces ' ' <> s +indentTo :: Int -> T.Text -> T.Text +indentTo numIndentSpaces input = T.intercalate "\n" [padding <> line | line <- T.splitOn "\n" input] + where padding = T.replicate numIndentSpaces " " exportPrefix :: ExportMode -> String exportPrefix ExportEach = "export " @@ -69,7 +70,10 @@ formatTSField (TSField optional name typ maybeDoc) = docPrefix <> [i|#{name}#{if where docPrefix = case maybeDoc of Nothing -> "" - Just doc -> "/* " <> doc <> " */\n" + Just doc | '\n' `L.elem` doc -> "/* " <> (deleteLeadingWhitespace doc) <> " */\n" + Just doc -> "// " <> (deleteLeadingWhitespace doc) <> "\n" + + deleteLeadingWhitespace = L.dropWhile (== ' ') getGenericBrackets :: [String] -> String getGenericBrackets [] = "" From 32f5672dac4da8fd16b9e258454c3671b93ca628 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 28 Feb 2023 22:44:37 -0800 Subject: [PATCH 158/208] Don't try to run GetDoc tests when not supported --- test/Spec.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/Spec.hs b/test/Spec.hs index 3424fef..d7f7548 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE CPP #-} module Main where @@ -30,7 +31,9 @@ main = hspec $ parallel $ do ClosedTypeFamilies.tests Formatting.tests Generic.tests +#if MIN_VERSION_template_haskell(2,18,0) GetDoc.tests +#endif HigherKind.tests LegalNameSpec.tests From 431d8543ae997f155ee0a01768be4f78b605d5be Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 1 Mar 2023 00:27:38 -0800 Subject: [PATCH 159/208] Get docs on interface names --- src/Data/Aeson/TypeScript/Formatting.hs | 12 +++++++++--- src/Data/Aeson/TypeScript/Instances.hs | 4 ++-- src/Data/Aeson/TypeScript/TH.hs | 23 +++++++++++++++-------- src/Data/Aeson/TypeScript/Types.hs | 3 ++- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index eec3e7d..2869f2c 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -30,10 +30,15 @@ formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVar enumType = [i|\n\ntype #{name} = keyof typeof #{typeNameModifier name};|] :: T.Text toEnumName = T.replace "\"" "" -formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceName genericVariables members) = - [i|#{exportPrefix exportMode}interface #{modifiedInterfaceName}#{getGenericBrackets genericVariables} { +formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceName genericVariables members maybeDoc) = + docPrefix <> [i|#{exportPrefix exportMode}interface #{modifiedInterfaceName}#{getGenericBrackets genericVariables} { #{ls} }|] where + docPrefix = case maybeDoc of + Nothing -> "" + Just doc | '\n' `L.elem` doc -> "/* " <> (deleteLeadingWhitespace doc) <> " */\n" + Just doc -> "// " <> (deleteLeadingWhitespace doc) <> "\n" + ls = T.intercalate "\n" $ [indentTo numIndentSpaces (T.pack (formatTSField member <> ";")) | member <- members] modifiedInterfaceName = (\(li, name) -> li <> interfaceNameModifier name) . splitAt 1 $ interfaceName @@ -73,7 +78,8 @@ formatTSField (TSField optional name typ maybeDoc) = docPrefix <> [i|#{name}#{if Just doc | '\n' `L.elem` doc -> "/* " <> (deleteLeadingWhitespace doc) <> " */\n" Just doc -> "// " <> (deleteLeadingWhitespace doc) <> "\n" - deleteLeadingWhitespace = L.dropWhile (== ' ') +deleteLeadingWhitespace :: String -> String +deleteLeadingWhitespace = L.dropWhile (== ' ') getGenericBrackets :: [String] -> String getGenericBrackets [] = "" diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index ee566be..6d50c5b 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -90,8 +90,8 @@ instance {-# OVERLAPPING #-} TypeScript [Char] where instance (TypeScript a, TypeScript b) => TypeScript (Either a b) where getTypeScriptType _ = [i|Either<#{getTypeScriptType (Proxy :: Proxy a)}, #{getTypeScriptType (Proxy :: Proxy b)}>|] getTypeScriptDeclarations _ = [TSTypeAlternatives "Either" ["T1", "T2"] ["Left", "Right"] - , TSInterfaceDeclaration "Left" ["T"] [TSField False "Left" "T" Nothing] - , TSInterfaceDeclaration "Right" ["T"] [TSField False "Right" "T" Nothing] + , TSInterfaceDeclaration "Left" ["T"] [TSField False "Left" "T" Nothing] Nothing + , TSInterfaceDeclaration "Right" ["T"] [TSField False "Right" "T" Nothing] Nothing ] getParentTypes _ = L.nub [ (TSType (Proxy :: Proxy a)) , (TSType (Proxy :: Proxy b)) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 73c764e..773e634 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -314,7 +314,20 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci = do assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) $(genericVariablesListExpr True genericVariables) - $(return members)|] + $(return members) + $(tryGetDoc (constructorName ci))|] + + tryGetDoc :: Name -> Q Exp + tryGetDoc n = do +#if MIN_VERSION_template_haskell(2,18,0) + maybeDoc <- nothingOnFail $ getDoc (DeclDoc n) +#else + let maybeDoc = Nothing +#endif + + case maybeDoc of + Just (Just doc) -> [|Just $(TH.stringE doc)|] + _ -> [|Nothing|] getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] getTSFields = forM (namesAndTypes options genericVariables ci) $ \(name, nameString, typ) -> do @@ -323,13 +336,7 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci = do ( , ) <$> [|$(getTypeAsStringExp t) <> " | null"|] <*> getOptionalAsBoolExp t _ -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ -#if MIN_VERSION_template_haskell(2,18,0) - maybeDoc <- lift $ nothingOnFail $ getDoc (DeclDoc name) -#else - let maybeDoc = Nothing -#endif - - lift [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) $(case maybeDoc of Just (Just doc) -> [|Just $(TH.stringE doc)|]; _ -> [|Nothing|]) |] + lift [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) $(tryGetDoc name) |] isSingleRecordConstructor (constructorVariant -> RecordConstructor [_]) = True isSingleRecordConstructor _ = False diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index c42af91..ec23d1c 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -86,7 +86,8 @@ instance Show TSType where data TSDeclaration = TSInterfaceDeclaration { interfaceName :: String , interfaceGenericVariables :: [String] - , interfaceMembers :: [TSField] } + , interfaceMembers :: [TSField] + , interfaceDoc :: Maybe String } | TSTypeAlternatives { typeName :: String , typeGenericVariables :: [String] , alternativeTypes :: [String]} From 856370007867e013d2499c5a6570ffa917f47a9c Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 1 Mar 2023 00:37:08 -0800 Subject: [PATCH 160/208] Update tests to use docs on interface names --- src/Data/Aeson/TypeScript/Lookup.hs | 2 +- test/ClosedTypeFamilies.hs | 8 ++++---- test/Generic.hs | 16 +++++----------- test/HigherKind.hs | 6 +++--- test/NoOmitNothingFields.hs | 1 + test/OmitNothingFields.hs | 1 + test/OpenTypeFamilies.hs | 8 ++++---- 7 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Lookup.hs b/src/Data/Aeson/TypeScript/Lookup.hs index 8c245c8..ef0e9a8 100644 --- a/src/Data/Aeson/TypeScript/Lookup.hs +++ b/src/Data/Aeson/TypeScript/Lookup.hs @@ -55,7 +55,7 @@ getClosedTypeFamilyInterfaceDecl name eqns = do #endif x -> fail [i|aeson-typescript doesn't know yet how to handle this type family equation: '#{x}'|] - [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] (L.sortBy (compare `on` fieldName) $(listE $ fmap return fields)) |] + [| TSInterfaceDeclaration $(TH.stringE $ nameBase name) [] (L.sortBy (compare `on` fieldName) $(listE $ fmap return fields)) Nothing |] getClosedTypeFamilyImage :: [TySynEqn] -> Q [Type] getClosedTypeFamilyImage eqns = do diff --git a/test/ClosedTypeFamilies.hs b/test/ClosedTypeFamilies.hs index d8381db..606a0cf 100644 --- a/test/ClosedTypeFamilies.hs +++ b/test/ClosedTypeFamilies.hs @@ -43,7 +43,7 @@ tests = describe "Closed type families" $ do TSField False "\"k8s_env\"" "\"k8s\"" Nothing , TSField False "\"single_node_env\"" "\"single\"" Nothing , TSField False "T" "void" Nothing - ] + ] Nothing , TSTypeAlternatives "ISimple" ["T extends keyof DeployEnvironment2"] ["DeployEnvironment2[T]"] , TSTypeAlternatives "Simple" ["T extends keyof DeployEnvironment2"] ["ISimple"] ]) @@ -56,7 +56,7 @@ tests = describe "Closed type families" $ do TSField False "_userUsername" "string" Nothing , TSField False "_userCreatedAt" "number" Nothing , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" Nothing - ] + ] Nothing ]) it [i|get the declarations recursively|] $ do @@ -65,12 +65,12 @@ tests = describe "Closed type families" $ do TSField False "\"k8s_env\"" "\"k8s\"" Nothing , TSField False "\"single_node_env\"" "\"single\"" Nothing , TSField False "T" "void" Nothing - ] + ] Nothing , TSInterfaceDeclaration "IUser" ["T extends keyof DeployEnvironment"] [ TSField False "_userUsername" "string" Nothing , TSField False "_userCreatedAt" "number" Nothing , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" Nothing - ] + ] Nothing , TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] ]) diff --git a/test/Generic.hs b/test/Generic.hs index 889ac8d..fa36a3e 100644 --- a/test/Generic.hs +++ b/test/Generic.hs @@ -29,8 +29,8 @@ tests :: SpecWith () tests = describe "Generic instances" $ do it [i|Complex makes the declaration and types correctly|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex String))) `shouldBe` [ - TSInterfaceDeclaration {interfaceName = "IProduct", interfaceGenericVariables = ["T"], interfaceMembers = [TSField False "tag" "\"Product\"" Nothing, TSField False "contents" "[number, T]" Nothing]} - ,TSInterfaceDeclaration {interfaceName = "IUnary", interfaceGenericVariables = ["T"], interfaceMembers = [TSField False "tag" "\"Unary\"" Nothing, TSField False "contents" "number" Nothing]} + TSInterfaceDeclaration "IProduct" ["T"] [TSField False "tag" "\"Product\"" Nothing, TSField False "contents" "[number, T]" Nothing] Nothing + ,TSInterfaceDeclaration "IUnary" ["T"] [TSField False "tag" "\"Unary\"" Nothing, TSField False "contents" "number" Nothing] Nothing ,TSTypeAlternatives {typeName = "Complex", typeGenericVariables = ["T"], alternativeTypes = ["IProduct","IUnary"]} ] @@ -42,24 +42,18 @@ tests = describe "Generic instances" $ do it [i|Complex3 makes the declaration and types correctly|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex3 String))) `shouldBe` [ - TSInterfaceDeclaration {interfaceName = "IProduct3", interfaceGenericVariables = ["T"], interfaceMembers = [ - TSField False "record3" "T[]" Nothing - ]} + TSInterfaceDeclaration "IProduct3" ["T"] [TSField False "record3" "T[]" Nothing] Nothing ,TSTypeAlternatives {typeName = "Complex3", typeGenericVariables = ["T"], alternativeTypes = ["IProduct3"]} ] (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex3 Int))) `shouldBe` [ - TSInterfaceDeclaration {interfaceName = "IProduct3", interfaceGenericVariables = ["T"], interfaceMembers = [ - TSField False "record3" "T[]" Nothing - ]} + TSInterfaceDeclaration "IProduct3" ["T"] [TSField False "record3" "T[]" Nothing] Nothing ,TSTypeAlternatives {typeName = "Complex3", typeGenericVariables = ["T"], alternativeTypes = ["IProduct3"]} ] it [i|Complex4 makes the declaration and types correctly|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex4 String))) `shouldBe` [ - TSInterfaceDeclaration {interfaceName = "IProduct4", interfaceGenericVariables = ["T"], interfaceMembers = [ - TSField False "record4" "{[k in string]?: T}" Nothing - ]} + TSInterfaceDeclaration "IProduct4" ["T"] [TSField False "record4" "{[k in string]?: T}" Nothing] Nothing ,TSTypeAlternatives {typeName = "Complex4", typeGenericVariables = ["T"], alternativeTypes = ["IProduct4"]} ] diff --git a/test/HigherKind.hs b/test/HigherKind.hs index b272ecc..00aeac5 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -37,7 +37,7 @@ tests = describe "Higher kinds" $ do it [i|makes the declaration and types correctly|] $ do (getTypeScriptDeclarations (Proxy :: Proxy (HigherKind T))) `shouldBe` ([ TSTypeAlternatives "HigherKind" ["T"] ["IHigherKind"], - TSInterfaceDeclaration "IHigherKind" ["T"] [TSField False "higherKindList" "T[]" Nothing] + TSInterfaceDeclaration "IHigherKind" ["T"] [TSField False "higherKindList" "T[]" Nothing] Nothing ]) (getTypeScriptType (Proxy :: Proxy (HigherKind Int))) `shouldBe` "HigherKind" @@ -47,7 +47,7 @@ tests = describe "Higher kinds" $ do (getTypeScriptDeclarations (Proxy :: Proxy Foo)) `shouldBe` ([ TSTypeAlternatives "Foo" [] ["IFoo"], TSInterfaceDeclaration "IFoo" [] [TSField False "fooString" "string" Nothing - , TSField False "fooHigherKindReference" "HigherKind" Nothing] + , TSField False "fooHigherKindReference" "HigherKind" Nothing] Nothing ]) it [i|works with an interface inside|] $ do @@ -61,7 +61,7 @@ tests = describe "Higher kinds" $ do (getTypeScriptDeclarations (Proxy :: Proxy (DoubleHigherKind T1 T2))) `shouldBe` ([ TSTypeAlternatives "DoubleHigherKind" ["T1","T2"] ["IDoubleHigherKind"], TSInterfaceDeclaration "IDoubleHigherKind" ["T1","T2"] [TSField False "someList" "T2[]" Nothing - , TSField False "higherKindThing" "HigherKind" Nothing] + , TSField False "higherKindThing" "HigherKind" Nothing] Nothing ]) (getTypeScriptType (Proxy :: Proxy (DoubleHigherKind Int String))) `shouldBe` "DoubleHigherKind" diff --git a/test/NoOmitNothingFields.hs b/test/NoOmitNothingFields.hs index 4f5da71..36c5d39 100644 --- a/test/NoOmitNothingFields.hs +++ b/test/NoOmitNothingFields.hs @@ -24,6 +24,7 @@ allTests = describe "NoOmitNothingFields" $ do interfaceName = "IOptional" , interfaceGenericVariables = [] , interfaceMembers = [TSField False "optionalInt" "number | null" Nothing] + , interfaceDoc = Nothing }] tests diff --git a/test/OmitNothingFields.hs b/test/OmitNothingFields.hs index 9993e3b..360f2a1 100644 --- a/test/OmitNothingFields.hs +++ b/test/OmitNothingFields.hs @@ -21,6 +21,7 @@ main = hspec $ describe "OmitNothingFields" $ do , interfaceMembers = [ TSField True "optionalInt" "number" Nothing ] + , interfaceDoc = Nothing }] tests diff --git a/test/OpenTypeFamilies.hs b/test/OpenTypeFamilies.hs index f668757..1b1aeaa 100644 --- a/test/OpenTypeFamilies.hs +++ b/test/OpenTypeFamilies.hs @@ -43,7 +43,7 @@ tests = describe "Open type families" $ do TSField False "\"single_node_env\"" "\"single\"" Nothing , TSField False "\"k8s_env\"" "\"k8s\"" Nothing , TSField False "T" "void" Nothing - ] + ] Nothing , TSTypeAlternatives "ISimple" ["T extends keyof DeployEnvironment2"] ["DeployEnvironment2[T]"] , TSTypeAlternatives "Simple" ["T extends keyof DeployEnvironment2"] ["ISimple"] ]) @@ -56,7 +56,7 @@ tests = describe "Open type families" $ do TSField False "_userUsername" "string" Nothing , TSField False "_userCreatedAt" "number" Nothing , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" Nothing - ] + ] Nothing ]) it [i|get the declarations recursively|] $ do @@ -65,12 +65,12 @@ tests = describe "Open type families" $ do TSField False "\"single_node_env\"" "\"single\"" Nothing , TSField False "\"k8s_env\"" "\"k8s\"" Nothing , TSField False "T" "void" Nothing - ] + ] Nothing , TSInterfaceDeclaration "IUser" ["T extends keyof DeployEnvironment"] [ TSField False "_userUsername" "string" Nothing , TSField False "_userCreatedAt" "number" Nothing , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" Nothing - ] + ] Nothing , TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] ]) From 01ec42f268ff0c2e4a48fb55cac5e78324777aed Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 1 Mar 2023 00:37:16 -0800 Subject: [PATCH 161/208] Fix warning in test/Util.hs --- test/Util.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Util.hs b/test/Util.hs index 180f657..88a088d 100644 --- a/test/Util.hs +++ b/test/Util.hs @@ -14,7 +14,8 @@ import System.Environment import System.Exit import System.FilePath import System.IO.Temp -import System.Process +import System.Process hiding (cwd) + npmInstallScript, yarnInstallScript, localTSC :: String npmInstallScript = "test/assets/npm_install.sh" From 8cba46725d7761ef7b061652b57290bf5b6317f7 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 1 Mar 2023 00:47:23 -0800 Subject: [PATCH 162/208] Add haddockModifier to ExtraTypeScriptOptions --- src/Data/Aeson/TypeScript/Formatting.hs | 21 +++++++-------------- src/Data/Aeson/TypeScript/TH.hs | 10 ++++++---- src/Data/Aeson/TypeScript/Types.hs | 11 ++++++++++- test/GetDoc.hs | 16 +++++++++------- 4 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index 2869f2c..7990790 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -31,14 +31,9 @@ formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVar toEnumName = T.replace "\"" "" formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceName genericVariables members maybeDoc) = - docPrefix <> [i|#{exportPrefix exportMode}interface #{modifiedInterfaceName}#{getGenericBrackets genericVariables} { + makeDocPrefix maybeDoc <> [i|#{exportPrefix exportMode}interface #{modifiedInterfaceName}#{getGenericBrackets genericVariables} { #{ls} }|] where - docPrefix = case maybeDoc of - Nothing -> "" - Just doc | '\n' `L.elem` doc -> "/* " <> (deleteLeadingWhitespace doc) <> " */\n" - Just doc -> "// " <> (deleteLeadingWhitespace doc) <> "\n" - ls = T.intercalate "\n" $ [indentTo numIndentSpaces (T.pack (formatTSField member <> ";")) | member <- members] modifiedInterfaceName = (\(li, name) -> li <> interfaceNameModifier name) . splitAt 1 $ interfaceName @@ -71,15 +66,13 @@ validateFormattingOptions options@FormattingOptions{..} decls isPlainSumType ds = (not . any isInterface $ ds) && length ds == 1 formatTSField :: TSField -> String -formatTSField (TSField optional name typ maybeDoc) = docPrefix <> [i|#{name}#{if optional then ("?" :: String) else ""}: #{typ}|] - where - docPrefix = case maybeDoc of - Nothing -> "" - Just doc | '\n' `L.elem` doc -> "/* " <> (deleteLeadingWhitespace doc) <> " */\n" - Just doc -> "// " <> (deleteLeadingWhitespace doc) <> "\n" +formatTSField (TSField optional name typ maybeDoc) = makeDocPrefix maybeDoc <> [i|#{name}#{if optional then ("?" :: String) else ""}: #{typ}|] -deleteLeadingWhitespace :: String -> String -deleteLeadingWhitespace = L.dropWhile (== ' ') +makeDocPrefix :: Maybe String -> String +makeDocPrefix maybeDoc = case maybeDoc of + Nothing -> "" + Just doc | '\n' `L.elem` doc -> "/* " <> doc <> " */\n" + Just doc -> "// " <> doc <> "\n" getGenericBrackets :: [String] -> String getGenericBrackets [] = "" diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 773e634..0207026 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -217,7 +217,7 @@ deriveTypeScript' options name extraOptions = do let typeVariablePreds :: [Pred] = [AppT (ConT ''TypeScript) x | x <- getDataTypeVars dti] -- Build the declarations - (types, (extraDeclsOrGenericInfosInitial <>) -> extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor options dti genericVariablesAndSuffixes) (datatypeCons dti) + (types, (extraDeclsOrGenericInfosInitial <>) -> extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor extraOptions options dti genericVariablesAndSuffixes) (datatypeCons dti) typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName (datatypeName dti)) $(genericVariablesListExpr True genericVariablesAndSuffixes) $(listE $ fmap return types)|] @@ -244,8 +244,8 @@ deriveTypeScript' options name extraOptions = do return (mconcat [x | ExtraTopLevelDecs x <- extraDeclsOrGenericInfos] <> inst) -- | Return a string to go in the top-level type declaration, plus an optional expression containing a declaration -handleConstructor :: Options -> DatatypeInfo -> [(Name, (Suffix, Var))] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp -handleConstructor options (DatatypeInfo {..}) genericVariables ci = do +handleConstructor :: ExtraTypeScriptOptions -> Options -> DatatypeInfo -> [(Name, (Suffix, Var))] -> ConstructorInfo -> WriterT [ExtraDeclOrGenericInfo] Q Exp +handleConstructor (ExtraTypeScriptOptions {..}) options (DatatypeInfo {..}) genericVariables ci = do if | (length datatypeCons == 1) && not (getTagSingleConstructors options) -> do writeSingleConstructorEncoding brackets <- lift $ getBracketsExpression False genericVariables @@ -320,7 +320,9 @@ handleConstructor options (DatatypeInfo {..}) genericVariables ci = do tryGetDoc :: Name -> Q Exp tryGetDoc n = do #if MIN_VERSION_template_haskell(2,18,0) - maybeDoc <- nothingOnFail $ getDoc (DeclDoc n) + maybeDoc <- nothingOnFail (getDoc (DeclDoc n)) >>= \case + Just (Just doc) -> return $ Just $ Just $ haddockModifier doc + x -> return x #else let maybeDoc = Nothing #endif diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index ec23d1c..4b98999 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -11,6 +11,7 @@ module Data.Aeson.TypeScript.Types where import qualified Data.Aeson as A import Data.Aeson.TypeScript.LegalName +import qualified Data.List as L import qualified Data.List.NonEmpty as NonEmpty import Data.Proxy import Data.String @@ -203,11 +204,19 @@ allStarConstructors'' = ["T1", "T2", "T3", "T4", "T5", "T6", "T7", "T8", "T9", " data ExtraTypeScriptOptions = ExtraTypeScriptOptions { typeFamiliesToMapToTypeScript :: [Name] + , keyType :: Maybe String + + -- | Function which is applied to all Haddocks we read in. + -- By default, just drops leading whitespace. + , haddockModifier :: String -> String } defaultExtraTypeScriptOptions :: ExtraTypeScriptOptions -defaultExtraTypeScriptOptions = ExtraTypeScriptOptions [] Nothing +defaultExtraTypeScriptOptions = ExtraTypeScriptOptions [] Nothing deleteLeadingWhitespace + where + deleteLeadingWhitespace :: String -> String + deleteLeadingWhitespace = L.dropWhile (== ' ') data ExtraDeclOrGenericInfo = ExtraDecl Exp | ExtraGeneric GenericInfo diff --git a/test/GetDoc.hs b/test/GetDoc.hs index 0f91f82..8ee8f80 100644 --- a/test/GetDoc.hs +++ b/test/GetDoc.hs @@ -10,11 +10,13 @@ import Prelude hiding (Double) import Test.Hspec --- | OneField is a type with a single field -data OneField = OneField { - -- | This is a simple string - simpleString :: String - } +-- | OneField type doc +data OneField = + -- | OneField constructor doc + OneField { + -- | This is a simple string + simpleString :: String + } $(deriveTypeScript A.defaultOptions ''OneField) tests :: SpecWith () @@ -23,8 +25,8 @@ tests = describe "getDoc tests" $ do (getTypeScriptDeclarations (Proxy :: Proxy OneField)) `shouldBe` ([ TSTypeAlternatives "OneField" [] ["IOneField"] , TSInterfaceDeclaration "IOneField" [] [ - TSField False "simpleString" "string" (Just " This is a simple string") - ] + TSField False "simpleString" "string" (Just "This is a simple string") + ] (Just "OneField constructor doc") ]) main :: IO () From 68be59f818391d7b5f1259698b96bdeb819ccd4c Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 1 Mar 2023 00:56:52 -0800 Subject: [PATCH 163/208] Working on TSTypeAlternatives docs and cleaning up extensions --- aeson-typescript.cabal | 10 +++++++ package.yaml | 18 ++++++++--- src/Data/Aeson/TypeScript/Formatting.hs | 12 ++++---- src/Data/Aeson/TypeScript/Instances.hs | 6 +--- src/Data/Aeson/TypeScript/Lookup.hs | 9 ------ src/Data/Aeson/TypeScript/Recursive.hs | 8 ----- src/Data/Aeson/TypeScript/TH.hs | 30 +++---------------- src/Data/Aeson/TypeScript/Transform.hs | 9 ------ src/Data/Aeson/TypeScript/TypeManipulation.hs | 9 ------ src/Data/Aeson/TypeScript/Types.hs | 7 ++--- src/Data/Aeson/TypeScript/Util.hs | 21 ++++++++----- test/Basic.hs | 6 ++-- 12 files changed, 55 insertions(+), 90 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 2837766..93c2d8b 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -48,6 +48,16 @@ library Paths_aeson_typescript hs-source-dirs: src + default-extensions: + LambdaCase + RecordWildCards + NamedFieldPuns + MultiWayIf + TupleSections + QuasiQuotes + OverloadedStrings + ViewPatterns + ScopedTypeVariables build-depends: aeson , base >=4.7 && <5 diff --git a/package.yaml b/package.yaml index 4626235..07a1080 100644 --- a/package.yaml +++ b/package.yaml @@ -39,11 +39,21 @@ dependencies: library: source-dirs: src + default-extensions: + - LambdaCase + - RecordWildCards + - NamedFieldPuns + - MultiWayIf + - TupleSections + - QuasiQuotes + - OverloadedStrings + - ViewPatterns + - ScopedTypeVariables exposed-modules: - - Data.Aeson.TypeScript.TH - - Data.Aeson.TypeScript.Internal - - Data.Aeson.TypeScript.Recursive - - Data.Aeson.TypeScript.LegalName + - Data.Aeson.TypeScript.TH + - Data.Aeson.TypeScript.Internal + - Data.Aeson.TypeScript.Recursive + - Data.Aeson.TypeScript.LegalName tests: aeson-typescript-tests: diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index 7990790..c099c62 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -18,12 +18,14 @@ formatTSDeclarations = formatTSDeclarations' defaultFormattingOptions -- | Format a single TypeScript declaration. This version accepts a FormattingOptions object in case you want more control over the output. formatTSDeclaration :: FormattingOptions -> TSDeclaration -> String -formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVariables names) = - case typeAlternativesFormat of - Enum -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnum} }|] - EnumWithType -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnumWithType} }#{enumType}|] - TypeAlias -> [i|#{exportPrefix exportMode}type #{typeNameModifier name}#{getGenericBrackets genericVariables} = #{alternatives};|] +formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVariables names maybeDoc) = + makeDocPrefix maybeDoc <> mainDeclaration where + mainDeclaration = case typeAlternativesFormat of + Enum -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnum} }|] + EnumWithType -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnumWithType} }#{enumType}|] + TypeAlias -> [i|#{exportPrefix exportMode}type #{typeNameModifier name}#{getGenericBrackets genericVariables} = #{alternatives};|] + 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] diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index 6d50c5b..3833dcc 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -1,8 +1,4 @@ -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverlappingInstances #-} @@ -89,7 +85,7 @@ instance {-# OVERLAPPING #-} TypeScript [Char] where instance (TypeScript a, TypeScript b) => TypeScript (Either a b) where getTypeScriptType _ = [i|Either<#{getTypeScriptType (Proxy :: Proxy a)}, #{getTypeScriptType (Proxy :: Proxy b)}>|] - getTypeScriptDeclarations _ = [TSTypeAlternatives "Either" ["T1", "T2"] ["Left", "Right"] + getTypeScriptDeclarations _ = [TSTypeAlternatives "Either" ["T1", "T2"] ["Left", "Right"] Nothing , TSInterfaceDeclaration "Left" ["T"] [TSField False "Left" "T" Nothing] Nothing , TSInterfaceDeclaration "Right" ["T"] [TSField False "Right" "T" Nothing] Nothing ] diff --git a/src/Data/Aeson/TypeScript/Lookup.hs b/src/Data/Aeson/TypeScript/Lookup.hs index ef0e9a8..82544fe 100644 --- a/src/Data/Aeson/TypeScript/Lookup.hs +++ b/src/Data/Aeson/TypeScript/Lookup.hs @@ -1,17 +1,8 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE PolyKinds #-} -{-# LANGUAGE LambdaCase #-} module Data.Aeson.TypeScript.Lookup where diff --git a/src/Data/Aeson/TypeScript/Recursive.hs b/src/Data/Aeson/TypeScript/Recursive.hs index e525bf6..b0c7c12 100755 --- a/src/Data/Aeson/TypeScript/Recursive.hs +++ b/src/Data/Aeson/TypeScript/Recursive.hs @@ -1,14 +1,6 @@ -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE ViewPatterns #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE PolyKinds #-} module Data.Aeson.TypeScript.Recursive ( diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 0207026..c52c511 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -1,17 +1,8 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE PolyKinds #-} -{-# LANGUAGE LambdaCase #-} {-| Module: Data.Aeson.TypeScript.TH @@ -220,7 +211,8 @@ deriveTypeScript' options name extraOptions = do (types, (extraDeclsOrGenericInfosInitial <>) -> extraDeclsOrGenericInfos) <- runWriterT $ mapM (handleConstructor extraOptions options dti genericVariablesAndSuffixes) (datatypeCons dti) typeDeclaration <- [|TSTypeAlternatives $(TH.stringE $ getTypeName (datatypeName dti)) $(genericVariablesListExpr True genericVariablesAndSuffixes) - $(listE $ fmap return types)|] + $(listE $ fmap return types) + $(tryGetDoc (haddockModifier extraOptions) (datatypeName dti))|] declarationsFunctionBody <- [| $(return typeDeclaration) : $(listE (fmap return [x | ExtraDecl x <- extraDeclsOrGenericInfos])) |] @@ -315,21 +307,7 @@ handleConstructor (ExtraTypeScriptOptions {..}) options (DatatypeInfo {..}) gene assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) $(genericVariablesListExpr True genericVariables) $(return members) - $(tryGetDoc (constructorName ci))|] - - tryGetDoc :: Name -> Q Exp - tryGetDoc n = do -#if MIN_VERSION_template_haskell(2,18,0) - maybeDoc <- nothingOnFail (getDoc (DeclDoc n)) >>= \case - Just (Just doc) -> return $ Just $ Just $ haddockModifier doc - x -> return x -#else - let maybeDoc = Nothing -#endif - - case maybeDoc of - Just (Just doc) -> [|Just $(TH.stringE doc)|] - _ -> [|Nothing|] + $(tryGetDoc haddockModifier (constructorName ci))|] getTSFields :: WriterT [ExtraDeclOrGenericInfo] Q [Exp] getTSFields = forM (namesAndTypes options genericVariables ci) $ \(name, nameString, typ) -> do @@ -338,7 +316,7 @@ handleConstructor (ExtraTypeScriptOptions {..}) options (DatatypeInfo {..}) gene ( , ) <$> [|$(getTypeAsStringExp t) <> " | null"|] <*> getOptionalAsBoolExp t _ -> ( , ) <$> getTypeAsStringExp typ <*> getOptionalAsBoolExp typ - lift [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) $(tryGetDoc name) |] + lift [| TSField $(return optAsBool) $(TH.stringE nameString) $(return fieldTyp) $(tryGetDoc haddockModifier name) |] isSingleRecordConstructor (constructorVariant -> RecordConstructor [_]) = True isSingleRecordConstructor _ = False diff --git a/src/Data/Aeson/TypeScript/Transform.hs b/src/Data/Aeson/TypeScript/Transform.hs index 259267a..f5494e1 100644 --- a/src/Data/Aeson/TypeScript/Transform.hs +++ b/src/Data/Aeson/TypeScript/Transform.hs @@ -1,17 +1,8 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE PolyKinds #-} -{-# LANGUAGE LambdaCase #-} module Data.Aeson.TypeScript.Transform ( diff --git a/src/Data/Aeson/TypeScript/TypeManipulation.hs b/src/Data/Aeson/TypeScript/TypeManipulation.hs index dc36c11..9462d49 100644 --- a/src/Data/Aeson/TypeScript/TypeManipulation.hs +++ b/src/Data/Aeson/TypeScript/TypeManipulation.hs @@ -1,17 +1,8 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE PolyKinds #-} -{-# LANGUAGE LambdaCase #-} module Data.Aeson.TypeScript.TypeManipulation ( searchForConstraints diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 4b98999..58f5317 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -1,8 +1,4 @@ -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE StandaloneDeriving #-} @@ -91,7 +87,8 @@ data TSDeclaration = TSInterfaceDeclaration { interfaceName :: String , interfaceDoc :: Maybe String } | TSTypeAlternatives { typeName :: String , typeGenericVariables :: [String] - , alternativeTypes :: [String]} + , alternativeTypes :: [String] + , typeDoc :: Maybe String } | TSRawDeclaration { text :: String } deriving (Show, Eq, Ord) diff --git a/src/Data/Aeson/TypeScript/Util.hs b/src/Data/Aeson/TypeScript/Util.hs index 74b2c29..cedf0bf 100644 --- a/src/Data/Aeson/TypeScript/Util.hs +++ b/src/Data/Aeson/TypeScript/Util.hs @@ -1,14 +1,7 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE PolyKinds #-} module Data.Aeson.TypeScript.Util where @@ -227,3 +220,17 @@ isStarType _ = Nothing nothingOnFail :: Q a -> Q (Maybe a) nothingOnFail action = recover (return Nothing) (Just <$> action) + +tryGetDoc :: (String -> String) -> Name -> Q Exp +tryGetDoc haddockModifier n = do +#if MIN_VERSION_template_haskell(2,18,0) + maybeDoc <- nothingOnFail (getDoc (DeclDoc n)) >>= \case + Just (Just doc) -> return $ Just $ Just $ haddockModifier doc + x -> return x +#else + let maybeDoc = Nothing +#endif + + case maybeDoc of + Just (Just doc) -> [|Just $(TH.stringE doc)|] + _ -> [|Nothing|] diff --git a/test/Basic.hs b/test/Basic.hs index eec08ea..18c226d 100644 --- a/test/Basic.hs +++ b/test/Basic.hs @@ -22,13 +22,13 @@ tests = describe "Basic tests" $ do describe "tagSingleConstructors and constructorTagModifier" $ do it [i|Works with a normal unit|] $ do (getTypeScriptDeclarations (Proxy :: Proxy Unit1)) `shouldBe` ([ - TSTypeAlternatives "Unit1" [] ["IUnit1"] - , TSTypeAlternatives "IUnit1" [] ["void[]"] + TSTypeAlternatives "Unit1" [] ["IUnit1"] Nothing + , TSTypeAlternatives "IUnit1" [] ["void[]"] Nothing ]) it [i|Works with a unit with constructorTagModifier|] $ do (getTypeScriptDeclarations (Proxy :: Proxy Unit2)) `shouldBe` ([ - TSTypeAlternatives "Unit2" [] ["\"foo\""] + TSTypeAlternatives "Unit2" [] ["\"foo\""] Nothing ]) From e75d17a220bc4e0fa54f6e00dbf9b9a0377d85fd Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 1 Mar 2023 01:10:37 -0800 Subject: [PATCH 164/208] Consolidate extensions and update TSTypeAlternatives in tests --- aeson-typescript.cabal | 21 +++++++++++++-------- package.yaml | 27 ++++++++++++--------------- src/Data/Aeson/TypeScript/TH.hs | 6 ++++-- test/ClosedTypeFamilies.hs | 8 ++++---- test/Generic.hs | 12 ++++++------ test/GetDoc.hs | 2 +- test/HigherKind.hs | 10 +++++----- test/NoOmitNothingFields.hs | 13 ++----------- test/OpenTypeFamilies.hs | 8 ++++---- test/UnwrapUnaryRecords.hs | 4 ++-- 10 files changed, 53 insertions(+), 58 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 93c2d8b..645ac1a 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -50,14 +50,14 @@ library src default-extensions: LambdaCase - RecordWildCards - NamedFieldPuns MultiWayIf - TupleSections - QuasiQuotes + NamedFieldPuns OverloadedStrings - ViewPatterns + QuasiQuotes + RecordWildCards ScopedTypeVariables + TupleSections + ViewPatterns build-depends: aeson , base >=4.7 && <5 @@ -113,14 +113,19 @@ test-suite aeson-typescript-tests test src default-extensions: + LambdaCase + MultiWayIf + NamedFieldPuns OverloadedStrings + QuasiQuotes + RecordWildCards ScopedTypeVariables - KindSignatures + TupleSections + ViewPatterns FlexibleContexts - QuasiQuotes + KindSignatures TemplateHaskell TypeFamilies - LambdaCase ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -haddock -fno-warn-unused-top-binds -fno-warn-orphans build-depends: aeson diff --git a/package.yaml b/package.yaml index 07a1080..c8e44f4 100644 --- a/package.yaml +++ b/package.yaml @@ -37,18 +37,19 @@ dependencies: - transformers - unordered-containers +default-extensions: +- LambdaCase +- MultiWayIf +- NamedFieldPuns +- OverloadedStrings +- QuasiQuotes +- RecordWildCards +- ScopedTypeVariables +- TupleSections +- ViewPatterns + library: source-dirs: src - default-extensions: - - LambdaCase - - RecordWildCards - - NamedFieldPuns - - MultiWayIf - - TupleSections - - QuasiQuotes - - OverloadedStrings - - ViewPatterns - - ScopedTypeVariables exposed-modules: - Data.Aeson.TypeScript.TH - Data.Aeson.TypeScript.Internal @@ -70,14 +71,10 @@ tests: - -fno-warn-unused-top-binds - -fno-warn-orphans default-extensions: - - OverloadedStrings - - ScopedTypeVariables - - KindSignatures - FlexibleContexts - - QuasiQuotes + - KindSignatures - TemplateHaskell - TypeFamilies - - LambdaCase dependencies: - aeson-typescript - bytestring diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index c52c511..7cd0e49 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -287,7 +287,8 @@ handleConstructor (ExtraTypeScriptOptions {..}) options (DatatypeInfo {..}) gene _ -> getTypeAsStringExp typ alternatives <- lift [|TSTypeAlternatives $(TH.stringE interfaceName) $(genericVariablesListExpr True genericVariables) - [$(return stringExp)]|] + [$(return stringExp)] + $(tryGetDoc haddockModifier (constructorName ci))|] tell [ExtraDecl alternatives] #endif @@ -302,7 +303,8 @@ handleConstructor (ExtraTypeScriptOptions {..}) options (DatatypeInfo {..}) gene tupleEncoding = lift [|TSTypeAlternatives $(TH.stringE interfaceName) $(genericVariablesListExpr True genericVariables) - [getTypeScriptType (Proxy :: Proxy $(return (contentsTupleTypeSubstituted genericVariables ci)))]|] + [getTypeScriptType (Proxy :: Proxy $(return (contentsTupleTypeSubstituted genericVariables ci)))] + $(tryGetDoc haddockModifier (constructorName ci))|] assembleInterfaceDeclaration members = [|TSInterfaceDeclaration $(TH.stringE interfaceName) $(genericVariablesListExpr True genericVariables) diff --git a/test/ClosedTypeFamilies.hs b/test/ClosedTypeFamilies.hs index 606a0cf..0e75647 100644 --- a/test/ClosedTypeFamilies.hs +++ b/test/ClosedTypeFamilies.hs @@ -44,14 +44,14 @@ tests = describe "Closed type families" $ do , TSField False "\"single_node_env\"" "\"single\"" Nothing , TSField False "T" "void" Nothing ] Nothing - , TSTypeAlternatives "ISimple" ["T extends keyof DeployEnvironment2"] ["DeployEnvironment2[T]"] - , TSTypeAlternatives "Simple" ["T extends keyof DeployEnvironment2"] ["ISimple"] + , TSTypeAlternatives "ISimple" ["T extends keyof DeployEnvironment2"] ["DeployEnvironment2[T]"] Nothing + , TSTypeAlternatives "Simple" ["T extends keyof DeployEnvironment2"] ["ISimple"] Nothing ]) describe "Complicated Beam-like user type" $ do it [i|makes the declaration and types correctly|] $ do (getTypeScriptDeclarations (Proxy :: Proxy (UserT T Identity))) `shouldBe` ([ - TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] + TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] Nothing , TSInterfaceDeclaration "IUser" ["T extends keyof DeployEnvironment"] [ TSField False "_userUsername" "string" Nothing , TSField False "_userCreatedAt" "number" Nothing @@ -71,7 +71,7 @@ tests = describe "Closed type families" $ do , TSField False "_userCreatedAt" "number" Nothing , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" Nothing ] Nothing - , TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] + , TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] Nothing ]) main :: IO () diff --git a/test/Generic.hs b/test/Generic.hs index fa36a3e..68f92fb 100644 --- a/test/Generic.hs +++ b/test/Generic.hs @@ -31,30 +31,30 @@ tests = describe "Generic instances" $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex String))) `shouldBe` [ TSInterfaceDeclaration "IProduct" ["T"] [TSField False "tag" "\"Product\"" Nothing, TSField False "contents" "[number, T]" Nothing] Nothing ,TSInterfaceDeclaration "IUnary" ["T"] [TSField False "tag" "\"Unary\"" Nothing, TSField False "contents" "number" Nothing] Nothing - ,TSTypeAlternatives {typeName = "Complex", typeGenericVariables = ["T"], alternativeTypes = ["IProduct","IUnary"]} + ,TSTypeAlternatives "Complex" ["T"] ["IProduct","IUnary"] Nothing ] it [i|Complex2 makes the declaration and types correctly|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex2 String))) `shouldBe` [ - TSTypeAlternatives {typeName = "Complex2", typeGenericVariables = ["T"], alternativeTypes = ["IProduct2"]} - ,TSTypeAlternatives {typeName = "IProduct2", typeGenericVariables = ["T"], alternativeTypes = ["[number, T]"]} + TSTypeAlternatives "Complex2" ["T"] ["IProduct2"] Nothing + ,TSTypeAlternatives "IProduct2" ["T"] ["[number, T]"] Nothing ] it [i|Complex3 makes the declaration and types correctly|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex3 String))) `shouldBe` [ TSInterfaceDeclaration "IProduct3" ["T"] [TSField False "record3" "T[]" Nothing] Nothing - ,TSTypeAlternatives {typeName = "Complex3", typeGenericVariables = ["T"], alternativeTypes = ["IProduct3"]} + ,TSTypeAlternatives "Complex3" ["T"] ["IProduct3"] Nothing ] (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex3 Int))) `shouldBe` [ TSInterfaceDeclaration "IProduct3" ["T"] [TSField False "record3" "T[]" Nothing] Nothing - ,TSTypeAlternatives {typeName = "Complex3", typeGenericVariables = ["T"], alternativeTypes = ["IProduct3"]} + ,TSTypeAlternatives "Complex3" ["T"] ["IProduct3"] Nothing ] it [i|Complex4 makes the declaration and types correctly|] $ do (getTypeScriptDeclarationsRecursively (Proxy :: Proxy (Complex4 String))) `shouldBe` [ TSInterfaceDeclaration "IProduct4" ["T"] [TSField False "record4" "{[k in string]?: T}" Nothing] Nothing - ,TSTypeAlternatives {typeName = "Complex4", typeGenericVariables = ["T"], alternativeTypes = ["IProduct4"]} + ,TSTypeAlternatives "Complex4" ["T"] ["IProduct4"] Nothing ] main :: IO () diff --git a/test/GetDoc.hs b/test/GetDoc.hs index 8ee8f80..73f25b7 100644 --- a/test/GetDoc.hs +++ b/test/GetDoc.hs @@ -23,7 +23,7 @@ tests :: SpecWith () tests = describe "getDoc tests" $ do it [i|Works with a simple record type|] $ do (getTypeScriptDeclarations (Proxy :: Proxy OneField)) `shouldBe` ([ - TSTypeAlternatives "OneField" [] ["IOneField"] + TSTypeAlternatives "OneField" [] ["IOneField"] (Just "OneField type doc") , TSInterfaceDeclaration "IOneField" [] [ TSField False "simpleString" "string" (Just "This is a simple string") ] (Just "OneField constructor doc") diff --git a/test/HigherKind.hs b/test/HigherKind.hs index 00aeac5..3587785 100644 --- a/test/HigherKind.hs +++ b/test/HigherKind.hs @@ -36,7 +36,7 @@ tests = describe "Higher kinds" $ do describe "Kind * -> *" $ do it [i|makes the declaration and types correctly|] $ do (getTypeScriptDeclarations (Proxy :: Proxy (HigherKind T))) `shouldBe` ([ - TSTypeAlternatives "HigherKind" ["T"] ["IHigherKind"], + TSTypeAlternatives "HigherKind" ["T"] ["IHigherKind"] Nothing, TSInterfaceDeclaration "IHigherKind" ["T"] [TSField False "higherKindList" "T[]" Nothing] Nothing ]) @@ -45,21 +45,21 @@ tests = describe "Higher kinds" $ do it [i|works when referenced in another type|] $ do (getTypeScriptDeclarations (Proxy :: Proxy Foo)) `shouldBe` ([ - TSTypeAlternatives "Foo" [] ["IFoo"], + TSTypeAlternatives "Foo" [] ["IFoo"] Nothing, TSInterfaceDeclaration "IFoo" [] [TSField False "fooString" "string" Nothing , TSField False "fooHigherKindReference" "HigherKind" Nothing] Nothing ]) it [i|works with an interface inside|] $ do (getTypeScriptDeclarations (Proxy :: Proxy (HigherKindWithUnary T))) `shouldBe` ([ - TSTypeAlternatives "HigherKindWithUnary" ["T"] ["IUnary"], - TSTypeAlternatives "IUnary" ["T"] ["number"] + TSTypeAlternatives "HigherKindWithUnary" ["T"] ["IUnary"] Nothing, + TSTypeAlternatives "IUnary" ["T"] ["number"] Nothing ]) describe "Kind * -> * -> *" $ do it [i|makes the declaration and type correctly|] $ do (getTypeScriptDeclarations (Proxy :: Proxy (DoubleHigherKind T1 T2))) `shouldBe` ([ - TSTypeAlternatives "DoubleHigherKind" ["T1","T2"] ["IDoubleHigherKind"], + TSTypeAlternatives "DoubleHigherKind" ["T1","T2"] ["IDoubleHigherKind"] Nothing, TSInterfaceDeclaration "IDoubleHigherKind" ["T1","T2"] [TSField False "someList" "T2[]" Nothing , TSField False "higherKindThing" "HigherKind" Nothing] Nothing ]) diff --git a/test/NoOmitNothingFields.hs b/test/NoOmitNothingFields.hs index 36c5d39..148e822 100644 --- a/test/NoOmitNothingFields.hs +++ b/test/NoOmitNothingFields.hs @@ -15,16 +15,7 @@ allTests = describe "NoOmitNothingFields" $ do it "encodes as expected" $ do let decls = getTypeScriptDeclarations (Proxy :: Proxy Optional) - decls `shouldBe` [TSTypeAlternatives { - typeName = "Optional" - , typeGenericVariables = [] - , alternativeTypes = ["IOptional"] - } - , TSInterfaceDeclaration { - interfaceName = "IOptional" - , interfaceGenericVariables = [] - , interfaceMembers = [TSField False "optionalInt" "number | null" Nothing] - , interfaceDoc = Nothing - }] + decls `shouldBe` [TSTypeAlternatives "Optional" [] ["IOptional"] Nothing + , TSInterfaceDeclaration "IOptional" [] [TSField False "optionalInt" "number | null" Nothing] Nothing] tests diff --git a/test/OpenTypeFamilies.hs b/test/OpenTypeFamilies.hs index 1b1aeaa..52a148e 100644 --- a/test/OpenTypeFamilies.hs +++ b/test/OpenTypeFamilies.hs @@ -44,14 +44,14 @@ tests = describe "Open type families" $ do , TSField False "\"k8s_env\"" "\"k8s\"" Nothing , TSField False "T" "void" Nothing ] Nothing - , TSTypeAlternatives "ISimple" ["T extends keyof DeployEnvironment2"] ["DeployEnvironment2[T]"] - , TSTypeAlternatives "Simple" ["T extends keyof DeployEnvironment2"] ["ISimple"] + , TSTypeAlternatives "ISimple" ["T extends keyof DeployEnvironment2"] ["DeployEnvironment2[T]"] Nothing + , TSTypeAlternatives "Simple" ["T extends keyof DeployEnvironment2"] ["ISimple"] Nothing ]) describe "Complicated Beam-like user type" $ do it [i|makes the declaration and types correctly|] $ do (getTypeScriptDeclarations (Proxy :: Proxy (UserT T Identity))) `shouldBe` ([ - TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] + TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] Nothing , TSInterfaceDeclaration "IUser" ["T extends keyof DeployEnvironment"] [ TSField False "_userUsername" "string" Nothing , TSField False "_userCreatedAt" "number" Nothing @@ -71,7 +71,7 @@ tests = describe "Open type families" $ do , TSField False "_userCreatedAt" "number" Nothing , TSField False "_userDeployEnvironment" "DeployEnvironment[T]" Nothing ] Nothing - , TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] + , TSTypeAlternatives "UserT" ["T extends keyof DeployEnvironment"] ["IUser"] Nothing ]) main :: IO () diff --git a/test/UnwrapUnaryRecords.hs b/test/UnwrapUnaryRecords.hs index 99c702d..1a75fb2 100644 --- a/test/UnwrapUnaryRecords.hs +++ b/test/UnwrapUnaryRecords.hs @@ -19,8 +19,8 @@ allTests = describe "UnwrapUnaryRecords" $ do let decls = getTypeScriptDeclarations (Proxy :: Proxy OneField) decls `shouldBe` [ - TSTypeAlternatives {typeName = "OneField", typeGenericVariables = [], alternativeTypes = ["IOneField"]} - ,TSTypeAlternatives {typeName = "IOneField", typeGenericVariables = [], alternativeTypes = ["string"]} + TSTypeAlternatives "OneField" [] ["IOneField"] Nothing + ,TSTypeAlternatives "IOneField" [] ["string"] Nothing ] tests From b8149881c317ef95ca702747fc74ef5affddff83 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 1 Mar 2023 01:17:33 -0800 Subject: [PATCH 165/208] Change default haddockModifier to stripStart on each line --- src/Data/Aeson/TypeScript/Types.hs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Types.hs b/src/Data/Aeson/TypeScript/Types.hs index 58f5317..a96f635 100644 --- a/src/Data/Aeson/TypeScript/Types.hs +++ b/src/Data/Aeson/TypeScript/Types.hs @@ -7,10 +7,11 @@ module Data.Aeson.TypeScript.Types where import qualified Data.Aeson as A import Data.Aeson.TypeScript.LegalName -import qualified Data.List as L +import Data.Function ((&)) import qualified Data.List.NonEmpty as NonEmpty import Data.Proxy import Data.String +import qualified Data.Text as T import Data.Typeable import Language.Haskell.TH @@ -205,15 +206,20 @@ data ExtraTypeScriptOptions = ExtraTypeScriptOptions { , keyType :: Maybe String -- | Function which is applied to all Haddocks we read in. - -- By default, just drops leading whitespace. + -- By default, just drops leading whitespace from each line. , haddockModifier :: String -> String } defaultExtraTypeScriptOptions :: ExtraTypeScriptOptions -defaultExtraTypeScriptOptions = ExtraTypeScriptOptions [] Nothing deleteLeadingWhitespace +defaultExtraTypeScriptOptions = ExtraTypeScriptOptions [] Nothing stripStartEachLine where - deleteLeadingWhitespace :: String -> String - deleteLeadingWhitespace = L.dropWhile (== ' ') + stripStartEachLine :: String -> String + stripStartEachLine s = s + & T.pack + & T.splitOn "\n" + & fmap T.stripStart + & T.intercalate "\n" + & T.unpack data ExtraDeclOrGenericInfo = ExtraDecl Exp | ExtraGeneric GenericInfo From bab4047f5c551e4f4b48cccf46d6c9ccbf13421d Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 1 Mar 2023 01:17:52 -0800 Subject: [PATCH 166/208] Format haddocks with // comments always --- src/Data/Aeson/TypeScript/Formatting.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index c099c62..990971e 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -3,6 +3,7 @@ module Data.Aeson.TypeScript.Formatting where import Data.Aeson.TypeScript.Types +import Data.Function ((&)) import qualified Data.List as L import Data.String.Interpolate import qualified Data.Text as T @@ -73,8 +74,10 @@ formatTSField (TSField optional name typ maybeDoc) = makeDocPrefix maybeDoc <> [ makeDocPrefix :: Maybe String -> String makeDocPrefix maybeDoc = case maybeDoc of Nothing -> "" - Just doc | '\n' `L.elem` doc -> "/* " <> doc <> " */\n" - Just doc -> "// " <> doc <> "\n" + Just (T.pack -> text) -> ["// " <> line | line <- T.splitOn "\n" text] + & T.intercalate "\n" + & (<> "\n") + & T.unpack getGenericBrackets :: [String] -> String getGenericBrackets [] = "" From 4c587754358dad3463ae5e5dd46b4b334408348d Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 1 Mar 2023 03:03:43 -0800 Subject: [PATCH 167/208] Implement @no-emit-typescript annotation address #31 --- CHANGELOG.md | 1 + src/Data/Aeson/TypeScript/Formatting.hs | 39 +++++++++++- test/Formatting.hs | 80 +++++++++++++++++-------- 3 files changed, 91 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0610c1..e817f8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * [#35](https://github.com/codedownio/aeson-typescript/pull/35) * Add `Data.Aeson.TypeScript.LegalName` module for checking whether a name is a legal JavaScript name or not. * The `defaultFormatter` will `error` if the name contains illegal characters. +* Add support for @no-emit-typescript in Haddocks for constructors and record fields (requires GHC >= 9.2) ## 0.4.2.0 diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index 990971e..b4e4c77 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -1,10 +1,11 @@ -{-# LANGUAGE QuasiQuotes, OverloadedStrings, TemplateHaskell, RecordWildCards, ScopedTypeVariables, NamedFieldPuns, CPP #-} +{-# LANGUAGE CPP #-} module Data.Aeson.TypeScript.Formatting where import Data.Aeson.TypeScript.Types import Data.Function ((&)) import qualified Data.List as L +import Data.Maybe import Data.String.Interpolate import qualified Data.Text as T @@ -33,7 +34,7 @@ formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVar enumType = [i|\n\ntype #{name} = keyof typeof #{typeNameModifier name};|] :: T.Text toEnumName = T.replace "\"" "" -formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceName genericVariables members maybeDoc) = +formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceName genericVariables (filter (not . isNoEmitTypeScriptField) -> members) maybeDoc) = makeDocPrefix maybeDoc <> [i|#{exportPrefix exportMode}interface #{modifiedInterfaceName}#{getGenericBrackets genericVariables} { #{ls} }|] where @@ -52,7 +53,27 @@ exportPrefix ExportNone = "" -- | Format a list of TypeScript declarations into a string, suitable for putting directly into a @.d.ts@ file. formatTSDeclarations' :: FormattingOptions -> [TSDeclaration] -> String -formatTSDeclarations' options declarations = T.unpack $ T.intercalate "\n\n" (fmap (T.pack . formatTSDeclaration (validateFormattingOptions options declarations)) declarations) +formatTSDeclarations' options allDeclarations = + declarations & fmap (T.pack . formatTSDeclaration (validateFormattingOptions options declarations)) + & 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 + + removeReferencesToRemovedNames :: [String] -> TSDeclaration -> TSDeclaration + removeReferencesToRemovedNames removedNames decl@(TSTypeAlternatives {..}) = decl { alternativeTypes = [x | x <- alternativeTypes, not (x `L.elem` removedNames)] } + removeReferencesToRemovedNames _ x = x + + declarations = allDeclarations + & filter (not . isNoEmitTypeScriptDeclaration) + & fmap (removeReferencesToRemovedNames removedDeclarationNames) validateFormattingOptions :: FormattingOptions -> [TSDeclaration] -> FormattingOptions validateFormattingOptions options@FormattingOptions{..} decls @@ -82,3 +103,15 @@ makeDocPrefix maybeDoc = case maybeDoc of getGenericBrackets :: [String] -> String getGenericBrackets [] = "" getGenericBrackets xs = [i|<#{T.intercalate ", " (fmap T.pack xs)}>|] + +-- * Support for @no-emit-typescript + +noEmitTypeScriptAnnotation :: String +noEmitTypeScriptAnnotation = "@no-emit-typescript" + +isNoEmitTypeScriptField (TSField {fieldDoc=(Just doc)}) = noEmitTypeScriptAnnotation `L.isInfixOf` doc +isNoEmitTypeScriptField _ = False + +isNoEmitTypeScriptDeclaration (TSInterfaceDeclaration {interfaceDoc=(Just doc)}) = noEmitTypeScriptAnnotation `L.isInfixOf` doc +isNoEmitTypeScriptDeclaration (TSTypeAlternatives {typeDoc=(Just doc)}) = noEmitTypeScriptAnnotation `L.isInfixOf` doc +isNoEmitTypeScriptDeclaration _ = False diff --git a/test/Formatting.hs b/test/Formatting.hs index c4fec32..f9a4906 100644 --- a/test/Formatting.hs +++ b/test/Formatting.hs @@ -19,30 +19,58 @@ $(deriveTypeScript defaultOptions ''PrimeInType') data PrimeInConstr = PrimeInConstr' $(deriveTypeScript defaultOptions ''PrimeInConstr) +data FooBar = + Foo { + -- | @no-emit-typescript + recordString :: String + , recordInt :: Int + } + | + -- | @no-emit-typescript + Bar { + barInt :: Int + } +$(deriveTypeScript defaultOptions ''FooBar) + +data NormalConstructors = + -- | @no-emit-typescript + Con1 String + | Con2 Int +$(deriveTypeScript defaultOptions ''NormalConstructors) + tests :: Spec -tests = do - describe "Formatting" $ do - describe "when given a Sum Type" $ do - describe "and the TypeAlias format option is set" $ - it "should generate a TS string literal type" $ - formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @D Proxy) `shouldBe` - [i|type D = "S" | "F";|] - describe "and the Enum format option is set" $ - it "should generate a TS Enum" $ - formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @D Proxy) `shouldBe` - [i|enum D { S, F }|] - describe "and the EnumWithType format option is set" $ - 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;|] - describe "when the name has an apostrophe" $ do - describe "in the type" $ do - it "throws an error" $ do - evaluate (formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @PrimeInType' Proxy)) - `shouldThrow` - anyErrorCall - describe "in the constructor" $ do - it "throws an error" $ do - evaluate (formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @PrimeInConstr Proxy)) - `shouldThrow` - anyErrorCall +tests = describe "Formatting" $ do + describe "when given a Sum Type" $ do + describe "and the TypeAlias format option is set" $ + it "should generate a TS string literal type" $ + formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @D Proxy) `shouldBe` + [i|type D = "S" | "F";|] + + describe "and the Enum format option is set" $ + it "should generate a TS Enum" $ + formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @D Proxy) `shouldBe` + [i|enum D { S, F }|] + + describe "and the EnumWithType format option is set" $ + 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;|] + + describe "when the name has an apostrophe" $ do + describe "in the type" $ do + it "throws an error" $ do + evaluate (formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @PrimeInType' Proxy)) `shouldThrow` anyErrorCall + + describe "in the constructor" $ do + it "throws an error" $ do + evaluate (formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @PrimeInConstr Proxy)) `shouldThrow` anyErrorCall + + describe "when @no-emit-typescript is present" $ do + it [i|works on records and constructors of record types|] $ do + formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @FooBar Proxy) `shouldBe` [i|type FooBar = IFoo;\n\ninterface IFoo {\n tag: "Foo";\n recordInt: number;\n}|] + + it [i|works on normal constructors|] $ do + formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @NormalConstructors Proxy) `shouldBe` [i|type NormalConstructors = ICon2;\n\ninterface ICon2 {\n tag: "Con2";\n contents: number;\n}|] + +main :: IO () +main = hspec tests From d6a3addf220d51dcc1199744869beda2b8bb2aa1 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 1 Mar 2023 03:17:19 -0800 Subject: [PATCH 168/208] Check for compatible template-haskell when testing @no-emit-typescript --- test/Formatting.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/Formatting.hs b/test/Formatting.hs index f9a4906..d035c4e 100644 --- a/test/Formatting.hs +++ b/test/Formatting.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE TypeApplications #-} module Formatting (tests) where @@ -65,12 +66,14 @@ tests = describe "Formatting" $ do it "throws an error" $ do evaluate (formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @PrimeInConstr Proxy)) `shouldThrow` anyErrorCall +#if MIN_VERSION_template_haskell(2,18,0) describe "when @no-emit-typescript is present" $ do it [i|works on records and constructors of record types|] $ do formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @FooBar Proxy) `shouldBe` [i|type FooBar = IFoo;\n\ninterface IFoo {\n tag: "Foo";\n recordInt: number;\n}|] it [i|works on normal constructors|] $ do formatTSDeclarations' defaultFormattingOptions (getTypeScriptDeclarations @NormalConstructors Proxy) `shouldBe` [i|type NormalConstructors = ICon2;\n\ninterface ICon2 {\n tag: "Con2";\n contents: number;\n}|] +#endif main :: IO () main = hspec tests From 6c4cbc9dd5127c6f36faab5ec73df52531a68644 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 22 Mar 2023 21:33:09 -0700 Subject: [PATCH 169/208] GHC 9.2.6 -> 9.2.7 in CI --- .github/workflows/aeson-typescript.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 669806a..5d5b9ee 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -70,7 +70,7 @@ jobs: - "8.8.4" - "8.10.7" - "9.0.2" - - "9.2.6" + - "9.2.7" - "9.4.4" steps: From 6f5803aaa179e11410b4a714b5479d2f84a15b87 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 22 Mar 2023 21:33:24 -0700 Subject: [PATCH 170/208] See if we can test GHC 9.6.1 Cabal in CI --- .github/workflows/aeson-typescript.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 5d5b9ee..3246c6a 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -19,6 +19,7 @@ jobs: - "9.0.2" - "9.2.6" - "9.4.4" + - "9.6.1" # exclude: # - os: macOS-latest # ghc: 8.8.3 From 5ae6687ba09c609c6698ef06ad6b3dac490395d1 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Wed, 22 Mar 2023 11:31:00 -0700 Subject: [PATCH 171/208] Enable building with mtl-2.3 --- src/Data/Aeson/TypeScript/Recursive.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Data/Aeson/TypeScript/Recursive.hs b/src/Data/Aeson/TypeScript/Recursive.hs index b0c7c12..8eb673c 100755 --- a/src/Data/Aeson/TypeScript/Recursive.hs +++ b/src/Data/Aeson/TypeScript/Recursive.hs @@ -16,6 +16,7 @@ module Data.Aeson.TypeScript.Recursive ( , getAllParentTypes ) where +import Control.Monad import Control.Monad.State import Control.Monad.Trans.Maybe import Control.Monad.Writer From 1ddb9d529436d804957c52f89a7f6cfbdf5e1a5c Mon Sep 17 00:00:00 2001 From: Langston Barrett Date: Wed, 3 Aug 2022 10:14:40 -0400 Subject: [PATCH 172/208] Add instances for more types from base Specifically: * `Data.Functor.Compose.Compose` * `Data.Functor.Const.Const` * `Data.Functor.Identity.Identity` * `Data.Functor.Product.Product` * `Data.List.NonEmpty.NonEmpty` * `Data.Word.Word` * `Data.Word.Word16` * `Data.Word.Word32` * `Data.Word.Word64` * `Numeric.Natural.Natural` --- src/Data/Aeson/TypeScript/Instances.hs | 43 ++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index 3833dcc..8759207 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -13,9 +13,14 @@ module Data.Aeson.TypeScript.Instances where import qualified Data.Aeson as A import Data.Aeson.TypeScript.Types import Data.Data +import Data.Functor.Compose (Compose) +import Data.Functor.Const (Const) +import Data.Functor.Identity (Identity) +import Data.Functor.Product (Product) import Data.HashMap.Strict import Data.HashSet import qualified Data.List as L +import Data.List.NonEmpty (NonEmpty) import Data.Map.Strict import Data.Set import Data.String.Interpolate @@ -23,6 +28,7 @@ import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Data.Void import Data.Word +import Numeric.Natural (Natural) import GHC.Int #if !MIN_VERSION_base(4,11,0) @@ -49,6 +55,9 @@ instance TypeScript TL.Text where instance TypeScript Integer where getTypeScriptType _ = "number" +instance TypeScript Natural where + getTypeScriptType _ = "number" + instance TypeScript Float where getTypeScriptType _ = "number" @@ -73,13 +82,29 @@ instance TypeScript Int64 where instance TypeScript Char where getTypeScriptType _ = "string" +instance TypeScript Word where + getTypeScriptType _ = "number" + instance TypeScript Word8 where getTypeScriptType _ = "number" +instance TypeScript Word16 where + getTypeScriptType _ = "number" + +instance TypeScript Word32 where + getTypeScriptType _ = "number" + +instance TypeScript Word64 where + getTypeScriptType _ = "number" + instance {-# OVERLAPPABLE #-} (TypeScript a) => TypeScript [a] where getTypeScriptType _ = (getTypeScriptType (Proxy :: Proxy a)) ++ "[]" getParentTypes _ = [TSType (Proxy :: Proxy a)] +instance (TypeScript a) => TypeScript (NonEmpty a) where + getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy [a]) + getParentTypes _ = [TSType (Proxy :: Proxy a)] + instance {-# OVERLAPPING #-} TypeScript [Char] where getTypeScriptType _ = "string" @@ -114,6 +139,24 @@ instance (TypeScript a, TypeScript b, TypeScript c, TypeScript d) => TypeScript , (TSType (Proxy :: Proxy d)) ] +instance (TypeScript a) => TypeScript (Const a) where + getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) + getParentTypes _ = [TSType (Proxy :: Proxy a)] + +instance (TypeScript a) => TypeScript (Identity a) where + getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) + getParentTypes _ = [TSType (Proxy :: Proxy a)] + +instance (Typeable f, Typeable g, Typeable a, TypeScript (f (g a)), TypeScript a) => TypeScript (Compose f g a) where + getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy (f (g a))) + getParentTypes _ = getParentTypes (Proxy :: Proxy (f (g a))) + +instance (Typeable f, Typeable g, Typeable a, TypeScript (f a), TypeScript (g a)) => TypeScript (Product f g a) where + getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy (f a, g a)) + getParentTypes _ = L.nub [ (TSType (Proxy :: Proxy (f a))) + , (TSType (Proxy :: Proxy (g a))) + ] + instance (TypeScript a) => TypeScript (Maybe a) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) getTypeScriptOptional _ = True From 0043bb7fd9cfad055fa085be46dfc872485ce3cd Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 22 Mar 2023 23:06:14 -0700 Subject: [PATCH 173/208] Bump dev stack resolver --- stack.yaml | 2 +- stack.yaml.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stack.yaml b/stack.yaml index 812491d..677d2f3 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,5 +1,5 @@ -resolver: lts-20.12 +resolver: lts-20.15 packages: - . diff --git a/stack.yaml.lock b/stack.yaml.lock index b1d5d3a..79b93cf 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -6,7 +6,7 @@ packages: [] snapshots: - completed: - sha256: af5d667f6096e535b9c725a72cffe0f6c060e0568d9f9eeda04caee70d0d9d2d - size: 649133 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/12.yaml - original: lts-20.12 + sha256: 5d1df60a0aaf19ab42eb79d5ca01ab812318a96be3925559e902e1bfd8cac569 + size: 649582 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/15.yaml + original: lts-20.15 From 5ef8e5507cbef37eb89a8e4d2c0c667dec931d16 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 22 Mar 2023 23:07:07 -0700 Subject: [PATCH 174/208] Release 0.5.0.0 --- CHANGELOG.md | 6 +++++- aeson-typescript.cabal | 2 +- package.yaml | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e817f8a..0b614a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,14 @@ ## (unreleased) +## 0.5.0.0 + * [#35](https://github.com/codedownio/aeson-typescript/pull/35) * Add `Data.Aeson.TypeScript.LegalName` module for checking whether a name is a legal JavaScript name or not. * The `defaultFormatter` will `error` if the name contains illegal characters. -* Add support for @no-emit-typescript in Haddocks for constructors and record fields (requires GHC >= 9.2) +* Be able to transfer Haddock comments to emitted TypeScript (requires GHC >= 9.2 and `-haddock` flag) +* Add support for @no-emit-typescript in Haddocks for constructors and record fields (requires GHC >= 9.2 and `-haddock` flag) +* Support GHC 9.6.1 ## 0.4.2.0 diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 645ac1a..6416159 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: aeson-typescript -version: 0.4.2.0 +version: 0.5.0.0 synopsis: Generate TypeScript definition files from your ADTs description: Please see the README on Github at category: Text, Web, JSON diff --git a/package.yaml b/package.yaml index c8e44f4..a029bb2 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: aeson-typescript -version: 0.4.2.0 +version: 0.5.0.0 github: "codedownio/aeson-typescript" license: BSD3 category: Text, Web, JSON From c8c94fd49b052eb8ac09680cd3dd6efc7661b0a4 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 23 Mar 2023 00:29:31 -0700 Subject: [PATCH 175/208] Try fixing up new instances and add test of the new number ones --- src/Data/Aeson/TypeScript/Instances.hs | 14 ++++++++++---- test/TestBoilerplate.hs | 12 ++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Instances.hs b/src/Data/Aeson/TypeScript/Instances.hs index 8759207..97de51c 100644 --- a/src/Data/Aeson/TypeScript/Instances.hs +++ b/src/Data/Aeson/TypeScript/Instances.hs @@ -3,6 +3,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverlappingInstances #-} {-# LANGUAGE CPP #-} +{-# LANGUAGE PolyKinds #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -- Note: the OverlappingInstances pragma is only here so the overlapping instances in this file @@ -19,6 +20,7 @@ import Data.Functor.Identity (Identity) import Data.Functor.Product (Product) import Data.HashMap.Strict import Data.HashSet +import Data.Kind (Type) import qualified Data.List as L import Data.List.NonEmpty (NonEmpty) import Data.Map.Strict @@ -28,8 +30,8 @@ import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Data.Void import Data.Word -import Numeric.Natural (Natural) import GHC.Int +import Numeric.Natural (Natural) #if !MIN_VERSION_base(4,11,0) import Data.Monoid @@ -139,7 +141,7 @@ instance (TypeScript a, TypeScript b, TypeScript c, TypeScript d) => TypeScript , (TSType (Proxy :: Proxy d)) ] -instance (TypeScript a) => TypeScript (Const a) where +instance forall a k (b :: k). (Typeable k, Typeable b, TypeScript a) => TypeScript (Const a b) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) getParentTypes _ = [TSType (Proxy :: Proxy a)] @@ -147,11 +149,15 @@ instance (TypeScript a) => TypeScript (Identity a) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy a) getParentTypes _ = [TSType (Proxy :: Proxy a)] -instance (Typeable f, Typeable g, Typeable a, TypeScript (f (g a)), TypeScript a) => TypeScript (Compose f g a) where +instance forall k k1 (f :: k -> Type) (g :: k1 -> k) a. ( + Typeable k, Typeable k1, Typeable f, Typeable g, Typeable a, TypeScript (f (g a)), TypeScript a + ) => TypeScript (Compose f g a) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy (f (g a))) getParentTypes _ = getParentTypes (Proxy :: Proxy (f (g a))) -instance (Typeable f, Typeable g, Typeable a, TypeScript (f a), TypeScript (g a)) => TypeScript (Product f g a) where +instance forall k (f :: k -> Type) (g :: k -> Type) a. ( + Typeable k, Typeable f, Typeable g, Typeable a, TypeScript (f a), TypeScript (g a) + ) => TypeScript (Product f g a) where getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy (f a, g a)) getParentTypes _ = L.nub [ (TSType (Proxy :: Proxy (f a))) , (TSType (Proxy :: Proxy (g a))) diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index ac4afd0..a642548 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -10,7 +10,9 @@ import Data.Functor.Identity import Data.Kind import Data.Proxy import Data.String.Interpolate +import Data.Word import Language.Haskell.TH hiding (Type) +import Numeric.Natural (Natural) import Test.Hspec import Util import Util.Aeson @@ -25,6 +27,13 @@ data TwoConstructor = Con1 { con1String :: String } | Con2 { con2String :: Strin data Complex a = Nullary | Unary Int | Product String Char a | Record { testOne :: Int, testTwo :: Bool, testThree :: Complex a} deriving Eq data Optional = Optional {optionalInt :: Maybe Int} data AesonTypes = AesonTypes { aesonValue :: A.Value, aesonObject :: A.Object } +data Numbers = Numbers { + natural :: Natural + , word :: Word + , word16 :: Word16 + , word32 :: Word32 + , word64 :: Word64 + } -- * For testing type families @@ -65,6 +74,7 @@ testDeclarations testName aesonOptions = do deriveInstances ''Complex deriveInstances ''Optional deriveInstances ''AesonTypes + deriveInstances ''Numbers typesAndValues :: Exp <- [e|[(getTypeScriptType (Proxy :: Proxy Unit), A.encode Unit) @@ -94,6 +104,8 @@ testDeclarations testName aesonOptions = do aesonValue = A.object [("foo" :: A.Key, A.Number 42)] , aesonObject = aesonFromList [("foo", A.Number 42)] })) + + , (getTypeScriptType (Proxy :: Proxy Optional), A.encode (Numbers 42 42 42 42 42)) ]|] declarations :: Exp <- [e|getTypeScriptDeclarations (Proxy :: Proxy Unit) From 7a7fd49bf99ad3462b43468d698b5ceb0a7babed Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 23 Mar 2023 00:31:54 -0700 Subject: [PATCH 176/208] Fix numbers boilerplate --- test/TestBoilerplate.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index a642548..2dbfbb0 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -105,7 +105,7 @@ testDeclarations testName aesonOptions = do , aesonObject = aesonFromList [("foo", A.Number 42)] })) - , (getTypeScriptType (Proxy :: Proxy Optional), A.encode (Numbers 42 42 42 42 42)) + , (getTypeScriptType (Proxy :: Proxy Numbers), A.encode (Numbers 42 42 42 42 42)) ]|] declarations :: Exp <- [e|getTypeScriptDeclarations (Proxy :: Proxy Unit) From ba24350b7117ec57f829a95b28ed41d6b8817c7a Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 23 Mar 2023 00:39:32 -0700 Subject: [PATCH 177/208] One more boilerplate fix --- test/TestBoilerplate.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index 2dbfbb0..f19112d 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -118,6 +118,7 @@ testDeclarations testName aesonOptions = do <> getTypeScriptDeclarations (Proxy :: Proxy (Complex T)) <> getTypeScriptDeclarations (Proxy :: Proxy Optional) <> getTypeScriptDeclarations (Proxy :: Proxy AesonTypes) + <> getTypeScriptDeclarations (Proxy :: Proxy Numbers) |] tests <- [d|tests :: SpecWith () From aed83abcfee2aecd3399b33c67a308cbd2e340db Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 23 Mar 2023 00:39:39 -0700 Subject: [PATCH 178/208] Fix up package.yaml tested-with --- .github/workflows/aeson-typescript.yml | 2 +- aeson-typescript.cabal | 8 +++++++- package.yaml | 9 ++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 3246c6a..2f60f89 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -17,7 +17,7 @@ jobs: - "8.8.4" - "8.10.7" - "9.0.2" - - "9.2.6" + - "9.2.7" - "9.4.4" - "9.6.1" # exclude: diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 6416159..34423cd 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -18,7 +18,13 @@ license: BSD3 license-file: LICENSE build-type: Simple tested-with: - GHC == 9.0.1, GHC == 8.10.4, GHC == 8.10.3, GHC == 8.8.4, GHC == 8.8.3 + GHC == 9.6.1 + , GHC == 9.4.4 + , GHC == 9.2.7 + , GHC == 9.0.2 + , GHC == 8.10.7 + , GHC == 8.8.4 + , GHC == 8.6.5 extra-source-files: README.md CHANGELOG.md diff --git a/package.yaml b/package.yaml index a029bb2..87ad92c 100644 --- a/package.yaml +++ b/package.yaml @@ -23,7 +23,14 @@ synopsis: Generate TypeScript definition files from your ADTs # common to point users to the README.md file. description: Please see the README on Github at -tested-with: GHC == 9.0.1, GHC == 8.10.4, GHC == 8.10.3, GHC == 8.8.4, GHC == 8.8.3 +tested-with: +- GHC == 9.6.1 +- GHC == 9.4.4 +- GHC == 9.2.7 +- GHC == 9.0.2 +- GHC == 8.10.7 +- GHC == 8.8.4 +- GHC == 8.6.5 dependencies: - aeson From afbf9210ea53f29f70631603f73b51403fa49584 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 23 Mar 2023 00:43:22 -0700 Subject: [PATCH 179/208] Bump CI setup-node version (v2 -> v3) and node (v12 -> v16) --- .github/workflows/aeson-typescript.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 2f60f89..6577565 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -89,10 +89,9 @@ jobs: path: ~/.stack key: ${{ runner.os }}-${{ matrix.ghc }}-stack - # Install TSC - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v3 with: - node-version: '12' + node-version: '16' - name: Install TSC run: | npm install -g typescript From 55ed89b0eec31fc01afc81ced236905476781e2f Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 23 Mar 2023 01:54:30 -0700 Subject: [PATCH 180/208] More FancyFunctors fields --- test/TestBoilerplate.hs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index f19112d..5b4ad98 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -6,8 +6,12 @@ import Control.Monad.Writer.Lazy hiding (Product) import qualified Data.Aeson as A import Data.Aeson.TH as A import Data.Aeson.TypeScript.TH +import Data.Functor.Compose +import Data.Functor.Const import Data.Functor.Identity +import Data.Functor.Product import Data.Kind +import Data.List.NonEmpty import Data.Proxy import Data.String.Interpolate import Data.Word @@ -34,6 +38,17 @@ data Numbers = Numbers { , word32 :: Word32 , word64 :: Word64 } +data FancyFunctors = FancyFunctors { + nonEmpty :: NonEmpty Int + , const :: Const Int Int + , product :: Product Identity Identity Int + , compose :: Compose Identity Identity Int + } + +-- * Values + +fancyFunctorsValue :: FancyFunctors +fancyFunctorsValue = FancyFunctors (42 :| []) (Const 42) (Pair 42 42) (Compose 42) -- * For testing type families @@ -75,6 +90,7 @@ testDeclarations testName aesonOptions = do deriveInstances ''Optional deriveInstances ''AesonTypes deriveInstances ''Numbers + deriveInstances ''FancyFunctors typesAndValues :: Exp <- [e|[(getTypeScriptType (Proxy :: Proxy Unit), A.encode Unit) @@ -106,6 +122,7 @@ testDeclarations testName aesonOptions = do })) , (getTypeScriptType (Proxy :: Proxy Numbers), A.encode (Numbers 42 42 42 42 42)) + , (getTypeScriptType (Proxy :: Proxy FancyFunctors), A.encode fancyFunctorsValue) ]|] declarations :: Exp <- [e|getTypeScriptDeclarations (Proxy :: Proxy Unit) @@ -119,6 +136,7 @@ testDeclarations testName aesonOptions = do <> getTypeScriptDeclarations (Proxy :: Proxy Optional) <> getTypeScriptDeclarations (Proxy :: Proxy AesonTypes) <> getTypeScriptDeclarations (Proxy :: Proxy Numbers) + <> getTypeScriptDeclarations (Proxy :: Proxy FancyFunctors) |] tests <- [d|tests :: SpecWith () From 671347e3739b63bf04d5412330dc9a4748c7832e Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 27 Apr 2023 17:15:06 -0700 Subject: [PATCH 181/208] Release 0.6.0.0 --- CHANGELOG.md | 5 ++++- aeson-typescript.cabal | 2 +- package.yaml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b614a8..0d7aba8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ # Change log -## (unreleased) +## 0.6.0.0 + +* New word instances: Word, Word16, Word32, Word64 +* New instances from Data.Functor: Compose, Const, Identity, Product ## 0.5.0.0 diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 34423cd..aead56b 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: aeson-typescript -version: 0.5.0.0 +version: 0.6.0.0 synopsis: Generate TypeScript definition files from your ADTs description: Please see the README on Github at category: Text, Web, JSON diff --git a/package.yaml b/package.yaml index 87ad92c..3821c55 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: aeson-typescript -version: 0.5.0.0 +version: 0.6.0.0 github: "codedownio/aeson-typescript" license: BSD3 category: Text, Web, JSON From 903774c23902adb714662758bfc8b836f26a057e Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 22 Jun 2023 21:05:35 -0700 Subject: [PATCH 182/208] ci: try specifying stack resolver properly --- .github/workflows/aeson-typescript.yml | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 6577565..9290bd6 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -67,27 +67,33 @@ jobs: strategy: fail-fast: false matrix: - ghc: - - "8.8.4" - - "8.10.7" - - "9.0.2" - - "9.2.7" - - "9.4.4" + include: + - ghc: "8.8.4" + resolver: "lts-16.31" + - ghc: "8.10.7" + resolver: "lts-18.28" + - ghc: "9.0.2" + resolver: "lts-19.33" + - ghc: "9.2.8" + resolver: "lts-20.26" + - ghc: "9.4.5" + resolver: "lts-21.0" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: haskell/actions/setup@v2 name: Setup Haskell Stack with: ghc-version: ${{ matrix.ghc }} + enable-stack: true stack-version: "latest" - uses: actions/cache@v1 name: Cache ~/.stack with: path: ~/.stack - key: ${{ runner.os }}-${{ matrix.ghc }}-stack + key: ${{ runner.os }}-${{ matrix.ghc }}-${{ matrix.resolver }}-stack - uses: actions/setup-node@v3 with: @@ -98,8 +104,8 @@ jobs: - name: Build run: | - stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks + stack build --resolver ${{matrix.resolver}} --system-ghc --test --bench --no-run-tests --no-run-benchmarks - name: Test run: | - stack test --system-ghc + stack test --resolver ${{matrix.resolver}} --system-ghc From 0f4b0d3f22c95601a99859e712ee2279bd38b948 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 28 Jun 2023 04:43:42 -0700 Subject: [PATCH 183/208] Modernize CI by using explicit stack.yaml files --- .github/workflows/aeson-typescript.yml | 29 +++++++++++--------------- stack-8.10.7.yaml | 5 +++++ stack-8.10.7.yaml.lock | 12 +++++++++++ stack-9.0.2.yaml | 5 +++++ stack-9.0.2.yaml.lock | 12 +++++++++++ stack-9.2.8.yaml | 5 +++++ stack-9.2.8.yaml.lock | 12 +++++++++++ stack-9.4.5.yaml | 5 +++++ stack-9.4.5.yaml.lock | 12 +++++++++++ stack.yaml | 5 +---- stack.yaml.lock | 8 +++---- 11 files changed, 85 insertions(+), 25 deletions(-) create mode 100644 stack-8.10.7.yaml create mode 100644 stack-8.10.7.yaml.lock create mode 100644 stack-9.0.2.yaml create mode 100644 stack-9.0.2.yaml.lock create mode 100644 stack-9.2.8.yaml create mode 100644 stack-9.2.8.yaml.lock create mode 100644 stack-9.4.5.yaml create mode 100644 stack-9.4.5.yaml.lock diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 9290bd6..f660f16 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -13,16 +13,11 @@ jobs: matrix: os: [ubuntu-latest, macOS-latest] ghc: - - "8.6.5" - - "8.8.4" - "8.10.7" - "9.0.2" - - "9.2.7" - - "9.4.4" - - "9.6.1" - # exclude: - # - os: macOS-latest - # ghc: 8.8.3 + - "9.2.8" + - "9.4.5" + - "9.6.2" steps: - uses: actions/checkout@v2 @@ -68,16 +63,16 @@ jobs: fail-fast: false matrix: include: - - ghc: "8.8.4" - resolver: "lts-16.31" - ghc: "8.10.7" - resolver: "lts-18.28" + yaml: "stack-8.10.7" - ghc: "9.0.2" - resolver: "lts-19.33" + yaml: "stack-9.0.2.yaml" - ghc: "9.2.8" - resolver: "lts-20.26" + yaml: "stack-9.2.8.yaml" - ghc: "9.4.5" - resolver: "lts-21.0" + yaml: "stack-9.4.5.yaml" + - ghc: "9.6.2" + yaml: "stack.yaml" steps: - uses: actions/checkout@v3 @@ -93,7 +88,7 @@ jobs: name: Cache ~/.stack with: path: ~/.stack - key: ${{ runner.os }}-${{ matrix.ghc }}-${{ matrix.resolver }}-stack + key: ${{ runner.os }}-${{ matrix.ghc }}-${{ matrix.yaml }} - uses: actions/setup-node@v3 with: @@ -104,8 +99,8 @@ jobs: - name: Build run: | - stack build --resolver ${{matrix.resolver}} --system-ghc --test --bench --no-run-tests --no-run-benchmarks + stack build --stack-yaml ${{matrix.yaml}} --system-ghc --test --bench --no-run-tests --no-run-benchmarks - name: Test run: | - stack test --resolver ${{matrix.resolver}} --system-ghc + stack test --stack-yaml ${{matrix.yaml}} --system-ghc diff --git a/stack-8.10.7.yaml b/stack-8.10.7.yaml new file mode 100644 index 0000000..2b383ea --- /dev/null +++ b/stack-8.10.7.yaml @@ -0,0 +1,5 @@ + +resolver: lts-18.28 + +packages: +- . diff --git a/stack-8.10.7.yaml.lock b/stack-8.10.7.yaml.lock new file mode 100644 index 0000000..da10c3e --- /dev/null +++ b/stack-8.10.7.yaml.lock @@ -0,0 +1,12 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: [] +snapshots: +- completed: + sha256: 428ec8d5ce932190d3cbe266b9eb3c175cd81e984babf876b64019e2cbe4ea68 + size: 590100 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/28.yaml + original: lts-18.28 diff --git a/stack-9.0.2.yaml b/stack-9.0.2.yaml new file mode 100644 index 0000000..fe2c91a --- /dev/null +++ b/stack-9.0.2.yaml @@ -0,0 +1,5 @@ + +resolver: lts-19.33 + +packages: +- . diff --git a/stack-9.0.2.yaml.lock b/stack-9.0.2.yaml.lock new file mode 100644 index 0000000..d79c369 --- /dev/null +++ b/stack-9.0.2.yaml.lock @@ -0,0 +1,12 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: [] +snapshots: +- completed: + sha256: 6d1532d40621957a25bad5195bfca7938e8a06d923c91bc52aa0f3c41181f2d4 + size: 619204 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/19/33.yaml + original: lts-19.33 diff --git a/stack-9.2.8.yaml b/stack-9.2.8.yaml new file mode 100644 index 0000000..028d2f7 --- /dev/null +++ b/stack-9.2.8.yaml @@ -0,0 +1,5 @@ + +resolver: lts-20.26 + +packages: +- . diff --git a/stack-9.2.8.yaml.lock b/stack-9.2.8.yaml.lock new file mode 100644 index 0000000..ea5a850 --- /dev/null +++ b/stack-9.2.8.yaml.lock @@ -0,0 +1,12 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: [] +snapshots: +- completed: + sha256: 5a59b2a405b3aba3c00188453be172b85893cab8ebc352b1ef58b0eae5d248a2 + size: 650475 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/26.yaml + original: lts-20.26 diff --git a/stack-9.4.5.yaml b/stack-9.4.5.yaml new file mode 100644 index 0000000..a05c602 --- /dev/null +++ b/stack-9.4.5.yaml @@ -0,0 +1,5 @@ + +resolver: lts-21.0 + +packages: +- . diff --git a/stack-9.4.5.yaml.lock b/stack-9.4.5.yaml.lock new file mode 100644 index 0000000..ad1be6c --- /dev/null +++ b/stack-9.4.5.yaml.lock @@ -0,0 +1,12 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: [] +snapshots: +- completed: + sha256: 1867d84255dff8c87373f5dd03e5a5cb1c10a99587e26c8793e750c54e83ffdc + size: 639139 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/0.yaml + original: lts-21.0 diff --git a/stack.yaml b/stack.yaml index 677d2f3..4d5471e 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,8 +1,5 @@ -resolver: lts-20.15 +resolver: nightly-2023-06-27 packages: - . - -# ghc-options: -# "$locals": -fwrite-ide-info diff --git a/stack.yaml.lock b/stack.yaml.lock index 79b93cf..204a9e1 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -6,7 +6,7 @@ packages: [] snapshots: - completed: - sha256: 5d1df60a0aaf19ab42eb79d5ca01ab812318a96be3925559e902e1bfd8cac569 - size: 649582 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/15.yaml - original: lts-20.15 + sha256: 7cb8c85885c204500c43790ea0e7802a6f8bdaf1a27309de33cbd8898f2c1c00 + size: 531943 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2023/6/27.yaml + original: nightly-2023-06-27 From 968a95bc695129209586b5c70ab07cc60003fdd9 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 28 Jun 2023 04:43:57 -0700 Subject: [PATCH 184/208] Fix Aeson CPP --- test/TestBoilerplate.hs | 2 +- test/Util/Aeson.hs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/test/TestBoilerplate.hs b/test/TestBoilerplate.hs index 5b4ad98..3b45997 100644 --- a/test/TestBoilerplate.hs +++ b/test/TestBoilerplate.hs @@ -117,7 +117,7 @@ testDeclarations testName aesonOptions = do , (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)] })) diff --git a/test/Util/Aeson.hs b/test/Util/Aeson.hs index b38b900..3954546 100644 --- a/test/Util/Aeson.hs +++ b/test/Util/Aeson.hs @@ -8,8 +8,15 @@ import qualified Data.Aeson.KeyMap as KM aesonFromList :: [(K.Key, v)] -> KM.KeyMap v aesonFromList = KM.fromList + +type AesonKey = A.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 From 870982a53f939f29754138ff29da45b395019cc7 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 28 Jun 2023 04:54:10 -0700 Subject: [PATCH 185/208] Fix a yaml path --- .github/workflows/aeson-typescript.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index f660f16..343791a 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -64,7 +64,7 @@ jobs: matrix: include: - ghc: "8.10.7" - yaml: "stack-8.10.7" + yaml: "stack-8.10.7.yaml" - ghc: "9.0.2" yaml: "stack-9.0.2.yaml" - ghc: "9.2.8" From a80b62c1906a82cb9853d0f647b4d6ec6ef19195 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 28 Jun 2023 04:54:35 -0700 Subject: [PATCH 186/208] Another Util/Aeson.hs fix --- test/Util/Aeson.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Util/Aeson.hs b/test/Util/Aeson.hs index 3954546..38aded1 100644 --- a/test/Util/Aeson.hs +++ b/test/Util/Aeson.hs @@ -9,7 +9,7 @@ import qualified Data.Aeson.KeyMap as KM aesonFromList :: [(K.Key, v)] -> KM.KeyMap v aesonFromList = KM.fromList -type AesonKey = A.Key +type AesonKey = K.Key #else import Data.Aeson as A import Data.HashMap.Strict as HM From c8e64b65aec4247d5942d5fe0006ac6d525f4efe Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 28 Jun 2023 05:05:58 -0700 Subject: [PATCH 187/208] Improve output when TSC check fails --- test/Util.hs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/Util.hs b/test/Util.hs index 88a088d..8436f05 100644 --- a/test/Util.hs +++ b/test/Util.hs @@ -70,12 +70,19 @@ testTypeCheckDeclarations tsDeclarations typesAndVals = withSystemTempDirectory 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 () From d7deffad3d9f6652ef820be7eca71b8f77b0dee9 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 28 Jun 2023 05:07:56 -0700 Subject: [PATCH 188/208] Try fixing cabal tests in CI --- .github/workflows/aeson-typescript.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 343791a..9d31265 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -33,16 +33,15 @@ jobs: run: | cabal freeze - - uses: actions/cache@v1 + - uses: actions/cache@v3 name: Cache ~/.cabal/store with: path: ${{ steps.setup-haskell-cabal.outputs.cabal-store }} key: ${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }} - # Install TSC - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v3 with: - node-version: '12' + node-version: '16' - name: Install TSC run: | npm install -g typescript @@ -84,7 +83,7 @@ jobs: enable-stack: true stack-version: "latest" - - uses: actions/cache@v1 + - uses: actions/cache@v3 name: Cache ~/.stack with: path: ~/.stack From 40674f9628732eb8afd5918373debb4a764bf3d8 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 12 Oct 2023 21:24:27 -0700 Subject: [PATCH 189/208] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4cdf469..249e3b5 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,6 @@ Now you can generate the types by running `stack runhaskell tsdef/Main.hs > type # 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`! It 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). From 0d0f4b2d54f0efa16e0af1e47ef5e6353a6b8250 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 12 Oct 2023 21:25:20 -0700 Subject: [PATCH 190/208] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 249e3b5..227be17 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,6 @@ Now you can generate the types by running `stack runhaskell tsdef/Main.hs > type # See also -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`! It also has the advantage of magically collecting all the types used in your API, so you don't have to list them out manually. +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). From 0c62a9291dd5e69e40d0682900a759b5498a9029 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 13 Nov 2023 17:25:32 -0800 Subject: [PATCH 191/208] Apply typeNameModifier to fields within interfaces --- CHANGELOG.md | 3 +++ src/Data/Aeson/TypeScript/Formatting.hs | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d7aba8..720c2c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Change log +## Unreleased + +* Apply `typeNameModifier` to type names emitted on the RHS of fields within interfaces, for consistency. ## 0.6.0.0 diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index b4e4c77..c6051fc 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -41,6 +41,9 @@ formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceNa 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 ""}: #{typeNameModifier typ}|] + formatTSDeclaration _ (TSRawDeclaration text) = text indentTo :: Int -> T.Text -> T.Text @@ -89,9 +92,6 @@ validateFormattingOptions options@FormattingOptions{..} decls -- 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 -> "" From 9465d8dea3488450a69220dc919c094fff8a3e07 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 13 Nov 2023 17:33:39 -0800 Subject: [PATCH 192/208] Update stack.yaml files/GHC versions --- .github/workflows/aeson-typescript.yml | 10 +++++----- stack-9.4.5.yaml | 5 ----- stack-9.4.7.yaml | 5 +++++ stack-9.4.5.yaml.lock => stack-9.4.7.yaml.lock | 8 ++++---- stack.yaml | 2 +- stack.yaml.lock | 8 ++++---- 6 files changed, 19 insertions(+), 19 deletions(-) delete mode 100644 stack-9.4.5.yaml create mode 100644 stack-9.4.7.yaml rename stack-9.4.5.yaml.lock => stack-9.4.7.yaml.lock (65%) diff --git a/.github/workflows/aeson-typescript.yml b/.github/workflows/aeson-typescript.yml index 9d31265..9c63d79 100644 --- a/.github/workflows/aeson-typescript.yml +++ b/.github/workflows/aeson-typescript.yml @@ -16,8 +16,8 @@ jobs: - "8.10.7" - "9.0.2" - "9.2.8" - - "9.4.5" - - "9.6.2" + - "9.4.7" + - "9.6.3" steps: - uses: actions/checkout@v2 @@ -68,9 +68,9 @@ jobs: yaml: "stack-9.0.2.yaml" - ghc: "9.2.8" yaml: "stack-9.2.8.yaml" - - ghc: "9.4.5" - yaml: "stack-9.4.5.yaml" - - ghc: "9.6.2" + - ghc: "9.4.7" + yaml: "stack-9.4.7.yaml" + - ghc: "9.6.3" yaml: "stack.yaml" steps: diff --git a/stack-9.4.5.yaml b/stack-9.4.5.yaml deleted file mode 100644 index a05c602..0000000 --- a/stack-9.4.5.yaml +++ /dev/null @@ -1,5 +0,0 @@ - -resolver: lts-21.0 - -packages: -- . diff --git a/stack-9.4.7.yaml b/stack-9.4.7.yaml new file mode 100644 index 0000000..13994e4 --- /dev/null +++ b/stack-9.4.7.yaml @@ -0,0 +1,5 @@ + +resolver: lts-21.20 + +packages: +- . diff --git a/stack-9.4.5.yaml.lock b/stack-9.4.7.yaml.lock similarity index 65% rename from stack-9.4.5.yaml.lock rename to stack-9.4.7.yaml.lock index ad1be6c..1b8f599 100644 --- a/stack-9.4.5.yaml.lock +++ b/stack-9.4.7.yaml.lock @@ -6,7 +6,7 @@ packages: [] snapshots: - completed: - sha256: 1867d84255dff8c87373f5dd03e5a5cb1c10a99587e26c8793e750c54e83ffdc - size: 639139 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/0.yaml - original: lts-21.0 + sha256: 5921ddc75f5dd3f197fbc32e1e5676895a8e7b971d4f82ef6b556657801dd18a + size: 640054 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/20.yaml + original: lts-21.20 diff --git a/stack.yaml b/stack.yaml index 4d5471e..bfc9a75 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,5 +1,5 @@ -resolver: nightly-2023-06-27 +resolver: nightly-2023-11-14 packages: - . diff --git a/stack.yaml.lock b/stack.yaml.lock index 204a9e1..dd4da80 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -6,7 +6,7 @@ packages: [] snapshots: - completed: - sha256: 7cb8c85885c204500c43790ea0e7802a6f8bdaf1a27309de33cbd8898f2c1c00 - size: 531943 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2023/6/27.yaml - original: nightly-2023-06-27 + sha256: 0eaacfc9de6b0ab46ab6026166d2ba7718ab06a8612086b3ee21d7667e682df0 + size: 698974 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2023/11/14.yaml + original: nightly-2023-11-14 From 7cd51838d0c69fe299f52b8ce496d94795898a57 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 13 Nov 2023 17:45:44 -0800 Subject: [PATCH 193/208] Undo 0c62a92 --- src/Data/Aeson/TypeScript/Formatting.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index c6051fc..fdaad6e 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -42,7 +42,7 @@ formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceNa 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 ""}: #{typeNameModifier typ}|] + formatTSField (TSField optional name typ maybeDoc) = makeDocPrefix maybeDoc <> [i|#{name}#{if optional then ("?" :: String) else ""}: #{typ}|] formatTSDeclaration _ (TSRawDeclaration text) = text From 70af53a251b854c3767e7407226e197716a7f43f Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Mon, 13 Nov 2023 17:54:42 -0800 Subject: [PATCH 194/208] Remove changelog entry until we fix this for real --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 720c2c8..79f651a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,6 @@ ## Unreleased -* Apply `typeNameModifier` to type names emitted on the RHS of fields within interfaces, for consistency. - ## 0.6.0.0 * New word instances: Word, Word16, Word32, Word64 From 1afccfe6318bea005f6fe319bcda52ee8d09b7a2 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 30 Nov 2023 20:35:50 -0700 Subject: [PATCH 195/208] Add more enum formatting tests --- test/Formatting.hs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/test/Formatting.hs b/test/Formatting.hs index d035c4e..b4953e2 100644 --- a/test/Formatting.hs +++ b/test/Formatting.hs @@ -14,6 +14,12 @@ import Test.Hspec data D = S | F deriving (Eq, Show) $(deriveTypeScript defaultOptions ''D) +data D2 = S2 | F2 deriving (Eq, Show) +$(deriveTypeScript defaultOptions ''D2) + +data Unit = U deriving (Eq, Show) +$(deriveTypeScript defaultOptions ''Unit) + data PrimeInType' = PrimeInType $(deriveTypeScript defaultOptions ''PrimeInType') @@ -47,11 +53,21 @@ tests = describe "Formatting" $ do 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 }|] + it "should generate a TS Enum with multiple" $ + formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @D Proxy <> getTypeScriptDeclarations @D2 Proxy) `shouldBe` + [__i|enum D { S, F } + + enum D2 { S2, F2 }|] + + it "should generate a TS Enum from unit" $ + formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @Unit Proxy) `shouldBe` + [__i|enum Unit { U }|] + describe "and the EnumWithType format option is set" $ it "should generate a TS Enum with a type declaration" $ formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = EnumWithType }) (getTypeScriptDeclarations @D Proxy) `shouldBe` From a7d273e80bf6b5aeeb827deab54d1d1f7d4f3213 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 30 Nov 2023 20:36:16 -0700 Subject: [PATCH 196/208] Get rid of validateFormattingOptions --- src/Data/Aeson/TypeScript/Formatting.hs | 34 +++++++------------------ 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index fdaad6e..f2464e4 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -25,13 +25,13 @@ formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVar where mainDeclaration = case typeAlternativesFormat of Enum -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnum} }|] - EnumWithType -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnumWithType} }#{enumType}|] + EnumWithType -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name}Enum { #{alternativesEnumWithType} }#{enumType}|] TypeAlias -> [i|#{exportPrefix exportMode}type #{typeNameModifier name}#{getGenericBrackets genericVariables} = #{alternatives};|] 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 + enumType = [i|\n\ntype #{name} = keyof typeof #{typeNameModifier name}Enum;|] :: T.Text toEnumName = T.replace "\"" "" formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceName genericVariables (filter (not . isNoEmitTypeScriptField) -> members) maybeDoc) = @@ -57,18 +57,16 @@ exportPrefix ExportNone = "" -- | 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 + _ = Nothing removeReferencesToRemovedNames :: [String] -> TSDeclaration -> TSDeclaration removeReferencesToRemovedNames removedNames decl@(TSTypeAlternatives {..}) = decl { alternativeTypes = [x | x <- alternativeTypes, not (x `L.elem` removedNames)] } @@ -78,20 +76,6 @@ formatTSDeclarations' options allDeclarations = & 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 - makeDocPrefix :: Maybe String -> String makeDocPrefix maybeDoc = case maybeDoc of Nothing -> "" From 07bc4923850d0b12b14496f132ee8ffdc4d215f3 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 30 Nov 2023 20:39:22 -0700 Subject: [PATCH 197/208] Comment Unit test for now --- test/Formatting.hs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/Formatting.hs b/test/Formatting.hs index b4953e2..35ab299 100644 --- a/test/Formatting.hs +++ b/test/Formatting.hs @@ -5,6 +5,7 @@ module Formatting (tests) where import Control.Exception import Data.Aeson (defaultOptions) +import Data.Aeson.TH import Data.Aeson.TypeScript.TH import Data.Proxy import Data.String.Interpolate @@ -19,6 +20,7 @@ $(deriveTypeScript defaultOptions ''D2) data Unit = U deriving (Eq, Show) $(deriveTypeScript defaultOptions ''Unit) +$(deriveJSON defaultOptions ''Unit) data PrimeInType' = PrimeInType $(deriveTypeScript defaultOptions ''PrimeInType') @@ -64,9 +66,9 @@ tests = describe "Formatting" $ do enum D2 { S2, F2 }|] - it "should generate a TS Enum from unit" $ - formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @Unit Proxy) `shouldBe` - [__i|enum Unit { U }|] + -- it "should generate a TS Enum from unit" $ + -- formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @Unit Proxy) `shouldBe` + -- [__i|enum Unit { U }|] describe "and the EnumWithType format option is set" $ it "should generate a TS Enum with a type declaration" $ From 5afd1649ac1dcfed2a1a92312c3c182a20b0cf83 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 30 Nov 2023 21:58:59 -0700 Subject: [PATCH 198/208] Fix bug in getDeclarationName --- src/Data/Aeson/TypeScript/Formatting.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index f2464e4..50a9544 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -66,7 +66,7 @@ formatTSDeclarations' options allDeclarations = getDeclarationName :: TSDeclaration -> Maybe String getDeclarationName (TSInterfaceDeclaration {..}) = Just interfaceName getDeclarationName (TSTypeAlternatives {..}) = Just typeName - _ = Nothing + getDeclarationName _ = Nothing removeReferencesToRemovedNames :: [String] -> TSDeclaration -> TSDeclaration removeReferencesToRemovedNames removedNames decl@(TSTypeAlternatives {..}) = decl { alternativeTypes = [x | x <- alternativeTypes, not (x `L.elem` removedNames)] } From 7933c644a485c07b7f0fc90daddd2ee59f6039f1 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 30 Nov 2023 21:59:31 -0700 Subject: [PATCH 199/208] Fix a couple warnings --- src/Data/Aeson/TypeScript/Formatting.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index 50a9544..5590433 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -42,7 +42,7 @@ formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceNa 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}|] + formatTSField (TSField optional name typ maybeDoc') = makeDocPrefix maybeDoc' <> [i|#{name}#{if optional then ("?" :: String) else ""}: #{typ}|] formatTSDeclaration _ (TSRawDeclaration text) = text @@ -93,9 +93,11 @@ getGenericBrackets xs = [i|<#{T.intercalate ", " (fmap T.pack xs)}>|] 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 From 2e5c1234e770877ce62a18e23a8dc811ade25079 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 30 Nov 2023 21:59:53 -0700 Subject: [PATCH 200/208] Add a better check for enum mode --- aeson-typescript.cabal | 3 ++- package.yaml | 1 + src/Data/Aeson/TypeScript/Formatting.hs | 28 ++++++++++++++++++++----- test/Formatting.hs | 10 ++++----- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index aead56b..139cbd6 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -1,6 +1,6 @@ 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 @@ -67,6 +67,7 @@ library build-depends: aeson , base >=4.7 && <5 + , bytestring , containers , mtl , string-interpolate diff --git a/package.yaml b/package.yaml index 3821c55..a1e476d 100644 --- a/package.yaml +++ b/package.yaml @@ -35,6 +35,7 @@ tested-with: dependencies: - aeson - base >= 4.7 && < 5 +- bytestring - containers - mtl - string-interpolate diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index 5590433..fea6941 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -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 -> TSDeclaration -> String 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} }|] + where + alternativesEnum = T.intercalate ", " $ [toEnumName 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) + + -- 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 - 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}Enum;|] :: T.Text toEnumName = T.replace "\"" "" formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceName genericVariables (filter (not . isNoEmitTypeScriptField) -> members) maybeDoc) = diff --git a/test/Formatting.hs b/test/Formatting.hs index 35ab299..ab3c770 100644 --- a/test/Formatting.hs +++ b/test/Formatting.hs @@ -5,7 +5,6 @@ module Formatting (tests) where import Control.Exception import Data.Aeson (defaultOptions) -import Data.Aeson.TH import Data.Aeson.TypeScript.TH import Data.Proxy import Data.String.Interpolate @@ -20,7 +19,6 @@ $(deriveTypeScript defaultOptions ''D2) data Unit = U deriving (Eq, Show) $(deriveTypeScript defaultOptions ''Unit) -$(deriveJSON defaultOptions ''Unit) data PrimeInType' = PrimeInType $(deriveTypeScript defaultOptions ''PrimeInType') @@ -66,9 +64,11 @@ tests = describe "Formatting" $ do enum D2 { S2, F2 }|] - -- it "should generate a TS Enum from unit" $ - -- formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @Unit Proxy) `shouldBe` - -- [__i|enum Unit { U }|] + it "should generate a TS Enum from unit" $ + formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @Unit Proxy) `shouldBe` + [__i|type Unit = IU; + + type IU = void[];|] describe "and the EnumWithType format option is set" $ it "should generate a TS Enum with a type declaration" $ From e39ae7c19e0a661fac351ec79f00795e45079cde Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Thu, 30 Nov 2023 22:08:45 -0700 Subject: [PATCH 201/208] Add CHANGELOG entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79f651a..eaf6f71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +* Fix a bug which caused enum formatting mode to turn off when multiple declarations were provided (#41) + ## 0.6.0.0 * New word instances: Word, Word16, Word32, Word64 From 007e161fb59191010d175479bb8e4e216072db16 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 5 Dec 2023 19:13:45 -0800 Subject: [PATCH 202/208] Always include string in enums to match aeson --- src/Data/Aeson/TypeScript/Formatting.hs | 2 +- test/Formatting.hs | 25 +++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index fea6941..9412b53 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -28,7 +28,7 @@ formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVar mainDeclaration = case chooseTypeAlternativesFormat typeAlternativesFormat of Enum -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnum} }|] where - alternativesEnum = T.intercalate ", " $ [toEnumName entry | entry <- T.pack <$> names] + 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] diff --git a/test/Formatting.hs b/test/Formatting.hs index ab3c770..a86f948 100644 --- a/test/Formatting.hs +++ b/test/Formatting.hs @@ -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 @@ -17,9 +17,14 @@ $(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') @@ -56,25 +61,33 @@ tests = describe "Formatting" $ do 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" }|] it "should generate a TS Enum with multiple" $ formatTSDeclarations' (defaultFormattingOptions { typeAlternativesFormat = Enum }) (getTypeScriptDeclarations @D Proxy <> getTypeScriptDeclarations @D2 Proxy) `shouldBe` - [__i|enum D { S, F } + [__i|enum D { S="S", F="F" } - enum D2 { S2, F2 }|] + enum D2 { S2="S2", F2="F2" }|] - it "should generate a TS Enum from unit" $ + 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[];|] - describe "and the EnumWithType format option is set" $ + 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 it "throws an error" $ do From 78bbf423ef445c9b76df4f41dc6a970596a19284 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 6 Dec 2023 15:56:51 -0800 Subject: [PATCH 203/208] Release 0.6.1.0 --- CHANGELOG.md | 3 +++ aeson-typescript.cabal | 2 +- package.yaml | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eaf6f71..a5020c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,10 @@ ## 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 diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 139cbd6..1caf1f6 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- 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 category: Text, Web, JSON diff --git a/package.yaml b/package.yaml index a1e476d..9de924e 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: aeson-typescript -version: 0.6.0.0 +version: 0.6.1.0 github: "codedownio/aeson-typescript" license: BSD3 category: Text, Web, JSON From 5802281709c959a6de339a7883dfd8082c673475 Mon Sep 17 00:00:00 2001 From: jjkv Date: Tue, 9 Jan 2024 15:26:38 -0500 Subject: [PATCH 204/208] export type variables 4 through 10 --- src/Data/Aeson/TypeScript/TH.hs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 7cd0e49..4663a8f 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -137,6 +137,13 @@ module Data.Aeson.TypeScript.TH ( , T1(..) , T2(..) , T3(..) + , T4(..) + , T5(..) + , T6(..) + , T7(..) + , T8(..) + , T9(..) + , T10(..) , module Data.Aeson.TypeScript.Instances ) where From 02b279563d5780b3d07008a2690bf275c48d7185 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 10 Jan 2024 13:30:51 -0800 Subject: [PATCH 205/208] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5020c4..729808f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 0.6.2.0 + +* Expose generic type constructors `T4` through `T10`. (We only exposed `T`, `T1`, `T2`, and `T3` before.) + ## 0.6.1.0 * Fix a bug which caused enum formatting mode to turn off when multiple declarations were provided (#41) From b834e60c91295afba6d7f92e87e57de19dc7cc49 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 10 Jan 2024 14:02:32 -0800 Subject: [PATCH 206/208] Bump version to 0.6.2.0 --- aeson-typescript.cabal | 2 +- package.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 1caf1f6..6ae61ba 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: aeson-typescript -version: 0.6.1.0 +version: 0.6.2.0 synopsis: Generate TypeScript definition files from your ADTs description: Please see the README on Github at category: Text, Web, JSON diff --git a/package.yaml b/package.yaml index 9de924e..8b52337 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: aeson-typescript -version: 0.6.1.0 +version: 0.6.2.0 github: "codedownio/aeson-typescript" license: BSD3 category: Text, Web, JSON From 9a07b612e953adf95d229d92455698b674949d0c Mon Sep 17 00:00:00 2001 From: Ian Shipman Date: Tue, 5 Jan 2021 13:22:19 -0600 Subject: [PATCH 207/208] Changes approach to optional fields --- CHANGELOG.md | 5 ++ src/Data/Aeson/TypeScript/Formatting.hs | 95 +++++++++++++------------ stack.yaml | 2 +- stack.yaml.lock | 8 +-- 4 files changed, 59 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 729808f..54445bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +## 0.7.0.0 + +Represents optional fields (according to the Haskell model) as required fields +with type `A | null` rather than optional fields. + ## 0.6.2.0 * Expose generic type constructors `T4` through `T10`. (We only exposed `T`, `T1`, `T2`, and `T3` before.) diff --git a/src/Data/Aeson/TypeScript/Formatting.hs b/src/Data/Aeson/TypeScript/Formatting.hs index 9412b53..ca1457c 100644 --- a/src/Data/Aeson/TypeScript/Formatting.hs +++ b/src/Data/Aeson/TypeScript/Formatting.hs @@ -15,58 +15,58 @@ import qualified Data.Text as T import Data.Monoid #endif - -- | Same as 'formatTSDeclarations'', but uses default formatting options. formatTSDeclarations :: [TSDeclaration] -> String formatTSDeclarations = formatTSDeclarations' defaultFormattingOptions -- | Format a single TypeScript declaration. This version accepts a FormattingOptions object in case you want more control over the output. formatTSDeclaration :: FormattingOptions -> TSDeclaration -> String -formatTSDeclaration (FormattingOptions {..}) (TSTypeAlternatives name genericVariables names maybeDoc) = - makeDocPrefix maybeDoc <> mainDeclaration +formatTSDeclaration (FormattingOptions{..}) (TSTypeAlternatives name genericVariables names maybeDoc) = + makeDocPrefix maybeDoc <> mainDeclaration where mainDeclaration = case chooseTypeAlternativesFormat typeAlternativesFormat of - Enum -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnum} }|] - 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) + Enum -> [i|#{exportPrefix exportMode}enum #{typeNameModifier name} { #{alternativesEnum} }|] + 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) -- Only allow certain formats if some checks pass chooseTypeAlternativesFormat Enum - | all isDoubleQuotedString names = Enum - | otherwise = TypeAlias + | all isDoubleQuotedString names = Enum + | otherwise = TypeAlias chooseTypeAlternativesFormat EnumWithType - | all isDoubleQuotedString names = EnumWithType - | otherwise = TypeAlias + | all isDoubleQuotedString names = EnumWithType + | otherwise = TypeAlias chooseTypeAlternativesFormat x = x isDoubleQuotedString s = case A.eitherDecode (BL8.pack s) of - Right (A.String _) -> True - _ -> False + Right (A.String _) -> True + _ -> False toEnumName = T.replace "\"" "" - -formatTSDeclaration (FormattingOptions {..}) (TSInterfaceDeclaration interfaceName genericVariables (filter (not . isNoEmitTypeScriptField) -> members) maybeDoc) = - makeDocPrefix maybeDoc <> [i|#{exportPrefix exportMode}interface #{modifiedInterfaceName}#{getGenericBrackets genericVariables} { +formatTSDeclaration (FormattingOptions{..}) (TSInterfaceDeclaration interfaceName genericVariables (filter (not . isNoEmitTypeScriptField) -> members) maybeDoc) = + makeDocPrefix maybeDoc + <> [i|#{exportPrefix exportMode}interface #{modifiedInterfaceName}#{getGenericBrackets genericVariables} { #{ls} -}|] where - 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}|] +}|] + where + 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}: #{typ}#{if optional then ("| null" :: String) else ""}|] formatTSDeclaration _ (TSRawDeclaration text) = text indentTo :: Int -> T.Text -> T.Text indentTo numIndentSpaces input = T.intercalate "\n" [padding <> line | line <- T.splitOn "\n" input] - where padding = T.replicate numIndentSpaces " " + where + padding = T.replicate numIndentSpaces " " exportPrefix :: ExportMode -> String exportPrefix ExportEach = "export " @@ -75,32 +75,35 @@ exportPrefix ExportNone = "" -- | 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 options) - & T.intercalate "\n\n" - & T.unpack + declarations + & fmap (T.pack . formatTSDeclaration options) + & T.intercalate "\n\n" + & T.unpack where removedDeclarationNames = mapMaybe getDeclarationName (filter isNoEmitTypeScriptDeclaration allDeclarations) where getDeclarationName :: TSDeclaration -> Maybe String - getDeclarationName (TSInterfaceDeclaration {..}) = Just interfaceName - getDeclarationName (TSTypeAlternatives {..}) = Just typeName + 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)] } + removeReferencesToRemovedNames removedNames decl@(TSTypeAlternatives{..}) = decl{alternativeTypes = [x | x <- alternativeTypes, not (x `L.elem` removedNames)]} removeReferencesToRemovedNames _ x = x - declarations = allDeclarations - & filter (not . isNoEmitTypeScriptDeclaration) - & fmap (removeReferencesToRemovedNames removedDeclarationNames) + declarations = + allDeclarations + & filter (not . isNoEmitTypeScriptDeclaration) + & fmap (removeReferencesToRemovedNames removedDeclarationNames) makeDocPrefix :: Maybe String -> String makeDocPrefix maybeDoc = case maybeDoc of - Nothing -> "" - Just (T.pack -> text) -> ["// " <> line | line <- T.splitOn "\n" text] - & T.intercalate "\n" - & (<> "\n") - & T.unpack + Nothing -> "" + Just (T.pack -> text) -> + ["// " <> line | line <- T.splitOn "\n" text] + & T.intercalate "\n" + & (<> "\n") + & T.unpack getGenericBrackets :: [String] -> String getGenericBrackets [] = "" @@ -112,10 +115,10 @@ noEmitTypeScriptAnnotation :: String noEmitTypeScriptAnnotation = "@no-emit-typescript" isNoEmitTypeScriptField :: TSField -> Bool -isNoEmitTypeScriptField (TSField {fieldDoc=(Just doc)}) = noEmitTypeScriptAnnotation `L.isInfixOf` doc +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 (TSInterfaceDeclaration{interfaceDoc = (Just doc)}) = noEmitTypeScriptAnnotation `L.isInfixOf` doc +isNoEmitTypeScriptDeclaration (TSTypeAlternatives{typeDoc = (Just doc)}) = noEmitTypeScriptAnnotation `L.isInfixOf` doc isNoEmitTypeScriptDeclaration _ = False diff --git a/stack.yaml b/stack.yaml index bfc9a75..8e34e8c 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,5 +1,5 @@ -resolver: nightly-2023-11-14 +resolver: lts-22.7 packages: - . diff --git a/stack.yaml.lock b/stack.yaml.lock index dd4da80..5fe18da 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -6,7 +6,7 @@ packages: [] snapshots: - completed: - sha256: 0eaacfc9de6b0ab46ab6026166d2ba7718ab06a8612086b3ee21d7667e682df0 - size: 698974 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2023/11/14.yaml - original: nightly-2023-11-14 + sha256: 7b975b104cb3dbf0c297dfd01f936a4d2ee523241dd0b1ae960522b833fe3027 + size: 714096 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/7.yaml + original: lts-22.7 From 987b8eb40c171d78f94b1f1aed665f18a60d84ee Mon Sep 17 00:00:00 2001 From: Brad Sherman Date: Mon, 29 Jan 2024 11:02:51 -0600 Subject: [PATCH 208/208] Remove OmitNothingFields test --- aeson-typescript.cabal | 1 - test/OmitNothingFields.hs | 27 -------------------------- test/Spec.hs | 40 ++++++++++++++++++--------------------- test/assets/package.json | 4 ---- test/assets/yarn.lock | 37 +++++++++++++++++++++++++++++++----- 5 files changed, 50 insertions(+), 59 deletions(-) delete mode 100644 test/OmitNothingFields.hs diff --git a/aeson-typescript.cabal b/aeson-typescript.cabal index 1c86874..882d685 100644 --- a/aeson-typescript.cabal +++ b/aeson-typescript.cabal @@ -94,7 +94,6 @@ test-suite aeson-typescript-tests NoOmitNothingFields ObjectWithSingleFieldNoTagSingleConstructors ObjectWithSingleFieldTagSingleConstructors - OmitNothingFields OpenTypeFamilies TaggedObjectNoTagSingleConstructors TaggedObjectTagSingleConstructors diff --git a/test/OmitNothingFields.hs b/test/OmitNothingFields.hs deleted file mode 100644 index 360f2a1..0000000 --- a/test/OmitNothingFields.hs +++ /dev/null @@ -1,27 +0,0 @@ - -module OmitNothingFields (main, tests) where - -import Data.Aeson as A -import Data.Aeson.TypeScript.TH -import Data.Aeson.TypeScript.Types -import Data.Proxy -import Test.Hspec -import TestBoilerplate - -$(testDeclarations "OmitNothingFields" (A.defaultOptions {omitNothingFields=True})) - -main :: IO () -main = hspec $ describe "OmitNothingFields" $ do - it "encodes as expected" $ do - let decls = getTypeScriptDeclarations (Proxy :: Proxy Optional) - - decls `shouldBe` [TSInterfaceDeclaration { - interfaceName = "Optional" - , interfaceGenericVariables = [] - , interfaceMembers = [ - TSField True "optionalInt" "number" Nothing - ] - , interfaceDoc = Nothing - }] - - tests diff --git a/test/Spec.hs b/test/Spec.hs index d7f7548..a8c1a70 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -5,17 +5,16 @@ module Main where import Test.Hspec import qualified Basic +import qualified ClosedTypeFamilies import qualified Formatting import qualified Generic import qualified GetDoc import qualified HigherKind -import qualified ClosedTypeFamilies import qualified LegalNameSpec import qualified NoOmitNothingFields import qualified ObjectWithSingleFieldNoTagSingleConstructors import qualified ObjectWithSingleFieldTagSingleConstructors -import qualified OmitNothingFields import qualified TaggedObjectNoTagSingleConstructors import qualified TaggedObjectTagSingleConstructors import qualified TwoElemArrayNoTagSingleConstructors @@ -24,27 +23,24 @@ import qualified UntaggedNoTagSingleConstructors import qualified UntaggedTagSingleConstructors import qualified UnwrapUnaryRecords - main :: IO () main = hspec $ parallel $ do - Basic.tests - ClosedTypeFamilies.tests - Formatting.tests - Generic.tests + Basic.tests + ClosedTypeFamilies.tests + Formatting.tests + Generic.tests + HigherKind.tests + LegalNameSpec.tests + NoOmitNothingFields.allTests + ObjectWithSingleFieldNoTagSingleConstructors.tests + ObjectWithSingleFieldTagSingleConstructors.tests + TaggedObjectNoTagSingleConstructors.tests + TaggedObjectTagSingleConstructors.tests + TwoElemArrayNoTagSingleConstructors.tests + TwoElemArrayTagSingleConstructors.tests + UntaggedNoTagSingleConstructors.tests + UntaggedTagSingleConstructors.tests + UnwrapUnaryRecords.allTests #if MIN_VERSION_template_haskell(2,18,0) - GetDoc.tests + GetDoc.tests #endif - HigherKind.tests - - LegalNameSpec.tests - NoOmitNothingFields.allTests - ObjectWithSingleFieldNoTagSingleConstructors.tests - ObjectWithSingleFieldTagSingleConstructors.tests - OmitNothingFields.tests - TaggedObjectNoTagSingleConstructors.tests - TaggedObjectTagSingleConstructors.tests - TwoElemArrayNoTagSingleConstructors.tests - TwoElemArrayTagSingleConstructors.tests - UntaggedNoTagSingleConstructors.tests - UntaggedTagSingleConstructors.tests - UnwrapUnaryRecords.allTests diff --git a/test/assets/package.json b/test/assets/package.json index 29171df..4c6aec1 100644 --- a/test/assets/package.json +++ b/test/assets/package.json @@ -1,9 +1,5 @@ { "private": true, - "scripts": { - - }, - "dependencies": {}, "devDependencies": { "typescript": "2.5.3" } diff --git a/test/assets/yarn.lock b/test/assets/yarn.lock index faf7597..45ab7c0 100644 --- a/test/assets/yarn.lock +++ b/test/assets/yarn.lock @@ -1,7 +1,34 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! +__metadata: + version: 8 + cacheKey: 10c0 -typescript@2.5.3: - version "2.5.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.3.tgz#df3dcdc38f3beb800d4bc322646b04a3f6ca7f0d" +"root-workspace-0b6124@workspace:.": + version: 0.0.0-use.local + resolution: "root-workspace-0b6124@workspace:." + dependencies: + typescript: "npm:2.5.3" + languageName: unknown + linkType: soft + +"typescript@npm:2.5.3": + version: 2.5.3 + resolution: "typescript@npm:2.5.3" + bin: + tsc: ./bin/tsc + tsserver: ./bin/tsserver + checksum: 56092000fa36c9af67e2be79722e95e72c35450dba3e830afae602d38c009af18d1aed07bf545dee446adb598812612acd73abd81955687b65343632fa159a1b + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A2.5.3#optional!builtin": + version: 2.5.3 + resolution: "typescript@patch:typescript@npm%3A2.5.3#optional!builtin::version=2.5.3&hash=3bafbf" + bin: + tsc: ./bin/tsc + tsserver: ./bin/tsserver + checksum: aaf016a1f934cb088930e7b5ffeea9a1d910aba4df5a5e8ff89f6bc742da6776f860ac9deebb74849466462b5c73f4392aeb0a0d806d8d8fd813652e38ae761f + languageName: node + linkType: hard