Skip to content

Commit

Permalink
Fix building with TS config (#2372)
Browse files Browse the repository at this point in the history
  • Loading branch information
sodic authored Nov 19, 2024
1 parent c350cbe commit 1e62987
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 33 deletions.
118 changes: 87 additions & 31 deletions waspc/cli/src/Wasp/Cli/Command/Build.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,40 @@ module Wasp.Cli.Command.Build
)
where

import Control.Lens
import Control.Monad (unless, when)
import Control.Monad.Except (throwError)
import Control.Monad.Except (ExceptT (ExceptT), runExceptT, throwError)
import Control.Monad.IO.Class (liftIO)
import Data.Aeson (Value (..))
import Data.Aeson.Lens
import qualified Data.HashMap.Strict as HM
import Data.List (isSuffixOf)
import Data.Text (Text, unpack)
import StrongPath (Abs, Dir, Path', castRel, (</>))
import qualified System.FilePath as FP
import Wasp.Cli.Command (Command, CommandError (..))
import Wasp.Cli.Command.Compile (compileIOWithOptions, printCompilationResult)
import Wasp.Cli.Command.Message (cliSendMessageC)
import Wasp.Cli.Command.Require (InWaspProject (InWaspProject), require)
import Wasp.Cli.Message (cliSendMessage)
import Wasp.CompileOptions (CompileOptions (..))
import qualified Wasp.Generator
import Wasp.Generator.Common (ProjectRootDir)
import Wasp.Generator.Monad (GeneratorWarning (GeneratorNeedsMigrationWarning))
import Wasp.Generator.SdkGenerator.Common (sdkRootDirInGeneratedCodeDir, sdkRootDirInProjectRootDir)
import qualified Wasp.Message as Msg
import Wasp.Project (CompileError, CompileWarning, WaspProjectDir)
import Wasp.Project.Common (buildDirInDotWaspDir, dotWaspDirInWaspProjectDir, generatedCodeDirInDotWaspDir, packageJsonInWaspProjectDir, packageLockJsonInWaspProjectDir, srcDirInWaspProjectDir)
import Wasp.Project.Common
( CompileError,
CompileWarning,
WaspProjectDir,
buildDirInDotWaspDir,
dotWaspDirInWaspProjectDir,
generatedCodeDirInDotWaspDir,
packageJsonInWaspProjectDir,
packageLockJsonInWaspProjectDir,
srcDirInWaspProjectDir,
)
import Wasp.Util.IO (copyDirectory, copyFile, doesDirectoryExist, removeDirectory)
import Wasp.Util.Json (updateJsonFile)

-- | Builds Wasp project that the current working directory is part of.
-- Does all the steps, from analysis to generation, and at the end writes generated code
Expand Down Expand Up @@ -58,40 +75,79 @@ build = do
throwError $
CommandError "Building of wasp project failed" $ show (length errors) ++ " errors found."

liftIO $ copyUserFilesNecessaryForBuild waspProjectDir buildDir
liftIO (prepareFilesNecessaryForDockerBuild waspProjectDir buildDir) >>= \case
Left err -> throwError $ CommandError "Failed to prepare files necessary for docker build" err
Right () -> return ()

cliSendMessageC $
Msg.Success "Your wasp project has been successfully built! Check it out in the .wasp/build directory."
where
-- Until we implement the solution described in https://github.com/wasp-lang/wasp/issues/1769,
-- we're copying all files and folders necessary for the build into the .wasp/build directory.
-- We chose this approach for 0.12.0 (instead of building from the project root) because:
-- - The build context remains small (~1.5 MB vs ~900 MB).
-- - We don't risk copying possible secrets from the project root into the build context.
-- - The commands for building the project stay the same as before
-- 0.12.0, which is good for both us (e.g., for fly deployment) and our
-- users (no changes in CI/CD scripts).
-- For more details, read the issue linked above.
copyUserFilesNecessaryForBuild waspProjectDir buildDir = do
copyDirectory
(waspProjectDir </> srcDirInWaspProjectDir)
(buildDir </> castRel srcDirInWaspProjectDir)

copyDirectory
(waspProjectDir </> dotWaspDirInWaspProjectDir </> generatedCodeDirInDotWaspDir </> sdkRootDirInGeneratedCodeDir)
(buildDir </> sdkRootDirInGeneratedCodeDir)

copyFile
(waspProjectDir </> packageJsonInWaspProjectDir)
(buildDir </> castRel packageJsonInWaspProjectDir)

copyFile
(waspProjectDir </> packageLockJsonInWaspProjectDir)
(buildDir </> castRel packageLockJsonInWaspProjectDir)
prepareFilesNecessaryForDockerBuild waspProjectDir buildDir = runExceptT $ do
-- Until we implement the solution described in https://github.com/wasp-lang/wasp/issues/1769,
-- we're copying all files and folders necessary for Docker build into the .wasp/build directory.
-- We chose this approach for 0.12.0 (instead of building from the project root) because:
-- - The Docker build context remains small (~1.5 MB vs ~900 MB).
-- - We don't risk copying possible secrets from the project root into Docker's build context.
-- - The commands for building the project stay the same as before
-- 0.12.0, which is good for both us (e.g., for fly deployment) and our
-- users (no changes in CI/CD scripts).
-- For more details, read the issue linked above.
liftIO $
copyDirectory
(waspProjectDir </> srcDirInWaspProjectDir)
(buildDir </> castRel srcDirInWaspProjectDir)

liftIO $
copyDirectory
(waspProjectDir </> dotWaspDirInWaspProjectDir </> generatedCodeDirInDotWaspDir </> sdkRootDirInGeneratedCodeDir)
(buildDir </> sdkRootDirInGeneratedCodeDir)

let packageJsonInBuildDir = buildDir </> castRel packageJsonInWaspProjectDir
let packageLockJsonInBuildDir = buildDir </> castRel packageLockJsonInWaspProjectDir

liftIO $
copyFile
(waspProjectDir </> packageJsonInWaspProjectDir)
packageJsonInBuildDir

liftIO $
copyFile
(waspProjectDir </> packageLockJsonInWaspProjectDir)
packageLockJsonInBuildDir

-- A hacky quick fix for https://github.com/wasp-lang/wasp/issues/2368
-- We should remove this code once we implement a proper solution.
ExceptT $ updateJsonFile removeWaspConfigFromDevDependenciesArray packageJsonInBuildDir
ExceptT $ updateJsonFile removeAllMentionsOfWaspConfigInPackageLockJson packageLockJsonInBuildDir

removeAllMentionsOfWaspConfigInPackageLockJson :: Value -> Value
removeAllMentionsOfWaspConfigInPackageLockJson packageLockJsonObject =
-- We want to:
-- 1. Remove the `wasp-config` dev dependency from the root package in package-lock.json.
-- This is at `packageLock["packages"][""]["wasp-config"]`.
-- 2. Remove all package location entries for the `wasp-config` package
-- (i.e., entries whose location keys end in `/wasp-config`).
-- Example locations include:
-- packageLock["packages"]["../../data/packages/wasp-config"]
-- packageLock["packages"]["node_modules/wasp-config"]
-- packageLock["packages"]["/home/filip/../wasp-config"]
packageLockJsonObject
& key "packages" . key "" %~ removeWaspConfigFromDevDependenciesArray
& key "packages" . _Object
%~ HM.filterWithKey
(\packageLocation _ -> not $ isWaspConfigPackageLocation packageLocation)

isWaspConfigPackageLocation :: Text -> Bool
isWaspConfigPackageLocation packageLocation =
(FP.pathSeparator : "wasp-config") `isSuffixOf` unpack packageLocation

removeWaspConfigFromDevDependenciesArray :: Value -> Value
removeWaspConfigFromDevDependenciesArray original =
original & key "devDependencies" . _Object . at "wasp-config" .~ Nothing

buildIO ::
Path' Abs (Dir WaspProjectDir) ->
Path' Abs (Dir Wasp.Generator.ProjectRootDir) ->
Path' Abs (Dir ProjectRootDir) ->
IO ([CompileWarning], [CompileError])
buildIO waspProjectDir buildDir = compileIOWithOptions options waspProjectDir buildDir
where
Expand Down
4 changes: 4 additions & 0 deletions waspc/src/Wasp/Util/IO.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module Wasp.Util.IO
isDirectoryEmpty,
writeFileFromText,
readFileBytes,
writeFileBytes,
)
where

Expand Down Expand Up @@ -115,6 +116,9 @@ readFileStrict = T.IO.readFile . SP.toFilePath
writeFile :: Path' Abs (File f) -> String -> IO ()
writeFile = P.writeFile . SP.fromAbsFile

writeFileBytes :: Path' Abs (File f) -> B.ByteString -> IO ()
writeFileBytes = B.writeFile . SP.fromAbsFile

writeFileFromText :: Path' Abs (File f) -> Text -> IO ()
writeFileFromText = T.IO.writeFile . SP.fromAbsFile

Expand Down
20 changes: 18 additions & 2 deletions waspc/src/Wasp/Util/Json.hs
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
module Wasp.Util.Json (parseJsonWithComments) where
module Wasp.Util.Json
( parseJsonWithComments,
updateJsonFile,
)
where

import Data.Aeson (FromJSON)
import Control.Monad.Except (ExceptT (..), runExceptT)
import Control.Monad.IO.Class (liftIO)
import Data.Aeson (FromJSON, Value (..), eitherDecode, encode)
import StrongPath (Abs, File, Path')
import System.Exit (ExitCode (..))
import qualified System.Process as P
import Wasp.Util.Aeson (decodeFromString)
import qualified Wasp.Util.IO as IOUtil

-- | Uses Node.js to parse JSON with comments by treating it as a JavaScript object.
-- We use this technique because Aeson can't read JSON with comments and we didn't want to write
Expand All @@ -16,3 +24,11 @@ parseJsonWithComments jsonStr = do
case exitCode of
ExitSuccess -> return $ decodeFromString response
_exitFailure -> return $ Left stderr

updateJsonFile :: (Value -> Value) -> Path' Abs (File a) -> IO (Either String ())
updateJsonFile updateFn jsonFilePath = runExceptT $ do
jsonContent <- ExceptT $ eitherDecode <$> IOUtil.readFileBytes jsonFilePath
liftIO $ writeJsonValue jsonFilePath $ updateFn jsonContent

writeJsonValue :: Path' Abs (File f) -> Value -> IO ()
writeJsonValue file = IOUtil.writeFileBytes file . encode
3 changes: 3 additions & 0 deletions waspc/waspc.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,8 @@ library cli-lib
, mtl
, async
, exceptions
, lens ^>=5.1
, lens-aeson ^>=1.1.3
, cryptonite
, fsnotify
, http-conduit
Expand All @@ -521,6 +523,7 @@ library cli-lib
, waspls
, neat-interpolation
, unliftio ^>= 0.2.20
, unordered-containers ^>= 0.2.16
, bytestring ^>= 0.10.12
, tar ^>=0.5.1.1
, zlib ^>=0.6.3.0
Expand Down

0 comments on commit 1e62987

Please sign in to comment.