Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Plugin system #410

Merged
merged 5 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions src/Garn/Common.hs
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
module Garn.Common (nixpkgsInput, nixArgs, currentSystem) where
{-# LANGUAGE TemplateHaskell #-}

module Garn.Common
( garnCliVersion,
Garn.Common.nixpkgsInput,
nixArgs,
currentSystem,
)
where

import Cradle (StdoutUntrimmed (..), run)
import Data.Aeson (eitherDecode)
import Data.String.Conversions (cs)
import Garn.ImportVersion (Versions (..), versionsSplice)

versionFromJson :: Versions
versionFromJson = $(versionsSplice)

garnCliVersion :: String
garnCliVersion = tsLibVersion versionFromJson

-- | pinned to master on 2023-10-26
nixpkgsInput :: String
nixpkgsInput = "github:NixOS/nixpkgs/6fc7203e423bbf1c8f84cccf1c4818d097612566"
nixpkgsInput = Garn.ImportVersion.nixpkgsInput versionFromJson

nixArgs :: [String]
nixArgs =
Expand Down
3 changes: 1 addition & 2 deletions src/Garn/GarnConfig.hs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ import Data.String.Conversions (cs)
import Data.String.Interpolate (i)
import Data.String.Interpolate.Util (unindent)
import GHC.Generics (Generic)
import Garn.Common (nixArgs, nixpkgsInput)
import Garn.Common (garnCliVersion, nixArgs, nixpkgsInput)
import qualified Garn.Errors
import Garn.Utils (garnCliVersion)
import System.Directory (doesFileExist, getCurrentDirectory)
import System.Exit (ExitCode (..), exitWith)
import System.IO (hClose, hPutStr, stderr)
Expand Down
25 changes: 15 additions & 10 deletions src/Garn/ImportVersion.hs
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
module Garn.ImportVersion (garnVersionSplice) where
{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE TemplateHaskell #-}

module Garn.ImportVersion (Versions (..), versionsSplice) where

import Data.Aeson (FromJSON, eitherDecodeFileStrict')
import GHC.Generics (Generic)
import Language.Haskell.TH (Exp (LitE), Lit (StringL), Q, runIO)
import Language.Haskell.TH (Exp, Q, runIO)
import Language.Haskell.TH.Syntax (Lift (..))

newtype Version = Version
{ tsLibVersion :: String
data Versions = Versions
{ tsLibVersion :: String,
nixpkgsInput :: String
}
deriving stock (Generic)
deriving stock (Generic, Lift)
deriving anyclass (FromJSON)

garnVersionSplice :: Q Exp
garnVersionSplice = do
version <- runIO $ eitherDecodeFileStrict' "./ts/internal/version.json"
case version of
Right version -> pure $ LitE $ StringL $ tsLibVersion version
versionsSplice :: Q Exp
versionsSplice = do
versions <- runIO $ eitherDecodeFileStrict' "./ts/internal/version.json"
case versions of
Right (versions :: Versions) -> [|versions|]
Left err -> fail err
2 changes: 1 addition & 1 deletion src/Garn/Optparse.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ module Garn.Optparse
where

import qualified Data.Map as Map
import Garn.Common (garnCliVersion)
import Garn.GarnConfig
import Garn.Utils (garnCliVersion)
import Options.Applicative hiding (command)
import qualified Options.Applicative as OA
import qualified Options.Applicative.Help.Pretty as OA
Expand Down
6 changes: 0 additions & 6 deletions src/Garn/Utils.hs
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
{-# LANGUAGE TemplateHaskell #-}

module Garn.Utils where

import Data.String.Conversions (cs)
import Debug.Trace (trace)
import Garn.ImportVersion (garnVersionSplice)
import Text.Pretty.Simple (pShow)

dbg :: (Show a) => a -> a
dbg a = trace (cs $ pShow a) a

garnCliVersion :: String
garnCliVersion = $(garnVersionSplice)
4 changes: 3 additions & 1 deletion ts/internal/version.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{
"tsLibVersion": "v0.0.15"
"tsLibVersion": "v0.0.15",
"nixpkgsInputComment": "pinned to master on 2023-10-26",
"nixpkgsInput": "github:NixOS/nixpkgs/6fc7203e423bbf1c8f84cccf1c4818d097612566"
}
7 changes: 6 additions & 1 deletion ts/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ export {
} from "./environment.ts";
export { type Executable } from "./executable.ts";
export { mkPackage, type Package } from "./package.ts";
export { mkProject, type Project } from "./project.ts";
export {
mkProject,
type Project,
type Plugin,
type ProjectHelpers,
} from "./project.ts";
export { editGarnConfig } from "./edit.ts";

// languages
Expand Down
42 changes: 20 additions & 22 deletions ts/project.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import { Check } from "./check.ts";
import { Executable } from "./executable.ts";
import { Plugin, Project } from "./project.ts";
import { describe, it } from "https://deno.land/[email protected]/testing/bdd.ts";
import * as garn from "./mod.ts";
import * as nix from "./nix.ts";
import { assertStdout, assertSuccess, runExecutable } from "./testUtils.ts";
import { Package } from "./mod.ts";

const assertTypeIsCheck = (_c: Check) => {};
const assertTypeIsExecutable = (_e: Executable) => {};
const assertTypeIsPackage = (_p: Package) => {};
const assertTypeIsCheck = (_c: garn.Check) => {};
const assertTypeIsExecutable = (_e: garn.Executable) => {};
const assertTypeIsPackage = (_p: garn.Package) => {};

const _testTypeCheckingOfAddCheck = (project: Project) => {
const _testTypeCheckingOfAddCheck = (project: garn.Project) => {
const p = project
.addExecutable("unrelated1", "true")
.addCheck("check", "true")
Expand All @@ -23,7 +18,7 @@ const _testTypeCheckingOfAddCheck = (project: Project) => {
p.check``;
};

const _testTypeCheckingOfAddCheckTemplate = (project: Project) => {
const _testTypeCheckingOfAddCheckTemplate = (project: garn.Project) => {
const p = project
.addExecutable("unrelated1", "true")
.addCheck("check")`true`.addExecutable("unrelated2", "true");
Expand All @@ -34,7 +29,7 @@ const _testTypeCheckingOfAddCheckTemplate = (project: Project) => {
p.check``;
};

const _testTypeCheckingOfAddExecutable = (project: Project) => {
const _testTypeCheckingOfAddExecutable = (project: garn.Project) => {
const p = project
.addExecutable("unrelated1", "true")
.addExecutable("shell", "true")
Expand All @@ -46,7 +41,7 @@ const _testTypeCheckingOfAddExecutable = (project: Project) => {
p.shell``;
};

const _testTypeCheckingOfAddExecutableTemplate = (project: Project) => {
const _testTypeCheckingOfAddExecutableTemplate = (project: garn.Project) => {
const p = project
.addExecutable("unrelated1", "true")
.addExecutable("shell")`true`.addExecutable("unrelated2", "true");
Expand All @@ -61,7 +56,7 @@ describe("Project.add", () => {
it("allows adding fields with .add", () => {
const project = garn
.mkProject({ description: "" }, {})
.add((self) => ({ ...self, foo: garn.shell("echo foo") }));
.add((_self) => ({ foo: garn.shell("echo foo") }));
const output = runExecutable(project.foo);
assertSuccess(output);
assertStdout(output, "foo\n");
Expand All @@ -73,8 +68,8 @@ describe("Project.add", () => {
{ description: "", defaultEnvironment: garn.emptyEnvironment },
{},
)
.withDevTools([garn.mkPackage(nix.nixRaw`pkgs.hello`, "")])
.add((self) => ({ ...self, foo: self.shell("hello") }));
.withDevTools([garn.mkPackage(garn.nix.nixRaw`pkgs.hello`, "")])
.add((self) => ({ foo: self.shell("hello") }));
const output = runExecutable(project.foo);
assertSuccess(output);
assertStdout(output, "Hello, world!\n");
Expand All @@ -92,7 +87,7 @@ describe("Project.add", () => {
`,
},
)
.add((self) => ({ ...self, foo: self.shell`${self.package}/bin/main` }));
.add((self) => ({ foo: self.shell`${self.package}/bin/main` }));
const output = runExecutable(project.foo);
assertSuccess(output);
assertStdout(output, "main executable\n");
Expand Down Expand Up @@ -137,7 +132,7 @@ describe("Project.add", () => {
});

it("provides a nice type synonym for plugins that add a field", () => {
const plugin: Plugin<{ addedField: garn.Package }> = (p) => ({
const plugin: garn.Plugin<{ addedField: garn.Package }> = (_p) => ({
addedField: garn.build``,
});
const project = garn
Expand All @@ -152,7 +147,9 @@ describe("Project.add", () => {
});

it("provides a nice type synonym for plugins that add multiple fields", () => {
const plugin: Plugin<{ one: garn.Package; two: garn.Check }> = (p) => ({
const plugin: garn.Plugin<{ one: garn.Package; two: garn.Check }> = (
_p,
) => ({
one: garn.build``,
two: garn.check(""),
});
Expand All @@ -169,9 +166,10 @@ describe("Project.add", () => {
});

it("provides a nice interface for plugins that depend on a non-standard field", () => {
const plugin: Plugin<{ addedField: Executable }, { dep: Package }> = (
p,
) => ({
const plugin: garn.Plugin<
{ addedField: garn.Executable },
{ dep: garn.Package }
> = (p) => ({
addedField: garn.shell`${p.dep}/bin/whatever`,
});
const project = garn
Expand All @@ -188,7 +186,7 @@ describe("Project.add", () => {
});

it("allows overwriting fields", () => {
const plugin: Plugin<{ field: Package }> = (p) => ({
const plugin: garn.Plugin<{ field: garn.Package }> = (_p) => ({
field: garn.build``,
});
const project = garn
Expand Down
36 changes: 35 additions & 1 deletion ts/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,45 @@ export type ProjectData = {
defaultExecutable?: Executable;
};

/**
* `Plugin`s automatically add one or more fields to your project. You can use
* existing plugins to automatically add prepackaged functionality to your `Project`.
* declare them manually yourself.
*
* Here's an example of a simple `Plugin` and how to use it:
*
* ```typescript
* import * as garn from "https://garn.io/ts/v0.0.15/mod.ts";
*
* const prettier =
* (
* globPattern: string,
* ): garn.Plugin<
* { formatCheck: garn.Check; format: garn.Executable },
* garn.ProjectHelpers
* > =>
* (p) =>
* p
* .withDevTools([
* garn.mkPackage(garn.nix.nixRaw`pkgs.nodePackages.prettier`, "prettier"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forget - what's the status of including nodePackage in nixpkgs? If it's merged, then this should be changed. If not, then reminder to change this (and the website!) when it is merged.

(Maybe cc @alexdavid)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, not merged yet. Here's the PR: #382.

* ])
* .addCheck("formatCheck", `prettier --check '${globPattern}'`)
* .addExecutable("format", `prettier --write '${globPattern}'`);
*
* export const frontend = garn.javascript
* .mkNpmProject({
* src: ".",
* description: "An NPM frontend",
* nodeVersion: "18",
* })
* .add(prettier("src/*.ts"));
* ```
*/
export type Plugin<Additions, Dependencies = object> = (
project: Dependencies & ProjectData,
) => Additions;

type ProjectHelpers = {
export type ProjectHelpers = {
/**
* Returns a new Project with the provided devtools added to the default
* Environment.
Expand Down