git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Merge pull request #1304 from hdgarrood/publish-to-library

Move psc-publish code into the library

commitce072c416622afd9f4db3c7da73d1372512ae12e authorPhil Freeman <paf31@cantab.net> dateSun 26 Jul 2015, 09:03 (11 years ago) parent24eb832bc257ee
6 files changed+345−321
psc-publish/Main.hs+3−305 purescript.cabal+11−11 src/Language/PureScript/Publish.hs+314 psc-publish/BoxesHelpers.hs → src/Language/PureScript/Publish/BoxesHelpers.hs+2−2 psc-publish/ErrorsWarnings.hs → src/Language/PureScript/Publish/ErrorsWarnings.hs+14−2 psc-publish/Utils.hs → src/Language/PureScript/Publish/Utils.hs+1−1
psc-publish/Main.hs+3−305
@@ -1,53 +1,14 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−{-# LANGUAGE RecordWildCards #-}
3−{-# LANGUAGE TupleSections #-}
4−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
51
62module Main where
73
8−import Prelude hiding (userError)
9−
10−import Data.Maybe
11−import Data.Char (isSpace)
12−import Data.String (fromString)
13−import Data.List (stripPrefix, isSuffixOf, (\\), nubBy)
14−import Data.List.Split (splitOn)
15−import Data.List.NonEmpty (NonEmpty(..))
16−import Data.Version
17−import Data.Function (on)
18−import Safe (headMay)
19−
20−import qualified Data.ByteString.Lazy.Char8 as BL
21−import qualified Data.Text as T
22−
4+import Data.Version (showVersion)
235import qualified Data.Aeson as A
24−import Data.Aeson.BetterErrors
25−
26−import Control.Applicative
27−import Control.Category ((>>>))
28−import Control.Arrow ((***))
29−import Control.Exception (catch, try)
30−import Control.Monad.Trans.Except
31−import Control.Monad.Error.Class (MonadError(..))
32−import Control.Monad.Writer
6+import qualified Data.ByteString.Lazy.Char8 as BL
337
348import Options.Applicative hiding (str)
359
36−import System.Directory (doesFileExist)
37−import System.Process (readProcess)
38−import System.Exit (exitFailure)
39−import qualified System.FilePath.Glob as Glob
40−
41−import Web.Bower.PackageMeta (PackageMeta(..), BowerError(..), PackageName,
42− runPackageName, parsePackageName, Repository(..))
43−import qualified Web.Bower.PackageMeta as Bower
44−
45−import qualified Language.PureScript as P (version)
46−import qualified Language.PureScript.Docs as D
4710import qualified Paths_purescript as Paths
48−
49−import Utils
50−import ErrorsWarnings
11+import Language.PureScript.Publish (preparePackage)
5112
5213main :: IO ()
5314main = execParser opts >> publish
@@ -65,266 +26,3 @@ publish = do
6526 pkg <- preparePackage
6627 BL.putStrLn (A.encode pkg)
6728
68−-- | Attempt to retrieve package metadata from the current directory.
69−-- Calls exitFailure if no package metadata could be retrieved.
70−preparePackage :: IO D.UploadedPackage
71−preparePackage =
72− runPrepareM preparePackage'
73− >>= either (\e -> printError e >> exitFailure)
74− handleWarnings
75− where
76− handleWarnings (result, warns) = do
77− printWarnings warns
78− return result
79−
80−newtype PrepareM a =
81− PrepareM { unPrepareM :: WriterT [PackageWarning] (ExceptT PackageError IO) a }
82− deriving (Functor, Applicative, Monad,
83− MonadWriter [PackageWarning],
84− MonadError PackageError)
85−
86−-- This MonadIO instance ensures that IO errors don't crash the program.
87−instance MonadIO PrepareM where
88− liftIO act =
89− lift' (try act) >>= either (otherError . IOExceptionThrown) return
90− where
91− lift' :: IO a -> PrepareM a
92− lift' = PrepareM . lift . lift
93−
94−runPrepareM :: PrepareM a -> IO (Either PackageError (a, [PackageWarning]))
95−runPrepareM = runExceptT . runWriterT . unPrepareM
96−
97−warn :: PackageWarning -> PrepareM ()
98−warn w = tell [w]
99−
100−userError :: UserError -> PrepareM a
101−userError = throwError . UserError
102−
103−internalError :: InternalError -> PrepareM a
104−internalError = throwError . InternalError
105−
106−otherError :: OtherError -> PrepareM a
107−otherError = throwError . OtherError
108−
109−catchLeft :: Applicative f => Either a b -> (a -> f b) -> f b
110−catchLeft a f = either f pure a
111−
112−preparePackage' :: PrepareM D.UploadedPackage
113−preparePackage' = do
114− exists <- liftIO (doesFileExist "bower.json")
115− unless exists (userError BowerJSONNotFound)
116−
117− pkgMeta <- liftIO (Bower.decodeFile "bower.json")
118− >>= flip catchLeft (userError . CouldntParseBowerJSON)
119− (pkgVersionTag, pkgVersion) <- getVersionFromGitTag
120− pkgGithub <- getBowerInfo pkgMeta
121− (pkgBookmarks, pkgModules) <- getModulesAndBookmarks
122−
123− let declaredDeps = map fst (bowerDependencies pkgMeta ++
124− bowerDevDependencies pkgMeta)
125− pkgResolvedDependencies <- getResolvedDependencies declaredDeps
126−
127− let pkgUploader = D.NotYetKnown
128− let pkgCompilerVersion = P.version
129−
130− return D.Package{..}
131−
132−getModulesAndBookmarks :: PrepareM ([D.Bookmark], [D.Module])
133−getModulesAndBookmarks = do
134− (inputFiles, depsFiles) <- liftIO getInputAndDepsFiles
135− liftIO (D.parseAndDesugar inputFiles depsFiles renderModules)
136− >>= either (userError . ParseAndDesugarError) return
137− where
138− renderModules bookmarks modules =
139− return (bookmarks, map D.convertModule modules)
140−
141−getVersionFromGitTag :: PrepareM (String, Version)
142−getVersionFromGitTag = do
143− out <- readProcess' "git" ["tag", "--list", "--points-at", "HEAD"] ""
144− let vs = map trimWhitespace (lines out)
145− case mapMaybe parseMay vs of
146− [] -> userError TagMustBeCheckedOut
147− [x] -> return x
148− xs -> userError (AmbiguousVersions (map snd xs))
149− where
150− trimWhitespace =
151− dropWhile isSpace >>> reverse >>> dropWhile isSpace >>> reverse
152− parseMay str =
153− (str,) <$> D.parseVersion' (dropPrefix "v" str)
154− dropPrefix prefix str =
155− fromMaybe str (stripPrefix prefix str)
156−
157−getBowerInfo :: PackageMeta -> PrepareM (D.GithubUser, D.GithubRepo)
158−getBowerInfo = either (userError . BadRepositoryField) return . tryExtract
159− where
160− tryExtract pkgMeta =
161− case bowerRepository pkgMeta of
162− Nothing -> Left RepositoryFieldMissing
163− Just Repository{..} -> do
164− unless (repositoryType == "git")
165− (Left (BadRepositoryType repositoryType))
166− maybe (Left NotOnGithub) Right (extractGithub repositoryUrl)
167−
168−extractGithub :: String -> Maybe (D.GithubUser, D.GithubRepo)
169−extractGithub =
170− stripPrefix "git://github.com/"
171− >>> fmap (splitOn "/")
172− >=> takeTwo
173− >>> fmap (D.GithubUser *** (D.GithubRepo . dropDotGit))
174−
175− where
176− takeTwo :: [a] -> Maybe (a, a)
177− takeTwo [x, y] = Just (x, y)
178− takeTwo _ = Nothing
179−
180− dropDotGit :: String -> String
181− dropDotGit str
182− | ".git" `isSuffixOf` str = take (length str - 4) str
183− | otherwise = str
184−
185−readProcess' :: String -> [String] -> String -> PrepareM String
186−readProcess' prog args stdin = do
187− out <- liftIO (catch (Right <$> readProcess prog args stdin)
188− (return . Left))
189− either (otherError . ProcessFailed prog args) return out
190−
191−data DependencyStatus
192− = Missing
193− -- ^ Listed in bower.json, but not installed.
194− | NoResolution
195− -- ^ In the output of `bower list --json --offline`, there was no
196− -- _resolution key. This can be caused by adding the dependency using
197− -- `bower link`, or simply copying it into bower_components instead of
198− -- installing it normally.
199− | ResolvedOther String
200− -- ^ Resolved, but to something other than a version. The String argument
201− -- is the resolution type. The values it can take that I'm aware of are
202− -- "commit" and "branch".
203− | ResolvedVersion String
204− -- ^ Resolved to a version. The String argument is the resolution tag (eg,
205− -- "v0.1.0").
206− deriving (Show, Eq)
207−
208−-- Go through all bower dependencies which contain purescript code, and
209−-- extract their versions.
210−--
211−-- In the case where a bower dependency is taken from a particular version,
212−-- that's easy; take that version. In any other case (eg, a branch, or a commit
213−-- sha) we print a warning that documentation links will not work, and avoid
214−-- linking to documentation for any types from that package.
215−--
216−-- The rationale for this is: people will prefer to use a released version
217−-- where possible. If they are not using a released version, then this is
218−-- probably for a reason. However, docs are only ever available for released
219−-- versions. Therefore there will probably be no version of the docs which is
220−-- appropriate to link to, and we should omit links.
221−getResolvedDependencies :: [PackageName] -> PrepareM [(PackageName, Version)]
222−getResolvedDependencies declaredDeps = do
223− depsBS <- fromString <$> readProcess' "bower" ["list", "--json", "--offline"] ""
224−
225− -- Check for undeclared dependencies
226− toplevels <- catchJSON (parse asToplevelDependencies depsBS)
227− warnUndeclared declaredDeps toplevels
228−
229− deps <- catchJSON (parse asResolvedDependencies depsBS)
230− handleDeps deps
231−
232− where
233− catchJSON = flip catchLeft (internalError . JSONError FromBowerList)
234−
235−-- | Extracts all dependencies and their versions from
236−-- `bower list --json --offline`
237−asResolvedDependencies :: Parse BowerError [(PackageName, DependencyStatus)]
238−asResolvedDependencies = nubBy ((==) `on` fst) <$> go
239− where
240− go =
241− fmap (fromMaybe []) $
242− keyMay "dependencies" $
243− (++) <$> eachInObjectWithKey (parsePackageName . T.unpack)
244− asDependencyStatus
245− <*> (concatMap snd <$> eachInObject asResolvedDependencies)
246−
247−-- | Extracts only the top level dependency names from the output of
248−-- `bower list --json --offline`
249−asToplevelDependencies :: Parse BowerError [PackageName]
250−asToplevelDependencies =
251− fmap (map fst) $
252− key "dependencies" $
253− eachInObjectWithKey (parsePackageName . T.unpack) (return ())
254−
255−asDependencyStatus :: Parse e DependencyStatus
256−asDependencyStatus = do
257− isMissing <- keyOrDefault "missing" False asBool
258− if isMissing
259− then
260− return Missing
261− else
262− key "pkgMeta" $
263− keyOrDefault "_resolution" NoResolution $ do
264− type_ <- key "type" asString
265− case type_ of
266− "version" -> ResolvedVersion <$> key "tag" asString
267− other -> return (ResolvedOther other)
268−
269−warnUndeclared :: [PackageName] -> [PackageName] -> PrepareM ()
270−warnUndeclared declared actual =
271− mapM_ (warn . UndeclaredDependency) (actual \\ declared)
272−
273−handleDeps ::
274− [(PackageName, DependencyStatus)] -> PrepareM [(PackageName, Version)]
275−handleDeps deps = do
276− let (missing, noVersion, installed) = partitionDeps deps
277− case missing of
278− (x:xs) ->
279− userError (MissingDependencies (x :| xs))
280− [] -> do
281− mapM_ (warn . NoResolvedVersion) noVersion
282− withVersions <- catMaybes <$> mapM tryExtractVersion' installed
283− filterM (liftIO . isPureScript . bowerDir . fst) withVersions
284−
285− where
286− partitionDeps = foldr go ([], [], [])
287− go (pkgName, d) (ms, os, is) =
288− case d of
289− Missing -> (pkgName : ms, os, is)
290− NoResolution -> (ms, pkgName : os, is)
291− ResolvedOther _ -> (ms, pkgName : os, is)
292− ResolvedVersion v -> (ms, os, (pkgName, v) : is)
293−
294− bowerDir pkgName = "bower_components/" ++ runPackageName pkgName
295−
296− -- Try to extract a version, and warn if unsuccessful.
297− tryExtractVersion' pair =
298− maybe (warn (UnacceptableVersion pair) >> return Nothing)
299− (return . Just)
300− (tryExtractVersion pair)
301−
302−tryExtractVersion :: (PackageName, String) -> Maybe (PackageName, Version)
303−tryExtractVersion (pkgName, tag) =
304− let tag' = fromMaybe tag (stripPrefix "v" tag)
305− in (pkgName,) <$> D.parseVersion' tag'
306−
307−-- | Returns whether it looks like there is a purescript package checked out
308−-- in the given directory.
309−isPureScript :: FilePath -> IO Bool
310−isPureScript dir = do
311− files <- Glob.globDir1 purescriptSourceFiles dir
312− return (not (null files))
313−
314−getInputAndDepsFiles :: IO ([FilePath], [(PackageName, FilePath)])
315−getInputAndDepsFiles = do
316− inputFiles <- globRelative purescriptSourceFiles
317− depsFiles' <- globRelative purescriptDepsFiles
318− return (inputFiles, mapMaybe withPackageName depsFiles')
319−
320−withPackageName :: FilePath -> Maybe (PackageName, FilePath)
321−withPackageName fp = (,fp) <$> getPackageName fp
322−
323−getPackageName :: FilePath -> Maybe PackageName
324−getPackageName fp = do
325− let xs = splitOn "/" fp
326− ys <- stripPrefix ["bower_components"] xs
327− y <- headMay ys
328− case Bower.mkPackageName y of
329− Right name -> Just name
330− Left _ -> Nothing
purescript.cabal+11−11
@@ -48,7 +48,11 @@ library
4848 text -any,
4949 split -any,
5050 language-javascript == 0.5.*,
51− syb -any
51+ syb -any,
52+ Glob >= 0.7 && < 0.8,
53+ process >= 1.2.0 && < 1.3,
54+ safe >= 0.3.9 && < 0.4,
55+ semigroups >= 0.16.2 && < 0.17
5256
5357 exposed-modules: Language.PureScript
5458 Language.PureScript.AST
@@ -140,6 +144,11 @@ library
140144 Language.PureScript.Docs.ParseAndDesugar
141145 Language.PureScript.Docs.Utils.MonoidExtras
142146
147+ Language.PureScript.Publish
148+ Language.PureScript.Publish.Utils
149+ Language.PureScript.Publish.ErrorsWarnings
150+ Language.PureScript.Publish.BoxesHelpers
151+
143152 Control.Monad.Unify
144153 Control.Monad.Supply
145154 Control.Monad.Supply.Class
@@ -190,19 +199,10 @@ executable psc-docs
190199 ghc-options: -Wall -O2
191200
192201executable psc-publish
193− build-depends: base >=4 && <5, purescript -any,
194− optparse-applicative >= 0.10.0, process -any, mtl -any,
195− pattern-arrows -any, aeson -any, bytestring -any,
196− directory -any, transformers -any, text -any, containers
197− -any, boxes -any, split -any, Glob -any, aeson-better-errors
198− -any, transformers-compat -any, bower-json -any, semigroups
199− -any, safe -any
202+ build-depends: base >=4 && <5, purescript -any, bytestring -any, aeson -any, optparse-applicative -any
200203 main-is: Main.hs
201204 buildable: True
202205 hs-source-dirs: psc-publish
203− other-modules: Utils
204− ErrorsWarnings
205− BoxesHelpers
206206 ghc-options: -Wall -O2
207207
208208executable psc-hierarchy
src/Language/PureScript/Publish.hsnew file+314
@@ -0,0 +1,314 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE RecordWildCards #-}
3+{-# LANGUAGE TupleSections #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+
6+module Language.PureScript.Publish
7+ ( preparePackage
8+ , PrepareM()
9+ , runPrepareM
10+ , getVersionFromGitTag
11+ , getBowerInfo
12+ , getModulesAndBookmarks
13+ , getResolvedDependencies
14+ ) where
15+
16+import Prelude hiding (userError)
17+
18+import Data.Maybe
19+import Data.Char (isSpace)
20+import Data.String (fromString)
21+import Data.List (stripPrefix, isSuffixOf, (\\), nubBy)
22+import Data.List.Split (splitOn)
23+import Data.List.NonEmpty (NonEmpty(..))
24+import Data.Version
25+import Data.Function (on)
26+import Safe (headMay)
27+import Data.Aeson.BetterErrors
28+import qualified Data.Text as T
29+
30+import Control.Applicative
31+import Control.Category ((>>>))
32+import Control.Arrow ((***))
33+import Control.Exception (catch, try)
34+import Control.Monad.Trans.Except
35+import Control.Monad.Error.Class (MonadError(..))
36+import Control.Monad.Writer
37+
38+import System.Directory (doesFileExist)
39+import System.Process (readProcess)
40+import System.Exit (exitFailure)
41+import qualified System.FilePath.Glob as Glob
42+
43+import Web.Bower.PackageMeta (PackageMeta(..), BowerError(..), PackageName,
44+ runPackageName, parsePackageName, Repository(..))
45+import qualified Web.Bower.PackageMeta as Bower
46+
47+import qualified Language.PureScript as P (version)
48+import qualified Language.PureScript.Docs as D
49+import Language.PureScript.Publish.Utils
50+import Language.PureScript.Publish.ErrorsWarnings
51+
52+-- | Attempt to retrieve package metadata from the current directory.
53+-- Calls exitFailure if no package metadata could be retrieved.
54+preparePackage :: IO D.UploadedPackage
55+preparePackage =
56+ runPrepareM preparePackage'
57+ >>= either (\e -> printError e >> exitFailure)
58+ handleWarnings
59+ where
60+ handleWarnings (result, warns) = do
61+ printWarnings warns
62+ return result
63+
64+newtype PrepareM a =
65+ PrepareM { unPrepareM :: WriterT [PackageWarning] (ExceptT PackageError IO) a }
66+ deriving (Functor, Applicative, Monad,
67+ MonadWriter [PackageWarning],
68+ MonadError PackageError)
69+
70+-- This MonadIO instance ensures that IO errors don't crash the program.
71+instance MonadIO PrepareM where
72+ liftIO act =
73+ lift' (try act) >>= either (otherError . IOExceptionThrown) return
74+ where
75+ lift' :: IO a -> PrepareM a
76+ lift' = PrepareM . lift . lift
77+
78+runPrepareM :: PrepareM a -> IO (Either PackageError (a, [PackageWarning]))
79+runPrepareM = runExceptT . runWriterT . unPrepareM
80+
81+warn :: PackageWarning -> PrepareM ()
82+warn w = tell [w]
83+
84+userError :: UserError -> PrepareM a
85+userError = throwError . UserError
86+
87+internalError :: InternalError -> PrepareM a
88+internalError = throwError . InternalError
89+
90+otherError :: OtherError -> PrepareM a
91+otherError = throwError . OtherError
92+
93+catchLeft :: Applicative f => Either a b -> (a -> f b) -> f b
94+catchLeft a f = either f pure a
95+
96+preparePackage' :: PrepareM D.UploadedPackage
97+preparePackage' = do
98+ exists <- liftIO (doesFileExist "bower.json")
99+ unless exists (userError BowerJSONNotFound)
100+
101+ pkgMeta <- liftIO (Bower.decodeFile "bower.json")
102+ >>= flip catchLeft (userError . CouldntParseBowerJSON)
103+ (pkgVersionTag, pkgVersion) <- getVersionFromGitTag
104+ pkgGithub <- getBowerInfo pkgMeta
105+ (pkgBookmarks, pkgModules) <- getModulesAndBookmarks
106+
107+ let declaredDeps = map fst (bowerDependencies pkgMeta ++
108+ bowerDevDependencies pkgMeta)
109+ pkgResolvedDependencies <- getResolvedDependencies declaredDeps
110+
111+ let pkgUploader = D.NotYetKnown
112+ let pkgCompilerVersion = P.version
113+
114+ return D.Package{..}
115+
116+getModulesAndBookmarks :: PrepareM ([D.Bookmark], [D.Module])
117+getModulesAndBookmarks = do
118+ (inputFiles, depsFiles) <- liftIO getInputAndDepsFiles
119+ liftIO (D.parseAndDesugar inputFiles depsFiles renderModules)
120+ >>= either (userError . ParseAndDesugarError) return
121+ where
122+ renderModules bookmarks modules =
123+ return (bookmarks, map D.convertModule modules)
124+
125+getVersionFromGitTag :: PrepareM (String, Version)
126+getVersionFromGitTag = do
127+ out <- readProcess' "git" ["tag", "--list", "--points-at", "HEAD"] ""
128+ let vs = map trimWhitespace (lines out)
129+ case mapMaybe parseMay vs of
130+ [] -> userError TagMustBeCheckedOut
131+ [x] -> return x
132+ xs -> userError (AmbiguousVersions (map snd xs))
133+ where
134+ trimWhitespace =
135+ dropWhile isSpace >>> reverse >>> dropWhile isSpace >>> reverse
136+ parseMay str =
137+ (str,) <$> D.parseVersion' (dropPrefix "v" str)
138+ dropPrefix prefix str =
139+ fromMaybe str (stripPrefix prefix str)
140+
141+getBowerInfo :: PackageMeta -> PrepareM (D.GithubUser, D.GithubRepo)
142+getBowerInfo = either (userError . BadRepositoryField) return . tryExtract
143+ where
144+ tryExtract pkgMeta =
145+ case bowerRepository pkgMeta of
146+ Nothing -> Left RepositoryFieldMissing
147+ Just Repository{..} -> do
148+ unless (repositoryType == "git")
149+ (Left (BadRepositoryType repositoryType))
150+ maybe (Left NotOnGithub) Right (extractGithub repositoryUrl)
151+
152+extractGithub :: String -> Maybe (D.GithubUser, D.GithubRepo)
153+extractGithub =
154+ stripPrefix "git://github.com/"
155+ >>> fmap (splitOn "/")
156+ >=> takeTwo
157+ >>> fmap (D.GithubUser *** (D.GithubRepo . dropDotGit))
158+
159+ where
160+ takeTwo :: [a] -> Maybe (a, a)
161+ takeTwo [x, y] = Just (x, y)
162+ takeTwo _ = Nothing
163+
164+ dropDotGit :: String -> String
165+ dropDotGit str
166+ | ".git" `isSuffixOf` str = take (length str - 4) str
167+ | otherwise = str
168+
169+readProcess' :: String -> [String] -> String -> PrepareM String
170+readProcess' prog args stdin = do
171+ out <- liftIO (catch (Right <$> readProcess prog args stdin)
172+ (return . Left))
173+ either (otherError . ProcessFailed prog args) return out
174+
175+data DependencyStatus
176+ = Missing
177+ -- ^ Listed in bower.json, but not installed.
178+ | NoResolution
179+ -- ^ In the output of `bower list --json --offline`, there was no
180+ -- _resolution key. This can be caused by adding the dependency using
181+ -- `bower link`, or simply copying it into bower_components instead of
182+ -- installing it normally.
183+ | ResolvedOther String
184+ -- ^ Resolved, but to something other than a version. The String argument
185+ -- is the resolution type. The values it can take that I'm aware of are
186+ -- "commit" and "branch".
187+ | ResolvedVersion String
188+ -- ^ Resolved to a version. The String argument is the resolution tag (eg,
189+ -- "v0.1.0").
190+ deriving (Show, Eq)
191+
192+-- Go through all bower dependencies which contain purescript code, and
193+-- extract their versions.
194+--
195+-- In the case where a bower dependency is taken from a particular version,
196+-- that's easy; take that version. In any other case (eg, a branch, or a commit
197+-- sha) we print a warning that documentation links will not work, and avoid
198+-- linking to documentation for any types from that package.
199+--
200+-- The rationale for this is: people will prefer to use a released version
201+-- where possible. If they are not using a released version, then this is
202+-- probably for a reason. However, docs are only ever available for released
203+-- versions. Therefore there will probably be no version of the docs which is
204+-- appropriate to link to, and we should omit links.
205+getResolvedDependencies :: [PackageName] -> PrepareM [(PackageName, Version)]
206+getResolvedDependencies declaredDeps = do
207+ depsBS <- fromString <$> readProcess' "bower" ["list", "--json", "--offline"] ""
208+
209+ -- Check for undeclared dependencies
210+ toplevels <- catchJSON (parse asToplevelDependencies depsBS)
211+ warnUndeclared declaredDeps toplevels
212+
213+ deps <- catchJSON (parse asResolvedDependencies depsBS)
214+ handleDeps deps
215+
216+ where
217+ catchJSON = flip catchLeft (internalError . JSONError FromBowerList)
218+
219+-- | Extracts all dependencies and their versions from
220+-- `bower list --json --offline`
221+asResolvedDependencies :: Parse BowerError [(PackageName, DependencyStatus)]
222+asResolvedDependencies = nubBy ((==) `on` fst) <$> go
223+ where
224+ go =
225+ fmap (fromMaybe []) $
226+ keyMay "dependencies" $
227+ (++) <$> eachInObjectWithKey (parsePackageName . T.unpack)
228+ asDependencyStatus
229+ <*> (concatMap snd <$> eachInObject asResolvedDependencies)
230+
231+-- | Extracts only the top level dependency names from the output of
232+-- `bower list --json --offline`
233+asToplevelDependencies :: Parse BowerError [PackageName]
234+asToplevelDependencies =
235+ fmap (map fst) $
236+ key "dependencies" $
237+ eachInObjectWithKey (parsePackageName . T.unpack) (return ())
238+
239+asDependencyStatus :: Parse e DependencyStatus
240+asDependencyStatus = do
241+ isMissing <- keyOrDefault "missing" False asBool
242+ if isMissing
243+ then
244+ return Missing
245+ else
246+ key "pkgMeta" $
247+ keyOrDefault "_resolution" NoResolution $ do
248+ type_ <- key "type" asString
249+ case type_ of
250+ "version" -> ResolvedVersion <$> key "tag" asString
251+ other -> return (ResolvedOther other)
252+
253+warnUndeclared :: [PackageName] -> [PackageName] -> PrepareM ()
254+warnUndeclared declared actual =
255+ mapM_ (warn . UndeclaredDependency) (actual \\ declared)
256+
257+handleDeps ::
258+ [(PackageName, DependencyStatus)] -> PrepareM [(PackageName, Version)]
259+handleDeps deps = do
260+ let (missing, noVersion, installed) = partitionDeps deps
261+ case missing of
262+ (x:xs) ->
263+ userError (MissingDependencies (x :| xs))
264+ [] -> do
265+ mapM_ (warn . NoResolvedVersion) noVersion
266+ withVersions <- catMaybes <$> mapM tryExtractVersion' installed
267+ filterM (liftIO . isPureScript . bowerDir . fst) withVersions
268+
269+ where
270+ partitionDeps = foldr go ([], [], [])
271+ go (pkgName, d) (ms, os, is) =
272+ case d of
273+ Missing -> (pkgName : ms, os, is)
274+ NoResolution -> (ms, pkgName : os, is)
275+ ResolvedOther _ -> (ms, pkgName : os, is)
276+ ResolvedVersion v -> (ms, os, (pkgName, v) : is)
277+
278+ bowerDir pkgName = "bower_components/" ++ runPackageName pkgName
279+
280+ -- Try to extract a version, and warn if unsuccessful.
281+ tryExtractVersion' pair =
282+ maybe (warn (UnacceptableVersion pair) >> return Nothing)
283+ (return . Just)
284+ (tryExtractVersion pair)
285+
286+tryExtractVersion :: (PackageName, String) -> Maybe (PackageName, Version)
287+tryExtractVersion (pkgName, tag) =
288+ let tag' = fromMaybe tag (stripPrefix "v" tag)
289+ in (pkgName,) <$> D.parseVersion' tag'
290+
291+-- | Returns whether it looks like there is a purescript package checked out
292+-- in the given directory.
293+isPureScript :: FilePath -> IO Bool
294+isPureScript dir = do
295+ files <- Glob.globDir1 purescriptSourceFiles dir
296+ return (not (null files))
297+
298+getInputAndDepsFiles :: IO ([FilePath], [(PackageName, FilePath)])
299+getInputAndDepsFiles = do
300+ inputFiles <- globRelative purescriptSourceFiles
301+ depsFiles' <- globRelative purescriptDepsFiles
302+ return (inputFiles, mapMaybe withPackageName depsFiles')
303+
304+withPackageName :: FilePath -> Maybe (PackageName, FilePath)
305+withPackageName fp = (,fp) <$> getPackageName fp
306+
307+getPackageName :: FilePath -> Maybe PackageName
308+getPackageName fp = do
309+ let xs = splitOn "/" fp
310+ ys <- stripPrefix ["bower_components"] xs
311+ y <- headMay ys
312+ case Bower.mkPackageName y of
313+ Right name -> Just name
314+ Left _ -> Nothing
psc-publish/BoxesHelpers.hs → src/Language/PureScript/Publish/BoxesHelpers.hsrenamed+2−2
@@ -1,7 +1,7 @@
1−module BoxesHelpers
1+module Language.PureScript.Publish.BoxesHelpers
22 ( Boxes.Box
33 , Boxes.nullBox
4− , module BoxesHelpers
4+ , module Language.PureScript.Publish.BoxesHelpers
55 ) where
66
77import System.IO (hPutStr, stderr)
psc-publish/ErrorsWarnings.hs → src/Language/PureScript/Publish/ErrorsWarnings.hsrenamed+14−2
@@ -1,7 +1,19 @@
11{-# LANGUAGE OverloadedStrings #-}
22{-# LANGUAGE RecordWildCards #-}
33
4−module ErrorsWarnings where
4+module Language.PureScript.Publish.ErrorsWarnings
5+ ( PackageError(..)
6+ , PackageWarning(..)
7+ , UserError(..)
8+ , InternalError(..)
9+ , OtherError(..)
10+ , RepositoryFieldError(..)
11+ , JSONSource(..)
12+ , printError
13+ , renderError
14+ , printWarnings
15+ , renderWarnings
16+ ) where
517
618import Control.Applicative ((<$>))
719import Data.Aeson.BetterErrors
@@ -22,7 +34,7 @@ import qualified Web.Bower.PackageMeta as Bower
2234import qualified Language.PureScript as P
2335import qualified Language.PureScript.Docs as D
2436
25−import BoxesHelpers
37+import Language.PureScript.Publish.BoxesHelpers
2638
2739-- | An error which meant that it was not possible to retrieve metadata for a
2840-- package.
psc-publish/Utils.hs → src/Language/PureScript/Publish/Utils.hsrenamed+1−1
@@ -1,5 +1,5 @@
11
2−module Utils where
2+module Language.PureScript.Publish.Utils where
33
44import Data.List
55import Data.Maybe