@@ -17,6 +17,7 @@ import qualified Data.List.NonEmpty as NonEmpty
17
17
import qualified Data.Map as Map
18
18
import qualified Data.Set as Set
19
19
import qualified Data.Text as Text
20
+ import qualified Data.Versions as Version
20
21
import System.Directory (getCurrentDirectory )
21
22
import System.FilePath (splitDirectories )
22
23
import qualified System.FilePath.Glob as Glob
@@ -36,6 +37,7 @@ import qualified Spago.Packages as Packages
36
37
import qualified Spago.Purs as Purs
37
38
import qualified Spago.Templates as Templates
38
39
import qualified Spago.Watch as Watch
40
+ import qualified Spago.Cmd as Cmd
39
41
40
42
41
43
prepareBundleDefaults
@@ -50,7 +52,7 @@ prepareBundleDefaults maybeModuleName maybeTargetPath maybePlatform = (moduleNam
50
52
platform = fromMaybe Browser maybePlatform
51
53
52
54
-- eventually running some other action after the build
53
- build :: HasBuildEnv env => Maybe (RIO Env () ) -> RIO env ()
55
+ build :: HasBuildEnv env => Maybe (RIO env () ) -> RIO env ()
54
56
build maybePostBuild = do
55
57
logDebug " Running `spago build`"
56
58
BuildOptions {.. } <- view (the @ BuildOptions )
@@ -143,8 +145,7 @@ build maybePostBuild = do
143
145
checkImports
144
146
145
147
buildAction globs = do
146
- env <- Run. getEnv
147
- let action = buildBackend globs >> (runRIO env $ fromMaybe (pure () ) maybePostBuild)
148
+ let action = buildBackend globs >> (fromMaybe (pure () ) maybePostBuild)
148
149
runCommands " Before" beforeCommands
149
150
action `onException` (runCommands " Else" elseCommands)
150
151
runCommands " Then" thenCommands
@@ -309,6 +310,22 @@ script modulePath tag packageDeps opts = do
309
310
310
311
data RunDirectories = RunDirectories { sourceDir :: FilePath , executeDir :: FilePath }
311
312
313
+ data NodeEsSupport = Unsupported Version. SemVer | Experimental | Supported
314
+
315
+ hasNodeEsSupport :: (HasLogFunc env ) => RIO env NodeEsSupport
316
+ hasNodeEsSupport = do
317
+ nodeVersion <- Cmd. getCmdVersion " node"
318
+ case nodeVersion of
319
+ Left err -> do
320
+ logDebug $ display $ " Unable to get Node.js version: " <> displayShow err
321
+ pure Supported
322
+ Right nv@ Version. SemVer {} | Version. _svMajor nv < 12 ->
323
+ pure $ Unsupported nv
324
+ Right nv@ Version. SemVer {} | Version. _svMajor nv >= 12 && Version. _svMajor nv < 13 ->
325
+ pure Experimental
326
+ _ -> pure Supported
327
+
328
+
312
329
-- | Run the project with node (or the chosen alternate backend):
313
330
-- compile and run the provided ModuleName
314
331
runBackend
@@ -323,15 +340,17 @@ runBackend
323
340
runBackend maybeBackend RunDirectories { sourceDir, executeDir } moduleName maybeSuccessMessage failureMessage extraArgs = do
324
341
logDebug $ display $ " Running with backend: " <> fromMaybe " nodejs" maybeBackend
325
342
BuildOptions { pursArgs } <- view (the @ BuildOptions )
326
- isES <- Purs. hasMinPursVersion " 0.15.0 "
327
- let postBuild = maybe (nodeAction isES $ Path. getOutputPath pursArgs) backendAction maybeBackend
343
+ let
344
+ postBuild = maybe (nodeAction $ Path. getOutputPath pursArgs) backendAction maybeBackend
328
345
build (Just postBuild)
329
346
where
330
347
fromFilePath = Text. pack . Turtle. encodeString
348
+ runJsSource = fromFilePath (sourceDir Turtle. </> " .spago/run.js" )
349
+ packageJson = fromFilePath (sourceDir Turtle. </> " .spago/package.json" )
331
350
nodeArgs = Text. intercalate " " $ map unBackendArg extraArgs
332
351
esContents outputPath' =
333
352
fold
334
- [ " import { main } from '"
353
+ [ " import { main } from 'file:// "
335
354
, Text. replace " \\ " " /" (fromFilePath sourceDir)
336
355
, " /"
337
356
, Text. pack outputPath'
@@ -352,19 +371,36 @@ runBackend maybeBackend RunDirectories{ sourceDir, executeDir } moduleName maybe
352
371
, unModuleName moduleName
353
372
, " ').main()"
354
373
]
355
- nodeCmd isES outputPath'=
356
- if isES then
357
- " node --input-type=module -e \" " <> esContents outputPath' <> " \" -- " <> nodeArgs
358
- else
359
- " node -e \" " <> cjsContents outputPath' <> " \" -- " <> nodeArgs
360
-
361
- nodeAction isES outputPath' = do
374
+ nodeContents isES outputPath' =
375
+ if isES then esContents outputPath'
376
+ else cjsContents outputPath'
377
+
378
+ packageJsonContents = " {\" type\" :\" module\" }"
379
+
380
+ nodeCmd isES Experimental | isES = " node --experimental-modules \" " <> runJsSource <> " \" " <> nodeArgs
381
+ nodeCmd _ _ = " node \" " <> runJsSource <> " \" " <> nodeArgs
382
+
383
+ nodeAction outputPath' = do
384
+ isES <- Purs. hasMinPursVersion " 0.15.0-alpha-01"
385
+ nodeVersion <- hasNodeEsSupport
386
+ case (isES, nodeVersion) of
387
+ (True , Unsupported nv) ->
388
+ die [ " Unsupported Node.js version: " <> display (Version. prettySemVer nv), " Required Node.js version >=12." ]
389
+ _ ->
390
+ pure ()
391
+ logDebug $ " Writing " <> displayShow @ Text runJsSource
392
+ writeTextFile runJsSource (nodeContents isES outputPath')
393
+ void $ chmod executable $ pathFromText runJsSource
394
+ if isES then do
395
+ logDebug $ " Writing " <> displayShow @ Text packageJson
396
+ writeTextFile packageJson packageJsonContents
397
+ else pure ()
362
398
-- cd to executeDir in case it isn't the same as sourceDir
363
399
logDebug $ " Executing from: " <> displayShow @ FilePath executeDir
364
400
Turtle. cd executeDir
365
401
-- We build a process by hand here because we need to forward the stdin to the backend process
366
- logDebug $ " Running node command: `" <> display (nodeCmd isES outputPath' ) <> " `"
367
- let processWithStdin = (Process. shell (Text. unpack $ nodeCmd isES outputPath' )) { Process. std_in = Process. Inherit }
402
+ logDebug $ " Running node command: `" <> display (nodeCmd isES nodeVersion ) <> " `"
403
+ let processWithStdin = (Process. shell (Text. unpack $ nodeCmd isES nodeVersion )) { Process. std_in = Process. Inherit }
368
404
Turtle. system processWithStdin empty >>= \ case
369
405
ExitSuccess -> maybe (pure () ) (logInfo . display) maybeSuccessMessage
370
406
ExitFailure n -> die [ display failureMessage <> " exit code: " <> repr n ]
@@ -376,26 +412,28 @@ runBackend maybeBackend RunDirectories{ sourceDir, executeDir } moduleName maybe
376
412
ExitFailure n -> die [ display failureMessage <> " Backend " <> displayShow backend <> " exited with error:" <> repr n ]
377
413
378
414
379
- bundleWithEsbuild :: HasLogFunc env => WithMain -> ModuleName -> TargetPath -> Platform -> Minify -> RIO env ()
380
- bundleWithEsbuild withMain (ModuleName moduleName) (TargetPath targetPath) platform minify = do
415
+ bundleWithEsbuild :: HasLogFunc env => WithMain -> WithSrcMap -> ModuleName -> TargetPath -> Platform -> Minify -> RIO env ()
416
+ bundleWithEsbuild withMain srcMap (ModuleName moduleName) (TargetPath targetPath) platform minify = do
381
417
esbuild <- getESBuild
382
418
let
383
419
platformOpt = case platform of
384
- Browser -> " browser"
385
- Node -> " node"
420
+ Browser -> [ " --platform= browser" ]
421
+ Node -> [ " --platform= node" ]
386
422
minifyOpt = case minify of
387
- NoMinify -> " "
388
- Minify -> " --minify"
389
- cmd = case withMain of
390
- WithMain ->
391
- " echo \" import { main } from './output/" <> moduleName <> " /index.js'\n main()\" | "
392
- <> esbuild <> " --platform=" <> platformOpt <> minifyOpt <> " --bundle "
393
- <> " --outfile=" <> targetPath
423
+ NoMinify -> []
424
+ Minify -> [" --minify" ]
425
+ srcMapOpt = case srcMap of
426
+ WithSrcMap -> [" --sourcemap" ]
427
+ WithoutSrcMap -> []
428
+ esbuildBase = platformOpt <> minifyOpt <> srcMapOpt <> [" --format=esm" , " --bundle" , " --outfile=" <> targetPath]
429
+ (input, cmd) = case withMain of
430
+ WithMain -> do
431
+ let
432
+ echoLine = " import { main } from './output/" <> moduleName <> " /index.js'; main();"
433
+ (Turtle. textToLine echoLine, esbuild :| esbuildBase)
394
434
WithoutMain ->
395
- esbuild <> " --platform=" <> platformOpt <> minifyOpt <> " --bundle "
396
- <> " output/" <> moduleName <> " /index.js"
397
- <> " --outfile=" <> targetPath
398
- runWithOutput cmd
435
+ (Nothing , esbuild :| esbuildBase <> [" output/" <> moduleName <> " /index.js" ])
436
+ runProcessWithOutput cmd input
399
437
(" Bundle succeeded and output file to " <> targetPath)
400
438
" Bundle failed."
401
439
where
@@ -414,12 +452,12 @@ bundleModule
414
452
-> UsePsa
415
453
-> RIO env ()
416
454
bundleModule withMain BundleOptions { maybeModuleName, maybeTargetPath, maybePlatform, minify, noBuild } buildOpts usePsa = do
417
- isES <- Purs. hasMinPursVersion " 0.15.0"
455
+ isES <- Purs. hasMinPursVersion " 0.15.0-alpha-01 "
418
456
let
419
457
(moduleName, targetPath, platform) = prepareBundleDefaults maybeModuleName maybeTargetPath maybePlatform
420
458
bundleAction =
421
459
if isES then
422
- bundleWithEsbuild withMain moduleName targetPath platform minify
460
+ bundleWithEsbuild withMain (withSourceMap buildOpts) moduleName targetPath platform minify
423
461
else case withMain of
424
462
WithMain -> Purs. bundle WithMain (withSourceMap buildOpts) moduleName targetPath
425
463
WithoutMain ->
@@ -437,7 +475,7 @@ bundleModule withMain BundleOptions { maybeModuleName, maybeTargetPath, maybePla
437
475
Left (n :: SomeException ) -> die [ " Make module failed: " <> repr n ]
438
476
case noBuild of
439
477
DoBuild -> Run. withBuildEnv usePsa buildOpts $ build (Just bundleAction)
440
- NoBuild -> Run. getEnv >>= ( flip runRIO) bundleAction
478
+ NoBuild -> Run. withBuildEnv usePsa buildOpts bundleAction
441
479
442
480
docsSearchTemplate :: (HasType LogFunc env , HasType PursCmd env ) => RIO env Text
443
481
docsSearchTemplate = ifM (Purs. hasMinPursVersion " 0.14.0" )
0 commit comments