diff --git a/hix.cabal b/hix.cabal
--- a/hix.cabal
+++ b/hix.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           hix
-version:        0.1.1.0
+version:        0.2.0.0
 synopsis:       Haskell/Nix development build tools
 description:    See https://hackage.haskell.org/package/hix/docs/Hix.html
 category:       Build
@@ -77,14 +77,14 @@
   ghc-options: -Wall -Widentities -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Wredundant-constraints -Wunused-type-patterns -Wunused-packages
   build-depends:
       Cabal
-    , aeson ==2.0.*
+    , aeson >=2.0 && <2.2
     , base ==4.*
     , exon ==1.4.*
     , extra ==1.7.*
     , filepattern ==0.1.*
     , generic-lens ==2.2.*
     , incipit-base ==0.5.*
-    , lens ==5.1.*
+    , lens >=5.1 && <5.3
     , lens-regex-pcre ==1.1.*
     , optparse-applicative ==0.17.*
     , path ==0.9.*
@@ -198,13 +198,13 @@
       Cabal
     , base ==4.*
     , exon ==1.4.*
-    , hedgehog ==1.1.*
+    , hedgehog >=1.1 && <1.3
     , hix
     , incipit-base ==0.5.*
     , path ==0.9.*
     , path-io ==1.7.*
     , tasty ==1.4.*
-    , tasty-hedgehog ==1.3.*
+    , tasty-hedgehog >=1.3 && <1.5
     , transformers
   mixins:
       base hiding (Prelude)
diff --git a/lib/Hix.hs b/lib/Hix.hs
--- a/lib/Hix.hs
+++ b/lib/Hix.hs
@@ -1,9 +1,8 @@
 module Hix where
 
-import Control.Monad.Trans.Class (lift)
 import Path.IO (getCurrentDir)
 
-import Hix.Data.Error (Error (..), printEnvError, printGhciError, printPreprocError, printFatalError)
+import Hix.Data.Error (Error (..), printEnvError, printFatalError, printGhciError, printPreprocError)
 import Hix.Env (printEnvRunner)
 import Hix.Ghci (printGhciCmdline, printGhcidCmdline)
 import Hix.Monad (M, runM)
@@ -31,7 +30,7 @@
 
 runCommand :: Command -> M ()
 runCommand = \case
-  Preproc opts -> lift (preprocess opts)
+  Preproc opts -> preprocess opts
   EnvRunner opts -> printEnvRunner opts.options
   GhcidCmd opts -> printGhcidCmdline opts
   GhciCmd opts -> printGhciCmdline opts
diff --git a/lib/Hix/Cabal.hs b/lib/Hix/Cabal.hs
--- a/lib/Hix/Cabal.hs
+++ b/lib/Hix/Cabal.hs
@@ -1,8 +1,14 @@
+{-# language CPP #-}
+
 module Hix.Cabal where
 
 import Control.Monad.Trans.Except (ExceptT (ExceptT), throwE)
 import Distribution.PackageDescription (BuildInfo (..), GenericPackageDescription (..))
+#if MIN_VERSION_Cabal(3,8,0)
+import Distribution.Simple.PackageDescription (readGenericPackageDescription)
+#else
 import Distribution.PackageDescription.Parsec (readGenericPackageDescription)
+#endif
 import Distribution.Types.Benchmark (benchmarkBuildInfo)
 import Distribution.Types.CondTree (CondTree (..))
 import qualified Distribution.Types.Executable as Executable
diff --git a/lib/Hix/Data/GhciConfig.hs b/lib/Hix/Data/GhciConfig.hs
--- a/lib/Hix/Data/GhciConfig.hs
+++ b/lib/Hix/Data/GhciConfig.hs
@@ -1,6 +1,6 @@
 module Hix.Data.GhciConfig where
 
-import Data.Aeson (FromJSON (parseJSON), FromJSONKey)
+import Data.Aeson (FromJSON (parseJSON), FromJSONKey, withObject, (.:))
 import GHC.Exts (IsList)
 import Path (Abs, Dir, File, Path, Rel)
 
@@ -49,11 +49,42 @@
   deriving stock (Eq, Show, Generic)
   deriving newtype (FromJSON)
 
+data PreludePackage =
+  PreludePackageName Text
+  |
+  PreludePackageSpec { name :: Text }
+  deriving stock (Eq, Show, Generic)
+
+instance FromJSON PreludePackage where
+  parseJSON v =
+    hpackStruct v <|> plainName
+    where
+      hpackStruct = withObject "PreludePackageSpec" \ o -> o .: "name"
+      plainName = PreludePackageName <$> parseJSON v
+
+data PreludeConfig =
+  PreludeConfig {
+    package :: PreludePackage,
+    module_ :: ModuleName
+  }
+  deriving stock (Eq, Show, Generic)
+
+instance FromJSON PreludeConfig where
+  parseJSON =
+    withObject "PreludeConfig" \ o -> do
+      package <- o .: "package"
+      module_ <- o .: "module"
+      pure PreludeConfig {..}
+
 data ComponentConfig =
   ComponentConfig {
     name :: ComponentName,
     sourceDirs :: SourceDirs,
-    runner :: Maybe EnvRunner
+    runner :: Maybe EnvRunner,
+    extensions :: [String],
+    language :: String,
+    ghcOptions :: [String],
+    prelude :: Maybe PreludeConfig
   }
   deriving stock (Eq, Show, Generic)
   deriving anyclass (FromJSON)
@@ -111,6 +142,13 @@
     setup :: Map RunnerName GhciSetupCode,
     run :: Map RunnerName GhciRunExpr,
     args :: GhciArgs
+  }
+  deriving stock (Eq, Show, Generic)
+  deriving anyclass (FromJSON)
+
+data PreprocConfig =
+  PreprocConfig {
+    packages :: PackagesConfig
   }
   deriving stock (Eq, Show, Generic)
   deriving anyclass (FromJSON)
diff --git a/lib/Hix/Monad.hs b/lib/Hix/Monad.hs
--- a/lib/Hix/Monad.hs
+++ b/lib/Hix/Monad.hs
@@ -5,7 +5,7 @@
 import Control.Monad.Trans.Reader (ReaderT (runReaderT))
 import Path (Abs, Dir, Path)
 
-import Hix.Data.Error (Error (GhciError), tryIO)
+import Hix.Data.Error (Error (EnvError, GhciError), tryIO)
 
 data Env =
   Env {
@@ -17,7 +17,7 @@
 
 noteEnv :: Text -> Maybe a -> M a
 noteEnv err =
-  maybe (lift (throwE (GhciError err))) pure
+  maybe (lift (throwE (EnvError err))) pure
 
 noteGhci :: Text -> Maybe a -> M a
 noteGhci err =
diff --git a/lib/Hix/Options.hs b/lib/Hix/Options.hs
--- a/lib/Hix/Options.hs
+++ b/lib/Hix/Options.hs
@@ -35,6 +35,7 @@
   GhciConfig,
   ModuleName,
   PackageName (PackageName),
+  PreprocConfig,
   RunnerName,
   SourceDir (SourceDir),
   )
@@ -42,11 +43,12 @@
 
 data PreprocOptions =
   PreprocOptions {
+    config :: Maybe (Either PreprocConfig JsonConfig),
     source :: Path Abs File,
     inFile :: Path Abs File,
     outFile :: Path Abs File
   }
-  deriving stock (Eq, Show, Generic)
+  deriving stock (Show, Generic)
 
 data PackageSpec =
   PackageSpec {
@@ -136,10 +138,17 @@
 fileParser longName helpText =
   option absFileOption (long longName <> completer (bashCompleter "file") <> help helpText)
 
+jsonConfigParser ::
+  Parser JsonConfig
+jsonConfigParser =
+  option jsonOption (long "config" <> short 'c' <> help "The Hix-generated config, file or text")
+
 preprocParser :: Parser PreprocOptions
 preprocParser =
   PreprocOptions
   <$>
+  (fmap Right <$> optional jsonConfigParser)
+  <*>
   fileParser "source" "The original source file"
   <*>
   fileParser "in" "The prepared input file"
@@ -199,11 +208,6 @@
 moduleParser :: Parser ModuleName
 moduleParser =
   strOption (long "module" <> short 'm' <> help "The module containing the test function" <> value "Main")
-
-jsonConfigParser ::
-  Parser JsonConfig
-jsonConfigParser =
-  option jsonOption (long "config" <> short 'c' <> help "The Hix-generated config, file or text")
 
 testOptionsParser :: Parser TestOptions
 testOptionsParser = do
diff --git a/lib/Hix/Preproc.hs b/lib/Hix/Preproc.hs
--- a/lib/Hix/Preproc.hs
+++ b/lib/Hix/Preproc.hs
@@ -2,6 +2,7 @@
 
 import Control.Lens (IndexedTraversal', has, index, ix, preview, (%~), (.~), (^..))
 import Control.Lens.Regex.ByteString (Match, group, groups, match, regex)
+import Control.Monad.Trans.Class (lift)
 import Control.Monad.Trans.Except (ExceptT, throwE)
 import qualified Data.ByteString as ByteString
 import Data.ByteString (elemIndex)
@@ -26,8 +27,14 @@
 import System.Random (randomRIO)
 
 import Hix.Cabal (buildInfoForFile)
+import Hix.Component (targetComponent)
 import Hix.Data.Error (Error (..), sourceError, tryIO)
-import Hix.Options (PreprocOptions (..))
+import qualified Hix.Data.GhciConfig
+import Hix.Data.GhciConfig (PreludeConfig, PreludePackage (PreludePackageName, PreludePackageSpec), PreprocConfig)
+import Hix.Json (jsonConfig)
+import Hix.Monad (M)
+import Hix.Options (PreprocOptions (..), TargetSpec (TargetForFile))
+import Hix.Optparse (JsonConfig)
 
 type Regex = IndexedTraversal' Int ByteString Match
 
@@ -38,6 +45,22 @@
   }
   deriving stock (Show)
 
+fromPreludeConfig :: PreludeConfig -> Prelude
+fromPreludeConfig conf =
+  Prelude (encodeUtf8 (name conf.package)) (encodeUtf8 conf.module_.unModuleName)
+  where
+    name = \case
+      PreludePackageName n -> n
+      PreludePackageSpec n -> n
+
+data CabalConfig =
+  CabalConfig {
+    extensions :: [Builder],
+    ghcOptions :: [Builder],
+    prelude :: Maybe Prelude
+  }
+  deriving stock (Show, Generic)
+
 newtype DummyExportName =
   DummyExportName { unDummyExportName :: ByteString }
   deriving stock (Eq, Show, Generic)
@@ -86,15 +109,10 @@
 languagePragma exts =
   [exon|{-# language #{Exon.intercalate ", " exts} #-}|]
 
-extensionsPragma :: BuildInfo -> Maybe Builder
-extensionsPragma info
-  | null exts = Nothing
-  | otherwise = Just (languagePragma exts)
-  where
-    exts = maybeToList (dlExtension =<< info.defaultLanguage) ++ mapMaybe extension info.defaultExtensions
-    dlExtension = \case
-      UnknownLanguage _ -> Nothing
-      lang -> Just (stringUtf8 (show lang))
+extensionsPragma :: CabalConfig -> Maybe Builder
+extensionsPragma conf
+  | null conf.extensions = Nothing
+  | otherwise = Just (languagePragma conf.extensions)
 
 optionsPragma :: Builder -> Builder
 optionsPragma opts =
@@ -440,24 +458,53 @@
 
 preprocessModule ::
   Path Abs File ->
-  BuildInfo ->
+  CabalConfig ->
   DummyExportName ->
   ByteString ->
   Builder
-preprocessModule source info dummyExportName inLines =
-  assemble source header (extensionsPragma info) options dummyExportName
+preprocessModule source conf dummyExportName inLines =
+  assemble source header (extensionsPragma conf) options dummyExportName
   where
-    options = Exon.intercalate " " <$> nonEmpty (stringUtf8 <$> ghcOptions)
-    PerCompilerFlavor ghcOptions _ = info.options
-    customPrelude = findPrelude info.mixins
-    header = scanHeader customPrelude inLines
+    options = Exon.intercalate " " <$> nonEmpty conf.ghcOptions
+    header = scanHeader conf.prelude inLines
 
--- TODO add common stanzas
-preprocess :: PreprocOptions -> ExceptT Error IO ()
-preprocess PreprocOptions {..} = do
-  info <- buildInfoForFile source
-  inLines <- tryIO (ByteString.readFile (toFilePath inFile))
+preprocessWith :: PreprocOptions -> CabalConfig -> M ()
+preprocessWith opt conf = do
+  inLines <- lift (tryIO (ByteString.readFile (toFilePath opt.inFile)))
   dummyNumber :: Int <- randomRIO (10000, 10000000)
   let dummyExportName = DummyExportName [exon|Hix_Dummy_#{show dummyNumber}|]
-  let result = preprocessModule source info dummyExportName inLines
-  tryIO (ByteStringBuilder.writeFile (toFilePath outFile) result)
+  let result = preprocessModule opt.source conf dummyExportName inLines
+  lift (tryIO (ByteStringBuilder.writeFile (toFilePath opt.outFile) result))
+
+fromConfig :: Path Abs File -> Either PreprocConfig JsonConfig -> M CabalConfig
+fromConfig source pconf = do
+  conf <- either pure jsonConfig pconf
+  target <- targetComponent conf.packages (TargetForFile source)
+  pure CabalConfig {
+    extensions = stringUtf8 <$> target.component.language : target.component.extensions,
+    ghcOptions = stringUtf8 <$> target.component.ghcOptions,
+    prelude = fromPreludeConfig <$> target.component.prelude
+    }
+
+fromCabal :: BuildInfo -> CabalConfig
+fromCabal info =
+  CabalConfig {
+    extensions = maybeToList (dlExtension =<< info.defaultLanguage) <> mapMaybe extension info.defaultExtensions,
+    ghcOptions = stringUtf8 <$> ghcOptions,
+    prelude = findPrelude info.mixins
+    }
+  where
+    PerCompilerFlavor ghcOptions _ = info.options
+    dlExtension = \case
+      UnknownLanguage _ -> Nothing
+      lang -> Just (stringUtf8 (show lang))
+
+fromCabalFile :: Path Abs File -> M CabalConfig
+fromCabalFile source =
+  fromCabal <$> lift (buildInfoForFile source)
+
+-- TODO add common stanzas
+preprocess :: PreprocOptions -> M ()
+preprocess opt = do
+  conf <- maybe (fromCabalFile opt.source) (fromConfig opt.source) opt.config
+  preprocessWith opt conf
diff --git a/test/Hix/Test/CabalFile.hs b/test/Hix/Test/CabalFile.hs
--- a/test/Hix/Test/CabalFile.hs
+++ b/test/Hix/Test/CabalFile.hs
@@ -1,6 +1,12 @@
+{-# language CPP #-}
+
 module Hix.Test.CabalFile where
 
+#if MIN_VERSION_Cabal(3,8,0)
+import Distribution.Simple.PackageDescription (parseString)
+#else
 import Distribution.Fields.ParseResult (parseString)
+#endif
 import Distribution.PackageDescription (GenericPackageDescription)
 import Distribution.PackageDescription.Parsec (parseGenericPackageDescription)
 import Distribution.Verbosity (silent)
diff --git a/test/Hix/Test/GhciTest.hs b/test/Hix/Test/GhciTest.hs
--- a/test/Hix/Test/GhciTest.hs
+++ b/test/Hix/Test/GhciTest.hs
@@ -54,7 +54,11 @@
   ComponentConfig {
     name,
     sourceDirs = SourceDirs [SourceDir dir],
-    runner = Just runner
+    runner = Just runner,
+    extensions = [],
+    language = "GHC2021",
+    ghcOptions = [],
+    prelude = Nothing
   }
 
 packages :: PackagesConfig
diff --git a/test/Hix/Test/PreprocTest.hs b/test/Hix/Test/PreprocTest.hs
--- a/test/Hix/Test/PreprocTest.hs
+++ b/test/Hix/Test/PreprocTest.hs
@@ -11,7 +11,7 @@
 import Hedgehog (TestT, evalMaybe, (===))
 import Path (absfile)
 
-import Hix.Preproc (preprocessModule)
+import Hix.Preproc (fromCabal, preprocessModule)
 import Hix.Test.CabalFile (testPackage, testPackageNoPrelude)
 
 pragmas :: Text
@@ -27,8 +27,8 @@
 preprocTestNoPrelude module_ target =
   withFrozenCallStack do
     pkg <- liftIO testPackageNoPrelude
-    info <- evalMaybe (pkg.condLibrary <&> \ l -> l.condTreeData.libBuildInfo)
-    let result = toLazyByteString (preprocessModule [absfile|/foo/bar/Foo.hs|] info "Hix_Dummy" module_)
+    conf <- evalMaybe (pkg.condLibrary <&> \ l -> fromCabal l.condTreeData.libBuildInfo)
+    let result = toLazyByteString (preprocessModule [absfile|/foo/bar/Foo.hs|] conf "Hix_Dummy" module_)
     Text.lines target === Text.lines (decodeUtf8 result)
 
 preprocTest ::
@@ -39,8 +39,8 @@
 preprocTest module_ target =
   withFrozenCallStack do
     pkg <- liftIO testPackage
-    info <- evalMaybe (pkg.condLibrary <&> \ l -> l.condTreeData.libBuildInfo)
-    let result = toLazyByteString (preprocessModule [absfile|/foo/bar/Foo.hs|] info "Hix_Dummy" module_)
+    conf <- evalMaybe (pkg.condLibrary <&> \ l -> fromCabal l.condTreeData.libBuildInfo)
+    let result = toLazyByteString (preprocessModule [absfile|/foo/bar/Foo.hs|] conf "Hix_Dummy" module_)
     Text.lines target === Text.lines (decodeUtf8 result)
 
 moduleInsert :: ByteString
