git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Move psc-publish code into a library
commit5725ebfe5fefd1cfa9699283afbefc4aef40dab7 authorHarry Garrood <harry@garrood.me> dateSat 18 Jul 2015, 01:51 (11 years ago) parentda99bc1
6 files changed+325−321
psc-publish/Main.hs+3−305 purescript.cabal+11−11 src/Language/PureScript/Publish.hs+306 psc-publish/BoxesHelpers.hs → src/Language/PureScript/Publish/BoxesHelpers.hs+2−2 psc-publish/ErrorsWarnings.hs → src/Language/PureScript/Publish/ErrorsWarnings.hs+2−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+306
@@ -0,0 +1,306 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE RecordWildCards #-}
3+{-# LANGUAGE TupleSections #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+
6+module Language.PureScript.Publish where
7+
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+import Data.Aeson.BetterErrors
20+import qualified Data.Text as T
21+
22+import Control.Applicative
23+import Control.Category ((>>>))
24+import Control.Arrow ((***))
25+import Control.Exception (catch, try)
26+import Control.Monad.Trans.Except
27+import Control.Monad.Error.Class (MonadError(..))
28+import Control.Monad.Writer
29+
30+import System.Directory (doesFileExist)
31+import System.Process (readProcess)
32+import System.Exit (exitFailure)
33+import qualified System.FilePath.Glob as Glob
34+
35+import Web.Bower.PackageMeta (PackageMeta(..), BowerError(..), PackageName,
36+ runPackageName, parsePackageName, Repository(..))
37+import qualified Web.Bower.PackageMeta as Bower
38+
39+import qualified Language.PureScript as P (version)
40+import qualified Language.PureScript.Docs as D
41+import Language.PureScript.Publish.Utils
42+import Language.PureScript.Publish.ErrorsWarnings
43+
44+-- | Attempt to retrieve package metadata from the current directory.
45+-- Calls exitFailure if no package metadata could be retrieved.
46+preparePackage :: IO D.UploadedPackage
47+preparePackage =
48+ runPrepareM preparePackage'
49+ >>= either (\e -> printError e >> exitFailure)
50+ handleWarnings
51+ where
52+ handleWarnings (result, warns) = do
53+ printWarnings warns
54+ return result
55+
56+newtype PrepareM a =
57+ PrepareM { unPrepareM :: WriterT [PackageWarning] (ExceptT PackageError IO) a }
58+ deriving (Functor, Applicative, Monad,
59+ MonadWriter [PackageWarning],
60+ MonadError PackageError)
61+
62+-- This MonadIO instance ensures that IO errors don't crash the program.
63+instance MonadIO PrepareM where
64+ liftIO act =
65+ lift' (try act) >>= either (otherError . IOExceptionThrown) return
66+ where
67+ lift' :: IO a -> PrepareM a
68+ lift' = PrepareM . lift . lift
69+
70+runPrepareM :: PrepareM a -> IO (Either PackageError (a, [PackageWarning]))
71+runPrepareM = runExceptT . runWriterT . unPrepareM
72+
73+warn :: PackageWarning -> PrepareM ()
74+warn w = tell [w]
75+
76+userError :: UserError -> PrepareM a
77+userError = throwError . UserError
78+
79+internalError :: InternalError -> PrepareM a
80+internalError = throwError . InternalError
81+
82+otherError :: OtherError -> PrepareM a
83+otherError = throwError . OtherError
84+
85+catchLeft :: Applicative f => Either a b -> (a -> f b) -> f b
86+catchLeft a f = either f pure a
87+
88+preparePackage' :: PrepareM D.UploadedPackage
89+preparePackage' = do
90+ exists <- liftIO (doesFileExist "bower.json")
91+ unless exists (userError BowerJSONNotFound)
92+
93+ pkgMeta <- liftIO (Bower.decodeFile "bower.json")
94+ >>= flip catchLeft (userError . CouldntParseBowerJSON)
95+ (pkgVersionTag, pkgVersion) <- getVersionFromGitTag
96+ pkgGithub <- getBowerInfo pkgMeta
97+ (pkgBookmarks, pkgModules) <- getModulesAndBookmarks
98+
99+ let declaredDeps = map fst (bowerDependencies pkgMeta ++
100+ bowerDevDependencies pkgMeta)
101+ pkgResolvedDependencies <- getResolvedDependencies declaredDeps
102+
103+ let pkgUploader = D.NotYetKnown
104+ let pkgCompilerVersion = P.version
105+
106+ return D.Package{..}
107+
108+getModulesAndBookmarks :: PrepareM ([D.Bookmark], [D.Module])
109+getModulesAndBookmarks = do
110+ (inputFiles, depsFiles) <- liftIO getInputAndDepsFiles
111+ liftIO (D.parseAndDesugar inputFiles depsFiles renderModules)
112+ >>= either (userError . ParseAndDesugarError) return
113+ where
114+ renderModules bookmarks modules =
115+ return (bookmarks, map D.convertModule modules)
116+
117+getVersionFromGitTag :: PrepareM (String, Version)
118+getVersionFromGitTag = do
119+ out <- readProcess' "git" ["tag", "--list", "--points-at", "HEAD"] ""
120+ let vs = map trimWhitespace (lines out)
121+ case mapMaybe parseMay vs of
122+ [] -> userError TagMustBeCheckedOut
123+ [x] -> return x
124+ xs -> userError (AmbiguousVersions (map snd xs))
125+ where
126+ trimWhitespace =
127+ dropWhile isSpace >>> reverse >>> dropWhile isSpace >>> reverse
128+ parseMay str =
129+ (str,) <$> D.parseVersion' (dropPrefix "v" str)
130+ dropPrefix prefix str =
131+ fromMaybe str (stripPrefix prefix str)
132+
133+getBowerInfo :: PackageMeta -> PrepareM (D.GithubUser, D.GithubRepo)
134+getBowerInfo = either (userError . BadRepositoryField) return . tryExtract
135+ where
136+ tryExtract pkgMeta =
137+ case bowerRepository pkgMeta of
138+ Nothing -> Left RepositoryFieldMissing
139+ Just Repository{..} -> do
140+ unless (repositoryType == "git")
141+ (Left (BadRepositoryType repositoryType))
142+ maybe (Left NotOnGithub) Right (extractGithub repositoryUrl)
143+
144+extractGithub :: String -> Maybe (D.GithubUser, D.GithubRepo)
145+extractGithub =
146+ stripPrefix "git://github.com/"
147+ >>> fmap (splitOn "/")
148+ >=> takeTwo
149+ >>> fmap (D.GithubUser *** (D.GithubRepo . dropDotGit))
150+
151+ where
152+ takeTwo :: [a] -> Maybe (a, a)
153+ takeTwo [x, y] = Just (x, y)
154+ takeTwo _ = Nothing
155+
156+ dropDotGit :: String -> String
157+ dropDotGit str
158+ | ".git" `isSuffixOf` str = take (length str - 4) str
159+ | otherwise = str
160+
161+readProcess' :: String -> [String] -> String -> PrepareM String
162+readProcess' prog args stdin = do
163+ out <- liftIO (catch (Right <$> readProcess prog args stdin)
164+ (return . Left))
165+ either (otherError . ProcessFailed prog args) return out
166+
167+data DependencyStatus
168+ = Missing
169+ -- ^ Listed in bower.json, but not installed.
170+ | NoResolution
171+ -- ^ In the output of `bower list --json --offline`, there was no
172+ -- _resolution key. This can be caused by adding the dependency using
173+ -- `bower link`, or simply copying it into bower_components instead of
174+ -- installing it normally.
175+ | ResolvedOther String
176+ -- ^ Resolved, but to something other than a version. The String argument
177+ -- is the resolution type. The values it can take that I'm aware of are
178+ -- "commit" and "branch".
179+ | ResolvedVersion String
180+ -- ^ Resolved to a version. The String argument is the resolution tag (eg,
181+ -- "v0.1.0").
182+ deriving (Show, Eq)
183+
184+-- Go through all bower dependencies which contain purescript code, and
185+-- extract their versions.
186+--
187+-- In the case where a bower dependency is taken from a particular version,
188+-- that's easy; take that version. In any other case (eg, a branch, or a commit
189+-- sha) we print a warning that documentation links will not work, and avoid
190+-- linking to documentation for any types from that package.
191+--
192+-- The rationale for this is: people will prefer to use a released version
193+-- where possible. If they are not using a released version, then this is
194+-- probably for a reason. However, docs are only ever available for released
195+-- versions. Therefore there will probably be no version of the docs which is
196+-- appropriate to link to, and we should omit links.
197+getResolvedDependencies :: [PackageName] -> PrepareM [(PackageName, Version)]
198+getResolvedDependencies declaredDeps = do
199+ depsBS <- fromString <$> readProcess' "bower" ["list", "--json", "--offline"] ""
200+
201+ -- Check for undeclared dependencies
202+ toplevels <- catchJSON (parse asToplevelDependencies depsBS)
203+ warnUndeclared declaredDeps toplevels
204+
205+ deps <- catchJSON (parse asResolvedDependencies depsBS)
206+ handleDeps deps
207+
208+ where
209+ catchJSON = flip catchLeft (internalError . JSONError FromBowerList)
210+
211+-- | Extracts all dependencies and their versions from
212+-- `bower list --json --offline`
213+asResolvedDependencies :: Parse BowerError [(PackageName, DependencyStatus)]
214+asResolvedDependencies = nubBy ((==) `on` fst) <$> go
215+ where
216+ go =
217+ fmap (fromMaybe []) $
218+ keyMay "dependencies" $
219+ (++) <$> eachInObjectWithKey (parsePackageName . T.unpack)
220+ asDependencyStatus
221+ <*> (concatMap snd <$> eachInObject asResolvedDependencies)
222+
223+-- | Extracts only the top level dependency names from the output of
224+-- `bower list --json --offline`
225+asToplevelDependencies :: Parse BowerError [PackageName]
226+asToplevelDependencies =
227+ fmap (map fst) $
228+ key "dependencies" $
229+ eachInObjectWithKey (parsePackageName . T.unpack) (return ())
230+
231+asDependencyStatus :: Parse e DependencyStatus
232+asDependencyStatus = do
233+ isMissing <- keyOrDefault "missing" False asBool
234+ if isMissing
235+ then
236+ return Missing
237+ else
238+ key "pkgMeta" $
239+ keyOrDefault "_resolution" NoResolution $ do
240+ type_ <- key "type" asString
241+ case type_ of
242+ "version" -> ResolvedVersion <$> key "tag" asString
243+ other -> return (ResolvedOther other)
244+
245+warnUndeclared :: [PackageName] -> [PackageName] -> PrepareM ()
246+warnUndeclared declared actual =
247+ mapM_ (warn . UndeclaredDependency) (actual \\ declared)
248+
249+handleDeps ::
250+ [(PackageName, DependencyStatus)] -> PrepareM [(PackageName, Version)]
251+handleDeps deps = do
252+ let (missing, noVersion, installed) = partitionDeps deps
253+ case missing of
254+ (x:xs) ->
255+ userError (MissingDependencies (x :| xs))
256+ [] -> do
257+ mapM_ (warn . NoResolvedVersion) noVersion
258+ withVersions <- catMaybes <$> mapM tryExtractVersion' installed
259+ filterM (liftIO . isPureScript . bowerDir . fst) withVersions
260+
261+ where
262+ partitionDeps = foldr go ([], [], [])
263+ go (pkgName, d) (ms, os, is) =
264+ case d of
265+ Missing -> (pkgName : ms, os, is)
266+ NoResolution -> (ms, pkgName : os, is)
267+ ResolvedOther _ -> (ms, pkgName : os, is)
268+ ResolvedVersion v -> (ms, os, (pkgName, v) : is)
269+
270+ bowerDir pkgName = "bower_components/" ++ runPackageName pkgName
271+
272+ -- Try to extract a version, and warn if unsuccessful.
273+ tryExtractVersion' pair =
274+ maybe (warn (UnacceptableVersion pair) >> return Nothing)
275+ (return . Just)
276+ (tryExtractVersion pair)
277+
278+tryExtractVersion :: (PackageName, String) -> Maybe (PackageName, Version)
279+tryExtractVersion (pkgName, tag) =
280+ let tag' = fromMaybe tag (stripPrefix "v" tag)
281+ in (pkgName,) <$> D.parseVersion' tag'
282+
283+-- | Returns whether it looks like there is a purescript package checked out
284+-- in the given directory.
285+isPureScript :: FilePath -> IO Bool
286+isPureScript dir = do
287+ files <- Glob.globDir1 purescriptSourceFiles dir
288+ return (not (null files))
289+
290+getInputAndDepsFiles :: IO ([FilePath], [(PackageName, FilePath)])
291+getInputAndDepsFiles = do
292+ inputFiles <- globRelative purescriptSourceFiles
293+ depsFiles' <- globRelative purescriptDepsFiles
294+ return (inputFiles, mapMaybe withPackageName depsFiles')
295+
296+withPackageName :: FilePath -> Maybe (PackageName, FilePath)
297+withPackageName fp = (,fp) <$> getPackageName fp
298+
299+getPackageName :: FilePath -> Maybe PackageName
300+getPackageName fp = do
301+ let xs = splitOn "/" fp
302+ ys <- stripPrefix ["bower_components"] xs
303+ y <- headMay ys
304+ case Bower.mkPackageName y of
305+ Right name -> Just name
306+ 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+2−2
@@ -1,7 +1,7 @@
11{-# LANGUAGE OverloadedStrings #-}
22{-# LANGUAGE RecordWildCards #-}
33
4−module ErrorsWarnings where
4+module Language.PureScript.Publish.ErrorsWarnings where
55
66import Control.Applicative ((<$>))
77import Data.Aeson.BetterErrors
@@ -22,7 +22,7 @@ import qualified Web.Bower.PackageMeta as Bower
2222import qualified Language.PureScript as P
2323import qualified Language.PureScript.Docs as D
2424
25−import BoxesHelpers
25+import Language.PureScript.Publish.BoxesHelpers
2626
2727-- | An error which meant that it was not possible to retrieve metadata for a
2828-- 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