diff --git a/CHANGELOG.md b/CHANGELOG.md index fe6650a37..4c0fc05be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ Other improvements: current workspace. - #1110: `spago publish` will now install packages returned by the registry solver before trying to build with them. +- Spago no longer ignores config fields that it doesn't recognize. This should + help catch typos in field names. ## [0.21.0] - 2023-05-04 diff --git a/core/src/Config.purs b/core/src/Config.purs index 6eff2e149..5dd95970e 100644 --- a/core/src/Config.purs +++ b/core/src/Config.purs @@ -43,20 +43,21 @@ import Data.Array.NonEmpty as NonEmptyArray import Data.Codec as Codec import Data.Codec.JSON as CJ import Data.Codec.JSON.Record as CJ.Record +import Data.Codec.JSON.Strict as CJS import Data.Codec.JSON.Sum as CJ.Sum import Data.Either as Either import Data.List as List import Data.Map as Map import Data.Profunctor as Profunctor import Partial.Unsafe (unsafeCrashWith) -import Registry.Internal.Codec as Internal.Codec +import Registry.Internal.Codec as Reg.Internal.Codec +import Registry.Internal.Parsing as Reg.Internal.Parsing import Registry.License as License import Registry.Location as Location import Registry.PackageName as PackageName import Registry.Range as Range import Registry.Sha256 as Sha256 import Registry.Version as Version -import Type.Proxy (Proxy(..)) type Config = { package :: Maybe PackageConfig @@ -64,10 +65,10 @@ type Config = } configCodec :: CJ.Codec Config -configCodec = CJ.object - $ CJ.recordPropOptional (Proxy @"package") packageConfigCodec - $ CJ.recordPropOptional (Proxy @"workspace") workspaceConfigCodec - $ CJ.record +configCodec = CJS.objectStrict + $ CJS.recordPropOptional @"package" packageConfigCodec + $ CJS.recordPropOptional @"workspace" workspaceConfigCodec + $ CJS.record type PackageConfig = { name :: PackageName @@ -81,16 +82,16 @@ type PackageConfig = } packageConfigCodec :: CJ.Codec PackageConfig -packageConfigCodec = CJ.named "PackageConfig" $ CJ.object - $ CJ.recordProp (Proxy @"name") PackageName.codec - $ CJ.recordPropOptional (Proxy @"description") CJ.string - $ CJ.recordProp (Proxy @"dependencies") dependenciesCodec - $ CJ.recordPropOptional (Proxy @"build") packageBuildOptionsCodec - $ CJ.recordPropOptional (Proxy @"bundle") bundleConfigCodec - $ CJ.recordPropOptional (Proxy @"run") runConfigCodec - $ CJ.recordPropOptional (Proxy @"test") testConfigCodec - $ CJ.recordPropOptional (Proxy @"publish") publishConfigCodec - $ CJ.record +packageConfigCodec = CJ.named "PackageConfig" $ CJS.objectStrict + $ CJS.recordProp @"name" PackageName.codec + $ CJS.recordPropOptional @"description" CJ.string + $ CJS.recordProp @"dependencies" dependenciesCodec + $ CJS.recordPropOptional @"build" packageBuildOptionsCodec + $ CJS.recordPropOptional @"bundle" bundleConfigCodec + $ CJS.recordPropOptional @"run" runConfigCodec + $ CJS.recordPropOptional @"test" testConfigCodec + $ CJS.recordPropOptional @"publish" publishConfigCodec + $ CJS.record type PublishConfig = { version :: Version @@ -101,13 +102,51 @@ type PublishConfig = } publishConfigCodec :: CJ.Codec PublishConfig -publishConfigCodec = CJ.named "PublishConfig" $ CJ.object - $ CJ.recordProp (Proxy @"version") Version.codec - $ CJ.recordProp (Proxy @"license") License.codec - $ CJ.recordPropOptional (Proxy @"location") Location.codec - $ CJ.recordPropOptional (Proxy @"include") (CJ.array CJ.string) - $ CJ.recordPropOptional (Proxy @"exclude") (CJ.array CJ.string) - $ CJ.record +publishConfigCodec = CJ.named "PublishConfig" $ CJS.objectStrict + $ CJS.recordProp @"version" Version.codec + $ CJS.recordProp @"license" License.codec + $ CJS.recordPropOptional @"location" publishLocationCodec + $ CJS.recordPropOptional @"include" (CJ.array CJ.string) + $ CJS.recordPropOptional @"exclude" (CJ.array CJ.string) + $ CJS.record + +-- This codec duplicates `Location.codec` from the Registry library, but with +-- strict parsing of fields, so that we would error out on unknown fields, thus +-- catching typos in field names. We do not want to modify the original codec in +-- the Registry library, because it's used for network communication, not for +-- reading user input, and therefore it's more important there to ignore unknown +-- fields for backwards compatibiility. +publishLocationCodec :: CJ.Codec Location +publishLocationCodec = CJ.named "Publish Location" $ Codec.codec' decode encode + where + decode json = + (Location.Git <$> Codec.decode gitCodec json) + <|> (Location.GitHub <$> Codec.decode githubCodec json) + + encode = case _ of + Location.Git git -> CJ.encode gitCodec git + Location.GitHub github -> CJ.encode githubCodec github + + githubCodec :: CJ.Codec Location.GitHubData + githubCodec = Profunctor.dimap toJsonRep fromJsonRep $ CJ.named "GitHub" $ CJ.Record.objectStrict + { githubOwner: CJ.string + , githubRepo: CJ.string + , subdir: CJ.Record.optional CJ.string + } + where + toJsonRep { owner, repo, subdir } = { githubOwner: owner, githubRepo: repo, subdir } + fromJsonRep { githubOwner, githubRepo, subdir } = { owner: githubOwner, repo: githubRepo, subdir } + + gitCodec :: CJ.Codec Location.GitData + gitCodec = Profunctor.dimap toJsonRep fromJsonRep $ CJ.named "Git" $ CJ.Record.objectStrict + { gitUrl: Reg.Internal.Codec.parsedString Reg.Internal.Parsing.gitUrl + , subdir: CJ.Record.optional CJ.string + } + where + -- The JSON representation of the GitHub type uses 'gitUrl', but in PureScript + -- we use 'url' for convenience. + toJsonRep { url, subdir } = { gitUrl: url, subdir } + fromJsonRep { gitUrl, subdir } = { url: gitUrl, subdir } type RunConfig = { main :: Maybe String @@ -115,10 +154,10 @@ type RunConfig = } runConfigCodec :: CJ.Codec RunConfig -runConfigCodec = CJ.named "RunConfig" $ CJ.object - $ CJ.recordPropOptional (Proxy @"main") CJ.string - $ CJ.recordPropOptional (Proxy @"execArgs") (CJ.array CJ.string) - $ CJ.record +runConfigCodec = CJ.named "RunConfig" $ CJS.objectStrict + $ CJS.recordPropOptional @"main" CJ.string + $ CJS.recordPropOptional @"execArgs" (CJ.array CJ.string) + $ CJS.record type TestConfig = { main :: String @@ -130,14 +169,14 @@ type TestConfig = } testConfigCodec :: CJ.Codec TestConfig -testConfigCodec = CJ.named "TestConfig" $ CJ.object - $ CJ.recordProp (Proxy @"main") CJ.string - $ CJ.recordPropOptional (Proxy @"execArgs") (CJ.array CJ.string) - $ CJ.recordPropOptional (Proxy @"censorTestWarnings") censorBuildWarningsCodec - $ CJ.recordPropOptional (Proxy @"strict") CJ.boolean - $ CJ.recordPropOptional (Proxy @"pedanticPackages") CJ.boolean - $ CJ.recordProp (Proxy @"dependencies") dependenciesCodec - $ CJ.record +testConfigCodec = CJ.named "TestConfig" $ CJS.objectStrict + $ CJS.recordProp @"main" CJ.string + $ CJS.recordPropOptional @"execArgs" (CJ.array CJ.string) + $ CJS.recordPropOptional @"censorTestWarnings" censorBuildWarningsCodec + $ CJS.recordPropOptional @"strict" CJ.boolean + $ CJS.recordPropOptional @"pedanticPackages" CJ.boolean + $ CJS.recordProp @"dependencies" dependenciesCodec + $ CJS.record type BackendConfig = { cmd :: String @@ -145,10 +184,10 @@ type BackendConfig = } backendConfigCodec :: CJ.Codec BackendConfig -backendConfigCodec = CJ.named "BackendConfig" $ CJ.object - $ CJ.recordProp (Proxy @"cmd") CJ.string - $ CJ.recordPropOptional (Proxy @"args") (CJ.array CJ.string) - $ CJ.record +backendConfigCodec = CJ.named "BackendConfig" $ CJS.objectStrict + $ CJS.recordProp @"cmd" CJ.string + $ CJS.recordPropOptional @"args" (CJ.array CJ.string) + $ CJS.record type PackageBuildOptionsInput = { censorProjectWarnings :: Maybe CensorBuildWarnings @@ -157,11 +196,11 @@ type PackageBuildOptionsInput = } packageBuildOptionsCodec :: CJ.Codec PackageBuildOptionsInput -packageBuildOptionsCodec = CJ.named "PackageBuildOptionsInput" $ CJ.object - $ CJ.recordPropOptional (Proxy @"censorProjectWarnings") censorBuildWarningsCodec - $ CJ.recordPropOptional (Proxy @"strict") CJ.boolean - $ CJ.recordPropOptional (Proxy @"pedanticPackages") CJ.boolean - $ CJ.record +packageBuildOptionsCodec = CJ.named "PackageBuildOptionsInput" $ CJS.objectStrict + $ CJS.recordPropOptional @"censorProjectWarnings" censorBuildWarningsCodec + $ CJS.recordPropOptional @"strict" CJ.boolean + $ CJS.recordPropOptional @"pedanticPackages" CJ.boolean + $ CJS.record type BundleConfig = { minify :: Maybe Boolean @@ -173,17 +212,19 @@ type BundleConfig = } bundleConfigCodec :: CJ.Codec BundleConfig -bundleConfigCodec = CJ.named "BundleConfig" $ CJ.object - $ CJ.recordPropOptional (Proxy @"minify") CJ.boolean - $ CJ.recordPropOptional (Proxy @"module") CJ.string - $ CJ.recordPropOptional (Proxy @"outfile") CJ.string - $ CJ.recordPropOptional (Proxy @"platform") bundlePlatformCodec - $ CJ.recordPropOptional (Proxy @"type") bundleTypeCodec - $ CJ.recordPropOptional (Proxy @"extraArgs") (CJ.array CJ.string) - $ CJ.record +bundleConfigCodec = CJ.named "BundleConfig" $ CJS.objectStrict + $ CJS.recordPropOptional @"minify" CJ.boolean + $ CJS.recordPropOptional @"module" CJ.string + $ CJS.recordPropOptional @"outfile" CJ.string + $ CJS.recordPropOptional @"platform" bundlePlatformCodec + $ CJS.recordPropOptional @"type" bundleTypeCodec + $ CJS.recordPropOptional @"extraArgs" (CJ.array CJ.string) + $ CJS.record data BundlePlatform = BundleNode | BundleBrowser +derive instance Eq BundlePlatform + instance Show BundlePlatform where show = case _ of BundleNode -> "node" @@ -202,6 +243,8 @@ bundlePlatformCodec = CJ.Sum.enumSum show (parsePlatform) -- App bundles with a main fn, while Module does not include a main. data BundleType = BundleApp | BundleModule +derive instance Eq BundleType + instance Show BundleType where show = case _ of BundleApp -> "app" @@ -238,7 +281,7 @@ instance Monoid Dependencies where dependenciesCodec :: CJ.Codec Dependencies dependenciesCodec = Profunctor.dimap to from $ CJ.array dependencyCodec where - packageSingletonCodec = Internal.Codec.packageMap spagoRangeCodec + packageSingletonCodec = Reg.Internal.Codec.packageMap spagoRangeCodec to :: Dependencies -> Array (Either PackageName (Map PackageName Range)) to (Dependencies deps) = @@ -291,12 +334,12 @@ type WorkspaceConfig = } workspaceConfigCodec :: CJ.Codec WorkspaceConfig -workspaceConfigCodec = CJ.named "WorkspaceConfig" $ CJ.object - $ CJ.recordPropOptional (Proxy @"packageSet") setAddressCodec - $ CJ.recordPropOptional (Proxy @"backend") backendConfigCodec - $ CJ.recordPropOptional (Proxy @"buildOpts") buildOptionsCodec - $ CJ.recordPropOptional (Proxy @"extraPackages") (Internal.Codec.packageMap extraPackageCodec) - $ CJ.record +workspaceConfigCodec = CJ.named "WorkspaceConfig" $ CJS.objectStrict + $ CJS.recordPropOptional @"packageSet" setAddressCodec + $ CJS.recordPropOptional @"backend" backendConfigCodec + $ CJS.recordPropOptional @"buildOpts" buildOptionsCodec + $ CJS.recordPropOptional @"extraPackages" (Reg.Internal.Codec.packageMap extraPackageCodec) + $ CJS.record type WorkspaceBuildOptionsInput = { output :: Maybe FilePath @@ -305,11 +348,11 @@ type WorkspaceBuildOptionsInput = } buildOptionsCodec :: CJ.Codec WorkspaceBuildOptionsInput -buildOptionsCodec = CJ.named "WorkspaceBuildOptionsInput" $ CJ.object - $ CJ.recordPropOptional (Proxy @"output") CJ.string - $ CJ.recordPropOptional (Proxy @"censorLibraryWarnings") censorBuildWarningsCodec - $ CJ.recordPropOptional (Proxy @"statVerbosity") statVerbosityCodec - $ CJ.record +buildOptionsCodec = CJ.named "WorkspaceBuildOptionsInput" $ CJS.objectStrict + $ CJS.recordPropOptional @"output" CJ.string + $ CJS.recordPropOptional @"censorLibraryWarnings" censorBuildWarningsCodec + $ CJS.recordPropOptional @"statVerbosity" statVerbosityCodec + $ CJS.record data CensorBuildWarnings = CensorAllWarnings @@ -361,13 +404,15 @@ warningCensorTestCodec = Codec.codec' decode encode byCode = ByCode <$> Codec.decode CJ.string json byPrefix = (ByMessagePrefix <<< _.byPrefix) <$> Codec.decode byMessagePrefixCodec json - byMessagePrefixCodec = CJ.named "ByMessagePrefix" $ CJ.Record.object { byPrefix: CJ.string } + byMessagePrefixCodec = CJ.named "ByMessagePrefix" $ CJ.Record.objectStrict { byPrefix: CJ.string } data StatVerbosity = NoStats | CompactStats | VerboseStats +derive instance Eq StatVerbosity + instance Show StatVerbosity where show = case _ of NoStats -> "NoStats" @@ -397,9 +442,9 @@ derive instance Eq SetAddress setAddressCodec :: CJ.Codec SetAddress setAddressCodec = Codec.codec' decode encode where - setFromRegistryCodec = CJ.named "SetFromRegistry" $ CJ.Record.object { registry: Version.codec } - setFromUrlCodec = CJ.named "SetFromUrl" $ CJ.Record.object { url: CJ.string, hash: CJ.Record.optional Sha256.codec } - setFromPathCodec = CJ.named "SetFromPath" $ CJ.Record.object { path: CJ.string } + setFromRegistryCodec = CJ.named "SetFromRegistry" $ CJ.Record.objectStrict { registry: Version.codec } + setFromUrlCodec = CJ.named "SetFromUrl" $ CJ.Record.objectStrict { url: CJ.string, hash: CJ.Record.optional Sha256.codec } + setFromPathCodec = CJ.named "SetFromPath" $ CJ.Record.objectStrict { path: CJ.string } encode (SetFromRegistry r) = CJ.encode setFromRegistryCodec r encode (SetFromUrl u) = CJ.encode setFromUrlCodec u @@ -427,7 +472,7 @@ extraPackageCodec = Codec.codec' decode encode type LocalPackage = { path :: FilePath } localPackageCodec :: CJ.Codec LocalPackage -localPackageCodec = CJ.named "LocalPackage" $ CJ.Record.object { path: CJ.string } +localPackageCodec = CJ.named "LocalPackage" $ CJ.Record.objectStrict { path: CJ.string } data RemotePackage = RemoteGitPackage GitPackage @@ -455,12 +500,12 @@ type GitPackage = } gitPackageCodec :: CJ.Codec GitPackage -gitPackageCodec = CJ.named "GitPackage" $ CJ.object - $ CJ.recordProp (Proxy @"git") CJ.string - $ CJ.recordProp (Proxy @"ref") CJ.string - $ CJ.recordPropOptional (Proxy @"subdir") CJ.string - $ CJ.recordPropOptional (Proxy @"dependencies") dependenciesCodec - $ CJ.record +gitPackageCodec = CJ.named "GitPackage" $ CJS.objectStrict + $ CJS.recordProp @"git" CJ.string + $ CJS.recordProp @"ref" CJ.string + $ CJS.recordPropOptional @"subdir" CJ.string + $ CJS.recordPropOptional @"dependencies" dependenciesCodec + $ CJS.record -- | The format of a legacy packages.json package set entry for an individual -- | package. @@ -471,8 +516,8 @@ type LegacyPackageSetEntry = } legacyPackageSetEntryCodec :: CJ.Codec LegacyPackageSetEntry -legacyPackageSetEntryCodec = CJ.named "LegacyPackageSetEntry" $ CJ.object - $ CJ.recordProp (Proxy @"repo") CJ.string - $ CJ.recordProp (Proxy @"version") CJ.string - $ CJ.recordProp (Proxy @"dependencies") (CJ.array PackageName.codec) - $ CJ.record +legacyPackageSetEntryCodec = CJ.named "LegacyPackageSetEntry" $ CJS.objectStrict + $ CJS.recordProp @"repo" CJ.string + $ CJS.recordProp @"version" CJ.string + $ CJS.recordProp @"dependencies" (CJ.array PackageName.codec) + $ CJS.record diff --git a/docs-search/client-halogen/spago.yaml b/docs-search/client-halogen/spago.yaml index 49cfe8ae9..eee42289e 100644 --- a/docs-search/client-halogen/spago.yaml +++ b/docs-search/client-halogen/spago.yaml @@ -38,7 +38,6 @@ package: bundle: type: "app" minify: true - sourceMaps: true module: Docs.Search.App outfile: "../../bin/docs-search-app.js" platform: browser @@ -47,3 +46,4 @@ package: # The node module is also considered deprecated and recommends using the upstream npm package punycode. So its an easy swap-in. # The extra / at the end is how you tell node and esbuild to override a builtin node package with a user-space package. - "--alias:punycode=punycode/" + - "--sourcemap" diff --git a/spago.lock b/spago.lock index 0cf2d5b83..b7968ba07 100644 --- a/spago.lock +++ b/spago.lock @@ -440,7 +440,6 @@ "posix-types", "prelude", "profunctor", - "psci-support", "record", "refs", "safe-coerce", @@ -704,7 +703,6 @@ "posix-types", "prelude", "profunctor", - "psci-support", "quickcheck", "random", "record", @@ -834,7 +832,6 @@ "prelude", "profunctor", "profunctor-lenses", - "psci-support", "quickcheck", "quickcheck-laws", "random", @@ -1011,7 +1008,7 @@ }, "package_set": { "address": { - "registry": "56.4.0" + "registry": "59.0.0" }, "compiler": ">=0.15.15 <0.16.0", "content": { @@ -1077,7 +1074,7 @@ "classnames": "2.0.0", "codec": "6.1.0", "codec-argonaut": "10.0.0", - "codec-json": "1.2.0", + "codec-json": "2.0.0", "colors": "7.0.1", "concur-core": "0.5.0", "concur-react": "0.5.0", @@ -1180,7 +1177,7 @@ "halogen-css": "10.0.0", "halogen-echarts-simple": "0.0.4", "halogen-formless": "4.0.3", - "halogen-helix": "1.0.0", + "halogen-helix": "1.0.1", "halogen-hooks": "0.6.3", "halogen-hooks-extra": "0.9.0", "halogen-infinite-scroll": "1.1.0", @@ -1307,9 +1304,11 @@ "oak-debug": "1.2.2", "object-maps": "0.3.0", "ocarina": "1.5.4", - "open-folds": "6.3.0", - "open-memoize": "6.1.0", - "open-pairing": "6.1.0", + "oooooooooorrrrrrrmm-lib": "0.0.1", + "open-folds": "6.4.0", + "open-memoize": "6.2.0", + "open-mkdirp-aff": "1.2.0", + "open-pairing": "6.2.0", "options": "7.0.0", "optparse": "5.0.1", "ordered-collections": "3.2.0", @@ -1332,12 +1331,12 @@ "pointed-list": "0.5.1", "polymorphic-vectors": "4.0.0", "posix-types": "6.0.0", - "postgresql": "2.0.17", + "postgresql": "2.0.19", "precise": "6.0.0", "precise-datetime": "7.0.0", "prelude": "6.0.1", "prettier-printer": "3.0.0", - "priority-queue": "0.1.0", + "priority-queue": "0.1.2", "profunctor": "6.0.1", "profunctor-lenses": "8.0.0", "protobuf": "4.3.0", @@ -1409,7 +1408,7 @@ "soundfonts": "4.1.0", "sparse-matrices": "2.0.1", "sparse-polynomials": "3.0.1", - "spec": "7.6.1", + "spec": "8.0.0", "spec-discovery": "8.3.0", "spec-mocha": "5.1.1", "spec-node": "0.0.1", @@ -1437,6 +1436,7 @@ "thermite-dom": "0.3.1", "these": "6.0.0", "threading": "0.0.3", + "tldr": "0.0.0", "toestand": "0.9.0", "transformation-matrix": "1.0.1", "transformers": "6.1.0", @@ -1478,7 +1478,7 @@ "vectors": "2.1.0", "versions": "7.0.0", "visx": "0.0.2", - "web-clipboard": "5.0.0", + "web-clipboard": "6.0.0", "web-cssom": "2.0.0", "web-cssom-view": "0.1.0", "web-dom": "6.0.0", @@ -1517,7 +1517,6 @@ } }, "extra_packages": { - "codec-json": "1.2.0", "dodo-printer": { "git": "https://github.com/natefaubion/purescript-dodo-printer.git", "ref": "v2.2.1", @@ -1686,7 +1685,7 @@ }, "registry-lib": { "git": "https://github.com/purescript/registry-dev.git", - "ref": "be0d7ffd1c16aa70e8f065a928e941ebc053c013", + "ref": "fc203a9e2a0b96a90ace20dc6958c157cbbca16b", "subdir": "lib" }, "search-trie": { @@ -1969,8 +1968,8 @@ }, "codec-json": { "type": "registry", - "version": "1.2.0", - "integrity": "sha256-59+uYYe/5uTFa/Q6EqF8ekvP/Y4SOjUNfwIqIYtNiGI=", + "version": "2.0.0", + "integrity": "sha256-NLm5BmDZKKexQwtnoZ/NI4yI16srsqIe0M7NdlqB96U=", "dependencies": [ "codec", "foreign-object", @@ -2998,11 +2997,9 @@ }, "open-memoize": { "type": "registry", - "version": "6.1.0", - "integrity": "sha256-ws/Cix/wgUKvrbPPxTqDSGCPpkTIvP1adTYLHAquBC4=", + "version": "6.2.0", + "integrity": "sha256-p1m7wF3aHQ80yUvqMs20OTMl496WS6YpKlmI2Nkg9j0=", "dependencies": [ - "console", - "effect", "either", "integers", "lazy", @@ -3010,7 +3007,6 @@ "maybe", "partial", "prelude", - "psci-support", "strings", "tuples" ] @@ -3218,16 +3214,6 @@ "tuples" ] }, - "psci-support": { - "type": "registry", - "version": "6.0.0", - "integrity": "sha256-C6ql4P9TEP06hft/1Z5QumPA4yARR4VIxDdhmL1EO+Y=", - "dependencies": [ - "console", - "effect", - "prelude" - ] - }, "quickcheck": { "type": "registry", "version": "8.0.1", @@ -3302,7 +3288,7 @@ "registry-lib": { "type": "git", "url": "https://github.com/purescript/registry-dev.git", - "rev": "be0d7ffd1c16aa70e8f065a928e941ebc053c013", + "rev": "fc203a9e2a0b96a90ace20dc6958c157cbbca16b", "subdir": "lib", "dependencies": [ "aff", @@ -3392,8 +3378,8 @@ }, "spec": { "type": "registry", - "version": "7.6.1", - "integrity": "sha256-VycY2SgN8rHZGqcMqZEiTh9OdCw40CUMX2pCoSUJ/zY=", + "version": "8.0.0", + "integrity": "sha256-Yn7MhDai1YULlQF45+9FTOTf2rcjoda1Jf2IrEFCoeg=", "dependencies": [ "aff", "ansi", @@ -3679,8 +3665,8 @@ }, "web-clipboard": { "type": "registry", - "version": "5.0.0", - "integrity": "sha256-TbZEMovHhks9Qa7JXodJabvKDpeFkl6swfPKqhXd/lE=", + "version": "6.0.0", + "integrity": "sha256-PBtjiekTFBRs2obkiwNAEs3f6e1daFGHeeG0pilug4c=", "dependencies": [ "js-promise", "web-html" diff --git a/spago.yaml b/spago.yaml index ca3321441..c8a672fb4 100644 --- a/spago.yaml +++ b/spago.yaml @@ -82,12 +82,11 @@ package: - spec-node workspace: packageSet: - registry: 56.4.0 + registry: 59.0.0 extraPackages: - codec-json: 1.2.0 registry-lib: git: https://github.com/purescript/registry-dev.git - ref: be0d7ffd1c16aa70e8f065a928e941ebc053c013 + ref: fc203a9e2a0b96a90ace20dc6958c157cbbca16b subdir: lib html-parser-halogen: dependencies: diff --git a/src/Spago/Lock.purs b/src/Spago/Lock.purs index 4cb06a195..3d0aa4013 100644 --- a/src/Spago/Lock.purs +++ b/src/Spago/Lock.purs @@ -18,6 +18,7 @@ import Codec.JSON.DecodeError as CJ.DecodeError import Data.Codec as Codec import Data.Codec.JSON as CJ import Data.Codec.JSON.Common as CJ.Common +import Data.Codec.JSON.Strict as CJS import Data.Profunctor as Profunctor import Record as Record import Registry.Internal.Codec as Registry.Codec @@ -65,35 +66,35 @@ type WorkspaceLockPackageEnv = } lockfileCodec :: CJ.Codec Lockfile -lockfileCodec = CJ.named "Lockfile" $ CJ.object - $ CJ.recordProp (Proxy @"workspace") workspaceLockCodec - $ CJ.recordProp (Proxy @"packages") (Registry.Codec.packageMap lockEntryCodec) - $ CJ.record +lockfileCodec = CJ.named "Lockfile" $ CJS.objectStrict + $ CJS.recordProp @"workspace" workspaceLockCodec + $ CJS.recordProp @"packages" (Registry.Codec.packageMap lockEntryCodec) + $ CJS.record workspaceLockCodec :: CJ.Codec WorkspaceLock -workspaceLockCodec = CJ.named "WorkspaceLock" $ CJ.object - $ CJ.recordProp (Proxy @"packages") (Registry.Codec.packageMap dependenciesCodec) - $ CJ.recordPropOptional (Proxy @"package_set") packageSetCodec - $ CJ.recordProp (Proxy @"extra_packages") (Registry.Codec.packageMap Config.extraPackageCodec) - $ CJ.record +workspaceLockCodec = CJ.named "WorkspaceLock" $ CJS.objectStrict + $ CJS.recordProp @"packages" (Registry.Codec.packageMap dependenciesCodec) + $ CJS.recordPropOptional @"package_set" packageSetCodec + $ CJS.recordProp @"extra_packages" (Registry.Codec.packageMap Config.extraPackageCodec) + $ CJS.record where - dependenciesCodec = CJ.named "Dependencies" $ CJ.object - $ CJ.recordProp (Proxy @"path") CJ.string - $ CJ.recordProp (Proxy @"core") envCodec - $ CJ.recordProp (Proxy @"test") envCodec - $ CJ.record + dependenciesCodec = CJ.named "Dependencies" $ CJS.objectStrict + $ CJS.recordProp @"path" CJ.string + $ CJS.recordProp @"core" envCodec + $ CJS.recordProp @"test" envCodec + $ CJS.record - envCodec = CJ.named "Environment" $ CJ.object - $ CJ.recordProp (Proxy @"dependencies") Config.dependenciesCodec - $ CJ.recordProp (Proxy @"build_plan") (CJ.Common.set PackageName.codec) - $ CJ.record + envCodec = CJ.named "Environment" $ CJS.objectStrict + $ CJS.recordProp @"dependencies" Config.dependenciesCodec + $ CJS.recordProp @"build_plan" (CJ.Common.set PackageName.codec) + $ CJS.record packageSetCodec :: CJ.Codec PackageSetInfo -packageSetCodec = CJ.named "PackageSetInfo" $ CJ.object - $ CJ.recordProp (Proxy @"address") Config.setAddressCodec - $ CJ.recordProp (Proxy @"compiler") Range.codec - $ CJ.recordProp (Proxy @"content") (Registry.Codec.packageMap Core.remotePackageCodec) - $ CJ.record +packageSetCodec = CJ.named "PackageSetInfo" $ CJS.objectStrict + $ CJS.recordProp @"address" Config.setAddressCodec + $ CJS.recordProp @"compiler" Range.codec + $ CJS.recordProp @"content" (Registry.Codec.packageMap Core.remotePackageCodec) + $ CJS.record lockEntryCodec :: CJ.Codec LockEntry lockEntryCodec = Codec.codec' decode encode @@ -123,11 +124,11 @@ type PathLock = } pathLockCodec :: CJ.Codec PathLock -pathLockCodec = Profunctor.dimap toRep fromRep $ CJ.named "PathLock" $ CJ.object - $ CJ.recordProp (Proxy @"type") (constant pathLockType) - $ CJ.recordProp (Proxy @"path") CJ.string - $ CJ.recordProp (Proxy @"dependencies") (CJ.array PackageName.codec) - $ CJ.record +pathLockCodec = Profunctor.dimap toRep fromRep $ CJ.named "PathLock" $ CJS.objectStrict + $ CJS.recordProp @"type" (constant pathLockType) + $ CJS.recordProp @"path" CJ.string + $ CJS.recordProp @"dependencies" (CJ.array PackageName.codec) + $ CJS.record where toRep = Record.insert (Proxy @"type") pathLockType fromRep = Record.delete (Proxy @"type") @@ -140,13 +141,13 @@ type GitLock = } gitLockCodec :: CJ.Codec GitLock -gitLockCodec = Profunctor.dimap toRep fromRep $ CJ.named "GitLock" $ CJ.object - $ CJ.recordProp (Proxy @"type") (constant gitLockType) - $ CJ.recordProp (Proxy @"url") CJ.string - $ CJ.recordProp (Proxy @"rev") CJ.string - $ CJ.recordPropOptional (Proxy @"subdir") CJ.string - $ CJ.recordProp (Proxy @"dependencies") (CJ.array PackageName.codec) - $ CJ.record +gitLockCodec = Profunctor.dimap toRep fromRep $ CJ.named "GitLock" $ CJS.objectStrict + $ CJS.recordProp @"type" (constant gitLockType) + $ CJS.recordProp @"url" CJ.string + $ CJS.recordProp @"rev" CJ.string + $ CJS.recordPropOptional @"subdir" CJ.string + $ CJS.recordProp @"dependencies" (CJ.array PackageName.codec) + $ CJS.record where toRep = Record.insert (Proxy @"type") gitLockType fromRep = Record.delete (Proxy @"type") @@ -158,12 +159,12 @@ type RegistryLock = } registryLockCodec :: CJ.Codec RegistryLock -registryLockCodec = Profunctor.dimap toRep fromRep $ CJ.named "RegistryLock" $ CJ.object - $ CJ.recordProp (Proxy @"type") (constant registryLockType) - $ CJ.recordProp (Proxy @"version") Version.codec - $ CJ.recordProp (Proxy @"integrity") Sha256.codec - $ CJ.recordProp (Proxy @"dependencies") (CJ.array PackageName.codec) - $ CJ.record +registryLockCodec = Profunctor.dimap toRep fromRep $ CJ.named "RegistryLock" $ CJS.objectStrict + $ CJS.recordProp @"type" (constant registryLockType) + $ CJS.recordProp @"version" Version.codec + $ CJS.recordProp @"integrity" Sha256.codec + $ CJS.recordProp @"dependencies" (CJ.array PackageName.codec) + $ CJS.record where toRep = Record.insert (Proxy @"type") registryLockType fromRep = Record.delete (Proxy @"type") diff --git a/test/Spago.purs b/test/Spago.purs index 8de1702bc..a452d7bf1 100644 --- a/test/Spago.purs +++ b/test/Spago.purs @@ -8,6 +8,7 @@ import Effect.Aff (Milliseconds(..)) import Test.Spago.Build as Build import Test.Spago.Bundle as Bundle import Test.Spago.Cli as Cli +import Test.Spago.Config as Config import Test.Spago.Docs as Docs import Test.Spago.Errors as Errors import Test.Spago.Glob as Glob @@ -62,3 +63,4 @@ main = do Unit.spec Glob.spec Errors.spec + Config.spec diff --git a/test/Spago/Config.purs b/test/Spago/Config.purs new file mode 100644 index 000000000..45280866a --- /dev/null +++ b/test/Spago/Config.purs @@ -0,0 +1,180 @@ +module Test.Spago.Config where + +import Test.Prelude + +import Codec.JSON.DecodeError as CJ +import Registry.License as License +import Registry.Location (Location(..)) +import Registry.PackageName as PackageName +import Registry.Version as Version +import Spago.Core.Config (SetAddress(..)) +import Spago.Core.Config as C +import Spago.Yaml as Yaml +import Test.Spec (Spec) +import Test.Spec as Spec +import Test.Spec.Assertions as Assert + +spec :: Spec Unit +spec = + Spec.describe "config codec" do + Spec.it "parses valid spago.yaml" do + let parsedConfig = Yaml.parseYaml C.configCodec validSpagoYaml.serialized + case parsedConfig of + Left err -> + Assert.fail $ "Failed to parse valid config: " <> show err + Right parsed | parsed == validSpagoYaml.parsed -> + pure unit + Right parsed -> + Assert.fail $ + "\n-------\nExpected:\n-------\n" <> Yaml.stringifyYaml C.configCodec validSpagoYaml.parsed <> + "\n\n\n-------\nActual:\n-------\n" <> Yaml.stringifyYaml C.configCodec parsed + + Spec.it "reports errors" do + Yaml.parseYaml C.configCodec invalidLicenseYaml `shouldFailWith` + ( "$.package.publish.license: Could not decode PackageConfig:" + <> "\n Could not decode PublishConfig:" + <> "\n Could not decode License:" + <> "\n Invalid SPDX identifier bogus" + ) + + Spec.describe "reports unrecognized fields" do + Spec.it "under 'package'" do + Yaml.parseYaml C.configCodec unrecognizedPackageFieldYaml `shouldFailWith` + ( "$.package: Could not decode PackageConfig:" + <> "\n Unknown field(s): bogus_field" + ) + + Spec.it "under 'workspace'" do + Yaml.parseYaml C.configCodec unrecognizedBuildOptsFieldYaml `shouldFailWith` + ( "$.workspace.buildOpts: Could not decode WorkspaceConfig:" + <> "\n Could not decode WorkspaceBuildOptionsInput:" + <> "\n Unknown field(s): bogus_field" + ) + + Spec.it "under 'publish.location'" do + Yaml.parseYaml C.configCodec unrecognizedPublishLocationFieldYaml `shouldFailWith` + ( "$.package.publish.location: Could not decode PackageConfig:" + <> "\n Could not decode PublishConfig:" + <> "\n Could not decode Publish Location:" + <> "\n Failed to decode alternatives:" + <> "\n - $.gitUrl: Could not decode Git:" + <> "\n No value found" + <> "\n - Could not decode GitHub:" + <> "\n Unknown field(s): bogus_field" + ) + where + shouldFailWith result expectedError = + case result of + Right _ -> Assert.fail "Expected an error, but parsed successfully" + Left err -> CJ.print err `shouldEqual` expectedError + +validSpagoYaml :: { serialized :: String, parsed :: C.Config } +validSpagoYaml = + { serialized: """ + package: + name: testpackage + publish: + version: 0.0.0 + license: BSD-3-Clause + location: + githubOwner: purescript + githubRepo: testpackage + build: + strict: true + dependencies: + - aff + - prelude + - console + - effect + test: + main: Test.Main + dependencies: + - spec + - spec-node + workspace: + packageSet: + registry: 56.4.0 + """ + , parsed: + { package: Just + { name: unsafeFromRight $ PackageName.parse "testpackage" + , publish: Just + { version: unsafeFromRight $ Version.parse "0.0.0" + , license: unsafeFromRight $ License.parse "BSD-3-Clause" + , location: Just $ GitHub { owner: "purescript", repo: "testpackage", subdir: Nothing } + , include: Nothing + , exclude: Nothing + } + , build: Just + { strict: Just true + , censorProjectWarnings: Nothing + , pedanticPackages: Nothing + } + , bundle: Nothing + , run: Nothing + , description: Nothing + , dependencies: mkDependencies [ "aff", "prelude", "console", "effect" ] + , test: Just + { main: "Test.Main" + , dependencies: mkDependencies [ "spec", "spec-node" ] + , censorTestWarnings: Nothing + , execArgs: Nothing + , strict: Nothing + , pedanticPackages: Nothing + } + } + , workspace: Just + { packageSet: Just $ SetFromRegistry { registry: unsafeFromRight $ Version.parse "56.4.0" } + , extraPackages: Nothing + , backend: Nothing + , buildOpts: Nothing + } + } + } + +invalidLicenseYaml :: String +invalidLicenseYaml = """ + package: + name: spago + dependencies: [] + publish: + version: 0.0.0 + license: bogus +""" + +unrecognizedPackageFieldYaml :: String +unrecognizedPackageFieldYaml = """ + package: + name: spago + dependencies: [] + bogus_field: bogus_value +""" + +unrecognizedBuildOptsFieldYaml :: String +unrecognizedBuildOptsFieldYaml = """ + package: + name: spago + dependencies: [] + workspace: + packageSet: + registry: 56.4.0 + buildOpts: + bogus_field: bogus_value +""" + +unrecognizedPublishLocationFieldYaml :: String +unrecognizedPublishLocationFieldYaml = """ + package: + name: spago + dependencies: [] + publish: + version: 0.0.0 + license: MIT + location: + githubOwner: purescript + githubRepo: spago + bogus_field: bogus_value + workspace: + packageSet: + registry: 56.4.0 +"""