git.iko.soy/rowtype-yoga/purescript-native
on be85d8d help sign in
623 lines 23 kB history raw
1-- |
2-- Bundles compiled PureScript modules for the browser.
3--
4-- This module takes as input the individual generated modules from 'Language.PureScript.Make' and
5-- performs dead code elimination, filters empty modules,
6-- and generates the final Javascript bundle.
7module Language.PureScript.Bundle
8 ( bundle
9 , guessModuleIdentifier
10 , ModuleIdentifier(..)
11 , moduleName
12 , ModuleType(..)
13 , ErrorMessage(..)
14 , printErrorMessage
15 , getExportedIdentifiers
16 ) where
18import Prelude.Compat
20import Control.Monad
21import Control.Monad.Error.Class
23import Data.Char (chr, digitToInt)
24import Data.Generics (everything, everywhere, mkQ, mkT)
25import Data.Graph
26import Data.List (nub, stripPrefix)
27import Data.Maybe (mapMaybe, catMaybes)
28import Data.Version (showVersion)
29import qualified Data.Set as S
31import Language.JavaScript.Parser
32import Language.JavaScript.Parser.AST
34import qualified Paths_purescript as Paths
36import System.FilePath (takeFileName, takeDirectory)
38-- | The type of error messages. We separate generation and rendering of errors using a data
39-- type, in case we need to match on error types later.
40data ErrorMessage
41 = UnsupportedModulePath String
42 | InvalidTopLevel
43 | UnableToParseModule String
44 | UnsupportedExport
45 | ErrorInModule ModuleIdentifier ErrorMessage
46 deriving (Show)
48-- | Modules are either "regular modules" (i.e. those generated by psc) or foreign modules.
49data ModuleType
50 = Regular
51 | Foreign
52 deriving (Show, Eq, Ord)
54showModuleType :: ModuleType -> String
55showModuleType Regular = "Regular"
56showModuleType Foreign = "Foreign"
58-- | A module is identified by its module name and its type.
59data ModuleIdentifier = ModuleIdentifier String ModuleType deriving (Show, Eq, Ord)
61moduleName :: ModuleIdentifier -> String
62moduleName (ModuleIdentifier name _) = name
64-- | Given a filename, assuming it is in the correct place on disk, infer a ModuleIdentifier.
65guessModuleIdentifier :: MonadError ErrorMessage m => FilePath -> m ModuleIdentifier
66guessModuleIdentifier filename = ModuleIdentifier (takeFileName (takeDirectory filename)) <$> guessModuleType (takeFileName filename)
67 where
68 guessModuleType "index.js" = pure Regular
69 guessModuleType "foreign.js" = pure Foreign
70 guessModuleType name = throwError $ UnsupportedModulePath name
72-- | A piece of code is identified by its module and its name. These keys are used to label vertices
73-- in the dependency graph.
74type Key = (ModuleIdentifier, String)
76-- | An export is either a "regular export", which exports a name from the regular module we are in,
77-- or a reexport of a declaration in the corresponding foreign module.
78--
79-- Regular exports are labelled, since they might re-export an operator with another name.
80data ExportType
81 = RegularExport String
82 | ForeignReexport
83 deriving (Show, Eq, Ord)
85-- | There are four types of module element we are interested in:
86--
87-- 1) Require statements
88-- 2) Member declarations
89-- 3) Export lists
90-- 4) Everything else
91--
92-- Each is labelled with the original AST node which generated it, so that we can dump it back
93-- into the output during codegen.
94data ModuleElement
95 = Require JSStatement String (Either String ModuleIdentifier)
96 | Member JSStatement Bool String JSExpression [Key]
97 | ExportsList [(ExportType, String, JSExpression, [Key])]
98 | Other JSStatement
99 deriving (Show)
101-- | A module is just a list of elements of the types listed above.
102data Module = Module ModuleIdentifier [ModuleElement] deriving (Show)
104-- | Prepare an error message for consumption by humans.
105printErrorMessage :: ErrorMessage -> [String]
106printErrorMessage (UnsupportedModulePath s) =
107 [ "A CommonJS module has an unsupported name (" ++ show s ++ ")."
108 , "The following file names are supported:"
109 , " 1) index.js (psc native modules)"
110 , " 2) foreign.js (psc foreign modules)"
111 ]
112printErrorMessage InvalidTopLevel =
113 [ "Expected a list of source elements at the top level." ]
114printErrorMessage (UnableToParseModule err) =
115 [ "The module could not be parsed:"
116 , err
117 ]
118printErrorMessage UnsupportedExport =
119 [ "An export was unsupported. Exports can be defined in one of two ways: "
120 , " 1) exports.name = ..."
121 , " 2) exports = { ... }"
122 ]
123printErrorMessage (ErrorInModule mid e) =
124 ("Error in module " ++ displayIdentifier mid ++ ":")
125 : ""
126 : map (" " ++) (printErrorMessage e)
127 where
128 displayIdentifier (ModuleIdentifier name ty) =
129 name ++ " (" ++ showModuleType ty ++ ")"
131-- | Calculate the ModuleIdentifier which a require(...) statement imports.
132checkImportPath :: String -> ModuleIdentifier -> S.Set String -> Either String ModuleIdentifier
133checkImportPath "./foreign" m _ =
134 Right (ModuleIdentifier (moduleName m) Foreign)
135checkImportPath name _ names
136 | Just name' <- stripPrefix "../" name
137 , name' `S.member` names = Right (ModuleIdentifier name' Regular)
138checkImportPath name _ _ = Left name
140-- | Compute the dependencies of all elements in a module, and add them to the tree.
141--
142-- Members and exports can have dependencies. A dependency is of one of the following forms:
143--
144-- 1) module.name or member["name"]
145--
146-- where module was imported using
147--
148-- var module = require("Module.Name");
149--
150-- 2) name
151--
152-- where name is the name of a member defined in the current module.
153withDeps :: Module -> Module
154withDeps (Module modulePath es) = Module modulePath (map expandDeps es)
155 where
156 -- | Collects all modules which are imported, so that we can identify dependencies of the first type.
157 imports :: [(String, ModuleIdentifier)]
158 imports = mapMaybe toImport es
159 where
160 toImport :: ModuleElement -> Maybe (String, ModuleIdentifier)
161 toImport (Require _ nm (Right mid)) = Just (nm, mid)
162 toImport _ = Nothing
164 -- | Collects all member names in scope, so that we can identify dependencies of the second type.
165 boundNames :: [String]
166 boundNames = mapMaybe toBoundName es
167 where
168 toBoundName :: ModuleElement -> Maybe String
169 toBoundName (Member _ _ nm _ _) = Just nm
170 toBoundName _ = Nothing
172 -- | Calculate dependencies and add them to the current element.
173 expandDeps :: ModuleElement -> ModuleElement
174 expandDeps (Member n f nm decl _) = Member n f nm decl (nub $ dependencies modulePath decl)
175 expandDeps (ExportsList exps) = ExportsList (map expand exps)
176 where
177 expand (ty, nm, n1, _) = (ty, nm, n1, nub (dependencies modulePath n1))
178 expandDeps other = other
180 dependencies :: ModuleIdentifier -> JSExpression -> [(ModuleIdentifier, String)]
181 dependencies m = everything (++) (mkQ [] toReference)
182 where
183 toReference :: JSExpression -> [(ModuleIdentifier, String)]
184 toReference (JSMemberDot mn _ nm)
185 | JSIdentifier _ mn' <- mn
186 , JSIdentifier _ nm' <- nm
187 , Just mid <- lookup mn' imports
188 = [(mid, nm')]
189 toReference (JSMemberSquare mn _ nm _)
190 | JSIdentifier _ mn' <- mn
191 , Just nm' <- fromStringLiteral nm
192 , Just mid <- lookup mn' imports
193 = [(mid, nm')]
194 toReference (JSIdentifier _ nm)
195 | nm `elem` boundNames
196 = [(m, nm)]
197 toReference _ = []
199-- String literals include the quote chars
200fromStringLiteral :: JSExpression -> Maybe String
201fromStringLiteral (JSStringLiteral _ str) = Just $ strValue str
202fromStringLiteral _ = Nothing
204strValue :: String -> String
205strValue str = go $ drop 1 str
206 where
207 go ('\\' : 'b' : xs) = '\b' : go xs
208 go ('\\' : 'f' : xs) = '\f' : go xs
209 go ('\\' : 'n' : xs) = '\n' : go xs
210 go ('\\' : 'r' : xs) = '\r' : go xs
211 go ('\\' : 't' : xs) = '\t' : go xs
212 go ('\\' : 'v' : xs) = '\v' : go xs
213 go ('\\' : '0' : xs) = '\0' : go xs
214 go ('\\' : 'x' : a : b : xs) = chr (a' + b') : go xs
215 where
216 a' = 16 * digitToInt a
217 b' = digitToInt b
218 go ('\\' : 'u' : a : b : c : d : xs) = chr (a' + b' + c' + d') : go xs
219 where
220 a' = 16 * 16 * 16 * digitToInt a
221 b' = 16 * 16 * digitToInt b
222 c' = 16 * digitToInt c
223 d' = digitToInt d
224 go ('\\' : x : xs) = x : go xs
225 go "\"" = ""
226 go "'" = ""
227 go (x : xs) = x : go xs
228 go "" = ""
230commaList :: JSCommaList a -> [a]
231commaList JSLNil = []
232commaList (JSLOne x) = [x]
233commaList (JSLCons l _ x) = commaList l ++ [x]
235trailingCommaList :: JSCommaTrailingList a -> [a]
236trailingCommaList (JSCTLComma l _) = commaList l
237trailingCommaList (JSCTLNone l) = commaList l
239-- | Attempt to create a Module from a Javascript AST.
240--
241-- Each type of module element is matched using pattern guards, and everything else is bundled into the
242-- Other constructor.
243toModule :: forall m. (MonadError ErrorMessage m) => S.Set String -> ModuleIdentifier -> JSAST -> m Module
244toModule mids mid top
245 | JSAstProgram smts _ <- top = Module mid <$> traverse toModuleElement smts
246 | otherwise = err InvalidTopLevel
247 where
248 err = throwError . ErrorInModule mid
250 toModuleElement :: JSStatement -> m ModuleElement
251 toModuleElement stmt
252 | Just (importName, importPath) <- matchRequire mids mid stmt
253 = pure (Require stmt importName importPath)
254 toModuleElement stmt
255 | Just (exported, name, decl) <- matchMember stmt
256 = pure (Member stmt exported name decl [])
257 toModuleElement stmt
258 | Just props <- matchExportsAssignment stmt
259 = (ExportsList <$> traverse toExport (trailingCommaList props))
260 where
261 toExport :: JSObjectProperty -> m (ExportType, String, JSExpression, [Key])
262 toExport (JSPropertyNameandValue name _ [val]) =
263 (,,val,[]) <$> exportType val
264 <*> extractLabel' name
265 toExport _ = err UnsupportedExport
267 exportType :: JSExpression -> m ExportType
268 exportType (JSMemberDot f _ _)
269 | JSIdentifier _ "$foreign" <- f
270 = pure ForeignReexport
271 exportType (JSMemberSquare f _ _ _)
272 | JSIdentifier _ "$foreign" <- f
273 = pure ForeignReexport
274 exportType (JSIdentifier _ s) = pure (RegularExport s)
275 exportType _ = err UnsupportedExport
277 extractLabel' = maybe (err UnsupportedExport) pure . extractLabel
279 toModuleElement other = pure (Other other)
281-- Get a list of all the exported identifiers from a foreign module.
282--
283-- TODO: what if we assign to exports.foo and then later assign to
284-- module.exports (presumably overwriting exports.foo)?
285getExportedIdentifiers :: (MonadError ErrorMessage m)
286 => String
287 -> JSAST
288 -> m [String]
289getExportedIdentifiers mname top
290 | JSAstProgram stmts _ <- top = concat <$> traverse go stmts
291 | otherwise = err InvalidTopLevel
292 where
293 err = throwError . ErrorInModule (ModuleIdentifier mname Foreign)
295 go stmt
296 | Just props <- matchExportsAssignment stmt
297 = traverse toIdent (trailingCommaList props)
298 | Just (True, name, _) <- matchMember stmt
299 = pure [name]
300 | otherwise
301 = pure []
303 toIdent (JSPropertyNameandValue name _ [_]) =
304 extractLabel' name
305 toIdent _ =
306 err UnsupportedExport
308 extractLabel' = maybe (err UnsupportedExport) pure . extractLabel
310-- Matches JS statements like this:
311-- var ModuleName = require("file");
312matchRequire :: S.Set String
313 -> ModuleIdentifier
314 -> JSStatement
315 -> Maybe (String, Either String ModuleIdentifier)
316matchRequire mids mid stmt
317 | JSVariable _ jsInit _ <- stmt
318 , [JSVarInitExpression var varInit] <- commaList jsInit
319 , JSIdentifier _ importName <- var
320 , JSVarInit _ jsInitEx <- varInit
321 , JSMemberExpression req _ argsE _ <- jsInitEx
322 , JSIdentifier _ "require" <- req
323 , [ Just importPath ] <- map fromStringLiteral (commaList argsE)
324 , importPath' <- checkImportPath importPath mid mids
325 = Just (importName, importPath')
326 | otherwise
327 = Nothing
329-- Matches JS member declarations.
330matchMember :: JSStatement -> Maybe (Bool, String, JSExpression)
331matchMember stmt
332 -- var foo = expr;
333 | JSVariable _ jsInit _ <- stmt
334 , [JSVarInitExpression var varInit] <- commaList jsInit
335 , JSIdentifier _ name <- var
336 , JSVarInit _ decl <- varInit
337 = Just (False, name, decl)
338 -- exports.foo = expr; exports["foo"] = expr;
339 | JSAssignStatement e (JSAssign _) decl _ <- stmt
340 , Just name <- accessor e
341 = Just (True, name, decl)
342 | otherwise
343 = Nothing
344 where
345 accessor :: JSExpression -> Maybe String
346 accessor (JSMemberDot exports _ nm)
347 | JSIdentifier _ "exports" <- exports
348 , JSIdentifier _ name <- nm
349 = Just name
350 accessor (JSMemberSquare exports _ nm _)
351 | JSIdentifier _ "exports" <- exports
352 , Just name <- fromStringLiteral nm
353 = Just name
354 accessor _ = Nothing
356-- Matches assignments to module.exports, like this:
357-- module.exports = { ... }
358matchExportsAssignment :: JSStatement -> Maybe JSObjectPropertyList
359matchExportsAssignment stmt
360 | JSAssignStatement e (JSAssign _) decl _ <- stmt
361 , JSMemberDot module' _ exports <- e
362 , JSIdentifier _ "module" <- module'
363 , JSIdentifier _ "exports" <- exports
364 , JSObjectLiteral _ props _ <- decl
365 = Just props
366 | otherwise
367 = Nothing
369extractLabel :: JSPropertyName -> Maybe String
370extractLabel (JSPropertyString _ nm) = Just $ strValue nm
371extractLabel (JSPropertyIdent _ nm) = Just nm
372extractLabel _ = Nothing
374-- | Eliminate unused code based on the specified entry point set.
375compile :: [Module] -> [ModuleIdentifier] -> [Module]
376compile modules [] = modules
377compile modules entryPoints = filteredModules
378 where
379 (graph, _, vertexFor) = graphFromEdges verts
381 -- | The vertex set
382 verts :: [(ModuleElement, Key, [Key])]
383 verts = do
384 Module mid els <- modules
385 concatMap (toVertices mid) els
386 where
387 -- | Create a set of vertices for a module element.
388 --
389 -- Some special cases worth commenting on:
390 --
391 -- 1) Regular exports which simply export their own name do not count as dependencies.
392 -- Regular exports which rename and reexport an operator do count, however.
393 --
394 -- 2) Require statements don't contribute towards dependencies, since they effectively get
395 -- inlined wherever they are used inside other module elements.
396 toVertices :: ModuleIdentifier -> ModuleElement -> [(ModuleElement, Key, [Key])]
397 toVertices p m@(Member _ _ nm _ deps) = [(m, (p, nm), deps)]
398 toVertices p m@(ExportsList exps) = mapMaybe toVertex exps
399 where
400 toVertex (ForeignReexport, nm, _, ks) = Just (m, (p, nm), ks)
401 toVertex (RegularExport nm, nm1, _, ks) | nm /= nm1 = Just (m, (p, nm1), ks)
402 toVertex _ = Nothing
403 toVertices _ _ = []
405 -- | The set of vertices whose connected components we are interested in keeping.
406 entryPointVertices :: [Vertex]
407 entryPointVertices = catMaybes $ do
408 (_, k@(mid, _), _) <- verts
409 guard $ mid `elem` entryPoints
410 return (vertexFor k)
412 -- | The set of vertices reachable from an entry point
413 reachableSet :: S.Set Vertex
414 reachableSet = S.fromList (concatMap (reachable graph) entryPointVertices)
416 filteredModules :: [Module]
417 filteredModules = map filterUsed modules
418 where
419 filterUsed :: Module -> Module
420 filterUsed (Module mid ds) = Module mid (map filterExports (go ds))
421 where
422 go :: [ModuleElement] -> [ModuleElement]
423 go [] = []
424 go (d : rest)
425 | not (isDeclUsed d) = go rest
426 | otherwise = d : go rest
428 -- | Filter out the exports for members which aren't used.
429 filterExports :: ModuleElement -> ModuleElement
430 filterExports (ExportsList exps) = ExportsList (filter (\(_, nm, _, _) -> isKeyUsed (mid, nm)) exps)
431 filterExports me = me
433 isDeclUsed :: ModuleElement -> Bool
434 isDeclUsed (Member _ _ nm _ _) = isKeyUsed (mid, nm)
435 isDeclUsed _ = True
437 isKeyUsed :: Key -> Bool
438 isKeyUsed k
439 | Just me <- vertexFor k = me `S.member` reachableSet
440 | otherwise = False
442-- | Topologically sort the module dependency graph, so that when we generate code, modules can be
443-- defined in the right order.
444sortModules :: [Module] -> [Module]
445sortModules modules = map (\v -> case nodeFor v of (n, _, _) -> n) (reverse (topSort graph))
446 where
447 (graph, nodeFor, _) = graphFromEdges $ do
448 m@(Module mid els) <- modules
449 return (m, mid, mapMaybe getKey els)
451 getKey :: ModuleElement -> Maybe ModuleIdentifier
452 getKey (Require _ _ (Right mi)) = Just mi
453 getKey _ = Nothing
455-- | A module is empty if it contains no exported members (in other words,
456-- if the only things left after dead code elimination are module imports and
457-- "other" foreign code).
458--
459-- If a module is empty, we don't want to generate code for it.
460isModuleEmpty :: Module -> Bool
461isModuleEmpty (Module _ els) = all isElementEmpty els
462 where
463 isElementEmpty :: ModuleElement -> Bool
464 isElementEmpty (ExportsList exps) = null exps
465 isElementEmpty Require{} = True
466 isElementEmpty (Other _) = True
467 isElementEmpty _ = False
469-- | Generate code for a set of modules, including a call to main().
470--
471-- Modules get defined on the global PS object, as follows:
472--
473-- var PS = { };
474-- (function(exports) {
475-- ...
476-- })(PS["Module.Name"] = PS["Module.Name"] || {});
477--
478-- In particular, a module and its foreign imports share the same namespace inside PS.
479-- This saves us from having to generate unique names for a module and its foreign imports,
480-- and is safe since a module shares a namespace with its foreign imports in PureScript as well
481-- (so there is no way to have overlaps in code generated by psc).
482codeGen :: Maybe String -- ^ main module
483 -> String -- ^ namespace
484 -> [Module] -- ^ input modules
485 -> String
486codeGen optionsMainModule optionsNamespace ms = renderToString (JSAstProgram (prelude : concatMap moduleToJS ms ++ maybe [] runMain optionsMainModule) JSNoAnnot)
487 where
488 moduleToJS :: Module -> [JSStatement]
489 moduleToJS (Module mn ds) = wrap (moduleName mn) (indent (concatMap declToJS ds))
490 where
491 declToJS :: ModuleElement -> [JSStatement]
492 declToJS (Member n _ _ _ _) = [n]
493 declToJS (Other n) = [n]
494 declToJS (Require _ nm req) =
495 [
496 JSVariable lfsp
497 (cList [
498 JSVarInitExpression (JSIdentifier sp nm)
499 (JSVarInit sp $ either require (moduleReference sp . moduleName) req )
500 ]) (JSSemi JSNoAnnot)
501 ]
502 declToJS (ExportsList exps) = map toExport exps
504 where
505 toExport :: (ExportType, String, JSExpression, [Key]) -> JSStatement
506 toExport (_, nm, val, _) =
507 JSAssignStatement
508 (JSMemberSquare (JSIdentifier lfsp "exports") JSNoAnnot
509 (str nm) JSNoAnnot)
510 (JSAssign sp)
511 val
512 (JSSemi JSNoAnnot)
514 -- comma lists are reverse-consed
515 cList :: [a] -> JSCommaList a
516 cList [] = JSLNil
517 cList [x] = JSLOne x
518 cList l = go $ reverse l
519 where
520 go [x] = JSLOne x
521 go (h:t)= JSLCons (go t) JSNoAnnot h
522 go [] = error "Invalid case in comma-list"
524 indent :: [JSStatement] -> [JSStatement]
525 indent = everywhere (mkT squash)
526 where
527 squash JSNoAnnot = (JSAnnot (TokenPn 0 0 2) [])
528 squash (JSAnnot pos ann) = JSAnnot (keepCol pos) (map splat ann)
530 splat (CommentA pos s) = CommentA (keepCol pos) s
531 splat (WhiteSpace pos w) = WhiteSpace (keepCol pos) w
532 splat ann = ann
534 keepCol (TokenPn _ _ c) = TokenPn 0 0 (if c >= 0 then c + 2 else 2)
536 prelude :: JSStatement
537 prelude = JSVariable (JSAnnot tokenPosnEmpty [ CommentA tokenPosnEmpty $ "// Generated by psc-bundle " ++ showVersion Paths.version
538 , WhiteSpace tokenPosnEmpty "\n" ])
539 (cList [
540 JSVarInitExpression (JSIdentifier sp optionsNamespace)
541 (JSVarInit sp (emptyObj sp))
542 ]) (JSSemi JSNoAnnot)
544 require :: String -> JSExpression
545 require mn =
546 JSMemberExpression (JSIdentifier JSNoAnnot "require") JSNoAnnot (cList [ str mn ]) JSNoAnnot
548 moduleReference :: JSAnnot -> String -> JSExpression
549 moduleReference a mn =
550 JSMemberSquare (JSIdentifier a optionsNamespace) JSNoAnnot
551 (str mn) JSNoAnnot
553 str :: String -> JSExpression
554 str s = JSStringLiteral JSNoAnnot $ "\"" ++ s ++ "\""
557 emptyObj :: JSAnnot -> JSExpression
558 emptyObj a = JSObjectLiteral a (JSCTLNone JSLNil) JSNoAnnot
560 wrap :: String -> [JSStatement] -> [JSStatement]
561 wrap mn ds =
562 [
563 JSMethodCall (JSExpressionParen lf (JSFunctionExpression JSNoAnnot JSIdentNone JSNoAnnot
564 (JSLOne (JSIdentName JSNoAnnot "exports")) JSNoAnnot
565 (JSBlock sp (lfHead ds) lf)) -- \n not quite in right place
566 JSNoAnnot)
567 JSNoAnnot
568 (JSLOne (JSAssignExpression (moduleReference JSNoAnnot mn) (JSAssign sp)
569 (JSExpressionBinary (moduleReference sp mn) (JSBinOpOr sp) (emptyObj sp))))
570 JSNoAnnot
571 (JSSemi JSNoAnnot)
572 ]
573 where
574 lfHead (h:t) = (addAnn (WhiteSpace tokenPosnEmpty "\n ") h) : t
575 lfHead x = x
577 addAnn :: CommentAnnotation -> JSStatement -> JSStatement
578 addAnn a (JSExpressionStatement (JSStringLiteral ann s) _) =
579 (JSExpressionStatement (JSStringLiteral (appendAnn a ann) s) (JSSemi JSNoAnnot))
580 addAnn _ x = x
582 appendAnn a JSNoAnnot = (JSAnnot tokenPosnEmpty [a])
583 appendAnn a (JSAnnot _ anns) = JSAnnot tokenPosnEmpty (a:anns ++ [WhiteSpace tokenPosnEmpty " "])
585 runMain :: String -> [JSStatement]
586 runMain mn =
587 [JSMethodCall
588 (JSMemberDot (moduleReference lf mn) JSNoAnnot
589 (JSIdentifier JSNoAnnot "main"))
590 JSNoAnnot (cList []) JSNoAnnot (JSSemi JSNoAnnot)]
592 lf :: JSAnnot
593 lf = JSAnnot tokenPosnEmpty [ WhiteSpace tokenPosnEmpty "\n" ]
596 lfsp :: JSAnnot
597 lfsp = JSAnnot tokenPosnEmpty [ WhiteSpace tokenPosnEmpty "\n " ]
599 sp :: JSAnnot
600 sp = JSAnnot tokenPosnEmpty [ WhiteSpace tokenPosnEmpty " " ]
602-- | The bundling function.
603-- This function performs dead code elimination, filters empty modules
604-- and generates and prints the final Javascript bundle.
605bundle :: (MonadError ErrorMessage m)
606 => [(ModuleIdentifier, String)] -- ^ The input modules. Each module should be javascript rendered from 'Language.PureScript.Make' or @psc@.
607 -> [ModuleIdentifier] -- ^ Entry points. These module identifiers are used as the roots for dead-code elimination
608 -> Maybe String -- ^ An optional main module.
609 -> String -- ^ The namespace (e.g. PS).
610 -> m String
611bundle inputStrs entryPoints mainModule namespace = do
612 input <- forM inputStrs $ \(ident, js) -> do
613 ast <- either (throwError . ErrorInModule ident . UnableToParseModule) pure $ parse js (moduleName ident)
614 return (ident, ast)
616 let mids = S.fromList (map (moduleName . fst) input)
618 modules <- traverse (fmap withDeps . uncurry (toModule mids)) input
620 let compiled = compile modules entryPoints
621 sorted = sortModules (filter (not . isModuleEmpty) compiled)
623 return (codeGen mainModule namespace sorted)