Skip to content

Commit

Permalink
Add core source code (#4)
Browse files Browse the repository at this point in the history
* rename server to core

* add core codebase

* add exports and remove namespaces

* changeset
  • Loading branch information
AlexGeb authored Dec 13, 2024
1 parent ede511b commit 84fb9b8
Show file tree
Hide file tree
Showing 30 changed files with 786 additions and 123 deletions.
5 changes: 5 additions & 0 deletions .changeset/nice-carrots-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@inato-form/core": minor
---

Add effect-form core codebase
14 changes: 6 additions & 8 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
actions: write # Necessary to cancel workflow executions
checks: write # Necessary to write reports
permissions:
actions: write # Necessary to cancel workflow executions
checks: write # Necessary to write reports
pull-requests: write # Necessary to comment on PRs
contents: read
packages: write
Expand All @@ -26,9 +26,7 @@ jobs:
- uses: actions/checkout@v4
- name: Install dependencies
uses: ./.github/actions/setup
- run: pnpm codegen
- name: Check source state
run: git add packages/*/src && git diff-index --cached HEAD --exit-code packages/*/src
- run: pnpm build

types:
name: Types
Expand All @@ -51,13 +49,13 @@ jobs:
- run: pnpm lint

test:
name: Test
name: Test
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Install dependencies
uses: ./.github/actions/setup
- run: pnpm vitest
- run: pnpm vitest
env:
NODE_OPTIONS: --max_old_space_size=8192
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[typescriptreact]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"eslint.validate": ["markdown", "javascript", "typescript"],
"editor.codeActionsOnSave": {
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
],
"scripts": {
"clean": "node scripts/clean.mjs",
"codegen": "pnpm --recursive --parallel run codegen && pnpm lint-fix",
"build": "tsc -b tsconfig.build.json && pnpm --recursive --parallel run build",
"check": "tsc -b tsconfig.json",
"check-recursive": "pnpm --recursive exec tsc -b tsconfig.json",
Expand Down
1 change: 0 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"directory": "dist"
},
"scripts": {
"codegen": "build-utils prepare-v2",
"build": "pnpm build-esm && pnpm build-annotate && pnpm build-cjs && build-utils pack-v2",
"build-esm": "tsc -b tsconfig.build.json",
"build-cjs": "babel build/esm --plugins @babel/transform-export-namespace-from --plugins @babel/transform-modules-commonjs --out-dir build/cjs --source-maps",
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
18 changes: 13 additions & 5 deletions packages/server/package.json → packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@
"version": "0.0.1",
"type": "module",
"license": "MIT",
"description": "The server template",
"description": "The core package",
"repository": {
"type": "git",
"url": "Inato SAS",
"directory": "packages/server"
"directory": "packages/core"
},
"publishConfig": {
"access": "public",
"directory": "dist"
},
"scripts": {
"codegen": "build-utils prepare-v2",
"build": "pnpm build-esm && pnpm build-annotate && pnpm build-cjs && build-utils pack-v2",
"build-esm": "tsc -b tsconfig.build.json",
"build-cjs": "babel build/esm --plugins @babel/transform-export-namespace-from --plugins @babel/transform-modules-commonjs --out-dir build/cjs --source-maps",
Expand All @@ -26,8 +25,7 @@
"dependencies": {
"@effect/platform": "latest",
"@effect/platform-node": "latest",
"@inato-form/fields": "workspace:^",
"effect": "latest"
"@inato-form/fields": "workspace:^"
},
"effect": {
"generateExports": {
Expand All @@ -40,5 +38,15 @@
"**/*.ts"
]
}
},
"devDependencies": {
"@types/react": "^19.0.1",
"effect": "^3.11.6",
"react": "^19.0.0",
"react-hook-form": "^7.54.1"
},
"peerDependencies": {
"effect": "^3.11.6",
"react": "^18"
}
}
171 changes: 171 additions & 0 deletions packages/core/src/FormBody.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import type { Types } from "effect"
import { Predicate, Schema, Tuple } from "effect"

import type * as FormField from "./FormField.js"

type FormSchemaFields<Fields extends AnyFields> = Types.Simplify<
{
[key in keyof Fields]: Fields[key] extends Any ? FormSchema<Fields[key]["fields"]>
: Fields[key]["schema"]
}
>

type FormSchemaStruct<T> = T extends Schema.Struct.Fields ? Schema.Struct<T>
: never

type FormSchema<Fields extends AnyFields> = FormSchemaStruct<
FormSchemaFields<Fields>
>

export const FormStructTypeId = Symbol.for("@inato/Form/FormBody/FormStruct")
export type FormStructTypeId = typeof FormStructTypeId

export const isFormStruct = (
value: unknown
): value is FormStruct<AnyFields> => Predicate.hasProperty(value, FormStructTypeId)

interface FormStruct<Fields extends AnyFields> {
[FormStructTypeId]: FormStructTypeId
fields: Fields
schema: FormSchema<Fields>
defaultValue: FormSchema<Fields>["Encoded"]
}

export const FormArrayTypeId = Symbol.for("@inato/Form/FormBody/FormArray")
export type FormArrayTypeId = typeof FormArrayTypeId

export const isFormArray = (value: unknown): value is AnyArray => Predicate.hasProperty(value, FormArrayTypeId)

interface FormArray<Field extends AnyNonIterableField> {
[FormArrayTypeId]: FormArrayTypeId
field: Field
schema: Schema.Array$<Field["schema"]>
defaultValue: FormArray<Field>["schema"]["Encoded"]
}

export const FormMapTypeId = Symbol.for("@inato/Form/FormBody/FormMap")
export type FormMapTypeId = typeof FormMapTypeId

export const isFormMap = (value: unknown): value is AnyMap => Predicate.hasProperty(value, FormMapTypeId)

interface FormMap<
Key extends Schema.Schema.AnyNoContext,
Field extends AnyNonIterableField
> {
[FormMapTypeId]: FormMapTypeId
field: Field
keySchema: Key
schema: Schema.HashMap<Key, Field["schema"]>
defaultValue: FormMap<Key, Field>["schema"]["Encoded"]
defaultValueFor: (
keys: ReadonlyArray<Key["Encoded"]>
) => FormMap<Key, Field>["defaultValue"]
}

export const FormRawTypeId = Symbol.for("@inato/Form/FormBody/FormRaw")
export type FormRawTypeId = typeof FormRawTypeId

export const isFormRaw = (value: unknown): value is AnyRaw => Predicate.hasProperty(value, FormRawTypeId)

interface FormRaw<S extends Schema.Schema.AnyNoContext> {
[FormRawTypeId]: FormRawTypeId
schema: S
defaultValue: S["Encoded"]
}

const makeStructSchema = <Fields extends AnyFields>(
fields: Fields
): {
schema: FormSchema<Fields>
defaultValue: FormSchema<Fields>["Encoded"]
} => {
const schemaFields: Types.Mutable<Schema.Struct.Fields> = {}
const defaultValue: Record<string, unknown> = {}
for (const [key, field] of Object.entries(fields)) {
schemaFields[key] = field.schema
if ("matchDefaultValue" in field) {
field.matchDefaultValue({
withDefaultValue: (value) => {
defaultValue[key] = value
}
})
} else {
defaultValue[key] = field.defaultValue
}
}
// @ts-expect-error "structSchema is indeed of type FormSchema<Fields>"
const structSchema: FormSchema<Fields> = Schema.Struct(schemaFields)
return { schema: structSchema, defaultValue }
}

export const struct = <Fields extends AnyFields>(
fields: Fields
): FormStruct<Fields> => {
const { defaultValue, schema } = makeStructSchema(fields)
return { [FormStructTypeId]: FormStructTypeId, fields, schema, defaultValue }
}

export const array = <Field extends AnyNonIterableField>(
field: Field
): FormArray<Field> => {
return {
[FormArrayTypeId]: FormArrayTypeId,
field,
schema: Schema.Array(field.schema),
defaultValue: []
}
}

export const map = <A, I, Field extends AnyNonIterableField>({
field,
key
}: {
key: Schema.Schema<A, I>
field: Field
}): FormMap<Schema.Schema<A, I>, Field> => {
return {
[FormMapTypeId]: FormMapTypeId,
field,
keySchema: key,
schema: Schema.HashMap({ key, value: field.schema }),
defaultValue: [],
defaultValueFor(keys) {
const defaultValue: typeof field.schema.Encoded = "getDefaultValue" in field
? field.getDefaultValue()
: field.defaultValue
return keys.map((key) => Tuple.make(key, defaultValue))
}
}
}

export const raw = <S extends Schema.Schema.AnyNoContext>({
defaultValue,
schema
}: {
schema: S
defaultValue: S["Encoded"]
}): FormRaw<S> => {
return {
[FormRawTypeId]: FormRawTypeId,
schema,
defaultValue
}
}

export type AnyNonIterableField = Any | FormField.Any

export type AnyArray = FormArray<AnyNonIterableField>

export type AnyMap = FormMap<Schema.Schema.AnyNoContext, AnyNonIterableField>

export type AnyRaw = FormRaw<Schema.Schema.AnyNoContext>

export type AnyIterable =
| FormArray<AnyNonIterableField>
| FormMap<Schema.Schema.AnyNoContext, AnyNonIterableField>

export type AnyField = AnyNonIterableField | AnyIterable | AnyRaw

export type AnyFields = Record<string, AnyField>

export type Any = FormStruct<AnyFields>
Loading

0 comments on commit 84fb9b8

Please sign in to comment.