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