@@ -11,6 +11,7 @@ module Make
1111 where
1212
1313
14+import Control.Monad (when)
1415import qualified Data.ByteString.Builder as B
1516import qualified Data.Maybe as Maybe
1617import qualified Data.NonEmptyList as NE
@@ -23,7 +24,6 @@ import qualified System.Process as Process
2324import qualified AST.Optimized as Opt
2425import qualified BackgroundWriter as BW
2526import qualified Build
26−import qualified Data.Name as Name
2727import qualified Elm.Details as Details
2828import qualified Elm.ModuleName as ModuleName
2929import qualified File
@@ -123,10 +123,9 @@ runHelp root paths style (Flags debug optimize maybeOutput _ maybeDocs) =
123123 generate style target (Html.sandwich name builder) (NE.List name [])
124124
125125 Just (Bundle target) ->
126− do name <- hasOneMain artifacts
126+ do _name <- hasOneMain artifacts
127127 builder <- toBuilder root details desiredMode artifacts
128− let js = builder <> "\nvar app = Elm." <> Name.toBuilder name <> ".init({});\n"
129− Task.io $ bundle style target js name
128+ Task.io $ bundle style target builder (Build.getRootNames artifacts)
130129
131130
132131
@@ -340,47 +339,59 @@ isDevNull name =
340339-- BUNDLE
341340
342341
343−bundle :: Reporting.Style -> FilePath -> B.Builder -> Name.Name -> IO ()
344−bundle style target js name =
342+bundle :: Reporting.Style -> FilePath -> B.Builder -> NE.List ModuleName.Raw -> IO ()
343+bundle style target js names =
345344 do home <- Stuff.getElmHome
346345 let bundlerDir = home </> "bundler"
347346 let srcDir = bundlerDir </> "src"
348− let entryFile = srcDir </> "index.js"
347+ let elmFile = srcDir </> "elm.js"
349348 let nodeModules = bundlerDir </> "node_modules"
350349
351− -- Set up bundler project (first time)
350+ -- Set up bundler project
352351 Dir.createDirectoryIfMissing True srcDir
353352 setupBundlerProject bundlerDir
354353
355− -- Write generated JS as entry point
356− File.writeBuilder entryFile js
354+ -- Write compiled Elm JS
355+ File.writeBuilder elmFile js
357356
358357 -- Install deps if needed
359− hasNodeModules <- Dir.doesDirectoryExist nodeModules
360− if hasNodeModules
361− then return ()
362− else
363− do putStrLn "Installing bundler dependencies (first time)..."
364− callIn bundlerDir "npm" ["install"]
365−
366− -- Run rspeedy build
358+ let rspackDir = nodeModules </> "@rspack" </> "cli"
359+ hasCorrectDeps <- Dir.doesDirectoryExist rspackDir
360+ when (not hasCorrectDeps) $
361+ do putStrLn "Installing bundler dependencies..."
362+ callIn bundlerDir "npm" ["install"]
363+
364+ -- Assemble all.js and build bundle
367365 putStrLn "Bundling..."
368− callIn bundlerDir "bun" ["run", "rspeedy", "build", "--config", "lynx.config.mjs"]
366+ assembleAndBuild bundlerDir
369367
370− -- Find and copy the output bundle
368+ -- Copy the output bundle
371369 let distBundle = bundlerDir </> "dist" </> "main.lynx.bundle"
372370 Dir.createDirectoryIfMissing True (FP.takeDirectory target)
373371 Dir.copyFile distBundle target
374− Reporting.reportGenerate style (NE.List name []) target
372+ Reporting.reportGenerate style names target
373+
374+
375+assembleAndBuild :: FilePath -> IO ()
376+assembleAndBuild bundlerDir =
377+ do let srcDir = bundlerDir </> "src"
378+ stubs <- readFile (srcDir </> "stubs.js")
379+ elm <- readFile (srcDir </> "elm.js")
380+ initCode <- readFile (srcDir </> "init.js")
381+ writeFile (srcDir </> "all.js") (stubs ++ elm ++ initCode)
382+ callIn bundlerDir "npx" ["rspack", "build"]
375383
376384
377385setupBundlerProject :: FilePath -> IO ()
378386setupBundlerProject dir =
379387 do let pkgFile = dir </> "package.json"
380− let cfgFile = dir </> "lynx.config.mjs"
381− hasPkg <- Dir.doesFileExist pkgFile
382− if hasPkg then return () else writeFile pkgFile packageJson
383− writeFile cfgFile rspeedyConfig
388+ let cfgFile = dir </> "rspack.config.js"
389+ let stubsFile = dir </> "src" </> "stubs.js"
390+ let initFile = dir </> "src" </> "init.js"
391+ writeFile pkgFile packageJson
392+ writeFile cfgFile rspackConfig
393+ writeFile stubsFile stubsJs
394+ writeFile initFile initJs
384395
385396
386397callIn :: FilePath -> String -> [String] -> IO ()
@@ -399,23 +410,66 @@ packageJson = unlines
399410 [ "{"
400411 , " \"name\": \"lynxjs-elm-bundler\","
401412 , " \"private\": true,"
402− , " \"devDependencies\": {"
403− , " \"@lynx-js/rspeedy\": \"latest\","
404− , " \"@lynx-js/react-rsbuild-plugin\": \"latest\""
413+ , " \"type\": \"module\","
414+ , " \"dependencies\": {"
415+ , " \"@lynx-js/template-webpack-plugin\": \"0.6.4\","
416+ , " \"@rspack/cli\": \"^1.7.0\","
417+ , " \"@rspack/core\": \"^1.7.0\""
405418 , " }"
406419 , "}"
407420 ]
408421
409422
410−rspeedyConfig :: String
411−rspeedyConfig = unlines
412− [ "import { defineConfig } from '@lynx-js/rspeedy';"
413− , "import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';"
423+rspackConfig :: String
424+rspackConfig = unlines
425+ [ "import { LynxEncodePlugin, LynxTemplatePlugin } from '@lynx-js/template-webpack-plugin';"
426+ , "import { defineConfig } from '@rspack/cli';"
414427 , ""
415428 , "export default defineConfig({"
416− , " source: {"
417− , " entry: './src/index.js',"
418− , " },"
419− , " plugins: [pluginReactLynx()],"
429+ , " entry: { main: './src/all.js' },"
430+ , " plugins: ["
431+ , " new LynxEncodePlugin(),"
432+ , " new LynxTemplatePlugin({ filename: 'main.lynx.bundle', intermediate: 'main' }),"
433+ , " (compiler) => {"
434+ , " compiler.hooks.thisCompilation.tap('MarkMainThread', (compilation) => {"
435+ , " compilation.hooks.processAssets.tap('MarkMainThread', () => {"
436+ , " const asset = compilation.getAsset('main.js');"
437+ , " compilation.updateAsset(asset.name, asset.source, { ...asset.info, 'lynx:main-thread': true });"
438+ , " });"
439+ , " });"
440+ , " },"
441+ , " ]"
420442 , "});"
421443 ]
444+
445+
446+stubsJs :: String
447+stubsJs = unlines
448+ [ "globalThis['requestAnimationFrame'] = lynx['requestAnimationFrame'];"
449+ , "globalThis['cancelAnimationFrame'] = lynx['cancelAnimationFrame'];"
450+ , "globalThis['runWorklet'] = function(worklet, params) { return worklet(params); };"
451+ , "globalThis['native'] = {};"
452+ , "globalThis['processData'] = function() {};"
453+ , "globalThis['updatePage'] = function() {};"
454+ , "globalThis['updateGlobalProps'] = function() {};"
455+ ]
456+
457+
458+initJs :: String
459+initJs = unlines
460+ [ "globalThis['renderPage'] = function() {"
461+ , " var page = __CreatePage('0', 0);"
462+ , " var pageId = __GetElementUniqueID(page);"
463+ , " globalThis['native']['currentPageId'] = pageId;"
464+ , " globalThis['page'] = page;"
465+ , " var mods = globalThis.Elm;"
466+ , " if (mods) {"
467+ , " for (var k in mods) {"
468+ , " if (mods[k] && mods[k].init) {"
469+ , " mods[k].init({});"
470+ , " break;"
471+ , " }"
472+ , " }"
473+ , " }"
474+ , "};"
475+ ]